OptionUI.py 9.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281
  1. from typing import Union, Sequence, List
  2. from PyQt5 import QtWidgets
  3. from flatcamGUI.GUIElements import RadioSet, FCCheckBox, FCButton, FCComboBox, FCEntry, FCSpinner, FCColorEntry, \
  4. FCSliderWithSpinner, FCDoubleSpinner, FloatEntry
  5. import gettext
  6. import FlatCAMTranslation as fcTranslate
  7. import builtins
  8. fcTranslate.apply_language('strings')
  9. if '_' not in builtins.__dict__:
  10. _ = gettext.gettext
  11. class OptionUI:
  12. def __init__(self, option: str):
  13. self.option = option
  14. def add_to_grid(self, grid: QtWidgets.QGridLayout, row: int) -> int:
  15. """
  16. Adds the necessary widget to the grid, starting at the supplied row.
  17. Returns the number of rows used (normally 1)
  18. """
  19. raise NotImplementedError()
  20. def get_field(self):
  21. raise NotImplementedError()
  22. class BasicOptionUI(OptionUI):
  23. """Abstract OptionUI that has a label on the left then some other widget on the right"""
  24. def __init__(self, option: str, label_text: str, label_tooltip: Union[str, None] = None, label_bold: bool = False, label_color: Union[str, None] = None):
  25. super().__init__(option=option)
  26. self.label_text = label_text
  27. self.label_tooltip = label_tooltip
  28. self.label_bold = label_bold
  29. self.label_color = label_color
  30. self.label_widget = self.build_label_widget()
  31. self.entry_widget = self.build_entry_widget()
  32. def build_label_widget(self) -> QtWidgets.QLabel:
  33. fmt = "%s:"
  34. if self.label_bold:
  35. fmt = "<b>%s</b>" % fmt
  36. if self.label_color:
  37. fmt = "<span style=\"color:%s;\">%s</span>" % (self.label_color, fmt)
  38. label_widget = QtWidgets.QLabel(fmt % _(self.label_text))
  39. if self.label_tooltip is not None:
  40. label_widget.setToolTip(_(self.label_tooltip))
  41. return label_widget
  42. def build_entry_widget(self) -> QtWidgets.QWidget:
  43. raise NotImplementedError()
  44. def add_to_grid(self, grid: QtWidgets.QGridLayout, row: int) -> int:
  45. grid.addWidget(self.label_widget, row, 0)
  46. grid.addWidget(self.entry_widget, row, 1)
  47. return 1
  48. def get_field(self):
  49. return self.entry_widget
  50. class LineEntryOptionUI(BasicOptionUI):
  51. def build_entry_widget(self) -> QtWidgets.QWidget:
  52. return FCEntry()
  53. # Not sure why this is needed over DoubleSpinnerOptionUI
  54. class FloatEntryOptionUI(BasicOptionUI):
  55. def build_entry_widget(self) -> QtWidgets.QWidget:
  56. return FloatEntry()
  57. class RadioSetOptionUI(BasicOptionUI):
  58. def __init__(self, option: str, label_text: str, choices: list, orientation='horizontal', **kwargs):
  59. self.choices = choices
  60. self.orientation = orientation
  61. super().__init__(option=option, label_text=label_text, **kwargs)
  62. def build_entry_widget(self) -> QtWidgets.QWidget:
  63. return RadioSet(choices=self.choices, orientation=self.orientation)
  64. class CheckboxOptionUI(OptionUI):
  65. def __init__(self, option: str, label_text: str, label_tooltip: str):
  66. super().__init__(option=option)
  67. self.label_text = label_text
  68. self.label_tooltip = label_tooltip
  69. self.checkbox_widget = self.build_checkbox_widget()
  70. def build_checkbox_widget(self):
  71. checkbox = FCCheckBox('%s' % _(self.label_text))
  72. checkbox.setToolTip(_(self.label_tooltip))
  73. return checkbox
  74. def add_to_grid(self, grid: QtWidgets.QGridLayout, row: int) -> int:
  75. grid.addWidget(self.checkbox_widget, row, 0, 1, 3)
  76. return 1
  77. def get_field(self):
  78. return self.checkbox_widget
  79. class ComboboxOptionUI(BasicOptionUI):
  80. def __init__(self, option: str, label_text: str, choices: Sequence, **kwargs):
  81. self.choices = choices
  82. super().__init__(option=option, label_text=label_text, **kwargs)
  83. def build_entry_widget(self):
  84. combo = FCComboBox()
  85. for choice in self.choices:
  86. # don't translate the QCombo items as they are used in QSettings and identified by name
  87. combo.addItem(choice)
  88. return combo
  89. class ColorOptionUI(BasicOptionUI):
  90. def build_entry_widget(self) -> QtWidgets.QWidget:
  91. entry = FCColorEntry()
  92. return entry
  93. class SliderWithSpinnerOptionUI(BasicOptionUI):
  94. def __init__(self, option: str, label_text: str, min_value=0, max_value=100, step=1, **kwargs):
  95. self.min_value = min_value
  96. self.max_value = max_value
  97. self.step = step
  98. super().__init__(option=option, label_text=label_text, **kwargs)
  99. def build_entry_widget(self) -> QtWidgets.QWidget:
  100. entry = FCSliderWithSpinner(min=self.min_value, max=self.max_value, step=self.step)
  101. return entry
  102. class ColorAlphaSliderOptionUI(SliderWithSpinnerOptionUI):
  103. def __init__(self, applies_to: List[str], group, label_text: str, **kwargs):
  104. self.applies_to = applies_to
  105. self.group = group
  106. super().__init__(option="__color_alpha_slider", label_text=label_text, min_value=0, max_value=255, step=1, **kwargs)
  107. self.get_field().valueChanged.connect(self._on_alpha_change)
  108. def add_to_grid(self, grid: QtWidgets.QGridLayout, row: int) -> int:
  109. for index, field in enumerate(self._get_target_fields()):
  110. field.entry.textChanged.connect(lambda value, i=index: self._on_target_change(target_index=i))
  111. return super().add_to_grid(grid, row)
  112. def _get_target_fields(self):
  113. return list(map(lambda n: self.group.option_dict()[n].get_field(), self.applies_to))
  114. def _on_target_change(self, target_index: int):
  115. field = self._get_target_fields()[target_index]
  116. color = field.get_value()
  117. alpha_part = color[7:]
  118. if len(alpha_part) != 2:
  119. return
  120. alpha = int(alpha_part, 16)
  121. if alpha < 0 or alpha > 255 or self.get_field().get_value() == alpha:
  122. return
  123. self.get_field().set_value(alpha)
  124. def _on_alpha_change(self):
  125. alpha = self.get_field().get_value()
  126. for field in self._get_target_fields():
  127. old_value = field.get_value()
  128. new_value = self._modify_color_alpha(old_value, alpha=alpha)
  129. field.set_value(new_value)
  130. def _modify_color_alpha(self, color: str, alpha: int):
  131. color_without_alpha = color[:7]
  132. if alpha > 255:
  133. return color_without_alpha + "FF"
  134. elif alpha < 0:
  135. return color_without_alpha + "00"
  136. else:
  137. hexalpha = hex(alpha)[2:]
  138. if len(hexalpha) == 1:
  139. hexalpha = "0" + hexalpha
  140. return color_without_alpha + hexalpha
  141. class SpinnerOptionUI(BasicOptionUI):
  142. def __init__(self, option: str, label_text: str, min_value: int, max_value: int, step: int = 1, **kwargs):
  143. self.min_value = min_value
  144. self.max_value = max_value
  145. self.step = step
  146. super().__init__(option=option, label_text=label_text, **kwargs)
  147. def build_entry_widget(self) -> QtWidgets.QWidget:
  148. entry = FCSpinner()
  149. entry.set_range(self.min_value, self.max_value)
  150. entry.set_step(self.step)
  151. entry.setWrapping(True)
  152. return entry
  153. class DoubleSpinnerOptionUI(BasicOptionUI):
  154. def __init__(self, option: str, label_text: str, step: float, decimals: int, min_value=None, max_value=None, suffix=None, **kwargs):
  155. self.min_value = min_value
  156. self.max_value = max_value
  157. self.step = step
  158. self.suffix = suffix
  159. self.decimals = decimals
  160. super().__init__(option=option, label_text=label_text, **kwargs)
  161. def build_entry_widget(self) -> QtWidgets.QWidget:
  162. entry = FCDoubleSpinner(suffix=self.suffix)
  163. entry.set_precision(self.decimals)
  164. entry.setSingleStep(self.step)
  165. if self.min_value is None:
  166. self.min_value = entry.minimum()
  167. else:
  168. entry.setMinimum(self.min_value)
  169. if self.max_value is None:
  170. self.max_value = entry.maximum()
  171. else:
  172. entry.setMaximum(self.max_value)
  173. return entry
  174. class HeadingOptionUI(OptionUI):
  175. def __init__(self, label_text: str, label_tooltip: Union[str, None] = None):
  176. super().__init__(option="__heading")
  177. self.label_text = label_text
  178. self.label_tooltip = label_tooltip
  179. def build_heading_widget(self):
  180. heading = QtWidgets.QLabel('<b>%s</b>' % _(self.label_text))
  181. heading.setToolTip(_(self.label_tooltip))
  182. return heading
  183. def add_to_grid(self, grid: QtWidgets.QGridLayout, row: int) -> int:
  184. grid.addWidget(self.build_heading_widget(), row, 0, 1, 2)
  185. return 1
  186. def get_field(self):
  187. return None
  188. class SeparatorOptionUI(OptionUI):
  189. def __init__(self):
  190. super().__init__(option="__separator")
  191. def build_separator_widget(self):
  192. separator = QtWidgets.QFrame()
  193. separator.setFrameShape(QtWidgets.QFrame.HLine)
  194. separator.setFrameShadow(QtWidgets.QFrame.Sunken)
  195. return separator
  196. def add_to_grid(self, grid: QtWidgets.QGridLayout, row: int) -> int:
  197. grid.addWidget(self.build_separator_widget(), row, 0, 1, 2)
  198. return 1
  199. def get_field(self):
  200. return None
  201. class FullWidthButtonOptionUI(OptionUI):
  202. def __init__(self, option: str, label_text: str, label_tooltip: str):
  203. super().__init__(option=option)
  204. self.label_text = label_text
  205. self.label_tooltip = label_tooltip
  206. self.button_widget = self.build_button_widget()
  207. def build_button_widget(self):
  208. button = FCButton(_(self.label_text))
  209. button.setToolTip(_(self.label_tooltip))
  210. return button
  211. def add_to_grid(self, grid: QtWidgets.QGridLayout, row: int) -> int:
  212. grid.addWidget(self.button_widget, row, 0, 1, 3)
  213. return 1
  214. def get_field(self):
  215. return self.button_widget