Parcourir la source

add menu action to show rudimentary window with webcam image updating repeatedly

Thomas Buck il y a 3 ans
Parent
révision
4f6e1f3817
1 fichiers modifiés avec 152 ajouts et 10 suppressions
  1. 152
    10
      octotray

+ 152
- 10
octotray Voir le fichier

6
 # - python-pyqt5
6
 # - python-pyqt5
7
 # - curl
7
 # - curl
8
 # - xdg-open
8
 # - xdg-open
9
+#
10
+# see also:
11
+# https://doc.qt.io/qt-5/qtwidgets-widgets-imageviewer-example.html
12
+# https://stackoverflow.com/a/22618496
9
 
13
 
10
 import json
14
 import json
11
 import subprocess
15
 import subprocess
13
 import os
17
 import os
14
 import threading
18
 import threading
15
 import time
19
 import time
16
-from PyQt5 import QtWidgets, QtGui, QtCore
17
-from PyQt5.QtWidgets import QSystemTrayIcon, QAction, QMenu, QMessageBox
18
-from PyQt5.QtGui import QIcon, QPixmap
19
-from PyQt5.QtCore import QCoreApplication, QSettings
20
+from PyQt5 import QtWidgets, QtGui, QtCore, QtNetwork
21
+from PyQt5.QtWidgets import QSystemTrayIcon, QAction, QMenu, QMessageBox, QWidget, QLabel, QVBoxLayout, QHBoxLayout, QDesktopWidget, QSizePolicy, QSlider, QLayout
22
+from PyQt5.QtGui import QIcon, QPixmap, QImageReader
23
+from PyQt5.QtCore import QCoreApplication, QSettings, QUrl, QTimer, QSize, Qt
24
+
25
+class AspectRatioPixmapLabel(QLabel):
26
+    def __init__(self, *args, **kwargs):
27
+        super(AspectRatioPixmapLabel, self).__init__(*args, **kwargs)
28
+        self.setMinimumSize(1, 1)
29
+        self.setScaledContents(False)
30
+        self.pix = QPixmap(0, 0)
31
+
32
+    def setPixmap(self, p):
33
+        self.pix = p
34
+        super(AspectRatioPixmapLabel, self).setPixmap(self.scaledPixmap())
35
+
36
+    def heightForWidth(self, width):
37
+        if self.pix.isNull():
38
+            return self.height()
39
+        else:
40
+            return (self.pix.height() * width) / self.pix.width()
41
+
42
+    def sizeHint(self):
43
+        w = self.width()
44
+        return QSize(int(w), int(self.heightForWidth(w)))
45
+
46
+    def scaledPixmap(self):
47
+        return self.pix.scaled(self.size(), Qt.KeepAspectRatio, Qt.SmoothTransformation)
48
+
49
+    def resizeEvent(self, e):
50
+        if not self.pix.isNull():
51
+            super(AspectRatioPixmapLabel, self).setPixmap(self.scaledPixmap())
52
+
53
+class CamWindow(QWidget):
54
+    reloadDelayDefault = 1000 # in ms
55
+    addSize = 100
56
+    reloadOn = True
57
+
58
+    def __init__(self, parent, name, icon, app, manager, host, *args, **kwargs):
59
+        super(CamWindow, self).__init__(*args, **kwargs)
60
+        self.url = "http://" + host + ":8080/?action=snapshot"
61
+
62
+        self.app = app
63
+        self.parent = parent
64
+        self.manager = manager
65
+        self.manager.finished.connect(self.handleResponse)
66
+
67
+        self.setWindowTitle(name + " Webcam Stream")
68
+        self.setWindowIcon(icon)
69
+
70
+        box = QVBoxLayout()
71
+        self.setLayout(box)
72
+
73
+        label = QLabel(self.url)
74
+        box.addWidget(label, 0)
75
+        box.setAlignment(label, Qt.AlignHCenter)
76
+
77
+        self.img = AspectRatioPixmapLabel()
78
+        self.img.setPixmap(QPixmap(640, 480))
79
+        box.addWidget(self.img, 1)
80
+
81
+        slide = QHBoxLayout()
82
+        box.addLayout(slide, 0)
83
+
84
+        self.slider = QSlider(Qt.Horizontal)
85
+        self.slider.setMinimum(0)
86
+        self.slider.setMaximum(2000)
87
+        self.slider.setTickInterval(100)
88
+        self.slider.setPageStep(100)
89
+        self.slider.setSingleStep(100)
90
+        self.slider.setTickPosition(QSlider.TicksBelow)
91
+        self.slider.setValue(self.reloadDelayDefault)
92
+        self.slider.valueChanged.connect(self.sliderChanged)
93
+        slide.addWidget(self.slider, 1)
94
+
95
+        self.slideLabel = QLabel(str(self.reloadDelayDefault) + "ms")
96
+        slide.addWidget(self.slideLabel, 0)
97
+
98
+        size = self.size()
99
+        size.setHeight(size.height() + self.addSize)
100
+        self.resize(size)
101
+
102
+        self.loadImage()
103
+
104
+    def sliderChanged(self):
105
+        self.slideLabel.setText(str(self.slider.value()) + "ms")
106
+
107
+    def closeEvent(self, event):
108
+        self.reloadOn = False
109
+        self.url = ""
110
+        self.parent.removeWebcamWindow(self)
111
+
112
+    def scheduleLoad(self):
113
+        if self.reloadOn:
114
+            QTimer.singleShot(self.slider.value(), self.loadImage)
115
+
116
+    def loadImage(self):
117
+        url = QUrl(self.url)
118
+        request = QtNetwork.QNetworkRequest(url)
119
+        self.manager.get(request)
120
+
121
+    def handleResponse(self, reply):
122
+        if reply.url().url() == self.url:
123
+            if reply.error() == QtNetwork.QNetworkReply.NoError:
124
+                reader = QImageReader(reply)
125
+                reader.setAutoTransform(True)
126
+                image = reader.read()
127
+                if image != None:
128
+                    if image.colorSpace().isValid():
129
+                        image.convertToColorSpace(QColorSpace.SRgb)
130
+                    self.img.setPixmap(QPixmap.fromImage(image))
131
+                    self.scheduleLoad()
132
+                else:
133
+                    print("Error decoding image: " + reader.errorString())
134
+            else:
135
+                print("Error loading image: " + reply.errorString())
20
 
