From 33fa556705d457bdc0c4704026a05e7ba6bf250e Mon Sep 17 00:00:00 2001 From: Olivier Matz Date: Sun, 7 Jun 2015 20:14:27 +0200 Subject: [PATCH] early support of camera --- qtosd/qtosd.py | 143 +++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 121 insertions(+), 22 deletions(-) diff --git a/qtosd/qtosd.py b/qtosd/qtosd.py index 2d4583b..449f243 100644 --- a/qtosd/qtosd.py +++ b/qtosd/qtosd.py @@ -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_()) -- 2.20.1