cgse-gui 2024.1.4__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (124) hide show
  1. cgse_gui-2024.1.4/.gitignore +35 -0
  2. cgse_gui-2024.1.4/PKG-INFO +13 -0
  3. cgse_gui-2024.1.4/README.md +1 -0
  4. cgse_gui-2024.1.4/pyproject.toml +37 -0
  5. cgse_gui-2024.1.4/src/egse/gui/__init__.py +56 -0
  6. cgse_gui-2024.1.4/src/egse/gui/buttons.py +378 -0
  7. cgse_gui-2024.1.4/src/egse/gui/icons/aeu-cs-start.svg +117 -0
  8. cgse_gui-2024.1.4/src/egse/gui/icons/aeu-cs-stop.svg +118 -0
  9. cgse_gui-2024.1.4/src/egse/gui/icons/aeu-cs.svg +107 -0
  10. cgse_gui-2024.1.4/src/egse/gui/icons/aeu_cs-started.svg +112 -0
  11. cgse_gui-2024.1.4/src/egse/gui/icons/aeu_cs-stopped.svg +112 -0
  12. cgse_gui-2024.1.4/src/egse/gui/icons/aeu_cs.svg +55 -0
  13. cgse_gui-2024.1.4/src/egse/gui/icons/alert.svg +1 -0
  14. cgse_gui-2024.1.4/src/egse/gui/icons/arrow-double-left.png +0 -0
  15. cgse_gui-2024.1.4/src/egse/gui/icons/arrow-double-right.png +0 -0
  16. cgse_gui-2024.1.4/src/egse/gui/icons/arrow-up.svg +11 -0
  17. cgse_gui-2024.1.4/src/egse/gui/icons/backward.svg +1 -0
  18. cgse_gui-2024.1.4/src/egse/gui/icons/busy.svg +1 -0
  19. cgse_gui-2024.1.4/src/egse/gui/icons/cleaning.svg +115 -0
  20. cgse_gui-2024.1.4/src/egse/gui/icons/color-scheme.svg +1 -0
  21. cgse_gui-2024.1.4/src/egse/gui/icons/cs-connected-alert.svg +91 -0
  22. cgse_gui-2024.1.4/src/egse/gui/icons/cs-connected-disabled.svg +43 -0
  23. cgse_gui-2024.1.4/src/egse/gui/icons/cs-connected.svg +89 -0
  24. cgse_gui-2024.1.4/src/egse/gui/icons/cs-not-connected.svg +44 -0
  25. cgse_gui-2024.1.4/src/egse/gui/icons/double-left-arrow.svg +1 -0
  26. cgse_gui-2024.1.4/src/egse/gui/icons/double-right-arrow.svg +1 -0
  27. cgse_gui-2024.1.4/src/egse/gui/icons/erase-disabled.svg +19 -0
  28. cgse_gui-2024.1.4/src/egse/gui/icons/erase.svg +59 -0
  29. cgse_gui-2024.1.4/src/egse/gui/icons/fitsgen-start.svg +47 -0
  30. cgse_gui-2024.1.4/src/egse/gui/icons/fitsgen-stop.svg +48 -0
  31. cgse_gui-2024.1.4/src/egse/gui/icons/fitsgen.svg +1 -0
  32. cgse_gui-2024.1.4/src/egse/gui/icons/forward.svg +1 -0
  33. cgse_gui-2024.1.4/src/egse/gui/icons/fov-hk-start.svg +33 -0
  34. cgse_gui-2024.1.4/src/egse/gui/icons/fov-hk-stop.svg +37 -0
  35. cgse_gui-2024.1.4/src/egse/gui/icons/fov-hk.svg +1 -0
  36. cgse_gui-2024.1.4/src/egse/gui/icons/front-desk.svg +1 -0
  37. cgse_gui-2024.1.4/src/egse/gui/icons/home-actioned.svg +15 -0
  38. cgse_gui-2024.1.4/src/egse/gui/icons/home-disabled.svg +15 -0
  39. cgse_gui-2024.1.4/src/egse/gui/icons/home.svg +13 -0
  40. cgse_gui-2024.1.4/src/egse/gui/icons/info.svg +1 -0
  41. cgse_gui-2024.1.4/src/egse/gui/icons/invalid.png +0 -0
  42. cgse_gui-2024.1.4/src/egse/gui/icons/led-green.svg +20 -0
  43. cgse_gui-2024.1.4/src/egse/gui/icons/led-grey.svg +20 -0
  44. cgse_gui-2024.1.4/src/egse/gui/icons/led-orange.svg +20 -0
  45. cgse_gui-2024.1.4/src/egse/gui/icons/led-red.svg +20 -0
  46. cgse_gui-2024.1.4/src/egse/gui/icons/led-square-green.svg +134 -0
  47. cgse_gui-2024.1.4/src/egse/gui/icons/led-square-grey.svg +134 -0
  48. cgse_gui-2024.1.4/src/egse/gui/icons/led-square-orange.svg +134 -0
  49. cgse_gui-2024.1.4/src/egse/gui/icons/led-square-red.svg +134 -0
  50. cgse_gui-2024.1.4/src/egse/gui/icons/limit-switch-all-green.svg +115 -0
  51. cgse_gui-2024.1.4/src/egse/gui/icons/limit-switch-all-red.svg +117 -0
  52. cgse_gui-2024.1.4/src/egse/gui/icons/limit-switch-el+.svg +116 -0
  53. cgse_gui-2024.1.4/src/egse/gui/icons/limit-switch-el-.svg +117 -0
  54. cgse_gui-2024.1.4/src/egse/gui/icons/location-marker.svg +1 -0
  55. cgse_gui-2024.1.4/src/egse/gui/icons/logo-dpu.svg +48 -0
  56. cgse_gui-2024.1.4/src/egse/gui/icons/logo-gimbal.svg +112 -0
  57. cgse_gui-2024.1.4/src/egse/gui/icons/logo-huber.svg +23 -0
  58. cgse_gui-2024.1.4/src/egse/gui/icons/logo-ogse.svg +31 -0
  59. cgse_gui-2024.1.4/src/egse/gui/icons/logo-puna.svg +92 -0
  60. cgse_gui-2024.1.4/src/egse/gui/icons/logo-tcs.svg +29 -0
  61. cgse_gui-2024.1.4/src/egse/gui/icons/logo-zonda.svg +66 -0
  62. cgse_gui-2024.1.4/src/egse/gui/icons/maximize.svg +1 -0
  63. cgse_gui-2024.1.4/src/egse/gui/icons/meter.svg +1 -0
  64. cgse_gui-2024.1.4/src/egse/gui/icons/more.svg +45 -0
  65. cgse_gui-2024.1.4/src/egse/gui/icons/n-fee-hk-start.svg +24 -0
  66. cgse_gui-2024.1.4/src/egse/gui/icons/n-fee-hk-stop.svg +25 -0
  67. cgse_gui-2024.1.4/src/egse/gui/icons/n-fee-hk.svg +83 -0
  68. cgse_gui-2024.1.4/src/egse/gui/icons/observing-off.svg +46 -0
  69. cgse_gui-2024.1.4/src/egse/gui/icons/observing-on.svg +46 -0
  70. cgse_gui-2024.1.4/src/egse/gui/icons/open-document-hdf5.png +0 -0
  71. cgse_gui-2024.1.4/src/egse/gui/icons/open-document-hdf5.svg +21 -0
  72. cgse_gui-2024.1.4/src/egse/gui/icons/ops-mode.svg +1 -0
  73. cgse_gui-2024.1.4/src/egse/gui/icons/play-green.svg +17 -0
  74. cgse_gui-2024.1.4/src/egse/gui/icons/plugged-disabled.svg +27 -0
  75. cgse_gui-2024.1.4/src/egse/gui/icons/plugged.svg +21 -0
  76. cgse_gui-2024.1.4/src/egse/gui/icons/pm_ui.svg +1 -0
  77. cgse_gui-2024.1.4/src/egse/gui/icons/power-button-green.svg +27 -0
  78. cgse_gui-2024.1.4/src/egse/gui/icons/power-button-red.svg +27 -0
  79. cgse_gui-2024.1.4/src/egse/gui/icons/power-button.svg +27 -0
  80. cgse_gui-2024.1.4/src/egse/gui/icons/radar.svg +1 -0
  81. cgse_gui-2024.1.4/src/egse/gui/icons/radioactive.svg +2 -0
  82. cgse_gui-2024.1.4/src/egse/gui/icons/reload.svg +1 -0
  83. cgse_gui-2024.1.4/src/egse/gui/icons/remote-control-off.svg +28 -0
  84. cgse_gui-2024.1.4/src/egse/gui/icons/remote-control-on.svg +28 -0
  85. cgse_gui-2024.1.4/src/egse/gui/icons/repeat-blue.svg +15 -0
  86. cgse_gui-2024.1.4/src/egse/gui/icons/repeat.svg +1 -0
  87. cgse_gui-2024.1.4/src/egse/gui/icons/settings.svg +1 -0
  88. cgse_gui-2024.1.4/src/egse/gui/icons/shrink.svg +1 -0
  89. cgse_gui-2024.1.4/src/egse/gui/icons/shutter.svg +1 -0
  90. cgse_gui-2024.1.4/src/egse/gui/icons/sign-off.svg +1 -0
  91. cgse_gui-2024.1.4/src/egse/gui/icons/sign-on.svg +1 -0
  92. cgse_gui-2024.1.4/src/egse/gui/icons/sim-mode.svg +1 -0
  93. cgse_gui-2024.1.4/src/egse/gui/icons/small-buttons-go.svg +20 -0
  94. cgse_gui-2024.1.4/src/egse/gui/icons/small-buttons-minus.svg +51 -0
  95. cgse_gui-2024.1.4/src/egse/gui/icons/small-buttons-plus.svg +51 -0
  96. cgse_gui-2024.1.4/src/egse/gui/icons/sponge.svg +220 -0
  97. cgse_gui-2024.1.4/src/egse/gui/icons/start-button-disabled.svg +84 -0
  98. cgse_gui-2024.1.4/src/egse/gui/icons/start-button.svg +50 -0
  99. cgse_gui-2024.1.4/src/egse/gui/icons/stop-button-disabled.svg +84 -0
  100. cgse_gui-2024.1.4/src/egse/gui/icons/stop-button.svg +50 -0
  101. cgse_gui-2024.1.4/src/egse/gui/icons/stop-red.svg +17 -0
  102. cgse_gui-2024.1.4/src/egse/gui/icons/stop.svg +1 -0
  103. cgse_gui-2024.1.4/src/egse/gui/icons/switch-disabled-square.svg +87 -0
  104. cgse_gui-2024.1.4/src/egse/gui/icons/switch-disabled.svg +15 -0
  105. cgse_gui-2024.1.4/src/egse/gui/icons/switch-off-square.svg +87 -0
  106. cgse_gui-2024.1.4/src/egse/gui/icons/switch-off.svg +72 -0
  107. cgse_gui-2024.1.4/src/egse/gui/icons/switch-on-square.svg +87 -0
  108. cgse_gui-2024.1.4/src/egse/gui/icons/switch-on.svg +61 -0
  109. cgse_gui-2024.1.4/src/egse/gui/icons/temperature-control.svg +44 -0
  110. cgse_gui-2024.1.4/src/egse/gui/icons/th_ui_logo.svg +1 -0
  111. cgse_gui-2024.1.4/src/egse/gui/icons/unplugged.svg +23 -0
  112. cgse_gui-2024.1.4/src/egse/gui/icons/unvalid.png +0 -0
  113. cgse_gui-2024.1.4/src/egse/gui/icons/user-interface.svg +1 -0
  114. cgse_gui-2024.1.4/src/egse/gui/icons/vacuum.svg +1 -0
  115. cgse_gui-2024.1.4/src/egse/gui/icons/valid.png +0 -0
  116. cgse_gui-2024.1.4/src/egse/gui/icons/zoom-to-pixel-dark.svg +64 -0
  117. cgse_gui-2024.1.4/src/egse/gui/icons/zoom-to-pixel-white.svg +36 -0
  118. cgse_gui-2024.1.4/src/egse/gui/led.py +162 -0
  119. cgse_gui-2024.1.4/src/egse/gui/limitswitch.py +143 -0
  120. cgse_gui-2024.1.4/src/egse/gui/states.py +148 -0
  121. cgse_gui-2024.1.4/src/egse/gui/styles/dark.qss +343 -0
  122. cgse_gui-2024.1.4/src/egse/gui/styles/default.qss +48 -0
  123. cgse_gui-2024.1.4/src/egse/gui/styles.qss +48 -0
  124. cgse_gui-2024.1.4/src/egse/gui/switch.py +108 -0
