import argparse
from PyQt5 import QtCore
-from PyQt5.QtCore import (pyqtSlot, QTimer, QRect, QPoint, Qt)
+from PyQt5.QtCore import (pyqtSlot, QTimer, QRect, QPoint, Qt, QByteArray,
+ QSizeF, QRectF)
from PyQt5.QtGui import (QPainter, QColor, QPen, QBrush, QLinearGradient, QFont,
QPainterPath, QPolygonF)
-from PyQt5.QtWidgets import (QApplication, QMainWindow, QWidget)
+from PyQt5.QtWidgets import (QApplication, QMainWindow, QWidget, QAction,
+ QActionGroup, QGraphicsScene, QGraphicsView)
+from PyQt5.QtMultimediaWidgets import (QGraphicsVideoItem)
+from PyQt5.QtMultimedia import (QCamera, QAbstractVideoSurface,
+ QAbstractVideoBuffer)
+
import serialfpv
import qtosd_ui
return QApplication.translate(context, text, disambig)
class OSDWidget(QWidget):
- def __init__(self, roundWidget = False, filename = None):
+ def __init__(self, mode = "camera", filename = None):
super(OSDWidget, self).__init__()
# init parameters
- self.roundWidget = roundWidget
+ self.mode = mode
self.fpv = None
# parameters that will be modified by the user
self.user_pitch = 0
self.adjdev = None
self.setMinimumSize(250, 250)
+ self.setStyleSheet("background-color: transparent;")
+
# how many degrees between pitch reference lines
- if roundWidget:
+ if mode == "round":
self.CAM_ANGLE = 90.
self.PITCH_REFLINES_STEP_ANGLE = 10.
self.PITCH_REFLINES_BOLD_STEP_ANGLE = 30.
def draw(self, painter):
"""Draw the widget."""
- if self.roundWidget:
+ if self.mode == "round":
self.drawHorizonRound(painter)
- else:
+ elif self.mode == "rectangle":
self.drawHorizon(painter)
self.drawPitchGraduation(painter)
self.drawRollGraduation(painter)
self.drawYaw(painter)
self.drawCenterRef(painter)
- if self.roundWidget == False:
+ if self.mode != "round":
self.drawSpeed(painter)
self.drawAlt(painter)
self.drawReturnToHome(painter)
def getCenterRadius(self):
"""Return the radius of the widget circle"""
- if self.roundWidget:
+ if self.mode == "round":
return self.adjdev.width() / 2.
else:
return self.adjdev.width() / 3.5
font = QFont("Meiryo UI", 0, QFont.Bold)
font.setPointSizeF(self.FONT_SIZE * r)
painter.setFont(font)
- if self.roundWidget == True:
+ if self.mode == "round":
y_txt = center.y() + r * 0.6
y1 = center.y() + r * 0.7
y2 = center.y() + r * 0.8
"""set the left text"""
self.right_txt = txt
+class OSDGraphicsView(QGraphicsView):
+ def __init__(self, parent=None):
+ super(OSDGraphicsView, self).__init__(parent)
+ self.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
+ self.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
+ self.setMinimumSize(200, 150)
+ def resizeEvent(self, event):
+ # use item 0 to resize automatically
+ self.fitInView(self.items()[0], Qt.KeepAspectRatio)
+ super(OSDGraphicsView, self).resizeEvent(event)
+
class Ui_OSD(QMainWindow):
- def __init__(self, parent = None, roundWidget = False, filename = None):
+ def __init__(self, parent = None, mode = "camera", filename = None):
super(Ui_OSD, self).__init__(parent)
self.ui = qtosd_ui.Ui_MainWindow()
self.ui.setupUi(self)
- self.roundWidget = roundWidget
+ self.mode = mode
self.filename = filename
- self.osd = OSDWidget(roundWidget = self.roundWidget,
+ self.osd = OSDWidget(mode = self.mode,
filename = self.filename)
- self.ui.gridLayout.addWidget(self.osd, 0, 1)
+ self.osd.setObjectName("osd")
self.ui.pitchSlider.valueChanged[int].connect(self.changePitch)
self.ui.rollSlider.valueChanged[int].connect(self.changeRoll)
self.ui.yawSlider.valueChanged[int].connect(self.changeYaw)
self.ui.actionExit.triggered.connect(self.close)
+ self.scene = QGraphicsScene(self)
+ self.graphicsView = OSDGraphicsView(self.scene)
+
+ if self.mode == "camera":
+ self.videoItem = QGraphicsVideoItem()
+ self.videoItem.setSize(QSizeF(640, 480)) # XXX
+ self.scene.addItem(self.videoItem)
+
+ x = self.videoItem.boundingRect().width() / 2.0
+ y = self.videoItem.boundingRect().height() / 2.0
+ #self.videoItem.setTransform(
+ # QTransform().translate(x, y).rotate(70).translate(-x, -y))
+
+ self.initCamera()
+
+ self.scene.addWidget(self.osd)
+ self.ui.gridLayout.addWidget(self.graphicsView, 0, 1)
+
+ def initCamera(self):
+ # find camera devices and add them in the menu
+ cameraDevice = QByteArray()
+ videoDevicesGroup = QActionGroup(self)
+ videoDevicesGroup.setExclusive(True)
+ for deviceName in QCamera.availableDevices():
+ description = QCamera.deviceDescription(deviceName)
+ videoDeviceAction = QAction(description, videoDevicesGroup)
+ videoDeviceAction.setCheckable(True)
+ videoDeviceAction.setData(deviceName)
+ if cameraDevice.isEmpty():
+ cameraDevice = deviceName
+ videoDeviceAction.setChecked(True)
+ self.ui.menuDevices.addAction(videoDeviceAction)
+ videoDevicesGroup.triggered.connect(self.updateCameraDevice)
+ # select the first camera
+ self.setCamera(cameraDevice)
+
+ def setCamera(self, cameraDevice):
+ if cameraDevice.isEmpty():
+ self.camera = QCamera()
+ else:
+ self.camera = QCamera(cameraDevice)
+
+ self.camera.stateChanged.connect(self.updateCameraState)
+ self.camera.error.connect(self.displayCameraError)
+
+ #self.ui.exposureCompensation.valueChanged.connect(
+ # self.setExposureCompensation)
+
+ self.camera.setCaptureMode(QCamera.CaptureViewfinder)
+ self.camera.setViewfinder(self.videoItem)
+ self.updateCameraState(self.camera.state())
+ self.camera.start()
+
+ #XXX stop camera? remove from scene?
+
+ def updateCameraDevice(self, action):
+ print "updateCameraDevice"
+ self.setCamera(action.data())
+
+ def updateCameraState(self, state):
+ print "updateCameraState %s"%(str(state))
+
+ def displayCameraError(self):
+ print "displayCameraError"
+ QMessageBox.warning(self, "Camera error", self.camera.errorString())
+
def keyPressEvent(self, event):
- if event.key() == Qt.Key_J:
+ key = event.key()
+ if key == Qt.Key_J:
self.osd.setRoll(self.osd.user_roll + 2)
event.accept()
- elif event.key() == Qt.Key_L:
+ elif key == Qt.Key_L:
self.osd.setRoll(self.osd.user_roll - 2)
event.accept()
- elif event.key() == Qt.Key_I:
+ elif key == Qt.Key_I:
self.osd.setPitch(self.osd.user_pitch + 2)
event.accept()
- elif event.key() == Qt.Key_K:
+ elif key == Qt.Key_K:
self.osd.setPitch(self.osd.user_pitch - 2)
event.accept()
- elif event.key() == Qt.Key_Q:
+ elif key == Qt.Key_Q:
self.close()
+ elif not event.isAutoRepeat():
+ if key == Qt.Key_CameraFocus:
+ self.camera.searchAndLock()
+ event.accept()
+ else:
+ super(Ui_OSD, self).keyPressEvent(event)
def keyReleaseEvent(self, event):
- return
+ key = event.key()
+ if event.isAutoRepeat():
+ return
+ if key == Qt.Key_CameraFocus:
+ self.camera.unlock()
+ else:
+ super(Ui_OSD, self).keyReleaseEvent(event)
def retranslateUi(self, MainWindow):
MainWindow.setWindowTitle(_translate("MainWindow", "MainWindow", None))
import sys
parser = argparse.ArgumentParser(description='OSD written in Qt.')
- parser.add_argument('--round', '-r', action="store_true",
+ parser.add_argument('--mode', '-m', action="store",
+ choices=["round", "rectangle", "camera"],
help='display the widget as a round attitude meter')
parser.add_argument('--filename', '-f',
help='specify the log file')
args = parser.parse_args()
app = QApplication(sys.argv)
- ui = Ui_OSD(filename = args.filename, roundWidget = args.round)
+ ui = Ui_OSD(filename = args.filename, mode = args.mode)
ui.show()
sys.exit(app.exec_())