From eca64ce5357b8248f9098da3073191ecc31a42c9 Mon Sep 17 00:00:00 2001 From: Aitor Martinez Date: Fri, 18 Nov 2016 10:39:52 +0100 Subject: [PATCH 1/2] [issue #614] Added UavViewer Py --- src/tools/uav_viewer_py/CMakeLists.txt | 14 + src/tools/uav_viewer_py/generateGUI | 9 + src/tools/uav_viewer_py/gui/GUI.py | 168 ++++ src/tools/uav_viewer_py/gui/__init__.py | 0 .../uav_viewer_py/gui/attitudeIndicator.py | 156 ++++ src/tools/uav_viewer_py/gui/cameraWidget.py | 83 ++ src/tools/uav_viewer_py/gui/communicator.py | 24 + src/tools/uav_viewer_py/gui/sensorsWidget.py | 270 ++++++ src/tools/uav_viewer_py/gui/teleopWidget.py | 113 +++ src/tools/uav_viewer_py/gui/threadGUI.py | 43 + src/tools/uav_viewer_py/gui/ui_gui.py | 107 +++ src/tools/uav_viewer_py/gui/ui_gui.ui | 279 ++++++ src/tools/uav_viewer_py/resources/__init__.py | 0 src/tools/uav_viewer_py/resources/ball.png | Bin 0 -> 1869 bytes src/tools/uav_viewer_py/resources/play.png | Bin 0 -> 5207 bytes .../uav_viewer_py/resources/resources.qrc | 7 + src/tools/uav_viewer_py/resources/stop.png | Bin 0 -> 4817 bytes src/tools/uav_viewer_py/resources_rc.py | 798 ++++++++++++++++++ src/tools/uav_viewer_py/uav_viewer.py | 61 ++ src/tools/uav_viewer_py/uav_viewer_py.cfg | 6 + 20 files changed, 2138 insertions(+) create mode 100644 src/tools/uav_viewer_py/CMakeLists.txt create mode 100755 src/tools/uav_viewer_py/generateGUI create mode 100644 src/tools/uav_viewer_py/gui/GUI.py create mode 100644 src/tools/uav_viewer_py/gui/__init__.py create mode 100644 src/tools/uav_viewer_py/gui/attitudeIndicator.py create mode 100644 src/tools/uav_viewer_py/gui/cameraWidget.py create mode 100644 src/tools/uav_viewer_py/gui/communicator.py create mode 100644 src/tools/uav_viewer_py/gui/sensorsWidget.py create mode 100644 src/tools/uav_viewer_py/gui/teleopWidget.py create mode 100644 src/tools/uav_viewer_py/gui/threadGUI.py create mode 100644 src/tools/uav_viewer_py/gui/ui_gui.py create mode 100644 src/tools/uav_viewer_py/gui/ui_gui.ui create mode 100644 src/tools/uav_viewer_py/resources/__init__.py create mode 100644 src/tools/uav_viewer_py/resources/ball.png create mode 100644 src/tools/uav_viewer_py/resources/play.png create mode 100644 src/tools/uav_viewer_py/resources/resources.qrc create mode 100644 src/tools/uav_viewer_py/resources/stop.png create mode 100644 src/tools/uav_viewer_py/resources_rc.py create mode 100755 src/tools/uav_viewer_py/uav_viewer.py create mode 100644 src/tools/uav_viewer_py/uav_viewer_py.cfg diff --git a/src/tools/uav_viewer_py/CMakeLists.txt b/src/tools/uav_viewer_py/CMakeLists.txt new file mode 100644 index 000000000..b628d8e1c --- /dev/null +++ b/src/tools/uav_viewer_py/CMakeLists.txt @@ -0,0 +1,14 @@ + + +## INSTALL ## + +# Install .py +FILE(GLOB_RECURSE HEADERS_FILES ${CMAKE_CURRENT_SOURCE_DIR}/*py) +FOREACH(header ${HEADERS_FILES}) + INSTALL(FILES ${header} DESTINATION share/jderobot/python/colorTuner_py/ COMPONENT tools) +ENDFOREACH(header) + +# Install gui +INSTALL (DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/gui DESTINATION share/jderobot/python/colorTuner_py COMPONENT tools PATTERN .svn EXCLUDE) + + diff --git a/src/tools/uav_viewer_py/generateGUI b/src/tools/uav_viewer_py/generateGUI new file mode 100755 index 000000000..d2d26b788 --- /dev/null +++ b/src/tools/uav_viewer_py/generateGUI @@ -0,0 +1,9 @@ +#!/bin/bash + cd resources + pyrcc5 resources.qrc -o resources_rc.py + mv resources_rc.py .. + + cd ../gui + pyuic5 ui_gui.ui > ui_gui.py + cd .. + diff --git a/src/tools/uav_viewer_py/gui/GUI.py b/src/tools/uav_viewer_py/gui/GUI.py new file mode 100644 index 000000000..9e234979c --- /dev/null +++ b/src/tools/uav_viewer_py/gui/GUI.py @@ -0,0 +1,168 @@ +# +# Copyright (C) 1997-2016 JDE Developers Team +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see http://www.gnu.org/licenses/. +# Authors : +# Alberto Martin Florido +# Aitor Martinez Fernandez +# + + +from PyQt5.QtCore import pyqtSignal, Qt +from PyQt5.QtWidgets import QMainWindow +from gui.ui_gui import Ui_MainWindow +from gui.teleopWidget import TeleopWidget +from gui.cameraWidget import CameraWidget +from gui.communicator import Communicator +from gui.sensorsWidget import SensorsWidget + + +class MainWindow(QMainWindow, Ui_MainWindow): + updGUI = pyqtSignal() + + def __init__(self, parent=None): + super(MainWindow, self).__init__(parent) + self.setupUi(self) + self.teleop = TeleopWidget(self) + self.tlLayout.addWidget(self.teleop) + self.teleop.setVisible(True) + + self.record = False + + self.updGUI.connect(self.updateGUI) + + self.cameraCheck.stateChanged.connect(self.showCameraWidget) + self.sensorsCheck.stateChanged.connect(self.showSensorsWidget) + + self.rotationDial.valueChanged.connect(self.rotationChange) + self.altdSlider.valueChanged.connect(self.altitudeChange) + + self.cameraWidget = CameraWidget(self) + self.sensorsWidget = SensorsWidget(self) + + self.cameraCommunicator = Communicator() + self.trackingCommunicator = Communicator() + + self.stopButton.clicked.connect(self.stopClicked) + self.resetButton.clicked.connect(self.resetClicked) + self.takeoffButton.clicked.connect(self.takeOffClicked) + self.takeoff = False + self.reset = False + + def getCamera(self): + return self.camera + + def setCamera(self, camera): + self.camera = camera + + def getNavData(self): + return self.navdata + + def setNavData(self, navdata): + self.navdata = navdata + + def getPose3D(self): + return self.pose + + def setPose3D(self, pose): + self.pose = pose + + def getCMDVel(self): + return self.cmdvel + + def setCMDVel(self, cmdvel): + self.cmdvel = cmdvel + + def getExtra(self): + return self.extra + + def setExtra(self, extra): + self.extra = extra + + + def updateGUI(self): + self.cameraWidget.imageUpdate.emit() + self.sensorsWidget.sensorsUpdate.emit() + + def stopClicked(self): + if self.record == True: + self.extra.record(False) + self.rotationDial.setValue(self.altdSlider.maximum() / 2) + self.altdSlider.setValue(self.altdSlider.maximum() / 2) + self.cmdvel.sendCMDVel(0, 0, 0, 0, 0, 0) + self.teleop.stopSIG.emit() + + def takeOffClicked(self): + if (self.takeoff == True): + self.takeoffButton.setText("Take Off") + self.extra.land() + self.takeoff = False + else: + self.takeoffButton.setText("Land") + self.extra.takeoff() + self.takeoff = True + + def resetClicked(self): + if self.reset == True: + self.resetButton.setText("Reset") + self.extra.reset() + self.reset = False + else: + self.resetButton.setText("Unreset") + self.extra.reset() + self.reset = True + + def showCameraWidget(self, state): + if state == Qt.Checked: + self.cameraWidget.show() + else: + self.cameraWidget.close() + + def closeCameraWidget(self): + self.cameraCheck.setChecked(False) + + def showSensorsWidget(self, state): + if state == Qt.Checked: + self.sensorsWidget.show() + else: + self.sensorsWidget.close() + + def closeSensorsWidget(self): + self.sensorsCheck.setChecked(False) + + def rotationChange(self, value): + value = (1.0 / (self.rotationDial.maximum() / 2)) * (value - (self.rotationDial.maximum() / 2)) + self.rotValue.setText('%.2f' % value) + self.cmdvel.setYaw(value) + self.cmdvel.sendVelocities() + + def altitudeChange(self, value): + value = (1.0 / (self.altdSlider.maximum() / 2)) * (value - (self.altdSlider.maximum() / 2)) + self.altdValue.setText('%.2f' % value) + self.cmdvel.setVZ(value) + self.cmdvel.sendVelocities() + + def setXYValues(self, newX, newY): + self.XValue.setText('%.2f' % newX) + self.YValue.setText('%.2f' % newY) + self.cmdvel.setVX(-newY) + self.cmdvel.setVY(-newX) + self.cmdvel.sendVelocities() + + def closeEvent(self, event): + self.camera.stop() + self.navdata.stop() + self.pose.stop() + event.accept() + diff --git a/src/tools/uav_viewer_py/gui/__init__.py b/src/tools/uav_viewer_py/gui/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/src/tools/uav_viewer_py/gui/attitudeIndicator.py b/src/tools/uav_viewer_py/gui/attitudeIndicator.py new file mode 100644 index 000000000..893025904 --- /dev/null +++ b/src/tools/uav_viewer_py/gui/attitudeIndicator.py @@ -0,0 +1,156 @@ +# +# Copyright (C) 1997-2015 JDE Developers Team +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see http://www.gnu.org/licenses/. +# Authors : +# Alberto Martin Florido +# +import math +from PyQt4 import Qt +import PyQt4.Qwt5 as Qwt + +def enumList(enum, sentinel): + ''' + ''' + return [enum(i) for i in range(sentinel)] + +colorGroupList = enumList( + Qt.QPalette.ColorGroup, Qt.QPalette.NColorGroups) +colorRoleList = enumList( + Qt.QPalette.ColorRole, Qt.QPalette.NColorRoles) +handList = enumList( + Qwt.QwtAnalogClock.Hand, Qwt.QwtAnalogClock.NHands) + +class AttitudeIndicatorNeedle(Qwt.QwtDialNeedle): + + def __init__(self, color): + Qwt.QwtDialNeedle.__init__(self) + palette = Qt.QPalette() + for colourGroup in colorGroupList: + palette.setColor(colourGroup, Qt.QPalette.Text, color) + self.setPalette(palette) + + # __init__() + + def draw(self, painter, center, length, direction, cg): + direction *= math.pi / 180.0 + triangleSize = int(round(length * 0.1)) + + painter.save() + + p0 = Qt.QPoint(center.x() + 1, center.y() + 1) + p1 = Qwt.qwtPolar2Pos(p0, length - 2 * triangleSize - 2, direction) + + pa = Qt.QPolygon([ + Qwt.qwtPolar2Pos(p1, 2 * triangleSize, direction), + Qwt.qwtPolar2Pos(p1, triangleSize, direction + math.pi/2), + Qwt.qwtPolar2Pos(p1, triangleSize, direction - math.pi/2), + ]) + + color = self.palette().color(cg, Qt.QPalette.Text) + painter.setBrush(color) + painter.drawPolygon(pa) + + painter.setPen(Qt.QPen(color, 3)) + painter.drawLine( + Qwt.qwtPolar2Pos(p0, length - 2, direction + math.pi/2), + Qwt.qwtPolar2Pos(p0, length - 2, direction - math.pi/2)) + + painter.restore() + + # draw() + +# class AttitudeIndicatorNeedle + + +class AttitudeIndicator(Qwt.QwtDial): + + def __init__(self, *args): + Qwt.QwtDial.__init__(self, *args) + self.__gradient = 0.0 + self.setMode(Qwt.QwtDial.RotateScale) + self.setWrapping(True) + self.setOrigin(270.0) + self.setScaleOptions(Qwt.QwtDial.ScaleTicks) + self.setScale(0, 0, 30.0) + self.setNeedle(AttitudeIndicatorNeedle( + self.palette().color(Qt.QPalette.Text))) + + # __init__() + + def angle(self): + return self.value() + + # angle() + + def setAngle(self, angle): + self.setValue(angle) + + # setAngle() + + def gradient(self): + return self.__gradient + + # gradient() + + def setGradient(self, gradient): + self.__gradient = gradient + + # setGradient() + + def keyPressEvent(self, event): + if event.key() == Qt.Qt.Key_Plus: + self.setGradient(self.gradient() + 0.05) + elif event.key() == Qt.Qt.Key_Minus: + self.setGradient(self.gradient() - 0.05) + else: + Qwt.QwtDial.keyPressEvent(self, event) + + # keyPressEvent() + + def drawScale(self, painter, center, radius, origin, minArc, maxArc): + dir = (360.0 - origin) * math.pi / 180.0 + offset = 4 + p0 = Qwt.qwtPolar2Pos(center, offset, dir + math.pi) + + w = self.contentsRect().width() + + # clip region to swallow 180 - 360 degrees + pa = [] + pa.append(Qwt.qwtPolar2Pos(p0, w, dir - math.pi/2)) + pa.append(Qwt.qwtPolar2Pos(pa[-1], 2 * w, dir + math.pi/2)) + pa.append(Qwt.qwtPolar2Pos(pa[-1], w, dir)) + pa.append(Qwt.qwtPolar2Pos(pa[-1], 2 * w, dir - math.pi/2)) + + painter.save() + painter.setClipRegion(Qt.QRegion(Qt.QPolygon(pa))) + Qwt.QwtDial.drawScale( + self, painter, center, radius, origin, minArc, maxArc) + painter.restore() + + # drawScale() + + def drawScaleContents(self, painter, center, radius): + dir = 360 - int(round(self.origin() - self.value())) + arc = 90 + int(round(self.gradient() * 90)) + skyColor = Qt.QColor(38, 151, 221) + painter.save() + painter.setBrush(skyColor) + painter.drawChord( + self.scaleContentsRect(), (dir - arc)*16, 2*arc*16) + painter.restore() + + # drawScaleContents() + +# class AttitudeIndicator \ No newline at end of file diff --git a/src/tools/uav_viewer_py/gui/cameraWidget.py b/src/tools/uav_viewer_py/gui/cameraWidget.py new file mode 100644 index 000000000..84af07f61 --- /dev/null +++ b/src/tools/uav_viewer_py/gui/cameraWidget.py @@ -0,0 +1,83 @@ +# +# Copyright (C) 1997-2015 JDE Developers Team +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see http://www.gnu.org/licenses/. +# Authors : +# Alberto Martin Florido +# +from PyQt5.QtCore import QSize, pyqtSignal +from PyQt5.QtGui import QImage, QPixmap +from PyQt5.QtWidgets import QPushButton, QWidget, QLabel + + +class CameraWidget(QWidget): + IMAGE_COLS_MAX = 640 + IMAGE_ROWS_MAX = 360 + LINX = 0.3 + LINY = 0.3 + LINZ = 0.8 + ANGZ = 1.0 + ANGY = 0.0 + ANGX = 0.0 + + imageUpdate = pyqtSignal() + + def __init__(self, winParent): + super(CameraWidget, self).__init__() + self.winParent = winParent + self.imageUpdate.connect(self.updateImage) + self.initUI() + + def initUI(self): + + self.setMinimumSize(680, 500) + self.setMaximumSize(680, 500) + + self.setWindowTitle("Camera") + changeCamButton = QPushButton("Change Camera") + changeCamButton.resize(170, 40) + changeCamButton.move(245, 450) + changeCamButton.setParent(self) + changeCamButton.clicked.connect(self.changeCamera) + + self.imgLabel = QLabel(self) + self.imgLabel.resize(640, 360) + self.imgLabel.move(10, 5) + self.imgLabel.show() + + def updateImage(self): + + img = self.winParent.getCamera().getImage() + if img is not None: + image = QImage(img.data, img.shape[1], img.shape[0], img.shape[1] * img.shape[2], QImage.Format_RGB888); + + if img.shape[1] == self.IMAGE_COLS_MAX: + x = 20 + else: + x = (self.IMAGE_COLS_MAX + 20) / 2 - (img.shape[1] / 2) + if img.shape[0] == self.IMAGE_ROWS_MAX: + y = 40 + else: + y = (self.IMAGE_ROWS_MAX + 40) / 2 - (img.shape[0] / 2) + + size = QSize(img.shape[1], img.shape[0]) + self.imgLabel.move(x, y) + self.imgLabel.resize(size) + self.imgLabel.setPixmap(QPixmap.fromImage(image)) + + def closeEvent(self, event): + self.winParent.closeCameraWidget() + + def changeCamera(self): + self.winParent.getExtra().toggleCam() diff --git a/src/tools/uav_viewer_py/gui/communicator.py b/src/tools/uav_viewer_py/gui/communicator.py new file mode 100644 index 000000000..ac7c1a026 --- /dev/null +++ b/src/tools/uav_viewer_py/gui/communicator.py @@ -0,0 +1,24 @@ +# +# Copyright (C) 1997-2015 JDE Developers Team +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see http://www.gnu.org/licenses/. +# Authors : +# Alberto Martin Florido +# +#!/usr/bin/python +# -*- coding: utf-8 -*- + +from PyQt5 import QtCore +class Communicator(QtCore.QObject): + updateBW = QtCore.pyqtSignal() diff --git a/src/tools/uav_viewer_py/gui/sensorsWidget.py b/src/tools/uav_viewer_py/gui/sensorsWidget.py new file mode 100644 index 000000000..31aa19e0e --- /dev/null +++ b/src/tools/uav_viewer_py/gui/sensorsWidget.py @@ -0,0 +1,270 @@ +# +# Copyright (C) 1997-2015 JDE Developers Team +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see http://www.gnu.org/licenses/. +# Authors : +# Alberto Martin Florido +# +from PyQt5.QtWidgets import QWidget, QLabel, QGridLayout, QVBoxLayout, QSpacerItem, QSizePolicy, QHBoxLayout, QProgressBar +from PyQt5 import QtCore +from PyQt5.QtCore import pyqtSignal, Qt +# from gui.speedoMeter import SpeedoMeter +# from gui.attitudeIndicator import AttitudeIndicator +from qfi import qfi_ADI, qfi_ALT, qfi_SI, qfi_HSI +import math + + +class SensorsWidget(QWidget): + sensorsUpdate = pyqtSignal() + + def __init__(self, winParent): + super(SensorsWidget, self).__init__() + self.winParent = winParent + self.sensorsUpdate.connect(self.updateSensors) + self.initUI() + + def initUI(self): + + self.mainLayout = QHBoxLayout() + self.indLayout = QGridLayout() + self.horizonLayout = QVBoxLayout() + self.horizonData = QGridLayout() + self.compassLayout = QVBoxLayout() + self.compassData = QGridLayout() + self.altLayout = QVBoxLayout() + self.altData = QGridLayout() + self.batteryLayout = QVBoxLayout() + self.batteryData = QGridLayout() + + self.setMinimumSize(750, 450) + self.setMaximumSize(750, 450) + + self.setWindowTitle("Sensors") + + self.pitchLabel = QLabel('Pitch:', self) + self.pitchValueLabel = QLabel('0', self) + self.pitchValueLabel.setAlignment(Qt.AlignRight | Qt.AlignTrailing | Qt.AlignVCenter) + + self.rollLabel = QLabel('Roll:', self) + self.rollValueLabel = QLabel('0', self) + self.rollValueLabel.setAlignment(Qt.AlignRight | Qt.AlignTrailing | Qt.AlignVCenter) + + self.yawLabel = QLabel('Yaw:', self) + self.yawValueLabel = QLabel('0', self) + self.yawValueLabel.setAlignment(Qt.AlignRight | Qt.AlignTrailing | Qt.AlignVCenter) + + self.altLabel = QLabel('Alt:', self) + self.altValueLabel = QLabel('0', self) + self.altValueLabel.setAlignment(Qt.AlignRight | Qt.AlignTrailing | Qt.AlignVCenter) + + self.pitchgLabel = QLabel("\272", self) + self.rollgLabel = QLabel("\272", self) + self.yawgLabel = QLabel("\272", self) + self.altmLabel = QLabel('m', self) + + hSpacer = QSpacerItem(100, 30, QSizePolicy.Ignored, QSizePolicy.Ignored) + + self.horizonData.addItem(hSpacer, 0, 0, 1, 1, Qt.AlignLeft) + self.horizonData.addWidget(self.pitchLabel, 0, 1, Qt.AlignCenter) + self.horizonData.addWidget(self.pitchValueLabel, 0, 2, Qt.AlignCenter) + self.horizonData.addWidget(self.pitchgLabel, 0, 3, Qt.AlignCenter) + self.horizonData.addWidget(self.rollLabel, 0, 4, Qt.AlignCenter) + self.horizonData.addWidget(self.rollValueLabel, 0, 5, Qt.AlignCenter) + self.horizonData.addWidget(self.rollgLabel, 0, 6, Qt.AlignCenter) + self.horizonData.addItem(hSpacer, 0, 7, 1, 1, Qt.AlignRight) + + self.compassData.addItem(hSpacer, 0, 0, 1, 1, Qt.AlignLeft) + self.compassData.addWidget(self.yawLabel, 0, 1, Qt.AlignCenter) + self.compassData.addWidget(self.yawValueLabel, 0, 2, Qt.AlignCenter) + self.compassData.addWidget(self.yawgLabel, 0, 3, Qt.AlignCenter) + self.compassData.addItem(hSpacer, 0, 4, 1, 1, Qt.AlignRight) + + self.altData.addItem(hSpacer, 0, 0, 1, 1, Qt.AlignLeft) + self.altData.addWidget(self.altLabel, 0, 1, Qt.AlignCenter) + self.altData.addWidget(self.altValueLabel, 0, 2, Qt.AlignCenter) + self.altData.addWidget(self.altmLabel, 0, 3, Qt.AlignCenter) + self.altData.addItem(hSpacer, 0, 4, 1, 1, Qt.AlignLeft) + + self.altd = qfi_ALT.qfi_ALT(self) + + self.altd.setFixedSize(QtCore.QSize(200,200)) + self.altLayout.addWidget(self.altd) + self.altLayout.addLayout(self.altData) + # self.altd.move(420,50) + + self.compass = qfi_HSI.qfi_HSI(self) + self.compass.setFixedSize(QtCore.QSize(200, 200)) + self.compassLayout.addWidget(self.compass) + self.compassLayout.addLayout(self.compassData) + + self.horizon = qfi_ADI.qfi_ADI(self) + self.horizon.setFixedSize(QtCore.QSize(200, 200)) + self.horizonLayout.addWidget(self.horizon) + self.horizonLayout.addLayout(self.horizonData) + + + self.velLinX = qfi_SI.qfi_SI(self) + self.velLinX.setFixedSize(QtCore.QSize(150, 150)) + # self.velLinX.move(60,270) + self.velXLabel = QLabel('Linear X (m/s)', self) + # self.velXLabel.move(95,420) + + + self.velLinY = qfi_SI.qfi_SI(self) + self.velLinY.setFixedSize(QtCore.QSize(150, 150)) + # self.velLinY.move(240,270) + self.velYLabel = QLabel('Linear Y (m/s)', self) + # self.velYLabel.move(275,420) + + self.velLinZ = qfi_SI.qfi_SI(self) + self.velLinZ.setFixedSize(QtCore.QSize(150, 150)) + # self.velLinZ.setLabel("8 m/s") + # self.velLinZ.move(420,270) + self.velZLabel = QLabel('Linear Z (m/s)', self) + # self.velZLabel.move(455,420) + + self.indLayout.addLayout(self.horizonLayout, 0, 0, Qt.AlignCenter) + self.indLayout.addLayout(self.compassLayout, 0, 1, Qt.AlignCenter) + self.indLayout.addLayout(self.altLayout, 0, 2, Qt.AlignCenter) + self.indLayout.addWidget(self.velLinX, 1, 0, Qt.AlignCenter) + self.indLayout.addWidget(self.velLinY, 1, 1, Qt.AlignCenter) + self.indLayout.addWidget(self.velLinZ, 1, 2, Qt.AlignCenter) + self.indLayout.addWidget(self.velXLabel, 2, 0, Qt.AlignCenter) + self.indLayout.addWidget(self.velYLabel, 2, 1, Qt.AlignCenter) + self.indLayout.addWidget(self.velZLabel, 2, 2, Qt.AlignCenter) + + self.battery=QProgressBar(self) + self.battery.setValue(0) + self.battery.resize(56,241) + self.battery.setOrientation(Qt.Vertical) + self.battery.setTextVisible(False) + + self.batteryLabel=QLabel('Battery (%)',self) + self.batteryValueLabel = QLabel('0', self) + self.batteryValueLabel.setAlignment(Qt.AlignRight | Qt.AlignTrailing | Qt.AlignVCenter) + + self.batteryData.addItem(hSpacer, 0, 0, 1, 1, Qt.AlignLeft) + self.batteryData.addWidget(self.batteryLabel, 0, 1, Qt.AlignCenter) + self.batteryData.addWidget(self.batteryValueLabel, 0, 2, Qt.AlignCenter) + self.batteryData.addItem(hSpacer, 0, 4, 1, 1, Qt.AlignLeft) + + self.batteryLayout.addWidget(self.battery, 0, Qt.AlignHCenter) + self.batteryLayout.addLayout(self.batteryData) + + self.mainLayout.addLayout(self.indLayout) + self.mainLayout.addLayout(self.batteryLayout) + self.setLayout(self.mainLayout); + + def updateSensors(self): + pose = self.winParent.getPose3D().getPose3D() + + if pose != None: + qw = pose.q0 + qx = pose.q1 + qy = pose.q2 + qz = pose.q3 + self.drawAltd(pose.z) + self.drawYawValues(self.quatToYaw(qw, qx, qy, qz) * 180 / math.pi) + self.drawPitchRollValues(self.quatToPitch(qw, qx, qy, qz) * 180 / math.pi, + self.quatToRoll(qw, qx, qy, qz) * 180 / math.pi) + + navdata = self.winParent.getNavData().getNavdata() + + if navdata != None: + self.battery.setValue(navdata.batteryPercent) + self.batteryValueLabel.setText(str(navdata.batteryPercent)) + self.drawVelocities(navdata.vx, navdata.vy, navdata.vz) + + def drawYawValues(self, degress): + value = "{0:.2f}".format(degress) + self.yawValueLabel.setText(value) + self.compass.setHeading(degress) + self.compass.viewUpdate.emit() + + def drawAltd(self, meters): + + self.altd.setAltitude(meters * 10) + self.altd.viewUpdate.emit() + + value = "{0:.0f}".format(meters) + self.altValueLabel.setText(value) + + def drawPitchRollValues(self, pitch, roll): + if (pitch > 0 and pitch <= 90): + result = pitch / 90 + result = -result + elif (pitch < 0 and pitch >= -90): + result = pitch / -90 + else: + result = 0.0 + + self.horizon.setPitch(pitch) + self.horizon.setRoll(-roll) + self.horizon.viewUpdate.emit() + pitchValue = "{0:.2f}".format(pitch) + rollValue = "{0:.2f}".format(roll) + self.pitchValueLabel.setText(pitchValue) + self.rollValueLabel.setText(rollValue) + + def drawVelocities(self, vx, vy, vz): + + vx = math.fabs(vx) + vx /= 1000.0 + self.velLinX.setSpeed(vx) + self.velLinX.viewUpdate.emit() + vx = math.fabs(vx) + + vy = math.fabs(vy) + vy /= 1000.0 + self.velLinY.setSpeed(vy) + self.velLinY.viewUpdate.emit() + + vz = math.fabs(vz) + vz /= 1000.0 + self.velLinZ.setSpeed(vz) + self.velLinZ.viewUpdate.emit() + + def quatToRoll(self, qw, qx, qy, qz): + rotateXa0 = 2.0 * (qy * qz + qw * qx) + rotateXa1 = qw * qw - qx * qx - qy * qy + qz * qz + rotateX = 0.0 + + if (rotateXa0 != 0.0 and rotateXa1 != 0.0): + rotateX = math.atan2(rotateXa0, rotateXa1) + + return rotateX + + def quatToPitch(self, qw, qx, qy, qz): + rotateYa0 = -2.0 * (qx * qz - qw * qy) + rotateY = 0.0 + if (rotateYa0 >= 1.0): + rotateY = math.pi / 2.0 + elif (rotateYa0 <= -1.0): + rotateY = -math.pi / 2.0 + else: + rotateY = math.asin(rotateYa0) + + return rotateY + + def quatToYaw(self, qw, qx, qy, qz): + rotateZa0 = 2.0 * (qx * qy + qw * qz) + rotateZa1 = qw * qw + qx * qx - qy * qy - qz * qz + rotateZ = 0.0 + if (rotateZa0 != 0.0 and rotateZa1 != 0.0): + rotateZ = math.atan2(rotateZa0, rotateZa1) + + return rotateZ + + def closeEvent(self, event): + self.winParent.closeSensorsWidget() diff --git a/src/tools/uav_viewer_py/gui/teleopWidget.py b/src/tools/uav_viewer_py/gui/teleopWidget.py new file mode 100644 index 000000000..93c7ea405 --- /dev/null +++ b/src/tools/uav_viewer_py/gui/teleopWidget.py @@ -0,0 +1,113 @@ +# +# Copyright (C) 1997-2015 JDE Developers Team +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see http://www.gnu.org/licenses/. +# Authors : +# Alberto Martin Florido +# +import resources_rc +from PyQt5.QtGui import QImage, QPainter, QPen +from PyQt5.QtCore import pyqtSignal, QPointF, Qt, QPoint +from PyQt5.QtWidgets import QWidget, QGridLayout + + +class TeleopWidget(QWidget): + stopSIG = pyqtSignal() + + def __init__(self, winParent): + super(TeleopWidget, self).__init__() + self.winParent = winParent + self.line = QPointF(0, 0); + self.qimage = QImage() + self.qimage.load(':images/ball.png') + self.stopSIG.connect(self.stop) + self.initUI() + + def initUI(self): + layout = QGridLayout() + self.setLayout(layout) + self.setAutoFillBackground(True) + p = self.palette() + p.setColor(self.backgroundRole(), Qt.black) + self.setPalette(p) + self.resize(300, 300) + self.setMinimumSize(300, 300) + + def stop(self): + self.line = QPointF(0, 0); + self.repaint(); + + def mouseMoveEvent(self, e): + if e.buttons() == Qt.LeftButton: + x = e.x() - self.width() / 2 + y = e.y() - self.height() / 2 + self.line = QPointF(x, y) + self.repaint() + + def paintEvent(self, e): + _width = self.width() + _height = self.height() + + width = 2 + + painter = QPainter(self) + + pen = QPen(Qt.blue, width) + painter.setPen(pen) + + # Centro del widget + painter.translate(QPoint(_width / 2, _height / 2)) + + # eje + painter.drawLine(QPointF(-_width, 0), + QPointF(_width, 0)) + + painter.drawLine(QPointF(0, -_height), + QPointF(0, _height)) + + # con el raton + pen = QPen(Qt.red, width) + painter.setPen(pen) + + # Comprobamos que el raton este dentro de los limites + if abs(self.line.x() * 2) >= self.size().width(): + if self.line.x() >= 0: + self.line.setX(self.size().width() / 2) + elif self.line.x() < 0: + self.line.setX((-self.size().width() / 2) + 1) + + if abs(self.line.y() * 2) >= self.size().height(): + if self.line.y() >= 0: + self.line.setY(self.size().height() / 2) + elif self.line.y() < 0: + self.line.setY((-self.size().height() / 2) + 1) + + painter.drawLine(QPointF(self.line.x(), -_height), + QPointF(self.line.x(), _height)) + + painter.drawLine(QPointF(-_width, self.line.y()), + QPointF(_width, self.line.y())) + + # print "x: %f y: %f" % (self.line.x(), self.line.y()) + + v_normalized = (1.0 / (self.size().height() / 2)) * self.line.y() + v_normalized = float("{0:.2f}".format(v_normalized)) + w_normalized = (1.0 / (self.size().width() / 2)) * self.line.x() + w_normalized = float("{0:.2f}".format(w_normalized)) + + # print "v: %f w: %f" % (v_normalized,w_normalized) + self.winParent.setXYValues(w_normalized, v_normalized) + painter.drawImage(self.line.x() - self.qimage.width() / 2, self.line.y() - self.qimage.height() / 2, + self.qimage); + diff --git a/src/tools/uav_viewer_py/gui/threadGUI.py b/src/tools/uav_viewer_py/gui/threadGUI.py new file mode 100644 index 000000000..1c6e80781 --- /dev/null +++ b/src/tools/uav_viewer_py/gui/threadGUI.py @@ -0,0 +1,43 @@ +# +# Copyright (C) 1997-2015 JDE Developers Team +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see http://www.gnu.org/licenses/. +# Authors : +# Alberto Martin Florido +# +import threading, time +from datetime import datetime + +time_cycle = 50; + +class ThreadGUI(threading.Thread): + def __init__(self, gui): + self.gui = gui + + threading.Thread.__init__(self) + + def run(self): + + while(True): + + start_time = datetime.now() + self.gui.updGUI.emit() + + finish_Time = datetime.now() + + dt = finish_Time - start_time + ms = (dt.days * 24 * 60 * 60 + dt.seconds) * 1000 + dt.microseconds / 1000.0 + + if(ms < time_cycle): + time.sleep((time_cycle-ms) / 1000.0); \ No newline at end of file diff --git a/src/tools/uav_viewer_py/gui/ui_gui.py b/src/tools/uav_viewer_py/gui/ui_gui.py new file mode 100644 index 000000000..e623fb963 --- /dev/null +++ b/src/tools/uav_viewer_py/gui/ui_gui.py @@ -0,0 +1,107 @@ +# -*- coding: utf-8 -*- + +# Form implementation generated from reading ui file 'ui_gui.ui' +# +# Created by: PyQt5 UI code generator 5.5.1 +# +# WARNING! All changes made in this file will be lost! + +from PyQt5 import QtCore, QtGui, QtWidgets + +class Ui_MainWindow(object): + def setupUi(self, MainWindow): + MainWindow.setObjectName("MainWindow") + MainWindow.resize(660, 370) + MainWindow.setMinimumSize(QtCore.QSize(660, 370)) + MainWindow.setMaximumSize(QtCore.QSize(660, 370)) + self.centralwidget = QtWidgets.QWidget(MainWindow) + self.centralwidget.setObjectName("centralwidget") + self.takeoffButton = QtWidgets.QPushButton(self.centralwidget) + self.takeoffButton.setGeometry(QtCore.QRect(470, 30, 161, 41)) + self.takeoffButton.setObjectName("takeoffButton") + self.altdSlider = QtWidgets.QSlider(self.centralwidget) + self.altdSlider.setGeometry(QtCore.QRect(400, 30, 19, 311)) + self.altdSlider.setMaximum(100) + self.altdSlider.setProperty("value", 49) + self.altdSlider.setOrientation(QtCore.Qt.Vertical) + self.altdSlider.setObjectName("altdSlider") + self.stopButton = QtWidgets.QPushButton(self.centralwidget) + self.stopButton.setGeometry(QtCore.QRect(470, 80, 161, 41)) + icon = QtGui.QIcon() + icon.addPixmap(QtGui.QPixmap(":/images/stop.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off) + self.stopButton.setIcon(icon) + self.stopButton.setObjectName("stopButton") + self.windowsLabel = QtWidgets.QLabel(self.centralwidget) + self.windowsLabel.setGeometry(QtCore.QRect(540, 190, 71, 21)) + self.windowsLabel.setObjectName("windowsLabel") + self.cameraCheck = QtWidgets.QCheckBox(self.centralwidget) + self.cameraCheck.setGeometry(QtCore.QRect(540, 220, 94, 26)) + self.cameraCheck.setObjectName("cameraCheck") + self.sensorsCheck = QtWidgets.QCheckBox(self.centralwidget) + self.sensorsCheck.setGeometry(QtCore.QRect(540, 250, 94, 26)) + self.sensorsCheck.setObjectName("sensorsCheck") + self.altdLabel = QtWidgets.QLabel(self.centralwidget) + self.altdLabel.setGeometry(QtCore.QRect(390, 340, 51, 21)) + self.altdLabel.setObjectName("altdLabel") + self.verticalLayoutWidget = QtWidgets.QWidget(self.centralwidget) + self.verticalLayoutWidget.setGeometry(QtCore.QRect(20, 30, 361, 301)) + self.verticalLayoutWidget.setObjectName("verticalLayoutWidget") + self.tlLayout = QtWidgets.QVBoxLayout(self.verticalLayoutWidget) + self.tlLayout.setObjectName("tlLayout") + self.rotationDial = QtWidgets.QDial(self.centralwidget) + self.rotationDial.setGeometry(QtCore.QRect(440, 220, 50, 64)) + self.rotationDial.setMaximum(100) + self.rotationDial.setProperty("value", 49) + self.rotationDial.setObjectName("rotationDial") + self.rotationLabel = QtWidgets.QLabel(self.centralwidget) + self.rotationLabel.setGeometry(QtCore.QRect(440, 280, 65, 21)) + self.rotationLabel.setObjectName("rotationLabel") + self.XLabel = QtWidgets.QLabel(self.centralwidget) + self.XLabel.setGeometry(QtCore.QRect(20, 340, 21, 21)) + self.XLabel.setObjectName("XLabel") + self.YLabel = QtWidgets.QLabel(self.centralwidget) + self.YLabel.setGeometry(QtCore.QRect(130, 340, 21, 21)) + self.YLabel.setObjectName("YLabel") + self.XValue = QtWidgets.QLabel(self.centralwidget) + self.XValue.setGeometry(QtCore.QRect(40, 340, 41, 21)) + self.XValue.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter) + self.XValue.setObjectName("XValue") + self.YValue = QtWidgets.QLabel(self.centralwidget) + self.YValue.setGeometry(QtCore.QRect(150, 340, 41, 21)) + self.YValue.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter) + self.YValue.setObjectName("YValue") + self.altdValue = QtWidgets.QLabel(self.centralwidget) + self.altdValue.setGeometry(QtCore.QRect(390, 10, 41, 21)) + self.altdValue.setAlignment(QtCore.Qt.AlignCenter) + self.altdValue.setObjectName("altdValue") + self.rotValue = QtWidgets.QLabel(self.centralwidget) + self.rotValue.setGeometry(QtCore.QRect(445, 200, 41, 21)) + self.rotValue.setAlignment(QtCore.Qt.AlignCenter) + self.rotValue.setObjectName("rotValue") + self.resetButton = QtWidgets.QPushButton(self.centralwidget) + self.resetButton.setGeometry(QtCore.QRect(470, 130, 161, 41)) + self.resetButton.setObjectName("resetButton") + MainWindow.setCentralWidget(self.centralwidget) + + self.retranslateUi(MainWindow) + QtCore.QMetaObject.connectSlotsByName(MainWindow) + + def retranslateUi(self, MainWindow): + _translate = QtCore.QCoreApplication.translate + MainWindow.setWindowTitle(_translate("MainWindow", "Uav Viewer")) + self.takeoffButton.setText(_translate("MainWindow", "Take off")) + self.stopButton.setText(_translate("MainWindow", "Stop")) + self.windowsLabel.setText(_translate("MainWindow", "Windows:")) + self.cameraCheck.setText(_translate("MainWindow", "Camera")) + self.sensorsCheck.setText(_translate("MainWindow", "Sensors")) + self.altdLabel.setText(_translate("MainWindow", "Altitude")) + self.rotationLabel.setText(_translate("MainWindow", "Rotation")) + self.XLabel.setText(_translate("MainWindow", "X:")) + self.YLabel.setText(_translate("MainWindow", "Y:")) + self.XValue.setText(_translate("MainWindow", "0")) + self.YValue.setText(_translate("MainWindow", "0")) + self.altdValue.setText(_translate("MainWindow", "0")) + self.rotValue.setText(_translate("MainWindow", "0")) + self.resetButton.setText(_translate("MainWindow", "Reset")) + +import resources_rc diff --git a/src/tools/uav_viewer_py/gui/ui_gui.ui b/src/tools/uav_viewer_py/gui/ui_gui.ui new file mode 100644 index 000000000..c515e08a9 --- /dev/null +++ b/src/tools/uav_viewer_py/gui/ui_gui.ui @@ -0,0 +1,279 @@ + + + MainWindow + + + + 0 + 0 + 660 + 370 + + + + + 660 + 370 + + + + + 660 + 370 + + + + Introrob_py + + + + + + 470 + 30 + 161 + 41 + + + + Take off + + + + + + 400 + 30 + 19 + 311 + + + + 100 + + + 49 + + + Qt::Vertical + + + + + + 470 + 80 + 161 + 41 + + + + Stop + + + + :/images/stop.png:/images/stop.png + + + + + + 540 + 190 + 71 + 21 + + + + Windows: + + + + + + 540 + 220 + 94 + 26 + + + + Camera + + + + + + 540 + 250 + 94 + 26 + + + + Sensors + + + + + + 390 + 340 + 51 + 21 + + + + Altitude + + + + + + 20 + 30 + 361 + 301 + + + + + + + + 440 + 220 + 50 + 64 + + + + 100 + + + 49 + + + + + + 440 + 280 + 65 + 21 + + + + Rotation + + + + + + 20 + 340 + 21 + 21 + + + + X: + + + + + + 130 + 340 + 21 + 21 + + + + Y: + + + + + + 40 + 340 + 41 + 21 + + + + 0 + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + 150 + 340 + 41 + 21 + + + + 0 + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + 390 + 10 + 41 + 21 + + + + 0 + + + Qt::AlignCenter + + + + + + 445 + 200 + 41 + 21 + + + + 0 + + + Qt::AlignCenter + + + + + + 470 + 130 + 161 + 41 + + + + Reset + + + + + + + + + diff --git a/src/tools/uav_viewer_py/resources/__init__.py b/src/tools/uav_viewer_py/resources/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/src/tools/uav_viewer_py/resources/ball.png b/src/tools/uav_viewer_py/resources/ball.png new file mode 100644 index 0000000000000000000000000000000000000000..7fbb31af939a79be26b875073570e302e17909d8 GIT binary patch literal 1869 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 QgZ~4K4J`EU>AFS#2gGzHq5uE@ literal 0 HcmV?d00001 diff --git a/src/tools/uav_viewer_py/resources/resources.qrc b/src/tools/uav_viewer_py/resources/resources.qrc new file mode 100644 index 000000000..5f475862c --- /dev/null +++ b/src/tools/uav_viewer_py/resources/resources.qrc @@ -0,0 +1,7 @@ + + + ball.png + play.png + stop.png + + diff --git a/src/tools/uav_viewer_py/resources/stop.png b/src/tools/uav_viewer_py/resources/stop.png new file mode 100644 index 0000000000000000000000000000000000000000..2ac1754ebfb30e1092b98f73645e70923f85f8ce GIT binary patch literal 4817 zcmb7Ic{r5q+aAUqg^=xKOtuO$mdG&H(V{3zmXge73}!JiGReLxC8bD0c10*ji6oIF zdy9PuV_(aX?KgPq_rCAr)V0}FvT{?^jK)}+FAUeUF zNQKei;B8zO^L^_x1Ps~^p}E1qhFgRnJF_z&V-f`m(oj)VMnl!LKpL7VYDd&GwKbJM zW+WoWkW2wV)j(=cs1``|2uxiQc32bi=LOc|0qIgO&M-^F(LWiPBRJTFMkB)@5HBw; z6)$xa5(NiQ)z;RAK-C~>YRb$AW$I-j4M|rfQul8`>|h|UR5S%ors0{STbM``$&&^L zgPHT7U&}#~E%7)P8ud2?k@}}`Oba1&BpITr0)`&LMupT?39TVO0dZ*p-m#`F&wHK0tA$d{V@VbyKJDD>(S=*yuX}XYqpl-2X{#%K^ zFd4ej{?4>j6qq}bh=YUa${4IO($k#=)|*Lt12RKmupnj`k<2%=jQoDOq0aUVOGmqV zQkn8V&Lj%Rih!iL;E6b95Dw{$Cu09i=r7i7q5l-NC6?-ma>b(ml(xMYjgYv%3)RD; zNkr=3(SO_J?2f!d@}&GPv(#T23ID3`H_ERngJ>ksPM!Y_xHI~tYu_p=P?tgfqx7JEQ}H)T|I4})`j=D4l(O@UdAgas1@gPOFbBWe6PC#ANfc)D znP#(SWi~D`nu#6FisVdVZa*wwr^lRNnuoQ-A~BaeYO%V^hM0T;VQ5Y7M6GRZ>H{?^ zSkc`-D4R`Hm6fwIP0}301RXIkLzqF(WDqNFgQO&{zyF%k^2!DfxcB&v{4e=p<*)r~ z`28kj-(3Xp4F<~@blSh!bh67;te@pP9=sd{t>Hz=N~Bm3J{%pHC}wb&ENUb z?t+Txe0}|lnXdXKp}XkzgSJ8-Lhi zgG(Ixuf<%eDl5uG{eHHSgJo>5ilazz1B=m{ocRnO5FE*p&Jq>~5SsLMUrRmN!7fL9Hpk-$Tyg#2 z3SezE+fd;HT1fiW4u>1Io0_l-TO8BjRt`FT;17p2V4{Pog2ICb-ICqxS~b%|!E0_j zyWi&!!Bes(*|)kOeLZ*5IK+?Kx}rbdTh_Fc`SkuG{j%{wL9hO<;a13_ksy6IhgEY!i!6>@ z`i#9Y>kMDVYr=s)cmwupZX3NK|@p0Cab{Vs?-G1

p9juth-(W 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 kxm|0<_j{Xpx$}FjU5=gQ)pZWr`X}myi8 +# Aitor Martinez Fernandez +# + +import sys +import easyiceconfig as EasyIce +from gui.threadGUI import ThreadGUI +from parallelIce.cameraClient import CameraClient +from parallelIce.navDataClient import NavDataClient +from parallelIce.cmdvel import CMDVel +from parallelIce.extra import Extra +from parallelIce.pose3dClient import Pose3DClient +from gui.GUI import MainWindow +from PyQt5.QtWidgets import QApplication + +import signal + +signal.signal(signal.SIGINT, signal.SIG_DFL) + +if __name__ == '__main__': + ic = EasyIce.initialize(sys.argv) + camera = CameraClient(ic, "UavViewer.Camera", True) + navdata = NavDataClient(ic, "UavViewer.Navdata", True) + pose = Pose3DClient(ic, "UavViewer.Pose3D", True) + cmdvel = CMDVel(ic, "UavViewer.CMDVel") + extra = Extra(ic, "UavViewer.Extra") + + + app = QApplication(sys.argv) + frame = MainWindow() + frame.setCamera(camera) + frame.setNavData(navdata) + frame.setPose3D(pose) + frame.setCMDVel(cmdvel) + frame.setExtra(extra) + frame.show() + + + + t2 = ThreadGUI(frame) + t2.daemon=True + t2.start() + + sys.exit(app.exec_()) diff --git a/src/tools/uav_viewer_py/uav_viewer_py.cfg b/src/tools/uav_viewer_py/uav_viewer_py.cfg new file mode 100644 index 000000000..f94437f21 --- /dev/null +++ b/src/tools/uav_viewer_py/uav_viewer_py.cfg @@ -0,0 +1,6 @@ +UavViewer.Camera.Proxy = ardrone_camera:default -h 0.0.0.0 -p 9999 +UavViewer.Pose3D.Proxy = ardrone_pose3d:default -h 0.0.0.0 -p 9998 +UavViewer.CMDVel.Proxy = ardrone_cmdvel:default -h 0.0.0.0 -p 9995 +UavViewer.Navdata.Proxy = ardrone_navdata:default -h 0.0.0.0 -p 9996 +UavViewer.Extra.Proxy = ardrone_extra:default -h 0.0.0.0 -p 9994 + From d770563d4340ece21266fe01414e18a1cc58f84c Mon Sep 17 00:00:00 2001 From: Aitor Martinez Date: Fri, 18 Nov 2016 11:10:08 +0100 Subject: [PATCH 2/2] [issue #614] solved bug in install clause --- src/tools/uav_viewer_py/CMakeLists.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/tools/uav_viewer_py/CMakeLists.txt b/src/tools/uav_viewer_py/CMakeLists.txt index b628d8e1c..784cd15df 100644 --- a/src/tools/uav_viewer_py/CMakeLists.txt +++ b/src/tools/uav_viewer_py/CMakeLists.txt @@ -5,10 +5,10 @@ # Install .py FILE(GLOB_RECURSE HEADERS_FILES ${CMAKE_CURRENT_SOURCE_DIR}/*py) FOREACH(header ${HEADERS_FILES}) - INSTALL(FILES ${header} DESTINATION share/jderobot/python/colorTuner_py/ COMPONENT tools) + INSTALL(FILES ${header} DESTINATION share/jderobot/python/uav_viewer_py/ COMPONENT tools) ENDFOREACH(header) # Install gui -INSTALL (DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/gui DESTINATION share/jderobot/python/colorTuner_py COMPONENT tools PATTERN .svn EXCLUDE) +INSTALL (DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/gui DESTINATION share/jderobot/python/uav_viewer_py COMPONENT tools PATTERN .svn EXCLUDE)