early support of camera
authorOlivier Matz <zer0@droids-corp.org>
Sun, 7 Jun 2015 18:14:27 +0000 (20:14 +0200)
committerOlivier Matz <zer0@droids-corp.org>
Sun, 7 Jun 2015 19:02:19 +0000 (21:02 +0200)
qtosd/qtosd.py

index 2d4583b..449f243 100644 (file)
@@ -24,10 +24,16 @@ import math
 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
 
@@ -46,10 +52,10 @@ except AttributeError:
         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
@@ -81,8 +87,10 @@ class OSDWidget(QWidget):
         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.
@@ -172,15 +180,15 @@ class OSDWidget(QWidget):
 
     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)
@@ -208,7 +216,7 @@ class OSDWidget(QWidget):
 
     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
@@ -439,7 +447,7 @@ class OSDWidget(QWidget):
         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
@@ -662,41 +670,131 @@ class OSDWidget(QWidget):
         """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))
@@ -717,14 +815,15 @@ if __name__ == "__main__":
     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_())