136
 
21
 class OctoTray():
137
 class OctoTray():
22
     name = "OctoTray"
138
     name = "OctoTray"
23
     vendor = "xythobuz"
139
     vendor = "xythobuz"
24
-    version = "0.1"
140
+    version = "0.2"
25
 
141
 
26
     iconPath = "/usr/share/pixmaps/"
142
     iconPath = "/usr/share/pixmaps/"
27
     iconName = "octotray_icon.png"
143
     iconName = "octotray_icon.png"
36
         "Printing", "Pausing", "Paused"
152
         "Printing", "Pausing", "Paused"
37
     ]
153
     ]
38
 
154
 
155
+    camWindows = []
156
+
39
     def __init__(self):
157
     def __init__(self):
40
-        app = QtWidgets.QApplication(sys.argv)
158
+        self.app = QtWidgets.QApplication(sys.argv)
41
         QCoreApplication.setApplicationName(self.name)
159
         QCoreApplication.setApplicationName(self.name)
42
 
160
 
43
         if not QSystemTrayIcon.isSystemTrayAvailable():
161
         if not QSystemTrayIcon.isSystemTrayAvailable():
44
             print("System Tray is not available on this platform!")
162
             print("System Tray is not available on this platform!")
45
             sys.exit(0)
163
             sys.exit(0)
46
 
164
 
165
+        self.manager = QtNetwork.QNetworkAccessManager()
166
+
47
         self.menu = QMenu()
167
         self.menu = QMenu()
48
 
168
 
49
         for p in self.printers:
169
         for p in self.printers:
72
             p.append(action)
192
             p.append(action)
73
             menu.addAction(action)
193
             menu.addAction(action)
74
 
194
 
195
+            action = QAction("Show Webcam")
196
+            action.triggered.connect(lambda chk, x=p: self.printerWebcamAction(x))
197
+            p.append(action)
198
+            menu.addAction(action)
199
+
75
             action = QAction("Open Web UI")
200
             action = QAction("Open Web UI")
76
             action.triggered.connect(lambda chk, x=p: self.printerWebAction(x))
201
             action.triggered.connect(lambda chk, x=p: self.printerWebAction(x))
77
             p.append(action)
202
             p.append(action)
89
         else:
214
         else:
90
             print("no icon found")
215
             print("no icon found")
91
 
216
 
92
-        icon = QIcon()
217
+        self.icon = QIcon()
93
         if iconPathName != "":
218
         if iconPathName != "":
94
             pic = QPixmap(32, 32)
219
             pic = QPixmap(32, 32)
95
             pic.load(iconPathName)
220
             pic.load(iconPathName)
96
-            icon = QIcon(pic)
221
+            self.icon = QIcon(pic)
97
 
222
 
98
-        trayIcon = QSystemTrayIcon(icon)
223
+        trayIcon = QSystemTrayIcon(self.icon)
99
         trayIcon.setToolTip(self.name + " " + self.version)
224
         trayIcon.setToolTip(self.name + " " + self.version)
100
         trayIcon.setContextMenu(self.menu)
225
         trayIcon.setContextMenu(self.menu)
101
         trayIcon.setVisible(True)
226
         trayIcon.setVisible(True)
102
 
227
 
103
-        sys.exit(app.exec_())
228
+        sys.exit(self.app.exec_())
104
 
229
 
105
     def openBrowser(self, url):
230
     def openBrowser(self, url):
106
         os.system("xdg-open http://" + url)
231
         os.system("xdg-open http://" + url)
256
             warning = True
381
             warning = True
257
         self.showDialog("OctoTray Status", s, None, False, warning)
382
         self.showDialog("OctoTray Status", s, None, False, warning)
258
 
383
 
384
+    def printerWebcamAction(self, item):
385
+        window = CamWindow(self, self.name, self.icon, self.app, self.manager, item[0])
386
+        self.camWindows.append(window)
387
+
388
+        screenGeometry = QDesktopWidget().screenGeometry()
389
+        width = screenGeometry.width()
390
+        height = screenGeometry.height()
391
+        x = (width - window.width()) / 2
392
+        y = (height - window.height()) / 2
393
+        window.setGeometry(int(x), int(y), int(window.width()), int(window.height()))
394
+
395
+        window.show()
396
+        window.activateWindow()
397
+
398
+    def removeWebcamWindow(self, window):
399
+        self.camWindows.remove(window)
400
+
259
 if __name__ == "__main__":
401
 if __name__ == "__main__":
260
     tray = OctoTray()
402
     tray = OctoTray()

Chargement…
Annuler
Enregistrer