Browse Source

added ability to list and print recent files. added actions to webcam window.

Thomas Buck 2 years ago
parent
commit
de7020a3cf
1 changed files with 263 additions and 9 deletions
  1. 263
    9
      src/octotray.py

+ 263
- 9
src/octotray.py View File

16
 import string
16
 import string
17
 import urllib.parse
17
 import urllib.parse
18
 import urllib.request
18
 import urllib.request
19
+import signal
20
+import operator
21
+import socket
19
 from os import path
22
 from os import path
20
 from PyQt5 import QtWidgets, QtGui, QtCore, QtNetwork
23
 from PyQt5 import QtWidgets, QtGui, QtCore, QtNetwork
21
-from PyQt5.QtWidgets import QSystemTrayIcon, QAction, QMenu, QMessageBox, QWidget, QLabel, QVBoxLayout, QHBoxLayout, QDesktopWidget, QSizePolicy, QSlider, QLayout, QTableWidget, QTableWidgetItem, QPushButton, QApplication
22
-from PyQt5.QtGui import QIcon, QPixmap, QImageReader, QDesktopServices, QFontDatabase, QCursor
24
+from PyQt5.QtWidgets import QSystemTrayIcon, QAction, QMenu, QMessageBox, QWidget, QLabel, QVBoxLayout, QHBoxLayout, QDesktopWidget, QSizePolicy, QSlider, QLayout, QTableWidget, QTableWidgetItem, QPushButton, QApplication, QLineEdit, QGridLayout
25
+from PyQt5.QtGui import QIcon, QPixmap, QImageReader, QDesktopServices, QFontDatabase, QCursor, QIntValidator
23
 from PyQt5.QtCore import QCoreApplication, QSettings, QUrl, QTimer, QSize, Qt, QSettings
26
 from PyQt5.QtCore import QCoreApplication, QSettings, QUrl, QTimer, QSize, Qt, QSettings
24
 
27
 
25
 class SettingsWindow(QWidget):
28
 class SettingsWindow(QWidget):
33
         self.setWindowTitle(parent.name + " Settings")
36
         self.setWindowTitle(parent.name + " Settings")
34
         self.setWindowIcon(parent.icon)
37
         self.setWindowIcon(parent.icon)
35
 
38
 
39
+
36
         box = QVBoxLayout()
40
         box = QVBoxLayout()
37
         self.setLayout(box)
41
         self.setLayout(box)
38
 
42
 
43
+        staticSettings = QGridLayout()
44
+        box.addLayout(staticSettings, 0)
45
+
46
+        self.jogSpeedText = QLabel("Jog Speed")
47
+        staticSettings.addWidget(self.jogSpeedText, 0, 0)
48
+
49
+        self.jogSpeed = QLineEdit(str(self.parent.jogMoveSpeed))
50
+        self.jogSpeed.setValidator(QIntValidator(1, 6000))
51
+        staticSettings.addWidget(self.jogSpeed, 0, 1)
52
+
53
+        self.jogSpeedUnitText = QLabel("mm/min")
54
+        staticSettings.addWidget(self.jogSpeedUnitText, 0, 2)
55
+
56
+        self.jogLengthText = QLabel("Jog Length")
57
+        staticSettings.addWidget(self.jogLengthText, 1, 0)
58
+
59
+        self.jogLength = QLineEdit(str(self.parent.jogMoveLength))
60
+        self.jogLength.setValidator(QIntValidator(1, 100))
61
+        staticSettings.addWidget(self.jogLength, 1, 1)
62
+
63
+        self.jogLengthUnitText = QLabel("mm")
64
+        staticSettings.addWidget(self.jogLengthUnitText, 1, 2)
65
+
39
         helpText = "Usage:\n"
66
         helpText = "Usage:\n"
40
         helpText += "1st Column: Printer Hostname or IP address\n"
67
         helpText += "1st Column: Printer Hostname or IP address\n"
41
         helpText += "2nd Column: OctoPrint API Key (32 char hexadecimal)\n"
68
         helpText += "2nd Column: OctoPrint API Key (32 char hexadecimal)\n"
124
                     s = "0"
151
                     s = "0"
125
                 if (len(s) < 1) or (len(s) > 3) or not all(c in string.digits for c in s):
152
                 if (len(s) < 1) or (len(s) > 3) or not all(c in string.digits for c in s):
126
                     return (False, "Temperature not a number from 0...999")
153
                     return (False, "Temperature not a number from 0...999")
