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,10 +16,13 @@ import time
16 16
 import string
17 17
 import urllib.parse
18 18
 import urllib.request
19
+import signal
20
+import operator
21
+import socket
19 22
 from os import path
20 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 26
 from PyQt5.QtCore import QCoreApplication, QSettings, QUrl, QTimer, QSize, Qt, QSettings
24 27
 
25 28
 class SettingsWindow(QWidget):
@@ -33,9 +36,33 @@ class SettingsWindow(QWidget):
33 36
         self.setWindowTitle(parent.name + " Settings")
34 37
         self.setWindowIcon(parent.icon)
35 38
 
39
+
36 40
         box = QVBoxLayout()
37 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 66
         helpText = "Usage:\n"
40 67
         helpText += "1st Column: Printer Hostname or IP address\n"
41 68
         helpText += "2nd Column: OctoPrint API Key (32 char hexadecimal)\n"
@@ -124,6 +151,15 @@ class SettingsWindow(QWidget):
124 151
                     s = "0"
125 152
                 if (len(s) < 1) or (len(s) > 3) or not all(c in string.digits for c in s):
126 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 163
         return (True, "")
128 164
 
129 165
     def closeEvent(self, event):
@@ -140,9 +176,14 @@ class SettingsWindow(QWidget):
140 176
                 self.parent.removeSettingsWindow()
141 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 184
             if r == True:
185
+                self.parent.jogMoveSpeed = js
186
+                self.parent.jogMoveLength = jl
146 187
                 self.parent.writeSettings(newPrinters)
147 188
                 self.parent.restartApp()
148 189
 
@@ -250,13 +291,12 @@ class CamWindow(QWidget):
250 291
         box.addWidget(label, 0)
251 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 294
         slide = QHBoxLayout()
258 295
         box.addLayout(slide, 0)
259 296
 
297
+        self.slideStaticLabel = QLabel("Refresh")
298
+        slide.addWidget(self.slideStaticLabel, 0)
299
+
260 300
         self.slider = QSlider(Qt.Horizontal)
261 301
         self.slider.setMinimum(int(100 / self.sliderFactor))
262 302
         self.slider.setMaximum(int(2000 / self.sliderFactor))
@@ -271,13 +311,150 @@ class CamWindow(QWidget):
271 311
         self.slideLabel = QLabel(str(self.reloadDelayDefault) + "ms")
272 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 318
         self.statusLabel = QLabel("Status: unavailable")
275 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 396
         self.loadImage()
279 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 458
     def getHost(self):
282 459
         return self.host
283 460
 
@@ -385,6 +562,10 @@ class OctoTray():
385 562
     camWindows = []
386 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 569
     def __init__(self, app, inSysTray):
389 570
         QCoreApplication.setApplicationName(self.name)
390 571
         self.app = app
@@ -455,6 +636,18 @@ class OctoTray():
455 636
 
456 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 651
             action = QAction("Get Status")
459 652
             action.triggered.connect(lambda chk, x=p: self.printerStatusAction(x))
460 653
             p.append(action)
@@ -518,9 +711,21 @@ class OctoTray():
518 711
     def showHide(self, activationReason):
519 712
         if activationReason == QSystemTrayIcon.Trigger:
520 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 718
     def readSettings(self):
523 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 729
         printers = []
525 730
         l = settings.beginReadArray("printers")
526 731
         for i in range(0, l):
@@ -536,6 +741,10 @@ class OctoTray():
536 741
 
537 742
     def writeSettings(self, printers):
538 743
         settings = QSettings(self.vendor, self.name)
744
+
745
+        settings.setValue("jog_speed", self.jogMoveSpeed)
746
+        settings.setValue("jog_length", self.jogMoveLength)
747
+
539 748
         settings.remove("printers")
540 749
         settings.beginWriteArray("printers")
541 750
         for i in range(0, len(printers)):
@@ -703,6 +912,20 @@ class OctoTray():
703 912
             pass
704 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 929
     def getMethod(self, host, key):
707 930
         r = self.sendGetRequest(host, key, "plugin/psucontrol")
708 931
         if r == "timeout":
@@ -792,6 +1015,32 @@ class OctoTray():
792 1015
 
793 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 1044
     def printerWebAction(self, item):
796 1045
         self.openBrowser(item[0])
797 1046
 
@@ -813,6 +1062,9 @@ class OctoTray():
813 1062
             s += "\n" + t
814 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 1068
     def setTemperature(self, host, key, what, temp):
817 1069
         path = "printer/bed"
818 1070
         s = "{\"command\": \"target\", \"target\": " + temp + "}"
@@ -903,6 +1155,8 @@ if __name__ == "__main__":
903 1155
     app = QApplication(sys.argv)
904 1156
     app.setQuitOnLastWindowClosed(False)
905 1157
 
1158
+    signal.signal(signal.SIGINT, signal.SIG_DFL)
1159
+
906 1160
     inSysTray = QSystemTrayIcon.isSystemTrayAvailable()
907 1161
     if ("windowed" in sys.argv) or ("--windowed" in sys.argv) or ("-w" in sys.argv):
908 1162
         inSysTray = False

Loading…
Cancel
Save