Linux PyQt tray application to control OctoPrint instances
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

SettingsWindow.py 9.2KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260
  1. #!/usr/bin/env python3
  2. # OctoTray Linux Qt System Tray OctoPrint client
  3. #
  4. # SettingsWindow.py
  5. #
  6. # UI for changes to application configuration.
  7. import string
  8. from PyQt5 import QtWidgets
  9. from PyQt5.QtWidgets import QWidget, QLabel, QVBoxLayout, QHBoxLayout, QTableWidget, QTableWidgetItem, QPushButton, QLineEdit, QGridLayout
  10. from PyQt5.QtGui import QFontDatabase, QIntValidator
  11. from PyQt5.QtCore import Qt
  12. class Printer(object):
  13. pass
  14. class SettingsWindow(QWidget):
  15. columns = [ "Hostname", "API Key", "Tool Preheat", "Bed Preheat" ]
  16. presets = [ "octopi.local", "000000000_API_KEY_HERE_000000000", "0", "0" ]
  17. def __init__(self, parent, *args, **kwargs):
  18. super(SettingsWindow, self).__init__(*args, **kwargs)
  19. self.parent = parent
  20. self.setWindowTitle(parent.name + " Settings")
  21. self.setWindowIcon(parent.icon)
  22. box = QVBoxLayout()
  23. self.setLayout(box)
  24. staticSettings = QGridLayout()
  25. box.addLayout(staticSettings, 0)
  26. self.jogSpeedText = QLabel("Jog Speed")
  27. staticSettings.addWidget(self.jogSpeedText, 0, 0)
  28. self.jogSpeed = QLineEdit(str(self.parent.jogMoveSpeed))
  29. self.jogSpeed.setValidator(QIntValidator(1, 6000))
  30. staticSettings.addWidget(self.jogSpeed, 0, 1)
  31. self.jogSpeedUnitText = QLabel("mm/min")
  32. staticSettings.addWidget(self.jogSpeedUnitText, 0, 2)
  33. self.jogLengthText = QLabel("Jog Length")
  34. staticSettings.addWidget(self.jogLengthText, 1, 0)
  35. self.jogLength = QLineEdit(str(self.parent.jogMoveLength))
  36. self.jogLength.setValidator(QIntValidator(1, 100))
  37. staticSettings.addWidget(self.jogLength, 1, 1)
  38. self.jogLengthUnitText = QLabel("mm")
  39. staticSettings.addWidget(self.jogLengthUnitText, 1, 2)
  40. helpText = "Usage:\n"
  41. helpText += "1st Column: Printer Hostname or IP address\n"
  42. helpText += "2nd Column: OctoPrint API Key (32 char hexadecimal)\n"
  43. helpText += "3rd Column: Tool Preheat Temperature (0 to disable)\n"
  44. helpText += "4th Column: Bed Preheat Temperature (0 to disable)"
  45. self.helpText = QLabel(helpText)
  46. box.addWidget(self.helpText, 0)
  47. box.setAlignment(self.helpText, Qt.AlignHCenter)
  48. buttons = QHBoxLayout()
  49. box.addLayout(buttons, 0)
  50. self.add = QPushButton("&Add Printer")
  51. self.add.clicked.connect(self.addPrinter)
  52. buttons.addWidget(self.add)
  53. self.remove = QPushButton("&Remove Printer")
  54. self.remove.clicked.connect(self.removePrinter)
  55. buttons.addWidget(self.remove)
  56. printers = self.parent.readSettings()
  57. self.rows = len(printers)
  58. self.table = QTableWidget(self.rows, len(self.columns))
  59. box.addWidget(self.table, 1)
  60. for i in range(0, self.rows):
  61. p = printers[i]
  62. item = QTableWidgetItem(p.host)
  63. self.table.setItem(i, 0, item)
  64. item = QTableWidgetItem(p.key)
  65. self.table.setItem(i, 1, item)
  66. font = item.font()
  67. font.setFamily(QFontDatabase.systemFont(QFontDatabase.FixedFont).family())
  68. item.setFont(font)
  69. if p.tempTool == None:
  70. item = QTableWidgetItem("0")
  71. else:
  72. item = QTableWidgetItem(p.tempTool)
  73. self.table.setItem(i, 2, item)
  74. if p.tempBed == None:
  75. item = QTableWidgetItem("0")
  76. else:
  77. item = QTableWidgetItem(p.tempBed)
  78. self.table.setItem(i, 3, item)
  79. buttons2 = QHBoxLayout()
  80. box.addLayout(buttons2, 0)
  81. self.up = QPushButton("Move &Up")
  82. self.up.clicked.connect(self.moveUp)
  83. buttons2.addWidget(self.up)
  84. self.down = QPushButton("Move &Down")
  85. self.down.clicked.connect(self.moveDown)
  86. buttons2.addWidget(self.down)
  87. self.openWeb = QPushButton("&Open Web UI of selected")
  88. self.openWeb.clicked.connect(self.openWebUI)
  89. box.addWidget(self.openWeb, 0)
  90. self.table.setHorizontalHeaderLabels(self.columns)
  91. self.table.setSizeAdjustPolicy(QtWidgets.QAbstractScrollArea.AdjustToContents)
  92. self.table.setSelectionBehavior(QtWidgets.QAbstractItemView.SelectRows);
  93. self.table.resizeColumnsToContents()
  94. if self.rows <= 0:
  95. self.addPrinter()
  96. def tableToList(self):
  97. printers = []
  98. for i in range(0, self.rows):
  99. p = Printer()
  100. p.host = self.table.item(i, 0).text()
  101. p.key = self.table.item(i, 1).text()
  102. p.tempTool = self.table.item(i, 2).text()
  103. p.tempBed = self.table.item(i, 3).text()
  104. if p.tempTool == "0":
  105. p.tempTool = None
  106. if p.tempBed == "0":
  107. p.tempBed = None
  108. printers.append(p)
  109. return printers
  110. def settingsValid(self, printers):
  111. for p in printers:
  112. # p.host needs to be valid hostname or IP
  113. # TODO
  114. # p.key needs to be valid API key (hexadecimal, 32 chars)
  115. if (len(p.key) != 32) or not all(c in string.hexdigits for c in p.key):
  116. return (False, "API Key not 32-digit hexadecimal")
  117. # p.tempTool and p.tempBed need to be integer temperatures (0...999)
  118. for s in [ p.tempTool, p.tempBed ]:
  119. if s == None:
  120. s = "0"
  121. if (len(s) < 1) or (len(s) > 3) or not all(c in string.digits for c in s):
  122. return (False, "Temperature not a number from 0...999")
  123. js = int(self.jogSpeed.text())
  124. if (js < 1) or (js > 6000):
  125. return (False, "Jog Speed not a number from 1...6000")
  126. jl = int(self.jogLength.text())
  127. if (jl < 1) or (jl > 100):
  128. return (False, "Jog Length not a number from 1...100")
  129. return (True, "")
  130. def printerDiffers(self, a, b):
  131. if (a.host != b.host) or (a.key != b.key) or (a.tempTool != b.tempTool) or (a.tempBed != b.tempBed):
  132. return True
  133. return False
  134. def printersDiffer(self, a, b):
  135. if (len(a) != len(b)):
  136. return True
  137. for i in range(0, len(a)):
  138. if self.printerDiffers(a[i], b[i]):
  139. return True
  140. return False
  141. def closeEvent(self, event):
  142. oldPrinters = self.parent.printers
  143. newPrinters = self.tableToList()
  144. valid, errorText = self.settingsValid(newPrinters)
  145. if valid == False:
  146. r = self.parent.showDialog(self.parent.name + " Settings Invalid", errorText + "!", "Do you want to edit it again?", True, True, False)
  147. if r == True:
  148. event.ignore()
  149. return
  150. else:
  151. self.parent.removeSettingsWindow()
  152. return
  153. js = int(self.jogSpeed.text())
  154. jl = int(self.jogLength.text())
  155. if self.printersDiffer(oldPrinters, newPrinters) or (js != self.parent.jogMoveSpeed) or (jl != self.parent.jogMoveLength):
  156. 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)
  157. if r == True:
  158. self.parent.jogMoveSpeed = js
  159. self.parent.jogMoveLength = jl
  160. self.parent.writeSettings(newPrinters)
  161. self.parent.restartApp()
  162. self.parent.removeSettingsWindow()
  163. def addPrinter(self):
  164. self.rows += 1
  165. self.table.setRowCount(self.rows)
  166. for i in range(0, len(self.columns)):
  167. item = QTableWidgetItem(self.presets[i])
  168. self.table.setItem(self.rows - 1, i, item)
  169. if i == 1:
  170. font = item.font()
  171. font.setFamily(QFontDatabase.systemFont(QFontDatabase.FixedFont).family())
  172. item.setFont(font)
  173. self.table.resizeColumnsToContents()
  174. self.table.setCurrentItem(self.table.item(self.rows - 1, 0))
  175. def removePrinter(self):
  176. r = self.table.currentRow()
  177. if (r >= 0) and (r < self.rows):
  178. self.rows -= 1
  179. self.table.removeRow(r)
  180. self.table.setCurrentItem(self.table.item(min(r, self.rows - 1), 0))
  181. def moveUp(self):
  182. i = self.table.currentRow()
  183. if i <= 0:
  184. return
  185. host = self.table.item(i, 0).text()
  186. key = self.table.item(i, 1).text()
  187. self.table.item(i, 0).setText(self.table.item(i - 1, 0).text())
  188. self.table.item(i, 1).setText(self.table.item(i - 1, 1).text())
  189. self.table.item(i - 1, 0).setText(host)
  190. self.table.item(i - 1, 1).setText(key)
  191. self.table.setCurrentItem(self.table.item(i - 1, 0))
  192. def moveDown(self):
  193. i = self.table.currentRow()
  194. if i >= (self.rows - 1):
  195. return
  196. host = self.table.item(i, 0).text()
  197. key = self.table.item(i, 1).text()
  198. self.table.item(i, 0).setText(self.table.item(i + 1, 0).text())
  199. self.table.item(i, 1).setText(self.table.item(i + 1, 1).text())
  200. self.table.item(i + 1, 0).setText(host)
  201. self.table.item(i + 1, 1).setText(key)
  202. self.table.setCurrentItem(self.table.item(i + 1, 0))
  203. def openWebUI(self):
  204. host = self.table.item(self.table.currentRow(), 0).text()
  205. self.parent.openBrowser(host)