154
+
155
+        js = int(self.jogSpeed.text())
156
+        if (js < 1) or (js > 6000):
157
+            return (False, "Jog Speed not a number from 1...6000")
158
+
159
+        jl = int(self.jogLength.text())
160
+        if (jl < 1) or (jl > 100):
161
+            return (False, "Jog Length not a number from 1...100")
162
+
127
         return (True, "")
163
         return (True, "")
128
 
164
 
129
     def closeEvent(self, event):
165
     def closeEvent(self, event):
140
                 self.parent.removeSettingsWindow()
176
                 self.parent.removeSettingsWindow()
141
                 return
177
                 return
142
 
178
 
143
-        if oldPrinters != newPrinters:
144
-            r = self.parent.showDialog(self.parent.name + " Settings Changed", "Do you want to save the new list of printers?", "This will restart the application!", True, False, False)
179
+        js = int(self.jogSpeed.text())
180
+        jl = int(self.jogLength.text())
181
+
182
+        if (oldPrinters != newPrinters) or (js != self.parent.jogMoveSpeed) or (jl != self.parent.jogMoveLength):
183
+            r = self.parent.showDialog(self.parent.name + " Settings Changed", "Do you want to save the new configuration?", "This will restart the application!", True, False, False)
145
             if r == True:
184
             if r == True:
185
+                self.parent.jogMoveSpeed = js
186
+                self.parent.jogMoveLength = jl
146
                 self.parent.writeSettings(newPrinters)
187
                 self.parent.writeSettings(newPrinters)
147
                 self.parent.restartApp()
188
                 self.parent.restartApp()
148
 
189
 
250
         box.addWidget(label, 0)
291
         box.addWidget(label, 0)
251
         box.setAlignment(label, Qt.AlignHCenter)
292
         box.setAlignment(label, Qt.AlignHCenter)
252
 
293
 
253
-        self.img = AspectRatioPixmapLabel()
254
-        self.img.setPixmap(QPixmap(640, 480))
255
-        box.addWidget(self.img, 1)
256
-
257
         slide = QHBoxLayout()
294
         slide = QHBoxLayout()
258
         box.addLayout(slide, 0)
295
         box.addLayout(slide, 0)
259
 
296
 
297
+        self.slideStaticLabel = QLabel("Refresh")
298
+        slide.addWidget(self.slideStaticLabel, 0)
299
+
260
         self.slider = QSlider(Qt.Horizontal)
300
         self.slider = QSlider(Qt.Horizontal)
261
         self.slider.setMinimum(int(100 / self.sliderFactor))
301
         self.slider.setMinimum(int(100 / self.sliderFactor))
262
         self.slider.setMaximum(int(2000 / self.sliderFactor))
302
         self.slider.setMaximum(int(2000 / self.sliderFactor))
271
         self.slideLabel = QLabel(str(self.reloadDelayDefault) + "ms")
311
         self.slideLabel = QLabel(str(self.reloadDelayDefault) + "ms")
272
         slide.addWidget(self.slideLabel, 0)
312
         slide.addWidget(self.slideLabel, 0)
273
 
313
 
314
+        self.img = AspectRatioPixmapLabel()
315
+        self.img.setPixmap(QPixmap(640, 480))
316
+        box.addWidget(self.img, 1)
317
+
274
         self.statusLabel = QLabel("Status: unavailable")
318
         self.statusLabel = QLabel("Status: unavailable")
275
         box.addWidget(self.statusLabel, 0)
319
         box.addWidget(self.statusLabel, 0)