@@ -0,0 +1,35 @@
1
+ # Python versions and environment
2
+
3
+ __pycache__
4
+ .python-version
5
+ .envrc
6
+
7
+ # Build systems
8
+
9
+ build
10
+ dist
11
+ **/*.egg-info
12
+
13
+ # Apple specific
14
+
15
+ .DS_Store
16
+
17
+ # Unit testing
18
+
19
+ .pytest_cache
20
+ .coverage
21
+ htmlcov
22
+
23
+ # Virtual environments
24
+
25
+ .env
26
+ .venv
27
+ venv
28
+
29
+ # PyCharm IDE
30
+
31
+ .idea
32
+
33
+ # Packaging
34
+
35
+ uv.lock
@@ -0,0 +1,13 @@
1
+ Metadata-Version: 2.4
2
+ Name: cgse-gui
3
+ Version: 2024.1.4
4
+ Summary: GUI components for CGSE
5
+ Author-email: Rik Huygen <rik.huygen@kuleuven.be>, Sara Regibo <sara.regibo@kuleuven.be>
6
+ License-Expression: MIT
7
+ Keywords: CGSE,Common-EGSE,GUI
8
+ Requires-Python: >=3.9
9
+ Requires-Dist: cgse-common
10
+ Requires-Dist: pyqt5>=5.15.11
11
+ Description-Content-Type: text/markdown
12
+
13
+ # GUI Components used in CGSE apps
@@ -0,0 +1 @@
1
+ # GUI Components used in CGSE apps
@@ -0,0 +1,37 @@
1
+ [project]
2
+ name = "cgse-gui"
3
+ version = "2024.1.4"
4
+ description = "GUI components for CGSE"
5
+ authors = [
6
+ {name = "Rik Huygen", email = "rik.huygen@kuleuven.be"},
7
+ {name = "Sara Regibo", email = "sara.regibo@kuleuven.be"}
8
+ ]
9
+ readme = {"file" = "README.md", "content-type" = "text/markdown"}
10
+ requires-python = ">=3.9"
11
+ license = "MIT"
12
+ keywords = [
13
+ "CGSE",
14
+ "Common-EGSE",
15
+ "GUI",
16
+ ]
17
+ dependencies = [
18
+ "cgse-common",
19
+ "PyQt5>=5.15.11",
20
+ ]
21
+
22
+ [project.entry-points."cgse.version"]
23
+ cgse-gui = 'egse.plugins'
24
+
25
+ [project.entry-points."cgse.resource"]
26
+ icons = 'egse.gui.icons'
27
+ styles = 'egse.gui.styles'
28
+
29
+ [tool.hatch.build.targets.sdist]
30
+ exclude = []
31
+
32
+ [tool.hatch.build.targets.wheel]
33
+ packages = ["src/egse"]
34
+
35
+ [build-system]
36
+ requires = ["hatchling"]
37
+ build-backend = "hatchling.build"
@@ -0,0 +1,56 @@
1
+ from pathlib import Path
2
+
3
+ from PyQt5.QtWidgets import QFrame
4
+ from PyQt5.QtWidgets import QMessageBox
5
+
6
+ from egse.resource import initialise_resources
7
+
8
+ # Load the resources that are defined by this package, we do not have resource ids other than those
9
+ # defined by default, therefore no need to call add_resource_id().
10
+
11
+ initialise_resources(Path(__file__).parent)
12
+
13
+
14
+ def show_warning_message(description: str, info_text: str = None, detailed_text: str = None):
15
+ msg_box = QMessageBox()
16
+
17
+ msg_box.setWindowTitle("Warning")
18
+ msg_box.setIcon(QMessageBox.Warning)
19
+ msg_box.setText(description)
20
+ if info_text is not None:
21
+ msg_box.setInformativeText(info_text)
22
+ if detailed_text is not None:
23
+ msg_box.setDetailedText(detailed_text)
24
+ msg_box.setStandardButtons(QMessageBox.Ok)
25
+ msg_box.exec_()
26
+
27
+
28
+ def show_info_message(description: str, info_text: str = None, detailed_text: str = None):
29
+ msg_box = QMessageBox()
30
+
31
+ msg_box.setWindowTitle("Warning")
32
+ msg_box.setIcon(QMessageBox.Information)
33
+ msg_box.setText(description)
34
+ if info_text is not None:
35
+ msg_box.setInformativeText(info_text)
36
+ if detailed_text is not None:
37
+ msg_box.setDetailedText(detailed_text)
38
+ msg_box.setStandardButtons(QMessageBox.Ok)
39
+ msg_box.exec_()
40
+
41
+
42
+ class VLine(QFrame):
43
+ """Presents a simple Vertical Bar that can be used in e.g. the status bar."""
44
+
45
+ def __init__(self):
46
+ super().__init__()
47
+ self.setFrameShape(self.VLine | self.Sunken)
48
+
49
+
50
+ class QHLine(QFrame):
51
+ """Presents a simple Horizontal Bar that can be used in e.g. the status bar."""
52
+
53
+ def __init__(self):
54
+ super(QHLine, self). __init__()
55
+ self.setFrameShape(QFrame.HLine)
56
+ self.setFrameShadow(QFrame.Sunken)
@@ -0,0 +1,378 @@
1
+ import sys
2
+ from pathlib import Path
3
+ from typing import Optional
4
+ from typing import Union
5
+
6
+ from PyQt5.QtCore import QPoint
7
+ from PyQt5.QtCore import Qt
8
+ from PyQt5.QtGui import QPainter
9
+ from PyQt5.QtSvg import QSvgRenderer
10
+ from PyQt5.QtWidgets import QApplication
11
+ from PyQt5.QtWidgets import QCheckBox
12
+ from PyQt5.QtWidgets import QPushButton
13
+ from PyQt5.QtWidgets import QToolButton
14
+ from PyQt5.QtWidgets import QWidget
15
+
16
+ from egse.resource import get_resource
17
+
18
+ BUTTON_DISABLED = 0
19
+ BUTTON_SELECTED = 1
20
+ BUTTON_NOT_SELECTED = 2
21
+ BUTTON_NO_CHANGE = 3
22
+ BUTTON_ACTIONED = 4
23
+
24
+
25
+ # Use the ToolTouchButton when you do not have a QToolBar to manage your buttons,
26
+ # but you have them in a QVBoxLayout or QHBoxLayout. The other TouchButton will not
27
+ # properly work if not used in a QToolBar, i.e. the icons will not have the proper size.
28
+
29
+ class ToolTouchButton(QToolButton):
30
+ def __init__(
31
+ self,
32
+ width: int = 32,
33
+ height: int = 32,
34
+ name: str = None,
35
+ status_tip: str = None,
36
+ selected: Union[str, Path] = None,
37
+ disabled: Union[str, Path] = None,
38
+ parent: Optional[QWidget] = None
39
+ ):
40
+ super().__init__(parent=parent)
41
+
42
+ self.setFixedSize(width, height)
43
+ self.setCursor(Qt.PointingHandCursor)
44
+ self.setText(name)
45
+ self.setStatusTip(status_tip)
46
+
47
+ self.status_tip = status_tip
48
+
49
+ self.button_selected = selected
50
+ self.button_disabled = disabled
51
+
52
+ self.resource = {
53
+ BUTTON_DISABLED: self.button_disabled,
54
+ BUTTON_SELECTED: self.button_selected,
55
+ }
56
+
57
+ self.state = BUTTON_SELECTED
58
+ self.disabled = False
59
+
60
+ self.clicked.connect(self.handle_clicked)
61
+
62
+ def handle_clicked(self):
63
+ self.repaint()
64
+
65
+ def print_clicked(self, *args, **kwargs):
66
+ print(f"clicked: {args=}, {kwargs=}")
67
+ print(f" {self.text()=}")
68
+ self.repaint()
69
+
70
+ def print_pressed(self, *args, **kwargs):
71
+ print(f"pressed: {args=}, {kwargs=}")
72
+
73
+ def print_released(self, *args, **kwargs):
74
+ print(f"released: {args=}, {kwargs=}")
75
+
76
+ def setDisabled(self, flag: bool = True):
77
+ self.disabled = flag
78
+ super().setDisabled(flag)
79
+ self.setStatusTip(f"{self.status_tip or ''} {'[DISABLED]' if flag else ''}")
80
+
81
+ def disable(self):
82
+ self.setDisabled(True)
83
+
84
+ def enable(self):
85
+ self.setDisabled(False)
86
+
87
+ def hitButton(self, pos: QPoint):
88
+ return self.contentsRect().contains(pos)
89
+
90
+ def paintEvent(self, *args, **kwargs):
91
+
92
+ painter = QPainter(self)
93
+ painter.setRenderHint(QPainter.Antialiasing)
94
+ painter.setPen(Qt.NoPen)
95
+
96
+ self.drawCustomWidget(painter)
97
+
98
+ painter.end()
99
+
100
+ def drawCustomWidget(self, painter):
101
+ renderer = QSvgRenderer()
102
+ resource = self.resource[self.state if not self.disabled else BUTTON_DISABLED]
103
+ renderer.load(str(resource))
104
+ renderer.render(painter)
105
+
106
+
107
+ class TouchButton(QPushButton):
108
+ def __init__(
109
+ self,
110
+ width: int = 32,
111
+ height: int = 32,
112
+ name: str = None,
113
+ status_tip: str = None,
114
+ selected: Union[str, Path] = None,
115
+ disabled: Union[str, Path] = None,
116
+ parent: Optional[QWidget] = None
117
+ ):
118
+ super().__init__(parent=parent)
119
+
120
+ self.setFixedSize(width, height)
121
+ self.setCursor(Qt.PointingHandCursor)
122
+ self.setText(name)
123
+ self.setStatusTip(status_tip)
124
+
125
+ self.status_tip = status_tip
126
+
127
+ self.button_selected = selected
128
+ self.button_disabled = disabled
129
+
130
+ self.resource = {
131
+ BUTTON_DISABLED: self.button_disabled,
132
+ BUTTON_SELECTED: self.button_selected,
133
+ }
134
+
135
+ self.state = BUTTON_SELECTED
136
+ self.disabled = False
137
+
138
+ self.clicked.connect(self.handle_clicked)
139
+
140
+ def handle_clicked(self):
141
+ self.repaint()
142
+
143
+ def print_clicked(self, *args, **kwargs):
144
+ print(f"clicked: {args=}, {kwargs=}")
145
+ print(f" {self.text()=}")
146
+ self.repaint()
147
+
148
+ def print_pressed(self, *args, **kwargs):
149
+ print(f"pressed: {args=}, {kwargs=}")
150
+
151
+ def print_released(self, *args, **kwargs):
152
+ print(f"released: {args=}, {kwargs=}")
153
+
154
+ def setDisabled(self, flag: bool = True):
155
+ self.disabled = flag
156
+ super().setDisabled(flag)
157
+ self.setStatusTip(f"{self.status_tip or ''} {'[DISABLED]' if flag else ''}")
158
+
159
+ def disable(self):
160
+ self.setDisabled(True)
161
+
162
+ def enable(self):
163
+ self.setDisabled(False)
164
+
165
+ def hitButton(self, pos: QPoint):
166
+ return self.contentsRect().contains(pos)
167
+
168
+ def paintEvent(self, *args, **kwargs):
169
+
170
+ painter = QPainter(self)
171
+ painter.setRenderHint(QPainter.Antialiasing)
172
+ painter.setPen(Qt.NoPen)
173
+
174
+ self.drawCustomWidget(painter)
175
+
176
+ painter.end()
177
+
178
+ def drawCustomWidget(self, painter):
179
+ renderer = QSvgRenderer()
180
+ resource = self.resource[self.state if not self.disabled else BUTTON_DISABLED]
181
+ renderer.load(str(resource))
182
+ renderer.render(painter)
183
+
184
+
185
+ class ToggleButton(QCheckBox):
186
+ def __init__(
187
+ self,
188
+ width: int = 32,
189
+ height: int = 32,
190
+ name: str = None,
191
+ status_tip: str = None,
192
+ selected: Union[str, Path] = None,
193
+ not_selected: Union[str, Path] = None,
194
+ no_change: Union[str, Path] = None,
195
+ disabled: Union[str, Path, list] = None,
196
+ tristate: bool = False,
197
+ parent: Optional[QWidget] = None
198
+ ):
199
+ super().__init__(parent=parent)
200
+
201
+ self.setFixedSize(width, height)
202
+ self.setCursor(Qt.PointingHandCursor)
203
+ self.setTristate(tristate)
204
+ self.setText(name)
205
+ self.setStatusTip(status_tip)
206
+
207
+ self.status_tip = status_tip
208
+
209
+ self.max_states = 3 if tristate else 2
210
+
211
+ self.button_selected = selected
212
+ self.button_not_selected = not_selected
213
+ self.no_change = no_change
214
+ if isinstance(disabled, list):
215
+ # this assumed we do not change the states definition anymore!
216
+ self.button_disabled = [0, *disabled]
217
+ else:
218
+ self.button_disabled = [0, disabled, disabled, disabled]
219
+
220
+ self.resource = {
221
+ BUTTON_DISABLED: self.button_disabled,
222
+ BUTTON_SELECTED: self.button_selected,
223
+ BUTTON_NOT_SELECTED: self.button_not_selected,
224
+ BUTTON_NO_CHANGE: self.no_change,
225
+ }
226
+
227
+ self.state = BUTTON_SELECTED
228
+ self.disabled = False
229
+
230
+ self.clicked.connect(self.handle_clicked)
231
+
232
+ def __str__(self):
233
+ return f"ToggleButton: name={self.text()}, tristate={self.isTristate()}, selected={self.is_selected()}"
234
+
235
+ def handle_clicked(self):
236
+ self.state = 1 if self.state == self.max_states else self.state + 1
237
+ self.repaint()
238
+
239
+ def setDisabled(self, flag: bool = True):
240
+ self.disabled = flag
241
+ super().setDisabled(flag)
242
+ self.setStatusTip(f"{self.status_tip or ''} {'[DISABLED]' if flag else ''}")
243
+
244
+ def disable(self):
245
+ self.setDisabled(True)
246
+
247
+ def enable(self):
248
+ self.setDisabled(False)
249
+
250
+ def is_selected(self):
251
+ return self.state == BUTTON_SELECTED
252
+
253
+ def set_selected(self, on: bool = True):
254
+ self.state = BUTTON_SELECTED if on else BUTTON_NOT_SELECTED
255
+ self.repaint()
256
+
257
+ def hitButton(self, pos: QPoint):
258
+ return self.contentsRect().contains(pos)
259
+
260
+ def paintEvent(self, *args, **kwargs):
261
+
262
+ painter = QPainter(self)
263
+ painter.setRenderHint(QPainter.Antialiasing)
264
+ painter.setPen(Qt.NoPen)
265
+
266
+ self.drawCustomWidget(painter)
267
+ painter.end()
268
+
269
+ def drawCustomWidget(self, painter):
270
+ renderer = QSvgRenderer()
271
+ if not self.disabled:
272
+ resource = self.resource[self.state]
273
+ else:
274
+ resource = self.resource[BUTTON_DISABLED][self.state]
275
+ renderer.load(str(resource))
276
+ renderer.render(painter)
277
+
278
+
279
+ if __name__ == "__main__":
280
+
281
+ from PyQt5.QtWidgets import QMainWindow
282
+ from PyQt5.QtWidgets import QFrame
283
+ from PyQt5.QtWidgets import QVBoxLayout
284
+
285
+
286
+ class MainWindow(QMainWindow):
287
+ def __init__(self):
288
+ super().__init__()
289
+ self.resize(200, 200)
290
+
291
+ self.container = QFrame()
292
+ self.container.setObjectName("Container")
293
+ self.layout = QVBoxLayout()
294
+
295
+ # button_selected = get_resource(":/icons/cs-connected.svg")
296
+ # button_not_selected = get_resource(":/icons/cs-not-connected.svg")
297
+ # button_no_change = get_resource(":/icons/cs-connected-alert.svg")
298
+ # button_disabled = get_resource(":/icons/cs-connected-disabled.svg")
299
+
300
+ button_selected = get_resource(":/icons/start-button.svg")
301
+ button_not_selected = get_resource(":/icons/stop-button.svg")
302
+ button_no_change = get_resource(":/icons/plugged.svg")
303
+ button_disabled = [
304
+ get_resource(":/icons/start-button-disabled.svg"),
305
+ get_resource(":/icons/stop-button-disabled.svg"),
306
+ get_resource(":/icons/plugged-disabled.svg"),
307
+ ]
308
+
309
+ self.toggle = ToggleButton(
310
+ name="CS-CONNECT",
311
+ status_tip="connect-disconnect hexapod control server",
312
+ selected=button_selected,
313
+ not_selected=button_not_selected,
314
+ no_change=button_no_change,
315
+ disabled=button_disabled,
316
+ tristate=True,
317
+ )
318
+
319
+ self.touch = TouchButton(
320
+ name="HOME",
321
+ status_tip="Perform a homing operation",
322
+ selected=get_resource(":/icons/home.svg"),
323
+ disabled=get_resource(":/icons/home-disabled.svg"),
324
+ )
325
+
326
+ self.layout.addWidget(self.toggle, Qt.AlignCenter, Qt.AlignCenter)
327
+ self.layout.addWidget(self.touch, Qt.AlignCenter, Qt.AlignCenter)
328
+ self.layout.addWidget(pb := QPushButton("disable"))
329
+ self.container.setLayout(self.layout)
330
+ self.setCentralWidget(self.container)
331
+
332
+ self.pb = pb
333
+ self.pb.setCheckable(True)
334
+ self.pb.clicked.connect(self.toggle_disable)
335
+
336
+ self.toggle.clicked.connect(self.print_clicked)
337
+ self.touch.clicked.connect(self.print_clicked)
338
+
339
+ # Use the following for further debugging
340
+
341
+ # self.toggle.stateChanged.connect(self.print_state_changed)
342
+ # self.toggle.pressed.connect(self.print_pressed)
343
+ # self.toggle.released.connect(self.print_released)
344
+
345
+ # self.touch.pressed.connect(self.print_pressed)
346
+ # self.touch.released.connect(self.print_released)
347
+
348
+ self.statusBar()
349
+
350
+ def toggle_disable(self, checked: bool):
351
+ self.toggle.disable() if checked else self.toggle.enable()
352
+ self.touch.disable() if checked else self.touch.enable()
353
+ self.pb.setText("enable" if checked else "disable")
354
+
355
+ def print_state_changed(self, *args, **kwargs):
356
+ sender = self.sender()
357
+ print(f"stateChanged: {args=}, {kwargs=}")
358
+ print(f" {sender.isChecked()=} {sender.checkState()=}")
359
+
360
+ def print_clicked(self, *args, **kwargs):
361
+ sender = self.sender()
362
+ print(f"clicked: {args=}, {kwargs=}")
363
+ print(f" {sender.state=}")
364
+ print(f" {sender.text()=}")
365
+
366
+ def print_pressed(self, *args, **kwargs):
367
+ print(f"pressed: {args=}, {kwargs=}")
368
+
369
+ def print_released(self, *args, **kwargs):
370
+ print(f"released: {args=}, {kwargs=}")
371
+
372
+
373
+
374
+ app = QApplication(sys.argv)
375
+ window = MainWindow()
376
+ window.show()
377
+
378
+ sys.exit(app.exec_())