Compare commits
36 Commits
v0.90.0
...
refactor/b
| Author | SHA1 | Date | |
|---|---|---|---|
| 302ae90139 | |||
|
|
1405068925 | ||
| 5aad401ef8 | |||
|
|
885dcfda89 | ||
| 30fef929cf | |||
| 1f30dd73a9 | |||
| 73cd11e472 | |||
| 7616ca0e14 | |||
|
|
ca29a69779 | ||
| dcc5fd71ee | |||
|
|
fee4901657 | ||
| 71873ddf35 | |||
|
|
f8552ca551 | ||
| 995a795060 | |||
|
|
7ab81c5797 | ||
| bc1e23944c | |||
| a3fe20500a | |||
| 61a4e32deb | |||
| 3d681f77e1 | |||
| 5a9ccfd1f6 | |||
| fc57b7a126 | |||
| 06205e0790 | |||
| 4be6fd6b83 | |||
| 714e1e139e | |||
|
|
01c0e0b1df | ||
| 4457ef2147 | |||
| 8ca60d54b3 | |||
| 5696c993dc | |||
| 1206e15309 | |||
|
|
4f2b51b211 | ||
| 1b9c55a46a | |||
| f4844d2e06 | |||
| 06fab0eab9 | |||
| a16b87ac28 | |||
| cce1367a72 | |||
| 28f26e92a4 |
@@ -43,13 +43,20 @@ stages:
|
||||
- export QTWEBENGINE_DISABLE_SANDBOX=1
|
||||
|
||||
.clone-repos: &clone-repos
|
||||
- echo -e "\033[35;1m Using branch $BEC_CORE_BRANCH of BEC CORE \033[0;m";
|
||||
- git clone --branch $BEC_CORE_BRANCH https://gitlab.psi.ch/bec/bec.git
|
||||
- echo -e "\033[35;1m Using branch $OPHYD_DEVICES_BRANCH of OPHYD_DEVICES \033[0;m";
|
||||
- git clone --branch $OPHYD_DEVICES_BRANCH https://gitlab.psi.ch/bec/ophyd_devices.git
|
||||
- export OHPYD_DEVICES_PATH=$PWD/ophyd_devices
|
||||
|
||||
.install-repos: &install-repos
|
||||
- pip install -e ./ophyd_devices
|
||||
- pip install -e ./bec/bec_lib[dev]
|
||||
- pip install -e ./bec/bec_ipython_client
|
||||
|
||||
.install-os-packages: &install-os-packages
|
||||
- apt-get update
|
||||
- apt-get install -y libgl1-mesa-glx libegl1-mesa x11-utils libxkbcommon-x11-0 libdbus-1-3
|
||||
- apt-get install -y libgl1-mesa-glx libegl1-mesa x11-utils libxkbcommon-x11-0 libdbus-1-3 xvfb
|
||||
- *install-qt-webengine-deps
|
||||
|
||||
before_script:
|
||||
@@ -131,8 +138,7 @@ tests:
|
||||
script:
|
||||
- *clone-repos
|
||||
- *install-os-packages
|
||||
- pip install -e ./bec/bec_lib[dev]
|
||||
- pip install -e ./bec/bec_ipython_client
|
||||
- *install-repos
|
||||
- pip install -e .[dev,pyqt6]
|
||||
- coverage run --source=./bec_widgets -m pytest -v --junitxml=report.xml --random-order --full-trace ./tests/unit_tests
|
||||
- coverage report
|
||||
@@ -169,8 +175,7 @@ test-matrix:
|
||||
script:
|
||||
- *clone-repos
|
||||
- *install-os-packages
|
||||
- pip install -e ./bec/bec_lib[dev]
|
||||
- pip install -e ./bec/bec_ipython_client
|
||||
- *install-repos
|
||||
- pip install -e .[dev,$QT_PCKG]
|
||||
- pytest -v --junitxml=report.xml --random-order ./tests/unit_tests
|
||||
allow_failure: true
|
||||
@@ -195,10 +200,9 @@ end-2-end-conda:
|
||||
|
||||
- cd ./bec
|
||||
- source ./bin/install_bec_dev.sh -t
|
||||
|
||||
- pip install -e ./bec_lib[dev]
|
||||
- pip install -e ./bec_ipython_client[dev]
|
||||
- cd ../
|
||||
- pip install -e ./ophyd_devices
|
||||
|
||||
- pip install -e .[dev,pyqt6]
|
||||
- cd ./tests/end-2-end
|
||||
- pytest -v --start-servers --flush-redis --random-order
|
||||
|
||||
200
CHANGELOG.md
@@ -1,5 +1,107 @@
|
||||
# CHANGELOG
|
||||
|
||||
## v0.93.0 (2024-08-05)
|
||||
|
||||
### Feature
|
||||
|
||||
* feat(themes): moved themes to bec_qthemes
|
||||
|
||||
This reverts commit fd6ae91993a23a7b8dbb2cf3c4b7c3eda6d2b0f6 ([`5aad401`](https://gitlab.psi.ch/bec/bec_widgets/-/commit/5aad401ef8774c7330784f72cd3b9d8c253e2b6a))
|
||||
|
||||
## v0.92.5 (2024-08-05)
|
||||
|
||||
### Fix
|
||||
|
||||
* fix(spinner): stop timer on close event ([`30fef92`](https://gitlab.psi.ch/bec/bec_widgets/-/commit/30fef929cf6fb4b73f48151c92a0ee54c734031d))
|
||||
|
||||
* fix(status_box): fix cleanup of status box ([`1f30dd7`](https://gitlab.psi.ch/bec/bec_widgets/-/commit/1f30dd73a9c1e3135087a5eef92c7329f54a604e))
|
||||
|
||||
### Refactor
|
||||
|
||||
* refactor(queue): refactored bec queue to inherit only from qwidget ([`7616ca0`](https://gitlab.psi.ch/bec/bec_widgets/-/commit/7616ca0e145e233ccb48029a8c0b54b54b5b4194))
|
||||
|
||||
### Test
|
||||
|
||||
* test: register all widgets with qtbot and close them ([`73cd11e`](https://gitlab.psi.ch/bec/bec_widgets/-/commit/73cd11e47277e4437554b785a9551b28a572094f))
|
||||
|
||||
## v0.92.4 (2024-07-31)
|
||||
|
||||
### Fix
|
||||
|
||||
* fix: fix missmatch of signal/slot in image and motormap ([`dcc5fd7`](https://gitlab.psi.ch/bec/bec_widgets/-/commit/dcc5fd71ee9f51767a7b2b1ed6200e89d1ef754c))
|
||||
|
||||
## v0.92.3 (2024-07-28)
|
||||
|
||||
### Fix
|
||||
|
||||
* fix(docs): moved to pyside6 ([`71873dd`](https://gitlab.psi.ch/bec/bec_widgets/-/commit/71873ddf359516ded8f74f4d2f73df4156aa1368))
|
||||
|
||||
## v0.92.2 (2024-07-28)
|
||||
|
||||
### Fix
|
||||
|
||||
* fix(widgets): fixed import for tictactoe example ([`995a795`](https://gitlab.psi.ch/bec/bec_widgets/-/commit/995a795060bebe25c17108d80ae0fa30463f03b1))
|
||||
|
||||
## v0.92.1 (2024-07-28)
|
||||
|
||||
### Build
|
||||
|
||||
* build(ci): install ophyd_devices in editable mode for pipelines ([`06205e0`](https://gitlab.psi.ch/bec/bec_widgets/-/commit/06205e07903d93accf40abab153f440059f236ed))
|
||||
|
||||
### Fix
|
||||
|
||||
* fix: use SafeSlot instead of Slot ([`bc1e239`](https://gitlab.psi.ch/bec/bec_widgets/-/commit/bc1e23944cc0e5a861e3d0b4dc5b4ac6292d5269))
|
||||
|
||||
* fix: linting ([`a3fe205`](https://gitlab.psi.ch/bec/bec_widgets/-/commit/a3fe20500ae2ac03dcde07432f7e21ce5262ce46))
|
||||
|
||||
* fix: always add a QApplication for tests ([`61a4e32`](https://gitlab.psi.ch/bec/bec_widgets/-/commit/61a4e32deb337ed27f2f43358b88b7266413b58e))
|
||||
|
||||
* fix: add xvfb to draw offscreen ([`3d681f7`](https://gitlab.psi.ch/bec/bec_widgets/-/commit/3d681f77e144e74138fc5fa65630004d7c166878))
|
||||
|
||||
* fix: reset ErrorPopup singleton between tests ([`5a9ccfd`](https://gitlab.psi.ch/bec/bec_widgets/-/commit/5a9ccfd1f6d2aacd5d86c1a34f74163b272d1ae4))
|
||||
|
||||
* fix: metaclass + QObject segfaults PyQt(cpp bindings) ([`fc57b7a`](https://gitlab.psi.ch/bec/bec_widgets/-/commit/fc57b7a1262031a2df9e6a99493db87e766b779a))
|
||||
|
||||
### Refactor
|
||||
|
||||
* refactor: renamed DeviceMonitor2DMessage ([`4be6fd6`](https://gitlab.psi.ch/bec/bec_widgets/-/commit/4be6fd6b83ea1048f16310f7d2bbe777b13b245e))
|
||||
|
||||
* refactor: rename device_monitor to device_monitor_2d ([`714e1e1`](https://gitlab.psi.ch/bec/bec_widgets/-/commit/714e1e139e0033d2725fefb636c419ca137a68c6))
|
||||
|
||||
## v0.92.0 (2024-07-24)
|
||||
|
||||
### Feature
|
||||
|
||||
* feat(dock): dock style sheets updated ([`8ca60d5`](https://gitlab.psi.ch/bec/bec_widgets/-/commit/8ca60d54b3cfa621172ce097fc1ba514c47ebac7))
|
||||
|
||||
* feat(general_gui): general gui added ([`5696c99`](https://gitlab.psi.ch/bec/bec_widgets/-/commit/5696c993dc1c0da40ff3e99f754c246cc017ea32))
|
||||
|
||||
### Fix
|
||||
|
||||
* fix(dock): custom label can be created closable ([`4457ef2`](https://gitlab.psi.ch/bec/bec_widgets/-/commit/4457ef2147e21b856c9dcaf63c81ba98002dcaf1))
|
||||
|
||||
* fix(device_combobox): set minimum size to 125px ([`1206e15`](https://gitlab.psi.ch/bec/bec_widgets/-/commit/1206e153094cd8505badf69a1461572a76b4c5ad))
|
||||
|
||||
## v0.91.0 (2024-07-23)
|
||||
|
||||
### Feature
|
||||
|
||||
* feat(dock_area): plugin added ([`a16b87a`](https://gitlab.psi.ch/bec/bec_widgets/-/commit/a16b87ac28d164230dd2e8020f50ff3a63cd407e))
|
||||
|
||||
* feat(dock_area): Added toolbar to dock area to add widgets without CLI interactions ([`cce1367`](https://gitlab.psi.ch/bec/bec_widgets/-/commit/cce1367a72fca7206d351894bd1831b7bbfa7ec6))
|
||||
|
||||
* feat(toolbar): expandable menu actions ([`28f26e9`](https://gitlab.psi.ch/bec/bec_widgets/-/commit/28f26e92a46063db1a194be552156a5d3b2c43e7))
|
||||
|
||||
### Fix
|
||||
|
||||
* fix(status_item): icons changed to material design ([`1b9c55a`](https://gitlab.psi.ch/bec/bec_widgets/-/commit/1b9c55a46a0dfd8678c8e95ff64dd6e8cfb9233e))
|
||||
|
||||
* fix(plugins): Qt Designer plugins icons adjusted ([`f4844d2`](https://gitlab.psi.ch/bec/bec_widgets/-/commit/f4844d2e067ce75dc64b89b230d7932b308ddfc2))
|
||||
|
||||
### Test
|
||||
|
||||
* test(dock_area): tests extended ([`06fab0e`](https://gitlab.psi.ch/bec/bec_widgets/-/commit/06fab0eab926cef5677d4988fd1fce09da342dd8))
|
||||
|
||||
## v0.90.0 (2024-07-23)
|
||||
|
||||
### Feature
|
||||
@@ -44,104 +146,6 @@ This reverts commit 3798714369adf4023f833b7749d2f46a0ec74eee ([`fd6ae91`](https:
|
||||
|
||||
## v0.88.1 (2024-07-22)
|
||||
|
||||
### Documentation
|
||||
|
||||
* docs: readthedocs icon path fixed ([`2bcaa42`](https://gitlab.psi.ch/bec/bec_widgets/-/commit/2bcaa4256d6daaefacb3ead8c72458d7b1498e29))
|
||||
|
||||
### Fix
|
||||
|
||||
* fix(plot_base): set_xy autorange moved to plotbase from waveform ([`a3dff7d`](https://gitlab.psi.ch/bec/bec_widgets/-/commit/a3dff7decc16115c12dc6b4ef1572552368da309))
|
||||
|
||||
### Refactor
|
||||
|
||||
* refactor(toolbar): generalizations of the ToolBarAction ([`ad112d1`](https://gitlab.psi.ch/bec/bec_widgets/-/commit/ad112d1f08157f6987edd48a0bacf9f669ef1997))
|
||||
|
||||
## v0.88.0 (2024-07-19)
|
||||
|
||||
### Feature
|
||||
|
||||
* feat(waveform_widget): designer plugin added ([`1f8ef52`](https://gitlab.psi.ch/bec/bec_widgets/-/commit/1f8ef52b606283038052640849094f515a463403))
|
||||
|
||||
* feat(waveform_widget): switch between drag and rectangle mode ([`2be009c`](https://gitlab.psi.ch/bec/bec_widgets/-/commit/2be009c6477ba26c5cfb4d827534c5d5eb428999))
|
||||
|
||||
* feat(waveform_widget): autorange button ([`8df6b00`](https://gitlab.psi.ch/bec/bec_widgets/-/commit/8df6b003e5c6a942fa2e875d9790e492c087bf26))
|
||||
|
||||
* feat(waveform_widget): dap parameter window ([`1e551d6`](https://gitlab.psi.ch/bec/bec_widgets/-/commit/1e551d6e9696f79ea2e0a179d13a4fc6c2a128b2))
|
||||
|
||||
* feat(waveform): export to matplotlib window of current scene ([`8d93405`](https://gitlab.psi.ch/bec/bec_widgets/-/commit/8d9340539967b06b1e15f21a2106a39d5c740f31))
|
||||
|
||||
* feat(figure): export dialog can be launched from CLI and from toolbar ([`6ff6111`](https://gitlab.psi.ch/bec/bec_widgets/-/commit/6ff611109153b9412dce37c527b19e839d99bba7))
|
||||
|
||||
* feat(waveform_widget): added error handle utility ([`a8ff1d4`](https://gitlab.psi.ch/bec/bec_widgets/-/commit/a8ff1d4cd09cae5eaeb4bd0ea90fdd102e32f3a3))
|
||||
|
||||
* feat(curve_dialog): add DAP functionality ([`e830565`](https://gitlab.psi.ch/bec/bec_widgets/-/commit/e8305652fde384da037242cf8f7e3606f22bcfb6))
|
||||
|
||||
* feat(curve_dialog): curves can be added ([`c926a75`](https://gitlab.psi.ch/bec/bec_widgets/-/commit/c926a75a7927d672c044ea8f68771209ae5accc6))
|
||||
|
||||
* feat(waveform_widget): BECWaveformWidget toolbar added import/export config ([`fa9b171`](https://gitlab.psi.ch/bec/bec_widgets/-/commit/fa9b17191ddbb4043a658dae9aa0801e1dc22b84))
|
||||
|
||||
* feat(waveform_widget): BECWaveformWidget added with toolbar ([`755b394`](https://gitlab.psi.ch/bec/bec_widgets/-/commit/755b394c1c4d7c443c442d89c630d08ce5415554))
|
||||
|
||||
### Fix
|
||||
|
||||
* fix(waveform_widget): plot API unified with BECFigure ([`2c8764a`](https://gitlab.psi.ch/bec/bec_widgets/-/commit/2c8764a27de89b39b717032b58465e120ec57fbc))
|
||||
|
||||
* fix(colormap_selector): compatibility for PyQt6 when using designer fixed ([`50135b5`](https://gitlab.psi.ch/bec/bec_widgets/-/commit/50135b5fe90a88618291e9357f180cb19251dace))
|
||||
|
||||
* fix(waveform_widget): adapted for BECWidget base class ([`6eb313f`](https://gitlab.psi.ch/bec/bec_widgets/-/commit/6eb313fa76e559d62ecd8fa8849142b83817e47c))
|
||||
|
||||
* fix(waveform_widget): temporary disabled save/load config ([`7089cf3`](https://gitlab.psi.ch/bec/bec_widgets/-/commit/7089cf356a43d805241d5621952e544d690e65e0))
|
||||
|
||||
* fix(waveform_widget): use @SafeSlot decorator for automatic error message ([`8e588d7`](https://gitlab.psi.ch/bec/bec_widgets/-/commit/8e588d79c86e950f6915e89c08fa9415c4bd8033))
|
||||
|
||||
* fix(waveform): colormaps of curves can be changed and normalised
|
||||
|
||||
feat(waveform): colormap can be changed from curve dialog
|
||||
|
||||
fix(curve_dialog): default dialog parameters fixed
|
||||
|
||||
curve Dialog colormap WIP ([`33495cf`](https://gitlab.psi.ch/bec/bec_widgets/-/commit/33495cfe03b363f18db61d8af2983f49027b7a43))
|
||||
|
||||
* fix(waveform_widget): adapted for changes from improved scan logic from waveform widget ([`8ac35d7`](https://gitlab.psi.ch/bec/bec_widgets/-/commit/8ac35d7280b1ff007c10612228d163cc0c5d1a99))
|
||||
|
||||
### Refactor
|
||||
|
||||
* refactor(icons): icons moved to the assets directory ([`a8b6ef2`](https://gitlab.psi.ch/bec/bec_widgets/-/commit/a8b6ef20cccae87515b10f054d0ed5b10e152769))
|
||||
|
||||
* refactor(waveform_widget): removed PYSIDE6 check ([`47fcb9e`](https://gitlab.psi.ch/bec/bec_widgets/-/commit/47fcb9ebfe35ae600cced95a1edc68f6f6e37a04))
|
||||
|
||||
### Test
|
||||
|
||||
* test(waveform_widget): test added ([`8d764e2`](https://gitlab.psi.ch/bec/bec_widgets/-/commit/8d764e2d46a1e017dadc3c4630648c1ca708afc2))
|
||||
|
||||
## v0.87.1 (2024-07-18)
|
||||
|
||||
### Fix
|
||||
|
||||
* fix(dock): added hasattr to cleanup method for widgets ([`d75c55b`](https://gitlab.psi.ch/bec/bec_widgets/-/commit/d75c55b2b1ccf156fb789c7813f1c5bdf256f860))
|
||||
|
||||
* fix: add missing close() call, ensure jupyter console client.shutdown() is called in closeEvent ([`e52ee26`](https://gitlab.psi.ch/bec/bec_widgets/-/commit/e52ee2604cb35096f1bd833ca9516d8a34197d35))
|
||||
|
||||
* fix: BECWidget checks if it is a widget, and implements closeEvent and cleanup ([`d64758f`](https://gitlab.psi.ch/bec/bec_widgets/-/commit/d64758f268cad69e6a17bd52dc9913a6367d3cde))
|
||||
|
||||
* fix: add exit handlers for BECConnection objects ([`6202d22`](https://gitlab.psi.ch/bec/bec_widgets/-/commit/6202d224fe85c103a4c33bd8c255f18cfd027303))
|
||||
|
||||
### Refactor
|
||||
|
||||
* refactor: BECWidget is a mixin based on BECConnector, for each QWidget in BEC
|
||||
|
||||
Handles closeEvent() and RPC registering/unregistering ([`c7feb69`](https://gitlab.psi.ch/bec/bec_widgets/-/commit/c7feb6952d590b569f7b0cba3b019a9af0ce0c93))
|
||||
|
||||
## v0.87.0 (2024-07-17)
|
||||
|
||||
### Feature
|
||||
|
||||
* feat(qt_utils): added warning utility with simple API to setup warning message ([`787f749`](https://gitlab.psi.ch/bec/bec_widgets/-/commit/787f74949bac27aaa51cbb43911919071481707c))
|
||||
|
||||
* feat(qt_utils): added error handle utility with popup messageBoxes ([`196ef7a`](https://gitlab.psi.ch/bec/bec_widgets/-/commit/196ef7afe11a1b5dcc536f8859dc3b6044ea628e))
|
||||
|
||||
### Unknown
|
||||
|
||||
* tests: add unit tests for error and warning message boxes ([`8f104cf`](https://gitlab.psi.ch/bec/bec_widgets/-/commit/8f104cf4024d3a4516e6aba5daa8fb78c85e2bfd))
|
||||
|
||||
## v0.86.0 (2024-07-17)
|
||||
|
||||
BIN
bec_widgets/assets/app_icons/BEC-Dark.png
Normal file
|
After Width: | Height: | Size: 1.6 MiB |
BIN
bec_widgets/assets/designer_icons/code.png
Normal file
|
After Width: | Height: | Size: 4.3 KiB |
BIN
bec_widgets/assets/designer_icons/color_button.png
Normal file
|
After Width: | Height: | Size: 4.7 KiB |
BIN
bec_widgets/assets/designer_icons/device_box.png
Normal file
|
After Width: | Height: | Size: 3.6 KiB |
BIN
bec_widgets/assets/designer_icons/device_combo_box.png
Normal file
|
After Width: | Height: | Size: 3.5 KiB |
BIN
bec_widgets/assets/designer_icons/device_line_edit.png
Normal file
|
After Width: | Height: | Size: 3.2 KiB |
BIN
bec_widgets/assets/designer_icons/dock_area.png
Normal file
|
After Width: | Height: | Size: 2.7 KiB |
BIN
bec_widgets/assets/designer_icons/games.png
Normal file
|
After Width: | Height: | Size: 4.5 KiB |
BIN
bec_widgets/assets/designer_icons/position_indicator.png
Normal file
|
After Width: | Height: | Size: 2.0 KiB |
BIN
bec_widgets/assets/designer_icons/queue.png
Normal file
|
After Width: | Height: | Size: 1.8 KiB |
BIN
bec_widgets/assets/designer_icons/ring_progress.png
Normal file
|
After Width: | Height: | Size: 10 KiB |
BIN
bec_widgets/assets/designer_icons/scan_control.png
Normal file
|
After Width: | Height: | Size: 4.3 KiB |
BIN
bec_widgets/assets/designer_icons/spinner.png
Normal file
|
After Width: | Height: | Size: 6.0 KiB |
BIN
bec_widgets/assets/designer_icons/status.png
Normal file
|
After Width: | Height: | Size: 2.8 KiB |
BIN
bec_widgets/assets/designer_icons/stop.png
Normal file
|
After Width: | Height: | Size: 4.1 KiB |
BIN
bec_widgets/assets/designer_icons/text.png
Normal file
|
After Width: | Height: | Size: 3.2 KiB |
BIN
bec_widgets/assets/designer_icons/toggle.png
Normal file
|
After Width: | Height: | Size: 4.6 KiB |
|
Before Width: | Height: | Size: 3.7 KiB After Width: | Height: | Size: 3.7 KiB |
BIN
bec_widgets/assets/designer_icons/web.png
Normal file
|
After Width: | Height: | Size: 8.5 KiB |
3
bec_widgets/assets/status_icons/error.svg
Normal file
@@ -0,0 +1,3 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" height="48px" viewBox="0 -960 960 960" width="48px" fill="#EA3323">
|
||||
<path d="M479.85-265.87q19.8 0 32.69-12.46 12.9-12.46 12.9-32.26 0-19.8-12.75-32.98-12.74-13.17-32.54-13.17-19.8 0-32.69 13.16-12.9 13.15-12.9 32.95 0 19.8 12.75 32.28 12.74 12.48 32.54 12.48Zm-36.46-166.56h79.22v-262.61h-79.22v262.61Zm36.95 366.56q-86.2 0-161.5-32.39-75.3-32.4-131.74-88.84-56.44-56.44-88.84-131.73-32.39-75.3-32.39-161.59t32.39-161.67q32.4-75.37 88.75-131.34t131.69-88.62q75.34-32.65 161.67-32.65 86.34 0 161.78 32.61 75.45 32.6 131.37 88.5 55.93 55.89 88.55 131.45 32.63 75.56 32.63 161.87 0 86.29-32.65 161.58t-88.62 131.48q-55.97 56.18-131.42 88.76-75.46 32.58-161.67 32.58Z"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 718 B |
3
bec_widgets/assets/status_icons/not_connected.svg
Normal file
@@ -0,0 +1,3 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" height="48px" viewBox="0 -960 960 960" width="48px" fill="#EA3323">
|
||||
<path d="m759.04-283.09-63.13-62q49.31-9.43 84.44-46.02 35.13-36.59 35.13-86.14 0-55.49-39.42-95.08-39.43-39.58-94.93-39.58h-153.3v-79.79h152.74q89.28 0 151.7 62.71Q894.7-566.28 894.7-477q0 63.7-38.26 115.96-38.27 52.26-97.4 77.95ZM596.83-443.61l-65.66-66.78h110.05v66.78h-44.39ZM804.96-56 58.48-802.48 106-850l746.48 746.48L804.96-56ZM443.22-265.87H279.43q-89.28 0-151.7-62.42Q65.3-390.72 65.3-480q0-72.57 43.09-129.54 43.09-56.98 112.09-76.07l70.13 70.7h-11.18q-55.73 0-95.32 39.3-39.59 39.31-39.59 95.61t39.66 95.61q39.66 39.3 95.5 39.3h163.54v79.22ZM319.35-446.61v-66.78h77.3l66.78 66.78H319.35Z"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 721 B |
3
bec_widgets/assets/status_icons/refresh.svg
Normal file
@@ -0,0 +1,3 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" height="48px" viewBox="0 -960 960 960" width="48px" fill="#FFFF55">
|
||||
<path d="M478.3-145.87q-138.65 0-236.39-97.74-97.74-97.74-97.74-236.25t97.74-236.68q97.74-98.16 236.39-98.16 88.4 0 155.45 35.76 67.04 35.76 115.86 98.9V-814.7h66.78v274.92H540.91V-606h165.74q-38.56-57.74-95.3-93.33-56.74-35.58-133.05-35.58-106.88 0-180.89 73.98-74.02 73.99-74.02 180.83 0 106.84 74.02 180.93 74.02 74.08 180.91 74.08 80.16 0 147.74-46.08 67.59-46.09 95.16-121.83H803q-29.56 110.65-119.67 178.89-90.1 68.24-205.03 68.24Z"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 559 B |
3
bec_widgets/assets/status_icons/running.svg
Normal file
@@ -0,0 +1,3 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" height="48px" viewBox="0 -960 960 960" width="48px" fill="#75FB4C">
|
||||
<path d="m419.87-289.52 289.22-289.22-57.31-56.87L419.87-403.7 304.96-518.61l-56.31 56.87 171.22 172.22Zm60.21 223.65q-85.47 0-161.01-32.39-75.53-32.4-131.97-88.84-56.44-56.44-88.84-131.89-32.39-75.46-32.39-160.93 0-86.47 32.39-162.01 32.4-75.53 88.75-131.5t131.85-88.62q75.5-32.65 161.01-32.65 86.52 0 162.12 32.61 75.61 32.6 131.53 88.5 55.93 55.89 88.55 131.45Q894.7-566.58 894.7-480q0 85.55-32.65 161.07-32.65 75.53-88.62 131.9-55.97 56.37-131.42 88.77-75.46 32.39-161.93 32.39Z"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 604 B |
3
bec_widgets/assets/status_icons/warning.svg
Normal file
@@ -0,0 +1,3 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" height="48px" viewBox="0 -960 960 960" width="48px" fill="#F19E39">
|
||||
<path d="M27.56-112.65 480-894.7l452.44 782.05H27.56Zm456.62-125.48q13.15 0 22.61-9.64 9.47-9.65 9.47-22.8t-9.64-22.33q-9.65-9.19-22.8-9.19t-22.61 9.36q-9.47 9.36-9.47 22.51 0 13.15 9.64 22.62 9.65 9.47 22.8 9.47ZM454-348h60v-219.48h-60V-348Z"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 364 B |
3
bec_widgets/assets/toolbar_icons/attach_all.svg
Normal file
@@ -0,0 +1,3 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" height="48px" viewBox="0 -960 960 960" width="48px" fill="#FFFFFF">
|
||||
<path d="m145.26-88.13-57.13-57.13 137.39-137.39H105.87v-79.22h256v256h-79.22v-119.65L145.26-88.13Zm669.48 0L677.91-225.52v119.65h-79.78v-256H854.7v79.22H734.48l137.39 137.39-57.13 57.13Zm-708.87-510v-79.78h119.65L88.13-814.74l57.13-57.13 137.39 137.39V-854.7h79.22v256.57h-256Zm492.26 0V-854.7h79.78v120.22l137.83-138.39 57.13 57.13-138.39 137.83H854.7v79.78H598.13Z"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 489 B |
3
bec_widgets/assets/toolbar_icons/device_box.svg
Normal file
@@ -0,0 +1,3 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" height="48px" viewBox="0 -960 960 960" width="48px" fill="#FFFFFF">
|
||||
<path d="M403.39-158.74 82.13-480l321.26-321.26v642.52Zm153.22 0v-642.52L878.44-480 556.61-158.74Zm81.57-195.74L763.13-480 638.18-605.52v251.04Z"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 266 B |
3
bec_widgets/assets/toolbar_icons/image.svg
Normal file
@@ -0,0 +1,3 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" height="48px" viewBox="0 -960 960 960" width="48px" fill="#FFFFFF">
|
||||
<path d="M185.09-105.87q-32.51 0-55.87-23.35-23.35-23.36-23.35-55.87v-589.82q0-32.74 23.35-56.26 23.36-23.53 55.87-23.53h589.82q32.74 0 56.26 23.53 23.53 23.52 23.53 56.26v589.82q0 32.51-23.53 55.87-23.52 23.35-56.26 23.35H185.09Zm43.56-166.04h503.7L578-481.48l-132 171-93-127-124.35 165.57Z"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 413 B |
3
bec_widgets/assets/toolbar_icons/motor_map.svg
Normal file
@@ -0,0 +1,3 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" height="48px" viewBox="0 -960 960 960" width="48px" fill="#FFFFFF">
|
||||
<path d="M443.78-28.43v-75Q305.65-118 211.54-212.11q-94.11-94.11-108.11-231.67h-75v-72.44h75Q118-654.35 212.11-748.46q94.11-94.11 231.67-108.11v-75h72.44v75q137.56 14 231.67 108.11Q842-654.35 856.57-516.22h75v72.44h-75q-14 137.56-108.11 231.67Q654.35-118 516.22-103.43v75h-72.44Zm36.12-152.66q123.4 0 211.21-87.7 87.8-87.71 87.8-211.11 0-123.4-87.7-211.21-87.71-87.8-211.11-87.8-123.4 0-211.21 87.7-87.8 87.71-87.8 211.11 0 123.4 87.7 211.21 87.71 87.8 211.11 87.8ZM480-330q-63 0-106.5-43.5T330-480q0-63 43.5-106.5T480-630q63 0 106.5 43.5T630-480q0 63-43.5 106.5T480-330Z"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 693 B |
3
bec_widgets/assets/toolbar_icons/progress.svg
Normal file
@@ -0,0 +1,3 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" height="48px" viewBox="0 -960 960 960" width="48px" fill="#FFFFFF">
|
||||
<path d="M480-65.87q-87.51 0-162.97-31.89-75.47-31.89-131.43-87.84-55.95-55.96-87.84-131.43Q65.87-392.49 65.87-480q0-87.47 31.88-162.87 31.89-75.39 87.75-131.51 55.87-56.11 131.41-88.21Q392.44-894.7 480-894.7q15.96 0 27.78 12.16 11.83 12.16 11.83 28.07 0 15.9-11.83 27.73-11.82 11.83-27.78 11.83-139.31 0-237.11 97.8-97.8 97.8-97.8 237.1 0 139.31 97.8 237.12 97.8 97.8 237.1 97.8 139.31 0 237.12-97.8 97.8-97.8 97.8-237.11 0-15.96 11.83-27.78 11.83-11.83 27.73-11.83 15.91 0 28.07 11.83Q894.7-495.96 894.7-480q0 87.56-32.14 163.1-32.14 75.54-88.11 131.44-55.97 55.9-131.44 87.74Q567.55-65.87 480-65.87Z"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 724 B |
3
bec_widgets/assets/toolbar_icons/queue.svg
Normal file
@@ -0,0 +1,3 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" height="48px" viewBox="0 -960 960 960" width="48px" fill="#FFFFFF">
|
||||
<path d="M854.7-105.87H105.87v-209.61H854.7v209.61Zm0-269.61H105.87v-209.04H854.7v209.04Zm0-269.04H105.87V-854.7H854.7v210.18Z"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 248 B |
3
bec_widgets/assets/toolbar_icons/restore_state.svg
Normal file
@@ -0,0 +1,3 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" height="48px" viewBox="0 -960 960 960" width="48px" fill="#FFFFFF">
|
||||
<path d="M336.04-487.39 145.09-678.35v156.65H65.87v-293H358.3v79.79H201.22l191.39 190.95-56.57 56.57ZM145.09-145.87q-32.51 0-55.87-23.35-23.35-23.36-23.35-55.87V-451.7h79.22v226.61H493v79.22H145.09Zm669.82-278.3v-310.74H428.3v-79.79h386.61q32.74 0 56.26 23.53 23.53 23.52 23.53 56.26v310.74h-79.79Zm79.79 60v218.87H553v-218.87h341.7Z"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 455 B |
3
bec_widgets/assets/toolbar_icons/ring_progress.svg
Normal file
@@ -0,0 +1,3 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" height="48px" viewBox="0 -960 960 960" width="48px" fill="#FFFFFF">
|
||||
<path d="M479.87-54q-87.96 0-165.74-33.42-77.79-33.43-135.53-91.18-57.75-57.74-91.18-135.66Q54-392.17 54-480.13q0-87.96 33.42-165.74 33.43-77.79 91.18-135.53 57.74-57.75 135.62-91.18Q392.09-906 480-906h36.22v340.7q24.82 10.69 40.8 33.52Q573-508.96 573-480.17q0 38.43-27.38 65.8Q518.24-387 479.79-387q-38.44 0-65.62-27.37Q387-441.74 387-480.17q0-28.79 15.98-51.61 15.98-22.83 40.8-33.52v-97.22Q378.22-650.39 336-599.76q-42.22 50.63-42.22 119.5 0 78.19 54.12 132.33 54.12 54.15 132.02 54.15 77.91 0 132.1-54.09 54.2-54.1 54.2-131.79 0-42.47-16.28-77.88-16.29-35.42-45.42-60.98l52.05-52.05q38.18 35.64 60.7 84.75 22.51 49.11 22.51 105.82 0 108.57-75.58 183.9-75.59 75.32-184.13 75.32-108.55 0-183.92-75.32-75.37-75.33-75.37-183.89 0-99.62 63.68-172.01 63.67-72.39 159.32-85.65v-93.22Q309.39-817.61 218.2-717.8 127-617.98 127-480.14q0 147.23 103.1 250.18Q333.19-127 480.14-127t249.9-103.02Q833-333.04 833-479.81q0-76.45-29.58-141.91-29.57-65.46-80.81-114.89l51.48-51.48q61.05 58.34 96.48 137.6Q906-571.23 906-479.73q0 87.82-33.42 165.6-33.43 77.79-91.18 135.53-57.74 57.75-135.66 91.18Q567.83-54 479.87-54Z"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.2 KiB |
3
bec_widgets/assets/toolbar_icons/save_state.svg
Normal file
@@ -0,0 +1,3 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" height="48px" viewBox="0 -960 960 960" width="48px" fill="#FFFFFF">
|
||||
<path d="M185.87-98.52v-681.39q0-32.74 23.35-56.26 23.36-23.53 55.87-23.53h429.82q32.74 0 56.26 23.53 23.53 23.52 23.53 56.26v681.39L480-224.17 185.87-98.52Zm79.22-120.39L480-309.18l214.91 90.27v-561H265.09v561Zm0-561h429.82-429.82Z"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 354 B |
3
bec_widgets/assets/toolbar_icons/scan_control.svg
Normal file
@@ -0,0 +1,3 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" height="48px" viewBox="0 -960 960 960" width="48px" fill="#FFFFFF">
|
||||
<path d="M185.09-105.87q-32.93 0-56.08-23.14-23.14-23.15-23.14-56.08v-589.82q0-33.16 23.14-56.47 23.15-23.32 56.08-23.32h589.82q33.16 0 56.47 23.32 23.32 23.31 23.32 56.47v589.82q0 32.93-23.32 56.08-23.31 23.14-56.47 23.14H185.09Zm439.21-79.22h71l79.61-79.61v-40.39H744.3l-120 120ZM281-389.48l141-140 90 90L725.52-654 679-700.52l-167 167-90-90L234.48-436 281-389.48Zm-95.91 204.39h34.56l120-120h-71l-83.56 83.57v36.43Zm350.91 0 120-120h-71l-120 120h71Zm-159.87 0 120-120h-71l-120 120h71Z"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 609 B |
3
bec_widgets/assets/toolbar_icons/status.svg
Normal file
@@ -0,0 +1,3 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" height="48px" viewBox="0 -960 960 960" width="48px" fill="#FFFFFF">
|
||||
<path d="M286.83-277h60v-205h-60v205Zm326.91 0h60v-420h-60v420ZM450-277h60v-118h-60v118Zm0-205h60v-60h-60v60ZM185.09-105.87q-32.51 0-55.87-23.35-23.35-23.36-23.35-55.87v-589.82q0-32.74 23.35-56.26 23.36-23.53 55.87-23.53h589.82q32.74 0 56.26 23.53 23.53 23.52 23.53 56.26v589.82q0 32.51-23.53 55.87-23.52 23.35-56.26 23.35H185.09Z"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 452 B |
3
bec_widgets/assets/toolbar_icons/terminal.svg
Normal file
@@ -0,0 +1,3 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" height="48px" viewBox="0 -960 960 960" width="48px" fill="#FFFFFF">
|
||||
<path d="M145.09-145.87q-32.51 0-55.87-23.35-23.35-23.36-23.35-55.87v-509.82q0-32.74 23.35-56.26 23.36-23.53 55.87-23.53h669.82q32.74 0 56.26 23.53 23.53 23.52 23.53 56.26v509.82q0 32.51-23.53 55.87-23.52 23.35-56.26 23.35H145.09Zm0-79.22h669.82v-425.82H145.09v425.82ZM300-292l-42-42 103-104-104-104 43-42 146 146-146 146Zm190 4v-60h220v60H490Z"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 466 B |
3
bec_widgets/assets/toolbar_icons/waveform.svg
Normal file
@@ -0,0 +1,3 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" height="48px" viewBox="0 -960 960 960" width="48px" fill="#FFFFFF">
|
||||
<path d="M126-206.43 66.43-266 380-579.57 539.43-419.7l298-335 56.14 54.57-352.44 399.26L380-460.43l-254 254Z"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 231 B |
@@ -351,7 +351,7 @@ class BECDockArea(RPCBase, BECGuiClientMixin):
|
||||
name: "str" = None,
|
||||
position: "Literal['bottom', 'top', 'left', 'right', 'above', 'below']" = None,
|
||||
relative_to: "BECDock | None" = None,
|
||||
closable: "bool" = False,
|
||||
closable: "bool" = True,
|
||||
floating: "bool" = False,
|
||||
prefix: "str" = "dock",
|
||||
widget: "str | QWidget | None" = None,
|
||||
|
||||
0
bec_widgets/examples/general_app/__init__.py
Normal file
92
bec_widgets/examples/general_app/general_app.py
Normal file
@@ -0,0 +1,92 @@
|
||||
import os
|
||||
import sys
|
||||
|
||||
from qtpy.QtCore import QSize
|
||||
from qtpy.QtGui import QActionGroup, QIcon
|
||||
from qtpy.QtWidgets import QApplication, QMainWindow, QStyle
|
||||
|
||||
import bec_widgets
|
||||
from bec_widgets.examples.general_app.web_links import BECWebLinksMixin
|
||||
from bec_widgets.utils.colors import apply_theme
|
||||
from bec_widgets.utils.ui_loader import UILoader
|
||||
|
||||
MODULE_PATH = os.path.dirname(bec_widgets.__file__)
|
||||
|
||||
|
||||
class BECGeneralApp(QMainWindow):
|
||||
def __init__(self, parent=None):
|
||||
super(BECGeneralApp, self).__init__(parent)
|
||||
ui_file_path = os.path.join(os.path.dirname(__file__), "general_app.ui")
|
||||
self.load_ui(ui_file_path)
|
||||
|
||||
self.resize(1280, 720)
|
||||
|
||||
self.ini_ui()
|
||||
|
||||
def ini_ui(self):
|
||||
self._setup_icons()
|
||||
self._hook_menubar_docs()
|
||||
self._hook_theme_bar()
|
||||
|
||||
def load_ui(self, ui_file):
|
||||
loader = UILoader(self)
|
||||
self.ui = loader.loader(ui_file)
|
||||
self.setCentralWidget(self.ui)
|
||||
|
||||
def _hook_menubar_docs(self):
|
||||
# BEC Docs
|
||||
self.ui.action_BEC_docs.triggered.connect(BECWebLinksMixin.open_bec_docs)
|
||||
# BEC Widgets Docs
|
||||
self.ui.action_BEC_widgets_docs.triggered.connect(BECWebLinksMixin.open_bec_widgets_docs)
|
||||
# Bug report
|
||||
self.ui.action_bug_report.triggered.connect(BECWebLinksMixin.open_bec_bug_report)
|
||||
|
||||
def change_theme(self, theme):
|
||||
apply_theme(theme)
|
||||
|
||||
def _setup_icons(self):
|
||||
help_icon = QApplication.style().standardIcon(QStyle.SP_MessageBoxQuestion)
|
||||
bug_icon = QApplication.style().standardIcon(QStyle.SP_MessageBoxInformation)
|
||||
computer_icon = QIcon.fromTheme("computer")
|
||||
widget_icon = QIcon(os.path.join(MODULE_PATH, "assets", "designer_icons", "dock_area.png"))
|
||||
|
||||
self.ui.action_BEC_docs.setIcon(help_icon)
|
||||
self.ui.action_BEC_widgets_docs.setIcon(help_icon)
|
||||
self.ui.action_bug_report.setIcon(bug_icon)
|
||||
|
||||
self.ui.central_tab.setTabIcon(0, widget_icon)
|
||||
self.ui.central_tab.setTabIcon(1, computer_icon)
|
||||
|
||||
def _hook_theme_bar(self):
|
||||
self.ui.action_light.setCheckable(True)
|
||||
self.ui.action_dark.setCheckable(True)
|
||||
|
||||
# Create an action group to make sure only one can be checked at a time
|
||||
theme_group = QActionGroup(self)
|
||||
theme_group.addAction(self.ui.action_light)
|
||||
theme_group.addAction(self.ui.action_dark)
|
||||
theme_group.setExclusive(True)
|
||||
|
||||
# Connect the actions to the theme change method
|
||||
|
||||
self.ui.action_light.triggered.connect(lambda: self.change_theme("light"))
|
||||
self.ui.action_dark.triggered.connect(lambda: self.change_theme("dark"))
|
||||
|
||||
self.ui.action_dark.trigger()
|
||||
|
||||
|
||||
def main(): # pragma: no cover
|
||||
|
||||
app = QApplication(sys.argv)
|
||||
icon = QIcon()
|
||||
icon.addFile(
|
||||
os.path.join(MODULE_PATH, "assets", "app_icons", "BEC-Dark.png"), size=QSize(48, 48)
|
||||
)
|
||||
app.setWindowIcon(icon)
|
||||
main_window = BECGeneralApp()
|
||||
main_window.show()
|
||||
sys.exit(app.exec_())
|
||||
|
||||
|
||||
if __name__ == "__main__": # pragma: no cover
|
||||
main()
|
||||
262
bec_widgets/examples/general_app/general_app.ui
Normal file
@@ -0,0 +1,262 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>MainWindow</class>
|
||||
<widget class="QMainWindow" name="MainWindow">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>1718</width>
|
||||
<height>1139</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>MainWindow</string>
|
||||
</property>
|
||||
<property name="tabShape">
|
||||
<enum>QTabWidget::TabShape::Rounded</enum>
|
||||
</property>
|
||||
<widget class="QWidget" name="centralwidget">
|
||||
<layout class="QVBoxLayout" name="verticalLayout_3">
|
||||
<item>
|
||||
<widget class="QTabWidget" name="central_tab">
|
||||
<property name="currentIndex">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<widget class="QWidget" name="dock_area_tab">
|
||||
<attribute name="title">
|
||||
<string>Dock Area</string>
|
||||
</attribute>
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<property name="leftMargin">
|
||||
<number>2</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>1</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>2</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>2</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="BECDockArea" name="dock_area"/>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<widget class="QWidget" name="vscode_tab">
|
||||
<attribute name="icon">
|
||||
<iconset theme="QIcon::ThemeIcon::Computer"/>
|
||||
</attribute>
|
||||
<attribute name="title">
|
||||
<string>Visual Studio Code</string>
|
||||
</attribute>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_2">
|
||||
<property name="leftMargin">
|
||||
<number>2</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>1</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>2</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>2</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="VSCodeEditor" name="vscode"/>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<widget class="QMenuBar" name="menubar">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>1718</width>
|
||||
<height>31</height>
|
||||
</rect>
|
||||
</property>
|
||||
<widget class="QMenu" name="menuHelp">
|
||||
<property name="title">
|
||||
<string>Help</string>
|
||||
</property>
|
||||
<addaction name="action_BEC_docs"/>
|
||||
<addaction name="action_BEC_widgets_docs"/>
|
||||
<addaction name="action_bug_report"/>
|
||||
</widget>
|
||||
<widget class="QMenu" name="menuTheme">
|
||||
<property name="title">
|
||||
<string>Theme</string>
|
||||
</property>
|
||||
<addaction name="action_light"/>
|
||||
<addaction name="action_dark"/>
|
||||
</widget>
|
||||
<addaction name="menuTheme"/>
|
||||
<addaction name="menuHelp"/>
|
||||
</widget>
|
||||
<widget class="QStatusBar" name="statusbar"/>
|
||||
<widget class="QDockWidget" name="dock_scan_control">
|
||||
<property name="windowTitle">
|
||||
<string>Scan Control</string>
|
||||
</property>
|
||||
<attribute name="dockWidgetArea">
|
||||
<number>2</number>
|
||||
</attribute>
|
||||
<widget class="QWidget" name="dockWidgetContents_2">
|
||||
<layout class="QVBoxLayout" name="verticalLayout_4">
|
||||
<item>
|
||||
<widget class="ScanControl" name="scan_control"/>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</widget>
|
||||
<widget class="QDockWidget" name="dock_status_2">
|
||||
<property name="windowTitle">
|
||||
<string>BEC Service Status</string>
|
||||
</property>
|
||||
<attribute name="dockWidgetArea">
|
||||
<number>2</number>
|
||||
</attribute>
|
||||
<widget class="QWidget" name="dockWidgetContents_3">
|
||||
<layout class="QVBoxLayout" name="verticalLayout_5">
|
||||
<property name="leftMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="BECStatusBox" name="bec_status_box_2"/>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</widget>
|
||||
<widget class="QDockWidget" name="dock_queue">
|
||||
<property name="windowTitle">
|
||||
<string>Scan Queue</string>
|
||||
</property>
|
||||
<attribute name="dockWidgetArea">
|
||||
<number>2</number>
|
||||
</attribute>
|
||||
<widget class="QWidget" name="dockWidgetContents_4">
|
||||
<layout class="QVBoxLayout" name="verticalLayout_6">
|
||||
<property name="leftMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="BECQueue" name="bec_queue">
|
||||
<row/>
|
||||
<column/>
|
||||
<column/>
|
||||
<column/>
|
||||
<item row="0" column="0"/>
|
||||
<item row="0" column="1"/>
|
||||
<item row="0" column="2"/>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</widget>
|
||||
<action name="action_BEC_docs">
|
||||
<property name="icon">
|
||||
<iconset theme="QIcon::ThemeIcon::DialogQuestion"/>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>BEC Docs</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="action_BEC_widgets_docs">
|
||||
<property name="icon">
|
||||
<iconset theme="QIcon::ThemeIcon::DialogQuestion"/>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>BEC Widgets Docs</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="action_bug_report">
|
||||
<property name="icon">
|
||||
<iconset theme="QIcon::ThemeIcon::DialogError"/>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Bug Report</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="action_light">
|
||||
<property name="checkable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Light</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="action_dark">
|
||||
<property name="checkable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Dark</string>
|
||||
</property>
|
||||
</action>
|
||||
</widget>
|
||||
<customwidgets>
|
||||
<customwidget>
|
||||
<class>WebsiteWidget</class>
|
||||
<extends>QWebEngineView</extends>
|
||||
<header>website_widget</header>
|
||||
</customwidget>
|
||||
<customwidget>
|
||||
<class>BECQueue</class>
|
||||
<extends>QTableWidget</extends>
|
||||
<header>bec_queue</header>
|
||||
</customwidget>
|
||||
<customwidget>
|
||||
<class>ScanControl</class>
|
||||
<extends>QWidget</extends>
|
||||
<header>scan_control</header>
|
||||
</customwidget>
|
||||
<customwidget>
|
||||
<class>VSCodeEditor</class>
|
||||
<extends>WebsiteWidget</extends>
|
||||
<header>vs_code_editor</header>
|
||||
</customwidget>
|
||||
<customwidget>
|
||||
<class>BECStatusBox</class>
|
||||
<extends>QWidget</extends>
|
||||
<header>bec_status_box</header>
|
||||
</customwidget>
|
||||
<customwidget>
|
||||
<class>BECDockArea</class>
|
||||
<extends>QWidget</extends>
|
||||
<header>dock_area</header>
|
||||
</customwidget>
|
||||
<customwidget>
|
||||
<class>QWebEngineView</class>
|
||||
<extends></extends>
|
||||
<header location="global">QtWebEngineWidgets/QWebEngineView</header>
|
||||
</customwidget>
|
||||
</customwidgets>
|
||||
<resources/>
|
||||
<connections/>
|
||||
</ui>
|
||||
15
bec_widgets/examples/general_app/web_links.py
Normal file
@@ -0,0 +1,15 @@
|
||||
import webbrowser
|
||||
|
||||
|
||||
class BECWebLinksMixin:
|
||||
@staticmethod
|
||||
def open_bec_docs():
|
||||
webbrowser.open("https://beamline-experiment-control.readthedocs.io/en/latest/")
|
||||
|
||||
@staticmethod
|
||||
def open_bec_widgets_docs():
|
||||
webbrowser.open("https://bec.readthedocs.io/projects/bec-widgets/en/latest/")
|
||||
|
||||
@staticmethod
|
||||
def open_bec_bug_report():
|
||||
webbrowser.open("https://gitlab.psi.ch/groups/bec/-/issues/")
|
||||
@@ -7,7 +7,8 @@ import sys
|
||||
|
||||
from bec_ipython_client.main import BECIPythonClient
|
||||
from qtpy.QtWidgets import QApplication
|
||||
from tictactoe import TicTacToe
|
||||
|
||||
from bec_widgets.examples.plugin_example_pyside.tictactoe import TicTacToe
|
||||
|
||||
if __name__ == "__main__": # pragma: no cover
|
||||
app = QApplication(sys.argv)
|
||||
|
||||
@@ -2,8 +2,9 @@
|
||||
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
|
||||
|
||||
from qtpy.QtDesigner import QPyDesignerCustomWidgetCollection
|
||||
from tictactoe import TicTacToe
|
||||
from tictactoeplugin import TicTacToePlugin
|
||||
|
||||
from bec_widgets.examples.plugin_example_pyside.tictactoe import TicTacToe
|
||||
from bec_widgets.examples.plugin_example_pyside.tictactoeplugin import TicTacToePlugin
|
||||
|
||||
# Set PYSIDE_DESIGNER_PLUGINS to point to this directory and load the plugin
|
||||
|
||||
|
||||
@@ -1,10 +1,13 @@
|
||||
# Copyright (C) 2022 The Qt Company Ltd.
|
||||
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
|
||||
import os
|
||||
|
||||
from qtpy.QtDesigner import QDesignerCustomWidgetInterface
|
||||
from qtpy.QtGui import QIcon
|
||||
from tictactoe import TicTacToe
|
||||
from tictactoetaskmenu import TicTacToeTaskMenuFactory
|
||||
|
||||
import bec_widgets
|
||||
from bec_widgets.examples.plugin_example_pyside.tictactoe import TicTacToe
|
||||
from bec_widgets.examples.plugin_example_pyside.tictactoetaskmenu import TicTacToeTaskMenuFactory
|
||||
|
||||
DOM_XML = """
|
||||
<ui language='c++'>
|
||||
@@ -24,6 +27,8 @@ DOM_XML = """
|
||||
</ui>
|
||||
"""
|
||||
|
||||
MODULE_PATH = os.path.dirname(bec_widgets.__file__)
|
||||
|
||||
|
||||
class TicTacToePlugin(QDesignerCustomWidgetInterface): # pragma: no cover
|
||||
def __init__(self):
|
||||
@@ -38,10 +43,11 @@ class TicTacToePlugin(QDesignerCustomWidgetInterface): # pragma: no cover
|
||||
return DOM_XML
|
||||
|
||||
def group(self):
|
||||
return ""
|
||||
return "Games"
|
||||
|
||||
def icon(self):
|
||||
return QIcon()
|
||||
icon_path = os.path.join(MODULE_PATH, "assets", "designer_icons", "games.png")
|
||||
return QIcon(icon_path)
|
||||
|
||||
def includeFile(self):
|
||||
return "tictactoe"
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
# Copyright (C) 2022 The Qt Company Ltd.
|
||||
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
|
||||
|
||||
from qtpy.QtCore import Slot
|
||||
from qtpy.QtDesigner import QExtensionFactory, QPyDesignerTaskMenuExtension
|
||||
from qtpy.QtGui import QAction
|
||||
from qtpy.QtWidgets import QDialog, QDialogButtonBox, QVBoxLayout
|
||||
from tictactoe import TicTacToe
|
||||
|
||||
from bec_widgets.examples.plugin_example_pyside.tictactoe import TicTacToe
|
||||
from bec_widgets.qt_utils.error_popups import SafeSlot as Slot
|
||||
|
||||
|
||||
class TicTacToeDialog(QDialog): # pragma: no cover
|
||||
|
||||
@@ -6,7 +6,7 @@ from qtpy.QtCore import QObject, Qt, Signal, Slot
|
||||
from qtpy.QtWidgets import QApplication, QMessageBox, QPushButton, QVBoxLayout, QWidget
|
||||
|
||||
|
||||
def SafeSlot(*slot_args, **slot_kwargs):
|
||||
def SafeSlot(*slot_args, **slot_kwargs): # pylint: disable=invalid-name
|
||||
"""Function with args, acting like a decorator, applying "error_managed" decorator + Qt Slot
|
||||
to the passed function, to display errors instead of potentially raising an exception
|
||||
|
||||
@@ -34,10 +34,7 @@ class WarningPopupUtility(QObject):
|
||||
Utility class to show warning popups in the application.
|
||||
"""
|
||||
|
||||
def __init__(self, parent=None):
|
||||
super().__init__(parent)
|
||||
|
||||
@Slot(str, str, str, QWidget)
|
||||
@SafeSlot(str, str, str, QWidget)
|
||||
def show_warning_message(self, title, message, detailed_text, widget):
|
||||
msg = QMessageBox(widget)
|
||||
msg.setIcon(QMessageBox.Warning)
|
||||
@@ -60,7 +57,10 @@ class WarningPopupUtility(QObject):
|
||||
self.show_warning_message(title, message, detailed_text, widget)
|
||||
|
||||
|
||||
class ErrorPopupUtility(QObject):
|
||||
_popup_utility_instance = None
|
||||
|
||||
|
||||
class _ErrorPopupUtility(QObject):
|
||||
"""
|
||||
Utility class to manage error popups in the application to show error messages to the users.
|
||||
This class is singleton and the error popup can be enabled or disabled globally or attach to widget methods with decorator @error_managed.
|
||||
@@ -68,24 +68,14 @@ class ErrorPopupUtility(QObject):
|
||||
|
||||
error_occurred = Signal(str, str, QWidget)
|
||||
|
||||
_instance = None
|
||||
_initialized = False
|
||||
|
||||
def __new__(cls, *args, **kwargs):
|
||||
if cls._instance is None:
|
||||
cls._instance = super(ErrorPopupUtility, cls).__new__(cls)
|
||||
cls._instance._initialized = False
|
||||
return cls._instance
|
||||
|
||||
def __init__(self, parent=None):
|
||||
if not self._initialized:
|
||||
super().__init__(parent=parent)
|
||||
self.error_occurred.connect(self.show_error_message)
|
||||
self.enable_error_popup = False
|
||||
self._initialized = True
|
||||
sys.excepthook = self.custom_exception_hook
|
||||
super().__init__(parent=parent)
|
||||
self.error_occurred.connect(self.show_error_message)
|
||||
self.enable_error_popup = False
|
||||
self._initialized = True
|
||||
sys.excepthook = self.custom_exception_hook
|
||||
|
||||
@Slot(str, str, QWidget)
|
||||
@SafeSlot(str, str, QWidget)
|
||||
def show_error_message(self, title, message, widget):
|
||||
detailed_text = self.format_traceback(message)
|
||||
error_message = self.parse_error_message(detailed_text)
|
||||
@@ -157,13 +147,12 @@ class ErrorPopupUtility(QObject):
|
||||
"""
|
||||
self.enable_error_popup = bool(state)
|
||||
|
||||
@classmethod
|
||||
def reset_singleton(cls):
|
||||
"""
|
||||
Reset the singleton instance.
|
||||
"""
|
||||
cls._instance = None
|
||||
cls._initialized = False
|
||||
|
||||
def ErrorPopupUtility():
|
||||
global _popup_utility_instance
|
||||
if not _popup_utility_instance:
|
||||
_popup_utility_instance = _ErrorPopupUtility()
|
||||
return _popup_utility_instance
|
||||
|
||||
|
||||
class ExampleWidget(QWidget): # pragma: no cover
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
from qtpy.QtCore import Slot
|
||||
from qtpy.QtWidgets import QDialog, QDialogButtonBox, QHBoxLayout, QPushButton, QVBoxLayout, QWidget
|
||||
|
||||
from bec_widgets.qt_utils.error_popups import SafeSlot as Slot
|
||||
|
||||
|
||||
class SettingWidget(QWidget):
|
||||
"""
|
||||
|
||||
@@ -5,7 +5,7 @@ from collections import defaultdict
|
||||
|
||||
from qtpy.QtCore import QSize
|
||||
from qtpy.QtGui import QAction, QIcon
|
||||
from qtpy.QtWidgets import QHBoxLayout, QLabel, QToolBar, QToolButton, QWidget
|
||||
from qtpy.QtWidgets import QHBoxLayout, QLabel, QMenu, QToolBar, QToolButton, QWidget
|
||||
|
||||
import bec_widgets
|
||||
|
||||
@@ -98,6 +98,52 @@ class DeviceSelectionAction(ToolBarAction):
|
||||
self.device_combobox.setStyleSheet(f"QComboBox {{ background-color: {color}; }}")
|
||||
|
||||
|
||||
class ExpandableMenuAction(ToolBarAction):
|
||||
"""
|
||||
Action for an expandable menu in the toolbar.
|
||||
|
||||
Args:
|
||||
label (str): The label for the menu.
|
||||
actions (dict): A dictionary of actions to populate the menu.
|
||||
icon_path (str, optional): The path to the icon file. Defaults to None.
|
||||
|
||||
"""
|
||||
|
||||
def __init__(self, label: str, actions: dict, icon_path: str = None):
|
||||
super().__init__(icon_path, label)
|
||||
self.actions = actions
|
||||
self.widgets = defaultdict(dict)
|
||||
|
||||
def add_to_toolbar(self, toolbar: QToolBar, target: QWidget):
|
||||
button = QToolButton(toolbar)
|
||||
if self.icon_path:
|
||||
button.setIcon(QIcon(self.icon_path))
|
||||
button.setText(self.tooltip)
|
||||
button.setPopupMode(QToolButton.InstantPopup)
|
||||
button.setStyleSheet(
|
||||
"""
|
||||
QToolButton {
|
||||
font-size: 14px;
|
||||
}
|
||||
QMenu {
|
||||
font-size: 14px;
|
||||
}
|
||||
"""
|
||||
)
|
||||
menu = QMenu(button)
|
||||
for action_id, action in self.actions.items():
|
||||
sub_action = QAction(action.tooltip, target)
|
||||
if action.icon_path:
|
||||
icon = QIcon()
|
||||
icon.addFile(action.icon_path, size=QSize(20, 20))
|
||||
sub_action.setIcon(icon)
|
||||
sub_action.setCheckable(action.checkable)
|
||||
menu.addAction(sub_action)
|
||||
self.widgets[action_id] = sub_action
|
||||
button.setMenu(menu)
|
||||
toolbar.addWidget(button)
|
||||
|
||||
|
||||
class ModularToolBar(QToolBar):
|
||||
"""Modular toolbar with optional automatic initialization.
|
||||
Args:
|
||||
@@ -108,7 +154,11 @@ class ModularToolBar(QToolBar):
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
self, parent=None, actions=None, target_widget=None, color: str = "rgba(255, 255, 255, 0)"
|
||||
self,
|
||||
parent=None,
|
||||
actions: dict | None = None,
|
||||
target_widget=None,
|
||||
color: str = "rgba(255, 255, 255, 0)",
|
||||
):
|
||||
super().__init__(parent)
|
||||
|
||||
|
||||
@@ -10,11 +10,11 @@ import yaml
|
||||
from bec_lib.utils.import_utils import lazy_import_from
|
||||
from pydantic import BaseModel, Field, field_validator
|
||||
from qtpy.QtCore import QObject, QRunnable, QThreadPool, Signal
|
||||
from qtpy.QtCore import Slot as pyqtSlot
|
||||
from qtpy.QtWidgets import QApplication
|
||||
|
||||
from bec_widgets.cli.rpc_register import RPCRegister
|
||||
from bec_widgets.qt_utils.error_popups import ErrorPopupUtility
|
||||
from bec_widgets.qt_utils.error_popups import SafeSlot as pyqtSlot
|
||||
from bec_widgets.utils.yaml_dialog import load_yaml, load_yaml_gui, save_yaml, save_yaml_gui
|
||||
|
||||
BECDispatcher = lazy_import_from("bec_widgets.utils.bec_dispatcher", ("BECDispatcher",))
|
||||
|
||||
@@ -2,11 +2,10 @@ import itertools
|
||||
import re
|
||||
from typing import Literal
|
||||
|
||||
import bec_qthemes
|
||||
import numpy as np
|
||||
import pyqtgraph as pg
|
||||
import qdarkstyle
|
||||
from pydantic_core import PydanticCustomError
|
||||
from qdarkstyle import DarkPalette, LightPalette
|
||||
from qtpy.QtGui import QColor
|
||||
from qtpy.QtWidgets import QApplication
|
||||
|
||||
@@ -14,7 +13,7 @@ CURRENT_THEME = "dark"
|
||||
|
||||
|
||||
def get_theme_palette():
|
||||
return DarkPalette if CURRENT_THEME == "dark" else LightPalette
|
||||
return bec_qthemes.load_palette(CURRENT_THEME)
|
||||
|
||||
|
||||
def apply_theme(theme: Literal["dark", "light"]):
|
||||
@@ -30,7 +29,7 @@ def apply_theme(theme: Literal["dark", "light"]):
|
||||
pg_widget.setBackground("k" if theme == "dark" else "w")
|
||||
|
||||
# now define stylesheet according to theme and apply it
|
||||
style = qdarkstyle.load_stylesheet(palette=get_theme_palette())
|
||||
style = bec_qthemes.load_stylesheet(theme)
|
||||
app.setStyleSheet(style)
|
||||
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@ import re
|
||||
|
||||
from qtpy.QtCore import QObject
|
||||
|
||||
EXCLUDED_PLUGINS = ["BECConnector", "BECDockArea", "BECDock"]
|
||||
EXCLUDED_PLUGINS = ["BECConnector", "BECDockArea", "BECDock", "BECFigure"]
|
||||
|
||||
|
||||
class DesignerPluginInfo:
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
from bec_lib.endpoints import MessageEndpoints
|
||||
from qtpy.QtCore import Qt, Slot
|
||||
from qtpy.QtWidgets import QHeaderView, QTableWidget, QTableWidgetItem, QWidget
|
||||
from qtpy.QtWidgets import QHBoxLayout, QHeaderView, QTableWidget, QTableWidgetItem, QWidget
|
||||
|
||||
from bec_widgets.utils.bec_connector import ConnectionConfig
|
||||
from bec_widgets.utils.bec_widget import BECWidget
|
||||
|
||||
|
||||
class BECQueue(BECWidget, QTableWidget):
|
||||
class BECQueue(BECWidget, QWidget):
|
||||
"""
|
||||
Widget to display the BEC queue.
|
||||
"""
|
||||
@@ -19,10 +19,14 @@ class BECQueue(BECWidget, QTableWidget):
|
||||
gui_id: str = None,
|
||||
):
|
||||
super().__init__(client, config, gui_id)
|
||||
QTableWidget.__init__(self, parent=parent)
|
||||
self.setColumnCount(3)
|
||||
self.setHorizontalHeaderLabels(["Scan Number", "Type", "Status"])
|
||||
header = self.horizontalHeader()
|
||||
QWidget.__init__(self, parent=parent)
|
||||
self.table = QTableWidget(self)
|
||||
self.layout = QHBoxLayout(self)
|
||||
self.layout.addWidget(self.table)
|
||||
|
||||
self.table.setColumnCount(3)
|
||||
self.table.setHorizontalHeaderLabels(["Scan Number", "Type", "Status"])
|
||||
header = self.table.horizontalHeader()
|
||||
header.setSectionResizeMode(QHeaderView.Stretch)
|
||||
self.bec_dispatcher.connect_slot(self.update_queue, MessageEndpoints.scan_queue_status())
|
||||
self.reset_content()
|
||||
@@ -38,8 +42,8 @@ class BECQueue(BECWidget, QTableWidget):
|
||||
"""
|
||||
# only show the primary queue for now
|
||||
queue_info = content.get("queue", {}).get("primary", {}).get("info", [])
|
||||
self.setRowCount(len(queue_info))
|
||||
self.clearContents()
|
||||
self.table.setRowCount(len(queue_info))
|
||||
self.table.clearContents()
|
||||
|
||||
if not queue_info:
|
||||
self.reset_content()
|
||||
@@ -73,6 +77,8 @@ class BECQueue(BECWidget, QTableWidget):
|
||||
Returns:
|
||||
QTableWidgetItem: The formatted item.
|
||||
"""
|
||||
if not content or not isinstance(content, str):
|
||||
content = ""
|
||||
item = QTableWidgetItem(content)
|
||||
item.setTextAlignment(Qt.AlignHCenter | Qt.AlignVCenter)
|
||||
return item
|
||||
@@ -88,16 +94,16 @@ class BECQueue(BECWidget, QTableWidget):
|
||||
status (str): The status.
|
||||
"""
|
||||
|
||||
self.setItem(index, 0, self.format_item(scan_number))
|
||||
self.setItem(index, 1, self.format_item(scan_type))
|
||||
self.setItem(index, 2, self.format_item(status))
|
||||
self.table.setItem(index, 0, self.format_item(scan_number))
|
||||
self.table.setItem(index, 1, self.format_item(scan_type))
|
||||
self.table.setItem(index, 2, self.format_item(status))
|
||||
|
||||
def reset_content(self):
|
||||
"""
|
||||
Reset the content of the table.
|
||||
"""
|
||||
|
||||
self.setRowCount(1)
|
||||
self.table.setRowCount(1)
|
||||
self.set_row(0, "", "", "")
|
||||
|
||||
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
# Copyright (C) 2022 The Qt Company Ltd.
|
||||
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
|
||||
import os
|
||||
|
||||
from qtpy.QtDesigner import QDesignerCustomWidgetInterface
|
||||
from qtpy.QtGui import QIcon
|
||||
|
||||
import bec_widgets
|
||||
from bec_widgets.widgets.bec_queue.bec_queue import BECQueue
|
||||
|
||||
DOM_XML = """
|
||||
@@ -13,6 +15,8 @@ DOM_XML = """
|
||||
</ui>
|
||||
"""
|
||||
|
||||
MODULE_PATH = os.path.dirname(bec_widgets.__file__)
|
||||
|
||||
|
||||
class BECQueuePlugin(QDesignerCustomWidgetInterface): # pragma: no cover
|
||||
def __init__(self):
|
||||
@@ -27,10 +31,11 @@ class BECQueuePlugin(QDesignerCustomWidgetInterface): # pragma: no cover
|
||||
return DOM_XML
|
||||
|
||||
def group(self):
|
||||
return ""
|
||||
return "BEC Services"
|
||||
|
||||
def icon(self):
|
||||
return QIcon()
|
||||
icon_path = os.path.join(MODULE_PATH, "assets", "designer_icons", "device_line_edit.png")
|
||||
return QIcon(icon_path)
|
||||
|
||||
def includeFile(self):
|
||||
return "bec_queue"
|
||||
|
||||
@@ -56,6 +56,11 @@ class BECServiceStatusMixin(QObject):
|
||||
self.client._update_existing_services()
|
||||
self.services_update.emit(self.client._services_info, self.client._services_metric)
|
||||
|
||||
def cleanup(self):
|
||||
"""Cleanup the BECServiceStatusMixin."""
|
||||
self._service_update_timer.stop()
|
||||
self._service_update_timer.deleteLater()
|
||||
|
||||
|
||||
class BECStatusBox(BECWidget, QWidget):
|
||||
"""An autonomous widget to display the status of BEC services.
|
||||
@@ -290,6 +295,11 @@ class BECStatusBox(BECWidget, QWidget):
|
||||
if objects["item"] == item:
|
||||
objects["widget"].show_popup()
|
||||
|
||||
def cleanup(self):
|
||||
"""Cleanup the BECStatusBox widget."""
|
||||
self.bec_service_status.cleanup()
|
||||
return super().cleanup()
|
||||
|
||||
|
||||
def main():
|
||||
"""Main method to run the BECStatusBox widget."""
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
# Copyright (C) 2022 The Qt Company Ltd.
|
||||
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
|
||||
import os
|
||||
|
||||
from qtpy.QtDesigner import QDesignerCustomWidgetInterface
|
||||
from qtpy.QtGui import QIcon
|
||||
|
||||
import bec_widgets
|
||||
from bec_widgets.widgets.bec_status_box.bec_status_box import BECStatusBox
|
||||
|
||||
DOM_XML = """
|
||||
@@ -13,6 +15,8 @@ DOM_XML = """
|
||||
</ui>
|
||||
"""
|
||||
|
||||
MODULE_PATH = os.path.dirname(bec_widgets.__file__)
|
||||
|
||||
|
||||
class BECStatusBoxPlugin(QDesignerCustomWidgetInterface): # pragma: no cover
|
||||
def __init__(self):
|
||||
@@ -27,10 +31,11 @@ class BECStatusBoxPlugin(QDesignerCustomWidgetInterface): # pragma: no cover
|
||||
return DOM_XML
|
||||
|
||||
def group(self):
|
||||
return ""
|
||||
return "BEC Services"
|
||||
|
||||
def icon(self):
|
||||
return QIcon()
|
||||
icon_path = os.path.join(MODULE_PATH, "assets", "designer_icons", "status.png")
|
||||
return QIcon(icon_path)
|
||||
|
||||
def includeFile(self):
|
||||
return "bec_status_box"
|
||||
|
||||
@@ -2,24 +2,30 @@
|
||||
The widget is bound to be used with the BECStatusBox widget."""
|
||||
|
||||
import enum
|
||||
import os
|
||||
from datetime import datetime
|
||||
|
||||
from bec_lib.utils.import_utils import lazy_import_from
|
||||
from qtpy.QtCore import Qt, Slot
|
||||
from qtpy.QtWidgets import QDialog, QHBoxLayout, QLabel, QStyle, QVBoxLayout, QWidget
|
||||
from qtpy.QtGui import QIcon
|
||||
from qtpy.QtWidgets import QDialog, QHBoxLayout, QLabel, QVBoxLayout, QWidget
|
||||
|
||||
import bec_widgets
|
||||
|
||||
# TODO : Put normal imports back when Pydantic gets faster
|
||||
BECStatus = lazy_import_from("bec_lib.messages", ("BECStatus",))
|
||||
|
||||
MODULE_PATH = os.path.dirname(bec_widgets.__file__)
|
||||
|
||||
|
||||
class IconsEnum(enum.Enum):
|
||||
"""Enum class for icons in the status item widget."""
|
||||
|
||||
RUNNING = "SP_DialogApplyButton"
|
||||
BUSY = "SP_BrowserReload"
|
||||
IDLE = "SP_MessageBoxWarning"
|
||||
ERROR = "SP_DialogCancelButton"
|
||||
NOTCONNECTED = "SP_TitleBarContextHelpButton"
|
||||
RUNNING = os.path.join(MODULE_PATH, "assets", "status_icons", "running.svg")
|
||||
BUSY = os.path.join(MODULE_PATH, "assets", "status_icons", "refresh.svg")
|
||||
IDLE = os.path.join(MODULE_PATH, "assets", "status_icons", "warning.svg")
|
||||
ERROR = os.path.join(MODULE_PATH, "assets", "status_icons", "error.svg")
|
||||
NOTCONNECTED = os.path.join(MODULE_PATH, "assets", "status_icons", "not_connected.svg")
|
||||
|
||||
|
||||
class StatusItem(QWidget):
|
||||
@@ -91,8 +97,8 @@ class StatusItem(QWidget):
|
||||
|
||||
def set_status(self) -> None:
|
||||
"""Set the status icon for the status item widget."""
|
||||
icon_name = IconsEnum[self.config.status].value
|
||||
icon = self.style().standardIcon(getattr(QStyle.StandardPixmap, icon_name))
|
||||
icon_path = IconsEnum[self.config.status].value
|
||||
icon = QIcon(icon_path)
|
||||
self._icon.setPixmap(icon.pixmap(*self.icon_size))
|
||||
self._icon.setAlignment(Qt.AlignmentFlag.AlignRight)
|
||||
|
||||
|
||||
|
Before Width: | Height: | Size: 5.5 KiB |
@@ -3,6 +3,7 @@ import os
|
||||
from qtpy.QtDesigner import QDesignerCustomWidgetInterface
|
||||
from qtpy.QtGui import QIcon
|
||||
|
||||
import bec_widgets
|
||||
from bec_widgets.widgets.color_button.color_button import ColorButton
|
||||
|
||||
DOM_XML = """
|
||||
@@ -11,6 +12,7 @@ DOM_XML = """
|
||||
</widget>
|
||||
</ui>
|
||||
"""
|
||||
MODULE_PATH = os.path.dirname(bec_widgets.__file__)
|
||||
|
||||
|
||||
class ColorButtonPlugin(QDesignerCustomWidgetInterface): # pragma: no cover
|
||||
@@ -29,8 +31,7 @@ class ColorButtonPlugin(QDesignerCustomWidgetInterface): # pragma: no cover
|
||||
return "BEC Buttons"
|
||||
|
||||
def icon(self):
|
||||
current_path = os.path.dirname(__file__)
|
||||
icon_path = os.path.join(current_path, "assets", "color_button.png")
|
||||
icon_path = os.path.join(MODULE_PATH, "assets", "designer_icons", "color_button.png")
|
||||
return QIcon(icon_path)
|
||||
|
||||
def includeFile(self):
|
||||
|
||||
@@ -18,10 +18,11 @@ import pyte
|
||||
from qtpy import QtCore, QtGui, QtWidgets
|
||||
from qtpy.QtCore import QSize, QSocketNotifier, Qt
|
||||
from qtpy.QtCore import Signal as pyqtSignal
|
||||
from qtpy.QtCore import Slot as pyqtSlot
|
||||
from qtpy.QtGui import QClipboard, QTextCursor
|
||||
from qtpy.QtWidgets import QApplication, QHBoxLayout, QScrollBar, QSizePolicy
|
||||
|
||||
from bec_widgets.qt_utils.error_popups import SafeSlot as Slot
|
||||
|
||||
ansi_colors = {
|
||||
"black": "#000000",
|
||||
"red": "#CD0000",
|
||||
@@ -289,7 +290,7 @@ class _TerminalWidget(QtWidgets.QPlainTextEdit):
|
||||
old["value"] = value
|
||||
self.dataReady(self.backend.screen, reset_scroll=False)
|
||||
|
||||
@pyqtSlot(object)
|
||||
@Slot(object)
|
||||
def keyPressEvent(self, event):
|
||||
"""
|
||||
Redirect all keystrokes to the terminal process.
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
# Copyright (C) 2022 The Qt Company Ltd.
|
||||
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
|
||||
import os
|
||||
|
||||
from qtpy.QtDesigner import QDesignerCustomWidgetInterface
|
||||
from qtpy.QtGui import QIcon
|
||||
|
||||
import bec_widgets
|
||||
from bec_widgets.widgets.device_box.device_box import DeviceBox
|
||||
|
||||
DOM_XML = """
|
||||
@@ -13,6 +15,8 @@ DOM_XML = """
|
||||
</ui>
|
||||
"""
|
||||
|
||||
MODULE_PATH = os.path.dirname(bec_widgets.__file__)
|
||||
|
||||
|
||||
class DeviceBoxPlugin(QDesignerCustomWidgetInterface): # pragma: no cover
|
||||
def __init__(self):
|
||||
@@ -30,7 +34,8 @@ class DeviceBoxPlugin(QDesignerCustomWidgetInterface): # pragma: no cover
|
||||
return "Device Control"
|
||||
|
||||
def icon(self):
|
||||
return QIcon()
|
||||
icon_path = os.path.join(MODULE_PATH, "assets", "designer_icons", "device_box.png")
|
||||
return QIcon(icon_path)
|
||||
|
||||
def includeFile(self):
|
||||
return "device_box"
|
||||
|
||||
|
Before Width: | Height: | Size: 2.5 KiB |
@@ -5,6 +5,7 @@ import os
|
||||
from qtpy.QtDesigner import QDesignerCustomWidgetInterface
|
||||
from qtpy.QtGui import QIcon
|
||||
|
||||
import bec_widgets
|
||||
from bec_widgets.widgets.device_combobox.device_combobox import DeviceComboBox
|
||||
|
||||
DOM_XML = """
|
||||
@@ -14,6 +15,8 @@ DOM_XML = """
|
||||
</ui>
|
||||
"""
|
||||
|
||||
MODULE_PATH = os.path.dirname(bec_widgets.__file__)
|
||||
|
||||
|
||||
class DeviceComboBoxPlugin(QDesignerCustomWidgetInterface): # pragma: no cover
|
||||
def __init__(self):
|
||||
@@ -28,11 +31,10 @@ class DeviceComboBoxPlugin(QDesignerCustomWidgetInterface): # pragma: no cover
|
||||
return DOM_XML
|
||||
|
||||
def group(self):
|
||||
return "BEC Device Inputs"
|
||||
return "Device Control"
|
||||
|
||||
def icon(self):
|
||||
current_path = os.path.dirname(__file__)
|
||||
icon_path = os.path.join(current_path, "assets", "device_combobox_icon.png")
|
||||
icon_path = os.path.join(MODULE_PATH, "assets", "designer_icons", "device_combo_box.png")
|
||||
return QIcon(icon_path)
|
||||
|
||||
def includeFile(self):
|
||||
@@ -2,7 +2,6 @@ from typing import TYPE_CHECKING
|
||||
|
||||
from qtpy.QtWidgets import QComboBox
|
||||
|
||||
from bec_widgets.utils.bec_widget import BECWidget
|
||||
from bec_widgets.widgets.base_classes.device_input_base import DeviceInputBase, DeviceInputConfig
|
||||
|
||||
if TYPE_CHECKING:
|
||||
@@ -35,7 +34,7 @@ class DeviceComboBox(DeviceInputBase, QComboBox):
|
||||
):
|
||||
super().__init__(client=client, config=config, gui_id=gui_id)
|
||||
QComboBox.__init__(self, parent=parent)
|
||||
|
||||
self.setMinimumSize(125, 26)
|
||||
self.populate_combobox()
|
||||
|
||||
if arg_name is not None:
|
||||
|
||||
@@ -6,7 +6,7 @@ def main(): # pragma: no cover
|
||||
return
|
||||
from PySide6.QtDesigner import QPyDesignerCustomWidgetCollection
|
||||
|
||||
from bec_widgets.widgets.device_combobox.device_combobox_plugin import DeviceComboBoxPlugin
|
||||
from bec_widgets.widgets.device_combobox.device_combo_box_plugin import DeviceComboBoxPlugin
|
||||
|
||||
QPyDesignerCustomWidgetCollection.addCustomWidget(DeviceComboBoxPlugin())
|
||||
|
||||
|
Before Width: | Height: | Size: 3.3 KiB |
@@ -5,6 +5,7 @@ import os
|
||||
from qtpy.QtDesigner import QDesignerCustomWidgetInterface
|
||||
from qtpy.QtGui import QIcon
|
||||
|
||||
import bec_widgets
|
||||
from bec_widgets.widgets.device_line_edit.device_line_edit import DeviceLineEdit
|
||||
|
||||
DOM_XML = """
|
||||
@@ -14,6 +15,8 @@ DOM_XML = """
|
||||
</ui>
|
||||
"""
|
||||
|
||||
MODULE_PATH = os.path.dirname(bec_widgets.__file__)
|
||||
|
||||
|
||||
class DeviceLineEditPlugin(QDesignerCustomWidgetInterface): # pragma: no cover
|
||||
def __init__(self):
|
||||
@@ -28,11 +31,10 @@ class DeviceLineEditPlugin(QDesignerCustomWidgetInterface): # pragma: no cover
|
||||
return DOM_XML
|
||||
|
||||
def group(self):
|
||||
return "BEC Device Inputs"
|
||||
return "Device Control"
|
||||
|
||||
def icon(self):
|
||||
current_path = os.path.dirname(__file__)
|
||||
icon_path = os.path.join(current_path, "assets", "line_edit_icon.png")
|
||||
icon_path = os.path.join(MODULE_PATH, "assets", "designer_icons", "device_line_edit.png")
|
||||
return QIcon(icon_path)
|
||||
|
||||
def includeFile(self):
|
||||
|
||||
@@ -3,7 +3,7 @@ from __future__ import annotations
|
||||
from typing import TYPE_CHECKING, Any, Literal, Optional
|
||||
|
||||
from pydantic import Field
|
||||
from pyqtgraph.dockarea import Dock
|
||||
from pyqtgraph.dockarea import Dock, DockLabel
|
||||
|
||||
from bec_widgets.cli.rpc_wigdet_handler import widget_handler
|
||||
from bec_widgets.utils import ConnectionConfig, GridLayoutManager
|
||||
@@ -25,6 +25,64 @@ class DockConfig(ConnectionConfig):
|
||||
)
|
||||
|
||||
|
||||
class CustomDockLabel(DockLabel):
|
||||
def updateStyle(self):
|
||||
r = "3px"
|
||||
if self.dim:
|
||||
fg = "#aaa"
|
||||
bg = "#44a"
|
||||
border = "#339"
|
||||
else:
|
||||
fg = "#fff"
|
||||
bg = "#3f4042"
|
||||
border = "#3f4042"
|
||||
|
||||
if self.orientation == "vertical":
|
||||
self.vStyle = """DockLabel {
|
||||
background-color : %s;
|
||||
color : %s;
|
||||
border-top-right-radius: 0px;
|
||||
border-top-left-radius: %s;
|
||||
border-bottom-right-radius: 0px;
|
||||
border-bottom-left-radius: %s;
|
||||
border-width: 0px;
|
||||
border-right: 2px solid %s;
|
||||
padding-top: 3px;
|
||||
padding-bottom: 3px;
|
||||
font-size: %s;
|
||||
}""" % (
|
||||
bg,
|
||||
fg,
|
||||
r,
|
||||
r,
|
||||
border,
|
||||
self.fontSize,
|
||||
)
|
||||
self.setStyleSheet(self.vStyle)
|
||||
else:
|
||||
self.hStyle = """DockLabel {
|
||||
background-color : %s;
|
||||
color : %s;
|
||||
border-top-right-radius: %s;
|
||||
border-top-left-radius: %s;
|
||||
border-bottom-right-radius: 0px;
|
||||
border-bottom-left-radius: 0px;
|
||||
border-width: 0px;
|
||||
border-bottom: 2px solid %s;
|
||||
padding-left: 3px;
|
||||
padding-right: 3px;
|
||||
font-size: %s;
|
||||
}""" % (
|
||||
bg,
|
||||
fg,
|
||||
r,
|
||||
r,
|
||||
border,
|
||||
self.fontSize,
|
||||
)
|
||||
self.setStyleSheet(self.hStyle)
|
||||
|
||||
|
||||
class BECDock(BECWidget, Dock):
|
||||
USER_ACCESS = [
|
||||
"_config_dict",
|
||||
@@ -46,11 +104,12 @@ class BECDock(BECWidget, Dock):
|
||||
def __init__(
|
||||
self,
|
||||
parent: QWidget | None = None,
|
||||
parent_dock_area: BECDockArea | None = None,
|
||||
parent_dock_area: QWidget | None = None,
|
||||
config: DockConfig | None = None,
|
||||
name: str | None = None,
|
||||
client=None,
|
||||
gui_id: str | None = None,
|
||||
closable: bool = True,
|
||||
**kwargs,
|
||||
) -> None:
|
||||
if config is None:
|
||||
@@ -62,7 +121,11 @@ class BECDock(BECWidget, Dock):
|
||||
config = DockConfig(**config)
|
||||
self.config = config
|
||||
super().__init__(client=client, config=config, gui_id=gui_id)
|
||||
Dock.__init__(self, name=name, **kwargs)
|
||||
label = CustomDockLabel(text=name, closable=closable)
|
||||
Dock.__init__(self, name=name, label=label, **kwargs)
|
||||
# Dock.__init__(self, name=name, **kwargs)
|
||||
|
||||
self.parent_dock_area = parent_dock_area
|
||||
|
||||
# Layout Manager
|
||||
self.layout_manager = GridLayoutManager(self.layout)
|
||||
@@ -230,7 +293,7 @@ class BECDock(BECWidget, Dock):
|
||||
Remove the dock from the parent dock area.
|
||||
"""
|
||||
# self.cleanup()
|
||||
self.orig_area.remove_dock(self.name())
|
||||
self.parent_dock_area.remove_dock(self.name())
|
||||
|
||||
def cleanup(self):
|
||||
"""
|
||||
@@ -249,3 +312,4 @@ class BECDock(BECWidget, Dock):
|
||||
"""
|
||||
self.cleanup()
|
||||
super().close()
|
||||
self.parent_dock_area.dock_area.docks.pop(self.name(), None)
|
||||
|
||||
@@ -7,11 +7,18 @@ from pydantic import Field
|
||||
from pyqtgraph.dockarea.DockArea import DockArea
|
||||
from qtpy.QtCore import Qt
|
||||
from qtpy.QtGui import QPainter, QPaintEvent
|
||||
from qtpy.QtWidgets import QWidget
|
||||
from qtpy.QtWidgets import QVBoxLayout, QWidget
|
||||
|
||||
from bec_widgets.qt_utils.toolbar import (
|
||||
ExpandableMenuAction,
|
||||
IconAction,
|
||||
ModularToolBar,
|
||||
SeparatorAction,
|
||||
)
|
||||
from bec_widgets.utils import ConnectionConfig, WidgetContainerUtils
|
||||
from bec_widgets.utils.bec_widget import BECWidget
|
||||
|
||||
from ...qt_utils.error_popups import SafeSlot
|
||||
from .dock import BECDock, DockConfig
|
||||
|
||||
|
||||
@@ -22,7 +29,7 @@ class DockAreaConfig(ConnectionConfig):
|
||||
)
|
||||
|
||||
|
||||
class BECDockArea(BECWidget, DockArea):
|
||||
class BECDockArea(BECWidget, QWidget):
|
||||
USER_ACCESS = [
|
||||
"_config_dict",
|
||||
"panels",
|
||||
@@ -51,15 +58,112 @@ class BECDockArea(BECWidget, DockArea):
|
||||
config = DockAreaConfig(**config)
|
||||
self.config = config
|
||||
super().__init__(client=client, config=config, gui_id=gui_id)
|
||||
DockArea.__init__(self, parent=parent)
|
||||
QWidget.__init__(self, parent=parent)
|
||||
self.layout = QVBoxLayout(self)
|
||||
self.layout.setSpacing(5)
|
||||
self.layout.setContentsMargins(0, 0, 0, 0)
|
||||
|
||||
self._instructions_visible = True
|
||||
|
||||
def paintEvent(self, event: QPaintEvent):
|
||||
self.dock_area = DockArea()
|
||||
self.toolbar = ModularToolBar(
|
||||
actions={
|
||||
"menu_plots": ExpandableMenuAction(
|
||||
label="Add Plot ",
|
||||
actions={
|
||||
"waveform": IconAction(icon_path="waveform.svg", tooltip="Add Waveform"),
|
||||
"image": IconAction(icon_path="image.svg", tooltip="Add Image"),
|
||||
"motor_map": IconAction(icon_path="motor_map.svg", tooltip="Add Motor Map"),
|
||||
},
|
||||
),
|
||||
"separator_0": SeparatorAction(),
|
||||
"menu_devices": ExpandableMenuAction(
|
||||
label="Add Device Control ",
|
||||
actions={
|
||||
"scan_control": IconAction(
|
||||
icon_path="scan_control.svg", tooltip="Add Scan Control"
|
||||
),
|
||||
"device_box": IconAction(
|
||||
icon_path="device_box.svg", tooltip="Add Device Box"
|
||||
),
|
||||
},
|
||||
),
|
||||
"separator_1": SeparatorAction(),
|
||||
"menu_utils": ExpandableMenuAction(
|
||||
label="Add Utils ",
|
||||
actions={
|
||||
"queue": IconAction(icon_path="queue.svg", tooltip="Add Scan Queue"),
|
||||
"vs_code": IconAction(icon_path="terminal.svg", tooltip="Add VS Code"),
|
||||
"status": IconAction(icon_path="status.svg", tooltip="Add BEC Status Box"),
|
||||
"progress_bar": IconAction(
|
||||
icon_path="ring_progress.svg", tooltip="Add Circular ProgressBar"
|
||||
),
|
||||
},
|
||||
),
|
||||
"separator_2": SeparatorAction(),
|
||||
"attach_all": IconAction(
|
||||
icon_path="attach_all.svg", tooltip="Attach all floating docks"
|
||||
),
|
||||
"save_state": IconAction(icon_path="save_state.svg", tooltip="Save Dock State"),
|
||||
"restore_state": IconAction(
|
||||
icon_path="restore_state.svg", tooltip="Restore Dock State"
|
||||
),
|
||||
},
|
||||
target_widget=self,
|
||||
)
|
||||
|
||||
self.layout.addWidget(self.toolbar)
|
||||
self.layout.addWidget(self.dock_area)
|
||||
self._hook_toolbar()
|
||||
|
||||
def _hook_toolbar(self):
|
||||
# Menu Plot
|
||||
self.toolbar.widgets["menu_plots"].widgets["waveform"].triggered.connect(
|
||||
lambda: self.add_dock(widget="BECWaveformWidget", prefix="waveform")
|
||||
)
|
||||
self.toolbar.widgets["menu_plots"].widgets["image"].triggered.connect(
|
||||
lambda: self.add_dock(widget="BECImageWidget", prefix="image")
|
||||
)
|
||||
self.toolbar.widgets["menu_plots"].widgets["motor_map"].triggered.connect(
|
||||
lambda: self.add_dock(widget="BECMotorMapWidget", prefix="motor_map")
|
||||
)
|
||||
|
||||
# Menu Devices
|
||||
self.toolbar.widgets["menu_devices"].widgets["scan_control"].triggered.connect(
|
||||
lambda: self.add_dock(widget="ScanControl", prefix="scan_control")
|
||||
)
|
||||
self.toolbar.widgets["menu_devices"].widgets["device_box"].triggered.connect(
|
||||
lambda: self.add_dock(widget="DeviceBox", prefix="device_box")
|
||||
)
|
||||
|
||||
# Menu Utils
|
||||
self.toolbar.widgets["menu_utils"].widgets["queue"].triggered.connect(
|
||||
lambda: self.add_dock(widget="BECQueue", prefix="queue")
|
||||
)
|
||||
self.toolbar.widgets["menu_utils"].widgets["status"].triggered.connect(
|
||||
lambda: self.add_dock(widget="BECStatusBox", prefix="status")
|
||||
)
|
||||
self.toolbar.widgets["menu_utils"].widgets["vs_code"].triggered.connect(
|
||||
lambda: self.add_dock(widget="VSCodeEditor", prefix="vs_code")
|
||||
)
|
||||
self.toolbar.widgets["menu_utils"].widgets["progress_bar"].triggered.connect(
|
||||
lambda: self.add_dock(widget="RingProgressBar", prefix="progress_bar")
|
||||
)
|
||||
|
||||
# Icons
|
||||
self.toolbar.widgets["attach_all"].action.triggered.connect(self.attach_all)
|
||||
self.toolbar.widgets["save_state"].action.triggered.connect(self.save_state)
|
||||
self.toolbar.widgets["restore_state"].action.triggered.connect(self.restore_state)
|
||||
|
||||
def paintEvent(self, event: QPaintEvent): # TODO decide if we want any default instructions
|
||||
super().paintEvent(event)
|
||||
if self._instructions_visible:
|
||||
painter = QPainter(self)
|
||||
painter.drawText(self.rect(), Qt.AlignCenter, "Add docks using 'add_dock' method")
|
||||
painter.drawText(
|
||||
self.rect(),
|
||||
Qt.AlignCenter,
|
||||
"Add docks using 'add_dock' method from CLI\n or \n Add widget docks using the toolbar",
|
||||
)
|
||||
|
||||
@property
|
||||
def panels(self) -> dict[str, BECDock]:
|
||||
@@ -68,11 +172,11 @@ class BECDockArea(BECWidget, DockArea):
|
||||
Returns:
|
||||
dock_dict(dict): The docks in the dock area.
|
||||
"""
|
||||
return dict(self.docks)
|
||||
return dict(self.dock_area.docks)
|
||||
|
||||
@panels.setter
|
||||
def panels(self, value: dict[str, BECDock]):
|
||||
self.docks = WeakValueDictionary(value)
|
||||
self.dock_area.docks = WeakValueDictionary(value)
|
||||
|
||||
@property
|
||||
def temp_areas(self) -> list:
|
||||
@@ -82,12 +186,13 @@ class BECDockArea(BECWidget, DockArea):
|
||||
Returns:
|
||||
list: The temporary areas in the dock area.
|
||||
"""
|
||||
return list(map(str, self.tempAreas))
|
||||
return list(map(str, self.dock_area.tempAreas))
|
||||
|
||||
@temp_areas.setter
|
||||
def temp_areas(self, value: list):
|
||||
self.tempAreas = list(map(str, value))
|
||||
self.dock_area.tempAreas = list(map(str, value))
|
||||
|
||||
@SafeSlot()
|
||||
def restore_state(
|
||||
self, state: dict = None, missing: Literal["ignore", "error"] = "ignore", extra="bottom"
|
||||
):
|
||||
@@ -101,8 +206,9 @@ class BECDockArea(BECWidget, DockArea):
|
||||
"""
|
||||
if state is None:
|
||||
state = self.config.docks_state
|
||||
self.restoreState(state, missing=missing, extra=extra)
|
||||
self.dock_area.restoreState(state, missing=missing, extra=extra)
|
||||
|
||||
@SafeSlot()
|
||||
def save_state(self) -> dict:
|
||||
"""
|
||||
Save the state of the dock area.
|
||||
@@ -110,7 +216,7 @@ class BECDockArea(BECWidget, DockArea):
|
||||
Returns:
|
||||
dict: The state of the dock area.
|
||||
"""
|
||||
last_state = self.saveState()
|
||||
last_state = self.dock_area.saveState()
|
||||
self.config.docks_state = last_state
|
||||
return last_state
|
||||
|
||||
@@ -121,23 +227,24 @@ class BECDockArea(BECWidget, DockArea):
|
||||
Args:
|
||||
name(str): The name of the dock to remove.
|
||||
"""
|
||||
dock = self.docks.pop(name, None)
|
||||
dock = self.dock_area.docks.pop(name, None)
|
||||
self.config.docks.pop(name, None)
|
||||
if dock:
|
||||
dock.close()
|
||||
if len(self.docks) <= 1:
|
||||
for dock in self.docks.values():
|
||||
if len(self.dock_area.docks) <= 1:
|
||||
for dock in self.dock_area.docks.values():
|
||||
dock.hide_title_bar()
|
||||
|
||||
else:
|
||||
raise ValueError(f"Dock with name {name} does not exist.")
|
||||
|
||||
@SafeSlot(popup_error=True)
|
||||
def add_dock(
|
||||
self,
|
||||
name: str = None,
|
||||
position: Literal["bottom", "top", "left", "right", "above", "below"] = None,
|
||||
relative_to: BECDock | None = None,
|
||||
closable: bool = False,
|
||||
closable: bool = True,
|
||||
floating: bool = False,
|
||||
prefix: str = "dock",
|
||||
widget: str | QWidget | None = None,
|
||||
@@ -167,10 +274,10 @@ class BECDockArea(BECWidget, DockArea):
|
||||
"""
|
||||
if name is None:
|
||||
name = WidgetContainerUtils.generate_unique_widget_id(
|
||||
container=self.docks, prefix=prefix
|
||||
container=self.dock_area.docks, prefix=prefix
|
||||
)
|
||||
|
||||
if name in set(self.docks.keys()):
|
||||
if name in set(self.dock_area.docks.keys()):
|
||||
raise ValueError(f"Dock with name {name} already exists.")
|
||||
|
||||
if position is None:
|
||||
@@ -180,19 +287,21 @@ class BECDockArea(BECWidget, DockArea):
|
||||
dock.config.position = position
|
||||
self.config.docks[name] = dock.config
|
||||
|
||||
self.addDock(dock=dock, position=position, relativeTo=relative_to)
|
||||
self.dock_area.addDock(dock=dock, position=position, relativeTo=relative_to)
|
||||
|
||||
if len(self.docks) <= 1:
|
||||
if len(self.dock_area.docks) <= 1:
|
||||
dock.hide_title_bar()
|
||||
elif len(self.docks) > 1:
|
||||
for dock in self.docks.values():
|
||||
elif len(self.dock_area.docks) > 1:
|
||||
for dock in self.dock_area.docks.values():
|
||||
dock.show_title_bar()
|
||||
|
||||
if widget is not None and isinstance(widget, str):
|
||||
dock.add_widget(widget=widget, row=row, col=col, rowspan=rowspan, colspan=colspan)
|
||||
elif widget is not None and isinstance(widget, QWidget):
|
||||
dock.addWidget(widget, row=row, col=col, rowspan=rowspan, colspan=colspan)
|
||||
if self._instructions_visible:
|
||||
if (
|
||||
self._instructions_visible
|
||||
): # TODO still decide how initial instructions should be handled
|
||||
self._instructions_visible = False
|
||||
self.update()
|
||||
if floating:
|
||||
@@ -209,26 +318,27 @@ class BECDockArea(BECWidget, DockArea):
|
||||
Returns:
|
||||
BECDock: The undocked dock.
|
||||
"""
|
||||
dock = self.docks[dock_name]
|
||||
dock = self.dock_area.docks[dock_name]
|
||||
dock.detach()
|
||||
return dock
|
||||
|
||||
@SafeSlot()
|
||||
def attach_all(self):
|
||||
"""
|
||||
Return all floating docks to the dock area.
|
||||
"""
|
||||
while self.tempAreas:
|
||||
for temp_area in self.tempAreas:
|
||||
self.removeTempArea(temp_area)
|
||||
while self.dock_area.tempAreas:
|
||||
for temp_area in self.dock_area.tempAreas:
|
||||
self.dock_area.removeTempArea(temp_area)
|
||||
|
||||
def clear_all(self):
|
||||
"""
|
||||
Close all docks and remove all temp areas.
|
||||
"""
|
||||
self.attach_all()
|
||||
for dock in dict(self.docks).values():
|
||||
for dock in dict(self.dock_area.docks).values():
|
||||
dock.remove()
|
||||
self.docks.clear()
|
||||
self.dock_area.docks.clear()
|
||||
|
||||
def cleanup(self):
|
||||
"""
|
||||
|
||||
1
bec_widgets/widgets/dock/dock_area.pyproject
Normal file
@@ -0,0 +1 @@
|
||||
{'files': ['dock_area.py','dock.py']}
|
||||
59
bec_widgets/widgets/dock/dock_area_plugin.py
Normal file
@@ -0,0 +1,59 @@
|
||||
# Copyright (C) 2022 The Qt Company Ltd.
|
||||
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
|
||||
import os
|
||||
|
||||
from qtpy.QtDesigner import QDesignerCustomWidgetInterface
|
||||
from qtpy.QtGui import QIcon
|
||||
|
||||
import bec_widgets
|
||||
from bec_widgets.widgets.dock import BECDockArea
|
||||
|
||||
DOM_XML = """
|
||||
<ui language='c++'>
|
||||
<widget class='BECDockArea' name='dock_area'>
|
||||
</widget>
|
||||
</ui>
|
||||
"""
|
||||
|
||||
MODULE_PATH = os.path.dirname(bec_widgets.__file__)
|
||||
|
||||
|
||||
class BECDockAreaPlugin(QDesignerCustomWidgetInterface): # pragma: no cover
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self._form_editor = None
|
||||
|
||||
def createWidget(self, parent):
|
||||
t = BECDockArea(parent)
|
||||
return t
|
||||
|
||||
def domXml(self):
|
||||
return DOM_XML
|
||||
|
||||
def group(self):
|
||||
return "BEC Plots"
|
||||
|
||||
def icon(self):
|
||||
icon_path = os.path.join(MODULE_PATH, "assets", "designer_icons", "dock_area.png")
|
||||
return QIcon(icon_path)
|
||||
|
||||
def includeFile(self):
|
||||
return "dock_area"
|
||||
|
||||
def initialize(self, form_editor):
|
||||
self._form_editor = form_editor
|
||||
|
||||
def isContainer(self):
|
||||
return False
|
||||
|
||||
def isInitialized(self):
|
||||
return self._form_editor is not None
|
||||
|
||||
def name(self):
|
||||
return "BECDockArea"
|
||||
|
||||
def toolTip(self):
|
||||
return "BECDockArea"
|
||||
|
||||
def whatsThis(self):
|
||||
return self.toolTip()
|
||||
15
bec_widgets/widgets/dock/register_dock_area.py
Normal file
@@ -0,0 +1,15 @@
|
||||
def main(): # pragma: no cover
|
||||
from qtpy import PYSIDE6
|
||||
|
||||
if not PYSIDE6:
|
||||
print("PYSIDE6 is not available in the environment. Cannot patch designer.")
|
||||
return
|
||||
from PySide6.QtDesigner import QPyDesignerCustomWidgetCollection
|
||||
|
||||
from bec_widgets.widgets.dock.dock_area_plugin import BECDockAreaPlugin
|
||||
|
||||
QPyDesignerCustomWidgetCollection.addCustomWidget(BECDockAreaPlugin())
|
||||
|
||||
|
||||
if __name__ == "__main__": # pragma: no cover
|
||||
main()
|
||||
@@ -164,7 +164,7 @@ class BECFigure(BECWidget, pg.GraphicsLayoutWidget):
|
||||
def __getitem__(self, key: tuple | str):
|
||||
if isinstance(key, tuple) and len(key) == 2:
|
||||
return self.axes(*key)
|
||||
elif isinstance(key, str):
|
||||
if isinstance(key, str):
|
||||
widget = self._widgets.get(key)
|
||||
if widget is None:
|
||||
raise KeyError(f"No widget with ID {key}")
|
||||
@@ -185,7 +185,7 @@ class BECFigure(BECWidget, pg.GraphicsLayoutWidget):
|
||||
self.change_theme(self.config.theme)
|
||||
|
||||
# widget_config has to be reset for not have each widget config twice when added to the figure
|
||||
widget_configs = [config for config in self.config.widgets.values()]
|
||||
widget_configs = list(self.config.widgets.values())
|
||||
self.config.widgets = {}
|
||||
for widget_config in widget_configs:
|
||||
getattr(self, self.widget_method_map[widget_config.widget_class])(
|
||||
@@ -233,8 +233,8 @@ class BECFigure(BECWidget, pg.GraphicsLayoutWidget):
|
||||
"""Export the plot widget."""
|
||||
try:
|
||||
plot_item = self.widget_list[0]
|
||||
except:
|
||||
raise ValueError("No plot widget available to export.")
|
||||
except Exception as exc:
|
||||
raise ValueError("No plot widget available to export.") from exc
|
||||
|
||||
scene = plot_item.scene()
|
||||
scene.contextMenuItem = plot_item
|
||||
@@ -529,12 +529,12 @@ class BECFigure(BECWidget, pg.GraphicsLayoutWidget):
|
||||
if row is not None and col is not None:
|
||||
if self.getItem(row, col):
|
||||
raise ValueError(f"Position at row {row} and column {col} is already occupied.")
|
||||
else:
|
||||
widget.config.row = row
|
||||
widget.config.col = col
|
||||
|
||||
# Add widget to the figure
|
||||
self.addItem(widget, row=row, col=col)
|
||||
widget.config.row = row
|
||||
widget.config.col = col
|
||||
|
||||
# Add widget to the figure
|
||||
self.addItem(widget, row=row, col=col)
|
||||
else:
|
||||
row, col = self._find_next_empty_position()
|
||||
widget.config.row = row
|
||||
@@ -721,7 +721,7 @@ class BECFigure(BECWidget, pg.GraphicsLayoutWidget):
|
||||
|
||||
# Populate the new grid with widgets' IDs
|
||||
current_idx = 0
|
||||
for widget_id, widget in self._widgets.items():
|
||||
for widget_id in self._widgets:
|
||||
row = current_idx // len(new_grid[0])
|
||||
col = current_idx % len(new_grid[0])
|
||||
new_grid[row][col] = widget_id
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import os
|
||||
|
||||
from qtpy.QtCore import Slot
|
||||
from qtpy.QtWidgets import QVBoxLayout
|
||||
|
||||
from bec_widgets.qt_utils.error_popups import SafeSlot as Slot
|
||||
from bec_widgets.qt_utils.settings_dialog import SettingWidget
|
||||
from bec_widgets.utils import UILoader
|
||||
from bec_widgets.utils.widget_io import WidgetIO
|
||||
|
||||
@@ -7,9 +7,9 @@ import numpy as np
|
||||
from bec_lib.endpoints import MessageEndpoints
|
||||
from pydantic import BaseModel, Field, ValidationError
|
||||
from qtpy.QtCore import QThread
|
||||
from qtpy.QtCore import Slot as pyqtSlot
|
||||
from qtpy.QtWidgets import QWidget
|
||||
|
||||
from bec_widgets.qt_utils.error_popups import SafeSlot as Slot
|
||||
from bec_widgets.utils import EntryValidator
|
||||
from bec_widgets.widgets.figure.plots.image.image_item import BECImageItem, ImageItemConfig
|
||||
from bec_widgets.widgets.figure.plots.image.image_processor import (
|
||||
@@ -247,7 +247,7 @@ class BECImageShow(BECPlotBase):
|
||||
Returns:
|
||||
BECImageItem: The image item.
|
||||
"""
|
||||
image_source = "device_monitor"
|
||||
image_source = "device_monitor_2d"
|
||||
|
||||
image_exits = self._check_image_id(monitor, self._images)
|
||||
if image_exits:
|
||||
@@ -287,7 +287,7 @@ class BECImageShow(BECPlotBase):
|
||||
**kwargs,
|
||||
):
|
||||
image_source = "custom"
|
||||
# image_source = "device_monitor"
|
||||
# image_source = "device_monitor_2d"
|
||||
|
||||
image_exits = self._check_image_id(name, self._images)
|
||||
if image_exits:
|
||||
@@ -500,21 +500,22 @@ class BECImageShow(BECPlotBase):
|
||||
self.update_image(device, data)
|
||||
self.update_vrange(device, self.processor.config.stats)
|
||||
|
||||
@pyqtSlot(dict)
|
||||
def on_image_update(self, msg: dict):
|
||||
@Slot(dict, dict)
|
||||
def on_image_update(self, msg: dict, metadata: dict):
|
||||
"""
|
||||
Update the image of the device monitor from bec.
|
||||
|
||||
Args:
|
||||
msg(dict): The message from bec.
|
||||
metadata(dict): The metadata of the message.
|
||||
"""
|
||||
data = msg["data"]
|
||||
device = msg["device"]
|
||||
image = self._images["device_monitor"][device]
|
||||
image = self._images["device_monitor_2d"][device]
|
||||
image.raw_data = data
|
||||
self.process_image(device, image, data)
|
||||
|
||||
@pyqtSlot(str, np.ndarray)
|
||||
@Slot(str, np.ndarray)
|
||||
def update_image(self, device: str, data: np.ndarray):
|
||||
"""
|
||||
Update the image of the device monitor.
|
||||
@@ -523,10 +524,10 @@ class BECImageShow(BECPlotBase):
|
||||
device(str): The name of the device.
|
||||
data(np.ndarray): The data to be updated.
|
||||
"""
|
||||
image_to_update = self._images["device_monitor"][device]
|
||||
image_to_update = self._images["device_monitor_2d"][device]
|
||||
image_to_update.updateImage(data, autoLevels=image_to_update.config.autorange)
|
||||
|
||||
@pyqtSlot(str, ImageStats)
|
||||
@Slot(str, ImageStats)
|
||||
def update_vrange(self, device: str, stats: ImageStats):
|
||||
"""
|
||||
Update the scaling of the image.
|
||||
@@ -534,7 +535,7 @@ class BECImageShow(BECPlotBase):
|
||||
Args:
|
||||
stats(ImageStats): The statistics of the image.
|
||||
"""
|
||||
image_to_update = self._images["device_monitor"][device]
|
||||
image_to_update = self._images["device_monitor_2d"][device]
|
||||
if image_to_update.config.autorange:
|
||||
image_to_update.auto_update_vrange(stats)
|
||||
|
||||
@@ -547,7 +548,7 @@ class BECImageShow(BECPlotBase):
|
||||
data = image.raw_data
|
||||
self.process_image(image_id, image, data)
|
||||
|
||||
def _connect_device_monitor(self, monitor: str):
|
||||
def _connect_device_monitor_2d(self, monitor: str):
|
||||
"""
|
||||
Connect to the device monitor.
|
||||
|
||||
@@ -561,13 +562,13 @@ class BECImageShow(BECPlotBase):
|
||||
previous_monitor = None
|
||||
if previous_monitor and image_item.connected is True:
|
||||
self.bec_dispatcher.disconnect_slot(
|
||||
self.on_image_update, MessageEndpoints.device_monitor(previous_monitor)
|
||||
self.on_image_update, MessageEndpoints.device_monitor_2d(previous_monitor)
|
||||
)
|
||||
image_item.connected = False
|
||||
if monitor and image_item.connected is False:
|
||||
self.entry_validator.validate_monitor(monitor)
|
||||
self.bec_dispatcher.connect_slot(
|
||||
self.on_image_update, MessageEndpoints.device_monitor(monitor)
|
||||
self.on_image_update, MessageEndpoints.device_monitor_2d(monitor)
|
||||
)
|
||||
image_item.set_monitor(monitor)
|
||||
image_item.connected = True
|
||||
@@ -581,8 +582,8 @@ class BECImageShow(BECPlotBase):
|
||||
if self.single_image is True and len(self.images) > 0:
|
||||
self.remove_image(0)
|
||||
self._images[source][name] = image
|
||||
if source == "device_monitor":
|
||||
self._connect_device_monitor(config.monitor)
|
||||
if source == "device_monitor_2d":
|
||||
self._connect_device_monitor_2d(config.monitor)
|
||||
self.config.images[name] = config
|
||||
if data is not None:
|
||||
image.setImage(data)
|
||||
@@ -668,15 +669,15 @@ class BECImageShow(BECPlotBase):
|
||||
image = self.find_image_by_monitor(image_id)
|
||||
if image:
|
||||
self.bec_dispatcher.disconnect_slot(
|
||||
self.on_image_update, MessageEndpoints.device_monitor(image.config.monitor)
|
||||
self.on_image_update, MessageEndpoints.device_monitor_2d(image.config.monitor)
|
||||
)
|
||||
|
||||
def cleanup(self):
|
||||
"""
|
||||
Clean up the widget.
|
||||
"""
|
||||
for monitor in self._images["device_monitor"]:
|
||||
for monitor in self._images["device_monitor_2d"]:
|
||||
self.bec_dispatcher.disconnect_slot(
|
||||
self.on_image_update, MessageEndpoints.device_monitor(monitor)
|
||||
self.on_image_update, MessageEndpoints.device_monitor_2d(monitor)
|
||||
)
|
||||
self.images.clear()
|
||||
|
||||
@@ -10,9 +10,9 @@ from pydantic import Field, ValidationError, field_validator
|
||||
from pydantic_core import PydanticCustomError
|
||||
from qtpy import QtCore, QtGui
|
||||
from qtpy.QtCore import Signal as pyqtSignal
|
||||
from qtpy.QtCore import Slot
|
||||
from qtpy.QtWidgets import QWidget
|
||||
|
||||
from bec_widgets.qt_utils.error_popups import SafeSlot as Slot
|
||||
from bec_widgets.utils import Colors, EntryValidator
|
||||
from bec_widgets.widgets.figure.plots.plot_base import BECPlotBase, SubplotConfig
|
||||
from bec_widgets.widgets.figure.plots.waveform.waveform import Signal, SignalData
|
||||
@@ -444,7 +444,7 @@ class BECMotorMap(BECPlotBase):
|
||||
return None
|
||||
|
||||
@Slot()
|
||||
def _update_plot(self):
|
||||
def _update_plot(self, _=None):
|
||||
"""Update the motor map plot."""
|
||||
# If the number of points exceeds max_points, delete the oldest points
|
||||
if len(self.database_buffer["x"]) > self.config.max_points:
|
||||
@@ -493,13 +493,14 @@ class BECMotorMap(BECPlotBase):
|
||||
f"Motor position: ({round(float(current_x),precision)}, {round(float(current_y),precision)})"
|
||||
)
|
||||
|
||||
@Slot(dict)
|
||||
def on_device_readback(self, msg: dict) -> None:
|
||||
@Slot(dict, dict)
|
||||
def on_device_readback(self, msg: dict, metadata: dict) -> None:
|
||||
"""
|
||||
Update the motor map plot with the new motor position.
|
||||
|
||||
Args:
|
||||
msg(dict): Message from the device readback.
|
||||
metadata(dict): Metadata of the message.
|
||||
"""
|
||||
if self.motor_x is None or self.motor_y is None:
|
||||
return
|
||||
|
||||
@@ -11,9 +11,9 @@ from bec_lib.endpoints import MessageEndpoints
|
||||
from pydantic import Field, ValidationError, field_validator
|
||||
from pyqtgraph.exporters import MatplotlibExporter
|
||||
from qtpy.QtCore import Signal as pyqtSignal
|
||||
from qtpy.QtCore import Slot as pyqtSlot
|
||||
from qtpy.QtWidgets import QWidget
|
||||
|
||||
from bec_widgets.qt_utils.error_popups import SafeSlot as Slot
|
||||
from bec_widgets.utils import Colors, EntryValidator
|
||||
from bec_widgets.widgets.figure.plots.plot_base import BECPlotBase, SubplotConfig
|
||||
from bec_widgets.widgets.figure.plots.waveform.waveform_curve import (
|
||||
@@ -391,7 +391,7 @@ class BECWaveform(BECPlotBase):
|
||||
self.async_signal_update.emit()
|
||||
self.scan_signal_update.emit()
|
||||
|
||||
@pyqtSlot()
|
||||
@Slot()
|
||||
def auto_range(self):
|
||||
self.plot_item.autoRange()
|
||||
|
||||
@@ -408,7 +408,7 @@ class BECWaveform(BECPlotBase):
|
||||
"""
|
||||
self.plot_item.enableAutoRange(axis, enabled)
|
||||
|
||||
@pyqtSlot()
|
||||
@Slot()
|
||||
def auto_range(self):
|
||||
self.plot_item.autoRange()
|
||||
|
||||
@@ -642,7 +642,7 @@ class BECWaveform(BECPlotBase):
|
||||
self.refresh_dap()
|
||||
return curve
|
||||
|
||||
@pyqtSlot()
|
||||
@Slot()
|
||||
def get_dap_params(self) -> dict:
|
||||
"""
|
||||
Get the DAP parameters of all DAP curves.
|
||||
@@ -655,7 +655,7 @@ class BECWaveform(BECPlotBase):
|
||||
params[curve_id] = curve.dap_params
|
||||
return params
|
||||
|
||||
@pyqtSlot()
|
||||
@Slot()
|
||||
def get_dap_summary(self) -> dict:
|
||||
"""
|
||||
Get the DAP summary of all DAP curves.
|
||||
@@ -921,7 +921,7 @@ class BECWaveform(BECPlotBase):
|
||||
else:
|
||||
raise IndexError(f"Curve order {N} out of range.")
|
||||
|
||||
@pyqtSlot(dict)
|
||||
@Slot(dict)
|
||||
def on_scan_status(self, msg):
|
||||
"""
|
||||
Handle the scan status message.
|
||||
@@ -945,7 +945,7 @@ class BECWaveform(BECPlotBase):
|
||||
for curve_id, curve in self._curves_data["async"].items():
|
||||
self.setup_async(curve.config.signals.y.name)
|
||||
|
||||
@pyqtSlot(dict, dict)
|
||||
@Slot(dict, dict)
|
||||
def on_scan_segment(self, msg: dict, metadata: dict):
|
||||
"""
|
||||
Handle new scan segments and saves data to a dictionary. Linked through bec_dispatcher.
|
||||
@@ -1004,7 +1004,7 @@ class BECWaveform(BECPlotBase):
|
||||
self.update_dap, MessageEndpoints.dap_response(f"{new_scan_id}-{self.gui_id}")
|
||||
)
|
||||
|
||||
@pyqtSlot(str)
|
||||
@Slot(str)
|
||||
def setup_async(self, device: str):
|
||||
self.bec_dispatcher.disconnect_slot(
|
||||
self.on_async_readback, MessageEndpoints.device_async_readback(self.old_scan_id, device)
|
||||
@@ -1020,8 +1020,8 @@ class BECWaveform(BECPlotBase):
|
||||
from_start=True,
|
||||
)
|
||||
|
||||
@pyqtSlot()
|
||||
def refresh_dap(self):
|
||||
@Slot()
|
||||
def refresh_dap(self, _=None):
|
||||
"""
|
||||
Refresh the DAP curves with the latest data from the DAP model MessageEndpoints.dap_response().
|
||||
"""
|
||||
@@ -1069,7 +1069,7 @@ class BECWaveform(BECPlotBase):
|
||||
)
|
||||
self.client.connector.set_and_publish(MessageEndpoints.dap_request(), msg)
|
||||
|
||||
@pyqtSlot(dict, dict)
|
||||
@Slot(dict, dict)
|
||||
def update_dap(self, msg, metadata):
|
||||
self.msg = msg
|
||||
scan_id, x_name, x_entry, y_name, y_entry = msg["dap_request"].content["config"]["args"]
|
||||
@@ -1089,7 +1089,7 @@ class BECWaveform(BECPlotBase):
|
||||
self.dap_summary_update.emit(curve.dap_summary)
|
||||
break
|
||||
|
||||
@pyqtSlot(dict, dict)
|
||||
@Slot(dict, dict)
|
||||
def on_async_readback(self, msg, metadata):
|
||||
"""
|
||||
Get async data readback.
|
||||
@@ -1127,7 +1127,7 @@ class BECWaveform(BECPlotBase):
|
||||
else:
|
||||
curve.setData(data_plot)
|
||||
|
||||
@pyqtSlot()
|
||||
@Slot()
|
||||
def replot_async_curve(self):
|
||||
try:
|
||||
data = self.scan_item.async_data
|
||||
@@ -1152,8 +1152,8 @@ class BECWaveform(BECPlotBase):
|
||||
else:
|
||||
curve.setData(data_x, data_y)
|
||||
|
||||
@pyqtSlot()
|
||||
def _update_scan_curves(self):
|
||||
@Slot()
|
||||
def _update_scan_curves(self, _=None):
|
||||
"""
|
||||
Update the scan curves with the data from the scan segment.
|
||||
"""
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import os
|
||||
|
||||
from qtpy.QtCore import Slot
|
||||
from qtpy.QtWidgets import QVBoxLayout
|
||||
|
||||
from bec_widgets.qt_utils.error_popups import SafeSlot as Slot
|
||||
from bec_widgets.qt_utils.settings_dialog import SettingWidget
|
||||
from bec_widgets.utils import UILoader
|
||||
from bec_widgets.utils.widget_io import WidgetIO
|
||||
|
||||
0
bec_widgets/widgets/position_indicator/__init__.py
Normal file
@@ -1,9 +1,11 @@
|
||||
# Copyright (C) 2022 The Qt Company Ltd.
|
||||
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
|
||||
import os
|
||||
|
||||
from qtpy.QtDesigner import QDesignerCustomWidgetInterface
|
||||
from qtpy.QtGui import QIcon
|
||||
|
||||
import bec_widgets
|
||||
from bec_widgets.widgets.position_indicator.position_indicator import PositionIndicator
|
||||
|
||||
DOM_XML = """
|
||||
@@ -13,6 +15,8 @@ DOM_XML = """
|
||||
</ui>
|
||||
"""
|
||||
|
||||
MODULE_PATH = os.path.dirname(bec_widgets.__file__)
|
||||
|
||||
|
||||
class PositionIndicatorPlugin(QDesignerCustomWidgetInterface): # pragma: no cover
|
||||
def __init__(self):
|
||||
@@ -27,10 +31,11 @@ class PositionIndicatorPlugin(QDesignerCustomWidgetInterface): # pragma: no cov
|
||||
return DOM_XML
|
||||
|
||||
def group(self):
|
||||
return ""
|
||||
return "BEC Utils"
|
||||
|
||||
def icon(self):
|
||||
return QIcon()
|
||||
icon_path = os.path.join(MODULE_PATH, "assets", "designer_icons", "position_indicator.png")
|
||||
return QIcon(icon_path)
|
||||
|
||||
def includeFile(self):
|
||||
return "position_indicator"
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
def main(): # pragma: no cover
|
||||
from qtpy import PYSIDE6
|
||||
|
||||
if not PYSIDE6:
|
||||
print("PYSIDE6 is not available in the environment. Cannot patch designer.")
|
||||
return
|
||||
from PySide6.QtDesigner import QPyDesignerCustomWidgetCollection
|
||||
|
||||
from bec_widgets.widgets.ring_progress_bar.ring_progress_bar_plugin import RingProgressBarPlugin
|
||||
|
||||
QPyDesignerCustomWidgetCollection.addCustomWidget(RingProgressBarPlugin())
|
||||
|
||||
|
||||
if __name__ == "__main__": # pragma: no cover
|
||||
main()
|
||||
@@ -0,0 +1 @@
|
||||
{'files': ['ring_progress_bar.py']}
|
||||
@@ -0,0 +1,58 @@
|
||||
# Copyright (C) 2022 The Qt Company Ltd.
|
||||
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
|
||||
import os
|
||||
|
||||
from qtpy.QtDesigner import QDesignerCustomWidgetInterface
|
||||
from qtpy.QtGui import QIcon
|
||||
|
||||
import bec_widgets
|
||||
from bec_widgets.widgets.ring_progress_bar.ring_progress_bar import RingProgressBar
|
||||
|
||||
DOM_XML = """
|
||||
<ui language='c++'>
|
||||
<widget class='RingProgressBar' name='ring_progress_bar'>
|
||||
</widget>
|
||||
</ui>
|
||||
"""
|
||||
MODULE_PATH = os.path.dirname(bec_widgets.__file__)
|
||||
|
||||
|
||||
class RingProgressBarPlugin(QDesignerCustomWidgetInterface): # pragma: no cover
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self._form_editor = None
|
||||
|
||||
def createWidget(self, parent):
|
||||
t = RingProgressBar(parent)
|
||||
return t
|
||||
|
||||
def domXml(self):
|
||||
return DOM_XML
|
||||
|
||||
def group(self):
|
||||
return "BEC Utils"
|
||||
|
||||
def icon(self):
|
||||
icon_path = os.path.join(MODULE_PATH, "assets", "designer_icons", "ring_progress.png")
|
||||
return QIcon(icon_path)
|
||||
|
||||
def includeFile(self):
|
||||
return "ring_progress_bar"
|
||||
|
||||
def initialize(self, form_editor):
|
||||
self._form_editor = form_editor
|
||||
|
||||
def isContainer(self):
|
||||
return False
|
||||
|
||||
def isInitialized(self):
|
||||
return self._form_editor is not None
|
||||
|
||||
def name(self):
|
||||
return "RingProgressBar"
|
||||
|
||||
def toolTip(self):
|
||||
return "RingProgressBar"
|
||||
|
||||
def whatsThis(self):
|
||||
return self.toolTip()
|
||||
15
bec_widgets/widgets/scan_control/register_scan_control.py
Normal file
@@ -0,0 +1,15 @@
|
||||
def main(): # pragma: no cover
|
||||
from qtpy import PYSIDE6
|
||||
|
||||
if not PYSIDE6:
|
||||
print("PYSIDE6 is not available in the environment. Cannot patch designer.")
|
||||
return
|
||||
from PySide6.QtDesigner import QPyDesignerCustomWidgetCollection
|
||||
|
||||
from bec_widgets.widgets.scan_control.scan_control_plugin import ScanControlPlugin
|
||||
|
||||
QPyDesignerCustomWidgetCollection.addCustomWidget(ScanControlPlugin())
|
||||
|
||||
|
||||
if __name__ == "__main__": # pragma: no cover
|
||||
main()
|
||||
1
bec_widgets/widgets/scan_control/scan_control.pyproject
Normal file
@@ -0,0 +1 @@
|
||||
{'files': ['scan_control.py']}
|
||||
58
bec_widgets/widgets/scan_control/scan_control_plugin.py
Normal file
@@ -0,0 +1,58 @@
|
||||
# Copyright (C) 2022 The Qt Company Ltd.
|
||||
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
|
||||
import os
|
||||
|
||||
from qtpy.QtDesigner import QDesignerCustomWidgetInterface
|
||||
from qtpy.QtGui import QIcon
|
||||
|
||||
import bec_widgets
|
||||
from bec_widgets.widgets.scan_control.scan_control import ScanControl
|
||||
|
||||
DOM_XML = """
|
||||
<ui language='c++'>
|
||||
<widget class='ScanControl' name='scan_control'>
|
||||
</widget>
|
||||
</ui>
|
||||
"""
|
||||
MODULE_PATH = os.path.dirname(bec_widgets.__file__)
|
||||
|
||||
|
||||
class ScanControlPlugin(QDesignerCustomWidgetInterface): # pragma: no cover
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self._form_editor = None
|
||||
|
||||
def createWidget(self, parent):
|
||||
t = ScanControl(parent)
|
||||
return t
|
||||
|
||||
def domXml(self):
|
||||
return DOM_XML
|
||||
|
||||
def group(self):
|
||||
return "Device Control"
|
||||
|
||||
def icon(self):
|
||||
icon_path = os.path.join(MODULE_PATH, "assets", "designer_icons", "scan_control.png")
|
||||
return QIcon(icon_path)
|
||||
|
||||
def includeFile(self):
|
||||
return "scan_control"
|
||||
|
||||
def initialize(self, form_editor):
|
||||
self._form_editor = form_editor
|
||||
|
||||
def isContainer(self):
|
||||
return False
|
||||
|
||||
def isInitialized(self):
|
||||
return self._form_editor is not None
|
||||
|
||||
def name(self):
|
||||
return "ScanControl"
|
||||
|
||||
def toolTip(self):
|
||||
return "ScanControl"
|
||||
|
||||
def whatsThis(self):
|
||||
return self.toolTip()
|
||||
@@ -55,7 +55,7 @@ class SpinnerWidget(QWidget):
|
||||
|
||||
color_palette = get_theme_palette()
|
||||
|
||||
color = QColor(color_palette.COLOR_ACCENT_4)
|
||||
color = QColor(color_palette.accent().color())
|
||||
|
||||
rect.adjust(line_width, line_width, -line_width, -line_width)
|
||||
|
||||
@@ -75,6 +75,10 @@ class SpinnerWidget(QWidget):
|
||||
painter.drawArc(adjusted_rect, self.angle * 16, int(angle_span))
|
||||
painter.end()
|
||||
|
||||
def closeEvent(self, event):
|
||||
self.timer.stop()
|
||||
super().closeEvent(event)
|
||||
|
||||
|
||||
if __name__ == "__main__": # pragma: no cover
|
||||
app = QApplication(sys.argv)
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
# Copyright (C) 2022 The Qt Company Ltd.
|
||||
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
|
||||
import os
|
||||
|
||||
from qtpy.QtDesigner import QDesignerCustomWidgetInterface
|
||||
from qtpy.QtGui import QIcon
|
||||
|
||||
import bec_widgets
|
||||
from bec_widgets.widgets.spinner.spinner import SpinnerWidget
|
||||
|
||||
DOM_XML = """
|
||||
@@ -13,6 +15,8 @@ DOM_XML = """
|
||||
</ui>
|
||||
"""
|
||||
|
||||
MODULE_PATH = os.path.dirname(bec_widgets.__file__)
|
||||
|
||||
|
||||
class SpinnerWidgetPlugin(QDesignerCustomWidgetInterface): # pragma: no cover
|
||||
def __init__(self):
|
||||
@@ -27,10 +31,11 @@ class SpinnerWidgetPlugin(QDesignerCustomWidgetInterface): # pragma: no cover
|
||||
return DOM_XML
|
||||
|
||||
def group(self):
|
||||
return ""
|
||||
return "BEC Utils"
|
||||
|
||||
def icon(self):
|
||||
return QIcon()
|
||||
icon_path = os.path.join(MODULE_PATH, "assets", "designer_icons", "spinner.png")
|
||||
return QIcon(icon_path)
|
||||
|
||||
def includeFile(self):
|
||||
return "spinner_widget"
|
||||
|
||||
|
Before Width: | Height: | Size: 8.2 KiB |
@@ -1,6 +1,6 @@
|
||||
from qtpy.QtCore import Slot
|
||||
from qtpy.QtWidgets import QPushButton
|
||||
|
||||
from bec_widgets.qt_utils.error_popups import SafeSlot as Slot
|
||||
from bec_widgets.utils.bec_widget import BECWidget
|
||||
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@ import os
|
||||
from qtpy.QtDesigner import QDesignerCustomWidgetInterface
|
||||
from qtpy.QtGui import QIcon
|
||||
|
||||
import bec_widgets
|
||||
from bec_widgets.widgets.stop_button.stop_button import StopButton
|
||||
|
||||
DOM_XML = """
|
||||
@@ -14,6 +15,8 @@ DOM_XML = """
|
||||
</ui>
|
||||
"""
|
||||
|
||||
MODULE_PATH = os.path.dirname(bec_widgets.__file__)
|
||||
|
||||
|
||||
class StopButtonPlugin(QDesignerCustomWidgetInterface): # pragma: no cover
|
||||
def __init__(self):
|
||||
@@ -28,11 +31,10 @@ class StopButtonPlugin(QDesignerCustomWidgetInterface): # pragma: no cover
|
||||
return DOM_XML
|
||||
|
||||
def group(self):
|
||||
return "BEC Buttons"
|
||||
return "BEC Utils"
|
||||
|
||||
def icon(self):
|
||||
current_path = os.path.dirname(__file__)
|
||||
icon_path = os.path.join(current_path, "assets", "stop.png")
|
||||
icon_path = os.path.join(MODULE_PATH, "assets", "designer_icons", "stop.png")
|
||||
return QIcon(icon_path)
|
||||
|
||||
def includeFile(self):
|
||||
|
||||
15
bec_widgets/widgets/text_box/register_text_box.py
Normal file
@@ -0,0 +1,15 @@
|
||||
def main(): # pragma: no cover
|
||||
from qtpy import PYSIDE6
|
||||
|
||||
if not PYSIDE6:
|
||||
print("PYSIDE6 is not available in the environment. Cannot patch designer.")
|
||||
return
|
||||
from PySide6.QtDesigner import QPyDesignerCustomWidgetCollection
|
||||
|
||||
from bec_widgets.widgets.text_box.text_box_plugin import TextBoxPlugin
|
||||
|
||||
QPyDesignerCustomWidgetCollection.addCustomWidget(TextBoxPlugin())
|
||||
|
||||
|
||||
if __name__ == "__main__": # pragma: no cover
|
||||
main()
|
||||
1
bec_widgets/widgets/text_box/text_box.pyproject
Normal file
@@ -0,0 +1 @@
|
||||
{'files': ['text_box.py']}
|
||||
58
bec_widgets/widgets/text_box/text_box_plugin.py
Normal file
@@ -0,0 +1,58 @@
|
||||
# Copyright (C) 2022 The Qt Company Ltd.
|
||||
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
|
||||
import os
|
||||
|
||||
from qtpy.QtDesigner import QDesignerCustomWidgetInterface
|
||||
from qtpy.QtGui import QIcon
|
||||
|
||||
import bec_widgets
|
||||
from bec_widgets.widgets.text_box.text_box import TextBox
|
||||
|
||||
DOM_XML = """
|
||||
<ui language='c++'>
|
||||
<widget class='TextBox' name='text_box'>
|
||||
</widget>
|
||||
</ui>
|
||||
"""
|
||||
MODULE_PATH = os.path.dirname(bec_widgets.__file__)
|
||||
|
||||
|
||||
class TextBoxPlugin(QDesignerCustomWidgetInterface): # pragma: no cover
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self._form_editor = None
|
||||
|
||||
def createWidget(self, parent):
|
||||
t = TextBox(parent)
|
||||
return t
|
||||
|
||||
def domXml(self):
|
||||
return DOM_XML
|
||||
|
||||
def group(self):
|
||||
return "BEC Utils"
|
||||
|
||||
def icon(self):
|
||||
icon_path = os.path.join(MODULE_PATH, "assets", "designer_icons", "text.png")
|
||||
return QIcon(icon_path)
|
||||
|
||||
def includeFile(self):
|
||||
return "text_box"
|
||||
|
||||
def initialize(self, form_editor):
|
||||
self._form_editor = form_editor
|
||||
|
||||
def isContainer(self):
|
||||
return False
|
||||
|
||||
def isInitialized(self):
|
||||
return self._form_editor is not None
|
||||
|
||||
def name(self):
|
||||
return "TextBox"
|
||||
|
||||
def toolTip(self):
|
||||
return "TextBox"
|
||||
|
||||
def whatsThis(self):
|
||||
return self.toolTip()
|
||||
@@ -1,9 +1,11 @@
|
||||
# Copyright (C) 2022 The Qt Company Ltd.
|
||||
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
|
||||
import os
|
||||
|
||||
from qtpy.QtDesigner import QDesignerCustomWidgetInterface
|
||||
from qtpy.QtGui import QIcon
|
||||
|
||||
import bec_widgets
|
||||
from bec_widgets.widgets.toggle.toggle import ToggleSwitch
|
||||
|
||||
DOM_XML = """
|
||||
@@ -13,6 +15,8 @@ DOM_XML = """
|
||||
</ui>
|
||||
"""
|
||||
|
||||
MODULE_PATH = os.path.dirname(bec_widgets.__file__)
|
||||
|
||||
|
||||
class ToggleSwitchPlugin(QDesignerCustomWidgetInterface): # pragma: no cover
|
||||
def __init__(self):
|
||||
@@ -27,10 +31,11 @@ class ToggleSwitchPlugin(QDesignerCustomWidgetInterface): # pragma: no cover
|
||||
return DOM_XML
|
||||
|
||||
def group(self):
|
||||
return ""
|
||||
return "BEC Utils"
|
||||
|
||||
def icon(self):
|
||||
return QIcon()
|
||||
icon_path = os.path.join(MODULE_PATH, "assets", "designer_icons", "toggle.png")
|
||||
return QIcon(icon_path)
|
||||
|
||||
def includeFile(self):
|
||||
return "toggle_switch"
|
||||
|
||||
15
bec_widgets/widgets/vscode/register_vs_code_editor.py
Normal file
@@ -0,0 +1,15 @@
|
||||
def main(): # pragma: no cover
|
||||
from qtpy import PYSIDE6
|
||||
|
||||
if not PYSIDE6:
|
||||
print("PYSIDE6 is not available in the environment. Cannot patch designer.")
|
||||
return
|
||||
from PySide6.QtDesigner import QPyDesignerCustomWidgetCollection
|
||||
|
||||
from bec_widgets.widgets.vscode.vs_code_editor_plugin import VSCodeEditorPlugin
|
||||
|
||||
QPyDesignerCustomWidgetCollection.addCustomWidget(VSCodeEditorPlugin())
|
||||
|
||||
|
||||
if __name__ == "__main__": # pragma: no cover
|
||||
main()
|
||||
1
bec_widgets/widgets/vscode/vs_code_editor.pyproject
Normal file
@@ -0,0 +1 @@
|
||||
{'files': ['vscode.py']}
|
||||