276
-        box.setAlignment(label, Qt.AlignHCenter)
320
+        box.setAlignment(self.statusLabel, Qt.AlignHCenter)
321
+
322
+        self.method = self.parent.getMethod(self.printer[0], self.printer[1])
323
+        if self.method != "unknown":
324
+            controls_power = QHBoxLayout()
325
+            box.addLayout(controls_power, 0)
326
+
327
+            self.turnOnButton = QPushButton("Turn O&n")
328
+            self.turnOnButton.clicked.connect(self.turnOn)
329
+            controls_power.addWidget(self.turnOnButton)
330
+
331
+            self.turnOffButton = QPushButton("Turn O&ff")
332
+            self.turnOffButton.clicked.connect(self.turnOff)
333
+            controls_power.addWidget(self.turnOffButton)
334
+
335
+        controls_temp = QHBoxLayout()
336
+        box.addLayout(controls_temp, 0)
337
+
338
+        self.cooldownButton = QPushButton("&Cooldown")
339
+        self.cooldownButton.clicked.connect(self.cooldown)
340
+        controls_temp.addWidget(self.cooldownButton)
341
+
342
+        self.preheatToolButton = QPushButton("Preheat &Tool")
343
+        self.preheatToolButton.clicked.connect(self.preheatTool)
344
+        controls_temp.addWidget(self.preheatToolButton)
345
+
346
+        self.preheatBedButton = QPushButton("Preheat &Bed")
347
+        self.preheatBedButton.clicked.connect(self.preheatBed)
348
+        controls_temp.addWidget(self.preheatBedButton)
349
+
350
+        controls_home = QHBoxLayout()
351
+        box.addLayout(controls_home, 0)
352
+
353
+        self.homeAllButton = QPushButton("Home &All")
354
+        self.homeAllButton.clicked.connect(self.homeAll)
355
+        controls_home.addWidget(self.homeAllButton, 1)
356
+
357
+        self.homeXButton = QPushButton("Home &X")
358
+        self.homeXButton.clicked.connect(self.homeX)
359
+        controls_home.addWidget(self.homeXButton, 0)
360
+
361
+        self.homeYButton = QPushButton("Home &Y")
362
+        self.homeYButton.clicked.connect(self.homeY)
363
+        controls_home.addWidget(self.homeYButton, 0)
364
+
365
+        self.homeZButton = QPushButton("Home &Z")
366
+        self.homeZButton.clicked.connect(self.homeZ)
367
+        controls_home.addWidget(self.homeZButton, 0)
368
+
369
+        controls_move = QHBoxLayout()
370
+        box.addLayout(controls_move, 0)
371
+
372
+        self.XPButton = QPushButton("X+")
373
+        self.XPButton.clicked.connect(self.moveXP)
374
+        controls_move.addWidget(self.XPButton)
375
+
376
+        self.XMButton = QPushButton("X-")
377
+        self.XMButton.clicked.connect(self.moveXM)
378
+        controls_move.addWidget(self.XMButton)
379
+
380
+        self.YPButton = QPushButton("Y+")
381
+        self.YPButton.clicked.connect(self.moveYP)
382
+        controls_move.addWidget(self.YPButton)
383
+
384
+        self.YMButton = QPushButton("Y-")
385
+        self.YMButton.clicked.connect(self.moveYM)
386
+        controls_move.addWidget(self.YMButton)
387
+
388
+        self.ZPButton = QPushButton("Z+")
389
+        self.ZPButton.clicked.connect(self.moveZP)
390
+        controls_move.addWidget(self.ZPButton)
391
+
392
+        self.ZMButton = QPushButton("Z-")
393
+        self.ZMButton.clicked.connect(self.moveZM)
394
+        controls_move.addWidget(self.ZMButton)
277
 
395
 
278
         self.loadImage()
396
         self.loadImage()
279
         self.loadStatus()
397
         self.loadStatus()
280
 
398
 
399
+    def moveXP(self):
400
+        self.parent.printerMoveAction(self.printer, "x", int(self.parent.jogMoveLength), True)
401
+
402
+    def moveXM(self):
403
+        self.parent.printerMoveAction(self.printer, "x", -1 * int(self.parent.jogMoveLength), True)
404
+
405
+    def moveYP(self):
406
+        self.parent.printerMoveAction(self.printer, "y", int(self.parent.jogMoveLength), True)
407
+
408
+    def moveYM(self):
409
+        self.parent.printerMoveAction(self.printer, "y", -1 * int(self.parent.jogMoveLength), True)
410
+
411
+    def moveZP(self):
412
+        self.parent.printerMoveAction(self.printer, "z", int(self.parent.jogMoveLength), True)
413
+
414
+    def moveZM(self):
415
+        self.parent.printerMoveAction(self.printer, "z", -1 * int(self.parent.jogMoveLength), True)
416
+
417
+    def homeX(self):
418
+        self.parent.printerHomingAction(self.printer, "x")
419
+
420
+    def homeY(self):
421
+        self.parent.printerHomingAction(self.printer, "y")
422
+
423
+    def homeZ(self):
424
+        self.parent.printerHomingAction(self.printer, "z")
425
+
426
+    def homeAll(self):
427
+        self.parent.printerHomingAction(self.printer, "xyz")
428
+
429
+    def turnOn(self):
430
+        if self.method == "psucontrol":
431
+            self.parent.printerOnAction(self.printer)
432
+        elif self.method == "system":
433
+            cmds = self.parent.getSystemCommands(self.printer[0], self.printer[1])
434
+            for cmd in cmds:
435
+                if "on" in cmd:
436
+                    self.parent.setSystemCommand(self.printer[0], self.printer[1], cmd)
437
+                    break
438
+
439
+    def turnOff(self):
440
+        if self.method == "psucontrol":
441
+            self.parent.printerOffAction(self.printer)
442
+        elif self.method == "system":
443
+            cmds = self.parent.getSystemCommands(self.printer[0], self.printer[1])
444
+            for cmd in cmds:
445
+                if "off" in cmd:
446
+                    self.parent.setSystemCommand(self.printer[0], self.printer[1], cmd)
447
+                    break
448
+
449
+    def cooldown(self):
450
+        self.parent.printerCooldown(self.printer)
451
+
452
+    def preheatTool(self):
453
+        self.parent.printerHeatTool(self.printer)
454
+
455
+    def preheatBed(self):
456
+        self.parent.printerHeatBed(self.printer)
457
+
281
     def getHost(self):
