bec-widgets 0.50.2__py3-none-any.whl → 0.52.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- bec_widgets/cli/__init__.py +1 -1
- bec_widgets/cli/bec_widgets_icon.png +0 -0
- bec_widgets/cli/client.py +213 -5
- bec_widgets/cli/client_utils.py +16 -5
- bec_widgets/cli/generate_cli.py +6 -3
- bec_widgets/cli/rpc_wigdet_handler.py +26 -0
- bec_widgets/cli/server.py +34 -9
- bec_widgets/examples/jupyter_console/jupyter_console_window.py +54 -2
- bec_widgets/examples/jupyter_console/jupyter_console_window.ui +26 -2
- bec_widgets/examples/jupyter_console/terminal_icon.png +0 -0
- bec_widgets/utils/__init__.py +1 -0
- bec_widgets/utils/bec_connector.py +11 -3
- bec_widgets/utils/layout_manager.py +117 -0
- bec_widgets/utils/plugin_utils.py +40 -0
- bec_widgets/widgets/__init__.py +1 -0
- bec_widgets/widgets/dock/__init__.py +2 -0
- bec_widgets/widgets/dock/dock.py +269 -0
- bec_widgets/widgets/dock/dock_area.py +220 -0
- bec_widgets/widgets/figure/figure.py +33 -16
- bec_widgets/widgets/plots/__init__.py +1 -1
- bec_widgets/widgets/plots/image.py +5 -9
- bec_widgets/widgets/plots/motor_map.py +3 -3
- bec_widgets/widgets/plots/plot_base.py +4 -3
- bec_widgets/widgets/plots/waveform.py +6 -10
- {bec_widgets-0.50.2.dist-info → bec_widgets-0.52.0.dist-info}/METADATA +8 -8
- {bec_widgets-0.50.2.dist-info → bec_widgets-0.52.0.dist-info}/RECORD +37 -28
- tests/end-2-end/conftest.py +19 -4
- tests/end-2-end/test_bec_dock_rpc_e2e.py +145 -0
- tests/end-2-end/test_bec_figure_rpc_e2e.py +17 -17
- tests/end-2-end/test_rpc_register_e2e.py +3 -3
- tests/unit_tests/test_bec_dock.py +114 -0
- tests/unit_tests/test_bec_figure.py +20 -22
- tests/unit_tests/test_generate_cli_client.py +1 -1
- tests/unit_tests/test_waveform1d.py +1 -1
- {bec_widgets-0.50.2.dist-info → bec_widgets-0.52.0.dist-info}/LICENSE +0 -0
- {bec_widgets-0.50.2.dist-info → bec_widgets-0.52.0.dist-info}/WHEEL +0 -0
- {bec_widgets-0.50.2.dist-info → bec_widgets-0.52.0.dist-info}/top_level.txt +0 -0
bec_widgets/cli/__init__.py
CHANGED
@@ -1,2 +1,2 @@
|
|
1
1
|
from .auto_updates import AutoUpdates, ScanInfo
|
2
|
-
from .client import BECFigure
|
2
|
+
from .client import BECDockArea, BECFigure
|
Binary file
|
bec_widgets/cli/client.py
CHANGED
@@ -2,7 +2,7 @@
|
|
2
2
|
|
3
3
|
from typing import Literal, Optional, overload
|
4
4
|
|
5
|
-
from bec_widgets.cli.client_utils import
|
5
|
+
from bec_widgets.cli.client_utils import BECGuiClientMixin, RPCBase, rpc_call
|
6
6
|
|
7
7
|
|
8
8
|
class BECPlotBase(RPCBase):
|
@@ -255,11 +255,11 @@ class BECWaveform(RPCBase):
|
|
255
255
|
"""
|
256
256
|
|
257
257
|
@rpc_call
|
258
|
-
def apply_config(self, config: "dict |
|
258
|
+
def apply_config(self, config: "dict | SubplotConfig", replot_last_scan: "bool" = False):
|
259
259
|
"""
|
260
260
|
Apply the configuration to the 1D waveform widget.
|
261
261
|
Args:
|
262
|
-
config(dict|
|
262
|
+
config(dict|SubplotConfig): Configuration settings.
|
263
263
|
replot_last_scan(bool, optional): If True, replot the last scan. Defaults to False.
|
264
264
|
"""
|
265
265
|
|
@@ -393,7 +393,7 @@ class BECWaveform(RPCBase):
|
|
393
393
|
"""
|
394
394
|
|
395
395
|
|
396
|
-
class BECFigure(RPCBase
|
396
|
+
class BECFigure(RPCBase):
|
397
397
|
@property
|
398
398
|
@rpc_call
|
399
399
|
def rpc_id(self) -> "str":
|
@@ -426,7 +426,9 @@ class BECFigure(RPCBase, BECFigureClientMixin):
|
|
426
426
|
@rpc_call
|
427
427
|
def widgets(self) -> "dict":
|
428
428
|
"""
|
429
|
-
|
429
|
+
All widgets within the figure with gui ids as keys.
|
430
|
+
Returns:
|
431
|
+
dict: All widgets within the figure.
|
430
432
|
"""
|
431
433
|
|
432
434
|
@rpc_call
|
@@ -1307,3 +1309,209 @@ class BECMotorMap(RPCBase):
|
|
1307
1309
|
Returns:
|
1308
1310
|
dict: Data of the motor map.
|
1309
1311
|
"""
|
1312
|
+
|
1313
|
+
|
1314
|
+
class BECDock(RPCBase):
|
1315
|
+
@property
|
1316
|
+
@rpc_call
|
1317
|
+
def widget_list(self) -> "list":
|
1318
|
+
"""
|
1319
|
+
Get the widgets in the dock.
|
1320
|
+
Returns:
|
1321
|
+
widgets(list): The widgets in the dock.
|
1322
|
+
"""
|
1323
|
+
|
1324
|
+
@rpc_call
|
1325
|
+
def show_title_bar(self):
|
1326
|
+
"""
|
1327
|
+
Hide the title bar of the dock.
|
1328
|
+
"""
|
1329
|
+
|
1330
|
+
@rpc_call
|
1331
|
+
def hide_title_bar(self):
|
1332
|
+
"""
|
1333
|
+
Hide the title bar of the dock.
|
1334
|
+
"""
|
1335
|
+
|
1336
|
+
@rpc_call
|
1337
|
+
def get_widgets_positions(self) -> "dict":
|
1338
|
+
"""
|
1339
|
+
Get the positions of the widgets in the dock.
|
1340
|
+
|
1341
|
+
Returns:
|
1342
|
+
dict: The positions of the widgets in the dock as dict -> {(row, col, rowspan, colspan):widget}
|
1343
|
+
"""
|
1344
|
+
|
1345
|
+
@rpc_call
|
1346
|
+
def set_title(self, title: "str"):
|
1347
|
+
"""
|
1348
|
+
Set the title of the dock.
|
1349
|
+
|
1350
|
+
Args:
|
1351
|
+
title(str): The title of the dock.
|
1352
|
+
"""
|
1353
|
+
|
1354
|
+
@rpc_call
|
1355
|
+
def add_widget_bec(
|
1356
|
+
self,
|
1357
|
+
widget_type: "str",
|
1358
|
+
row=None,
|
1359
|
+
col=0,
|
1360
|
+
rowspan=1,
|
1361
|
+
colspan=1,
|
1362
|
+
shift: "Literal['down', 'up', 'left', 'right']" = "down",
|
1363
|
+
):
|
1364
|
+
"""
|
1365
|
+
Add a widget to the dock.
|
1366
|
+
Args:
|
1367
|
+
widget_type(str): The widget to add. Only BEC RPC widgets from RPCWidgetHandler are allowed.
|
1368
|
+
row(int): The row to add the widget to. If None, the widget will be added to the next available row.
|
1369
|
+
col(int): The column to add the widget to.
|
1370
|
+
rowspan(int): The number of rows the widget should span.
|
1371
|
+
colspan(int): The number of columns the widget should span.
|
1372
|
+
shift(Literal["down", "up", "left", "right"]): The direction to shift the widgets if the position is occupied.
|
1373
|
+
"""
|
1374
|
+
|
1375
|
+
@rpc_call
|
1376
|
+
def list_eligible_widgets(self) -> "list":
|
1377
|
+
"""
|
1378
|
+
List all widgets that can be added to the dock.
|
1379
|
+
Returns:
|
1380
|
+
list: The list of eligible widgets.
|
1381
|
+
"""
|
1382
|
+
|
1383
|
+
@rpc_call
|
1384
|
+
def move_widget(self, widget: "QWidget", new_row: "int", new_col: "int"):
|
1385
|
+
"""
|
1386
|
+
Move a widget to a new position in the layout.
|
1387
|
+
Args:
|
1388
|
+
widget(QWidget): The widget to move.
|
1389
|
+
new_row(int): The new row to move the widget to.
|
1390
|
+
new_col(int): The new column to move the widget to.
|
1391
|
+
"""
|
1392
|
+
|
1393
|
+
@rpc_call
|
1394
|
+
def remove_widget(self, widget: "QWidget"):
|
1395
|
+
"""
|
1396
|
+
Remove a widget from the dock.
|
1397
|
+
Args:
|
1398
|
+
widget(QWidget): The widget to remove.
|
1399
|
+
"""
|
1400
|
+
|
1401
|
+
@rpc_call
|
1402
|
+
def remove(self):
|
1403
|
+
"""
|
1404
|
+
Remove the dock from the parent dock area.
|
1405
|
+
"""
|
1406
|
+
|
1407
|
+
@rpc_call
|
1408
|
+
def attach(self):
|
1409
|
+
"""
|
1410
|
+
None
|
1411
|
+
"""
|
1412
|
+
|
1413
|
+
@rpc_call
|
1414
|
+
def detach(self):
|
1415
|
+
"""
|
1416
|
+
None
|
1417
|
+
"""
|
1418
|
+
|
1419
|
+
|
1420
|
+
class BECDockArea(RPCBase, BECGuiClientMixin):
|
1421
|
+
@property
|
1422
|
+
@rpc_call
|
1423
|
+
def panels(self) -> "dict":
|
1424
|
+
"""
|
1425
|
+
Get the docks in the dock area.
|
1426
|
+
Returns:
|
1427
|
+
dock_dict(dict): The docks in the dock area.
|
1428
|
+
"""
|
1429
|
+
|
1430
|
+
@rpc_call
|
1431
|
+
def save_state(self) -> "dict":
|
1432
|
+
"""
|
1433
|
+
Save the state of the dock area.
|
1434
|
+
Returns:
|
1435
|
+
dict: The state of the dock area.
|
1436
|
+
"""
|
1437
|
+
|
1438
|
+
@rpc_call
|
1439
|
+
def remove_dock(self, name: "str"):
|
1440
|
+
"""
|
1441
|
+
Remove a dock by name and ensure it is properly closed and cleaned up.
|
1442
|
+
Args:
|
1443
|
+
name(str): The name of the dock to remove.
|
1444
|
+
"""
|
1445
|
+
|
1446
|
+
@rpc_call
|
1447
|
+
def restore_state(
|
1448
|
+
self, state: "dict" = None, missing: "Literal['ignore', 'error']" = "ignore", extra="bottom"
|
1449
|
+
):
|
1450
|
+
"""
|
1451
|
+
Restore the state of the dock area. If no state is provided, the last state is restored.
|
1452
|
+
Args:
|
1453
|
+
state(dict): The state to restore.
|
1454
|
+
missing(Literal['ignore','error']): What to do if a dock is missing.
|
1455
|
+
extra(str): Extra docks that are in the dockarea but that are not mentioned in state will be added to the bottom of the dockarea, unless otherwise specified by the extra argument.
|
1456
|
+
"""
|
1457
|
+
|
1458
|
+
@rpc_call
|
1459
|
+
def add_dock(
|
1460
|
+
self,
|
1461
|
+
name: "str" = None,
|
1462
|
+
position: "Literal['bottom', 'top', 'left', 'right', 'above', 'below']" = None,
|
1463
|
+
relative_to: "Optional[BECDock]" = None,
|
1464
|
+
closable: "bool" = False,
|
1465
|
+
prefix: "str" = "dock",
|
1466
|
+
widget: "QWidget" = None,
|
1467
|
+
row: "int" = None,
|
1468
|
+
col: "int" = 0,
|
1469
|
+
rowspan: "int" = 1,
|
1470
|
+
colspan: "int" = 1,
|
1471
|
+
) -> "BECDock":
|
1472
|
+
"""
|
1473
|
+
Add a dock to the dock area. Dock has QGridLayout as layout manager by default.
|
1474
|
+
|
1475
|
+
Args:
|
1476
|
+
name(str): The name of the dock to be displayed and for further references. Has to be unique.
|
1477
|
+
position(Literal["bottom", "top", "left", "right", "above", "below"]): The position of the dock.
|
1478
|
+
relative_to(BECDock): The dock to which the new dock should be added relative to.
|
1479
|
+
closable(bool): Whether the dock is closable.
|
1480
|
+
prefix(str): The prefix for the dock name if no name is provided.
|
1481
|
+
widget(QWidget): The widget to be added to the dock.
|
1482
|
+
row(int): The row of the added widget.
|
1483
|
+
col(int): The column of the added widget.
|
1484
|
+
rowspan(int): The rowspan of the added widget.
|
1485
|
+
colspan(int): The colspan of the added widget.
|
1486
|
+
Returns:
|
1487
|
+
BECDock: The created dock.
|
1488
|
+
"""
|
1489
|
+
|
1490
|
+
@rpc_call
|
1491
|
+
def clear_all(self):
|
1492
|
+
"""
|
1493
|
+
Close all docks and remove all temp areas.
|
1494
|
+
"""
|
1495
|
+
|
1496
|
+
@rpc_call
|
1497
|
+
def detach_dock(self, dock_name: "str") -> "BECDock":
|
1498
|
+
"""
|
1499
|
+
Undock a dock from the dock area.
|
1500
|
+
Args:
|
1501
|
+
dock(BECDock): The dock to undock.
|
1502
|
+
|
1503
|
+
Returns:
|
1504
|
+
BECDock: The undocked dock.
|
1505
|
+
"""
|
1506
|
+
|
1507
|
+
@rpc_call
|
1508
|
+
def attach_all(self):
|
1509
|
+
"""
|
1510
|
+
Return all floating docks to the dock area.
|
1511
|
+
"""
|
1512
|
+
|
1513
|
+
@rpc_call
|
1514
|
+
def get_all_rpc(self) -> "dict":
|
1515
|
+
"""
|
1516
|
+
Get all registered RPC objects.
|
1517
|
+
"""
|
bec_widgets/cli/client_utils.py
CHANGED
@@ -22,7 +22,7 @@ from bec_widgets.cli.auto_updates import AutoUpdates
|
|
22
22
|
from bec_widgets.utils.bec_dispatcher import BECDispatcher
|
23
23
|
|
24
24
|
if TYPE_CHECKING:
|
25
|
-
from bec_widgets.cli.client import BECFigure
|
25
|
+
from bec_widgets.cli.client import BECDockArea, BECFigure
|
26
26
|
|
27
27
|
|
28
28
|
def rpc_call(func):
|
@@ -56,7 +56,7 @@ def rpc_call(func):
|
|
56
56
|
return wrapper
|
57
57
|
|
58
58
|
|
59
|
-
class
|
59
|
+
class BECGuiClientMixin:
|
60
60
|
def __init__(self, **kwargs) -> None:
|
61
61
|
super().__init__(**kwargs)
|
62
62
|
self._process = None
|
@@ -94,7 +94,7 @@ class BECFigureClientMixin:
|
|
94
94
|
)
|
95
95
|
|
96
96
|
@staticmethod
|
97
|
-
def _handle_msg_update(msg: MessageObject, parent:
|
97
|
+
def _handle_msg_update(msg: MessageObject, parent: BECGuiClientMixin) -> None:
|
98
98
|
if parent.update_script is not None:
|
99
99
|
# pylint: disable=protected-access
|
100
100
|
parent._update_script_msg_parser(msg.value)
|
@@ -139,8 +139,19 @@ class BECFigureClientMixin:
|
|
139
139
|
config = self._client._service_config.redis
|
140
140
|
monitor_module = importlib.import_module("bec_widgets.cli.server")
|
141
141
|
monitor_path = monitor_module.__file__
|
142
|
-
|
143
|
-
|
142
|
+
gui_class = self.__class__.__name__
|
143
|
+
|
144
|
+
command = [
|
145
|
+
sys.executable,
|
146
|
+
"-u",
|
147
|
+
monitor_path,
|
148
|
+
"--id",
|
149
|
+
self._gui_id,
|
150
|
+
"--config",
|
151
|
+
config,
|
152
|
+
"--gui_class",
|
153
|
+
gui_class,
|
154
|
+
]
|
144
155
|
self._process = subprocess.Popen(
|
145
156
|
command, text=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE
|
146
157
|
)
|
bec_widgets/cli/generate_cli.py
CHANGED
@@ -22,7 +22,7 @@ else:
|
|
22
22
|
class ClientGenerator:
|
23
23
|
def __init__(self):
|
24
24
|
self.header = """# This file was automatically generated by generate_cli.py\n
|
25
|
-
from bec_widgets.cli.client_utils import rpc_call, RPCBase,
|
25
|
+
from bec_widgets.cli.client_utils import rpc_call, RPCBase, BECGuiClientMixin
|
26
26
|
from typing import Literal, Optional, overload"""
|
27
27
|
|
28
28
|
self.content = ""
|
@@ -53,9 +53,9 @@ from typing import Literal, Optional, overload"""
|
|
53
53
|
# from {module} import {class_name}"""
|
54
54
|
|
55
55
|
# Generate the content
|
56
|
-
if cls.__name__ == "
|
56
|
+
if cls.__name__ == "BECDockArea":
|
57
57
|
self.content += f"""
|
58
|
-
class {class_name}(RPCBase,
|
58
|
+
class {class_name}(RPCBase, BECGuiClientMixin):"""
|
59
59
|
else:
|
60
60
|
self.content += f"""
|
61
61
|
class {class_name}(RPCBase):"""
|
@@ -108,6 +108,7 @@ if __name__ == "__main__": # pragma: no cover
|
|
108
108
|
import os
|
109
109
|
|
110
110
|
from bec_widgets.utils import BECConnector
|
111
|
+
from bec_widgets.widgets.dock import BECDock, BECDockArea
|
111
112
|
from bec_widgets.widgets.figure import BECFigure
|
112
113
|
from bec_widgets.widgets.plots import BECImageShow, BECMotorMap, BECPlotBase, BECWaveform
|
113
114
|
from bec_widgets.widgets.plots.image import BECImageItem
|
@@ -124,6 +125,8 @@ if __name__ == "__main__": # pragma: no cover
|
|
124
125
|
BECConnector,
|
125
126
|
BECImageItem,
|
126
127
|
BECMotorMap,
|
128
|
+
BECDock,
|
129
|
+
BECDockArea,
|
127
130
|
]
|
128
131
|
generator = ClientGenerator()
|
129
132
|
generator.generate_client(clss)
|
@@ -0,0 +1,26 @@
|
|
1
|
+
from bec_widgets.utils import BECConnector
|
2
|
+
from bec_widgets.widgets.figure import BECFigure
|
3
|
+
|
4
|
+
|
5
|
+
class RPCWidgetHandler:
|
6
|
+
"""Handler class for creating widgets from RPC messages."""
|
7
|
+
|
8
|
+
widget_classes = {
|
9
|
+
"BECFigure": BECFigure,
|
10
|
+
}
|
11
|
+
|
12
|
+
@staticmethod
|
13
|
+
def create_widget(widget_type, **kwargs) -> BECConnector:
|
14
|
+
"""
|
15
|
+
Create a widget from an RPC message.
|
16
|
+
Args:
|
17
|
+
widget_type(str): The type of the widget.
|
18
|
+
**kwargs: The keyword arguments for the widget.
|
19
|
+
|
20
|
+
Returns:
|
21
|
+
widget(BECConnector): The created widget.
|
22
|
+
"""
|
23
|
+
widget_class = RPCWidgetHandler.widget_classes.get(widget_type)
|
24
|
+
if widget_class:
|
25
|
+
return widget_class(**kwargs)
|
26
|
+
raise ValueError(f"Unknown widget type: {widget_type}")
|
bec_widgets/cli/server.py
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
import inspect
|
2
2
|
import threading
|
3
3
|
import time
|
4
|
+
from typing import Literal, Union
|
4
5
|
|
5
6
|
from bec_lib import MessageEndpoints, messages
|
6
7
|
from qtpy.QtCore import QTimer
|
@@ -8,6 +9,7 @@ from qtpy.QtCore import QTimer
|
|
8
9
|
from bec_widgets.cli.rpc_register import RPCRegister
|
9
10
|
from bec_widgets.utils import BECDispatcher
|
10
11
|
from bec_widgets.utils.bec_connector import BECConnector
|
12
|
+
from bec_widgets.widgets.dock.dock_area import BECDockArea
|
11
13
|
from bec_widgets.widgets.figure import BECFigure
|
12
14
|
from bec_widgets.widgets.plots import BECCurve, BECImageShow, BECWaveform
|
13
15
|
|
@@ -16,15 +18,20 @@ class BECWidgetsCLIServer:
|
|
16
18
|
WIDGETS = [BECWaveform, BECFigure, BECCurve, BECImageShow]
|
17
19
|
|
18
20
|
def __init__(
|
19
|
-
self,
|
21
|
+
self,
|
22
|
+
gui_id: str = None,
|
23
|
+
dispatcher: BECDispatcher = None,
|
24
|
+
client=None,
|
25
|
+
config=None,
|
26
|
+
gui_class: Union["BECFigure", "BECDockArea"] = BECFigure,
|
20
27
|
) -> None:
|
21
28
|
self.dispatcher = BECDispatcher(config=config) if dispatcher is None else dispatcher
|
22
29
|
self.client = self.dispatcher.client if client is None else client
|
23
30
|
self.client.start()
|
24
31
|
self.gui_id = gui_id
|
25
|
-
self.
|
32
|
+
self.gui = gui_class(gui_id=self.gui_id)
|
26
33
|
self.rpc_register = RPCRegister()
|
27
|
-
self.rpc_register.add_rpc(self.
|
34
|
+
self.rpc_register.add_rpc(self.gui)
|
28
35
|
|
29
36
|
self.dispatcher.connect_slot(
|
30
37
|
self.on_rpc_update, MessageEndpoints.gui_instructions(self.gui_id)
|
@@ -102,7 +109,7 @@ class BECWidgetsCLIServer:
|
|
102
109
|
expire=10,
|
103
110
|
)
|
104
111
|
|
105
|
-
def shutdown(self):
|
112
|
+
def shutdown(self): # TODO not sure if needed when cleanup is done at level of BECConnector
|
106
113
|
self._shutdown_event = True
|
107
114
|
self._heartbeat_timer.stop()
|
108
115
|
self.client.shutdown()
|
@@ -110,6 +117,7 @@ class BECWidgetsCLIServer:
|
|
110
117
|
|
111
118
|
if __name__ == "__main__": # pragma: no cover
|
112
119
|
import argparse
|
120
|
+
import os
|
113
121
|
import sys
|
114
122
|
|
115
123
|
from qtpy.QtCore import QSize
|
@@ -118,8 +126,9 @@ if __name__ == "__main__": # pragma: no cover
|
|
118
126
|
|
119
127
|
app = QApplication(sys.argv)
|
120
128
|
app.setApplicationName("BEC Figure")
|
129
|
+
current_path = os.path.dirname(__file__)
|
121
130
|
icon = QIcon()
|
122
|
-
icon.addFile("bec_widgets_icon.png", size=QSize(48, 48))
|
131
|
+
icon.addFile(os.path.join(current_path, "bec_widgets_icon.png"), size=QSize(48, 48))
|
123
132
|
app.setWindowIcon(icon)
|
124
133
|
|
125
134
|
win = QMainWindow()
|
@@ -127,15 +136,31 @@ if __name__ == "__main__": # pragma: no cover
|
|
127
136
|
|
128
137
|
parser = argparse.ArgumentParser(description="BEC Widgets CLI Server")
|
129
138
|
parser.add_argument("--id", type=str, help="The id of the server")
|
139
|
+
parser.add_argument(
|
140
|
+
"--gui_class",
|
141
|
+
type=str,
|
142
|
+
help="Name of the gui class to be rendered. Possible values: \n- BECFigure\n- BECDockArea",
|
143
|
+
)
|
130
144
|
parser.add_argument("--config", type=str, help="Config to connect to redis.")
|
131
145
|
|
132
146
|
args = parser.parse_args()
|
133
147
|
|
134
|
-
|
135
|
-
|
148
|
+
if args.gui_class == "BECFigure":
|
149
|
+
gui_class = BECFigure
|
150
|
+
elif args.gui_class == "BECDockArea":
|
151
|
+
gui_class = BECDockArea
|
152
|
+
else:
|
153
|
+
print(
|
154
|
+
"Please specify a valid gui_class to run. Use -h for help."
|
155
|
+
"\n Starting with default gui_class BECFigure."
|
156
|
+
)
|
157
|
+
gui_class = BECFigure
|
158
|
+
|
159
|
+
server = BECWidgetsCLIServer(gui_id=args.id, config=args.config, gui_class=gui_class)
|
136
160
|
|
137
|
-
|
138
|
-
win.setCentralWidget(
|
161
|
+
gui = server.gui
|
162
|
+
win.setCentralWidget(gui)
|
163
|
+
win.resize(800, 600)
|
139
164
|
win.show()
|
140
165
|
|
141
166
|
app.aboutToQuit.connect(server.shutdown)
|
@@ -2,14 +2,17 @@ import os
|
|
2
2
|
|
3
3
|
import numpy as np
|
4
4
|
import pyqtgraph as pg
|
5
|
-
from pyqtgraph.Qt import uic
|
5
|
+
from pyqtgraph.Qt import QtWidgets, uic
|
6
6
|
from qtconsole.inprocess import QtInProcessKernelManager
|
7
7
|
from qtconsole.rich_jupyter_widget import RichJupyterWidget
|
8
|
+
from qtpy.QtCore import QSize
|
9
|
+
from qtpy.QtGui import QIcon
|
8
10
|
from qtpy.QtWidgets import QApplication, QVBoxLayout, QWidget
|
9
11
|
|
10
12
|
from bec_widgets.cli.rpc_register import RPCRegister
|
11
13
|
from bec_widgets.utils import BECDispatcher
|
12
14
|
from bec_widgets.widgets import BECFigure
|
15
|
+
from bec_widgets.widgets.dock.dock_area import BECDockArea
|
13
16
|
|
14
17
|
|
15
18
|
class JupyterConsoleWidget(RichJupyterWidget): # pragma: no cover:
|
@@ -46,15 +49,22 @@ class JupyterConsoleWindow(QWidget): # pragma: no cover:
|
|
46
49
|
|
47
50
|
self.register = RPCRegister()
|
48
51
|
self.register.add_rpc(self.figure)
|
49
|
-
|
52
|
+
|
50
53
|
# console push
|
51
54
|
self.console.kernel_manager.kernel.shell.push(
|
52
55
|
{
|
53
56
|
"fig": self.figure,
|
54
57
|
"register": self.register,
|
58
|
+
"dock": self.dock,
|
55
59
|
"w1": self.w1,
|
56
60
|
"w2": self.w2,
|
57
61
|
"w3": self.w3,
|
62
|
+
"d1": self.d1,
|
63
|
+
"d2": self.d2,
|
64
|
+
"d3": self.d3,
|
65
|
+
"b2a": self.button_2_a,
|
66
|
+
"b2b": self.button_2_b,
|
67
|
+
"b2c": self.button_2_c,
|
58
68
|
"bec": self.figure.client,
|
59
69
|
"scans": self.figure.client.scans,
|
60
70
|
"dev": self.figure.client.device_manager.devices,
|
@@ -67,9 +77,16 @@ class JupyterConsoleWindow(QWidget): # pragma: no cover:
|
|
67
77
|
self.figure = BECFigure(parent=self, gui_id="remote") # Create a new BECDeviceMonitor
|
68
78
|
self.glw_1_layout.addWidget(self.figure) # Add BECDeviceMonitor to the layout
|
69
79
|
|
80
|
+
self.dock_layout = QVBoxLayout(self.dock_placeholder)
|
81
|
+
self.dock = BECDockArea(gui_id="remote")
|
82
|
+
self.dock_layout.addWidget(self.dock)
|
83
|
+
|
70
84
|
# add stuff to figure
|
71
85
|
self._init_figure()
|
72
86
|
|
87
|
+
# init dock for testing
|
88
|
+
self._init_dock()
|
89
|
+
|
73
90
|
self.console_layout = QVBoxLayout(self.widget_console)
|
74
91
|
self.console = JupyterConsoleWidget()
|
75
92
|
self.console_layout.addWidget(self.console)
|
@@ -91,6 +108,36 @@ class JupyterConsoleWindow(QWidget): # pragma: no cover:
|
|
91
108
|
self.w1.add_curve_scan("samx", "samy", "bpm3a", pen_style="dash")
|
92
109
|
self.c1 = self.w1.get_config()
|
93
110
|
|
111
|
+
def _init_dock(self):
|
112
|
+
self.button_1 = QtWidgets.QPushButton("Button 1 ")
|
113
|
+
self.button_2_a = QtWidgets.QPushButton("Button to be added at place 0,0 in d3")
|
114
|
+
self.button_2_b = QtWidgets.QPushButton("button after without postions specified")
|
115
|
+
self.button_2_c = QtWidgets.QPushButton("button super late")
|
116
|
+
self.button_3 = QtWidgets.QPushButton("Button above Figure ")
|
117
|
+
self.label_1 = QtWidgets.QLabel("some scan info label with useful information")
|
118
|
+
|
119
|
+
self.label_2 = QtWidgets.QLabel("label which is added separately")
|
120
|
+
self.label_3 = QtWidgets.QLabel("Label above figure")
|
121
|
+
|
122
|
+
self.d1 = self.dock.add_dock(widget=self.button_1, position="left")
|
123
|
+
self.d1.addWidget(self.label_2)
|
124
|
+
self.d2 = self.dock.add_dock(widget=self.label_1, position="right")
|
125
|
+
self.d3 = self.dock.add_dock(name="figure")
|
126
|
+
self.fig_dock3 = BECFigure()
|
127
|
+
self.fig_dock3.plot("samx", "bpm4d")
|
128
|
+
self.d3.add_widget(self.label_3)
|
129
|
+
self.d3.add_widget(self.button_3)
|
130
|
+
self.d3.add_widget(self.fig_dock3)
|
131
|
+
|
132
|
+
self.dock.save_state()
|
133
|
+
|
134
|
+
def closeEvent(self, event):
|
135
|
+
"""Override to handle things when main window is closed."""
|
136
|
+
self.dock.cleanup()
|
137
|
+
self.figure.clear_all()
|
138
|
+
self.figure.client.shutdown()
|
139
|
+
super().closeEvent(event)
|
140
|
+
|
94
141
|
|
95
142
|
if __name__ == "__main__": # pragma: no cover
|
96
143
|
import sys
|
@@ -101,7 +148,12 @@ if __name__ == "__main__": # pragma: no cover
|
|
101
148
|
|
102
149
|
app = QApplication(sys.argv)
|
103
150
|
app.setApplicationName("Jupyter Console")
|
151
|
+
app.setApplicationDisplayName("Jupyter Console")
|
152
|
+
icon = QIcon()
|
153
|
+
icon.addFile("terminal_icon.png", size=QSize(48, 48))
|
154
|
+
app.setWindowIcon(icon)
|
104
155
|
win = JupyterConsoleWindow()
|
105
156
|
win.show()
|
106
157
|
|
158
|
+
app.aboutToQuit.connect(win.close)
|
107
159
|
sys.exit(app.exec_())
|
@@ -13,13 +13,37 @@
|
|
13
13
|
<property name="windowTitle">
|
14
14
|
<string>Plotting Console</string>
|
15
15
|
</property>
|
16
|
-
<layout class="QVBoxLayout" name="
|
16
|
+
<layout class="QVBoxLayout" name="verticalLayout_4">
|
17
17
|
<item>
|
18
18
|
<widget class="QSplitter" name="splitter">
|
19
19
|
<property name="orientation">
|
20
20
|
<enum>Qt::Horizontal</enum>
|
21
21
|
</property>
|
22
|
-
<widget class="
|
22
|
+
<widget class="QTabWidget" name="tabWidget">
|
23
|
+
<property name="currentIndex">
|
24
|
+
<number>0</number>
|
25
|
+
</property>
|
26
|
+
<widget class="QWidget" name="tab_1">
|
27
|
+
<attribute name="title">
|
28
|
+
<string>BECDock</string>
|
29
|
+
</attribute>
|
30
|
+
<layout class="QVBoxLayout" name="verticalLayout">
|
31
|
+
<item>
|
32
|
+
<widget class="QWidget" name="dock_placeholder" native="true"/>
|
33
|
+
</item>
|
34
|
+
</layout>
|
35
|
+
</widget>
|
36
|
+
<widget class="QWidget" name="tab_2">
|
37
|
+
<attribute name="title">
|
38
|
+
<string>BECFigure</string>
|
39
|
+
</attribute>
|
40
|
+
<layout class="QVBoxLayout" name="verticalLayout_3">
|
41
|
+
<item>
|
42
|
+
<widget class="QWidget" name="glw" native="true"/>
|
43
|
+
</item>
|
44
|
+
</layout>
|
45
|
+
</widget>
|
46
|
+
</widget>
|
23
47
|
<widget class="QWidget" name="widget_console" native="true"/>
|
24
48
|
</widget>
|
25
49
|
</item>
|
Binary file
|
bec_widgets/utils/__init__.py
CHANGED
@@ -5,5 +5,6 @@ from .colors import Colors
|
|
5
5
|
from .container_utils import WidgetContainerUtils
|
6
6
|
from .crosshair import Crosshair
|
7
7
|
from .entry_validator import EntryValidator
|
8
|
+
from .layout_manager import GridLayoutManager
|
8
9
|
from .rpc_decorator import register_rpc_methods, rpc_public
|
9
10
|
from .validator_delegate import DoubleValidationDelegate
|
@@ -148,6 +148,14 @@ class BECConnector:
|
|
148
148
|
else:
|
149
149
|
return self.config
|
150
150
|
|
151
|
-
def
|
152
|
-
|
153
|
-
|
151
|
+
def cleanup(self):
|
152
|
+
"""Cleanup the widget."""
|
153
|
+
self.rpc_register.remove_rpc(self)
|
154
|
+
all_connections = self.rpc_register.list_all_connections()
|
155
|
+
if len(all_connections) == 0:
|
156
|
+
print("No more connections. Shutting down GUI BEC client.")
|
157
|
+
self.client.shutdown()
|
158
|
+
|
159
|
+
# def closeEvent(self, event):
|
160
|
+
# self.cleanup()
|
161
|
+
# super().closeEvent(event)
|