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.
- cgse_gui-2024.1.4/.gitignore +35 -0
- cgse_gui-2024.1.4/PKG-INFO +13 -0
- cgse_gui-2024.1.4/README.md +1 -0
- cgse_gui-2024.1.4/pyproject.toml +37 -0
- cgse_gui-2024.1.4/src/egse/gui/__init__.py +56 -0
- cgse_gui-2024.1.4/src/egse/gui/buttons.py +378 -0
- cgse_gui-2024.1.4/src/egse/gui/icons/aeu-cs-start.svg +117 -0
- cgse_gui-2024.1.4/src/egse/gui/icons/aeu-cs-stop.svg +118 -0
- cgse_gui-2024.1.4/src/egse/gui/icons/aeu-cs.svg +107 -0
- cgse_gui-2024.1.4/src/egse/gui/icons/aeu_cs-started.svg +112 -0
- cgse_gui-2024.1.4/src/egse/gui/icons/aeu_cs-stopped.svg +112 -0
- cgse_gui-2024.1.4/src/egse/gui/icons/aeu_cs.svg +55 -0
- cgse_gui-2024.1.4/src/egse/gui/icons/alert.svg +1 -0
- cgse_gui-2024.1.4/src/egse/gui/icons/arrow-double-left.png +0 -0
- cgse_gui-2024.1.4/src/egse/gui/icons/arrow-double-right.png +0 -0
- cgse_gui-2024.1.4/src/egse/gui/icons/arrow-up.svg +11 -0
- cgse_gui-2024.1.4/src/egse/gui/icons/backward.svg +1 -0
- cgse_gui-2024.1.4/src/egse/gui/icons/busy.svg +1 -0
- cgse_gui-2024.1.4/src/egse/gui/icons/cleaning.svg +115 -0
- cgse_gui-2024.1.4/src/egse/gui/icons/color-scheme.svg +1 -0
- cgse_gui-2024.1.4/src/egse/gui/icons/cs-connected-alert.svg +91 -0
- cgse_gui-2024.1.4/src/egse/gui/icons/cs-connected-disabled.svg +43 -0
- cgse_gui-2024.1.4/src/egse/gui/icons/cs-connected.svg +89 -0
- cgse_gui-2024.1.4/src/egse/gui/icons/cs-not-connected.svg +44 -0
- cgse_gui-2024.1.4/src/egse/gui/icons/double-left-arrow.svg +1 -0
- cgse_gui-2024.1.4/src/egse/gui/icons/double-right-arrow.svg +1 -0
- cgse_gui-2024.1.4/src/egse/gui/icons/erase-disabled.svg +19 -0
- cgse_gui-2024.1.4/src/egse/gui/icons/erase.svg +59 -0
- cgse_gui-2024.1.4/src/egse/gui/icons/fitsgen-start.svg +47 -0
- cgse_gui-2024.1.4/src/egse/gui/icons/fitsgen-stop.svg +48 -0
- cgse_gui-2024.1.4/src/egse/gui/icons/fitsgen.svg +1 -0
- cgse_gui-2024.1.4/src/egse/gui/icons/forward.svg +1 -0
- cgse_gui-2024.1.4/src/egse/gui/icons/fov-hk-start.svg +33 -0
- cgse_gui-2024.1.4/src/egse/gui/icons/fov-hk-stop.svg +37 -0
- cgse_gui-2024.1.4/src/egse/gui/icons/fov-hk.svg +1 -0
- cgse_gui-2024.1.4/src/egse/gui/icons/front-desk.svg +1 -0
- cgse_gui-2024.1.4/src/egse/gui/icons/home-actioned.svg +15 -0
- cgse_gui-2024.1.4/src/egse/gui/icons/home-disabled.svg +15 -0
- cgse_gui-2024.1.4/src/egse/gui/icons/home.svg +13 -0
- cgse_gui-2024.1.4/src/egse/gui/icons/info.svg +1 -0
- cgse_gui-2024.1.4/src/egse/gui/icons/invalid.png +0 -0
- cgse_gui-2024.1.4/src/egse/gui/icons/led-green.svg +20 -0
- cgse_gui-2024.1.4/src/egse/gui/icons/led-grey.svg +20 -0
- cgse_gui-2024.1.4/src/egse/gui/icons/led-orange.svg +20 -0
- cgse_gui-2024.1.4/src/egse/gui/icons/led-red.svg +20 -0
- cgse_gui-2024.1.4/src/egse/gui/icons/led-square-green.svg +134 -0
- cgse_gui-2024.1.4/src/egse/gui/icons/led-square-grey.svg +134 -0
- cgse_gui-2024.1.4/src/egse/gui/icons/led-square-orange.svg +134 -0
- cgse_gui-2024.1.4/src/egse/gui/icons/led-square-red.svg +134 -0
- cgse_gui-2024.1.4/src/egse/gui/icons/limit-switch-all-green.svg +115 -0
- cgse_gui-2024.1.4/src/egse/gui/icons/limit-switch-all-red.svg +117 -0
- cgse_gui-2024.1.4/src/egse/gui/icons/limit-switch-el+.svg +116 -0
- cgse_gui-2024.1.4/src/egse/gui/icons/limit-switch-el-.svg +117 -0
- cgse_gui-2024.1.4/src/egse/gui/icons/location-marker.svg +1 -0
- cgse_gui-2024.1.4/src/egse/gui/icons/logo-dpu.svg +48 -0
- cgse_gui-2024.1.4/src/egse/gui/icons/logo-gimbal.svg +112 -0
- cgse_gui-2024.1.4/src/egse/gui/icons/logo-huber.svg +23 -0
- cgse_gui-2024.1.4/src/egse/gui/icons/logo-ogse.svg +31 -0
- cgse_gui-2024.1.4/src/egse/gui/icons/logo-puna.svg +92 -0
- cgse_gui-2024.1.4/src/egse/gui/icons/logo-tcs.svg +29 -0
- cgse_gui-2024.1.4/src/egse/gui/icons/logo-zonda.svg +66 -0
- cgse_gui-2024.1.4/src/egse/gui/icons/maximize.svg +1 -0
- cgse_gui-2024.1.4/src/egse/gui/icons/meter.svg +1 -0
- cgse_gui-2024.1.4/src/egse/gui/icons/more.svg +45 -0
- cgse_gui-2024.1.4/src/egse/gui/icons/n-fee-hk-start.svg +24 -0
- cgse_gui-2024.1.4/src/egse/gui/icons/n-fee-hk-stop.svg +25 -0
- cgse_gui-2024.1.4/src/egse/gui/icons/n-fee-hk.svg +83 -0
- cgse_gui-2024.1.4/src/egse/gui/icons/observing-off.svg +46 -0
- cgse_gui-2024.1.4/src/egse/gui/icons/observing-on.svg +46 -0
- cgse_gui-2024.1.4/src/egse/gui/icons/open-document-hdf5.png +0 -0
- cgse_gui-2024.1.4/src/egse/gui/icons/open-document-hdf5.svg +21 -0
- cgse_gui-2024.1.4/src/egse/gui/icons/ops-mode.svg +1 -0
- cgse_gui-2024.1.4/src/egse/gui/icons/play-green.svg +17 -0
- cgse_gui-2024.1.4/src/egse/gui/icons/plugged-disabled.svg +27 -0
- cgse_gui-2024.1.4/src/egse/gui/icons/plugged.svg +21 -0
- cgse_gui-2024.1.4/src/egse/gui/icons/pm_ui.svg +1 -0
- cgse_gui-2024.1.4/src/egse/gui/icons/power-button-green.svg +27 -0
- cgse_gui-2024.1.4/src/egse/gui/icons/power-button-red.svg +27 -0
- cgse_gui-2024.1.4/src/egse/gui/icons/power-button.svg +27 -0
- cgse_gui-2024.1.4/src/egse/gui/icons/radar.svg +1 -0
- cgse_gui-2024.1.4/src/egse/gui/icons/radioactive.svg +2 -0
- cgse_gui-2024.1.4/src/egse/gui/icons/reload.svg +1 -0
- cgse_gui-2024.1.4/src/egse/gui/icons/remote-control-off.svg +28 -0
- cgse_gui-2024.1.4/src/egse/gui/icons/remote-control-on.svg +28 -0
- cgse_gui-2024.1.4/src/egse/gui/icons/repeat-blue.svg +15 -0
- cgse_gui-2024.1.4/src/egse/gui/icons/repeat.svg +1 -0
- cgse_gui-2024.1.4/src/egse/gui/icons/settings.svg +1 -0
- cgse_gui-2024.1.4/src/egse/gui/icons/shrink.svg +1 -0
- cgse_gui-2024.1.4/src/egse/gui/icons/shutter.svg +1 -0
- cgse_gui-2024.1.4/src/egse/gui/icons/sign-off.svg +1 -0
- cgse_gui-2024.1.4/src/egse/gui/icons/sign-on.svg +1 -0
- cgse_gui-2024.1.4/src/egse/gui/icons/sim-mode.svg +1 -0
- cgse_gui-2024.1.4/src/egse/gui/icons/small-buttons-go.svg +20 -0
- cgse_gui-2024.1.4/src/egse/gui/icons/small-buttons-minus.svg +51 -0
- cgse_gui-2024.1.4/src/egse/gui/icons/small-buttons-plus.svg +51 -0
- cgse_gui-2024.1.4/src/egse/gui/icons/sponge.svg +220 -0
- cgse_gui-2024.1.4/src/egse/gui/icons/start-button-disabled.svg +84 -0
- cgse_gui-2024.1.4/src/egse/gui/icons/start-button.svg +50 -0
- cgse_gui-2024.1.4/src/egse/gui/icons/stop-button-disabled.svg +84 -0
- cgse_gui-2024.1.4/src/egse/gui/icons/stop-button.svg +50 -0
- cgse_gui-2024.1.4/src/egse/gui/icons/stop-red.svg +17 -0
- cgse_gui-2024.1.4/src/egse/gui/icons/stop.svg +1 -0
- cgse_gui-2024.1.4/src/egse/gui/icons/switch-disabled-square.svg +87 -0
- cgse_gui-2024.1.4/src/egse/gui/icons/switch-disabled.svg +15 -0
- cgse_gui-2024.1.4/src/egse/gui/icons/switch-off-square.svg +87 -0
- cgse_gui-2024.1.4/src/egse/gui/icons/switch-off.svg +72 -0
- cgse_gui-2024.1.4/src/egse/gui/icons/switch-on-square.svg +87 -0
- cgse_gui-2024.1.4/src/egse/gui/icons/switch-on.svg +61 -0
- cgse_gui-2024.1.4/src/egse/gui/icons/temperature-control.svg +44 -0
- cgse_gui-2024.1.4/src/egse/gui/icons/th_ui_logo.svg +1 -0
- cgse_gui-2024.1.4/src/egse/gui/icons/unplugged.svg +23 -0
- cgse_gui-2024.1.4/src/egse/gui/icons/unvalid.png +0 -0
- cgse_gui-2024.1.4/src/egse/gui/icons/user-interface.svg +1 -0
- cgse_gui-2024.1.4/src/egse/gui/icons/vacuum.svg +1 -0
- cgse_gui-2024.1.4/src/egse/gui/icons/valid.png +0 -0
- cgse_gui-2024.1.4/src/egse/gui/icons/zoom-to-pixel-dark.svg +64 -0
- cgse_gui-2024.1.4/src/egse/gui/icons/zoom-to-pixel-white.svg +36 -0
- cgse_gui-2024.1.4/src/egse/gui/led.py +162 -0
- cgse_gui-2024.1.4/src/egse/gui/limitswitch.py +143 -0
- cgse_gui-2024.1.4/src/egse/gui/states.py +148 -0
- cgse_gui-2024.1.4/src/egse/gui/styles/dark.qss +343 -0
- cgse_gui-2024.1.4/src/egse/gui/styles/default.qss +48 -0
- cgse_gui-2024.1.4/src/egse/gui/styles.qss +48 -0
- 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_())
|