458
     def getHost(self):
282
         return self.host
459
         return self.host
283
 
460
 
385
     camWindows = []
562
     camWindows = []
386
     settingsWindow = None
563
     settingsWindow = None
387
 
564
 
565
+    # default, can be overridden in config
566
+    jogMoveSpeed = 10 * 60 # in mm/min
567
+    jogMoveLength = 10 # in mm
568
+
388
     def __init__(self, app, inSysTray):
569
     def __init__(self, app, inSysTray):
389
         QCoreApplication.setApplicationName(self.name)
570
         QCoreApplication.setApplicationName(self.name)
390
         self.app = app
571
         self.app = app
455
 
636
 
456
             menu.addSeparator()
637
             menu.addSeparator()
457
 
638
 
639
+            fileMenu = QMenu("Recent Files")
640
+            p.append(fileMenu)
641
+            menu.addMenu(fileMenu)
642
+
643
+            files = self.getRecentFiles(p[0], p[1], 10)
644
+            for f in files:
645
+                fileName, filePath = f
646
+                action = QAction(fileName)
647
+                action.triggered.connect(lambda chk, x=p, y=filePath: self.printerFilePrint(x, y))
648
+                p.append(action)
649
+                fileMenu.addAction(action)
650
+
458
             action = QAction("Get Status")
651
             action = QAction("Get Status")
459
             action.triggered.connect(lambda chk, x=p: self.printerStatusAction(x))
652
             action.triggered.connect(lambda chk, x=p: self.printerStatusAction(x))
460
             p.append(action)
653
             p.append(action)
518
     def showHide(self, activationReason):
711
     def showHide(self, activationReason):
519
         if activationReason == QSystemTrayIcon.Trigger:
712
         if activationReason == QSystemTrayIcon.Trigger:
520
             self.menu.popup(QCursor.pos())
713
             self.menu.popup(QCursor.pos())
714
+        elif activationReason == QSystemTrayIcon.MiddleClick:
715
+            if len(self.printers) > 0:
716
+                self.printerWebcamAction(self.printers[0])
521
 
717
 
522
     def readSettings(self):
718
     def readSettings(self):
523
         settings = QSettings(self.vendor, self.name)
719
         settings = QSettings(self.vendor, self.name)
720
+
721
+        js = settings.value("jog_speed")
722
+        if js != None:
723
+            self.jogMoveSpeed = int(js)
724
+
725
+        jl = settings.value("jog_length")
726
+        if jl != None:
727
+            self.jogMoveLength = int(jl)
728
+
524
         printers = []
729
         printers = []
525
         l = settings.beginReadArray("printers")
730
         l = settings.beginReadArray("printers")
526
         for i in range(0, l):
731
         for i in range(0, l):
536
 
741
 
537
     def writeSettings(self, printers):
742
     def writeSettings(self, printers):
538
         settings = QSettings(self.vendor, self.name)
743
         settings = QSettings(self.vendor, self.name)
744
+
745
+        settings.setValue("jog_speed", self.jogMoveSpeed)
746
+        settings.setValue("jog_length", self.jogMoveLength)
747
+
539
         settings.remove("printers")
748
         settings.remove("printers")
540
         settings.beginWriteArray("printers")
749
         settings.beginWriteArray("printers")
