GUIElements.py 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407
  1. from PyQt4 import QtGui, QtCore
  2. from copy import copy
  3. #import FlatCAMApp
  4. import re
  5. import logging
  6. log = logging.getLogger('base')
  7. class RadioSet(QtGui.QWidget):
  8. def __init__(self, choices, orientation='horizontal', parent=None):
  9. """
  10. The choices are specified as a list of dictionaries containing:
  11. * 'label': Shown in the UI
  12. * 'value': The value returned is selected
  13. :param choices: List of choices. See description.
  14. :param orientation: 'horizontal' (default) of 'vertical'.
  15. :param parent: Qt parent widget.
  16. :type choices: list
  17. """
  18. super(RadioSet, self).__init__(parent)
  19. self.choices = copy(choices)
  20. if orientation == 'horizontal':
  21. layout = QtGui.QHBoxLayout()
  22. else:
  23. layout = QtGui.QVBoxLayout()
  24. group = QtGui.QButtonGroup(self)
  25. for choice in self.choices:
  26. choice['radio'] = QtGui.QRadioButton(choice['label'])
  27. group.addButton(choice['radio'])
  28. layout.addWidget(choice['radio'], stretch=0)
  29. choice['radio'].toggled.connect(self.on_toggle)
  30. layout.addStretch()
  31. self.setLayout(layout)
  32. self.group_toggle_fn = lambda: None
  33. def on_toggle(self):
  34. log.debug("Radio toggled")
  35. radio = self.sender()
  36. if radio.isChecked():
  37. self.group_toggle_fn()
  38. return
  39. def get_value(self):
  40. for choice in self.choices:
  41. if choice['radio'].isChecked():
  42. return choice['value']
  43. log.error("No button was toggled in RadioSet.")
  44. return None
  45. def set_value(self, val):
  46. for choice in self.choices:
  47. if choice['value'] == val:
  48. choice['radio'].setChecked(True)
  49. return
  50. log.error("Value given is not part of this RadioSet: %s" % str(val))
  51. class LengthEntry(QtGui.QLineEdit):
  52. def __init__(self, output_units='IN', parent=None):
  53. super(LengthEntry, self).__init__(parent)
  54. self.output_units = output_units
  55. self.format_re = re.compile(r"^([^\s]+)(?:\s([a-zA-Z]+))?$")
  56. # Unit conversion table OUTPUT-INPUT
  57. self.scales = {
  58. 'IN': {'IN': 1.0,
  59. 'MM': 1/25.4},
  60. 'MM': {'IN': 25.4,
  61. 'MM': 1.0}
  62. }
  63. self.readyToEdit = True
  64. def mousePressEvent(self, e, Parent=None):
  65. # required to deselect on 2nd click
  66. super(LengthEntry, self).mousePressEvent(e)
  67. if self.readyToEdit:
  68. self.selectAll()
  69. self.readyToEdit = False
  70. def focusOutEvent(self, e):
  71. # required to remove cursor on focusOut
  72. super(LengthEntry, self).focusOutEvent(e)
  73. self.deselect()
  74. self.readyToEdit = True
  75. def returnPressed(self, *args, **kwargs):
  76. val = self.get_value()
  77. if val is not None:
  78. self.set_text(str(val))
  79. else:
  80. log.warning("Could not interpret entry: %s" % self.get_text())
  81. def get_value(self):
  82. raw = str(self.text()).strip(' ')
  83. # match = self.format_re.search(raw)
  84. try:
  85. units = raw[-2:]
  86. units = self.scales[self.output_units][units.upper()]
  87. value = raw[:-2]
  88. return float(eval(value))*units
  89. except IndexError:
  90. value = raw
  91. return float(eval(value))
  92. except KeyError:
  93. value = raw
  94. return float(eval(value))
  95. except:
  96. log.warning("Could not parse value in entry: %s" % str(raw))
  97. return None
  98. def set_value(self, val):
  99. self.setText(str(val))
  100. class FloatEntry(QtGui.QLineEdit):
  101. def __init__(self, parent=None):
  102. super(FloatEntry, self).__init__(parent)
  103. self.readyToEdit = True
  104. def mousePressEvent(self, e, Parent=None):
  105. # required to deselect on 2nd click
  106. super(FloatEntry, self).mousePressEvent(e)
  107. if self.readyToEdit:
  108. self.selectAll()
  109. self.readyToEdit = False
  110. def focusOutEvent(self, e):
  111. # required to remove cursor on focusOut
  112. super(FloatEntry, self).focusOutEvent(e)
  113. self.deselect()
  114. self.readyToEdit = True
  115. def returnPressed(self, *args, **kwargs):
  116. val = self.get_value()
  117. if val is not None:
  118. self.set_text(str(val))
  119. else:
  120. log.warning("Could not interpret entry: %s" % self.text())
  121. def get_value(self):
  122. raw = str(self.text()).strip(' ')
  123. try:
  124. evaled = eval(raw)
  125. except:
  126. log.error("Could not evaluate: %s" % str(raw))
  127. return None
  128. return float(evaled)
  129. def set_value(self, val):
  130. self.setText("%.6f" % val)
  131. class IntEntry(QtGui.QLineEdit):
  132. def __init__(self, parent=None, allow_empty=False, empty_val=None):
  133. super(IntEntry, self).__init__(parent)
  134. self.allow_empty = allow_empty
  135. self.empty_val = empty_val
  136. self.readyToEdit = True
  137. def mousePressEvent(self, e, Parent=None):
  138. # required to deselect on 2nd click
  139. super(IntEntry, self).mousePressEvent(e)
  140. if self.readyToEdit:
  141. self.selectAll()
  142. self.readyToEdit = False
  143. def focusOutEvent(self, e):
  144. # required to remove cursor on focusOut
  145. super(IntEntry, self).focusOutEvent(e)
  146. self.deselect()
  147. self.readyToEdit = True
  148. def get_value(self):
  149. if self.allow_empty:
  150. if str(self.text()) == "":
  151. return self.empty_val
  152. return int(self.text())
  153. def set_value(self, val):
  154. if val == self.empty_val and self.allow_empty:
  155. self.setText("")
  156. return
  157. self.setText(str(val))
  158. class FCEntry(QtGui.QLineEdit):
  159. def __init__(self, parent=None):
  160. super(FCEntry, self).__init__(parent)
  161. self.readyToEdit = True
  162. def mousePressEvent(self, e, Parent=None):
  163. # required to deselect on 2nd click
  164. super(FCEntry, self).mousePressEvent(e)
  165. if self.readyToEdit:
  166. self.selectAll()
  167. self.readyToEdit = False
  168. def focusOutEvent(self, e):
  169. # required to remove cursor on focusOut
  170. super(FCEntry, self).focusOutEvent(e)
  171. self.deselect()
  172. self.readyToEdit = True
  173. def get_value(self):
  174. return str(self.text())
  175. def set_value(self, val):
  176. self.setText(str(val))
  177. class EvalEntry(QtGui.QLineEdit):
  178. def __init__(self, parent=None):
  179. super(EvalEntry, self).__init__(parent)
  180. self.readyToEdit = True
  181. def mousePressEvent(self, e, Parent=None):
  182. # required to deselect on 2nd click
  183. super(EvalEntry, self).mousePressEvent(e)
  184. if self.readyToEdit:
  185. self.selectAll()
  186. self.readyToEdit = False
  187. def focusOutEvent(self, e):
  188. # required to remove cursor on focusOut
  189. super(EvalEntry, self).focusOutEvent(e)
  190. self.deselect()
  191. self.readyToEdit = True
  192. def returnPressed(self, *args, **kwargs):
  193. val = self.get_value()
  194. if val is not None:
  195. self.setText(str(val))
  196. else:
  197. log.warning("Could not interpret entry: %s" % self.get_text())
  198. def get_value(self):
  199. raw = str(self.text()).strip(' ')
  200. try:
  201. return eval(raw)
  202. except:
  203. log.error("Could not evaluate: %s" % str(raw))
  204. return None
  205. def set_value(self, val):
  206. self.setText(str(val))
  207. class FCCheckBox(QtGui.QCheckBox):
  208. def __init__(self, label='', parent=None):
  209. super(FCCheckBox, self).__init__(str(label), parent)
  210. def get_value(self):
  211. return self.isChecked()
  212. def set_value(self, val):
  213. self.setChecked(val)
  214. def toggle(self):
  215. self.set_value(not self.get_value())
  216. class FCTextArea(QtGui.QPlainTextEdit):
  217. def __init__(self, parent=None):
  218. super(FCTextArea, self).__init__(parent)
  219. def set_value(self, val):
  220. self.setPlainText(val)
  221. def get_value(self):
  222. return str(self.toPlainText())
  223. class FCInputDialog(QtGui.QInputDialog):
  224. def __init__(self, parent=None, ok=False, val=None):
  225. super(FCInputDialog, self).__init__(parent)
  226. self.allow_empty = ok
  227. self.empty_val = val
  228. self.readyToEdit = True
  229. def mousePressEvent(self, e, Parent=None):
  230. # required to deselect on 2nd click
  231. super(FCInputDialog, self).mousePressEvent(e)
  232. if self.readyToEdit:
  233. self.selectAll()
  234. self.readyToEdit = False
  235. def focusOutEvent(self, e):
  236. # required to remove cursor on focusOut
  237. super(FCInputDialog, self).focusOutEvent(e)
  238. self.deselect()
  239. self.readyToEdit = True
  240. def get_value(self, title=None, message=None, min=None, max=None, decimals=None):
  241. if title is None:
  242. title = "FlatCAM action"
  243. if message is None:
  244. message = "Please enter the value: "
  245. if min is None:
  246. min = 0.0
  247. if max is None:
  248. max = 100.0
  249. if decimals is None:
  250. decimals = 1
  251. self.val,self.ok = self.getDouble(self, title, message, min=min,
  252. max=max, decimals=decimals)
  253. return [self.val,self.ok]
  254. def set_value(self, val):
  255. pass
  256. class FCButton(QtGui.QPushButton):
  257. def __init__(self, parent=None):
  258. super(FCButton, self).__init__(parent)
  259. def get_value(self):
  260. return self.isChecked()
  261. def set_value(self, val):
  262. self.setText(str(val))
  263. class VerticalScrollArea(QtGui.QScrollArea):
  264. """
  265. This widget extends QtGui.QScrollArea to make a vertical-only
  266. scroll area that also expands horizontally to accomodate
  267. its contents.
  268. """
  269. def __init__(self, parent=None):
  270. QtGui.QScrollArea.__init__(self, parent=parent)
  271. self.setWidgetResizable(True)
  272. self.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff)
  273. self.setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarAsNeeded)
  274. def eventFilter(self, source, event):
  275. """
  276. The event filter gets automatically installed when setWidget()
  277. is called.
  278. :param source:
  279. :param event:
  280. :return:
  281. """
  282. if event.type() == QtCore.QEvent.Resize and source == self.widget():
  283. # log.debug("VerticalScrollArea: Widget resized:")
  284. # log.debug(" minimumSizeHint().width() = %d" % self.widget().minimumSizeHint().width())
  285. # log.debug(" verticalScrollBar().width() = %d" % self.verticalScrollBar().width())
  286. self.setMinimumWidth(self.widget().sizeHint().width() +
  287. self.verticalScrollBar().sizeHint().width())
  288. # if self.verticalScrollBar().isVisible():
  289. # log.debug(" Scroll bar visible")
  290. # self.setMinimumWidth(self.widget().minimumSizeHint().width() +
  291. # self.verticalScrollBar().width())
  292. # else:
  293. # log.debug(" Scroll bar hidden")
  294. # self.setMinimumWidth(self.widget().minimumSizeHint().width())
  295. return QtGui.QWidget.eventFilter(self, source, event)
  296. class OptionalInputSection:
  297. def __init__(self, cb, optinputs):
  298. """
  299. Associates the a checkbox with a set of inputs.
  300. :param cb: Checkbox that enables the optional inputs.
  301. :param optinputs: List of widgets that are optional.
  302. :return:
  303. """
  304. assert isinstance(cb, FCCheckBox), \
  305. "Expected an FCCheckBox, got %s" % type(cb)
  306. self.cb = cb
  307. self.optinputs = optinputs
  308. self.on_cb_change()
  309. self.cb.stateChanged.connect(self.on_cb_change)
  310. def on_cb_change(self):
  311. if self.cb.checkState():
  312. for widget in self.optinputs:
  313. widget.setEnabled(True)
  314. else:
  315. for widget in self.optinputs:
  316. widget.setEnabled(False)