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 8.4KB

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