541
         for i in range(0, len(printers)):
750
         for i in range(0, len(printers)):
703
             pass
912
             pass
704
         return host
913
         return host
705
 
914
 
915
+    def getRecentFiles(self, host, key, count):
916
+        r = self.sendGetRequest(host, key, "files?recursive=true")
917
+        files = []
918
+        try:
919
+            rd = json.loads(r)
920
+            if "files" in rd:
921
+                t = [f for f in rd["files"] if "date" in f]
922
+                fs = sorted(t, key=operator.itemgetter("date"), reverse=True)
923
+                for f in fs[:count]:
924
+                    files.append((f["name"], f["origin"] + "/" + f["path"]))
925
+        except json.JSONDecodeError:
926
+            pass
927
+        return files
928
+
706
     def getMethod(self, host, key):
929
     def getMethod(self, host, key):
707
         r = self.sendGetRequest(host, key, "plugin/psucontrol")
930
         r = self.sendGetRequest(host, key, "plugin/psucontrol")
708
         if r == "timeout":
931
         if r == "timeout":
792
 
1015
 
793
         self.setPSUControl(item[0], item[1], False)
1016
         self.setPSUControl(item[0], item[1], False)
794
 
1017
 
1018
+    def printerHomingAction(self, item, axes = "xyz"):
1019
+        state = self.getState(item[0], item[1])
1020
+        if state in self.statesWithWarning:
1021
+            if self.showDialog("OctoTray Warning", "The printer seems to be running currently!", "Do you really want to home it?", True, True) == False:
1022
+                return
1023
+
1024
+        axes_string = ''
1025
+        for i in range(0, len(axes)):
1026
+            axes_string += '"' + str(axes[i]) + '"'
1027
+            if i < (len(axes) - 1):
1028
+                axes_string += ', '
1029
+
1030
+        self.sendPostRequest(item[0], item[1], "printer/printhead", '{ "command": "home", "axes": [' + axes_string + '] }')
1031
+
1032
+    def printerMoveAction(self, printer, axis, dist, relative = True):
1033
+        state = self.getState(printer[0], printer[1])
1034
+        if state in self.statesWithWarning:
1035
+            if self.showDialog("OctoTray Warning", "The printer seems to be running currently!", "Do you really want to move it?", True, True) == False:
1036
+                return
1037
+
1038
+        absolute = ''
1039
+        if relative == False:
1040
+            absolute = ', "absolute": true'
1041
+
1042
+        self.sendPostRequest(printer[0], printer[1], "printer/printhead", '{ "command": "jog", "' + str(axis) + '": ' + str(dist) + ', "speed": ' + str(self.jogMoveSpeed) + absolute + ' }')
1043
+
795
     def printerWebAction(self, item):
1044
     def printerWebAction(self, item):
796
         self.openBrowser(item[0])
1045
         self.openBrowser(item[0])
797
 
1046
 
813
             s += "\n" + t
1062
             s += "\n" + t
814
         self.showDialog("OctoTray Status", s, None, False, warning)
1063
         self.showDialog("OctoTray Status", s, None, False, warning)
815
 
1064
 
1065
+    def printerFilePrint(self, item, path):
1066
+        self.sendPostRequest(item[0], item[1], "files/" + path, '{ "command": "select", "print": true }')
1067
+
816
     def setTemperature(self, host, key, what, temp):
1068
     def setTemperature(self, host, key, what, temp):
817
         path = "printer/bed"
1069
         path = "printer/bed"
818
         s = "{\"command\": \"target\", \"target\": " + temp + "}"
1070
         s = "{\"command\": \"target\", \"target\": " + temp + "}"
903
     app = QApplication(sys.argv)
1155
     app = QApplication(sys.argv)
904
     app.setQuitOnLastWindowClosed(False)
1156
     app.setQuitOnLastWindowClosed(False)
905
 
1157
 
1158
+    signal.signal(signal.SIGINT, signal.SIG_DFL)
1159
+
906
     inSysTray = QSystemTrayIcon.isSystemTrayAvailable()
1160
     inSysTray = QSystemTrayIcon.isSystemTrayAvailable()
907
     if ("windowed" in sys.argv) or ("--windowed" in sys.argv) or ("-w" in sys.argv):
1161
     if ("windowed" in sys.argv) or ("--windowed" in sys.argv) or ("-w" in sys.argv):
908
         inSysTray = False
1162
         inSysTray = False

Loading…
Cancel
Save