1
0
mirror of https://github.com/bec-project/bec_widgets.git synced 2026-04-08 17:57:54 +02:00

Compare commits

...

1013 Commits

Author SHA1 Message Date
302ae90139 docs: added video tutorial section with BSEG YT video 2024-08-06 17:42:15 +02:00
semantic-release
1405068925 0.93.0
Automatically generated by python-semantic-release
2024-08-05 14:11:40 +00:00
5aad401ef8 feat(themes): moved themes to bec_qthemes
This reverts commit fd6ae91993
2024-08-05 14:24:05 +02:00
semantic-release
885dcfda89 0.92.5
Automatically generated by python-semantic-release
2024-08-05 12:20:01 +00:00
30fef929cf fix(spinner): stop timer on close event 2024-08-05 13:54:20 +02:00
1f30dd73a9 fix(status_box): fix cleanup of status box 2024-08-05 13:54:20 +02:00
73cd11e472 test: register all widgets with qtbot and close them 2024-08-05 13:54:20 +02:00
7616ca0e14 refactor(queue): refactored bec queue to inherit only from qwidget 2024-08-05 13:54:20 +02:00
semantic-release
ca29a69779 0.92.4
Automatically generated by python-semantic-release
2024-07-31 07:24:44 +00:00
dcc5fd71ee fix: fix missmatch of signal/slot in image and motormap 2024-07-29 16:05:21 +02:00
semantic-release
fee4901657 0.92.3
Automatically generated by python-semantic-release
2024-07-28 10:05:23 +00:00
71873ddf35 fix(docs): moved to pyside6 2024-07-28 11:17:17 +02:00
semantic-release
f8552ca551 0.92.2
Automatically generated by python-semantic-release
2024-07-28 08:53:31 +00:00
995a795060 fix(widgets): fixed import for tictactoe example 2024-07-28 10:42:32 +02:00
semantic-release
7ab81c5797 0.92.1
Automatically generated by python-semantic-release
2024-07-28 07:04:29 +00:00
bc1e23944c fix: use SafeSlot instead of Slot 2024-07-28 08:54:24 +02:00
a3fe20500a fix: linting 2024-07-28 08:54:24 +02:00
61a4e32deb fix: always add a QApplication for tests 2024-07-28 08:54:24 +02:00
3d681f77e1 fix: add xvfb to draw offscreen 2024-07-28 08:54:24 +02:00
5a9ccfd1f6 fix: reset ErrorPopup singleton between tests 2024-07-26 11:58:07 +02:00
fc57b7a126 fix: metaclass + QObject segfaults PyQt(cpp bindings) 2024-07-26 11:58:07 +02:00
06205e0790 build(ci): install ophyd_devices in editable mode for pipelines 2024-07-25 09:46:58 +02:00
4be6fd6b83 refactor: renamed DeviceMonitor2DMessage 2024-07-25 09:46:58 +02:00
714e1e139e refactor: rename device_monitor to device_monitor_2d 2024-07-25 09:46:58 +02:00
semantic-release
01c0e0b1df 0.92.0
Automatically generated by python-semantic-release
2024-07-24 18:52:56 +00:00
4457ef2147 fix(dock): custom label can be created closable 2024-07-23 22:22:16 +02:00
8ca60d54b3 feat(dock): dock style sheets updated 2024-07-23 22:22:16 +02:00
5696c993dc feat(general_gui): general gui added 2024-07-23 22:22:16 +02:00
1206e15309 fix(device_combobox): set minimum size to 125px 2024-07-23 22:22:16 +02:00
semantic-release
4f2b51b211 0.91.0
Automatically generated by python-semantic-release
2024-07-23 19:59:21 +00:00
1b9c55a46a fix(status_item): icons changed to material design 2024-07-23 20:41:02 +02:00
f4844d2e06 fix(plugins): Qt Designer plugins icons adjusted 2024-07-23 20:41:02 +02:00
06fab0eab9 test(dock_area): tests extended 2024-07-23 20:40:36 +02:00
a16b87ac28 feat(dock_area): plugin added 2024-07-23 20:40:36 +02:00
cce1367a72 feat(dock_area): Added toolbar to dock area to add widgets without CLI interactions 2024-07-23 20:40:36 +02:00
28f26e92a4 feat(toolbar): expandable menu actions 2024-07-23 20:40:36 +02:00
semantic-release
2f5cc3030d 0.90.0
Automatically generated by python-semantic-release
2024-07-23 16:26:53 +00:00
1cf6e32303 fix(axis_setting): fix compatibility for issue with horizontal line for PyQt6 2024-07-23 18:17:35 +02:00
7f49893d2c fix(image_widget): image_widget autorange fixed 2024-07-23 18:17:35 +02:00
ba0d1ea903 refactor(jupyter_console_example): added examples of standalone widgets 2024-07-23 18:17:35 +02:00
70fb276fdf test(image_widget): tests added 2024-07-23 18:17:35 +02:00
43711680ba feat(image_widget): plugin added 2024-07-23 18:17:35 +02:00
3d2ca4855c fix(image_widget): image widget adjusted 2024-07-23 18:17:35 +02:00
fe7e542b19 fix(image): only single monitor image is allowed 2024-07-23 18:17:35 +02:00
501eb923f1 feat(image_widget): all toolbar actions added 2024-07-23 18:17:35 +02:00
c15035b6b7 fix(image): raw data are saved in image item to always have precise processing 2024-07-23 18:17:35 +02:00
6a9317facd feat(image_widget): image_widget added 2024-07-23 18:17:35 +02:00
semantic-release
95e515114a 0.89.0
Automatically generated by python-semantic-release
2024-07-22 19:43:00 +00:00
fd6ae91993 Revert "feat(themes): moved themes to bec_qthemes"
This reverts commit 3798714369
2024-07-22 21:35:07 +02:00
3798714369 feat(themes): moved themes to bec_qthemes 2024-07-22 21:30:35 +02:00
semantic-release
067496b18c 0.88.1
Automatically generated by python-semantic-release
2024-07-22 15:56:01 +00:00
ad112d1f08 refactor(toolbar): generalizations of the ToolBarAction 2024-07-19 18:35:44 +02:00
a3dff7decc fix(plot_base): set_xy autorange moved to plotbase from waveform 2024-07-19 18:35:43 +02:00
2bcaa4256d docs: readthedocs icon path fixed 2024-07-19 16:40:45 +02:00
semantic-release
006f7c01fd 0.88.0
Automatically generated by python-semantic-release
2024-07-19 14:33:09 +00:00
2c8764a27d fix(waveform_widget): plot API unified with BECFigure 2024-07-19 16:12:36 +02:00
50135b5fe9 fix(colormap_selector): compatibility for PyQt6 when using designer fixed 2024-07-18 21:13:13 +02:00
8d764e2d46 test(waveform_widget): test added 2024-07-18 21:13:13 +02:00
6eb313fa76 fix(waveform_widget): adapted for BECWidget base class 2024-07-18 15:50:45 +02:00
1f8ef52b60 feat(waveform_widget): designer plugin added 2024-07-18 15:44:52 +02:00
2be009c647 feat(waveform_widget): switch between drag and rectangle mode 2024-07-18 15:44:52 +02:00
8df6b003e5 feat(waveform_widget): autorange button 2024-07-18 15:44:52 +02:00
7089cf356a fix(waveform_widget): temporary disabled save/load config 2024-07-18 15:44:52 +02:00
1e551d6e96 feat(waveform_widget): dap parameter window 2024-07-18 15:44:52 +02:00
8e588d79c8 fix(waveform_widget): use @SafeSlot decorator for automatic error message 2024-07-18 15:44:52 +02:00
8d93405399 feat(waveform): export to matplotlib window of current scene 2024-07-18 15:44:52 +02:00
6ff6111091 feat(figure): export dialog can be launched from CLI and from toolbar 2024-07-18 15:44:52 +02:00
a8b6ef20cc refactor(icons): icons moved to the assets directory 2024-07-18 15:44:52 +02:00
a8ff1d4cd0 feat(waveform_widget): added error handle utility 2024-07-18 15:44:52 +02:00
47fcb9ebfe refactor(waveform_widget): removed PYSIDE6 check 2024-07-18 15:44:52 +02:00
e8305652fd feat(curve_dialog): add DAP functionality 2024-07-18 15:44:52 +02:00
33495cfe03 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
2024-07-18 15:44:52 +02:00
8ac35d7280 fix(waveform_widget): adapted for changes from improved scan logic from waveform widget 2024-07-18 15:44:52 +02:00
c926a75a79 feat(curve_dialog): curves can be added 2024-07-18 15:44:52 +02:00
fa9b17191d feat(waveform_widget): BECWaveformWidget toolbar added import/export config 2024-07-18 15:44:52 +02:00
755b394c1c feat(waveform_widget): BECWaveformWidget added with toolbar 2024-07-18 15:44:52 +02:00
semantic-release
717df63d62 0.87.1
Automatically generated by python-semantic-release
2024-07-18 13:42:30 +00:00
d75c55b2b1 fix(dock): added hasattr to cleanup method for widgets 2024-07-18 15:28:45 +02:00
e52ee2604c fix: add missing close() call, ensure jupyter console client.shutdown() is called in closeEvent 2024-07-18 11:07:19 +02:00
c7feb6952d refactor: BECWidget is a mixin based on BECConnector, for each QWidget in BEC
Handles closeEvent() and RPC registering/unregistering
2024-07-18 11:07:19 +02:00
d64758f268 fix: BECWidget checks if it is a widget, and implements closeEvent and cleanup 2024-07-17 16:26:03 +02:00
6202d224fe fix: add exit handlers for BECConnection objects 2024-07-17 16:13:46 +02:00
semantic-release
4ac90e1e56 0.87.0
Automatically generated by python-semantic-release
2024-07-17 13:55:11 +00:00
8f104cf402 tests: add unit tests for error and warning message boxes 2024-07-17 13:26:39 +02:00
787f74949b feat(qt_utils): added warning utility with simple API to setup warning message 2024-07-17 13:26:38 +02:00
196ef7afe1 feat(qt_utils): added error handle utility with popup messageBoxes 2024-07-17 13:25:12 +02:00
semantic-release
18ac3ffac0 0.86.0
Automatically generated by python-semantic-release
2024-07-17 09:03:55 +00:00
ba69e7957c feat(toolbar): added separator action 2024-07-17 10:57:31 +02:00
semantic-release
5acb47532c 0.85.1
Automatically generated by python-semantic-release
2024-07-17 08:04:55 +00:00
b5b0aa4f82 fix(waveform): readout_priority dict fixed, not overwritten to 'baseline' key 2024-07-16 21:49:39 +02:00
semantic-release
9a91583ed0 0.85.0
Automatically generated by python-semantic-release
2024-07-16 10:53:17 +00:00
b98fd00ade feat(color_map_selector): added colormap selector with plugin 2024-07-15 22:26:14 +02:00
semantic-release
d5c5e12589 0.84.0
Automatically generated by python-semantic-release
2024-07-15 15:23:17 +00:00
e495fd30c4 fix(waveform): timestamp are not converted to human readable format 2024-07-15 16:51:22 +02:00
8516a1d639 fix(waveform): set_x method various bugs fixed 2024-07-14 19:32:38 +02:00
006992e43c test(waveform): tests extended 2024-07-14 19:32:36 +02:00
48911e9348 refactor(waveform): plot can be prompted without specifying kwargs 2024-07-14 19:29:32 +02:00
e4e1a905d1 fix(waveform): x axis switching logic fixed when axis are not compatible 2024-07-14 19:29:32 +02:00
fc935d9fc8 refactor(jupyter_console_window): added more examples of waveforms 2024-07-14 19:13:15 +02:00
0c6a9f2310 feat(waveform): async readback update implemented for async devices 2024-07-14 19:12:19 +02:00
d23fd8bd07 fix(waveform): dap leaked RID for all daps in current process; dap RID is now f"{scan_id}-{gui_id}" to distinguish for each plot instance 2024-07-14 19:12:19 +02:00
9d6ae87d0f fix(waveform): only one type of x axis allowed; x mode validated 2024-07-14 19:12:19 +02:00
fc5a8bdd8b fix(waveform): data for axis are taken by separate method; validation consolidated 2024-07-14 19:12:19 +02:00
b8717f1327 feat(waveform): data are taken directly from ScanItem which is defined from scan_status endpoint; scan update is triggered from scan_segment; plots can be added just specifying y_name -> best effort for setting x reported device 2024-07-14 19:12:19 +02:00
0aa317aae5 fix(bec_dispatcher): connect_slot can accept kwargs 2024-07-14 19:12:19 +02:00
semantic-release
edc19bdff8 0.83.1
Automatically generated by python-semantic-release
2024-07-14 17:09:51 +00:00
11a7204c98 test(toolbar): added reference pngs for spinner for Darwin 2024-07-14 16:49:33 +02:00
eab7883979 fix(toolbar): default transparent background 2024-07-14 16:49:04 +02:00
2d4249e73a fix: use apply_theme 2024-07-10 15:36:32 +02:00
63db1352ee fix: spinner: update reference image for widget test, use apply_theme 2024-07-10 15:36:06 +02:00
8308115f36 fix: replace pyqtdarktheme by qdarkstyle, add 'apply_theme' function (in utils/colors.py) 2024-07-10 15:36:06 +02:00
semantic-release
02fce838db 0.83.0
Automatically generated by python-semantic-release
2024-07-08 22:48:46 +00:00
360d171355 fix(terminal): added default args to avoid designer crashes on startup 2024-07-09 00:40:15 +02:00
eb26e2a11b test(vscode): fixed vscode tests for new cleanup routine 2024-07-09 00:40:15 +02:00
2b29e34b52 fix(widget): fixed widget cleanup routine 2024-07-09 00:40:15 +02:00
fd8766ed87 fix(bec_widget): added cleanup method to bec widget base class 2024-07-09 00:40:15 +02:00
5de8804da1 test(vscode): improved vscode test 2024-07-09 00:40:15 +02:00
2988fd387e feat: added reference utils to compare renderings of widgets 2024-07-09 00:40:15 +02:00
1b017edfad feat(widgets): added device box with spinner 2024-07-09 00:40:15 +02:00
903ce7d46b fix(website): fixed dummy input 2024-07-09 00:40:15 +02:00
41bcb80167 feat(designer): added option to skip the widget validation for DesignerPluginGenerator 2024-07-09 00:40:15 +02:00
semantic-release
d3f85060ca 0.82.2
Automatically generated by python-semantic-release
2024-07-08 14:24:06 +00:00
90178e2f61 fix(rpc_server): pass cli config to server 2024-07-07 23:25:45 +02:00
semantic-release
b9f9a003a2 0.82.1
Automatically generated by python-semantic-release
2024-07-07 21:12:19 +00:00
734f4c7750 tests(motor_map_widget): tests added 2024-07-07 22:59:02 +02:00
c78cd898f2 fix(motor_map): bug where motors without limits were selected 2024-07-07 22:59:02 +02:00
74a249bd06 test(setting_dialog): tests added 2024-07-07 22:59:02 +02:00
2020953b93 feat(settings_dialog):apply button 2024-07-07 22:59:02 +02:00
3826bb3d9e refactor(setting_dialog): moved to qt_utils 2024-07-07 22:59:02 +02:00
7ffc06f3c7 refactor(toolbar): toolbar moved from widgets to qt_utils 2024-07-07 22:59:02 +02:00
semantic-release
eea1a75d4a 0.82.0
Automatically generated by python-semantic-release
2024-07-07 20:58:30 +00:00
b9bff38b64 feat(toggle): added angular component-like toggle 2024-07-07 22:50:25 +02:00
f04862933f refactor(device_input): DeviceComboBox and DeviceLineEdit moved to top layer of widgets 2024-07-07 18:53:32 +02:00
f5b8375fd3 refactor(stop_button): moved to top layer, plugin added 2024-07-07 18:53:32 +02:00
db1cdf4280 refactor(motor_map_widget): removed restriction of only PySide6 for widget 2024-07-07 18:42:51 +02:00
fa1e86ff07 refactor(color_button): ColorButton moved to top level of widgets 2024-07-07 18:42:51 +02:00
semantic-release
9a95454723 0.81.2
Automatically generated by python-semantic-release
2024-07-07 16:28:57 +00:00
dd1875ea5c fix(waveform): scan_history error check for IndexError 2024-07-07 18:21:08 +02:00
semantic-release
df4fabb32a 0.81.1
Automatically generated by python-semantic-release
2024-07-07 11:45:09 +00:00
99114f14f6 fix(motor_control): temporary remove of motor control widgets 2024-07-07 11:41:07 +02:00
semantic-release
fc3a69bbb0 0.81.0
Automatically generated by python-semantic-release
2024-07-06 12:24:27 +00:00
9594be2606 feat(color_button): can get colors in RGBA or HEX 2024-07-06 12:31:29 +02:00
semantic-release
96fd239608 0.80.1
Automatically generated by python-semantic-release
2024-07-06 10:24:27 +00:00
61de7e9e22 fix(entry_validator): check for entry == "" 2024-07-06 12:16:29 +02:00
semantic-release
24c4cdc39f 0.80.0
Automatically generated by python-semantic-release
2024-07-06 10:14:47 +00:00
fadbf77866 feat(qt5): dropped support for qt5; pyside2 and pyqt5 2024-07-06 12:00:35 +02:00
03819a3d90 feat(plugins): moved plugin dict to dataclass and container 2024-07-06 12:00:35 +02:00
d6d0777113 feat(plugins): added support for pyqt6 ui files 2024-07-05 22:50:19 +02:00
1aa83e0ef1 feat(plugins): added bec widgets base class 2024-07-05 21:22:51 +02:00
semantic-release
b09c644e01 0.79.3
Automatically generated by python-semantic-release
2024-07-05 08:25:43 +00:00
e403870874 fix: changed inheritance to adress qt designer bug in rendering 2024-07-05 08:06:10 +02:00
1586ce2d6c fix: add designer plugin classes 2024-07-05 08:05:20 +02:00
576353cfe8 refactor: simplify logic in bec_status_box 2024-07-05 08:05:20 +02:00
semantic-release
cefc415c98 0.79.2
Automatically generated by python-semantic-release
2024-07-04 22:45:13 +00:00
bc0ef7893e fix: overwrite closeEvent and call super class 2024-07-04 17:27:29 +02:00
semantic-release
0e802d8194 0.79.1
Automatically generated by python-semantic-release
2024-07-03 09:34:03 +00:00
d7718d4dcb fix: use libdir env var to preload Python library, also for Linux platform 2024-07-03 11:07:30 +02:00
semantic-release
4c2e02e912 0.79.0
Automatically generated by python-semantic-release
2024-07-03 08:45:07 +00:00
b8774e0b0b fix(toolbar): change default color to black to match BECFigure theme 2024-07-03 10:34:05 +02:00
6e75642090 feat(motor_map_widget): standalone MotorMap Widget with toolbar + plugin 2024-07-03 10:34:05 +02:00
aaa0d1003d fix(motor_map): fixed bug with residual trace after changing motors 2024-07-03 10:34:05 +02:00
5960918137 feat(motor_map): method to reset history trace 2024-07-03 10:34:05 +02:00
3dc0532df0 fix(widget_io): widget handler adjusted for spinboxes and comboboxes 2024-07-03 10:34:05 +02:00
96863adf53 refactor(toolbar): cleanup and adjusted colors 2024-07-03 10:34:05 +02:00
semantic-release
08425a623e 0.78.1
Automatically generated by python-semantic-release
2024-07-02 21:06:54 +00:00
b787759f44 fix(ui_loader): ui loader is compatible with bec plugins 2024-07-02 22:07:24 +02:00
semantic-release
25ef7c05e6 0.78.0
Automatically generated by python-semantic-release
2024-07-02 20:03:45 +00:00
c36bb80d6a feat(color_button): patched ColorButton from pyqtgraph to be able to be opened in another QDialog 2024-07-02 20:54:39 +02:00
semantic-release
c069f3e1b3 0.77.0
Automatically generated by python-semantic-release
2024-07-02 11:00:18 +00:00
215d59c8bf fix(waveform): scatter 2D brush error 2024-07-02 12:43:56 +02:00
008a33a9b1 fix(figure): API cleanup 2024-07-02 12:43:38 +02:00
3e787234c7 fix(figure): if/else logic corrected in subplot_factory 2024-07-02 12:43:38 +02:00
1173510105 fix(image): processing of already displayed data; closes #106 2024-07-02 12:43:38 +02:00
a391f3018c feat(bec_connector): export config to yaml 2024-07-02 12:43:38 +02:00
b6e1e20b7c fix(bec_figure): full reconstruction with config from other bec figure 2024-07-02 12:43:38 +02:00
572f2fb811 feat(utils): colors added convertor for rgba to hex 2024-07-02 12:43:38 +02:00
2e2d422910 fix(motor_map): API changes updates current visualisation; motor_map can be initialised from config 2024-07-02 12:43:38 +02:00
f0556e4411 fix(image): image add_custom_image fixed, closes #225 2024-07-02 12:43:38 +02:00
4a97105e4b fix(figure): subplot methods consolidated; added subplot factory 2024-07-02 12:43:38 +02:00
797f73c39a fix(image): image can be fully reconstructed from config 2024-07-02 12:43:38 +02:00
b8f796fd3f fix(image_item): vrange added int for pydantic model check 2024-07-02 12:43:38 +02:00
78673ea11a fix(bec_figure): waveforms can be initialised from the config; widgets are deleteLater after removal 2024-07-02 12:43:38 +02:00
c6a14c0768 Resolve "add VT100 console executing BEC as a widget" 2024-07-01 15:11:09 +02:00
semantic-release
70a966d8dc 0.76.1
Automatically generated by python-semantic-release
2024-06-29 12:17:07 +00:00
c42511dd44 fix(plugins): fixes and tests for auto-gen plugins 2024-06-28 13:49:12 +02:00
semantic-release
db62f9e998 0.76.0
Automatically generated by python-semantic-release
2024-06-28 10:20:18 +00:00
0610d2f9f0 fix: fixed qwidget inheritance for ring progress bar 2024-06-28 12:12:18 +02:00
c1dd0ee190 feat(designer): added support for creating designer plugins automatically 2024-06-28 12:12:18 +02:00
a45c407568 fix:parent set as first kwarg TextBox and WebsiteWidget 2024-06-27 17:47:08 +02:00
semantic-release
813f57861c 0.75.0
Automatically generated by python-semantic-release
2024-06-26 19:38:24 +00:00
3faee98ec8 feat(widgets): added simple bec queue widget 2024-06-26 20:42:37 +02:00
ca02132c8d refactor(dispatcher): cleanup 2024-06-26 20:42:37 +02:00
semantic-release
cb4ef25b73 0.74.1
Automatically generated by python-semantic-release
2024-06-26 13:10:06 +00:00
c8b7367815 fix(rings): rings properties updated right after setting 2024-06-26 11:46:21 +02:00
a268caaa30 test(bec_figure): tests for removing widgets with rpc e2e 2024-06-26 11:41:29 +02:00
6b25abff70 fix(motor_map): motor map can be removed from BECFigure with .remove() 2024-06-26 11:24:46 +02:00
21c807f358 chore: sorted dependencies alphabetically 2024-06-26 10:27:46 +02:00
56fdae4275 build: added missing pytest-bec-e2e dependency; closes #219 2024-06-26 10:24:25 +02:00
e6a06c9f43 build: fixed dependency ranges; closes #135 2024-06-26 10:09:48 +02:00
f979a63d3d docs: fixed doc string 2024-06-26 09:46:14 +02:00
semantic-release
327bc54e22 0.74.0
Automatically generated by python-semantic-release
2024-06-25 16:44:56 +00:00
a51b15da3f docs(becfigure): docs added 2024-06-25 18:37:23 +02:00
7271b422f9 test(waveform1d): dap e2e test added 2024-06-25 18:37:23 +02:00
1866ba66c8 feat(waveform1d): dap LMFit model can be added to plot 2024-06-25 18:37:20 +02:00
semantic-release
6175a04a90 0.73.2
Automatically generated by python-semantic-release
2024-06-25 16:24:11 +00:00
7120f3e93b fix(vscode): only run terminate if the process is still alive 2024-06-25 18:17:02 +02:00
acc13183e2 fix(rpc): trigger shutdown of server when gui is terminated 2024-06-25 16:45:39 +02:00
f75fc19c5b fix(rpc): remove of calling "close" and waiting for gui_is_alive 2024-06-25 15:22:29 +02:00
semantic-release
2650c8b8cf 0.73.1
Automatically generated by python-semantic-release
2024-06-25 10:25:13 +00:00
1de3cbf65a fix(ringprogressbar): removed hard-coded endpoint strings 2024-06-25 12:18:14 +02:00
semantic-release
4a9d0c9e44 0.73.0
Automatically generated by python-semantic-release
2024-06-25 10:05:53 +00:00
88ecd05b95 test: add test for imageitem 2024-06-25 11:58:55 +02:00
df812eaad5 feat: add new default scaling of image_item 2024-06-25 11:58:55 +02:00
semantic-release
d62da494c8 0.72.2
Automatically generated by python-semantic-release
2024-06-25 09:23:32 +00:00
e631fc15d8 fix(designer): fixed designer for pyenv and venv; closes #237 2024-06-24 18:38:53 +02:00
semantic-release
ecbf1ce0c8 0.72.1
Automatically generated by python-semantic-release
2024-06-24 14:42:07 +00:00
e5c0087c9a fix: renamed spiral progress bar to ring progress bar; closes #235 2024-06-24 16:37:36 +02:00
4348ed1bb2 test: bugfix to prohibit leackage of mock 2024-06-24 14:04:42 +02:00
semantic-release
5c11fde0a9 0.72.0
Automatically generated by python-semantic-release
2024-06-24 11:47:52 +00:00
4ca1efeeb8 feat(connector): added threadpool wrapper 2024-06-24 13:41:10 +02:00
aa7ce2ea27 tests(status_box_test): temporary disabled tests for status_box due to high rate of failures 2024-06-24 13:15:59 +02:00
semantic-release
174f0cdcb6 0.71.1
Automatically generated by python-semantic-release
2024-06-23 15:30:25 +00:00
860517a321 fix: don't print exception if the auto-update module cannot be found in plugins 2024-06-23 17:23:39 +02:00
semantic-release
66daae6d9e 0.71.0
Automatically generated by python-semantic-release
2024-06-23 12:33:09 +00:00
83001a0d82 test(scan_control):e2e tests added 2024-06-23 12:53:15 +02:00
1b7921a7f2 doc(scan_control): docs added 2024-06-23 12:12:22 +02:00
8badb6adc1 fix(cleanup): cleanup added to device_input widgets and scan_control 2024-06-23 12:12:20 +02:00
37682e7b8a fix(scan_group_box): added row counter based on widgets 2024-06-23 12:11:30 +02:00
56e74a0e7d test(scan_control): tests added 2024-06-23 12:11:30 +02:00
ec4574ed5c fix(scan_control): added default min limit for args bundle if specified 2024-06-23 12:11:30 +02:00
21d20e0fc7 fix(device_line_edit):SizePolicy fixed for 100 horizontal 2024-06-23 12:11:30 +02:00
7ce3a83c58 fix(scan_control): argbox delete later added to prevent overlapping gui if scan changed 2024-06-23 12:11:30 +02:00
6dff1879c4 fix(scan_control): only scans with defined gui_config are allowed 2024-06-23 12:11:30 +02:00
c09644b29d tests WIP 2024-06-23 12:11:30 +02:00
d8cf44134c feat(scan_group_box): scan box for args and kwargs separated from ScanControlGUI code 2024-06-23 12:11:30 +02:00
ca856384f3 fix(WidgetIO): find handlers within base classes 2024-06-23 12:11:30 +02:00
4e2c9df6a4 refactor(device_line_edit): renamed default_device to default 2024-06-23 12:11:27 +02:00
8b822e0fa8 fix(scan_control): adapted widget to scan BEC gui config 2024-06-23 12:10:33 +02:00
67d398caf7 fix(scan_control): scan_control.py combatible with the newest BEC versions, test disabled 2024-06-23 12:10:33 +02:00
semantic-release
c2c27f8279 0.70.0
Automatically generated by python-semantic-release
2024-06-21 17:22:25 +00:00
50b3422528 fix(bec-desiger+plugins): imports fixed, PYSIDE6 check to not enable run plugins with pyqt6 2024-06-21 18:34:20 +02:00
4639eee0b9 feat(bec-designer): automatic plugin discovery 2024-06-21 18:34:20 +02:00
b4b27aea3d feat(device_line_edit): plugin added to bec-designer 2024-06-21 17:40:53 +02:00
e483b282db feat(device_combobox): plugin added to bec-designer 2024-06-21 17:30:02 +02:00
36391db607 feat: added entry point for bec-designer 2024-06-21 16:46:04 +02:00
5362334ff3 feat(utils/bec-designer): added startup script to launched QtDesigner compatible with conda environments 2024-06-21 16:46:04 +02:00
fdf11d8147 docs: fix typo in link 2024-06-21 14:44:50 +02:00
semantic-release
204f653b72 0.69.0
Automatically generated by python-semantic-release
2024-06-21 12:25:10 +00:00
48ae950d57 feat(widgets): added vscode widget 2024-06-21 13:47:05 +02:00
925c893f3f fix(generate_cli): fixed rpc generate for classes without user access; closes #226 2024-06-21 13:47:05 +02:00
semantic-release
b54423a151 0.68.0
Automatically generated by python-semantic-release
2024-06-21 11:35:42 +00:00
ce374163ca fix: ignore GUI server output (any output will go to log file)
If a logger is given to log `_start_log_process`, the server stdout and
stderr streams will be redirected as log entries with levels DEBUG or ERROR
in their parent process
2024-06-21 12:32:59 +02:00
3644f344da feat: properly handle SIGINT (ctrl-c) in BEC GUI server -> calls qapplication.quit() 2024-06-21 12:32:59 +02:00
d1266a1ce1 feat: bec-gui-server: redirect stdout and stderr (if any) as proper debug and error log entries 2024-06-21 12:32:59 +02:00
f7d0b0768a fix: do not create 'BECClient' logger when instantiating BECDispatcher 2024-06-21 12:32:59 +02:00
630616ec72 feat: add logger for BEC GUI server 2024-06-21 12:32:59 +02:00
semantic-release
7f7bef7581 0.67.0
Automatically generated by python-semantic-release
2024-06-21 08:42:15 +00:00
d2f2b206bb refactor: Change inheritance to QTreeWidget from QWidget 2024-06-21 10:27:15 +02:00
6fa1c06053 docs: add widget to documentation 2024-06-21 10:26:14 +02:00
5d4ca816cd test: add test suite for bec_status_box and status_item 2024-06-20 18:08:26 +02:00
443b6c1d7b feat: introduce BECStatusBox Widget 2024-06-20 17:45:44 +02:00
505a5ec833 Update file requirements.txt 2024-06-20 17:28:53 +02:00
semantic-release
3a7289bf5e 0.66.1
Automatically generated by python-semantic-release
2024-06-20 14:49:13 +00:00
2718bc6247 fix: fixed shutdown for pyside 2024-06-20 16:31:16 +02:00
semantic-release
515d2651bf 0.66.0
Automatically generated by python-semantic-release
2024-06-20 12:19:39 +00:00
ef25f56380 feat(rpc): discover widgets automatically 2024-06-20 14:04:30 +02:00
semantic-release
5b280ccc1e 0.65.2
Automatically generated by python-semantic-release
2024-06-20 12:04:07 +00:00
cbbd23aa33 fix(pyqt): webengine must be imported before qcoreapplication 2024-06-20 13:50:44 +02:00
semantic-release
860d0ad014 0.65.1
Automatically generated by python-semantic-release
2024-06-20 08:15:53 +00:00
fa344a5799 fix: prevent segfault by closing the QCoreApplication, if any 2024-06-20 10:05:23 +02:00
semantic-release
3919de5bd5 0.65.0
Automatically generated by python-semantic-release
2024-06-20 07:36:53 +00:00
1a0a98a453 test(device_input): tests added 2024-06-20 09:25:43 +02:00
d79f7e9ccd fix(device_input_base): bug with setting config and overwriting default device and filter 2024-06-20 09:25:43 +02:00
50e41ff261 feat(device_input): DeviceLineEdit with QCompleter added 2024-06-20 09:25:43 +02:00
430b282039 feat(device_combobox): DeviceInputBase and DeviceComboBox added 2024-06-20 09:25:43 +02:00
semantic-release
17133771bb 0.64.2
Automatically generated by python-semantic-release
2024-06-19 14:50:20 +00:00
e5a7d47b21 fix(client_utils): added close rpc command to shutdown of gui from bec_ipython_client 2024-06-19 16:36:05 +02:00
semantic-release
71ec61e27b 0.64.1
Automatically generated by python-semantic-release
2024-06-19 11:54:08 +00:00
b3575eb068 test: moved rpc_classes test 2024-06-19 13:38:46 +02:00
216511b951 fix(widgets): removed widget module import of sub widgets 2024-06-19 13:38:46 +02:00
6dabbf874f refactor(utils): moved get_rpc_widgets to plugin_utils 2024-06-19 13:38:46 +02:00
semantic-release
d5aad06c88 0.64.0
Automatically generated by python-semantic-release
2024-06-19 11:35:37 +00:00
5d6672069e fix(plot_base): font size is set with setScale which is scaling the whole legend window 2024-06-19 13:26:10 +02:00
140ad83380 test: add tests 2024-06-19 13:26:10 +02:00
ea805d1362 feat: add option to change size of the fonts 2024-06-19 13:26:10 +02:00
9e16f2faf9 docs: fix links in developer section 2024-06-14 14:34:26 +02:00
2a36d9364f docs: refactor developer section, add widget tutorial 2024-06-14 14:24:38 +02:00
27426ce7a5 ci: add job optional dependency check 2024-06-14 11:47:42 +02:00
semantic-release
69adadd6d7 0.63.2
Automatically generated by python-semantic-release
2024-06-14 08:14:22 +00:00
6f96498de6 fix: do not import "server" in client, prevents from having trouble with QApplication creation order
Like with QtWebEngine
2024-06-13 15:14:30 +02:00
836b6e64f6 Reapply "feat: implement non-polling, interruptible waiting of gui instruction response with timeout"
This reverts commit fe04dd80e5.
2024-06-13 15:14:30 +02:00
semantic-release
fab7dd7eec 0.63.1
Automatically generated by python-semantic-release
2024-06-13 13:12:54 +00:00
9263f8ef5c fix: just terminate the remote process in close() instead of communicating
The proper finalization sequence will be executed by the remote process
on SIGTERM
2024-06-13 14:56:21 +02:00
semantic-release
658728efef 0.63.0
Automatically generated by python-semantic-release
2024-06-13 12:47:57 +00:00
6b8432f5b2 refactor: add pydantic config, add change_theme 2024-06-13 14:08:22 +02:00
bc709c4184 docs: add documentation 2024-06-13 08:14:50 +02:00
b49462abeb test: add test for text box 2024-06-13 08:14:50 +02:00
d9d4e3c9bf feat: add textbox widget 2024-06-13 08:08:46 +02:00
fe04dd80e5 Revert "feat: implement non-polling, interruptible waiting of gui instruction response with timeout"
This reverts commit abc6caa2d0
2024-06-12 17:19:08 +02:00
semantic-release
718950cf0d 0.62.0
Automatically generated by python-semantic-release
2024-06-12 10:01:48 +00:00
17a0068757 doc: add documentation about creating custom GUI applications embedding BEC Widgets 2024-06-12 11:54:47 +02:00
abc6caa2d0 feat: implement non-polling, interruptible waiting of gui instruction response with timeout 2024-06-12 11:43:08 +02:00
semantic-release
99fb82561b 0.61.0
Automatically generated by python-semantic-release
2024-06-12 06:27:41 +00:00
61ba08d0b8 feat(widgets/stop_button): General stop button added 2024-06-12 01:11:06 +02:00
40b5688158 refactor: improve labe of auto_update script 2024-06-10 08:27:32 +02:00
semantic-release
0a4e253cbd 0.60.0
Automatically generated by python-semantic-release
2024-06-08 17:35:57 +00:00
6428e38ab9 fix: removed BECConnector from rpc client interface 2024-06-08 19:05:57 +02:00
fc4f4f81ad ci: added git fetch for target branch 2024-06-08 19:05:57 +02:00
f6629852eb test: added missing pylint statement to header 2024-06-08 19:05:57 +02:00
3adf6cfd58 refactor: minor cleanup 2024-06-08 19:05:57 +02:00
b15816ca9f refactor: disabled pylint for auto-gen client 2024-06-08 19:05:57 +02:00
6b1d5827d6 ci: fixed pylint-check 2024-06-08 19:05:57 +02:00
f0391f59c9 feat: added isort to bw-generate-cli 2024-06-08 19:05:57 +02:00
006a0894b8 fix: added bec_ipython_client as dependency; needed for jupyter widget 2024-06-08 19:05:57 +02:00
9c5a471234 refactor(isort): added bec_widgets as known first party package 2024-06-08 19:05:57 +02:00
1c7f4912ce feat: added entry point for bw-generate-cli 2024-06-08 19:05:57 +02:00
df1be10057 feat(cli): auto-discover rpc-enabled widgets 2024-06-08 19:05:57 +02:00
954c576131 fix(BECFigure): removed duplicated user access for plot 2024-06-08 19:05:57 +02:00
867720a897 fix(bec_connector): field validator should be a classmethod 2024-06-08 19:05:57 +02:00
2b40602bdc refactor(dock): parent_dock_area changed to orig_area (native for pyqtgraph) 2024-06-08 16:00:45 +02:00
11173b9c0a ci: cleanup 2024-06-08 08:54:08 +02:00
semantic-release
52d46e77db 0.59.1
Automatically generated by python-semantic-release
2024-06-07 23:24:10 +00:00
e7838b0f2f fix(curve): set_color_map_z typo fixed in user access 2024-06-08 01:17:13 +02:00
semantic-release
2ae3810cf6 0.59.0
Automatically generated by python-semantic-release
2024-06-07 20:47:10 +00:00
178fe4d2da ci: merged additional tests to parallel matrix job 2024-06-07 22:37:24 +02:00
2d79ef8fe5 ci: added webengine dependencies 2024-06-07 22:37:24 +02:00
d56c5493cd build: added webengine dependency 2024-06-07 22:37:24 +02:00
cf6e5a40fc docs: added website docs 2024-06-07 22:37:24 +02:00
64abd67b9b feat(widget): added simple website widget with rpc 2024-06-07 22:37:24 +02:00
semantic-release
c19e856800 0.58.1
Automatically generated by python-semantic-release
2024-06-07 19:39:16 +00:00
02a26086c4 fix(dock): new dock can be detached upon creation 2024-06-07 21:32:38 +02:00
semantic-release
35f880bc2f 0.58.0
Automatically generated by python-semantic-release
2024-06-07 19:25:17 +00:00
c0ddeceeea test(color): validation tests added 2024-06-07 19:16:58 +02:00
67fd5e8581 fix: bar colormap dynamic setting 2024-06-07 18:52:37 +02:00
bf699ec1fb fix: formatting isort 2024-06-07 18:40:42 +02:00
3094632134 feat(utils.colors): general color validators 2024-06-07 18:34:57 +02:00
6985ff0fce fix(curve): 2D scatter updated if color_map_z is changed 2024-06-07 16:28:51 +02:00
33f7be42c5 fix(curve): color_map_z setting works 2024-06-07 16:28:51 +02:00
semantic-release
36fac70361 0.57.7
Automatically generated by python-semantic-release
2024-06-07 14:15:17 +00:00
ca5e8d2fbb fix: add model_config to pydantic models to allow runtime checks after creation 2024-06-07 15:44:09 +02:00
828067f486 docs: added schema of BECDockArea and BECFigure 2024-06-06 20:14:47 +02:00
semantic-release
e5f4c0b952 0.57.6
Automatically generated by python-semantic-release
2024-06-06 18:05:05 +00:00
edb1775967 fix(bar): docstrings extended 2024-06-06 18:39:47 +02:00
semantic-release
d0d6908a74 0.57.5
Automatically generated by python-semantic-release
2024-06-06 16:01:55 +00:00
c037b87675 docs(figure): docs adjusted to be compatible with new signature 2024-06-06 17:54:41 +02:00
52bc322b2b refactor(figure): logic for .add_image and .image consolidated; logic for .add_plot and .plot consolidated 2024-06-06 17:54:41 +02:00
8479caf53a fix(waveform): added .plot method with the same signature as BECFigure.plot 2024-06-06 17:54:41 +02:00
82e2c898d2 fix(plot_base): .plot removed from plot_base.py, because there is no use case for it 2024-06-06 17:54:41 +02:00
semantic-release
4852076e4a 0.57.4
Automatically generated by python-semantic-release
2024-06-06 15:53:30 +00:00
15cbc21e5b fix(docks): set_title do update dock internal _name now 2024-06-06 16:01:27 +02:00
ffae5ee54e fix(docks): docks widget_list adn dockarea panels return values fixed 2024-06-06 16:01:27 +02:00
semantic-release
10d77f20d1 0.57.3
Automatically generated by python-semantic-release
2024-06-06 13:53:15 +00:00
4be0d14b74 docs(bar): docs updated 2024-06-06 13:56:10 +02:00
e883dbad81 fix(ring): automatic updates are disabled uf user specify updates manually with .set_update; 'scan_progres' do not reset number of rings 2024-06-06 13:56:10 +02:00
a2abad344f fix(ring): enable_auto_updates(True) do not reset properties of already setup bars 2024-06-06 13:56:10 +02:00
d44b1cf8b1 fix(ring): set_min_max accepts floats 2024-06-06 13:56:10 +02:00
c5b6499e41 fix(ring): set_update changed to Literals, no need to specify endpoint manually 2024-06-06 13:56:10 +02:00
a951ebf1be docs: fixed syntax of add_widget 2024-06-06 12:30:40 +02:00
32da803df9 docs: added auto update; closes #206 2024-06-06 12:26:34 +02:00
07d60cf735 docs: cleanup 2024-06-06 12:26:34 +02:00
semantic-release
fceb851c32 0.57.2
Automatically generated by python-semantic-release
2024-06-06 10:24:04 +00:00
e1af5ca60f fix(test/e2e): autoupdate e2e rewritten 2024-06-06 12:16:03 +02:00
7fb31fc4d7 fix(test/e2e): spiral_progress_bar e2e tests rewritten to use config_dict 2024-06-06 12:16:02 +02:00
5c6ba65469 fix(test/e2e): dockarea and dock e2e tests changed to check asserts against config_dict 2024-06-06 12:09:24 +02:00
cd9fc46ff8 fix: rpc_server_dock fixture now spawns the server process 2024-06-06 12:09:24 +02:00
2a88e17b23 fix: accept scalars or numpy arrays of 1 element 2024-06-06 12:09:24 +02:00
69f4371007 refactor: move _get_output and _start_plot_process at the module level 2024-06-06 12:09:24 +02:00
semantic-release
af2c816d35 0.57.1
Automatically generated by python-semantic-release
2024-06-06 07:31:09 +00:00
f51b25f0af fix: tests references to add_widget_bec refactored 2024-06-06 00:13:05 +02:00
c3f4845b4f docs: docs refactored from add_widget_bec to add_widget 2024-06-06 00:10:13 +02:00
8ae323f5c3 fix(dock): add_widget and add_widget_bec consolidated 2024-06-06 00:06:11 +02:00
semantic-release
a3805a765b 0.57.0
Automatically generated by python-semantic-release
2024-06-05 15:14:01 +00:00
8c03034acf feat(widgets/console): BECJupyterConsole added 2024-06-05 16:41:44 +02:00
4160f3d6d7 docs: extend user documentation for BEC Widgets 2024-06-05 16:30:29 +02:00
semantic-release
4742e1ff6a 0.56.3
Automatically generated by python-semantic-release
2024-06-05 14:26:41 +00:00
4af1abe4e1 ci: increased verbosity for e2e tests 2024-06-05 13:47:27 +02:00
131f49da8e fix: fixed support for auto updates 2024-06-05 13:47:27 +02:00
semantic-release
f9bf496bd3 0.56.2
Automatically generated by python-semantic-release
2024-06-05 10:41:50 +00:00
9648e3ea96 fix(bar): ring saves current value in config 2024-06-05 11:30:22 +02:00
4be756a867 fix(dock): dock saves configs of all children widgets 2024-06-05 11:07:14 +02:00
46face0ee5 fix(dock_area): save/restore state is saved in config 2024-06-05 11:01:31 +02:00
6f3b1ea985 fix(figure): added correct types of configs to subplot widgets 2024-06-05 10:47:50 +02:00
3c9181d93d docs: restructured docs layout 2024-06-04 12:22:02 +02:00
semantic-release
a05f24785e 0.56.1
Automatically generated by python-semantic-release
2024-06-04 09:13:07 +00:00
9d615c915c fix(spiral_progress_bar/rings): config min/max values added check for floats 2024-06-04 10:56:53 +02:00
d2539918b2 fix(spiral_progress_bar): Endpoint is always stored as a string in the RingConnection Config 2024-06-04 10:56:53 +02:00
semantic-release
ed264cb528 0.56.0
Automatically generated by python-semantic-release
2024-05-29 22:03:10 +00:00
ad208a5ef8 docs(examples): example apps section deleted 2024-05-29 21:34:19 +02:00
ddc9510c2b fix(examples): outdated examples removed (mca_plot.py, stream_plot.py, motor_example.py) 2024-05-29 20:45:21 +02:00
855be3551a ci: added tests for pyside6, pyqt6 and pyqt5, default test and e2e is python 3.11 and pyqt6 2024-05-29 18:56:53 +02:00
db301b1be2 build: added pyside6 as dependency 2024-05-29 14:36:42 +02:00
07b99d91a5 fix: compatibility adjustment to .ui loading and tests for PySide6 2024-05-29 14:36:42 +02:00
0fea8d6065 feat(utils/ui_loader): universal ui loader for pyside/pyqt 2024-05-28 10:59:31 +02:00
semantic-release
2a67f1667a 0.55.0
Automatically generated by python-semantic-release
2024-05-24 12:05:40 +00:00
76bd0d339a feat(widgets/progressbar): SpiralProgressBar added with rpc interface 2024-05-24 13:59:10 +02:00
semantic-release
43759082dd 0.54.0
Automatically generated by python-semantic-release
2024-05-24 09:05:54 +00:00
fc4d0f3bb2 feat(figure): changes to support direct plot functionality 2024-05-24 10:50:00 +02:00
a47a8ec413 build: added pyqt6 as sphinx build dependency 2024-05-23 11:43:48 +02:00
3455c60236 refactor(reconstruction): repository structure is changed to separate assets needed for each widget 2024-05-21 16:31:55 +02:00
edc25fbf9d refactor(clean-up): 1st generation widgets are removed 2024-05-21 16:31:55 +02:00
semantic-release
dc38f2308b 0.53.3
Automatically generated by python-semantic-release
2024-05-16 08:27:45 +00:00
7d64cac661 fix: removed apparently unnecessary sleep while waiting for an rpc response 2024-05-15 14:14:25 +02:00
semantic-release
ab4f1acd75 0.53.2
Automatically generated by python-semantic-release
2024-05-15 10:39:21 +00:00
9f8fbdd5fc fix: check device class without importing to speed up initial import time 2024-05-15 10:10:14 +02:00
d1e6cd388c fix: speed up initial import times using lazy import (from bec_lib) 2024-05-15 10:10:14 +02:00
5d09a13d88 fix: adapt to bec_lib changes (no more submodules in __init__.py) 2024-05-15 10:10:14 +02:00
0490e80c48 ci: added echo to highlight the current branch 2024-05-13 11:46:36 +02:00
semantic-release
48553ba9b1 0.53.1
Automatically generated by python-semantic-release
2024-05-09 18:58:27 +00:00
0f6a5e5fa9 fix: docs config 2024-05-09 20:51:26 +02:00
8ff36105d1 ci: fixed rtd pages url 2024-05-09 19:44:44 +02:00
semantic-release
ce78271af4 0.53.0
Automatically generated by python-semantic-release
2024-05-09 16:03:51 +00:00
57ee735e5c docs: update install instructions 2024-05-09 14:53:57 +02:00
32e1a9d847 fix: fixed semver job and upgraded to v9 2024-05-09 14:11:42 +02:00
5cc816d0af ci: use formatter config of toml file 2024-05-09 11:29:42 +02:00
4117fd7b5b refactor: applied formatter 2024-05-09 11:29:42 +02:00
c86ce302a9 feat: moved to pyproject.toml; closes #162 2024-05-09 11:21:18 +02:00
semantic-release
c33ce05951 0.52.1
Automatically generated by python-semantic-release
2024-05-08 13:56:39 +00:00
7f2f7cd07a fix(docstrings): docstrings formating fixed for sphinx to properly format readdocs 2024-05-08 15:31:22 +02:00
semantic-release
1454f6192b 0.52.0
Automatically generated by python-semantic-release
2024-05-07 14:37:46 +00:00
ceae979f37 fix(widgets/dock): BECDockArea close overwrites the default pyqtgraph Container close + minor improvements 2024-05-07 16:31:12 +02:00
fcd6ef0975 feat(utils/layout_manager): added GridLayoutManager to extend functionalities of native QGridLayout 2024-05-07 16:30:21 +02:00
d8ff8afcd4 feat(widget/dock): BECDock and BECDock area for dockable windows 2024-05-07 16:30:21 +02:00
03fa1f26d0 refactor(widget/plots): WidgetConfig changed to SubplotConfig 2024-05-07 16:30:21 +02:00
e65c7f3be8 ci: fixed support for child pipelines 2024-05-07 12:48:24 +02:00
semantic-release
20d6352351 0.51.0
Automatically generated by python-semantic-release
2024-05-07 10:13:25 +00:00
5ece269adb feat(utils): added plugin helper to find and load 2024-05-07 12:10:53 +02:00
e0851250ee ci: added rule for parent-child pipelines 2024-05-07 10:26:18 +02:00
799ea554de build(cli): changed repo name to bec_widgets 2024-05-06 16:00:46 +02:00
df323504fe build(setup): fakeredis added to dev env 2024-05-01 15:05:22 +02:00
0ab8aa3a2f build(setup): PyQt6 version is set to 6.7 2024-05-01 15:05:22 +02:00
semantic-release
dae8a3409a 0.50.2
Automatically generated by python-semantic-release
2024-04-30 16:08:47 +00:00
0dfcaa4b70 fix: 'disconnect_slot' has to be symmetric with 'connect_slot' regarding QtThreadSafeCallback 2024-04-30 17:27:02 +02:00
semantic-release
98cb2c08ea 0.50.1
Automatically generated by python-semantic-release
2024-04-29 15:58:30 +00:00
57cb136a09 fix(cli): BECFigure takes the port to connect to redis from the current BECClient, supporting plugins 2024-04-29 16:53:26 +02:00
semantic-release
1d84bca753 0.50.0
Automatically generated by python-semantic-release
2024-04-29 08:26:54 +00:00
4f261be4c7 test(cli/rpc_register): e2e RPCRegister 2024-04-28 18:54:20 +02:00
40eb75f85a test(cli/rpc_register): rpc_register tests added 2024-04-28 12:47:17 +02:00
13c018a797 fix(widgets/figure): access pattern changed for getting widgets by coordinates for rpc 2024-04-28 12:42:58 +02:00
8f20a0b3b1 fix(plots): cleanup policy reviewed for children items 2024-04-28 12:42:58 +02:00
6b6a6b2249 fix(rpc/client_utils): getoutput more transparent + error handling 2024-04-28 12:42:58 +02:00
2ca32675ec fix(rpc_register): thread lock for listign all connections 2024-04-28 12:42:58 +02:00
381d713837 feat(plots): universal cleanup and remove also for children items 2024-04-28 12:42:58 +02:00
a898e7e4f1 feat(rpc/rpc_register): singleton rpc register for all rpc connections for session 2024-04-28 12:42:58 +02:00
semantic-release
6d13a3283b 0.49.1
Automatically generated by python-semantic-release
2024-04-26 16:17:32 +00:00
ab8537483d fix(widgets/editor): qscintilla editor removed 2024-04-26 17:57:54 +02:00
a22229849c build(pyqt6): fixing PyQt6-Qt6 package to 6.6.3 2024-04-25 17:07:29 +02:00
semantic-release
1ba266080c 0.49.0
Automatically generated by python-semantic-release
2024-04-24 15:57:14 +00:00
6500a00682 feat(rpc/client_utils): timeout for rpc response 2024-04-24 17:49:23 +02:00
9602085f82 fix(rpc/client_utils): close clean up policy for BECFigure 2024-04-24 10:54:24 +02:00
semantic-release
a1c369de9b 0.48.0
Automatically generated by python-semantic-release
2024-04-24 05:29:44 +00:00
6238693ffb feat(cli): added auto updates plugin support 2024-04-23 15:22:45 +02:00
semantic-release
f3a387e77f 0.47.0
Automatically generated by python-semantic-release
2024-04-23 13:12:39 +00:00
71cb80d544 feat(utils/thread_checker): util class to check the thread leakage for closeEvent in qt 2024-04-23 14:53:13 +02:00
77ff7962cc refactor(utils/container_utils): part of the logic regarding locating widgets moved from BECFigure to utility class 2024-04-22 12:07:37 +02:00
semantic-release
a516b1b247 0.46.7
Automatically generated by python-semantic-release
2024-04-21 16:24:50 +00:00
67a99a1a19 fix(plot/image): monitors are now validated with current bec session 2024-04-20 01:35:17 +02:00
semantic-release
e55daee756 0.46.6
Automatically generated by python-semantic-release
2024-04-19 16:51:51 +00:00
1111610f32 fix(cli): fixed support for devices as cli input 2024-04-19 18:18:25 +02:00
81484e8160 ci: changed ophyd default branch to main 2024-04-19 13:34:44 +02:00
semantic-release
2e349bd705 0.46.5
Automatically generated by python-semantic-release
2024-04-19 08:17:58 +00:00
a156803389 test(rpc/bec_figure): test_rpc_plotting_shortcuts_init_configs extended by testing scatter z gradient for BECWaveform through RPC 2024-04-19 01:09:39 +02:00
2955b5ec02 refactor(rpc/client_utils): update script for grid_scan adds z axis device 2024-04-19 00:17:00 +02:00
ff52100e23 fix(widgets/figure): individual cleanup disabled, making stuck rpc 2024-04-19 00:16:22 +02:00
026c0792be fix(plots/waveform): colormap is correctly passed from BECFigure 2024-04-19 00:15:04 +02:00
b632ed1095 refactor(examples/jupyter_console_window): jupyter console debugging window moved to examples 2024-04-16 19:47:01 +02:00
semantic-release
98beea37e6 0.46.4
Automatically generated by python-semantic-release
2024-04-16 15:22:38 +00:00
4bcae0f921 ci: set branch name for semver 2024-04-16 17:10:48 +02:00
22fb5a5656 ci: fixed multi-project pipeline 2024-04-16 17:00:27 +02:00
4da625e439 fix: renaming of bec_client to bec_ipython_client 2024-04-16 17:00:27 +02:00
05e268d466 ci: "master" renamed to "main" in semver and pages section 2024-04-16 15:23:22 +02:00
42a9a0ca15 ci: added workflow .gitlab-ci.yml 2024-04-16 10:06:48 +02:00
b6feb9adb3 ci: CI_MERGE_REQUEST_TARGET_BRANCH_NAME changed to main 2024-04-16 09:53:16 +02:00
1bc18a201c test(e2e/rpc): rpc e2e tests extended 2024-04-16 09:51:39 +02:00
c12f2cee80 fix(plots/motor_map): user can get data as dict from BECMotorMap 2024-04-16 09:51:39 +02:00
c2c583fce6 fix(plots/image): user can get data as np.ndarray from BECImageItem 2024-04-16 09:51:39 +02:00
5600624c57 refactor(isort): isort applied 2024-04-16 09:51:39 +02:00
66c0649d7e ci(tests): unit tests ci path corrected 2024-04-16 09:51:39 +02:00
2446c401d9 test: unit tests moved to separate folder; scope of autouse bec_dispatcher fixture reduced only for unit tests; ci adjusted 2024-04-16 09:51:39 +02:00
4d0df364d3 test(end-2-end): rpc end-2-end tests 2024-04-16 09:51:39 +02:00
ecdf0f122b fix(rpc/server): server can accept client or dispatcher 2024-04-16 09:51:39 +02:00
df5234aa52 ci: pull images via gitlab dependency proxy 2024-04-16 09:31:01 +02:00
62080e6b40 Revert "ci: merge AdditionalTests with test stage"
This reverts commit 2e3f46ea36
2024-04-15 16:45:41 +02:00
2e3f46ea36 ci: merge AdditionalTests with test stage 2024-04-15 15:01:50 +02:00
be9847e9d2 refactor(plots/image): all rpc widgets can access config_dict as property 2024-04-15 11:45:06 +02:00
2f7317b328 refactor(plots/image): images are accessed as property .images -> returns list[BECImage] 2024-04-15 11:40:30 +02:00
bd3b1ba043 ci: changed default BEC branch to main 2024-04-12 17:04:42 +02:00
semantic-release
59e82dfd00 0.46.3
Automatically generated by python-semantic-release
2024-04-11 16:27:58 +00:00
0b86a0009d fix(test_fake_redis): TestMessage fixed to pydantic BaseModel 2024-04-11 15:28:06 +02:00
49327a8dbd fix(plots/motor_map): removed single callback flag for connecting device_readback motors 2024-04-11 11:53:28 +02:00
301bb916da test(utils/bec_dispatcher): tests fixed 2024-04-11 11:53:28 +02:00
285bf0164b fix(cli/client_utils): print_log is buffered; add output processing thread 2024-04-11 11:53:28 +02:00
90907e0a9c refactor(bec_dispatcher): new BEC dispatcher - rebased 2024-04-11 10:54:46 +02:00
9def3734af fix: producer->connector 2024-04-11 10:50:46 +02:00
semantic-release
3a241e897b 0.46.2
Automatically generated by python-semantic-release
2024-04-10 20:19:43 +00:00
ee617b73a2 fix(widget/plots): added "get_config" to all children of BECConnector to USER_ACCESS 2024-04-10 18:06:37 +02:00
92cea90971 refactor(utils/bec_dispatcher): new singleton definition 2024-04-10 16:41:28 +02:00
semantic-release
452ba20216 0.46.1
Automatically generated by python-semantic-release
2024-04-10 14:40:35 +00:00
cf29035e28 fix(rpc/client): correct name for RPC class BECWaveform (instead of BECWaveform1D) 2024-04-10 16:34:41 +02:00
semantic-release
3fc09dd2aa 0.46.0
Automatically generated by python-semantic-release
2024-04-09 20:26:07 +00:00
fe101f9328 refactor(widget/monitor_scatter_2D): deleted 2024-04-09 13:26:22 +02:00
754d81edf3 test(plot/figure): test extended to check shortcuts for creating subplots 2024-04-09 13:26:22 +02:00
3d399ba1f5 feat(plot/waveform1d): BECWaveform1D can show z data of scatter coded to different detector like BECMonitor2DScatter; BECWaveform1D name changed to BECWaveform 2024-04-09 13:26:22 +02:00
6dc1000de5 test: fixed default value for scan id 2024-04-06 08:50:19 +02:00
semantic-release
89b4deb5cd 0.45.0
Automatically generated by python-semantic-release
2024-03-26 22:10:53 +00:00
6e0e69b9f7 test(plot/motor_map): tests extended 2024-03-26 22:53:46 +01:00
b8519e8770 feat(plots/bec_figure): Motor Map integrated to BECFigure 2024-03-26 22:53:46 +01:00
0f69c346cd feat(plots/bec_motor_map): BECMotorMap build on BECPlotBase 2024-03-26 22:53:46 +01:00
88014d24c1 docs: added api reference; closes #123 2024-03-26 21:48:46 +01:00
ea4d743a25 test: mock_client unified for all tests 2024-03-26 15:39:42 +01:00
semantic-release
5935d4865c 0.44.5
Automatically generated by python-semantic-release
2024-03-25 12:49:00 +00:00
c5826f8887 fix: circular imports 2024-03-25 13:39:21 +01:00
62f0b15193 refactor: isort import formatting 2024-03-25 13:21:38 +01:00
d846266332 refactor: renamed scanID to scan_id 2024-03-22 16:15:44 +01:00
semantic-release
06d0331dee 0.44.4
Automatically generated by python-semantic-release
2024-03-22 15:04:58 +00:00
e6b065767c fix(cli/server): thread heartbeat replaced with QTimer 2024-03-22 15:59:53 +01:00
f3a96dedd7 fix(cli/server): removed BECFigure.start(), the QApplication event loop is started by server.py 2024-03-21 15:51:30 +01:00
semantic-release
016324e71c 0.44.3
Automatically generated by python-semantic-release
2024-03-21 13:12:40 +00:00
a92aead769 fix(cli): don't call user script if gui is not alive 2024-03-20 22:11:09 +01:00
882cf55fc5 fix(cli): added gui heartbeat 2024-03-20 22:02:02 +01:00
semantic-release
ee02c13d5d 0.44.2
Automatically generated by python-semantic-release
2024-03-20 11:50:29 +00:00
wyzula-jan
9ccd4ea235 fix(utils/bec_dispatcher): try/except to start client, to avoid crash when redis is not running 2024-03-20 12:06:22 +01:00
wyzula-jan
86416d50cb fix(utils/bec_dispatcher): bec_dispatcher adjusted to the new BECClient; dropped support to inject bec_config.yaml, instead BECClient can be passed as arg 2024-03-20 11:12:16 +01:00
1d5442ac08 ci: now testing against master branches of bec_lib and ophyd_devices 2024-03-20 11:11:49 +01:00
semantic-release
f3c7196921 0.44.1
Automatically generated by python-semantic-release
2024-03-19 09:05:40 +00:00
wyzula-jan
14f901f1be fix(examples/motor_compilation): motor_control_compilations.py do not have any hardcoded config anymore 2024-03-18 18:18:48 +01:00
semantic-release
9f93c01ff7 0.44.0
Automatically generated by python-semantic-release
2024-03-18 08:05:04 +00:00
203ae09606 fix(cli): removed hard-coded signal 2024-03-18 07:43:32 +01:00
2d39c5e4d1 fix(cli): fixed cleanup procedure 2024-03-18 07:43:32 +01:00
9049e0d27f feat(cli): added update script to BECFigure 2024-03-18 07:43:32 +01:00
semantic-release
d5d41fc759 0.43.2
Automatically generated by python-semantic-release
2024-03-18 06:38:10 +00:00
wyzula-jan
d0f9bf1733 fix(cli/server): added QApplications to enter separate QT event loop ensuring that QT objects are not deleted 2024-03-17 18:22:21 +01:00
semantic-release
7d46d1160d 0.43.1
Automatically generated by python-semantic-release
2024-03-15 16:22:33 +00:00
wyzula-jan
b8d4e697ac fix(plots/image): same access pattern for image and image_item for setting up parameters, autorange of z scale disabled by default 2024-03-15 16:44:32 +01:00
wyzula-jan
4664661cfb fix(widget/various): corrected USER_ACCESS methods for children widgets to include inherited methods to RPC 2024-03-15 16:44:32 +01:00
wyzula-jan
d99fd76c0b refactor(widget/figure): changed add_plot and add_image to specify what should be content of the widget, instead of widget id 2024-03-15 16:44:32 +01:00
wyzula-jan
fcf918c488 fix(widgets/figure): added widgets can be accessed as a list (fig.axes) or as a dictionary (fig.widgets) 2024-03-15 16:44:32 +01:00
wyzula-jan
32747baa27 refactor(cli): commented debug CLI messages 2024-03-14 15:17:11 +01:00
semantic-release
9e974eda27 0.43.0
Automatically generated by python-semantic-release
2024-03-14 13:54:29 +00:00
wyzula-jan
598479bb55 fix(plots/waveform1d): curves_data access disabled 2024-03-14 14:42:41 +01:00
wyzula-jan
4ef6ae90f2 fix(cli): find_widget_by_id for BECImageShow changed to be compatible with RPC logic 2024-03-14 14:42:41 +01:00
wyzula-jan
4865b10ced feat(plots/image): image processor can run in threaded or non-threaded version 2024-03-14 14:42:41 +01:00
wyzula-jan
3362fabed7 fix(plots/image): access pattern for ImageItems in BECImageShow 2024-03-14 14:42:41 +01:00
wyzula-jan
4076698530 fix(cli): fix cli connector.send to set_and_publish for gui_instruction_response 2024-03-14 14:42:41 +01:00
wyzula-jan
a21bfec3d9 refactor(plots/image): image logic moved to BECImageItem, image updated from bec_dispatcher with register_stream fetching data from dispatcher 2024-03-14 14:42:41 +01:00
wyzula-jan
7ffedd9ceb feat(plots/image): change stream processor to QThread with connector.get_last; cleanup method for BECFigure to kill all threads if App is closed during acquisition 2024-03-14 14:39:44 +01:00
wyzula-jan
9ad0055336 feat(plots/image): basic image visualisation, getting data are based on stream_connector (deprecated) 2024-03-14 14:39:44 +01:00
wyzula-jan
70c4e9bc5e refactor(plots/plot_base): BECPlotBase inherits from pg.GraphicalLayout instead of pg.PlotItem, this will allow us to add multiple plots into each coordinate of BECFigure. 2024-03-14 14:35:26 +01:00
semantic-release
43770e2967 0.42.1
Automatically generated by python-semantic-release
2024-03-10 18:33:42 +00:00
wyzula-jan
f3b3c2f526 fix(various): repo cleanup, removed - [plot_app, one_plot, scan_plot, scan2d_plot, crosshair_example, qtplugins], tests adjusted 2024-03-10 19:27:43 +01:00
semantic-release
279ac03dc3 0.42.0
Automatically generated by python-semantic-release
2024-03-07 19:33:46 +00:00
4c0a7bbec7 feat(utils/bec_dispatcher): BECDispatcher can register redis stream 2024-03-07 20:31:24 +01:00
semantic-release
f5f9158779 0.41.4
Automatically generated by python-semantic-release
2024-03-07 12:49:09 +00:00
c319dacb24 fix(utils/bec_dispatcher): BECDispatcher can accept new EndpointInfo dataclass. 2024-03-07 13:46:51 +01:00
814768525f ci: drop python/3.9 2024-03-05 12:46:49 +01:00
semantic-release
38d056570f 0.41.3
Automatically generated by python-semantic-release
2024-03-01 12:23:03 +00:00
wyzula-jan
f386563aa1 fix(cli/generate_cli): typing.get_overloads are only used if the python version is higher than 3.11 2024-03-01 12:35:12 +01:00
wyzula-jan
110506c9a9 test(cli/generate_cli): import from future 2024-02-29 15:17:20 +01:00
wyzula-jan
7e0058a611 test(cli/generate_cli): test added 2024-02-29 14:58:58 +01:00
wyzula-jan
d89f596a5d fix(cli/generate_cli): added automatic black formatting; added black as a dependency 2024-02-29 14:08:00 +01:00
semantic-release
5de2dfefcb 0.41.2
Automatically generated by python-semantic-release
2024-02-28 06:09:16 +00:00
wyzula-jan
bb1f066c3c fix(utils/bec_dispatcher): msg is unp[acked from dict before accessing .content 2024-02-27 16:46:19 +01:00
semantic-release
44b451e66b 0.41.1
Automatically generated by python-semantic-release
2024-02-26 20:04:48 +00:00
a2ed2ebe00 fix(bec_dispatcher): handle redis connection errors more gracefully 2024-02-26 20:58:46 +01:00
8127fc2960 fix(bec_dispatcher): adapt code to redis connector refactoring 2024-02-26 19:26:15 +01:00
semantic-release
6171790f66 0.41.0
Automatically generated by python-semantic-release
2024-02-26 14:40:20 +00:00
wyzula-jan
ebb36f62dd fix(cli/client_utils): "__rpc__" pop from msg_results 2024-02-26 15:30:43 +01:00
wyzula-jan
644f1031f6 fix(tests): BECDispatcher fixture putted back 2024-02-26 14:27:22 +01:00
wyzula-jan
fd711b475f fix(cli/rpc): rpc client can return any type of object + config dict of the widgets 2024-02-26 14:06:36 +01:00
wyzula-jan
57132a4721 fix(cli/rpc): server access children widget.find_widget_by_id(gui_id) 2024-02-26 13:26:55 +01:00
f71dc5c5ab fix(cli): fixed property access, rebased 2024-02-26 10:29:15 +01:00
4630d78fc2 fix(rpc_server): fixed gui_id lookup 2024-02-26 10:25:02 +01:00
da640e888d fix(cli): fixed rpc construction of nested widgets 2024-02-26 10:25:02 +01:00
wyzula-jan
35cd4fd6f1 fix(plots/waveform1d): pandas import clean up, export curves with none skipped 2024-02-25 18:06:33 +01:00
wyzula-jan
f06e652b82 test(plots/waveform1d): tests added 2024-02-25 17:52:11 +01:00
wyzula-jan
5fc8047c8f feat(widgets/waveform1d): data can be exported from rendered curve 2024-02-25 12:52:36 +01:00
wyzula-jan
0363fd5194 feat(widgets/figure): clear_all method for BECFigure 2024-02-23 15:27:09 +01:00
wyzula-jan
826a5e9874 test(test_plot_base): BECPlotBase tests added 2024-02-23 13:37:25 +01:00
wyzula-jan
f668eb8b9b test(test_bec_figure): tests for BECFigure added 2024-02-23 13:06:18 +01:00
wyzula-jan
5964778a64 refactor(widgets/BECCurve): set kwargs for curve style while adding curve 2024-02-23 11:05:01 +01:00
wyzula-jan
8135f68230 test(tests/test_bec_connector): test_bec_connector.py added 2024-02-23 10:53:10 +01:00
wyzula-jan
24c77376b2 fix(widgets/plots): added placeholder for cleanup method to BECPlotBase 2024-02-23 10:53:10 +01:00
wyzula-jan
f364afcb42 refactor(widgets/figure: fixed wrong references to debug jupyter console 2024-02-23 10:53:10 +01:00
wyzula-jan
4051902f09 test(tests/client_mocks): added general mock_client with container for fake devices for testing 2024-02-23 10:53:10 +01:00
wyzula-jan
a28b9c8981 fix(widget/figure): add cleanup method to disconnect all slots before removing Waveform1D from layout 2024-02-23 10:53:10 +01:00
wyzula-jan
9a5c86ea35 feat(widgets/Waveform1D): Waveform1D can be fully constructed by config 2024-02-23 10:53:10 +01:00
wyzula-jan
08534a4739 feat(widgets/figure.py): dark/light theme changer 2024-02-23 10:53:10 +01:00
wyzula-jan
1db77b969b feat(utils/entry_validator): possibility to validate add_scan_curve with current BEC session 2024-02-23 10:53:10 +01:00
wyzula-jan
99dce077c4 refactor(plot/Waveform1D,plot/BECCurve): BECCurve inherits from BECConnector and can refer to parent_id (Waveform1D) and has its own gui_id 2024-02-23 10:53:10 +01:00
wyzula-jan
402adc44e8 refactor(rpc/client): changed path to client.py to relative one 2024-02-23 10:53:10 +01:00
wyzula-jan
c6bdf0b6a5 fix(rpc): added annotations to pass py3.9 tests 2024-02-23 10:53:10 +01:00
wyzula-jan
1c2fb8b972 fix(rpc): connection to on_rpc_update done through bec_dispatcher 2024-02-23 10:53:10 +01:00
a61bf36df5 feat(cli): added cli interface, rebased 2024-02-23 10:53:10 +01:00
wyzula-jan
d678a85957 fix: after removing plot from BECFigure, the coordinates are correctly resigned 2024-02-23 10:53:10 +01:00
wyzula-jan
684592ae37 feat: curve can be modified after adding to the plot 2024-02-23 10:53:10 +01:00
wyzula-jan
f0ed243c91 feat: waveform1d.py curves can be removed by identifier by order(int) or by curve_id(str) 2024-02-23 10:53:10 +01:00
wyzula-jan
cba3863e5a feat: waveform1d.py curves can be stylised; access scan history by index or scanID 2024-02-23 10:53:10 +01:00
wyzula-jan
1d26b23221 feat: start method for BECFigure, jupyter console .ui added to git 2024-02-23 10:53:10 +01:00
wyzula-jan
b827e9eaa7 feat: added @user_access from bec_lib.utils 2024-02-23 10:53:10 +01:00
wyzula-jan
60d150a411 feat: plot can be removed from BECFigure 2024-02-23 10:53:10 +01:00
wyzula-jan
c781b1b4e4 feat: figure.py create widget factory 2024-02-23 10:53:10 +01:00
wyzula-jan
565e475ace feat: waveform1d.py draft 2024-02-23 10:53:10 +01:00
wyzula-jan
7c15d75011 fix: removed DI references, fixed set when adding plot by fig 2024-02-23 10:53:10 +01:00
wyzula-jan
b676877242 feat: rpc decorator to add methods to USER_ACCESS 2024-02-23 10:53:10 +01:00
wyzula-jan
7768e594b5 refactor: BECFigure, BECPlotBase changed back to pyqtgraph classes inheritance 2024-02-23 10:53:10 +01:00
wyzula-jan
9ef331c272 feat: BECFigure and BECPlotBase created 2024-02-23 10:53:10 +01:00
wyzula-jan
4a1792c209 refactor: BECConnector changed config structure 2024-02-23 10:53:10 +01:00
wyzula-jan
91447a2d62 feat: BECConnector -> mixin class for all BEC Widget to hook them to BEC client 2024-02-23 10:53:10 +01:00
semantic-release
ed5bdd99e6 0.40.1
Automatically generated by python-semantic-release
2024-02-23 09:51:25 +00:00
wyzula-jan
feca7a3dcd fix(utils/bec_dispatcher): _do_disconnect_slot will shutdown consumer of slots/signals which were already disconnected 2024-02-22 13:35:57 +01:00
semantic-release
2d9020358d 0.40.0
Automatically generated by python-semantic-release
2024-02-16 20:51:02 +00:00
wyzula-jan
51259097fa feat(utils.colors): golden_angle_color utility can return colors as a list of QColor, RGB or HEC 2024-02-16 20:16:19 +01:00
semantic-release
8a4aeb8dfe 0.39.0
Automatically generated by python-semantic-release
2024-02-12 13:01:47 +00:00
wyzula-jan
4b0542a513 refactor: pylint ignore for tests 2024-02-12 13:53:52 +01:00
wyzula-jan
bf04a4e04a test: motor_control_compilations.py and motor_control.py tests added 2024-02-12 13:53:52 +01:00
wyzula-jan
fa4ca935bb feat: added full app with all motor movement related widgets into motor_control_compilations.py 2024-02-12 13:53:52 +01:00
wyzula-jan
b52e22d81f refactor: motor_control.py clean up 2024-02-12 13:53:52 +01:00
wyzula-jan
2f96e10b9d feat: MotorCoordinateTable mode_switch added for "Individual" and "Start/Stop" modes 2024-02-12 13:53:52 +01:00
wyzula-jan
031cb094e7 feat: motor_control.py MotorCoordinateTable added basic version to store coordinates and show them in motor_map.py 2024-02-12 13:53:52 +01:00
wyzula-jan
8afc5f0c0c refactor: motor_control_compilations.py moved to example part of repository 2024-02-12 13:53:52 +01:00
wyzula-jan
17f14581d7 feat: active motors from motor_map.py can be changed by slot without changing the whole config 2024-02-12 13:53:52 +01:00
wyzula-jan
8361736679 feat: control panels compilations 2024-02-12 13:53:52 +01:00
wyzula-jan
0b9927fcf5 feat: comboboxes of motor selection are changed to orange if the motors are not connected yet 2024-02-12 13:53:52 +01:00
wyzula-jan
8139e271de refactor: base class for motor_control.py widgets 2024-02-12 13:53:52 +01:00
wyzula-jan
6fe08e6b82 feat: motor_control.py MotorControl widgets - Absolute + Relative movement, MotorSelection, ErrorMessage popups 2024-02-12 13:53:52 +01:00
wyzula-jan
968da6f558 build: added all .ui and .yaml files to pypi install; removed gauss_bpm from default config from monitor.py 2024-02-08 10:59:48 +01:00
semantic-release
11ae0b1054 0.38.2
Automatically generated by python-semantic-release
2024-02-07 16:26:49 +00:00
5ebfd2a3c2 test: fixed import in test_validator_errors.py 2024-02-07 17:23:03 +01:00
b36131eed5 fix: adapt code to BEC 1.0 2024-02-07 17:16:43 +01:00
semantic-release
a7bfcc12b9 0.38.1
Automatically generated by python-semantic-release
2024-01-26 15:45:41 +00:00
wyzula-jan
ab275b8e5f fix: monitor.py replots last scan after changing config with new signals; config_dialog.py checks if the new config is valid with BEC 2024-01-26 16:42:08 +01:00
wyzula-jan
d211b47f4c refactor: black v24 formatting 2024-01-26 15:17:59 +01:00
wyzula-jan
812ffaf8ea docs: 2D waveform scatter plot changed to 2D scatter plot 2024-01-24 10:50:51 +01:00
wyzula-jan
f7a496723c docs: documentation for example apps and widgets updated 2024-01-24 10:36:50 +01:00
semantic-release
48847a19c7 0.38.0
Automatically generated by python-semantic-release
2024-01-23 13:48:08 +00:00
wyzula-jan
8d0083c4aa test: fix test_bec_monitor_scatter2D.py database init test change to check defaultdict 2024-01-23 14:42:11 +01:00
wyzula-jan
3c143274c5 refactor: monitor_scatter_2D.py _init_database replaced with defaultdict 2024-01-23 14:31:20 +01:00
wyzula-jan
747e97e0c9 fix: monitor_scatter_2D.py changed to new BECDispatcher definition 2024-01-23 13:51:23 +01:00
wyzula-jan
c6fe9d2026 test: test_bec_monitor_scatter2D.py added 2024-01-23 13:51:23 +01:00
wyzula-jan
75090b8575 feat: BECMonitor2DScatter for plotting x/y/z signal as a mesh of scatter plot 2024-01-23 13:51:23 +01:00
semantic-release
8f76c789cf 0.37.1
Automatically generated by python-semantic-release
2024-01-23 12:42:02 +00:00
4664568672 fix(tests): ensure BEC service is shutdown after bec dispatcher test 2024-01-20 23:04:51 +01:00
3fb6644543 fix(tests): ensure threads started during plot tests are properly stopped 2024-01-20 23:01:41 +01:00
d909673071 refactor(tests): ensure BEC dispatcher singleton object is renewed at each test
and add a check for dangling threads
2024-01-19 19:40:21 +01:00
semantic-release
d281d6576c 0.37.0
Automatically generated by python-semantic-release
2024-01-17 14:09:10 +00:00
wyzula-jan
8bebc4f692 refactor: pylint improvement 2024-01-17 14:59:53 +01:00
wyzula-jan
1cd273c375 test: test_motor_map.py added 2024-01-17 14:59:53 +01:00
wyzula-jan
249170ea30 refactor: motor_map.py clean up 2024-01-17 14:59:53 +01:00
wyzula-jan
1a429b3024 feat: independent motor_map widget 2024-01-17 14:59:53 +01:00
semantic-release
e05cab812a 0.36.2
Automatically generated by python-semantic-release
2024-01-17 13:56:53 +00:00
wyzula-jan
7607d7a3b6 fix: bec_dispatcher.py can partially disconnect topics from slot 2024-01-16 16:02:31 +01:00
wyzula-jan
e51be04b95 fix: bec_dispatcher.py can connect multiple topics to one callback slot 2024-01-16 16:02:31 +01:00
semantic-release
de1f5c968a 0.36.1
Automatically generated by python-semantic-release
2024-01-15 16:04:22 +00:00
wyzula-jan
bf819bcf48 refactor: motor_example.py get coordinates by .readback.get() method 2024-01-15 16:14:15 +01:00
wyzula-jan
6f26e5cc3d refactor: using motor.readback.read() to access motor coordinates 2024-01-12 16:44:55 +01:00
wyzula-jan
f9c5c82381 fix: motor_example.py fix to the new .read() structure from bec_lib 2024-01-12 16:44:55 +01:00
semantic-release
79487dbec2 0.36.0
Automatically generated by python-semantic-release
2024-01-12 13:23:34 +00:00
58721bea1a feat: bec_dispatcher can link multiple endpoints topics for one qt slot 2024-01-12 14:22:29 +01:00
semantic-release
03e96669da 0.35.0
Automatically generated by python-semantic-release
2024-01-12 09:33:49 +00:00
wyzula-jan
eb529d24d2 refactor: review response for MR !31 2024-01-11 16:58:53 +01:00
wyzula-jan
ebd4fccda2 fix: monitor.py clear command from BECPlotter CLI clear now flush database and clear the plots 2024-01-10 17:59:36 +01:00
wyzula-jan
97dcc5ac76 fix: monitor.py crosshair enabled by default 2024-01-09 12:51:22 +01:00
wyzula-jan
9c7a189beb ci: fix cobertura for gitlab/16 2024-01-09 12:45:40 +01:00
wyzula-jan
6061b3150e fix: monitor.py change import of ConfigDialog from relative to absolute in order to make BECPlotter be able to open it 2024-01-09 12:32:45 +01:00
wyzula-jan
3982c5d498 refactor: config_dialog.py refactored to accept new config formatting 2024-01-08 16:31:56 +01:00
wyzula-jan
404ca49821 refactor: modular_app.py configs changed to new format 2024-01-08 16:17:14 +01:00
wyzula-jan
6e4775a124 feat: monitor.py can access custom data send through redis 2024-01-08 15:23:39 +01:00
wyzula-jan
5ab82bc133 fix: monitor_config_validator.py changed to check .describe() instead of signals 2023-12-21 16:59:02 +01:00
wyzula-jan
00ef3ae925 fix: monitor.py fixed not updating config changes after receiving refresh from BECPlotter 2023-12-18 15:42:10 +01:00
wyzula-jan
90d8069cc3 test: test_validator_errors.py fixed 2023-12-15 19:05:38 +01:00
wyzula-jan
457567ef74 test: test_bec_monitor.py fixed 2023-12-15 18:28:29 +01:00
wyzula-jan
1128ca5252 refactor: monitor.py clean up 2023-12-15 13:49:08 +01:00
wyzula-jan
86c5f25205 fix: monitor_config_validator.py valid color is Literal['black','white'] 2023-12-15 13:19:16 +01:00
wyzula-jan
a706da2490 fix: monitor.py fixed scan mode 2023-12-15 13:12:05 +01:00
wyzula-jan
d67bdd2616 fix: motor_config_validation changed to new monitor config structure 2023-12-14 14:01:28 +01:00
wyzula-jan
c3f2ad45c3 refactor: monitor.py data for scan segment are only accessed through queue.scan_storage 2023-12-13 10:30:58 +01:00
wyzula-jan
26c07c3205 feat: monitor.py access data directly from scan storage 2023-12-13 09:37:45 +01:00
wyzula-jan
c995e0d235 refactor: monitor.py config hierarchy refactor for source (can be 'scan_segment','history', 'redis') 2023-12-13 09:37:45 +01:00
wyzula-jan
463a60a99c refactor: monitor.py on_scan_segment old logic separated from on_scan_segment function 2023-12-13 09:37:21 +01:00
semantic-release
98a46a85b2 0.34.1
Automatically generated by python-semantic-release
2023-12-12 19:20:19 +00:00
wyzula-jan
186c42d667 fix: formatter and tests fixed 2023-12-12 18:24:38 +01:00
wyzula-jan
f3a47a5b08 refactor: repo reorganisation 2023-12-12 17:26:28 +01:00
wyzula-jan
af995a74f3 docs: readdocs updated 2023-12-12 17:26:28 +01:00
wyzula-jan
3abd955465 refactor: repo reorganization 2023-12-12 17:26:22 +01:00
wyzula-jan
cba8131367 docs: readme.md updated 2023-12-12 17:25:25 +01:00
wyzula-jan
831eddc136 docs: gitlab templates for issues and merge requests from main bec repo 2023-12-12 17:25:25 +01:00
9e852d1afc refactor: replace deprecated imports from typing
https://peps.python.org/pep-0585/#implementation
2023-12-12 15:22:59 +01:00
3ec9caae09 build: fix python requirement 2023-12-12 15:14:18 +01:00
11281fef53 ci: added rtd update job 2023-12-11 19:22:01 +01:00
semantic-release
9d497b70bf 0.34.0
Automatically generated by python-semantic-release
2023-12-08 09:57:26 +00:00
wyzula-jan
2a334156a8 test: validation errors tests 2023-12-07 19:32:21 +01:00
wyzula-jan
086804780d fix: monitor_config_validator.py - Signal validation changed from field_validator to model_validator to check first name and then entry 2023-12-07 19:32:21 +01:00
wyzula-jan
731fba55ec refactor: monitor.py pylint improvement 2023-12-07 19:32:21 +01:00
wyzula-jan
a3b24f9242 feat: monitor.py error message popup 2023-12-07 19:32:20 +01:00
wyzula-jan
af71e35e73 fix: monitor_config_validator.py fix entry validation executed only if name validator is successful 2023-12-07 19:20:26 +01:00
semantic-release
3e8996a024 0.33.0
Automatically generated by python-semantic-release
2023-12-07 17:10:16 +00:00
03bdf980bc fix: fixed default config options 2023-12-07 18:03:29 +01:00
1084bc0a80 fix: added hooks to react to incoming config messages and instructions 2023-12-07 15:28:45 +00:00
504944f696 feat: added axis_width and axis_color as optional plot settings 2023-12-07 15:28:45 +00:00
semantic-release
b53f72f0ad 0.32.2
Automatically generated by python-semantic-release
2023-12-06 19:39:51 +00:00
wyzula-jan
aad754f472 test: removed captured code for Permission tests 2023-12-06 16:27:09 +01:00
wyzula-jan
f5d1127d21 test: additional tests for error handling for yaml_dialog.py 2023-12-06 16:12:51 +01:00
wyzula-jan
080c258d15 fix: changed exec_ to exec for all apps 2023-12-06 16:02:28 +01:00
wyzula-jan
5adde23a45 fix: yaml_dialog.py changed to use native solution of OS -> should prevent crashing on py3.11 2023-12-06 16:01:19 +01:00
semantic-release
2359a08519 0.32.1
Automatically generated by python-semantic-release
2023-12-06 10:21:25 +00:00
wyzula-jan
d1f9979ab1 fix: widget_io print_widget_hierarchy fix comboboxes 2023-12-06 09:44:32 +01:00
wyzula-jan
bcc47f3740 refactor: improve pylint for WidgetIO 2023-12-04 19:47:56 +01:00
wyzula-jan
4f700976dd fix: WidgetIO combobox fixed for qt6 distributions 2023-12-04 19:37:42 +01:00
039f963661 Ci multiple python versions 2023-11-30 15:11:30 +01:00
semantic-release
3ebdb4bed0 0.32.0
Automatically generated by python-semantic-release
2023-11-30 13:28:36 +00:00
wyzula-jan
016b26f5cf feat: jupyter rich console added as alternative to default QTextEdit terminal output 2023-11-22 14:33:57 +01:00
wyzula-jan
e5010c7772 build: added qtconsole to dependency 2023-11-22 14:32:54 +01:00
wyzula-jan
a4d9713785 refactor: improve pylint score 2023-11-22 13:24:23 +01:00
wyzula-jan
b21c1db2a9 test: test_editor.py tests added 2023-11-22 13:12:25 +01:00
wyzula-jan
d967fafe3c refactor: editor.py open/save file refactored to not use native window 2023-11-22 12:48:47 +01:00
wyzula-jan
7d15397ce3 refactor: changed dependency to CAPS 2023-11-22 10:15:49 +01:00
wyzula-jan
d978740f98 fix: added missing dependency jedi 2023-11-22 00:50:25 +01:00
wyzula-jan
c174326762 build: disabled support to PySide2/PySide6, due to no QScintilla support; added pyqtdarktheme 2023-11-22 00:06:16 +01:00
wyzula-jan
3d9dc5c008 doc: editor.py and toolbar.py documentation added 2023-11-21 23:42:51 +01:00
wyzula-jan
3cc05cde14 fix: editor.py switch to disable docstring 2023-11-21 22:56:27 +01:00
wyzula-jan
f96caccfcb fix: editor.py compact signature on tooltip 2023-11-21 22:40:21 +01:00
wyzula-jan
d7a2c6830f refactor: editor.py signature tooltip process moved to AutoCompleter; simpler logic for signature tooltip 2023-11-21 19:45:52 +01:00
wyzula-jan
045b1baa60 feat: editor.py basic signature calltip 2023-11-21 17:28:11 +01:00
wyzula-jan
fb555b278a feat: editor.py jedi autocomplete hooked 2023-11-20 16:36:57 +01:00
wyzula-jan
d865e2f1af fix: editor.py removed automatic background behind edited text 2023-11-19 20:12:21 +01:00
wyzula-jan
c70ddb3cb1 feat: editor.py added splitter between editor and terminal 2023-11-19 19:24:01 +01:00
wyzula-jan
8ad3059592 fix: toolbar.py automatic initialisation works 2023-11-19 19:04:32 +01:00
wyzula-jan
ee3b616ec1 refactor: toolbar.py migration to native qt QToolBar 2023-11-19 15:17:23 +01:00
wyzula-jan
286e62df92 feat: toolbar.py proof-of-concept 2023-11-18 15:53:24 +01:00
wyzula-jan
b07bb3dde2 refactor: editor.py migration to qtpy 2023-11-18 13:35:57 +01:00
wyzula-jan
10dfe9fb65 refactor: change from QMainWindow to QWidget 2023-11-17 19:07:16 +01:00
wyzula-jan
a0d172e3dc fix: terminal output as QThread 2023-11-16 14:07:59 +01:00
wyzula-jan
94878448c8 feat: basic text editor + running terminal output 2023-11-16 14:06:06 +01:00
wyzula-jan
745aa6e812 ci: added pylint to ci 2023-11-15 12:19:06 +01:00
wyzula-jan
b14d95ad2b build: added option to add PyQt6/PyQt5/PySide2/PySide6 as qt distribution with PyQt6 as default 2023-11-14 00:34:57 +01:00
65cbd6ef28 ci: added libdbus 2023-11-13 21:56:24 +01:00
wyzula-jan
bb64088282 ci: added libegl1-mesa to the apt-get install command in tests 2023-11-13 18:51:21 +01:00
wyzula-jan
b6f6bc5b20 refactor: migration to qtpy 2023-11-13 18:37:32 +01:00
semantic-release
7957d2c566 0.31.0
Automatically generated by python-semantic-release
2023-11-13 14:52:28 +00:00
wyzula-jan
84ef7e59c9 refactor: monitor_config_validator.py name validation logic 2023-11-13 15:42:37 +01:00
wyzula-jan
cae4f8b934 refactor: fix bec_lib imports in tests 2023-11-13 15:42:37 +01:00
wyzula-jan
53494c7327 refactor: monitor_config_validator.py device_manager renamed to devices 2023-11-13 15:42:37 +01:00
wyzula-jan
05c822617a test: tests fixed; test_bec_monitor.py extended for FakeDevice class 2023-11-13 15:42:37 +01:00
wyzula-jan
6b114c2461 refactor: clean up 2023-11-13 15:42:37 +01:00
wyzula-jan
cd9cd9ef9d refactor: BECMonitor cleanup for validation in on_scan_segment; dropped support for multiple entries for single device 2023-11-13 15:42:37 +01:00
wyzula-jan
37278e363c refactor: configs for BECMonitor are validated by pydantic outside the main widget 2023-11-13 15:42:25 +01:00
wyzula-jan
92a5325aad docs: pydantic validation module docs 2023-11-13 15:41:55 +01:00
wyzula-jan
7fec0c7e44 feat: pydantic validation module for monitor.py 2023-11-13 15:41:55 +01:00
wyzula-jan
59bc40427c refactor: monitor.py update_config renamed to on_config_update; gui_id added 2023-11-13 15:41:55 +01:00
5ec2b08e34 refactor: fix bec_lib imports 2023-11-13 08:29:07 +01:00
semantic-release
b38e942acb 0.30.0
Automatically generated by python-semantic-release
2023-11-10 09:58:53 +00:00
wyzula-jan
832a438b24 test: tests for scan_control 2023-11-09 21:53:09 +01:00
wyzula-jan
43777f58f6 refactor: changed buttons name to be consistent with other projects 2023-11-09 21:02:26 +01:00
wyzula-jan
aa4c7c3385 feat: WidgetIO support for QLabel 2023-11-09 21:02:25 +01:00
wyzula-jan
b85cc898d5 fix: added imports to __init__.py in widget for ScanControl class 2023-11-09 16:30:06 +01:00
wyzula-jan
da9025e032 fix: scan_control.py args_size_max fixed 2023-11-09 12:13:24 +01:00
wyzula-jan
5c67026637 fix: scan_control.py default spinBox limits increases 2023-11-08 16:21:21 +01:00
wyzula-jan
ee2f36fb40 fix: scan_control.py supports minimum and maximum number of args 2023-11-08 16:02:50 +01:00
wyzula-jan
5ac3526384 fix: scan_control.py wipe table and reinitialise devices when scan is changed 2023-11-08 15:17:41 +01:00
wyzula-jan
3be9c974b5 refactor: scan_control.py refactor to use WidgetIO 2023-11-08 14:35:43 +01:00
wyzula-jan
18a702572f fix: widget_IO.py added handler for QCheckBox 2023-11-08 14:25:43 +01:00
wyzula-jan
63f23cf78e refactor: scan_control.py extraction of args separated 2023-11-08 14:25:43 +01:00
wyzula-jan
2e42ba174f fix: scan_control.py scan can be executed from GUI 2023-11-08 14:25:43 +01:00
wyzula-jan
8bc88ca195 refactor: scan_control.py kwargs are in grid layout, args in table widget 2023-11-08 14:25:43 +01:00
wyzula-jan
f5ff15fb9a refactor: scan_control.py kwargs and args layouts changed to QGridLayouts 2023-11-08 14:25:43 +01:00
wyzula-jan
4b7592c279 fix: scan_control.py all kwargs are rendered 2023-11-08 14:25:43 +01:00
wyzula-jan
0fe06ade5b feat: scan_control.py added option to limit scan selection from list of strings as init parameter 2023-11-08 14:25:43 +01:00
wyzula-jan
27f6a89a29 refactor: scan_control.py clean up 2023-11-08 14:25:42 +01:00
wyzula-jan
b311069722 fix: scan_control.py kwargs and args are added to the correct layouts 2023-11-08 14:25:42 +01:00
wyzula-jan
26c6e1f4b8 refactor: scan_control.py generate_input_field refactored into smaller functions 2023-11-08 14:25:42 +01:00
wyzula-jan
088fa516a8 feat: scan_control.py a general widget which can generate GUI for scan input 2023-11-08 14:25:42 +01:00
wyzula-jan
975aadbf07 refactor: changed widget_IO.py to widget_io.py for consistency; widget_io.py example excluded from coverage 2023-11-08 14:21:29 +01:00
wyzula-jan
1f0103480d refactor: clean up 2023-11-08 11:23:25 +01:00
wyzula-jan
679d3e1980 test: test_widget_io.py fixed 2023-11-08 11:20:40 +01:00
wyzula-jan
3c28cf0e01 refactor: widget_hierarchy.py renamed to widgets_io.py 2023-11-08 11:07:58 +01:00
wyzula-jan
9308f60b88 refactor: widget_hierarchy.py changed into general purpose modul to extract values from widgets using handlers 2023-11-08 11:06:37 +01:00
semantic-release
ebd0e588d4 0.29.0
Automatically generated by python-semantic-release
2023-10-31 15:22:02 +00:00
wyzula-jan
42fe859fca refactor: yaml_dialog.py save/load logic changed 2023-10-31 16:18:17 +01:00
wyzula-jan
850f02338e test: test_yaml_dialog.py tests for loading/saving dialog for .yaml export 2023-10-31 16:03:53 +01:00
wyzula-jan
10539f0ba5 fix: yaml_dialog.py added support for .yml files 2023-10-31 15:20:08 +01:00
wyzula-jan
4a6e73f4f7 docs: config_dialog.py comments added to example cases 2023-10-31 15:15:39 +01:00
wyzula-jan
f396f98e73 test: test_hierarchy.py added 2023-10-31 15:11:49 +01:00
wyzula-jan
de23c28e40 refactor: qt_utils/hierarchy function refactored to use widget handler allowing to add more widget support in the future 2023-10-31 13:04:44 +01:00
wyzula-jan
fd49f1b484 refactor: config_dialog.py add_new_plot_tab and add_new_scan_tab changed names 2023-10-31 13:03:20 +01:00
wyzula-jan
ff1d918d43 fix: yaml_dialog.py added return None if no file path is specified 2023-10-31 11:17:18 +01:00
wyzula-jan
d52aa15aac fix: wrong __init__.py in modular_app 2023-10-30 18:52:07 +01:00
wyzula-jan
3a4cbb1bb6 refactor: test_bec_monitor.py and test_config_dialog.py cleaned up 2023-10-30 17:48:09 +00:00
wyzula-jan
3866d7ce4d fix: test_bec_monitor.py config loaded fresh in the test function to avoid parameter leak 2023-10-30 17:48:09 +00:00
wyzula-jan
989cd51162 fix: test_bec_monitor.py setup_monitor help function changed to pytest.fixture 2023-10-30 17:48:09 +00:00
wyzula-jan
1fd018512f refactor: test_config_dialog.py and test_bec_monitor.py clean up 2023-10-30 17:48:09 +00:00
wyzula-jan
1cdd760e40 fix: test_config_dialog.py - QApplication removed 2023-10-30 17:48:09 +00:00
wyzula-jan
1333e6cbca fix: test_config_dialog.py - test_add_new_plot_and_modify qtbot action .click() changed -> function called directly 2023-10-30 17:48:09 +00:00
wyzula-jan
4e710dda5e fix: test_config_dialog.py disabled 2023-10-30 17:48:09 +00:00
wyzula-jan
77e1d0925d fix: test_bec_monitor.py QApplication instance removed 2023-10-30 17:48:09 +00:00
wyzula-jan
60e864b259 fix: test_config_dialog.py QApplication instance added 2023-10-30 17:48:09 +00:00
wyzula-jan
a3a72b9b93 refactor: test_bec_monitor.py widget name changed 2023-10-30 17:48:09 +00:00
wyzula-jan
6a5e0adfb2 test: test_config_dialog.py added 2023-10-30 17:48:09 +00:00
wyzula-jan
5ad19b4d7b refactor: test configs are saved as yaml and shared for similar tests 2023-10-30 17:48:09 +00:00
wyzula-jan
cb6fb9d78b refactor: BECDeviceMonitor changed to BECMonitor 2023-10-30 17:48:09 +00:00
wyzula-jan
e4336cca30 test: BECDeviceMonitor tests 2023-10-30 17:48:09 +00:00
wyzula-jan
a785bca880 docs: device_monitor.py update docstrings 2023-10-30 17:48:09 +00:00
wyzula-jan
afab283988 fix: device_monitor.py BECDeviceMonitor can be promoted in the QtDesigner and then setup in the modular app 2023-10-30 17:48:09 +00:00
wyzula-jan
644a97aee8 fix: device_monitor.py crosshairs can be attached again 2023-10-30 17:48:09 +00:00
wyzula-jan
12469c8c1e fix: config_dialog.py prevents to add one scan twice 2023-10-30 17:48:09 +00:00
wyzula-jan
93db0c21ef refactor: config_dialog.py clean up 2023-10-30 17:48:09 +00:00
wyzula-jan
7e99920fc5 fix: config_dialog.py export to .yaml fixed 2023-10-30 17:48:09 +00:00
wyzula-jan
cda8daeb35 feat: widget_hierarchy.py tool to inspect hierarchy of the widget 2023-10-30 17:48:09 +00:00
wyzula-jan
2b29b6cfe2 feat: yaml_dialog.py interactive QFileDialog window to load/save .yaml files to/from dict 2023-10-30 17:48:09 +00:00
wyzula-jan
7d5429a162 refactor: config_dialog.py load dict without scan mode 2023-10-30 17:48:09 +00:00
wyzula-jan
e41d81cbd9 fix: config_dialog.py scan_type structure implemented 2023-10-30 17:48:09 +00:00
wyzula-jan
55b5ca7381 fix: config_dialog.py config from default mode can be exported to dict 2023-10-30 17:48:09 +00:00
wyzula-jan
ec88564e65 fix: config_dialog.py tabs for scans and plots are closable now 2023-10-30 17:48:09 +00:00
wyzula-jan
fbb7a918cc refactor: config_dialog.py hook_plot_tab_signals refactored 2023-10-30 17:48:09 +00:00
wyzula-jan
8ffb7d8961 refactor: config_dialog.py simpler add_new_scan and add_new_plot 2023-10-30 17:48:09 +00:00
wyzula-jan
7db9e0ef16 feature: DialogConfig ability to add different scan configuration 2023-10-30 17:48:09 +00:00
wyzula-jan
f1d7abeb25 refactor: DialogConfig implemented directly to the BECDeviceMonitor 2023-10-30 17:48:09 +00:00
wyzula-jan
d78940da3f fix: modular_app.py configs are linked to the actual version of the state of the device monitor 2023-10-30 17:48:09 +00:00
wyzula-jan
a6616f5986 feat: qt_utils custom class for class where one can delete the row with backspace or delete 2023-10-30 17:48:09 +00:00
wyzula-jan
f94a29bf4b fix: config_dialog.py can load the current configuration of the plot 2023-10-30 17:48:09 +00:00
wyzula-jan
bf2a09e630 feat: modular_app.py, device_monitor.py, config_dialog.py linked together, plot configuration can be done through GUI 2023-10-30 17:48:09 +00:00
wyzula-jan
486ffa2505 fix,refactor: config_dialog.ui changed design of general box, remove plot works 2023-10-30 17:48:09 +00:00
wyzula-jan
c9e5dd542c feat: config_dialog.py interactive editor of plot settings 2023-10-30 17:48:09 +00:00
9d36b9686e docs: added sphinx base structure 2023-10-30 17:38:27 +01:00
semantic-release
282f2db756 0.28.1
Automatically generated by python-semantic-release
2023-10-19 12:52:14 +00:00
wyzula-jan
2925a5f20e test: test_stream_plot.py basic tests for stream_plot.py, test_basic_plot.py removed 2023-10-17 13:40:30 +02:00
7152c5b229 test: add bec_dispatcher tests 2023-10-17 13:38:33 +02:00
wyzula-jan
17ea7ab703 refactor: stream_plot.py changed client initialization 2023-10-17 11:11:11 +02:00
wyzula-jan
8f83115efc test: test_basic_plot.py deactivated due to non-existing method on_scan_segment 2023-10-17 10:45:06 +02:00
wyzula-jan
6d6b1e9155 refactor: bec_dispatcher.py changed to Ivan's version 2023-10-17 10:45:06 +02:00
wyzula-jan
144e56cdd9 refactor: duplicate scripts of BasicPlot removed, BasicPlot renamed to StreamPlot 2023-10-17 10:45:06 +02:00
wyzula-jan
28908dd07c fix: stream_plot.py on_dap_update data dict opened correctly 2023-10-17 10:45:06 +02:00
wyzula-jan
ad2b798f11 refactor: stream_plot.py color static methods removed 2023-10-17 10:45:06 +02:00
wyzula-jan
f022153fa2 refactor: placeholders for stream plot 2023-10-17 10:45:06 +02:00
semantic-release
141b49ff39 0.28.0
Automatically generated by python-semantic-release
2023-10-13 14:31:31 +00:00
wyzula-jan
59bba1429c fix: scan_mode for BECDeviceMonitor fixed init_ui 2023-10-12 15:45:17 +02:00
wyzula-jan
f3f55a7ee0 feat: BECDeviceMonitor modular class which can be used to replace placeholder in .ui file. 2023-10-12 15:45:17 +02:00
wyzula-jan
75af0404b3 feat: placeholders initialised 2023-10-12 15:45:17 +02:00
semantic-release
483e18259a 0.27.2
Automatically generated by python-semantic-release
2023-10-12 13:42:06 +00:00
f7cbdbc5ca fix: scan_plot tests
Add scanID key to scan_segment in tests
2023-10-12 15:32:52 +02:00
7335aa9597 refactor: replace connect with connect_slot 2023-10-12 15:13:57 +02:00
f01078fc21 refactor: remove all custom topic connection methods 2023-10-12 14:28:30 +02:00
616de26150 refactor: switch to generic connect_slot method in plots 2023-10-12 13:40:07 +02:00
78b666ffdb refactor: emit content and metadata from messages in connect_slot 2023-10-12 13:32:29 +02:00
semantic-release
68be2a0418 0.27.1
Automatically generated by python-semantic-release
2023-10-10 13:42:41 +00:00
wyzula-jan
78a2b21466 Merge remote-tracking branch 'origin/extreme-scan-tests' into extreme-scan-tests 2023-10-10 12:43:16 +02:00
wyzula-jan
5814113f73 fix: extreme.py default config file changed to the config_example.yaml 2023-10-10 12:43:07 +02:00
wyzula-jan
eb1f1d481e refactor: test_extreme.py corrected typos 2023-10-10 12:43:07 +02:00
wyzula-jan
6c3dfddd28 test: test_extreme.py MessageBox buttons Cancel and Retry tested 2023-10-10 12:43:07 +02:00
wyzula-jan
5162270d28 fix: extreme.py retry action fixed in ErrorHandler 2023-10-10 12:43:07 +02:00
wyzula-jan
ac2a41d2d8 test: test_extreme.py ErrorHandler tested separately 2023-10-10 12:43:07 +02:00
wyzula-jan
5637c938cf refactor: extreme.py ErrorHandler fixed, new configs are correctly loaded 2023-10-10 12:43:07 +02:00
wyzula-jan
d2c12a9f1c refactor: extreme.py error messages for config file moved to ErrorHandler class 2023-10-10 12:43:07 +02:00
wyzula-jan
51c3a9e9ee fix: extreme.py advanced error handling with possibility to reload different config 2023-10-10 12:43:07 +02:00
wyzula-jan
9750039097 fix: extreme.py error in configuration are displayed as messagebox 2023-10-10 12:43:07 +02:00
wyzula-jan
824ce821cd fix: extreme.py validation function to check config key component structure 2023-10-10 12:43:07 +02:00
wyzula-jan
90f22c2288 test: test_extreme.py error handling tested 2023-10-10 12:43:07 +02:00
wyzula-jan
fbd299c7e7 fix: extreme.py improved error handling for scan types mode 2023-10-10 12:43:07 +02:00
wyzula-jan
36942b316a test: test_extreme.py init_ui more edge cases 2023-10-10 12:43:07 +02:00
wyzula-jan
6c773c7c94 fix: extreme.py init_ui changed > to >= for setting number of columns 2023-10-10 12:43:07 +02:00
wyzula-jan
c525eba885 fix: extreme.py init_plot_background error handling 2023-10-10 12:43:07 +02:00
wyzula-jan
0338462a85 test: test_extreme.py test_init_config fixed for scan_config 2023-10-10 12:43:07 +02:00
wyzula-jan
fc6098414e fix: extreme.py ui is initialised for the first scan of config in scan mode 2023-10-10 12:43:07 +02:00
wyzula-jan
daf4ee190e test: test_extreme.py test_init_config new config tested 2023-10-10 12:43:07 +02:00
wyzula-jan
0ec65a0b41 test: test_extreme.py on_scan_segment tested with all entries correctly defined 2023-10-10 12:43:07 +02:00
wyzula-jan
ae79faa7ed fix: extreme.py client and device manager initialisation 2023-10-10 12:43:07 +02:00
wyzula-jan
d356cf734b fix: extreme.py default config file changed to the config_example.yaml 2023-10-10 12:17:05 +02:00
wyzula-jan
9e7224e0ae refactor: test_extreme.py corrected typos 2023-10-10 12:14:53 +02:00
wyzula-jan
2faeb639be test: test_extreme.py MessageBox buttons Cancel and Retry tested 2023-10-10 12:08:02 +02:00
wyzula-jan
b76df1b583 fix: extreme.py retry action fixed in ErrorHandler 2023-10-10 12:01:06 +02:00
wyzula-jan
835bda0a53 test: test_extreme.py ErrorHandler tested separately 2023-10-10 11:30:52 +02:00
wyzula-jan
aed65b411a refactor: extreme.py ErrorHandler fixed, new configs are correctly loaded 2023-10-04 18:49:58 +02:00
wyzula-jan
8050bdf82d refactor: extreme.py error messages for config file moved to ErrorHandler class 2023-10-03 16:33:46 +02:00
wyzula-jan
d623cf9539 fix: extreme.py advanced error handling with possibility to reload different config 2023-10-03 15:07:55 +02:00
wyzula-jan
89a52a0948 fix: extreme.py error in configuration are displayed as messagebox 2023-10-03 13:27:23 +02:00
wyzula-jan
5a7ac860a8 fix: extreme.py validation function to check config key component structure 2023-10-03 13:19:01 +02:00
wyzula-jan
fc31960c61 test: test_extreme.py error handling tested 2023-10-03 11:24:18 +02:00
wyzula-jan
ece1859a63 fix: extreme.py improved error handling for scan types mode 2023-10-03 11:23:51 +02:00
wyzula-jan
7b3a873800 test: test_extreme.py init_ui more edge cases 2023-10-03 10:47:46 +02:00
wyzula-jan
a0a89fe704 fix: extreme.py init_ui changed > to >= for setting number of columns 2023-10-03 10:38:24 +02:00
wyzula-jan
dafb6fae7a fix: extreme.py init_plot_background error handling 2023-10-03 10:20:35 +02:00
wyzula-jan
69aaea24f9 test: test_extreme.py test_init_config fixed for scan_config 2023-10-02 16:41:53 +02:00
wyzula-jan
82bebe6b41 fix: extreme.py ui is initialised for the first scan of config in scan mode 2023-10-02 16:38:12 +02:00
wyzula-jan
f8d30c9b0e test: test_extreme.py test_init_config new config tested 2023-10-02 16:26:05 +02:00
wyzula-jan
126451a7a9 test: test_extreme.py on_scan_segment tested with all entries correctly defined 2023-10-02 11:38:33 +02:00
wyzula-jan
cf15163bd9 fix: extreme.py client and device manager initialisation 2023-10-02 10:03:13 +02:00
wyzula-jan
6322c4720f test: test_eiger_plot.py added qtbot.waitExposed(widget) 2023-09-29 09:34:15 +00:00
wyzula-jan
08d956940e test: test_eiger_plot.py optimised imports 2023-09-29 09:34:15 +00:00
wyzula-jan
f74a6a0b8b refactor: added __init__.py to all example folders 2023-09-29 09:34:15 +00:00
wyzula-jan
779f34f500 test: added initial tests for extreme.py 2023-09-29 09:34:15 +00:00
wyzula-jan
c827a25dab test: added test_zmq_consumer for eiger_plot.py 2023-09-29 09:34:15 +00:00
wyzula-jan
0a0d51d278 test: added test_start_zmq_consumer for eiger_plot.py 2023-09-29 09:34:15 +00:00
wyzula-jan
70684d119f test: added test_on_image_update for eiger_plot.py 2023-09-29 09:34:15 +00:00
wyzula-jan
153c5f4f9d fix: formatter fixed 2023-09-28 17:19:45 +02:00
wyzula-jan
8b3a0baaa6 test: test_eiger_plot.py added qtbot.waitExposed(widget) 2023-09-28 17:06:39 +02:00
wyzula-jan
5e9deae765 test: test_eiger_plot.py optimised imports 2023-09-28 17:06:07 +02:00
wyzula-jan
4772c244c2 refactor: added __init__.py to all example folders 2023-09-28 16:13:04 +02:00
wyzula-jan
80190ccba7 test: added initial tests for extreme.py 2023-09-28 16:10:09 +02:00
wyzula-jan
abd3ebec1f test: added test_zmq_consumer for eiger_plot.py 2023-09-27 12:06:53 +02:00
wyzula-jan
7485aa999f test: added test_start_zmq_consumer for eiger_plot.py 2023-09-27 11:58:45 +02:00
wyzula-jan
ad1d69fa66 test: added test_on_image_update for eiger_plot.py 2023-09-25 17:07:12 +02:00
977ce3ae93 refactor: fixed formatting for mca plot 2023-09-25 13:52:55 +02:00
semantic-release
93f21eefd7 0.27.0
Automatically generated by python-semantic-release
2023-09-25 08:27:18 +00:00
wyzula-jan
44cc881ac9 fix: epics removed from requirements 2023-09-25 10:26:07 +02:00
wyzula-jan
ee3cae6472 Merge branch 'motor_go_end' 2023-09-25 10:21:52 +02:00
wyzula-jan
b78152b149 fix: motor_example.py load .csv logic fixed 2023-09-22 14:29:14 +02:00
wyzula-jan
85841cdf1f fix: motor_example.py export .csv logic fixed 2023-09-22 14:17:22 +02:00
wyzula-jan
ed3f656d5e refactor: motor_example.py removed old table related functions 2023-09-22 14:10:24 +02:00
wyzula-jan
a4fb6bd1d2 perf: motor_example.py replot logic optimizes 2023-09-22 13:52:48 +02:00
wyzula-jan
05f48de3f1 fix: motor_example.py precision in duplicate table fixed 2023-09-22 13:46:53 +02:00
wyzula-jan
401fec8539 fix: motor_example.py duplicate table fixed 2023-09-22 13:43:57 +02:00
wyzula-jan
b13509e9eb fix: motor_example.py manual changing coordinates in start/stop works again 2023-09-22 11:22:26 +02:00
wyzula-jan
a15860abac fix: motor_example.py replot points logic simplified 2023-09-22 10:59:08 +02:00
wyzula-jan
673ed325d1 fix: motor_example.py new independent mapping relying on the table 2023-09-22 09:39:58 +02:00
wyzula-jan
63f52fc841 fix: extreme.py formatting fixed 2023-09-21 11:27:27 +02:00
wyzula-jan
200e8b2351 Merge branch 'fix-line-plot' 2023-09-21 11:23:13 +02:00
wyzula-jan
e4f23f5101 fix: line_plot.py ROI interactions fixed 2023-09-21 11:22:43 +02:00
e21536
b41d63ea4d fix: online changes e21543 2023-09-21 10:43:45 +02:00
wyzula-jan
418480f1fc fix: motor_example.py user is blocked to duplicate last entry in start/end mode if end coordinate was not defined 2023-09-19 14:16:41 +02:00
semantic-release
6955b6e292 0.26.7
Automatically generated by python-semantic-release
2023-09-19 12:07:32 +00:00
wyzula-jan
abe35bf967 fix: eiger_plot_hist.py removed 2023-09-19 14:06:24 +02:00
semantic-release
174ab8fd8b 0.26.6
Automatically generated by python-semantic-release
2023-09-19 12:03:25 +00:00
wyzula-jan
7ff72b4086 docs: extreme.py updated documentation 2023-09-19 14:01:05 +02:00
wyzula-jan
cb144c7c2c fix: extreme.py saved to .yaml works correctly for different scans configurations 2023-09-19 12:09:17 +02:00
wyzula-jan
5f3d55b760 refactor: extreme.py plot init moved to config_init 2023-09-19 12:06:24 +02:00
wyzula-jan
a6940235be refactor: extreme.py changed initialisation of config 2023-09-19 11:57:43 +02:00
wyzula-jan
4287ac8885 fix: extreme.py fixed logic of loading new config.yaml during app operation 2023-09-19 11:51:57 +02:00
wyzula-jan
08f508f4c3 fix: motor_example.py - new more robust logic for getting coordinates for table go buttons 2023-09-14 17:11:24 +02:00
wyzula-jan
6124eab971 refactor: motor_example.py - function to connect buttons in the table 2023-09-14 15:50:15 +02:00
wyzula-jan
65b045e1a2 feat: motor_example.py in start/end mode new button allowing user to go to end position 2023-09-14 15:22:03 +02:00
semantic-release
bd28aa0361 0.26.5
Automatically generated by python-semantic-release
2023-09-13 08:05:28 +00:00
wyzula-jan
a5c6ffaa02 fix: motor_example.py help extended 2023-09-13 10:04:22 +02:00
wyzula-jan
34c785b92c refactor: extreme config example 2023-09-13 09:50:56 +02:00
semantic-release
7ad1cb47f3 0.26.4
Automatically generated by python-semantic-release
2023-09-12 15:43:45 +00:00
wyzula-jan
7cb56e9e7f fix: logic fixed 2023-09-12 17:41:47 +02:00
semantic-release
4fabee69d8 0.26.3
Automatically generated by python-semantic-release
2023-09-12 15:05:43 +00:00
wyzula-jan
230ccba909 Merge remote-tracking branch 'origin/master' 2023-09-12 17:04:37 +02:00
wyzula-jan
b867f25c78 fix: import works for both modes 2023-09-12 17:04:25 +02:00
semantic-release
9b715c69c0 0.26.2
Automatically generated by python-semantic-release
2023-09-12 14:54:55 +00:00
wyzula-jan
cacc076959 fix: import with start/stop mode works again 2023-09-12 16:53:50 +02:00
semantic-release
7df7aadea8 0.26.1
Automatically generated by python-semantic-release
2023-09-12 14:02:16 +00:00
wyzula-jan
56e619d239 Merge remote-tracking branch 'origin/master' 2023-09-12 16:01:12 +02:00
wyzula-jan
0e634ee2ac fix: removed scipy from eiger_plot.py 2023-09-12 15:59:38 +02:00
semantic-release
19746c0b76 0.26.0
Automatically generated by python-semantic-release
2023-09-12 13:57:21 +00:00
wyzula-jan
7b844c805d Merge branch 'extreme-feedback' 2023-09-12 15:56:08 +02:00
wyzula-jan
723503851b refactor: config_example.yaml 2023-09-12 15:55:59 +02:00
wyzula-jan
57e69907d5 feat: plot different signals and plot configurations based on different scans 2023-09-12 14:54:18 +02:00
semantic-release
f03dac0167 0.25.1
Automatically generated by python-semantic-release
2023-09-12 10:01:01 +00:00
wyzula-jan
8ff983f16e fix: specific config for csaxs 2023-09-12 11:59:34 +02:00
wyzula-jan
10ccf0cc97 fix: mode lock in config to disable changing the mode for users 2023-09-12 11:57:29 +02:00
semantic-release
c510f4eb63 0.25.0
Automatically generated by python-semantic-release
2023-09-12 09:44:32 +00:00
wyzula-jan
12b46a71a2 Merge branch 'cSAX-feedback' 2023-09-12 11:43:24 +02:00
semantic-release
8d860ec3d1 0.24.2
Automatically generated by python-semantic-release
2023-09-12 06:52:49 +00:00
e20643
265744076c fix: changes e20643 2023-09-12 08:51:24 +02:00
wyzula-jan
2123361ada fix: extra columns works again 2023-09-11 17:50:03 +02:00
wyzula-jan
f2fde2cf5c feat: comboBox to switch between entries mode 2023-09-11 17:04:38 +02:00
wyzula-jan
14a0c92fb9 refactor: changed order of columns 2023-09-11 16:42:12 +02:00
wyzula-jan
702e758812 refactor: align_table_center as a static method 2023-09-11 11:35:21 +02:00
wyzula-jan
63e3896725 fix: resize table is user controlled 2023-09-11 11:21:09 +02:00
semantic-release
ddaafa6a04 0.24.1
Automatically generated by python-semantic-release
2023-09-08 16:05:03 +00:00
wyzula-jan
f79a143417 Merge remote-tracking branch 'origin/master' 2023-09-08 18:04:03 +02:00
wyzula-jan
3b12f1bc1d fix: typo fixed in mca_plot.py 2023-09-08 18:03:54 +02:00
semantic-release
a7934d58d8 0.24.0
Automatically generated by python-semantic-release
2023-09-08 15:57:09 +00:00
wyzula-jan
ae040727fc feat: histogramLUT for mca_plot 2023-09-08 17:56:07 +02:00
semantic-release
7998a67e09 0.23.0
Automatically generated by python-semantic-release
2023-09-08 15:24:17 +00:00
wyzula-jan
ade893d33d feat: added key bindings and help dialog 2023-09-08 17:23:15 +02:00
semantic-release
ea64afdbc5 0.22.0
Automatically generated by python-semantic-release
2023-09-08 14:36:01 +00:00
wyzula-jan
3774b1ae81 Merge remote-tracking branch 'origin/master' 2023-09-08 16:34:58 +02:00
wyzula-jan
b984f0f36e feat: added FFT 2023-09-08 16:34:39 +02:00
semantic-release
b83a4926dc 0.21.2
Automatically generated by python-semantic-release
2023-09-08 14:28:30 +00:00
wyzula-jan
87d5467643 fix: moved mask as a last step of image processing 2023-09-08 16:27:23 +02:00
semantic-release
562b28365a 0.21.1
Automatically generated by python-semantic-release
2023-09-08 14:23:58 +00:00
wyzula-jan
43f03b5430 fix: update_signal typo fixed 2023-09-08 16:22:54 +02:00
semantic-release
5e579b5bc4 0.21.0
Automatically generated by python-semantic-release
2023-09-08 14:19:17 +00:00
wyzula-jan
d95c42eafc Merge remote-tracking branch 'origin/master' 2023-09-08 16:18:15 +02:00
wyzula-jan
ef42921c9a fix: path to mask fixed 2023-09-08 16:18:06 +02:00
wyzula-jan
33d1193c96 feat: added functionality to load mask 2023-09-08 16:16:02 +02:00
semantic-release
794f2aec3a 0.20.0
Automatically generated by python-semantic-release
2023-09-08 14:08:23 +00:00
wyzula-jan
ae8fc94979 fix: added missing .ui file to git 2023-09-08 15:57:57 +02:00
wyzula-jan
acd7a3bc92 feat: added rotate and transpose logic 2023-09-08 14:39:48 +02:00
semantic-release
2398ee3e8c 0.19.2
Automatically generated by python-semantic-release
2023-09-08 12:37:37 +00:00
wyzula-jan
c46b0024f6 Merge remote-tracking branch 'origin/master' 2023-09-08 14:36:34 +02:00
wyzula-jan
6733371c2c fix: rotation logic fixed 2023-09-08 14:36:25 +02:00
semantic-release
8cb3c377ad 0.19.1
Automatically generated by python-semantic-release
2023-09-08 12:34:47 +00:00
wyzula-jan
9f80f0f4d4 Merge remote-tracking branch 'origin/master' 2023-09-08 14:33:45 +02:00
wyzula-jan
00385abbf9 fix: rotation always counter-clockwise 2023-09-08 14:33:35 +02:00
semantic-release
f0ddfe47ca 0.19.0
Automatically generated by python-semantic-release
2023-09-08 12:31:40 +00:00
wyzula-jan
66da042882 Merge remote-tracking branch 'origin/master' 2023-09-08 14:31:19 +02:00
semantic-release
b36e154b8b 0.18.1
Automatically generated by python-semantic-release
2023-09-08 12:30:31 +00:00
wyzula-jan
f2ee03fa08 Merge remote-tracking branch 'origin/master' 2023-09-08 14:29:53 +02:00
e20636
ff640ddc24 Merge branch 'master' of https://gitlab.psi.ch/bec/bec-widgets 2023-09-08 14:29:20 +02:00
e20636
29c983fb26 fix: online changes 2023-09-08 14:28:43 +02:00
wyzula-jan
327f6b3df3 feat: rotation of the image to the left/right by 90, 180, 270 degree 2023-09-08 14:24:17 +02:00
wyzula-jan
4fa8d46631 feat: simulation stream with Gaussian peak in 1st quadrant 2023-09-08 14:14:04 +02:00
wyzula-jan
5cbedec5d9 feat: eiger_plot.py in example folder with new GUI 2023-09-08 13:57:37 +02:00
semantic-release
c2ae974bbb 0.18.0
Automatically generated by python-semantic-release
2023-09-08 09:44:56 +00:00
e20636
ba5a7f1248 Merge branch 'master' of https://gitlab.psi.ch/bec/bec-widgets 2023-09-08 11:43:19 +02:00
e20636
70d74c774d feat: eigerplot added 2023-09-08 11:42:19 +02:00
semantic-release
177421b4ea 0.17.1
Automatically generated by python-semantic-release
2023-09-08 06:46:44 +00:00
wyzula-jan
46a3981e7d fix: start_device_consumer changed from EP device_status to scan_status 2023-09-08 08:45:33 +02:00
semantic-release
9e63f9228d 0.17.0
Automatically generated by python-semantic-release
2023-09-07 14:44:48 +00:00
wyzula-jan
fb52b2a8e5 feat: console arguments added for Redis port, device, and sub_device tag 2023-09-07 16:43:04 +02:00
wyzula-jan
c368871919 feat: plot flips every second row 2023-09-07 16:35:13 +02:00
wyzula-jan
9271b91113 feat: device_consumer is getting scanID and initialise stream_consumer 2023-09-07 16:33:41 +02:00
wyzula-jan
b7136e769f refactor: functionalities separated to different methods 2023-09-07 15:43:44 +02:00
wyzula-jan
7d996ec8e7 refactor: project cleaned up 2023-09-07 15:29:49 +02:00
wyzula-jan
bfef71382e feat: simulation and simple 2D plot for mca card stream 2023-09-07 15:27:33 +02:00
semantic-release
a40f0a7070 0.16.4
Automatically generated by python-semantic-release
2023-09-06 15:25:44 +00:00
wyzula-jan
b62509a28e fix: self.limit_map_data fixed to be initialised only with integers from limits 2023-09-06 17:24:39 +02:00
semantic-release
3a68744b9a 0.16.3
Automatically generated by python-semantic-release
2023-09-06 15:21:32 +00:00
wyzula-jan
a1264fe4e2 fix: limit spinBoxes morphed to doubleSpinBoxes 2023-09-06 17:20:20 +02:00
wyzula-jan
2397af140f docs: pyqtGraph controls in help 2023-09-06 17:08:18 +02:00
wyzula-jan
63744b0fbf refactor: code cleaned up 2023-09-06 16:58:19 +02:00
wyzula-jan
92c81c6dd7 Merge remote-tracking branch 'origin/master' 2023-09-06 16:55:36 +02:00
wyzula-jan
197ebad765 refactor: code cleaned up 2023-09-06 16:55:10 +02:00
399 changed files with 30418 additions and 6299 deletions

View File

@@ -1,2 +1,3 @@
black --line-length=100 $(git diff --cached --name-only --diff-filter=ACM -- '***.py')
git add $(git diff --cached --name-only --diff-filter=ACM -- '***.py')
black --line-length=100 $(git diff --cached --name-only --diff-filter=ACM -- '*.py')
isort --line-length=100 --profile=black --multi-line=3 --trailing-comma $(git diff --cached --name-only --diff-filter=ACM -- '*.py')
git add $(git diff --cached --name-only --diff-filter=ACM -- '*.py')

View File

@@ -1,33 +1,87 @@
# This file is a template, and might need editing before it works on your project.
# Official language image. Look for the different tagged releases at:
# https://hub.docker.com/r/library/python/tags/
image: $CI_DOCKER_REGISTRY/python:3.9
image: $CI_DEPENDENCY_PROXY_GROUP_IMAGE_PREFIX/python:3.11
#commands to run in the Docker container before starting each job.
variables:
DOCKER_TLS_CERTDIR: ""
BEC_CORE_BRANCH: "main"
OPHYD_DEVICES_BRANCH: "main"
CHILD_PIPELINE_BRANCH: $CI_DEFAULT_BRANCH
workflow:
rules:
- if: $CI_PIPELINE_SOURCE == "schedule"
- if: $CI_PIPELINE_SOURCE == "web"
- if: $CI_PIPELINE_SOURCE == "pipeline"
- if: $CI_PIPELINE_SOURCE == "parent_pipeline"
- if: $CI_PIPELINE_SOURCE == "merge_request_event"
- if: $CI_COMMIT_BRANCH && $CI_OPEN_MERGE_REQUESTS
when: never
- if: $CI_COMMIT_BRANCH
include:
- template: Security/Secret-Detection.gitlab-ci.yml
- project: "bec/awi_utils"
file: "/templates/check-packages-job.yml"
inputs:
stage: test
path: "."
pytest_args: "-v --random-order tests/"
exclude_packages: ""
# different stages in the pipeline
stages:
- Formatter
- test
- AdditionalTests
- End2End
- Deploy
.install-qt-webengine-deps: &install-qt-webengine-deps
- apt-get -y install libnss3 libxdamage1 libasound2 libatomic1 libxcursor1
- 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 xvfb
- *install-qt-webengine-deps
before_script:
- if [[ "$CI_PROJECT_PATH" != "bec/bec_widgets" ]]; then
echo -e "\033[35;1m Using branch $CHILD_PIPELINE_BRANCH of BEC Widgets \033[0;m";
test -d bec_widgets || git clone --branch $CHILD_PIPELINE_BRANCH https://gitlab.psi.ch/bec/bec_widgets.git; cd bec_widgets;
fi
formatter:
stage: Formatter
needs: []
script:
- pip install black
- black --check --diff --color --line-length=100 ./
- pip install black isort
- isort --check --diff ./
- black --check --diff --color ./
rules:
- if: $CI_PROJECT_PATH == "bec/bec_widgets"
pylint:
stage: Formatter
needs: []
script:
before_script:
- pip install pylint pylint-exit anybadge
- pip install -e .[dev]
- pip install -e .[dev,pyqt6]
script:
- mkdir ./pylint
- pylint ./bec_widgets --output-format=text --output=./pylint/pylint.log | tee ./pylint/pylint.log || pylint-exit $?
- PYLINT_SCORE=$(sed -n 's/^Your code has been rated at \([-0-9.]*\)\/.*/\1/p' ./pylint/pylint.log)
@@ -37,25 +91,135 @@ pylint:
paths:
- ./pylint/
expire_in: 1 week
rules:
- if: $CI_PROJECT_PATH == "bec/bec_widgets"
pylint-check:
stage: Formatter
needs: []
allow_failure: true
before_script:
- pip install pylint pylint-exit anybadge
- apt-get update
- apt-get install -y bc
script:
- git fetch origin $CI_MERGE_REQUEST_TARGET_BRANCH_NAME
# Identify changed Python files
- if [ "$CI_PIPELINE_SOURCE" == "merge_request_event" ]; then
TARGET_BRANCH_COMMIT_SHA=$(git rev-parse origin/$CI_MERGE_REQUEST_TARGET_BRANCH_NAME);
CHANGED_FILES=$(git diff --name-only $TARGET_BRANCH_COMMIT_SHA HEAD | grep '\.py$' || true);
else
CHANGED_FILES=$(git diff --name-only $CI_COMMIT_BEFORE_SHA $CI_COMMIT_SHA | grep '\.py$' || true);
fi
- if [ -z "$CHANGED_FILES" ]; then echo "No Python files changed."; exit 0; fi
- echo "Changed Python files:"
- $CHANGED_FILES
# Run pylint only on changed files
- mkdir ./pylint
- pylint $CHANGED_FILES --output-format=text | tee ./pylint/pylint_changed_files.log || pylint-exit $?
- PYLINT_SCORE=$(sed -n 's/^Your code has been rated at \([-0-9.]*\)\/.*/\1/p' ./pylint/pylint_changed_files.log)
- echo "Pylint score is $PYLINT_SCORE"
# Fail the job if the pylint score is below 9
- if [ "$(echo "$PYLINT_SCORE < 9" | bc)" -eq 1 ]; then echo "Your pylint score is below the acceptable threshold (9)."; exit 1; fi
artifacts:
paths:
- ./pylint/
expire_in: 1 week
rules:
- if: $CI_PROJECT_PATH == "bec/bec_widgets"
tests:
stage: test
needs: []
variables:
QT_QPA_PLATFORM: "offscreen"
QT_QPA_PLATFORM: "offscreen"
script:
- apt-get update
- apt-get install -y libgl1-mesa-glx x11-utils libxkbcommon-x11-0
- pip install .[dev]
- coverage run --source=./bec_widgets -m pytest -v --junitxml=report.xml --random-order --full-trace ./tests
- *clone-repos
- *install-os-packages
- *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
- coverage xml
coverage: '/(?i)total.*? (100(?:\.0+)?\%|[1-9]?\d(?:\.\d+)?\%)$/'
artifacts:
reports:
junit: report.xml
cobertura: coverage.xml
coverage_report:
coverage_format: cobertura
path: coverage.xml
paths:
- tests/reference_failures/
when: always
test-matrix:
parallel:
matrix:
- PYTHON_VERSION:
- "3.10"
- "3.11"
- "3.12"
QT_PCKG:
- "pyside6"
- "pyqt6"
stage: AdditionalTests
needs: []
variables:
QT_QPA_PLATFORM: "offscreen"
PYTHON_VERSION: ""
QT_PCKG: ""
image: $CI_DEPENDENCY_PROXY_GROUP_IMAGE_PREFIX/python:$PYTHON_VERSION
script:
- *clone-repos
- *install-os-packages
- *install-repos
- pip install -e .[dev,$QT_PCKG]
- pytest -v --junitxml=report.xml --random-order ./tests/unit_tests
allow_failure: true
end-2-end-conda:
stage: End2End
needs: []
image: continuumio/miniconda3
allow_failure: false
variables:
QT_QPA_PLATFORM: "offscreen"
script:
- *clone-repos
- *install-os-packages
- conda config --prepend channels conda-forge
- conda config --set channel_priority strict
- conda config --set always_yes yes --set changeps1 no
- conda create -q -n test-environment python=3.11
- conda init bash
- source ~/.bashrc
- conda activate test-environment
- cd ./bec
- source ./bin/install_bec_dev.sh -t
- cd ../
- pip install -e ./ophyd_devices
- pip install -e .[dev,pyqt6]
- cd ./tests/end-2-end
- pytest -v --start-servers --flush-redis --random-order
artifacts:
when: on_failure
paths:
- ./logs/*.log
expire_in: 1 week
rules:
- if: '$CI_PIPELINE_SOURCE == "schedule"'
- if: '$CI_PIPELINE_SOURCE == "web"'
- if: '$CI_PIPELINE_SOURCE == "pipeline"'
- if: '$CI_PIPELINE_SOURCE == "parent_pipeline"'
- if: '$CI_MERGE_REQUEST_TARGET_BRANCH_NAME == "main"'
- if: '$CI_MERGE_REQUEST_TARGET_BRANCH_NAME == "production"'
semver:
stage: Deploy
@@ -71,31 +235,29 @@ semver:
- git fetch --tags
- git tag
# build
- pip install python-semantic-release==7.* wheel
# build and publish package
- pip install python-semantic-release==9.* wheel build twine
- export GL_TOKEN=$CI_UPDATES
- export REPOSITORY_USERNAME=__token__
- export REPOSITORY_PASSWORD=$CI_PYPI_TOKEN
- >
semantic-release publish -v DEBUG
-D version_variable=./setup.py:__version__
-D hvcs=gitlab
- semantic-release -vv version
# check if any artifacts were created
- if [ ! -d dist ]; then echo No release will be made; exit 0; fi
- twine upload dist/* -u __token__ -p $CI_PYPI_TOKEN --skip-existing
- semantic-release publish
allow_failure: false
rules:
- if: '$CI_COMMIT_REF_NAME == "master"'
- if: '$CI_COMMIT_REF_NAME == "main" && $CI_PROJECT_PATH == "bec/bec_widgets"'
# pages:
# stage: Deploy
# needs: ["tests"]
# script:
# - git clone --branch $OPHYD_DEVICES_BRANCH https://oauth2:$CI_OPHYD_DEVICES_KEY@gitlab.psi.ch/bec/ophyd_devices.git
# - export OPHYD_DEVICES_PATH=$PWD/ophyd_devices
# - pip install -r ./docs/source/requirements.txt
# - apt-get install -y gcc
# - *install-bec-services
# - cd ./docs/source; make html
# - curl -X POST -d "branches=$CI_COMMIT_REF_NAME" -d "token=$RTD_TOKEN" https://readthedocs.org/api/v2/webhook/beamline-experiment-control/221870/
# rules:
# - if: '$CI_COMMIT_REF_NAME == "master"'
# - if: '$CI_COMMIT_REF_NAME == "production"'
pages:
stage: Deploy
needs: ["semver"]
variables:
TARGET_BRANCH: $CI_COMMIT_REF_NAME
rules:
- if: "$CI_COMMIT_TAG != null"
variables:
TARGET_BRANCH: $CI_COMMIT_TAG
- if: '$CI_COMMIT_REF_NAME == "main" && $CI_PROJECT_PATH == "bec/bec_widgets"'
script:
- curl -X POST -d "branches=$CI_COMMIT_REF_NAME" -d "token=$RTD_TOKEN" https://readthedocs.org/api/v2/webhook/bec-widgets/253243/

View File

@@ -0,0 +1,17 @@
## Bug report
## Summary
[Provide a brief description of the bug.]
## Expected Behavior vs Actual Behavior
[Describe what you expected to happen and what actually happened.]
## Steps to Reproduce
[Outline the steps that lead to the bug's occurrence. Be specific and provide a clear sequence of actions.]
## Related Issues
[Paste links to any related issues or feature requests.]

View File

@@ -0,0 +1,27 @@
## Documentation Section
[Specify the section or page of the documentation that needs updating]
## Current Information
[Provide the current information in the documentation that needs to be updated]
## Proposed Update
[Describe the proposed update or correction. Be specific about the changes that need to be made]
## Reason for Update
[Explain the reason for the documentation update. Include any recent changes, new features, or corrections that necessitate the update]
## Additional Context
[Include any additional context or information that can help the documentation team understand the update better]
## Attachments
[Attach any files, screenshots, or references that can assist in making the documentation update]
## Priority
[Assign a priority level to the documentation update based on its urgency. Use a scale such as Low, Medium, High]

View File

@@ -0,0 +1,40 @@
## Feature Summary
[Provide a brief and clear summary of the new feature you are requesting]
## Problem Description
[Explain the problem or need that this feature aims to address. Be specific about the issues or gaps in the current functionality]
## Use Case
[Describe a real-world scenario or use case where this feature would be beneficial. Explain how it would improve the user experience or workflow]
## Proposed Solution
[If you have a specific solution in mind, describe it here. Explain how it would work and how it would address the problem described above]
## Benefits
[Explain the benefits and advantages of implementing this feature. Highlight how it adds value to the product or improves user satisfaction]
## Alternatives Considered
[If you've considered alternative solutions or workarounds, mention them here. Explain why the proposed feature is the preferred option]
## Impact on Existing Functionality
[Discuss how the new feature might impact or interact with existing features. Address any potential conflicts or dependencies]
## Priority
[Assign a priority level to the feature request based on its importance. Use a scale such as Low, Medium, High]
## Attachments
[Include any relevant attachments, such as sketches, diagrams, or references that can help the development team understand your feature request better]
## Additional Information
[Provide any additional information that might be relevant to the feature request, such as user feedback, market trends, or similar features in other products]

View File

@@ -0,0 +1,28 @@
## Description
[Provide a brief description of the changes introduced by this merge request.]
## Related Issues
[Cite any related issues or feature requests that are addressed or resolved by this merge request. Use the gitlab syntax for linking issues, for example, `fixes #123` or `closes #123`.]
## Type of Change
- Change 1
- Change 2
## Potential side effects
[Describe any potential side effects or risks of merging this MR.]
## Screenshots / GIFs (if applicable)
[Include any relevant screenshots or GIFs to showcase the changes made.]
## Additional Comments
[Add any additional comments or information that may be helpful for reviewers.]
## Definition of Done
- [ ] Documentation is up-to-date.

View File

@@ -3,7 +3,7 @@
# A comma-separated list of package or module names from where C extensions may
# be loaded. Extensions are loading into the active Python interpreter and may
# run arbitrary code.
extension-pkg-allow-list=PyQt5, pyqtgraph
extension-pkg-allow-list=PyQt6, PySide6, pyqtgraph
# A comma-separated list of package or module names from where C extensions may
# be loaded. Extensions are loading into the active Python interpreter and may
@@ -52,7 +52,7 @@ persistent=yes
# Minimum Python version to use for version dependent checks. Will default to
# the version used to run pylint.
py-version=3.9
py-version=3.10
# When enabled, pylint would attempt to guess common misconfiguration and emit
# user-friendly hints instead of false-positive error messages.

25
.readthedocs.yaml Normal file
View File

@@ -0,0 +1,25 @@
# .readthedocs.yaml
# Read the Docs configuration file
# See https://docs.readthedocs.io/en/stable/config-file/v2.html for details
# Required
version: 2
# Set the version of Python and other tools you might need
build:
os: ubuntu-20.04
tools:
python: "3.10"
# Build documentation in the docs/ directory with Sphinx
sphinx:
configuration: docs/conf.py
# If using Sphinx, optionally build your docs in additional formats such as PDF
# formats:
# - pdf
# Optionally declare the Python requirements required to build your docs
python:
install:
- requirements: docs/requirements.txt

View File

@@ -1,305 +1,151 @@
# Changelog
# CHANGELOG
<!--next-version-placeholder-->
## v0.16.2 (2023-09-06)
### Fix
* X and y motor can be linked again ([`f45512e`](https://gitlab.psi.ch/bec/bec-widgets/-/commit/f45512e0ae9c189a1d26456333c5b348cd681ce7))
## v0.16.1 (2023-09-06)
### Fix
* Default values fixed from .yaml ([`8a6e2da`](https://gitlab.psi.ch/bec/bec-widgets/-/commit/8a6e2daaf95cb5417951cbe3cca0cb3e909b08b4))
## v0.16.0 (2023-09-06)
## v0.93.0 (2024-08-05)
### Feature
* Added help button ([`2087d19`](https://gitlab.psi.ch/bec/bec-widgets/-/commit/2087d19d3c2349e160327880210a5cf129852f09))
* Table can be loaded from .csv ([`15d995f`](https://gitlab.psi.ch/bec/bec-widgets/-/commit/15d995f66b892f55526bd8b0954b6886d8f861ea))
* Table can be exported to csv ([`772f18f`](https://gitlab.psi.ch/bec/bec-widgets/-/commit/772f18fa09bef54c849d2fdd58e02e8dada84a4e))
* Additional extra rows takes values from previous row ([`1235294`](https://gitlab.psi.ch/bec/bec-widgets/-/commit/1235294b034dae50ff9a2ea93bc1a318383cbbf5))
* Additional columns can be added through .yaml ([`fa76acb`](https://gitlab.psi.ch/bec/bec-widgets/-/commit/fa76acbd6dda1695add1c1159c4a96c33741a4c7))
* 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
* Help extended ([`9fba033`](https://gitlab.psi.ch/bec/bec-widgets/-/commit/9fba0334a0389f66344b84dd434d4d9a39b1565e))
* Table loads number of columns correctly ([`bf12963`](https://gitlab.psi.ch/bec/bec-widgets/-/commit/bf129632471da2e6dc5d637a5b02c321d8d3dcac))
* Content always aligned to centre ([`74884a3`](https://gitlab.psi.ch/bec/bec-widgets/-/commit/74884a37076cd047e2dc75e07246f73e5f93167e))
* fix(spinner): stop timer on close event ([`30fef92`](https://gitlab.psi.ch/bec/bec_widgets/-/commit/30fef929cf6fb4b73f48151c92a0ee54c734031d))
## v0.15.0 (2023-09-06)
* 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
* Step for x and y can be linked or separated ([`16ab746`](https://gitlab.psi.ch/bec/bec-widgets/-/commit/16ab746f54007ee0647b6602b7d74a4a59401705))
* User can choose if to save coordinates when moving to absolute coordinates ([`6324199`](https://gitlab.psi.ch/bec/bec-widgets/-/commit/632419929921fbe4e970149ce8d4e617566f71fc))
* 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
* Table checkbox fixed ([`7e6244c`](https://gitlab.psi.ch/bec/bec-widgets/-/commit/7e6244c5d3698e6fea944b9501064470b6c884c7))
* Partial fix to table checkBox ([`75f5c8f`](https://gitlab.psi.ch/bec/bec-widgets/-/commit/75f5c8fcd6e80288e1f3bc1b9c0c0b3edd1335bc))
* Coordinates markers are updated on the map, if X, Y in table manually is changed ([`0aa667b`](https://gitlab.psi.ch/bec/bec-widgets/-/commit/0aa667b70d48356bdda59b879baa3862c5e2e756))
* Added float validator to the table ([`be1bd81`](https://gitlab.psi.ch/bec/bec-widgets/-/commit/be1bd81d60373a0d9e776dc3f0d879d1bf905f7a))
* Table bug, when deleted multiple rows ([`9d83a45`](https://gitlab.psi.ch/bec/bec-widgets/-/commit/9d83a455e899e3018364123707064882076c4eb0))
* Table bug, when user deleted row and wanted to go to the previous position ([`63e6d61`](https://gitlab.psi.ch/bec/bec-widgets/-/commit/63e6d61c2e6f9cbc069c9d55c7006d18b6b34b4d))
* fix(dock): custom label can be created closable ([`4457ef2`](https://gitlab.psi.ch/bec/bec_widgets/-/commit/4457ef2147e21b856c9dcaf63c81ba98002dcaf1))
## v0.14.2 (2023-09-05)
* fix(device_combobox): set minimum size to 125px ([`1206e15`](https://gitlab.psi.ch/bec/bec_widgets/-/commit/1206e153094cd8505badf69a1461572a76b4c5ad))
### Fix
* Bec_config initialisation by command line argument ([`b7a1b8b`](https://gitlab.psi.ch/bec/bec-widgets/-/commit/b7a1b8bca1b89df859c9ed0ed17862bb6d533de7))
## v0.14.1 (2023-09-05)
### Fix
* Gui default tab changed to coordinates table ([`3c74fa5`](https://gitlab.psi.ch/bec/bec-widgets/-/commit/3c74fa59b7b83976b13afc821c1333868e62a686))
## v0.14.0 (2023-09-05)
## v0.91.0 (2024-07-23)
### Feature
* Enable gui button, in the case that motor movement is not finished ([`84155d2`](https://gitlab.psi.ch/bec/bec-widgets/-/commit/84155d22640e229820fa5104975d2675f63cef31))
* Saved coordinates are shown on the map ([`0ca665a`](https://gitlab.psi.ch/bec/bec-widgets/-/commit/0ca665a1e91d9c5dee9af0218c2e211de8304b26))
* 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
* Motor position points can be switched on/off if points were deleted ([`5b30dfd`](https://gitlab.psi.ch/bec/bec-widgets/-/commit/5b30dfd43fcbe4b9941e26cab76005ffeb21d95f))
* Highlight disapear with new motor ([`3fb8651`](https://gitlab.psi.ch/bec/bec-widgets/-/commit/3fb8651dd5777861488928b414d5bdacb517d0e9))
* New points do not make invisible points visible again ([`fb10551`](https://gitlab.psi.ch/bec/bec-widgets/-/commit/fb105513e52bcd9c62dfead16e91b45ecd817612))
* Checkbox visibility toggle is working. ([`a178c43`](https://gitlab.psi.ch/bec/bec-widgets/-/commit/a178c434b1d9efc1795b6f5115e2a8b9685ccdf2))
* Saved coordinates can be removed from table and from the map again ([`c32e95a`](https://gitlab.psi.ch/bec/bec-widgets/-/commit/c32e95a57d3faec46652b413581d830698855367))
* fix(status_item): icons changed to material design ([`1b9c55a`](https://gitlab.psi.ch/bec/bec_widgets/-/commit/1b9c55a46a0dfd8678c8e95ff64dd6e8cfb9233e))
## v0.13.0 (2023-09-05)
* 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
* Crosshair highlight at motor position ([`9228e5a`](https://gitlab.psi.ch/bec/bec-widgets/-/commit/9228e5aea3d5e4192733539643654fd635c63559))
* Increase step size double with key bindings ([`e9ef1e3`](https://gitlab.psi.ch/bec/bec-widgets/-/commit/e9ef1e315bc7222c38c1f2f3f410f5cdff994f08))
* Go, set, save current coordinates and keyboard shortcuts ([`5d6a328`](https://gitlab.psi.ch/bec/bec-widgets/-/commit/5d6a328728a017eb4f1d191c96d2659800d41941))
* feat(image_widget): plugin added ([`4371168`](https://gitlab.psi.ch/bec/bec_widgets/-/commit/43711680ba253f81fb0ffe764bcaae701b02bb49))
* feat(image_widget): all toolbar actions added ([`501eb92`](https://gitlab.psi.ch/bec/bec_widgets/-/commit/501eb923f12fa6aaa93f5428ca78e57694edfbc0))
* feat(image_widget): image_widget added ([`6a9317f`](https://gitlab.psi.ch/bec/bec_widgets/-/commit/6a9317facda896ee784c7fc1db0cd3d68cdfcf73))
### Fix
* Spinbox limits in ui file ([`8de08cf`](https://gitlab.psi.ch/bec/bec-widgets/-/commit/8de08cf9ccb092b3cfa5cf751f69fbf5edd2b217))
* Precision updated correctly ([`172ccc6`](https://gitlab.psi.ch/bec/bec-widgets/-/commit/172ccc69056380abcddf572f668a4ddbd5d34eec))
* fix(axis_setting): fix compatibility for issue with horizontal line for PyQt6 ([`1cf6e32`](https://gitlab.psi.ch/bec/bec_widgets/-/commit/1cf6e32303f82bc7c3f3391d0e96a88bc31f29fc))
## v0.12.0 (2023-09-04)
* fix(image_widget): image_widget autorange fixed ([`7f49893`](https://gitlab.psi.ch/bec/bec_widgets/-/commit/7f49893d2ce3b9d02efa764f7f10442ed6ab8f3c))
* fix(image_widget): image widget adjusted ([`3d2ca48`](https://gitlab.psi.ch/bec/bec_widgets/-/commit/3d2ca4855c36fe0af59a4b540caa3c8023a81773))
* fix(image): only single monitor image is allowed ([`fe7e542`](https://gitlab.psi.ch/bec/bec_widgets/-/commit/fe7e542b19dc5b401523501acb74ac03edf62ad4))
* fix(image): raw data are saved in image item to always have precise processing ([`c15035b`](https://gitlab.psi.ch/bec/bec_widgets/-/commit/c15035b6b769a96780a16da9e7f75af3b823654c))
### Refactor
* refactor(jupyter_console_example): added examples of standalone widgets ([`ba0d1ea`](https://gitlab.psi.ch/bec/bec_widgets/-/commit/ba0d1ea9031b4ae2e2e73bf269fbfad973b924a5))
### Test
* test(image_widget): tests added ([`70fb276`](https://gitlab.psi.ch/bec/bec_widgets/-/commit/70fb276fdf31dffc105435d3dfe7c5caea0b10ce))
## v0.89.0 (2024-07-22)
### Feature
* Config from .yaml file ([`1a67758`](https://gitlab.psi.ch/bec/bec-widgets/-/commit/1a677584708e1c91491fe84db169103bdda488e5))
* Removal of motor configurations from user ([`34212d4`](https://gitlab.psi.ch/bec/bec-widgets/-/commit/34212d4d45c88a7bba75f289a25e5488ff95fc73))
* feat(themes): moved themes to bec_qthemes ([`3798714`](https://gitlab.psi.ch/bec/bec_widgets/-/commit/3798714369adf4023f833b7749d2f46a0ec74eee))
### Fix
### Unknown
* Error message if motor do not have limits attribute ([`bf93b02`](https://gitlab.psi.ch/bec/bec-widgets/-/commit/bf93b02cdc82086b32e2bd16f4b506c1bb76c65d))
* Revert &#34;feat(themes): moved themes to bec_qthemes&#34;
### Documentation
This reverts commit 3798714369adf4023f833b7749d2f46a0ec74eee ([`fd6ae91`](https://gitlab.psi.ch/bec/bec_widgets/-/commit/fd6ae91993a23a7b8dbb2cf3c4b7c3eda6d2b0f6))
* Added documentation to all classes and methods ([`4afaa1b`](https://gitlab.psi.ch/bec/bec-widgets/-/commit/4afaa1b0ce1f29e4193e6999ecc13b1f0f662213))
## v0.88.1 (2024-07-22)
## v0.11.0 (2023-09-04)
### Refactor
### Feature
* Colorbutton next to each curve in the table to be able to set up colors ([`2c6719c`](https://gitlab.psi.ch/bec/bec-widgets/-/commit/2c6719cf390e6638cadbc814eb0c085bb45c3c6c))
### Fix
* User selected colors are preserved with the new scan ([`8e7885f`](https://gitlab.psi.ch/bec/bec-widgets/-/commit/8e7885f36dd2812e3285c4d2d101212055644c7b))
* Colorbutton change now symbols as well ([`6d2e1c9`](https://gitlab.psi.ch/bec/bec-widgets/-/commit/6d2e1c9d08595a45f502287c6490905e8df3db10))
## v0.10.0 (2023-09-01)
### Feature
* Load and export configuration into .yaml from GUI ([`e527353`](https://gitlab.psi.ch/bec/bec-widgets/-/commit/e5273539741a1261e69b1bf76af78c7c1ab0d901))
* Error messages if name or entry is wrong ([`415c4ee`](https://gitlab.psi.ch/bec/bec-widgets/-/commit/415c4ee3f232c02ee5a00a82352c7fbb0d324449))
* Number of columns can be dynamically changed ([`65bfccc`](https://gitlab.psi.ch/bec/bec-widgets/-/commit/65bfccce8fce158150652fead769721de805d99e))
* Multi window interface created for extreme BL ([`69c38d6`](https://gitlab.psi.ch/bec/bec-widgets/-/commit/69c38d67e4e9b8a30767f6f67defce6c5c2e5b16))
### Fix
* Check if num_columns is not higher that actual number of plots ([`aac6e17`](https://gitlab.psi.ch/bec/bec-widgets/-/commit/aac6e172f6e4583e751bee00db6f381aaff8ac69))
* Add max number of columns according to the number of plots ([`fbd71c1`](https://gitlab.psi.ch/bec/bec-widgets/-/commit/fbd71c131386508a9ec7bb5963afefc13f8b1618))
* More specific error messages ([`583e643`](https://gitlab.psi.ch/bec/bec-widgets/-/commit/583e643dacac3d7aaa744751baef2da69f6f892e))
* Bec_dispatcher.py can take multiple workers as a list ([`7bcf88d`](https://gitlab.psi.ch/bec/bec-widgets/-/commit/7bcf88d5eb139aa3cf491185b9fb3f45aa5e39a2))
* Config.yaml can be passed as a console argument to extreme.py ([`b8aa373`](https://gitlab.psi.ch/bec/bec-widgets/-/commit/b8aa37321d6ac0ebd9f2237c8d2ed6594b614b57))
* Columns span generalised for any number of columns ([`2d851b6`](https://gitlab.psi.ch/bec/bec-widgets/-/commit/2d851b6b4eb0002e32908c2effbfb79122f18c24))
### Documentation
* Updated documentation and TODOs ([`0ebe35a`](https://gitlab.psi.ch/bec/bec-widgets/-/commit/0ebe35ac7a144db84c323f9ecb85dfdf6de66c21))
* Fixed documentation ([`2f7c1b9`](https://gitlab.psi.ch/bec/bec-widgets/-/commit/2f7c1b92a9624741f6dea44fc8f3c19a8a506fd9))
## v0.9.0 (2023-08-29)
### Feature
* Migrate to .yaml config file instead of argparse ([`a9f1688`](https://gitlab.psi.ch/bec/bec-widgets/-/commit/a9f16884b0b274e36fdb531b56a26343692a78f5))
* Better color coding of curves ([`0eff18f`](https://gitlab.psi.ch/bec/bec-widgets/-/commit/0eff18f5a074ea806d43d52ae72bf87f0187a26d))
## v0.8.1 (2023-08-29)
### Fix
* Added missing local .ui file ([`f0589b7`](https://gitlab.psi.ch/bec/bec-widgets/-/commit/f0589b79ec7f50ee9d040b911d1874b4232659d5))
## v0.8.0 (2023-08-29)
### Feature
* User can specify tuple of (x,y) devices which wants to plot ([`3344f1b`](https://gitlab.psi.ch/bec/bec-widgets/-/commit/3344f1b92a7e4f4ecd2e63c66aa01d3a4c325070))
* Fit table hardcode to "gaussian_fit_worker_3" ([`3af57ab`](https://gitlab.psi.ch/bec/bec-widgets/-/commit/3af57abc4888dfcd0224bf50708488dc8192be84))
* Crosshair snapped to x, y data automatically, clicked coordinates glows ([`49ba6fe`](https://gitlab.psi.ch/bec/bec-widgets/-/commit/49ba6feb3a8494336c5772a06e9569d611fc240a))
* Crosshair snaps to data, but it is activated with button due to debug ([`223f102`](https://gitlab.psi.ch/bec/bec-widgets/-/commit/223f102aa9f0e625fecef37c827c55f9062330d7))
* Dap fit plotted as curve, data as scatter ([`118f6af`](https://gitlab.psi.ch/bec/bec-widgets/-/commit/118f6af2b97188398a3dd0e2121f73328c53465b))
* Oneplot can receive one motor and one monitor signal ([`ff545bf`](https://gitlab.psi.ch/bec/bec-widgets/-/commit/ff545bf5c9e707f2dd9b43f9d059aa8605f3916b))
* Oneplot initialized as an example app for plotting motor vs monitor signals + dispatcher loop over msg ([`98c0c64`](https://gitlab.psi.ch/bec/bec-widgets/-/commit/98c0c64e8577f7e40eb0324dfe97d0ae4670c3a2))
### Fix
* User can disable dap_worker and just choose signals to plot ([`cab5354`](https://gitlab.psi.ch/bec/bec-widgets/-/commit/cab53543e644921df69c57c70ad2b3a03bbafcc1))
* Crosshair snaps correctly to x dataset ([`2ed5d72`](https://gitlab.psi.ch/bec/bec-widgets/-/commit/2ed5d7208c42f8a1175a49236d706ebf503875e4))
## v0.7.0 (2023-08-28)
### Feature
* Labels of current motors are shown in motors limits ([`413e435`](https://gitlab.psi.ch/bec/bec-widgets/-/commit/413e4356cfde6e2432682332e470eb69427ad397))
* Total number of points, scatter size and number of point to dim after last position can be changed from GUI ([`e0b52fc`](https://gitlab.psi.ch/bec/bec-widgets/-/commit/e0b52fcedca46d913d1677b45f9815eccd92e8f7))
* Speed and frequency can be updated from GUI ([`f391a2f`](https://gitlab.psi.ch/bec/bec-widgets/-/commit/f391a2fd004f1dc8187cfe12d60f856427ae01ec))
* Speed and frequency is retrieved from devices ([`ce98164`](https://gitlab.psi.ch/bec/bec-widgets/-/commit/ce9816480b82373895b602d1a1bca7d1d9725f01))
* Delete coordinate table row by DELETE or BACKSPACE key ([`5dd0af6`](https://gitlab.psi.ch/bec/bec-widgets/-/commit/5dd0af6894a5d97457d60ef18b098e40856e4875))
* Motor selection ([`cab32be`](https://gitlab.psi.ch/bec/bec-widgets/-/commit/cab32be0092185870b5a12398103475342c8b1fd))
* New GUI ([`0226188`](https://gitlab.psi.ch/bec/bec-widgets/-/commit/0226188079f1dac4eece6b1a6fa330620f1504bc))
* Keyboard shortcut to go to coordinates ([`3c0e595`](https://gitlab.psi.ch/bec/bec-widgets/-/commit/3c0e5955d40a67935b8fb064d5c52fd3f29bd1a1))
* Ability to choose how many points should be dimmed before reaching the threshold + total number of point which should be stored. ([`9eae697`](https://gitlab.psi.ch/bec/bec-widgets/-/commit/9eae697df8a2f3961454db9ed397353f110c0e67))
* Stop movement function, one callback function for 2 motors, move_finished is emitted in move_motor function not in callback ([`187c748`](https://gitlab.psi.ch/bec/bec-widgets/-/commit/187c748e87264448d5026d9fa2f15b5fc9a55949))
* Controls are disabled while motor is moving and enabled when motor movement is finished ([`ed84293`](https://gitlab.psi.ch/bec/bec-widgets/-/commit/ed842931971fbf87ed2f3e366eb822531ef5aacc))
* Motor coordinates are now scatter instead of image ([`3f6d5c6`](https://gitlab.psi.ch/bec/bec-widgets/-/commit/3f6d5c66411459703c402f7449e8b1abae9a2b08))
* Going to absolute coordinates saves coordinate in the table for later use with tag ([`8be98c9`](https://gitlab.psi.ch/bec/bec-widgets/-/commit/8be98c9bb6af941a69c593c62d5c52339d2262bc))
* Table with coordinates getting initial coordinates of motor ([`92388c3`](https://gitlab.psi.ch/bec/bec-widgets/-/commit/92388c3cab7e024978aaa2906afbd698015dec66))
* Motor move to absolute (X,Y) coordinates ([`cbe27e4`](https://gitlab.psi.ch/bec/bec-widgets/-/commit/cbe27e46cfb6282c71844641e1ed6059e8fa96bf))
* Motor limits can be changed by spinBoxes ([`2d1665c`](https://gitlab.psi.ch/bec/bec-widgets/-/commit/2d1665c76b8174d9fffa3442afa98fe1ea6ac207))
* Switch for keyboard shortcuts for motor movement ([`cac4562`](https://gitlab.psi.ch/bec/bec-widgets/-/commit/cac45626fc9a315f9012b110760a92e27e5ed226))
* Setting map according to motor limits ([`512e698`](https://gitlab.psi.ch/bec/bec-widgets/-/commit/512e698e26d9eef05b4f430475ccc268b68ad632))
* Map of motor position ([`e6952a6`](https://gitlab.psi.ch/bec/bec-widgets/-/commit/e6952a6d13c84487fd6ab08056f1f5b46d594b8a))
* Motor_example.py created, motor samx and samy can be moved by buttons ([`947ba9f`](https://gitlab.psi.ch/bec/bec-widgets/-/commit/947ba9f8b730e96082cb51ff6894734a0e119ca1))
### Fix
* Line_plot.py default changed back to "gauss_bpm" ([`64708bc`](https://gitlab.psi.ch/bec/bec-widgets/-/commit/64708bc1b2e6a4256da9123d0215fc87e0afa455))
* Motor selection is disabled while motor is moving ([`c7e35d7`](https://gitlab.psi.ch/bec/bec-widgets/-/commit/c7e35d7da69853343aa7eee53c8ad988eb490d93))
* Init_motor_map receive motor position from motor_thread ([`95ead71`](https://gitlab.psi.ch/bec/bec-widgets/-/commit/95ead7117e59e0979aec51b85b49537ab728cad4))
* Motor movement absolute fixed - movement by thread ([`11aa15f`](https://gitlab.psi.ch/bec/bec-widgets/-/commit/11aa15fefda7433e885cc8586f93c97af83b0c48))
## v0.6.3 (2023-08-17)
### Fix
* Crosshair handles dynamic changes of number of curves in 1D plot ([`242737b`](https://gitlab.psi.ch/bec/bec-widgets/-/commit/242737b516af7c524a6c8a98db566815f0f4ab65))
### Documentation
* Crosshair class documentation ([`8a60cad`](https://gitlab.psi.ch/bec/bec-widgets/-/commit/8a60cad9187df2b2bc93dc78dd01ceb42df9c9af))
## v0.6.2 (2023-08-17)
### Fix
* Correct coordinates for cursor table ([`ce54daf`](https://gitlab.psi.ch/bec/bec-widgets/-/commit/ce54daf754cb2410790216585467c0ffcc8e3587))
## v0.6.1 (2023-08-14)
### Fix
* Crosshair snaps to correct coordinates also with logx and logy ([`167a891`](https://gitlab.psi.ch/bec/bec-widgets/-/commit/167a891c474b09ef7738e473c4a2e89dbbcbe881))
## v0.6.0 (2023-08-11)
### Feature
* New GUI for line_plot.py ([`b57b3bb`](https://gitlab.psi.ch/bec/bec-widgets/-/commit/b57b3bb1afc7c85acc7ed328ac8a219f392869f1))
* Cursor universal signals ([`20e9516`](https://gitlab.psi.ch/bec/bec-widgets/-/commit/20e951659558b7fc023e357bfe07d812c5fd020a))
## v0.5.0 (2023-08-11)
### Feature
* Add generic connect function for slots ([`6a3df34`](https://gitlab.psi.ch/bec/bec-widgets/-/commit/6a3df34cdfbec2434153362ded630305e5dc5e28))
* Add possibility to provide service config ([`8c9a9c9`](https://gitlab.psi.ch/bec/bec-widgets/-/commit/8c9a9c93535ee77c0622b483a3157af367ebce1f))
### Fix
* Dispatcher argparse and scan_plot tests ([`67f619e`](https://gitlab.psi.ch/bec/bec-widgets/-/commit/67f619ee897e0040c6310e67d69fbb2e0685293d))
* Gui event removing bugs ([`a9dd191`](https://gitlab.psi.ch/bec/bec-widgets/-/commit/a9dd191629295ca476e2f9a1b9944ff355216583))
## v0.4.0 (2023-08-11)
### Feature
* Cursor universal for 1D and 2D ([`f75554b`](https://gitlab.psi.ch/bec/bec-widgets/-/commit/f75554bd7b072207847956a8720b9a62c20ba2c8))
* Added qt_utils package with general Crosshair function ([`5353fed`](https://gitlab.psi.ch/bec/bec-widgets/-/commit/5353fed7bfe1819819fa3348ec93d2d0ba540628))
* 2D plot updating ([`d32088b`](https://gitlab.psi.ch/bec/bec-widgets/-/commit/d32088b643a4d0613c32fb464a0a55a3b6b684d6))
* Metadata available on_dap_update ([`18b5d46`](https://gitlab.psi.ch/bec/bec-widgets/-/commit/18b5d46678619a972815532629ce96c121f5fcc9))
* Plotting from streamer ([`bb806c1`](https://gitlab.psi.ch/bec/bec-widgets/-/commit/bb806c149dee88023ecb647b523cbd5189ea9001))
* Added Legend to plot ([`0feca4b`](https://gitlab.psi.ch/bec/bec-widgets/-/commit/0feca4b1578820ec1f5f3ead3073e4d45c23798b))
* Cursor coordinate as a QTable ([`a999f76`](https://gitlab.psi.ch/bec/bec-widgets/-/commit/a999f7669a12910ad66e10a6d2e75197b2dce1c2))
* Changed from PlotItem to GraphicsLayoutWidget, added LabelItem ([`075cc79`](https://gitlab.psi.ch/bec/bec-widgets/-/commit/075cc79d6fa011803cf4a06fbff8faa951c1b59f))
* Add display_ui_file.py ([`91d8ffa`](https://gitlab.psi.ch/bec/bec-widgets/-/commit/91d8ffacffcbeebdf7623caf62e07244c4dcee16))
* Add disconnect_dap_slot ([`1325704`](https://gitlab.psi.ch/bec/bec-widgets/-/commit/1325704750ebab897e3dcae80c9d455bfbbf886f))
* Inherit from GraphicsView for consistency with 2D plot ([`d8c101c`](https://gitlab.psi.ch/bec/bec-widgets/-/commit/d8c101cdd7f960a152a1f318911cac6eecf6bad4))
* Add BECScanPlot2D ([`67905e8`](https://gitlab.psi.ch/bec/bec-widgets/-/commit/67905e896c81383f57c268db544b3314104bda38))
* Emit the full bec message to slots ([`1bb3020`](https://gitlab.psi.ch/bec/bec-widgets/-/commit/1bb30207038f3a54c0e96dbbbcd1ea7f6c70eca2))
### Fix
* Q selection for gui_event signal ([`0bf452a`](https://gitlab.psi.ch/bec/bec-widgets/-/commit/0bf452ad1b7d9ad941e2ef4b8d61ec4ed5266415))
* Fixed logic in data subscription ([`c2d469b`](https://gitlab.psi.ch/bec/bec-widgets/-/commit/c2d469b4543fcf237b274399b83969cc2213b61b))
* Scan_plot to accept metadata from dap signal ([`7bec0b5`](https://gitlab.psi.ch/bec/bec-widgets/-/commit/7bec0b5e6c1663670f8fcc2fc6aa6c8b6df28b61))
* Plotting latest 1d curves ([`378be81`](https://gitlab.psi.ch/bec/bec-widgets/-/commit/378be81bf6dd5e9239f8f1fb908cafc97161c79d))
* Testing the data structure of plotting ([`4fb0a3b`](https://gitlab.psi.ch/bec/bec-widgets/-/commit/4fb0a3b058957f5b37227ff7c8e9bdf5259a1cde))
* Fix examples when run directly as a script ([`cd11ee5`](https://gitlab.psi.ch/bec/bec-widgets/-/commit/cd11ee51c1c725255e748a32b89a74487e84a631))
* Module paths ([`e7f644c`](https://gitlab.psi.ch/bec/bec-widgets/-/commit/e7f644c5079a8665d7d872eb0b27ed7da6cbd078))
## v0.3.0 (2023-07-19)
### Feature
* Add auto-computed color_list from colormaps ([`3e1708b`](https://gitlab.psi.ch/bec/bec-widgets/-/commit/3e1708bf48bc15a25c0d01242fff28d6db868e02))
* Add functionality for plotting multiple signals ([`10e2906`](https://gitlab.psi.ch/bec/bec-widgets/-/commit/10e29064455f50bc3b66c55b4361575957db1489))
* Added lineplot widget ([`989a3f0`](https://gitlab.psi.ch/bec/bec-widgets/-/commit/989a3f080839b98f1e1c2118600cddf449120124))
* Added ctrl_c from grum ([`8fee13a`](https://gitlab.psi.ch/bec/bec-widgets/-/commit/8fee13a67bef3ed6ed6de9d47438f04687f548d8))
### Fix
* Add warning for non-existing signalz ([`48075e4`](https://gitlab.psi.ch/bec/bec-widgets/-/commit/48075e4fe3187f6ac8d0b61f94f8df73b8fd6daf))
* Documentation and bugfix for mouse_moved ([`a460f3c`](https://gitlab.psi.ch/bec/bec-widgets/-/commit/a460f3c0bd7b9e106a758bc330f361868407b1e3))
### Documentation
* Add notes about qt designer install via conda-forge ([`d8038a8`](https://gitlab.psi.ch/bec/bec-widgets/-/commit/d8038a8cd0efa3a16df403390164603e4e8afdd8))
* Added license ([`db2d33e`](https://gitlab.psi.ch/bec/bec-widgets/-/commit/db2d33e8912dc493cce9ee7f09d8336155110079))
## v0.2.1 (2023-07-13)
### Fix
* Fixed setup config (wrong name) ([`947db1e`](https://gitlab.psi.ch/bec/bec-widgets/-/commit/947db1e0f32b067e67f94a7c8321da5194b1547b))
* Fixed bec_lib dependency ([`86f4def`](https://gitlab.psi.ch/bec/bec-widgets/-/commit/86f4deffd899111e8997010487ec54c6c62c43ab))
## v0.2.0 (2023-07-13)
### Feature
* Move ivan's qtwidgets to bec-widgets ([`34e5ed2`](https://gitlab.psi.ch/bec/bec-widgets/-/commit/34e5ed2cf7e6128a51c110db8870d9560f2b2831))
## v0.1.0 (2023-07-11)
### Feature
* Added config plotter ([`db274c6`](https://gitlab.psi.ch/bec/bec-widgets/-/commit/db274c644f643f830c35b6a92edd328bf7e24f59))
* refactor(toolbar): generalizations of the ToolBarAction ([`ad112d1`](https://gitlab.psi.ch/bec/bec_widgets/-/commit/ad112d1f08157f6987edd48a0bacf9f669ef1997))

View File

@@ -1,2 +1,74 @@
# BEC Widgets
BEC Widgets is a GUI framework designed for interaction with [BEC (Beamline Experiment Control)](https://gitlab.psi.ch/bec/bec).
## Installation
Use the package manager [pip](https://pip.pypa.io/en/stable/) to install BEC Widgets:
```bash
pip install bec_widgets PyQt6
```
For development purposes, you can clone the repository and install the package locally in editable mode:
```bash
git clone https://gitlab.psi.ch/bec/bec-widgets
cd bec_widgets
pip install -e .[dev,pyqt6]
```
BEC Widgets currently supports both Pyside6 and PyQt6, however, no default distribution is specified. As a result, users must install one of the supported
Python Qt distributions manually.
To select a specific Python Qt distribution, install the package with an additional tag:
```bash
pip install bec_widgets[pyqt6]
```
or
```bash
pip install bec_widgets[pyside6]
```
## Documentation
Documentation of BEC Widgets can be found [here](https://bec-widgets.readthedocs.io/en/latest/). The documentation of the BEC can be found [here](https://bec.readthedocs.io/en/latest/).
## Contributing
All commits should use the Angular commit scheme:
> #### <a name="commit-header"></a>Angular Commit Message Header
>
> ```
> <type>(<scope>): <short summary>
> │ │ │
> │ │ └─⫸ Summary in present tense. Not capitalized. No period at the end.
> │ │
> │ └─⫸ Commit Scope: animations|bazel|benchpress|common|compiler|compiler-cli|core|
> │ elements|forms|http|language-service|localize|platform-browser|
> │ platform-browser-dynamic|platform-server|router|service-worker|
> │ upgrade|zone.js|packaging|changelog|docs-infra|migrations|ngcc|ve|
> │ devtools
>
> └─⫸ Commit Type: build|ci|docs|feat|fix|perf|refactor|test
> ```
>
> The `<type>` and `<summary>` fields are mandatory, the `(<scope>)` field is optional.
> ##### Type
>
> Must be one of the following:
>
> * **build**: Changes that affect the build system or external dependencies (example scopes: gulp, broccoli, npm)
> * **ci**: Changes to our CI configuration files and scripts (examples: CircleCi, SauceLabs)
> * **docs**: Documentation only changes
> * **feat**: A new feature
> * **fix**: A bug fix
> * **perf**: A code change that improves performance
> * **refactor**: A code change that neither fixes a bug nor adds a feature
> * **test**: Adding missing tests or correcting existing tests
## License
[BSD-3-Clause](https://choosealicense.com/licenses/bsd-3-clause/)

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.5 KiB

View 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

View 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

View 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

View 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

View 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

View 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="M440.39-440.39H185.87v-79.22h254.52V-774.7h79.22v255.09H774.7v79.22H519.61v254.52h-79.22v-254.52Z"/>
</svg>

After

Width:  |  Height:  |  Size: 228 B

View 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

View 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="M114.02-114.02v-308.13h68.13v192.02l547.72-547.72H537.85v-68.37h308.37v308.37h-68.37v-192.02L230.13-182.15h192.02v68.13H114.02Z"/>
</svg>

After

Width:  |  Height:  |  Size: 258 B

View 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="m311.5-154.02-47.74-47.74 116.94-116.7H74.02v-68.37H380.7L263.76-503.76l47.74-47.74 198.98 198.74L311.5-154.02Zm337-254.72L449.76-607.48 648.5-806.22l47.74 47.74-116.7 116.94h306.68v68.37H579.54l116.7 116.69-47.74 47.74Z"/>
</svg>

After

Width:  |  Height:  |  Size: 351 B

View File

@@ -0,0 +1,4 @@
<svg xmlns="http://www.w3.org/2000/svg" height="48px" viewBox="0 0 24 24" width="48px" fill="#FFFFFF">
<path d="M0 0h24v24H0V0z" fill="none"/>
<path d="M17 7h-4v2h4c1.65 0 3 1.35 3 3s-1.35 3-3 3h-4v2h4c2.76 0 5-2.24 5-5s-2.24-5-5-5zm-6 8H7c-1.65 0-3-1.35-3-3s1.35-3 3-3h4V7H7c-2.76 0-5 2.24-5 5s2.24 5 5 5h4v-2zm-3-4h8v2H8z"/>
</svg>

After

Width:  |  Height:  |  Size: 341 B

View 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

View 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-54 303.43-230.56 361-288.13l79.39 78.83v-231.09H209.3L283.13-366l-57.57 57.57L54-480l172.56-172.57L284.13-595l-74.83 75.39h231.09v-231.65L366-676.87l-57.57-57.57L480-906l171.57 171.56L594-676.87l-74.39-74.39v231.65h231.65L676.87-594l57.57-57.57L906-480 734.44-308.43 676.87-366l74.39-74.39H519.61v231.09L599-288.13l57.57 57.57L480-54Z"/>
</svg>

After

Width:  |  Height:  |  Size: 470 B

View File

@@ -0,0 +1,9 @@
<svg xmlns="http://www.w3.org/2000/svg" enable-background="new 0 0 24 24" height="48px" viewBox="0 0 24 24" width="48px"
fill="#FFFFFF">
<g>
<rect fill="none" height="24" width="24"/>
</g>
<g>
<path d="M18,15v3H6v-3H4v3c0,1.1,0.9,2,2,2h12c1.1,0,2-0.9,2-2v-3H18z M7,9l1.41,1.41L11,7.83V16h2V7.83l2.59,2.58L17,9l-5-5L7,9z"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 371 B

View File

@@ -0,0 +1,11 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg width="100%" height="100%" viewBox="0 0 100 96" version="1.1" xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" xmlns:serif="http://www.serif.com/"
style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:2;">
<rect id="Artboard1" x="0" y="0" width="100" height="96.486" style="fill:none;"/>
<g id="Artboard11" serif:id="Artboard1">
<path d="M11.379,24.832C11.379,24.261 11.843,23.798 12.414,23.798C18.11,23.798 20.117,19.072 22.06,14.503C23.704,10.634 25.403,6.634 29.483,6.634C33.902,6.634 35.376,12.91 36.934,19.555C38.473,26.113 40.065,32.893 44.138,32.893C48.25,32.893 50.78,28.962 53.457,24.8C56.279,20.412 59.198,15.876 64.31,15.876C69.322,15.876 72.165,20.305 74.915,24.588C77.707,28.935 80.343,33.04 84.999,33.04C85.571,33.04 86.034,33.503 86.034,34.075C86.034,34.647 85.571,35.11 84.999,35.11C79.212,35.11 76.004,30.115 73.175,25.708C70.612,21.716 68.192,17.946 64.31,17.946C60.328,17.946 57.835,21.819 55.197,25.92C52.338,30.366 49.381,34.963 44.138,34.963C38.425,34.963 36.643,27.371 34.92,20.028C33.613,14.46 32.262,8.703 29.482,8.703C26.953,8.703 25.71,11.2 23.963,15.311C21.964,20.014 19.479,25.867 12.413,25.867C11.843,25.867 11.379,25.403 11.379,24.832M44.361,44.584C43.504,44.584 42.557,44.882 42.557,45.739L42.586,50.703L39.522,50.703L43.922,61.878L48.807,50.703L45.691,50.703L45.604,46.255C45.602,45.398 45.218,44.584 44.361,44.584ZM6.034,37.487L6.034,6.674L5,6.674L5,38.522L95,38.522L95,37.487L6.034,37.487M77.414,91.881L77.414,63.849C77.414,63.277 76.951,62.814 76.379,62.814C75.808,62.814 75.345,63.277 75.345,63.849L75.345,91.881C75.345,92.045 75.391,92.194 75.458,92.332L61.955,92.332C62.022,92.194 62.068,92.045 62.068,91.881L62.068,82.718C62.068,82.146 61.605,81.683 61.034,81.683C60.462,81.683 59.999,82.146 59.999,82.718L59.999,91.881C59.999,92.045 60.045,92.194 60.112,92.332L45.059,92.332C45.126,92.194 45.172,92.045 45.172,91.881L45.172,75.943C45.172,75.372 44.709,74.909 44.138,74.909C43.567,74.909 43.104,75.372 43.104,75.943L43.104,91.881C43.104,92.045 43.15,92.194 43.217,92.332L23.852,92.332C23.92,92.194 23.966,92.045 23.966,91.881L23.966,63.849C23.966,63.277 23.502,62.814 22.931,62.814C22.36,62.814 21.897,63.277 21.897,63.849L21.897,91.881C21.897,92.045 21.943,92.194 22.011,92.332L6.034,92.332L6.034,62.881L5,62.881L5,93.366L95,93.366L95,92.332L77.301,92.332C77.368,92.194 77.414,92.045 77.414,91.881"
style="fill:white;fill-rule:nonzero;stroke:white;stroke-width:5px;"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.6 KiB

View 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="m78.89-112.59 263.02-367.17h202l303.5-354.22v721.39H78.89Zm62.24-262.74-54.7-39.78 166.59-233.02h201L640.46-864.8l51.45 44.26-205.82 240.78H287.33l-146.2 204.43Zm70.54 194.37h567.37v-468.78L574.98-411.63H376.22L211.67-180.96Zm567.37 0Z"/>
</svg>

After

Width:  |  Height:  |  Size: 366 B

View File

@@ -0,0 +1,4 @@
<svg xmlns="http://www.w3.org/2000/svg" height="48px" viewBox="0 0 24 24" width="48px" fill="#FFFFFF">
<path d="M0 0h24v24H0V0z" fill="none"/>
<path d="M13 3c-4.97 0-9 4.03-9 9H1l3.89 3.89.07.14L9 12H6c0-3.87 3.13-7 7-7s7 3.13 7 7-3.13 7-7 7c-1.93 0-3.68-.79-4.94-2.06l-1.42 1.42C8.27 19.99 10.51 21 13 21c4.97 0 9-4.03 9-9s-4.03-9-9-9zm-1 5v5l4.25 2.52.77-1.28-3.52-2.09V8z"/>
</svg>

After

Width:  |  Height:  |  Size: 392 B

View 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

View 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="M283.85-289.91h67.41l42.32-112.66h172.59l43.33 112.66h68.85l-164-428h-67.5l-163 428Zm127.74-165.72 67.34-182.87H481l68.41 182.87H411.59Zm68.53 381.61q-86.32 0-160.51-31t-128.89-85.7q-54.7-54.7-85.7-128.89-31-74.19-31-160.51 0-85.31 30.94-159.4t85.7-128.9q54.76-54.8 128.95-86.3t160.51-31.5q85.31 0 159.42 31.47 74.1 31.47 128.91 86.27 54.82 54.8 86.29 128.88 31.48 74.08 31.48 159.6 0 86.2-31.5 160.39-31.5 74.19-86.3 128.95-54.81 54.76-128.9 85.7-74.09 30.94-159.4 30.94ZM480-480Zm-.04 337.85q144.08 0 240.99-96.74 96.9-96.74 96.9-241.07 0-144.32-96.86-241.11-96.86-96.78-240.95-96.78-144.08 0-240.99 96.74-96.9 96.74-96.9 241.07 0 144.32 96.86 241.11 96.86 96.78 240.95 96.78Z"/>
</svg>

After

Width:  |  Height:  |  Size: 809 B

View File

@@ -0,0 +1,9 @@
<svg xmlns="http://www.w3.org/2000/svg" enable-background="new 0 0 24 24" height="48px" viewBox="0 0 24 24" width="48px"
fill="#FFFFFF">
<g>
<rect fill="none" height="24" width="24"/>
</g>
<g>
<path d="M18,15v3H6v-3H4v3c0,1.1,0.9,2,2,2h12c1.1,0,2-0.9,2-2v-3H18z M17,11l-1.41-1.41L13,12.17V4h-2v8.17L8.41,9.59L7,11l5,5 L17,11z"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 377 B

View 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="M110.39-110.39v-89.57l77.52-77.52v167.09h-77.52Zm165.57 0v-250.13l77.52-77.52v327.65h-77.52Zm165.56 0v-327.65l77.52 77.95v249.7h-77.52Zm165.57 0v-250.83l77.52-76.96v327.79h-77.52Zm165.56 0v-411.83l76.96-76.96v488.79h-76.96ZM110.39-335.65v-112.7L400-735.96l160 160 289.61-290.61v112.14L560-463.26l-160-160-289.61 287.61Z"/>
</svg>

After

Width:  |  Height:  |  Size: 450 B

View 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="M725.93-155.93q0-118.18-45-222.09t-122-180.91q-77-77-180.91-122t-222.09-45v-68.14q132.68 0 248.61 50.23 115.92 50.23 202.5 136.75 86.57 86.53 136.8 202.53 50.23 116.01 50.23 248.63h-68.14Z"/>
</svg>

After

Width:  |  Height:  |  Size: 319 B

View 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="M571.91-279.09h191v-194h-60v134h-131v60ZM198.09-486.91h60v-134h131v-60h-191v194Zm-53 341.04q-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-509.82H145.09v509.82Zm0 0v-509.82 509.82Z"/>
</svg>

After

Width:  |  Height:  |  Size: 487 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.3 KiB

View 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

View 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="M354.61-386.61h391l-127-171-103 135-68-87-93 123ZM274.7-195.48q-32.51 0-55.87-23.35-23.35-23.36-23.35-55.87v-549.82q0-32.74 23.35-56.26 23.36-23.53 55.87-23.53h549.82q32.74 0 56.26 23.53 23.53 23.52 23.53 56.26v549.82q0 32.51-23.53 55.87-23.52 23.35-56.26 23.35H274.7Zm0-79.22h549.82v-549.82H274.7v549.82ZM135.48-55.69q-32.74 0-56.26-23.53-23.53-23.52-23.53-56.26v-629.04h79.79v629.04h629.04v79.79H135.48ZM274.7-824.52v549.82-549.82Z"/>
</svg>

After

Width:  |  Height:  |  Size: 564 B

View 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

View 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

View 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="M444.19-421.3q39.72 0 67.4-27.78 27.67-27.78 27.67-66.91 0-39.14-27.71-66.57Q483.83-610 444.4-610q-39.44 0-66.92 27.28Q350-555.44 350-516.3q0 39.13 27.36 67.06 27.35 27.94 66.83 27.94ZM634.52-274 532.78-375.74q-22.56 12.31-44.41 19.02-21.84 6.72-43.28 6.72-69.57 0-117.7-48.62-48.13-48.62-48.13-117.88 0-67.98 48.29-116.39t117.08-48.41q68.79 0 117.08 48.41Q610-584.48 610-515.75q0 21.72-6.93 44.13-6.94 22.4-20.37 44.97l102.73 102.74L634.52-274ZM185.09-105.87q-32.51 0-55.87-23.35-23.35-23.36-23.35-55.87V-352h79.22v166.91H352v79.22H185.09Zm422.91 0v-79.22h166.91V-352h79.79v166.91q0 32.51-23.53 55.87-23.52 23.35-56.26 23.35H608ZM105.87-608v-166.91q0-32.74 23.35-56.26 23.36-23.53 55.87-23.53H352v79.79H185.09V-608h-79.22Zm669.04 0v-166.91H608v-79.79h166.91q32.74 0 56.26 23.53 23.53 23.52 23.53 56.26V-608h-79.79Z"/>
</svg>

After

Width:  |  Height:  |  Size: 946 B

View File

@@ -0,0 +1,5 @@
<svg xmlns="http://www.w3.org/2000/svg" enable-background="new 0 0 24 24" height="48px" viewBox="0 0 24 24" width="48px"
fill="#8B1A10">
<rect fill="none" height="24" width="24"/>
<path d="M19,19H5V5h14V19z M3,3v18h18V3H3z M17,15.59L15.59,17L12,13.41L8.41,17L7,15.59L10.59,12L7,8.41L8.41,7L12,10.59L15.59,7 L17,8.41L13.41,12L17,15.59z"/>
</svg>

After

Width:  |  Height:  |  Size: 357 B

View 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="M473.54-344.48v-63.59h187.18v63.59H473.54Zm81.92 230.46v-56.15h-81.92v-63.59h81.92v-56.39h63.58v176.13h-63.58Zm103.58-56.15v-63.59h187.18v63.59H659.04Zm41.68-116.92v-177.13h63.58v56.15h81.92v63.59H764.3v57.39h-63.58ZM841.22-540h-68.46q-22.98-101.89-103.94-170.83-80.95-68.93-188.89-68.93-125.29 0-212.37 87.18T180.48-480q0 78.61 36.59 143.32 36.58 64.7 96.95 104.46v-108.26h66.46v226.46H154.02v-66.46h117.41q-71.56-48.72-114.48-127.36-42.93-78.64-42.93-172.16 0-76.22 28.86-142.78 28.86-66.57 78.29-116.04 49.42-49.47 116.01-78.43 66.58-28.97 142.82-28.97 136.52 0 237.87 87.8Q819.22-670.63 841.22-540Z"/>
</svg>

After

Width:  |  Height:  |  Size: 733 B

View 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

View 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

View 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="M435-72.11q-49.67-7-95.99-25.36-46.31-18.36-86.55-49.55l49.21-49.74q31.76 24 65.29 37.26 33.52 13.26 68.04 19.02v68.37Zm90 0v-68.37q109.28-21.24 181.07-102.9 71.78-81.66 71.78-197.71 0-124.37-84.11-210.87t-208.72-86.5h-18.8L540.67-664l-49.74 49.74L332.2-773l158.73-158.74 49.74 49.26-75.41 75.65h19.28q75.48 0 141.34 28.72t114.98 78.56q49.12 49.83 77.24 116.29 28.12 66.46 28.12 142.17 0 142.39-90.8 245.07Q664.63-93.35 525-72.11ZM189.46-209.78q-28.96-38.72-47.82-86.3-18.86-47.57-25.62-100.01h68.89q5 37.76 18.38 72.17 13.38 34.4 36.14 64.16l-49.97 49.98Zm-73.44-276.31q6.52-50.71 25-97.29 18.48-46.58 48.44-87.77l50.21 48.26q-22.76 33-36.14 67.52-13.38 34.52-18.62 69.28h-68.89Z"/>
</svg>

After

Width:  |  Height:  |  Size: 811 B

View 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="M527-72.11v-68.37q34.52-5.76 68.04-19.02 33.53-13.26 65.29-37.26l49.21 49.74q-40.24 31.19-86.55 49.55Q576.67-79.11 527-72.11Zm-90 0Q297.37-93.35 206.7-196.02q-90.68-102.68-90.68-245.07 0-75.71 28-142.17t77.12-116.29q49.12-49.84 114.98-78.56 65.86-28.72 141.34-28.72h19.28l-75.41-75.65 49.74-49.26L629.8-773 471.07-614.26 421.33-664l74.69-74.46h-19.04q-124.61 0-208.72 86.5t-84.11 210.87q0 116.05 71.78 197.71 71.79 81.66 181.07 102.9v68.37Zm335.54-137.67-49.97-49.98q22.76-29.76 36.14-64.16 13.38-34.41 18.38-72.17h69.13q-7 52.44-25.86 100.01-18.86 47.58-47.82 86.3Zm73.68-276.31h-69.13q-5.24-34.76-18.62-69.28t-36.14-67.52l50.21-48.26q29.96 41.19 48.44 87.77 18.48 46.58 25.24 97.29Z"/>
</svg>

After

Width:  |  Height:  |  Size: 815 B

View 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-689.22v504.13q0 32.51-23.53 55.87-23.52 23.35-56.26 23.35H185.09q-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.53h504.13L854.7-689.22Zm-79.79 35.48L653.74-774.91H185.09v589.82h589.82v-468.65ZM479.76-250.09q43.24 0 73.74-30.26 30.5-30.27 30.5-73.5 0-43.24-30.26-73.74-30.27-30.5-73.5-30.5-43.24 0-73.74 30.27-30.5 30.26-30.5 73.5 0 43.23 30.26 73.73 30.27 30.5 73.5 30.5ZM238.09-578.91h358v-143h-358v143Zm-53-74.83v468.65-589.82 121.17Z"/>
</svg>

After

Width:  |  Height:  |  Size: 621 B

View 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

View 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

View File

@@ -0,0 +1,4 @@
<svg xmlns="http://www.w3.org/2000/svg" height="48px" viewBox="0 0 24 24" width="48px" fill="#FFFFFF">
<path d="M0 0h24v24H0V0z" fill="none"/>
<path d="M19.43 12.98c.04-.32.07-.64.07-.98 0-.34-.03-.66-.07-.98l2.11-1.65c.19-.15.24-.42.12-.64l-2-3.46c-.09-.16-.26-.25-.44-.25-.06 0-.12.01-.17.03l-2.49 1c-.52-.4-1.08-.73-1.69-.98l-.38-2.65C14.46 2.18 14.25 2 14 2h-4c-.25 0-.46.18-.49.42l-.38 2.65c-.61.25-1.17.59-1.69.98l-2.49-1c-.06-.02-.12-.03-.18-.03-.17 0-.34.09-.43.25l-2 3.46c-.13.22-.07.49.12.64l2.11 1.65c-.04.32-.07.65-.07.98 0 .33.03.66.07.98l-2.11 1.65c-.19.15-.24.42-.12.64l2 3.46c.09.16.26.25.44.25.06 0 .12-.01.17-.03l2.49-1c.52.4 1.08.73 1.69.98l.38 2.65c.03.24.24.42.49.42h4c.25 0 .46-.18.49-.42l.38-2.65c.61-.25 1.17-.59 1.69-.98l2.49 1c.06.02.12.03.18.03.17 0 .34-.09.43-.25l2-3.46c.12-.22.07-.49-.12-.64l-2.11-1.65zm-1.98-1.71c.04.31.05.52.05.73 0 .21-.02.43-.05.73l-.14 1.13.89.7 1.08.84-.7 1.21-1.27-.51-1.04-.42-.9.68c-.43.32-.84.56-1.25.73l-1.06.43-.16 1.13-.2 1.35h-1.4l-.19-1.35-.16-1.13-1.06-.43c-.43-.18-.83-.41-1.23-.71l-.91-.7-1.06.43-1.27.51-.7-1.21 1.08-.84.89-.7-.14-1.13c-.03-.31-.05-.54-.05-.74s.02-.43.05-.73l.14-1.13-.89-.7-1.08-.84.7-1.21 1.27.51 1.04.42.9-.68c.43-.32.84-.56 1.25-.73l1.06-.43.16-1.13.2-1.35h1.39l.19 1.35.16 1.13 1.06.43c.43.18.83.41 1.23.71l.91.7 1.06-.43 1.27-.51.7 1.21-1.07.85-.89.7.14 1.13zM12 8c-2.21 0-4 1.79-4 4s1.79 4 4 4 4-1.79 4-4-1.79-4-4-4zm0 6c-1.1 0-2-.9-2-2s.9-2 2-2 2 .9 2 2-.9 2-2 2z"/>
</svg>

After

Width:  |  Height:  |  Size: 1.4 KiB

View 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

View 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

View 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="M636.17-32.59 480.35-188.41l45.82-46.07 77.13 77.37v-137.32H356.93q-28.1 0-46.8-18.55-18.7-18.54-18.7-46.95V-600.3H74.02v-65.27h217.41v-137.32l-77.36 77.37-45.59-45.83 155.59-155.82 156.06 155.82-46.06 45.83-77.14-77.37v442.96h529.29v65.5H669.04v137.32l77.13-77.37L792-188.41 636.17-32.59ZM603.3-419.93V-600.3H416.93v-65.27H603.3q28.37 0 47.06 18.56 18.68 18.55 18.68 46.71v180.37H603.3Z"/>
</svg>

After

Width:  |  Height:  |  Size: 518 B

View 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

View File

@@ -1,457 +0,0 @@
import os
import threading
import time
import warnings
from typing import Any
import numpy as np
import pyqtgraph
import pyqtgraph as pg
from bec_lib.core import BECMessage
from PyQt5.QtCore import pyqtSlot
from PyQt5.QtWidgets import QCheckBox, QTableWidgetItem
from pyqtgraph import mkBrush, mkColor, mkPen
from pyqtgraph.Qt import QtCore, QtWidgets, uic
from pyqtgraph.Qt.QtCore import pyqtSignal
from bec_widgets.bec_dispatcher import bec_dispatcher
from bec_lib.core.redis_connector import MessageObject, RedisConnector
client = bec_dispatcher.client
class BasicPlot(QtWidgets.QWidget):
update_signal = pyqtSignal()
roi_signal = pyqtSignal(tuple)
def __init__(self, name="", y_value_list=["gauss_bpm"]) -> None:
"""
Basic plot widget for displaying scan data.
Args:
name (str, optional): Name of the plot. Defaults to "".
y_value_list (list, optional): List of signals to be plotted. Defaults to ["gauss_bpm"].
"""
super(BasicPlot, self).__init__()
# Set style for pyqtgraph plots
pg.setConfigOption("background", "w")
pg.setConfigOption("foreground", "k")
current_path = os.path.dirname(__file__)
uic.loadUi(os.path.join(current_path, "basic_plot.ui"), self)
# Set splitter distribution of widgets
self.splitter.setSizes([3, 1])
self._idle_time = 100
self.title = ""
self.label_bottom = ""
self.label_left = ""
self.producer = RedisConnector(["localhost:6379"]).producer()
self.scan_motors = []
self.y_value_list = y_value_list
self.previous_y_value_list = None
self.plotter_data_x = []
self.plotter_data_y = []
self.curves = []
self.pens = []
self.brushs = []
self.plotter_scan_id = None
# TODO to be moved to utils function
plotstyles = {
"symbol": "o",
"symbolSize": 10,
}
color_list = BasicPlot.golden_angle_color(colormap="CET-R2", num=len(self.y_value_list))
# setup plots - GraphicsLayoutWidget
# LabelItem
self.label = pg.LabelItem(justify="center")
self.glw.addItem(self.label)
self.label.setText("ROI region")
# PlotItem - main window
self.glw.nextRow()
self.plot = pg.PlotItem()
self.plot.setLogMode(True, True)
self.glw.addItem(self.plot)
self.plot.addLegend()
# ImageItem - 2D view #TODO add 2D plot for ROI and 1D plot for mouse click
self.glw.nextRow()
self.plot_roi = pg.PlotItem()
self.img = pg.ImageItem()
self.glw.addItem(self.plot_roi)
self.plot_roi.addItem(self.img)
# ROI selector - so far from [-1,1] #TODO update to scale with xrange
self.roi_selector = pg.LinearRegionItem([-1, 1])
for ii, y_value in enumerate(self.y_value_list):
pen = mkPen(color=color_list[ii], width=2, style=QtCore.Qt.DashLine)
brush = mkBrush(color=color_list[ii])
curve = pg.PlotDataItem(
**plotstyles, symbolBrush=brush, pen=pen, skipFiniteCheck=True, name=y_value
)
self.plot.addItem(curve)
self.curves.append(curve)
self.pens.append(pen)
self.brushs.append(brush)
self.add_crosshair(self.plot)
self.add_crosshair(self.plot_roi)
self.crosshair_v = pg.InfiniteLine(angle=90, movable=False)
self.crosshair_h = pg.InfiniteLine(angle=0, movable=False)
#
# for plot in (self.plot_roi, self.plot):
# plot.addItem(self.crosshair_v, ignoreBounds=True)
# plot.addItem(self.crosshair_h, ignoreBounds=True)
# self.plot.addItem(self.crosshair_v, ignoreBounds=True)
# self.plot.addItem(self.crosshair_h, ignoreBounds=True)
# self.plot_roi.addItem(self.crosshair_v, ignoreBounds=True)
# self.plot_roi.addItem(self.crosshair_h, ignoreBounds=True)
# Add textItems
self.add_text_items()
# Manage signals
self.proxy = pg.SignalProxy(
self.plot.scene().sigMouseMoved, rateLimit=60, slot=self.mouse_moved
)
self.proxy_update = pg.SignalProxy(self.update_signal, rateLimit=25, slot=self.update)
self.roi_selector.sigRegionChangeFinished.connect(self.get_roi_region)
# Debug functions
self.pushButton_debug.clicked.connect(self.generate_2D_data_update)
# self.generate_2D_data()
self._current_proj = None
self._current_metadata_ep = "px_stream/projection_{}/metadata"
self.data_retriever = threading.Thread(target=self.on_projection, daemon=True)
self.data_retriever.start()
def debug(self):
"""
Debug button just for quick testing
"""
def generate_2D_data(self):
data = np.random.normal(size=(1, 100))
self.img.setImage(data)
def generate_2D_data_update(self):
data = np.random.normal(size=(200, 300))
self.img.setImage(data, levels=(0.2, 0.5))
def add_crosshair(self, plot):
crosshair_v = pg.InfiniteLine(angle=90, movable=False)
crosshair_h = pg.InfiniteLine(angle=0, movable=False)
plot.addItem(crosshair_v)
plot.addItem(crosshair_h)
def get_roi_region(self):
"""For testing purpose now, get roi region and print it to self.label as tuple"""
region = self.roi_selector.getRegion()
self.label.setText(f"x = {(10**region[0]):.4f}, y ={(10**region[1]):.4f}")
return_dict = {
"horiz_roi": [
np.where(self.plotter_data_x[0] > 10 ** region[0])[0][0],
np.where(self.plotter_data_x[0] < 10 ** region[1])[0][-1],
]
}
msg = BECMessage.DeviceMessage(signals=return_dict).dumps()
self.producer.set_and_publish("px_stream/gui_event", msg=msg)
self.roi_signal.emit(region)
def add_text_items(self): # TODO probably can be removed
"""Add text items to the plot"""
# self.mouse_box_data.setText("Mouse cursor")
# TODO Via StyleSheet, one may set the color of the full QLabel
# self.mouse_box_data.setStyleSheet(f"QLabel {{color : rgba{self.pens[0].color().getRgb()}}}")
def mouse_moved(self, event: tuple) -> None:
"""
Update the mouse table with the current mouse position and the corresponding data.
Args:
event (tuple): Mouse event containing the position of the mouse cursor.
The position is stored in first entry as horizontal, vertical pixel.
"""
pos = event[0]
if not self.plot.sceneBoundingRect().contains(pos):
return
mousePoint = self.plot.vb.mapSceneToView(pos)
self.crosshair_v.setPos(mousePoint.x())
self.crosshair_h.setPos(mousePoint.y())
if not self.plotter_data_x:
return
closest_point = self.closest_x_y_value(
mousePoint.x(), self.plotter_data_x[0], self.plotter_data_y[0]
)
# self.precision = 3
# ii = 0
# y_value = self.y_value_list[ii]
# x_data = f"{10**closest_point[0]:.{self.precision}f}"
# y_data = f"{10**closest_point[1]:.{self.precision}f}"
#
# # Write coordinate to QTable
# self.mouse_table.setItem(ii, 1, QTableWidgetItem(str(y_value)))
# self.mouse_table.setItem(ii, 2, QTableWidgetItem(str(x_data)))
# self.mouse_table.setItem(ii, 3, QTableWidgetItem(str(y_data)))
#
# self.mouse_table.resizeColumnsToContents()
def closest_x_y_value(self, input_value, list_x, list_y) -> tuple:
"""
Find the closest x and y value to the input value.
Args:
input_value (float): Input value
list_x (list): List of x values
list_y (list): List of y values
Returns:
tuple: Closest x and y value
"""
arr = np.asarray(list_x)
i = (np.abs(arr - input_value)).argmin()
return list_x[i], list_y[i]
def update(self):
"""Update the plot with the new data."""
# check if roi selector is in the plot
if self.roi_selector not in self.plot.items:
self.plot.addItem(self.roi_selector)
# check if QTable was initialised and if list of devices was changed
if self.y_value_list != self.previous_y_value_list:
self.setup_cursor_table()
self.previous_y_value_list = self.y_value_list.copy() if self.y_value_list else None
self.curves[0].setData(self.plotter_data_x[0], self.plotter_data_y[0])
# if len(self.plotter_data_x[0]) <= 1:
# return
# self.plot.setLabel("bottom", self.label_bottom)
# self.plot.setLabel("left", self.label_left)
# for ii in range(len(self.y_value_list)):
# self.curves[0].setData(self.plotter_data_x[0], self.plotter_data_y[0])
@pyqtSlot(dict, dict)
def on_scan_segment(self, data: dict, metadata: dict) -> None:
"""Update function that is called during the scan callback. To avoid
too many renderings, the GUI is only processing events every <_idle_time> ms.
Args:
data (dict): Dictionary containing a new scan segment
metadata (dict): Scan metadata
"""
if metadata["scanID"] != self.plotter_scan_id:
self.plotter_scan_id = metadata["scanID"]
self._reset_plot_data()
self.title = f"Scan {metadata['scan_number']}"
self.scan_motors = scan_motors = metadata.get("scan_report_devices")
# client = BECClient()
remove_y_value_index = [
index
for index, y_value in enumerate(self.y_value_list)
if y_value not in client.device_manager.devices
]
if remove_y_value_index:
for ii in sorted(remove_y_value_index, reverse=True):
# TODO Use bec warning message??? to be discussed with Klaus
warnings.warn(
f"Warning: no matching signal for {self.y_value_list[ii]} found in list of devices. Removing from plot."
)
self.remove_curve_by_name(self.plot, self.y_value_list[ii])
self.y_value_list.pop(ii)
self.precision = client.device_manager.devices[scan_motors[0]]._info["describe"][
scan_motors[0]
]["precision"]
# TODO after update of bec_lib, this will be new way to access data
# self.precision = client.device_manager.devices[scan_motors[0]].precision
x = data["data"][scan_motors[0]][scan_motors[0]]["value"]
self.plotter_data_x.append(x)
for ii, y_value in enumerate(self.y_value_list):
y = data["data"][y_value][y_value]["value"]
self.plotter_data_y[ii].append(y)
self.label_bottom = scan_motors[0]
self.label_left = f"{', '.join(self.y_value_list)}"
# print(f'metadata scan N{metadata["scan_number"]}') #TODO put as label on top of plot
# print(f'Data point = {data["point_id"]}') #TODO can be used for progress bar
if len(self.plotter_data_x) <= 1:
return
self.update_signal.emit()
def _reset_plot_data(self):
"""Reset the plot data."""
self.plotter_data_x = []
self.plotter_data_y = []
for ii in range(len(self.y_value_list)):
self.curves[ii].setData([], [])
self.plotter_data_y.append([])
def setup_cursor_table(self):
"""QTable formatting according to N of devices displayed in plot."""
# Init number of rows in table according to n of devices
self.mouse_table.setRowCount(len(self.y_value_list))
for ii, y_value in enumerate(self.y_value_list):
checkbox = QCheckBox()
checkbox.setChecked(True)
# TODO just for testing, will be replaced by removing/adding curve
checkbox.stateChanged.connect(lambda: print("status Changed"))
# checkbox.stateChanged.connect(lambda: self.remove_curve_by_name(plot=self.plot, checkbox=checkbox, name=y_value))
self.mouse_table.setCellWidget(ii, 0, checkbox)
self.mouse_table.setItem(ii, 1, QTableWidgetItem(str(y_value)))
self.mouse_table.resizeColumnsToContents()
@staticmethod
def remove_curve_by_name(plot: pyqtgraph.PlotItem, name: str) -> None:
# def remove_curve_by_name(plot: pyqtgraph.PlotItem, checkbox: QtWidgets.QCheckBox, name: str) -> None:
"""Removes a curve from the given plot by the specified name.
Args:
plot (pyqtgraph.PlotItem): The plot from which to remove the curve.
name (str): The name of the curve to remove.
"""
# if checkbox.isChecked():
for item in plot.items:
if isinstance(item, pg.PlotDataItem) and getattr(item, "opts", {}).get("name") == name:
plot.removeItem(item)
return
# else:
# return
@staticmethod
def golden_ratio(num: int) -> list:
"""Calculate the golden ratio for a given number of angles.
Args:
num (int): Number of angles
"""
phi = 2 * np.pi * ((1 + np.sqrt(5)) / 2)
angles = []
for ii in range(num):
x = np.cos(ii * phi)
y = np.sin(ii * phi)
angle = np.arctan2(y, x)
angles.append(angle)
return angles
@staticmethod
def golden_angle_color(colormap: str, num: int) -> list:
"""
Extract num colors for from the specified colormap following golden angle distribution.
Args:
colormap (str): Name of the colormap
num (int): Number of requested colors
Returns:
list: List of colors with length <num>
Raises:
ValueError: If the number of requested colors is greater than the number of colors in the colormap.
"""
cmap = pg.colormap.get(colormap)
cmap_colors = cmap.color
if num > len(cmap_colors):
raise ValueError(
f"Number of colors requested ({num}) is greater than the number of colors in the colormap ({len(cmap_colors)})"
)
angles = BasicPlot.golden_ratio(len(cmap_colors))
color_selection = np.round(np.interp(angles, (-np.pi, np.pi), (0, len(cmap_colors))))
colors = [
mkColor(tuple((cmap_colors[int(ii)] * 255).astype(int))) for ii in color_selection[:num]
]
return colors
def on_projection(self):
while True:
if self._current_proj is None:
time.sleep(0.1)
continue
endpoint = f"px_stream/projection_{self._current_proj}/data"
msgs = client.producer.lrange(topic=endpoint, start=-1, end=-1)
data = [BECMessage.DeviceMessage.loads(msg) for msg in msgs]
if not data:
continue
with np.errstate(divide="ignore", invalid="ignore"):
self.plotter_data_y = [
np.sum(
np.sum(data[-1].content["signals"]["data"] * self._current_norm, axis=1)
/ np.sum(self._current_norm, axis=0),
axis=0,
).squeeze()
]
self.update_signal.emit()
@pyqtSlot(dict, dict)
def on_dap_update(self, data: dict, metadata: dict):
self.img.setImage(data["z"].T)
# time.sleep(0,1)
@pyqtSlot(dict)
def new_proj(self, data):
proj_nr = data["proj_nr"]
endpoint = f"px_stream/projection_{proj_nr}/metadata"
msg_raw = client.producer.get(topic=endpoint)
msg = BECMessage.DeviceMessage.loads(msg_raw)
self._current_q = msg.content["signals"]["q"]
self._current_norm = msg.content["signals"]["norm_sum"]
self._current_metadata = msg.content["signals"]["metadata"]
self.plotter_data_x = [self._current_q]
self._current_proj = proj_nr
if __name__ == "__main__":
import argparse
from bec_widgets import ctrl_c
from bec_widgets.bec_dispatcher import bec_dispatcher
parser = argparse.ArgumentParser()
parser.add_argument(
"--signals",
help="specify recorded signals",
nargs="+",
default=["gauss_bpm"],
)
# default = ["gauss_bpm", "bpm4i", "bpm5i", "bpm6i", "xert"],
value = parser.parse_args()
print(f"Plotting signals for: {', '.join(value.signals)}")
client = bec_dispatcher.client
# client.start()
app = QtWidgets.QApplication([])
ctrl_c.setup(app)
plot = BasicPlot(y_value_list=value.signals)
# bec_dispatcher.connect(plot)
bec_dispatcher.connect_proj_id(plot.new_proj)
bec_dispatcher.connect_dap_slot(plot.on_dap_update, "px_dap_worker")
plot.show()
# client.callbacks.register("scan_segment", plot, sync=False)
app.exec_()

View File

@@ -1,77 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>Form</class>
<widget class="QWidget" name="Form">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>845</width>
<height>635</height>
</rect>
</property>
<property name="windowTitle">
<string>Line Plot</string>
</property>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QSplitter" name="splitter">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="opaqueResize">
<bool>false</bool>
</property>
<widget class="QWidget" name="verticalLayoutWidget">
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QPushButton" name="pushButton_debug">
<property name="text">
<string>Debug</string>
</property>
</widget>
</item>
<item>
<widget class="GraphicsLayoutWidget" name="glw"/>
</item>
</layout>
</widget>
<widget class="QTableWidget" name="mouse_table">
<property name="textElideMode">
<enum>Qt::ElideMiddle</enum>
</property>
<column>
<property name="text">
<string>Display</string>
</property>
</column>
<column>
<property name="text">
<string>Device</string>
</property>
</column>
<column>
<property name="text">
<string>X</string>
</property>
</column>
<column>
<property name="text">
<string>Y</string>
</property>
</column>
</widget>
</widget>
</item>
</layout>
</widget>
<customwidgets>
<customwidget>
<class>GraphicsLayoutWidget</class>
<extends>QGraphicsView</extends>
<header>pyqtgraph.h</header>
</customwidget>
</customwidgets>
<resources/>
<connections/>
</ui>

View File

@@ -1,237 +0,0 @@
import argparse
import itertools
import os
from dataclasses import dataclass
from threading import RLock
from bec_lib import BECClient
from bec_lib.core import BECMessage, MessageEndpoints, ServiceConfig
from bec_lib.core.redis_connector import RedisConsumerThreaded
from PyQt5.QtCore import QObject, pyqtSignal
@dataclass
class _BECDap:
"""Utility class to keep track of slots associated with a particular dap redis consumer"""
consumer: RedisConsumerThreaded
slots = set()
# Adding a new pyqt signal requres a class factory, as they must be part of the class definition
# and cannot be dynamically added as class attributes after the class has been defined.
_signal_class_factory = (
type(f"Signal{i}", (QObject,), dict(signal=pyqtSignal(dict, dict))) for i in itertools.count()
)
@dataclass
class _Connection:
"""Utility class to keep track of slots connected to a particular redis consumer"""
consumer: RedisConsumerThreaded
slots = set()
# keep a reference to a new signal class, so it is not gc'ed
_signal_container = next(_signal_class_factory)()
def __post_init__(self):
self.signal = self._signal_container.signal
class _BECDispatcher(QObject):
new_scan = pyqtSignal(dict, dict)
scan_segment = pyqtSignal(dict, dict)
new_dap_data = pyqtSignal(dict, dict)
new_projection_id = pyqtSignal(dict)
new_projection_data = pyqtSignal(dict)
def __init__(self, bec_config=None):
super().__init__()
self.client = BECClient()
# TODO: this is a workaround for now to provide service config within qtdesigner, but is
# it possible to provide config via a cli arg?
if bec_config is None and os.path.isfile("bec_config.yaml"):
bec_config = "bec_config.yaml"
self.client.initialize(config=ServiceConfig(config_path=bec_config))
self._slot_signal_map = {
"on_scan_segment": self.scan_segment,
"on_new_scan": self.new_scan,
}
self._daps = {}
self._connections = {}
self._scan_id = None
scan_lock = RLock()
# self.new_projection_id.connect(self.new_projection_data)
def _scan_segment_cb(msg):
msg = BECMessage.ScanMessage.loads(msg.value)[0]
with scan_lock:
# TODO: use ScanStatusMessage instead?
scan_id = msg.content["scanID"]
if self._scan_id != scan_id:
self._scan_id = scan_id
self.new_scan.emit(msg.content, msg.metadata)
self.scan_segment.emit(msg.content, msg.metadata)
scan_segment_topic = MessageEndpoints.scan_segment()
self._scan_segment_thread = self.client.connector.consumer(
topics=scan_segment_topic,
cb=_scan_segment_cb,
)
self._scan_segment_thread.start()
def connect(self, widget):
for slot_name, signal in self._slot_signal_map.items():
slot = getattr(widget, slot_name, None)
if callable(slot):
signal.connect(slot)
def connect_slot(self, slot, topic):
# create new connection for topic if it doesn't exist
if topic not in self._connections:
def cb(msg):
msg = BECMessage.MessageReader.loads(msg.value)
if not isinstance(msg, list):
msg = [msg]
for msg_i in msg:
self._connections[topic].signal.emit(msg_i.content, msg_i.metadata)
consumer = self.client.connector.consumer(topics=topic, cb=cb)
consumer.start()
self._connections[topic] = _Connection(consumer)
# connect slot if it's not connected
if slot not in self._connections[topic].slots:
self._connections[topic].signal.connect(slot)
self._connections[topic].slots.add(slot)
def disconnect_slot(self, slot, topic):
if topic not in self._connections:
return
if slot not in self._connections[topic].slots:
return
self._connections[topic].signal.disconnect(slot)
self._connections[topic].slots.remove(slot)
if not self._connections[topic].slots:
# shutdown consumer if there are no more connected slots
self._connections[topic].consumer.shutdown()
del self._connections[topic]
def connect_dap_slot(self, slot, dap_names):
if not isinstance(dap_names, list):
dap_names = [dap_names]
for dap_name in dap_names:
if dap_name not in self._daps: # create a new consumer and connect slot
self.add_new_dap_connection(slot, dap_name)
else:
# connect slot if it's not yet connected
if slot not in self._daps[dap_name].slots:
self.new_dap_data.connect(slot)
self._daps[dap_name].slots.add(slot)
def add_new_dap_connection(self, slot, dap_name):
def _dap_cb(msg):
msg = BECMessage.ProcessedDataMessage.loads(msg.value)
if not isinstance(msg, list):
msg = [msg]
for i in msg:
self.new_dap_data.emit(i.content["data"], i.metadata)
dap_ep = MessageEndpoints.processed_data(dap_name)
consumer = self.client.connector.consumer(topics=dap_ep, cb=_dap_cb)
consumer.start()
self.new_dap_data.connect(slot)
self._daps[dap_name] = _BECDap(consumer)
self._daps[dap_name].slots.add(slot)
def disconnect_dap_slot(self, slot, dap_name):
if dap_name not in self._daps:
return
if slot not in self._daps[dap_name].slots:
return
self.new_dap_data.disconnect(slot)
self._daps[dap_name].slots.remove(slot)
if not self._daps[dap_name].slots:
# shutdown consumer if there are no more connected slots
self._daps[dap_name].consumer.shutdown()
del self._daps[dap_name]
# def connect_proj_data(self, slot):
# keys = self.client.producer.keys("px_stream/projection_*")
# keys = keys or []
#
# def _dap_cb(msg):
# msg = BECMessage.DeviceMessage.loads(msg.value)
# self.new_projection_data.emit(msg.content["data"])
#
# proj_numbers = set(key.decode().split("px_stream/projection_")[1].split("/")[0] for key in keys)
# last_proj_id = sorted(proj_numbers)[-1]
# dap_ep = MessageEndpoints.processed_data(f"px_stream/projection_{last_proj_id}/")
#
# consumer = self.client.connector.consumer(topics=dap_ep, cb=_dap_cb)
# consumer.start()
#
# self.new_projection_data.connect(slot)
def connect_proj_id(self, slot):
def _dap_cb(msg):
msg = BECMessage.DeviceMessage.loads(msg.value)
self.new_projection_id.emit(msg.content["signals"])
dap_ep = "px_stream/proj_nr"
consumer = self.client.connector.consumer(topics=dap_ep, cb=_dap_cb)
consumer.start()
self.new_projection_id.connect(slot)
def connect_proj_data(self, slot: object, data_ep: str) -> object:
def _dap_cb(msg):
msg = BECMessage.DeviceMessage.loads(msg.value)
self.new_projection_data.emit(msg.content["signals"])
consumer = self.client.connector.consumer(topics=data_ep, cb=_dap_cb)
consumer.start()
self._daps[data_ep] = _BECDap(consumer)
self._daps[data_ep].slots.add(slot)
self.new_projection_data.connect(slot)
def disconnect_proj_data(self, slot, data_ep):
if data_ep not in self._daps:
return
if slot not in self._daps[data_ep].slots:
return
self.new_projection_data.disconnect(slot)
self._daps[data_ep].slots.remove(slot)
if not self._daps[data_ep].slots:
# shutdown consumer if there are no more connected slots
self._daps[data_ep].consumer.shutdown()
del self._daps[data_ep]
parser = argparse.ArgumentParser()
parser.add_argument("--bec-config", default=None)
args, _ = parser.parse_known_args()
bec_dispatcher = _BECDispatcher(args.bec_config)

View File

@@ -0,0 +1 @@
from .client import *

View File

@@ -0,0 +1,153 @@
from __future__ import annotations
from typing import TYPE_CHECKING
from pydantic import BaseModel
if TYPE_CHECKING:
from .client import BECDockArea, BECFigure
class ScanInfo(BaseModel):
scan_id: str
scan_number: int
scan_name: str
scan_report_devices: list
monitored_devices: list
status: str
model_config: dict = {"validate_assignment": True}
class AutoUpdates:
create_default_dock: bool = False
enabled: bool = False
dock_name: str = None
def __init__(self, gui: BECDockArea):
self.gui = gui
def start_default_dock(self):
"""
Create a default dock for the auto updates.
"""
dock = self.gui.add_dock("default_figure")
dock.add_widget("BECFigure")
self.dock_name = "default_figure"
@staticmethod
def get_scan_info(msg) -> ScanInfo:
"""
Update the script with the given data.
"""
info = msg.info
status = msg.status
scan_id = msg.scan_id
scan_number = info.get("scan_number", 0)
scan_name = info.get("scan_name", "Unknown")
scan_report_devices = info.get("scan_report_devices", [])
monitored_devices = info.get("readout_priority", {}).get("monitored", [])
monitored_devices = [dev for dev in monitored_devices if dev not in scan_report_devices]
return ScanInfo(
scan_id=scan_id,
scan_number=scan_number,
scan_name=scan_name,
scan_report_devices=scan_report_devices,
monitored_devices=monitored_devices,
status=status,
)
def get_default_figure(self) -> BECFigure | None:
"""
Get the default figure from the GUI.
"""
dock = self.gui.panels.get(self.dock_name, [])
if not dock:
return None
widgets = dock.widget_list
if not widgets:
return None
return widgets[0]
def run(self, msg):
"""
Run the update function if enabled.
"""
if not self.enabled:
return
if msg.status != "open":
return
info = self.get_scan_info(msg)
self.handler(info)
@staticmethod
def get_selected_device(monitored_devices, selected_device):
"""
Get the selected device for the plot. If no device is selected, the first
device in the monitored devices list is selected.
"""
if selected_device:
return selected_device
if len(monitored_devices) > 0:
sel_device = monitored_devices[0]
return sel_device
return None
def handler(self, info: ScanInfo) -> None:
"""
Default update function.
"""
if info.scan_name == "line_scan" and info.scan_report_devices:
self.simple_line_scan(info)
return
if info.scan_name == "grid_scan" and info.scan_report_devices:
self.simple_grid_scan(info)
return
if info.scan_report_devices:
self.best_effort(info)
return
def simple_line_scan(self, info: ScanInfo) -> None:
"""
Simple line scan.
"""
fig = self.get_default_figure()
if not fig:
return
dev_x = info.scan_report_devices[0]
dev_y = self.get_selected_device(info.monitored_devices, self.gui.selected_device)
if not dev_y:
return
fig.clear_all()
plt = fig.plot(x_name=dev_x, y_name=dev_y, label=f"Scan {info.scan_number} - {dev_y}")
plt.set(title=f"Scan {info.scan_number}", x_label=dev_x, y_label=dev_y)
def simple_grid_scan(self, info: ScanInfo) -> None:
"""
Simple grid scan.
"""
fig = self.get_default_figure()
if not fig:
return
dev_x = info.scan_report_devices[0]
dev_y = info.scan_report_devices[1]
dev_z = self.get_selected_device(info.monitored_devices, self.gui.selected_device)
fig.clear_all()
plt = fig.plot(
x_name=dev_x, y_name=dev_y, z_name=dev_z, label=f"Scan {info.scan_number} - {dev_z}"
)
plt.set(title=f"Scan {info.scan_number}", x_label=dev_x, y_label=dev_y)
def best_effort(self, info: ScanInfo) -> None:
"""
Best effort scan.
"""
fig = self.get_default_figure()
if not fig:
return
dev_x = info.scan_report_devices[0]
dev_y = self.get_selected_device(info.monitored_devices, self.gui.selected_device)
if not dev_y:
return
fig.clear_all()
plt = fig.plot(x_name=dev_x, y_name=dev_y, label=f"Scan {info.scan_number} - {dev_y}")
plt.set(title=f"Scan {info.scan_number}", x_label=dev_x, y_label=dev_y)

2745
bec_widgets/cli/client.py Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,362 @@
from __future__ import annotations
import importlib
import importlib.metadata as imd
import json
import os
import select
import subprocess
import sys
import threading
import time
import uuid
from functools import wraps
from typing import TYPE_CHECKING
from bec_lib.endpoints import MessageEndpoints
from bec_lib.logger import bec_logger
from bec_lib.utils.import_utils import isinstance_based_on_class_name, lazy_import, lazy_import_from
from qtpy.QtCore import QEventLoop, QSocketNotifier, QTimer
import bec_widgets.cli.client as client
from bec_widgets.cli.auto_updates import AutoUpdates
if TYPE_CHECKING:
from bec_lib.device import DeviceBase
from bec_widgets.cli.client import BECDockArea, BECFigure
from bec_lib.serialization import MsgpackSerialization
messages = lazy_import("bec_lib.messages")
# from bec_lib.connector import MessageObject
MessageObject = lazy_import_from("bec_lib.connector", ("MessageObject",))
BECDispatcher = lazy_import_from("bec_widgets.utils.bec_dispatcher", ("BECDispatcher",))
logger = bec_logger.logger
def rpc_call(func):
"""
A decorator for calling a function on the server.
Args:
func: The function to call.
Returns:
The result of the function call.
"""
@wraps(func)
def wrapper(self, *args, **kwargs):
# we could rely on a strict type check here, but this is more flexible
# moreover, it would anyway crash for objects...
out = []
for arg in args:
if hasattr(arg, "name"):
arg = arg.name
out.append(arg)
args = tuple(out)
for key, val in kwargs.items():
if hasattr(val, "name"):
kwargs[key] = val.name
if not self.gui_is_alive():
raise RuntimeError("GUI is not alive")
return self._run_rpc(func.__name__, *args, **kwargs)
return wrapper
def _get_output(process, logger) -> None:
log_func = {process.stdout: logger.debug, process.stderr: logger.error}
stream_buffer = {process.stdout: [], process.stderr: []}
try:
os.set_blocking(process.stdout.fileno(), False)
os.set_blocking(process.stderr.fileno(), False)
while process.poll() is None:
readylist, _, _ = select.select([process.stdout, process.stderr], [], [], 1)
for stream in (process.stdout, process.stderr):
buf = stream_buffer[stream]
if stream in readylist:
buf.append(stream.read(4096))
output, _, remaining = "".join(buf).rpartition("\n")
if output:
log_func[stream](output)
buf.clear()
buf.append(remaining)
except Exception as e:
print(f"Error reading process output: {str(e)}")
def _start_plot_process(gui_id: str, gui_class: type, config: dict | str, logger=None) -> None:
"""
Start the plot in a new process.
Logger must be a logger object with "debug" and "error" functions,
or it can be left to "None" as default. None means output from the
process will not be captured.
"""
# pylint: disable=subprocess-run-check
command = ["bec-gui-server", "--id", gui_id, "--gui_class", gui_class.__name__]
if config:
if isinstance(config, dict):
config = json.dumps(config)
command.extend(["--config", config])
env_dict = os.environ.copy()
env_dict["PYTHONUNBUFFERED"] = "1"
if logger is None:
stdout_redirect = subprocess.DEVNULL
stderr_redirect = subprocess.DEVNULL
else:
stdout_redirect = subprocess.PIPE
stderr_redirect = subprocess.PIPE
process = subprocess.Popen(
command,
text=True,
start_new_session=True,
stdout=stdout_redirect,
stderr=stderr_redirect,
env=env_dict,
)
if logger is None:
process_output_processing_thread = None
else:
process_output_processing_thread = threading.Thread(
target=_get_output, args=(process, logger)
)
process_output_processing_thread.start()
return process, process_output_processing_thread
class BECGuiClientMixin:
def __init__(self, **kwargs) -> None:
super().__init__(**kwargs)
self._process = None
self._process_output_processing_thread = None
self.auto_updates = self._get_update_script()
self._target_endpoint = MessageEndpoints.scan_status()
self._selected_device = None
def _get_update_script(self) -> AutoUpdates | None:
eps = imd.entry_points(group="bec.widgets.auto_updates")
for ep in eps:
if ep.name == "plugin_widgets_update":
try:
spec = importlib.util.find_spec(ep.module)
# if the module is not found, we skip it
if spec is None:
continue
return ep.load()(gui=self)
except Exception as e:
print(f"Error loading auto update script from plugin: {str(e)}")
return None
@property
def selected_device(self):
"""
Selected device for the plot.
"""
return self._selected_device
@selected_device.setter
def selected_device(self, device: str | DeviceBase):
if isinstance_based_on_class_name(device, "bec_lib.device.DeviceBase"):
self._selected_device = device.name
elif isinstance(device, str):
self._selected_device = device
else:
raise ValueError("Device must be a string or a device object")
def _start_update_script(self) -> None:
self._client.connector.register(
self._target_endpoint, cb=self._handle_msg_update, parent=self
)
@staticmethod
def _handle_msg_update(msg: MessageObject, parent: BECGuiClientMixin) -> None:
if parent.auto_updates is not None:
# pylint: disable=protected-access
parent._update_script_msg_parser(msg.value)
def _update_script_msg_parser(self, msg: messages.BECMessage) -> None:
if isinstance(msg, messages.ScanStatusMessage):
if not self.gui_is_alive():
return
self.auto_updates.run(msg)
def show(self) -> None:
"""
Show the figure.
"""
if self._process is None or self._process.poll() is not None:
self._start_update_script()
self._process, self._process_output_processing_thread = _start_plot_process(
self._gui_id, self.__class__, self._client._service_config.config
)
while not self.gui_is_alive():
print("Waiting for GUI to start...")
time.sleep(1)
def close(self) -> None:
"""
Close the gui window.
"""
if self._process is None:
return
self._client.shutdown()
if self._process:
self._process.terminate()
if self._process_output_processing_thread:
self._process_output_processing_thread.join()
self._process.wait()
self._process = None
class RPCResponseTimeoutError(Exception):
"""Exception raised when an RPC response is not received within the expected time."""
def __init__(self, request_id, timeout):
super().__init__(
f"RPC response not received within {timeout} seconds for request ID {request_id}"
)
class QtRedisMessageWaiter:
def __init__(self, redis_connector, message_to_wait):
self.ev_loop = QEventLoop()
self.response = None
self.connector = redis_connector
self.message_to_wait = message_to_wait
self.pubsub = redis_connector._redis_conn.pubsub()
self.pubsub.subscribe(self.message_to_wait.endpoint)
fd = self.pubsub.connection._sock.fileno()
self.notifier = QSocketNotifier(fd, QSocketNotifier.Read)
self.notifier.activated.connect(self._pubsub_readable)
def _msg_received(self, msg_obj):
self.response = msg_obj.value
self.ev_loop.quit()
def wait(self, timeout=1):
timer = QTimer()
timer.singleShot(timeout * 1000, self.ev_loop.quit)
self.ev_loop.exec_()
timer.stop()
self.notifier.setEnabled(False)
self.pubsub.close()
return self.response
def _pubsub_readable(self, fd):
while True:
msg = self.pubsub.get_message()
if msg:
if msg["type"] == "subscribe":
# get_message buffers, so we may already have the answer
# let's check...
continue
else:
break
else:
return
channel = msg["channel"].decode()
msg = MessageObject(topic=channel, value=MsgpackSerialization.loads(msg["data"]))
self.connector._execute_callback(self._msg_received, msg, {})
class RPCBase:
def __init__(self, gui_id: str = None, config: dict = None, parent=None) -> None:
self._client = BECDispatcher().client
self._config = config if config is not None else {}
self._gui_id = gui_id if gui_id is not None else str(uuid.uuid4())
self._parent = parent
super().__init__()
# print(f"RPCBase: {self._gui_id}")
def __repr__(self):
type_ = type(self)
qualname = type_.__qualname__
return f"<{qualname} object at {hex(id(self))}>"
@property
def _root(self):
"""
Get the root widget. This is the BECFigure widget that holds
the anchor gui_id.
"""
parent = self
# pylint: disable=protected-access
while parent._parent is not None:
parent = parent._parent
return parent
def _run_rpc(self, method, *args, wait_for_rpc_response=True, timeout=3, **kwargs):
"""
Run the RPC call.
Args:
method: The method to call.
args: The arguments to pass to the method.
wait_for_rpc_response: Whether to wait for the RPC response.
kwargs: The keyword arguments to pass to the method.
Returns:
The result of the RPC call.
"""
request_id = str(uuid.uuid4())
rpc_msg = messages.GUIInstructionMessage(
action=method,
parameter={"args": args, "kwargs": kwargs, "gui_id": self._gui_id},
metadata={"request_id": request_id},
)
# pylint: disable=protected-access
receiver = self._root._gui_id
if wait_for_rpc_response:
redis_msg = QtRedisMessageWaiter(
self._client.connector, MessageEndpoints.gui_instruction_response(request_id)
)
self._client.connector.set_and_publish(MessageEndpoints.gui_instructions(receiver), rpc_msg)
if wait_for_rpc_response:
response = redis_msg.wait(timeout)
if response is None:
raise RPCResponseTimeoutError(request_id, timeout)
# get class name
if not response.accepted:
raise ValueError(response.message["error"])
msg_result = response.message.get("result")
return self._create_widget_from_msg_result(msg_result)
def _create_widget_from_msg_result(self, msg_result):
if msg_result is None:
return None
if isinstance(msg_result, list):
return [self._create_widget_from_msg_result(res) for res in msg_result]
if isinstance(msg_result, dict):
if "__rpc__" not in msg_result:
return {
key: self._create_widget_from_msg_result(val) for key, val in msg_result.items()
}
cls = msg_result.pop("widget_class", None)
msg_result.pop("__rpc__", None)
if not cls:
return msg_result
cls = getattr(client, cls)
# print(msg_result)
return cls(parent=self, **msg_result)
return msg_result
def gui_is_alive(self):
"""
Check if the GUI is alive.
"""
heart = self._client.connector.get(MessageEndpoints.gui_heartbeat(self._root._gui_id))
return heart is not None

View File

@@ -0,0 +1,189 @@
# pylint: disable=missing-module-docstring
from __future__ import annotations
import argparse
import inspect
import os
import sys
import black
import isort
from bec_widgets.utils.generate_designer_plugin import DesignerPluginGenerator
from bec_widgets.utils.plugin_utils import BECClassContainer, get_rpc_classes
if sys.version_info >= (3, 11):
from typing import get_overloads
else:
print(
"Python version is less than 3.11, using dummy function for get_overloads. "
"If you want to use the real function 'typing.get_overloads()', please use Python 3.11 or later."
)
def get_overloads(_obj):
"""
Dummy function for Python versions before 3.11.
"""
return []
class ClientGenerator:
def __init__(self):
self.header = """# This file was automatically generated by generate_cli.py\n
import enum
from typing import Literal, Optional, overload
from bec_widgets.cli.client_utils import RPCBase, rpc_call, BECGuiClientMixin
# pylint: skip-file"""
self.content = ""
def generate_client(self, class_container: BECClassContainer):
"""
Generate the client for the published classes.
Args:
class_container: The class container with the classes to generate the client for.
"""
rpc_top_level_classes = class_container.rpc_top_level_classes
rpc_top_level_classes.sort(key=lambda x: x.__name__)
connector_classes = class_container.connector_classes
connector_classes.sort(key=lambda x: x.__name__)
self.write_client_enum(rpc_top_level_classes)
for cls in connector_classes:
self.content += "\n\n"
self.generate_content_for_class(cls)
def write_client_enum(self, published_classes: list[type]):
"""
Write the client enum to the content.
"""
self.content += """
class Widgets(str, enum.Enum):
\"\"\"
Enum for the available widgets.
\"\"\"
"""
for cls in published_classes:
self.content += f'{cls.__name__} = "{cls.__name__}"\n '
def generate_content_for_class(self, cls):
"""
Generate the content for the class.
Args:
cls: The class for which to generate the content.
"""
class_name = cls.__name__
# Generate the content
if cls.__name__ == "BECDockArea":
self.content += f"""
class {class_name}(RPCBase, BECGuiClientMixin):"""
else:
self.content += f"""
class {class_name}(RPCBase):"""
if not cls.USER_ACCESS:
self.content += """...
"""
for method in cls.USER_ACCESS:
obj = getattr(cls, method)
if isinstance(obj, property):
self.content += """
@property
@rpc_call"""
sig = str(inspect.signature(obj.fget))
doc = inspect.getdoc(obj.fget)
else:
sig = str(inspect.signature(obj))
doc = inspect.getdoc(obj)
overloads = get_overloads(obj)
for overload in overloads:
sig_overload = str(inspect.signature(overload))
self.content += f"""
@overload
def {method}{str(sig_overload)}: ...
"""
self.content += """
@rpc_call"""
self.content += f"""
def {method}{str(sig)}:
\"\"\"
{doc}
\"\"\""""
def write(self, file_name: str):
"""
Write the content to a file, automatically formatted with black.
Args:
file_name(str): The name of the file to write to.
"""
# Combine header and content, then format with black
full_content = self.header + "\n" + self.content
try:
formatted_content = black.format_str(full_content, mode=black.FileMode(line_length=100))
except black.NothingChanged:
formatted_content = full_content
isort.Config(
profile="black",
line_length=100,
multi_line_output=3,
include_trailing_comma=True,
known_first_party=["bec_widgets"],
)
formatted_content = isort.code(formatted_content)
with open(file_name, "w", encoding="utf-8") as file:
file.write(formatted_content)
def main():
"""
Main entry point for the script, controlled by command line arguments.
"""
parser = argparse.ArgumentParser(description="Auto-generate the client for RPC widgets")
parser.add_argument("--core", action="store_true", help="Whether to generate the core client")
args = parser.parse_args()
if args.core:
current_path = os.path.dirname(__file__)
client_path = os.path.join(current_path, "client.py")
rpc_classes = get_rpc_classes("bec_widgets")
generator = ClientGenerator()
generator.generate_client(rpc_classes)
generator.write(client_path)
for cls in rpc_classes.plugins:
plugin = DesignerPluginGenerator(cls)
if not hasattr(plugin, "info"):
continue
# if the class directory already has a register, plugin and pyproject file, skip
if os.path.exists(
os.path.join(plugin.info.base_path, f"register_{plugin.info.plugin_name_snake}.py")
):
continue
if os.path.exists(
os.path.join(plugin.info.base_path, f"{plugin.info.plugin_name_snake}_plugin.py")
):
continue
if os.path.exists(
os.path.join(plugin.info.base_path, f"{plugin.info.plugin_name_snake}.pyproject")
):
continue
plugin.run()
if __name__ == "__main__": # pragma: no cover
sys.argv = ["generate_cli.py", "--core"]
main()

View File

@@ -0,0 +1,80 @@
from threading import Lock
from weakref import WeakValueDictionary
from qtpy.QtCore import QObject
class RPCRegister:
"""
A singleton class that keeps track of all the RPC objects registered in the system for CLI usage.
"""
_instance = None
_initialized = False
_lock = Lock()
def __new__(cls, *args, **kwargs):
if cls._instance is None:
cls._instance = super(RPCRegister, cls).__new__(cls)
cls._initialized = False
return cls._instance
def __init__(self):
if self._initialized:
return
self._rpc_register = WeakValueDictionary()
self._initialized = True
def add_rpc(self, rpc: QObject):
"""
Add an RPC object to the register.
Args:
rpc(QObject): The RPC object to be added to the register.
"""
if not hasattr(rpc, "gui_id"):
raise ValueError("RPC object must have a 'gui_id' attribute.")
self._rpc_register[rpc.gui_id] = rpc
def remove_rpc(self, rpc: str):
"""
Remove an RPC object from the register.
Args:
rpc(str): The RPC object to be removed from the register.
"""
if not hasattr(rpc, "gui_id"):
raise ValueError(f"RPC object {rpc} must have a 'gui_id' attribute.")
self._rpc_register.pop(rpc.gui_id, None)
def get_rpc_by_id(self, gui_id: str) -> QObject:
"""
Get an RPC object by its ID.
Args:
gui_id(str): The ID of the RPC object to be retrieved.
Returns:
QObject: The RPC object with the given ID.
"""
rpc_object = self._rpc_register.get(gui_id, None)
return rpc_object
def list_all_connections(self) -> dict:
"""
List all the registered RPC objects.
Returns:
dict: A dictionary containing all the registered RPC objects.
"""
with self._lock:
connections = dict(self._rpc_register)
return connections
@classmethod
def reset_singleton(cls):
"""
Reset the singleton instance.
"""
cls._instance = None
cls._initialized = False

View File

@@ -0,0 +1,53 @@
from bec_widgets.utils import BECConnector
class RPCWidgetHandler:
"""Handler class for creating widgets from RPC messages."""
def __init__(self):
self._widget_classes = None
@property
def widget_classes(self):
"""
Get the available widget classes.
Returns:
dict: The available widget classes.
"""
if self._widget_classes is None:
self.update_available_widgets()
return self._widget_classes
def update_available_widgets(self):
"""
Update the available widgets.
Returns:
None
"""
from bec_widgets.utils.plugin_utils import get_rpc_classes
clss = get_rpc_classes("bec_widgets")
self._widget_classes = {cls.__name__: cls for cls in clss.top_level_classes}
def create_widget(self, widget_type, **kwargs) -> BECConnector:
"""
Create a widget from an RPC message.
Args:
widget_type(str): The type of the widget.
**kwargs: The keyword arguments for the widget.
Returns:
widget(BECConnector): The created widget.
"""
if self._widget_classes is None:
self.update_available_widgets()
widget_class = self._widget_classes.get(widget_type)
if widget_class:
return widget_class(**kwargs)
raise ValueError(f"Unknown widget type: {widget_type}")
widget_handler = RPCWidgetHandler()

234
bec_widgets/cli/server.py Normal file
View File

@@ -0,0 +1,234 @@
from __future__ import annotations
import inspect
import json
import signal
import sys
from contextlib import redirect_stderr, redirect_stdout
from typing import Union
from bec_lib.endpoints import MessageEndpoints
from bec_lib.logger import bec_logger
from bec_lib.service_config import ServiceConfig
from bec_lib.utils.import_utils import lazy_import
from qtpy.QtCore import QTimer
from bec_widgets.cli.rpc_register import RPCRegister
from bec_widgets.utils import BECDispatcher
from bec_widgets.utils.bec_connector import BECConnector
from bec_widgets.utils.bec_dispatcher import QtRedisConnector
from bec_widgets.widgets.dock.dock_area import BECDockArea
from bec_widgets.widgets.figure import BECFigure
messages = lazy_import("bec_lib.messages")
logger = bec_logger.logger
class BECWidgetsCLIServer:
def __init__(
self,
gui_id: str = None,
dispatcher: BECDispatcher = None,
client=None,
config=None,
gui_class: Union[BECFigure, BECDockArea] = BECFigure,
) -> None:
self.dispatcher = BECDispatcher(config=config) if dispatcher is None else dispatcher
self.client = self.dispatcher.client if client is None else client
self.client.start()
self.gui_id = gui_id
self.gui = gui_class(gui_id=self.gui_id)
self.rpc_register = RPCRegister()
self.rpc_register.add_rpc(self.gui)
self.dispatcher.connect_slot(
self.on_rpc_update, MessageEndpoints.gui_instructions(self.gui_id)
)
# Setup QTimer for heartbeat
self._shutdown_event = False
self._heartbeat_timer = QTimer()
self._heartbeat_timer.timeout.connect(self.emit_heartbeat)
self._heartbeat_timer.start(200)
def on_rpc_update(self, msg: dict, metadata: dict):
request_id = metadata.get("request_id")
try:
obj = self.get_object_from_config(msg["parameter"])
method = msg["action"]
args = msg["parameter"].get("args", [])
kwargs = msg["parameter"].get("kwargs", {})
res = self.run_rpc(obj, method, args, kwargs)
except Exception as e:
print(e)
self.send_response(request_id, False, {"error": str(e)})
else:
self.send_response(request_id, True, {"result": res})
def send_response(self, request_id: str, accepted: bool, msg: dict):
self.client.connector.set_and_publish(
MessageEndpoints.gui_instruction_response(request_id),
messages.RequestResponseMessage(accepted=accepted, message=msg),
expire=60,
)
def get_object_from_config(self, config: dict):
gui_id = config.get("gui_id")
obj = self.rpc_register.get_rpc_by_id(gui_id)
if obj is None:
raise ValueError(f"Object with gui_id {gui_id} not found")
return obj
def run_rpc(self, obj, method, args, kwargs):
method_obj = getattr(obj, method)
# check if the method accepts args and kwargs
if not callable(method_obj):
res = method_obj
else:
sig = inspect.signature(method_obj)
if sig.parameters:
res = method_obj(*args, **kwargs)
else:
res = method_obj()
if isinstance(res, list):
res = [self.serialize_object(obj) for obj in res]
elif isinstance(res, dict):
res = {key: self.serialize_object(val) for key, val in res.items()}
else:
res = self.serialize_object(res)
return res
def serialize_object(self, obj):
if isinstance(obj, BECConnector):
return {
"gui_id": obj.gui_id,
"widget_class": obj.__class__.__name__,
"config": obj.config.model_dump(),
"__rpc__": True,
}
return obj
def emit_heartbeat(self):
if self._shutdown_event is False:
self.client.connector.set(
MessageEndpoints.gui_heartbeat(self.gui_id),
messages.StatusMessage(name=self.gui_id, status=1, info={}),
expire=1,
)
def shutdown(self): # TODO not sure if needed when cleanup is done at level of BECConnector
self._shutdown_event = True
self._heartbeat_timer.stop()
self.gui.close()
self.client.shutdown()
class SimpleFileLikeFromLogOutputFunc:
def __init__(self, log_func):
self._log_func = log_func
def write(self, buffer):
for line in buffer.rstrip().splitlines():
line = line.rstrip()
if line:
self._log_func(line)
def flush(self):
return
def close(self):
return
def _start_server(gui_id: str, gui_class: Union[BECFigure, BECDockArea], config: str | None = None):
if config:
try:
config = json.loads(config)
service_config = ServiceConfig(config=config)
except (json.JSONDecodeError, TypeError):
service_config = ServiceConfig(config_path=config)
else:
# if no config is provided, use the default config
service_config = ServiceConfig()
bec_logger.configure(
service_config.redis,
QtRedisConnector,
service_name="BECWidgetsCLIServer",
service_config=service_config.service_config,
)
server = BECWidgetsCLIServer(gui_id=gui_id, config=service_config, gui_class=gui_class)
return server
def main():
import argparse
import os
from qtpy.QtCore import QSize
from qtpy.QtGui import QIcon
from qtpy.QtWidgets import QApplication, QMainWindow
import bec_widgets
parser = argparse.ArgumentParser(description="BEC Widgets CLI Server")
parser.add_argument("--id", type=str, help="The id of the server")
parser.add_argument(
"--gui_class",
type=str,
help="Name of the gui class to be rendered. Possible values: \n- BECFigure\n- BECDockArea",
)
parser.add_argument("--config", type=str, help="Config file or config string.")
args = parser.parse_args()
if args.gui_class == "BECFigure":
gui_class = BECFigure
elif args.gui_class == "BECDockArea":
gui_class = BECDockArea
else:
print(
"Please specify a valid gui_class to run. Use -h for help."
"\n Starting with default gui_class BECFigure."
)
gui_class = BECFigure
with redirect_stdout(SimpleFileLikeFromLogOutputFunc(logger.debug)):
with redirect_stderr(SimpleFileLikeFromLogOutputFunc(logger.error)):
app = QApplication(sys.argv)
app.setApplicationName("BEC Figure")
module_path = os.path.dirname(bec_widgets.__file__)
icon = QIcon()
icon.addFile(
os.path.join(module_path, "assets", "app_icons", "bec_widgets_icon.png"),
size=QSize(48, 48),
)
app.setWindowIcon(icon)
win = QMainWindow()
win.setWindowTitle("BEC Widgets")
server = _start_server(args.id, gui_class, args.config)
gui = server.gui
win.setCentralWidget(gui)
win.resize(800, 600)
win.show()
app.aboutToQuit.connect(server.shutdown)
def sigint_handler(*args):
# display message, for people to let it terminate gracefully
print("Caught SIGINT, exiting")
app.quit()
signal.signal(signal.SIGINT, sigint_handler)
signal.signal(signal.SIGTERM, sigint_handler)
sys.exit(app.exec())
if __name__ == "__main__": # pragma: no cover
main()

View File

@@ -1,129 +0,0 @@
from typing import List
import numpy as np
import pyqtgraph as pg
from pyqtgraph import mkPen
from pyqtgraph.Qt import QtCore, QtWidgets
class ConfigPlotter(pg.GraphicsWidget):
"""
ConfigPlotter is a widget that can be used to plot data from multiple channels
in a grid layout. The layout is specified by a list of dicts, where each dict
specifies the position of the plot in the grid, the channels to plot, and the
type of plot to use. The plot type is specified by the name of the pyqtgraph
item to use. For example, to plot a single channel in a PlotItem, the config
would look like this:
config = [
{
"cols": 1,
"rows": 1,
"y": 0,
"x": 0,
"config": {"channels": ["a"], "label_xy": ["", "a"], "item": "PlotItem"},
}
]
"""
def __init__(self, configs: List[dict], parent=None):
super().__init__(parent)
self.configs = configs
self.plots = {}
self._init_ui()
self._init_plots()
def _init_ui(self):
pg.setConfigOption("background", "w")
pg.setConfigOption("foreground", "k")
# pylint: disable=no-member
self.pen = mkPen(color=(56, 76, 107), width=4, style=QtCore.Qt.SolidLine)
self.view = pg.GraphicsView()
self.view.setAntialiasing(True)
self.view.show()
self.layout = pg.GraphicsLayout()
self.view.setCentralWidget(self.layout)
def _init_plots(self):
for config in self.configs:
channels = config["config"]["channels"]
for channel in channels:
item = pg.PlotItem()
self.layout.addItem(
item,
row=config["y"],
col=config["x"],
rowspan=config["rows"],
colspan=config["cols"],
)
# call the corresponding init function, e.g. init_plotitem
init_func = getattr(self, f"init_{config['config']['item']}")
init_func(channel, config["config"], item)
# self.init_ImageItem(channel, config["config"], item)
def init_PlotItem(self, channel: str, config: dict, item: pg.GraphicsItem):
"""
Initialize a PlotItem
Args:
channel(str): channel to plot
config(dict): config dict for the channel
item(pg.GraphicsItem): PlotItem to plot the data
"""
# pylint: disable=invalid-name
plot_data = item.plot(np.random.rand(100), pen=self.pen)
item.setLabel("left", channel)
self.plots[channel] = {"item": item, "plot_data": plot_data}
def init_ImageItem(self, channel: str, config: dict, item: pg.GraphicsItem):
"""
Initialize an ImageItem
Args:
channel(str): channel to plot
config(dict): config dict for the channel
item(pg.GraphicsItem): ImageItem to plot the data
"""
# pylint: disable=invalid-name
img = pg.ImageItem()
item.addItem(img)
img.setImage(np.random.rand(100, 100))
self.plots[channel] = {"item": item, "plot_data": img}
if __name__ == "__main__":
import sys
CONFIG = [
{
"cols": 1,
"rows": 1,
"y": 0,
"x": 0,
"config": {"channels": ["a"], "label_xy": ["", "a"], "item": "PlotItem"},
},
{
"cols": 1,
"rows": 1,
"y": 1,
"x": 0,
"config": {"channels": ["b"], "label_xy": ["", "b"], "item": "PlotItem"},
},
{
"cols": 1,
"rows": 2,
"y": 0,
"x": 1,
"config": {"channels": ["c"], "label_xy": ["", "c"], "item": "ImageItem"},
},
]
app = QtWidgets.QApplication(sys.argv)
win = ConfigPlotter(CONFIG)
pg.exec()

View File

@@ -1,38 +0,0 @@
import signal
import socket
from PyQt5.QtNetwork import QAbstractSocket
def setup(app):
app.signalwatchdog = SignalWatchdog() # need to store to keep socket pair alive
signal.signal(signal.SIGINT, make_quit_handler(app))
def make_quit_handler(app):
def handler(*args):
print() # make ^C appear on its own line
app.quit()
return handler
class SignalWatchdog(QAbstractSocket):
def __init__(self):
"""
Propagates system signals from Python to QEventLoop
adapted from https://stackoverflow.com/a/65802260/655404
"""
super().__init__(QAbstractSocket.SctpSocket, None)
self.writer, self.reader = writer, reader = socket.socketpair()
writer.setblocking(False)
fd_writer = writer.fileno()
fd_reader = reader.fileno()
signal.set_wakeup_fd(fd_writer) # Python hook
self.setSocketDescriptor(fd_reader) # Qt hook
self.readyRead.connect(
lambda: None
) # dummy function call that lets the Python interpreter run

View File

@@ -1,33 +0,0 @@
import os
import sys
from PyQt5 import QtWidgets, uic
class UI(QtWidgets.QWidget):
def __init__(self, uipath):
super().__init__()
self.ui = uic.loadUi(uipath, self)
_, fname = os.path.split(uipath)
self.setWindowTitle(fname)
self.show()
def main():
"""A basic script to display UI file
Run the script, passing UI file path as an argument, e.g.
$ python bec_widgets/display_ui_file.py bec_widgets/line_plot.ui
"""
app = QtWidgets.QApplication(sys.argv)
UI(sys.argv[1])
sys.exit(app.exec_())
if __name__ == "__main__":
main()

View File

@@ -1,77 +0,0 @@
plot_settings:
background_color: "black"
num_columns: 3
colormap: "plasma"
#TODO add more settings
# - plot size
plot_data:
- plot_name: "BPM plot"
x:
label: 'Motor X'
signals:
- name: "samx"
# entry: "samx"
y:
label: 'BPM'
signals:
- name: "gauss_bpm"
entry: "gauss_bpm"
- name: "gauss_adc1"
entry: "gauss_adc1"
- name: "gauss_adc2"
entry: "gauss_adc2"
- plot_name: "BPM plot 2"
x:
label: 'Motor X'
signals:
- name: "samx"
entry: "samx"
y:
label: 'BPM'
signals:
- name: "gauss_bpm"
entry: "gauss_bpm"
- name: "gauss_adc1"
entry: "gauss_adc1"
- plot_name: "BPM plot 3"
x:
label: 'Motor X'
signals:
- name: "samx"
entry: "samx"
y:
label: 'BPM'
signals:
- name: "gauss_bpm"
entry: "gauss_bpm"
- name: "gauss_adc1"
entry: "gauss_adc1"
- plot_name: "ADC plot"
x:
label: 'Motor Y'
signals:
- name: "samy"
# entry: "samy" # here I also forgot to specify entry
y:
label: 'ADC'
signals:
- name: "gauss_bpm"
entry: "gauss_bpm"
- name: "samx"
# I will not specify entry, because I want to take hint from gauss_adc2
- plot_name: "Multi"
x:
label: 'Motor X'
signals:
- name: "samx"
entry: "samx"
y:
label: 'Multi'
signals:
- name: "gauss_bpm"
entry: "gauss_bpm"
- name: "samx"
entry: ["samx", "samx_setpoint"]
# entry: ["samx","incorect"] #multiple entries for one device

View File

@@ -1,516 +0,0 @@
import os
import numpy as np
import pyqtgraph as pg
from PyQt5.QtCore import pyqtSignal, pyqtSlot
from PyQt5.QtWidgets import QApplication, QWidget, QTableWidgetItem, QTableWidget, QFileDialog
from pyqtgraph import mkBrush, mkColor, mkPen
from pyqtgraph.Qt import QtCore, uic
from bec_lib.core import MessageEndpoints
from bec_widgets.qt_utils import Crosshair, Colors
from pyqtgraph.Qt import QtWidgets
from pyqtgraph import ColorButton
# TODO implement:
# - implement scanID database for visualizing previous scans
class PlotApp(QWidget):
"""
Main class for PlotApp, designed to plot multiple signals in a grid layout
based on a flexible YAML configuration.
Attributes:
update_signal (pyqtSignal): Signal to trigger plot updates.
plot_data (list of dict): List of dictionaries containing plot configurations.
Each dictionary specifies x and y signals, including their
name and entry, for a particular plot.
Args:
plot_settings (dict): Dictionary containing global plot settings such as background color.
plot_data (list of dict): List of dictionaries specifying the signals to plot.
Each dictionary should contain:
- 'x': Dictionary specifying the x-axis settings including
a 'signals' list with 'name' and 'entry' fields.
If there are multiple entries for one device name, they can be passed as a list.
- 'y': Similar to 'x', but for the y-axis.
Example:
[
{
'plot_name': 'Plot 1',
'x': {'label': 'X Label', 'signals': [{'name': 'x1', 'entry': 'x1_entry'}]},
'y': {'label': 'Y Label', 'signals': [{'name': 'y1', 'entry': 'y1_entry'}]}
},
...
]
parent (QWidget, optional): Parent widget.
"""
update_signal = pyqtSignal()
update_dap_signal = pyqtSignal()
def __init__(self, plot_settings: dict, plot_data: list, parent=None):
super(PlotApp, self).__init__(parent)
# YAML config
self.plot_settings = plot_settings
self.plot_data = plot_data
# Setting global plot settings
self.init_plot_background(self.plot_settings["background_color"])
# Loading UI
current_path = os.path.dirname(__file__)
uic.loadUi(os.path.join(current_path, "extreme.ui"), self)
# Nested dictionary to hold x and y data for multiple plots
self.data = {}
self.crosshairs = None
self.plots = None
self.curves_data = None
self.grid_coordinates = None
self.scanID = None
self.user_colors = {} # key: (plot_name, y_name, y_entry), value: color
# Initialize the UI
self.init_ui(self.plot_settings["num_columns"])
self.spinBox_N_columns.setValue(
self.plot_settings["num_columns"]
) # TODO has to be checked if it will not setup more columns than plots
self.spinBox_N_columns.setMaximum(len(self.plot_data))
self.splitter.setSizes([400, 100])
# Buttons
self.pushButton_save.clicked.connect(self.save_settings_to_yaml)
self.pushButton_load.clicked.connect(self.load_settings_from_yaml)
# Connect the update signal to the update plot method
self.proxy_update_plot = pg.SignalProxy(
self.update_signal, rateLimit=25, slot=self.update_plot
)
# Change layout of plots when the number of columns is changed in GUI
self.spinBox_N_columns.valueChanged.connect(lambda x: self.init_ui(x))
def init_plot_background(self, background_color: str) -> None:
"""
Initialize plot settings based on the background color.
Args:
background_color (str): The background color ('white' or 'black').
This method sets the background and foreground colors for pyqtgraph.
If the background is dark ('black'), the foreground will be set to 'white',
and vice versa.
"""
if background_color.lower() == "black":
pg.setConfigOption("background", "k")
pg.setConfigOption("foreground", "w")
elif background_color.lower() == "white":
pg.setConfigOption("background", "w")
pg.setConfigOption("foreground", "k")
else:
print(f"Warning: Unknown background color {background_color}. Using default settings.")
def init_ui(self, num_columns: int = 3) -> None:
"""
Initialize the UI components, create plots and store their grid positions.
Args:
num_columns (int): Number of columns to wrap the layout.
This method initializes a dictionary `self.plots` to store the plot objects
along with their corresponding x and y signal names. It dynamically arranges
the plots in a grid layout based on the given number of columns and dynamically
stretches the last plots to fit the remaining space.
"""
self.glw.clear()
self.plots = {}
self.grid_coordinates = []
num_plots = len(self.plot_data)
# Check if num_columns exceeds the number of plots
if num_columns > num_plots:
num_columns = num_plots
self.plot_settings["num_columns"] = num_columns # Update the settings
print(
f"Warning: num_columns in the YAML file was greater than the number of plots. Resetting num_columns to {num_columns}."
)
num_rows = num_plots // num_columns
last_row_cols = num_plots % num_columns
remaining_space = num_columns - last_row_cols
for i, plot_config in enumerate(self.plot_data):
row, col = i // num_columns, i % num_columns
colspan = 1
if row == num_rows and remaining_space > 0:
if last_row_cols == 1:
colspan = num_columns
else:
colspan = remaining_space // last_row_cols + 1
remaining_space -= colspan - 1
last_row_cols -= 1
plot_name = plot_config.get("plot_name", "")
x_label = plot_config["x"].get("label", "")
y_label = plot_config["y"].get("label", "")
plot = self.glw.addPlot(row=row, col=col, colspan=colspan, title=plot_name)
plot.setLabel("bottom", x_label)
plot.setLabel("left", y_label)
plot.addLegend()
self.plots[plot_name] = plot
self.grid_coordinates.append((row, col))
self.init_curves()
def init_curves(self) -> None:
"""
Initialize curve data and properties, and update table row labels.
This method initializes a nested dictionary `self.curves_data` to store
the curve objects for each x and y signal pair. It also updates the row labels
in `self.tableWidget_crosshair` to include the grid position for each y-value.
"""
self.curves_data = {}
row_labels = []
for idx, plot_config in enumerate(self.plot_data):
plot_name = plot_config.get("plot_name", "")
plot = self.plots[plot_name]
plot.clear()
y_configs = plot_config["y"]["signals"]
colors_ys = Colors.golden_angle_color(
colormap=self.plot_settings["colormap"], num=len(y_configs)
)
curve_list = []
for i, (y_config, color) in enumerate(zip(y_configs, colors_ys)):
y_name = y_config["name"]
y_entries = y_config.get("entry", [y_name])
if not isinstance(y_entries, list):
y_entries = [y_entries]
for y_entry in y_entries:
user_color = self.user_colors.get((plot_name, y_name, y_entry), None)
color_to_use = user_color if user_color else color
pen_curve = mkPen(color=color_to_use, width=2, style=QtCore.Qt.DashLine)
brush_curve = mkBrush(color=color_to_use)
curve_data = pg.PlotDataItem(
symbolSize=5,
symbolBrush=brush_curve,
pen=pen_curve,
skipFiniteCheck=True,
name=f"{y_name} ({y_entry})",
)
curve_list.append((y_name, y_entry, curve_data))
plot.addItem(curve_data)
row_labels.append(f"{y_name} ({y_entry}) - {plot_name}")
# Create a ColorButton and set its color
color_btn = ColorButton()
color_btn.setColor(color_to_use)
color_btn.sigColorChanged.connect(
lambda btn=color_btn, plot=plot_name, yname=y_name, yentry=y_entry, curve=curve_data: self.change_curve_color(
btn, plot, yname, yentry, curve
)
)
# Add the ColorButton as a QWidget to the table
color_widget = QtWidgets.QWidget()
layout = QtWidgets.QHBoxLayout()
layout.addWidget(color_btn)
layout.setContentsMargins(0, 0, 0, 0)
color_widget.setLayout(layout)
row = len(row_labels) - 1 # The row index in the table
self.tableWidget_crosshair.setCellWidget(row, 2, color_widget)
self.curves_data[plot_name] = curve_list
self.tableWidget_crosshair.setRowCount(len(row_labels))
self.tableWidget_crosshair.setVerticalHeaderLabels(row_labels)
self.hook_crosshair()
# def change_curve_color(self, btn, curve):
# """Change the color of a curve."""
# color = btn.color()
# pen_curve = mkPen(color=color, width=2, style=QtCore.Qt.DashLine)
# brush_curve = mkBrush(color=color)
# curve.setPen(pen_curve)
# curve.setSymbolBrush(brush_curve)
def change_curve_color(self, btn, plot_name, y_name, y_entry, curve):
"""
Change the color of a curve and update the corresponding ColorButton.
Args:
btn (ColorButton): The ColorButton that was clicked.
plot_name (str): The name of the plot where the curve belongs.
y_name (str): The name of the y signal.
y_entry (str): The entry of the y signal.
curve (PlotDataItem): The curve to be changed.
"""
color = btn.color()
pen_curve = mkPen(color=color, width=2, style=QtCore.Qt.DashLine)
brush_curve = mkBrush(color=color)
curve.setPen(pen_curve)
curve.setSymbolBrush(brush_curve)
self.user_colors[(plot_name, y_name, y_entry)] = color
def hook_crosshair(self):
"""Attach crosshairs to each plot and connect them to the update_table method."""
self.crosshairs = {}
for plot_name, plot in self.plots.items():
crosshair = Crosshair(plot, precision=3)
crosshair.coordinatesChanged1D.connect(
lambda x, y, plot=plot: self.update_table(
self.tableWidget_crosshair, x, y, column=0, plot=plot
)
)
crosshair.coordinatesClicked1D.connect(
lambda x, y, plot=plot: self.update_table(
self.tableWidget_crosshair, x, y, column=1, plot=plot
)
)
self.crosshairs[plot_name] = crosshair
def update_table(
self, table_widget: QTableWidget, x: float, y_values: list, column: int, plot: pg.PlotItem
) -> None:
"""
Update the table with coordinates based on cursor movements and clicks.
Args:
table_widget (QTableWidget): The table to be updated.
x (float): The x-coordinate from the plot.
y_values (list): The y-coordinates from the plot.
column (int): The column in the table to be updated.
plot (PlotItem): The plot from which the coordinates are taken.
This method calculates the correct row in the table for each y-value
and updates the cell at (row, column) with the new x and y coordinates.
"""
plot_name = [name for name, value in self.plots.items() if value == plot][0]
starting_row = 0
for plot_config in self.plot_data:
if plot_config.get("plot_name", "") == plot_name:
break
for y_config in plot_config.get("y", {}).get("signals", []):
y_entries = y_config.get("entry", [y_config.get("name", "")])
if not isinstance(y_entries, list):
y_entries = [y_entries]
starting_row += len(y_entries)
for i, y in enumerate(y_values):
row = starting_row + i
table_widget.setItem(row, column, QTableWidgetItem(f"({x}, {y})"))
table_widget.resizeColumnsToContents()
def update_plot(self) -> None:
"""Update the plot data based on the stored data dictionary."""
for plot_name, curve_list in self.curves_data.items():
for y_name, y_entry, curve in curve_list:
x_config = next(
(pc["x"] for pc in self.plot_data if pc.get("plot_name") == plot_name), {}
)
x_signal_config = x_config["signals"][0]
x_name = x_signal_config.get("name", "")
x_entry = x_signal_config.get("entry", x_name)
key = (x_name, x_entry, y_name, y_entry)
data_x = self.data.get(key, {}).get("x", [])
data_y = self.data.get(key, {}).get("y", [])
curve.setData(data_x, data_y)
@pyqtSlot(dict, dict)
def on_scan_segment(self, msg, metadata) -> None:
"""
Handle new scan segments and saves data to a dictionary.
Args:
msg (dict): Message received with scan data.
metadata (dict): Metadata of the scan.
"""
current_scanID = msg.get("scanID", None)
if current_scanID is None:
return
if current_scanID != self.scanID:
self.scanID = current_scanID
self.data = {}
self.init_curves()
for plot_config in self.plot_data:
plot_name = plot_config.get("plot_name", "Unnamed Plot")
x_config = plot_config["x"]
x_signal_config = x_config["signals"][0] # Assuming there's at least one signal for x
x_name = x_signal_config.get("name", "")
if not x_name:
raise ValueError(f"Name for x signal must be specified in plot: {plot_name}.")
x_entry_list = x_signal_config.get("entry", [])
if not x_entry_list:
x_entry_list = dev[x_name]._hints if hasattr(dev[x_name], "_hints") else [x_name]
if not isinstance(x_entry_list, list):
x_entry_list = [x_entry_list]
y_configs = plot_config["y"]["signals"]
for x_entry in x_entry_list:
for y_config in y_configs:
y_name = y_config.get("name", "")
if not y_name:
raise ValueError(
f"Name for y signal must be specified in plot: {plot_name}."
)
y_entry_list = y_config.get("entry", [])
if not y_entry_list:
y_entry_list = (
dev[y_name]._hints if hasattr(dev[y_name], "_hints") else [y_name]
)
if not isinstance(y_entry_list, list):
y_entry_list = [y_entry_list]
for y_entry in y_entry_list:
key = (x_name, x_entry, y_name, y_entry)
data_x = msg["data"].get(x_name, {}).get(x_entry, {}).get("value", None)
data_y = msg["data"].get(y_name, {}).get(y_entry, {}).get("value", None)
if data_x is None:
raise ValueError(
f"Incorrect entry '{x_entry}' specified for x in plot: {plot_name}, x name: {x_name}"
)
if data_y is None:
if hasattr(dev[y_name], "_hints"):
raise ValueError(
f"Incorrect entry '{y_entry}' specified for y in plot: {plot_name}, y name: {y_name}"
)
else:
raise ValueError(
f"No hints available for y in plot: {plot_name}, and name '{y_name}' did not work as entry"
)
if data_x is not None:
self.data.setdefault(key, {}).setdefault("x", []).append(data_x)
if data_y is not None:
self.data.setdefault(key, {}).setdefault("y", []).append(data_y)
self.update_signal.emit()
def save_settings_to_yaml(self):
"""Save the current settings to a .yaml file using a file dialog."""
options = QFileDialog.Options()
options |= QFileDialog.DontUseNativeDialog
file_path, _ = QFileDialog.getSaveFileName(
self, "Save Settings", "", "YAML Files (*.yaml);;All Files (*)", options=options
)
if file_path:
try:
if not file_path.endswith(".yaml"):
file_path += ".yaml"
with open(file_path, "w") as file:
yaml.dump(
{"plot_settings": self.plot_settings, "plot_data": self.plot_data}, file
)
print(f"Settings saved to {file_path}")
except Exception as e:
print(f"An error occurred while saving the settings to {file_path}: {e}")
def load_settings_from_yaml(self):
"""Load settings from a .yaml file using a file dialog and update the current settings."""
options = QFileDialog.Options()
options |= QFileDialog.DontUseNativeDialog
file_path, _ = QFileDialog.getOpenFileName(
self, "Load Settings", "", "YAML Files (*.yaml);;All Files (*)", options=options
)
if file_path:
try:
with open(file_path, "r") as file:
config = yaml.safe_load(file)
self.plot_settings = config.get("plot_settings", {})
self.plot_data = config.get("plot_data", {})
# Reinitialize the UI and plots
# TODO implement, change background works only before loading .ui file
# self.init_plot_background(self.plot_settings["background_color"])
self.init_ui(self.plot_settings["num_columns"])
self.init_curves()
print(f"Settings loaded from {file_path}")
except FileNotFoundError:
print(f"The file {file_path} was not found.")
except Exception as e:
print(f"An error occurred while loading the settings from {file_path}: {e}")
if __name__ == "__main__":
import yaml
import argparse
from bec_widgets import ctrl_c
from bec_widgets.bec_dispatcher import bec_dispatcher
parser = argparse.ArgumentParser(description="Plotting App")
parser.add_argument(
"--config", "-c", help="Path to the .yaml configuration file", default="config_example.yaml"
)
args = parser.parse_args()
try:
with open(args.config, "r") as file:
config = yaml.safe_load(file)
plot_settings = config.get("plot_settings", {})
plot_data = config.get("plot_data", {})
except FileNotFoundError:
print(f"The file {args.config} was not found.")
exit(1)
except Exception as e:
print(f"An error occurred while loading the config file: {e}")
exit(1)
# BECclient global variables
client = bec_dispatcher.client
client.start()
dev = client.device_manager.devices
scans = client.scans
queue = client.queue
app = QApplication([])
plotApp = PlotApp(plot_settings=plot_settings, plot_data=plot_data)
# Connecting signals from bec_dispatcher
bec_dispatcher.connect_slot(plotApp.on_scan_segment, MessageEndpoints.scan_segment())
ctrl_c.setup(app)
window = plotApp
window.show()
app.exec_()

View File

@@ -1,115 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>MultiWindow</class>
<widget class="QWidget" name="MultiWindow">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>1248</width>
<height>564</height>
</rect>
</property>
<property name="windowTitle">
<string>MultiWindow</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QSplitter" name="splitter">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<widget class="GraphicsLayoutWidget" name="glw"/>
<widget class="QWidget" name="">
<layout class="QGridLayout" name="gridLayout_2">
<item row="1" column="0">
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item row="2" column="0" colspan="3">
<widget class="QGroupBox" name="groupBox">
<property name="title">
<string>Cursor</string>
</property>
<layout class="QGridLayout" name="gridLayout">
<item row="0" column="0">
<widget class="QTableWidget" name="tableWidget_crosshair">
<column>
<property name="text">
<string>Moved</string>
</property>
</column>
<column>
<property name="text">
<string>Clicked</string>
</property>
</column>
<column>
<property name="text">
<string>Color</string>
</property>
</column>
</widget>
</item>
</layout>
</widget>
</item>
<item row="1" column="1">
<widget class="QLabel" name="label">
<property name="text">
<string>Number of Columns:</string>
</property>
</widget>
</item>
<item row="1" column="2">
<widget class="QSpinBox" name="spinBox_N_columns">
<property name="minimum">
<number>1</number>
</property>
<property name="maximum">
<number>10</number>
</property>
<property name="value">
<number>3</number>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QPushButton" name="pushButton_load">
<property name="text">
<string>Load Config</string>
</property>
</widget>
</item>
<item row="0" column="2">
<widget class="QPushButton" name="pushButton_save">
<property name="text">
<string>Save Config</string>
</property>
</widget>
</item>
</layout>
</widget>
</widget>
</item>
</layout>
</widget>
<customwidgets>
<customwidget>
<class>GraphicsLayoutWidget</class>
<extends>QGraphicsView</extends>
<header>pyqtgraph.h</header>
</customwidget>
</customwidgets>
<resources/>
<connections/>
</ui>

View 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()

View 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>

View 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/")

View File

@@ -0,0 +1,216 @@
import os
import numpy as np
import pyqtgraph as pg
from qtpy.QtCore import QSize
from qtpy.QtGui import QIcon
from qtpy.QtWidgets import (
QApplication,
QGroupBox,
QHBoxLayout,
QSplitter,
QTabWidget,
QVBoxLayout,
QWidget,
)
from bec_widgets.utils import BECDispatcher
from bec_widgets.utils.colors import apply_theme
from bec_widgets.widgets.dock.dock_area import BECDockArea
from bec_widgets.widgets.figure import BECFigure
from bec_widgets.widgets.jupyter_console.jupyter_console import BECJupyterConsole
class JupyterConsoleWindow(QWidget): # pragma: no cover:
"""A widget that contains a Jupyter console linked to BEC Widgets with full API access (contains Qt and pyqtgraph API)."""
def __init__(self, parent=None):
super().__init__(parent)
self._init_ui()
# console push
if self.console.inprocess is True:
self.console.kernel_manager.kernel.shell.push(
{
"np": np,
"pg": pg,
"fig": self.figure,
"dock": self.dock,
"w1": self.w1,
"w2": self.w2,
"w3": self.w3,
"w4": self.w4,
"w5": self.w5,
"w6": self.w6,
"w7": self.w7,
"w8": self.w8,
"w9": self.w9,
"d0": self.d0,
"d1": self.d1,
"d2": self.d2,
"wave": self.wf,
# "bar": self.bar,
# "cm": self.colormap,
"im": self.im,
"mm": self.mm,
}
)
def _init_ui(self):
self.layout = QHBoxLayout(self)
# Horizontal splitter
splitter = QSplitter(self)
self.layout.addWidget(splitter)
tab_widget = QTabWidget(splitter)
first_tab = QWidget()
first_tab_layout = QVBoxLayout(first_tab)
self.dock = BECDockArea(gui_id="dock")
first_tab_layout.addWidget(self.dock)
tab_widget.addTab(first_tab, "Dock Area")
second_tab = QWidget()
second_tab_layout = QVBoxLayout(second_tab)
self.figure = BECFigure(parent=self, gui_id="figure")
second_tab_layout.addWidget(self.figure)
tab_widget.addTab(second_tab, "BEC Figure")
group_box = QGroupBox("Jupyter Console", splitter)
group_box_layout = QVBoxLayout(group_box)
self.console = BECJupyterConsole(inprocess=True)
group_box_layout.addWidget(self.console)
# add stuff to figure
self._init_figure()
# init dock for testing
self._init_dock()
self.setWindowTitle("Jupyter Console Window")
def _init_figure(self):
self.w1 = self.figure.plot(
x_name="samx",
y_name="bpm4i",
# title="Standard Plot with sync device, custom labels - w1",
# x_label="Motor Position",
# y_label="Intensity (A.U.)",
row=0,
col=0,
)
self.w1.set(
title="Standard Plot with sync device, custom labels - w1",
x_label="Motor Position",
y_label="Intensity (A.U.)",
)
self.w2 = self.figure.motor_map("samx", "samy", row=0, col=1)
self.w3 = self.figure.image(
"eiger", color_map="viridis", vrange=(0, 100), title="Eiger Image - w3", row=0, col=2
)
self.w4 = self.figure.plot(
x_name="samx",
y_name="samy",
z_name="bpm4i",
color_map_z="magma",
new=True,
title="2D scatter plot - w4",
row=0,
col=3,
)
self.w5 = self.figure.plot(
y_name="bpm4i",
new=True,
title="Best Effort Plot - w5",
dap="GaussianModel",
row=1,
col=0,
)
self.w6 = self.figure.plot(
x_name="timestamp", y_name="bpm4i", new=True, title="Timestamp Plot - w6", row=1, col=1
)
self.w7 = self.figure.plot(
x_name="index", y_name="bpm4i", new=True, title="Index Plot - w7", row=1, col=2
)
self.w8 = self.figure.plot(
y_name="monitor_async", new=True, title="Async Plot - Best Effort - w8", row=2, col=0
)
self.w9 = self.figure.plot(
x_name="timestamp",
y_name="monitor_async",
new=True,
title="Async Plot - timestamp - w9",
row=2,
col=1,
)
self.w10 = self.figure.plot(
x_name="index",
y_name="monitor_async",
new=True,
title="Async Plot - index - w10",
row=2,
col=2,
)
def _init_dock(self):
self.d0 = self.dock.add_dock(name="dock_0")
self.mm = self.d0.add_widget("BECMotorMapWidget")
self.mm.change_motors("samx", "samy")
self.d1 = self.dock.add_dock(name="dock_1", position="right")
self.im = self.d1.add_widget("BECImageWidget")
self.im.image("eiger")
self.d2 = self.dock.add_dock(name="dock_2", position="bottom")
self.wf = self.d2.add_widget("BECWaveformWidget", row=0, col=0)
self.wf.plot(x_name="samx", y_name="bpm3a")
self.wf.plot(x_name="samx", y_name="bpm4i", dap="GaussianModel")
# self.bar = self.d2.add_widget("RingProgressBar", row=0, col=1)
# self.bar.set_diameter(200)
# self.d3 = self.dock.add_dock(name="dock_3", position="bottom")
# self.colormap = pg.GradientWidget()
# self.d3.add_widget(self.colormap, row=0, col=0)
self.dock.save_state()
def closeEvent(self, event):
"""Override to handle things when main window is closed."""
self.dock.cleanup()
self.dock.close()
self.figure.cleanup()
self.figure.close()
self.console.close()
super().closeEvent(event)
if __name__ == "__main__": # pragma: no cover
import sys
import bec_widgets
module_path = os.path.dirname(bec_widgets.__file__)
app = QApplication(sys.argv)
app.setApplicationName("Jupyter Console")
app.setApplicationDisplayName("Jupyter Console")
apply_theme("dark")
icon = QIcon()
icon.addFile(
os.path.join(module_path, "assets", "app_icons", "terminal_icon.png"), size=QSize(48, 48)
)
app.setWindowIcon(icon)
bec_dispatcher = BECDispatcher()
client = bec_dispatcher.client
client.start()
win = JupyterConsoleWindow()
win.show()
app.aboutToQuit.connect(win.close)
sys.exit(app.exec_())

Some files were not shown because too many files have changed in this diff Show More