bec-widgets 0.51.0__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.
Files changed (36) hide show
  1. bec_widgets/cli/__init__.py +1 -1
  2. bec_widgets/cli/bec_widgets_icon.png +0 -0
  3. bec_widgets/cli/client.py +213 -5
  4. bec_widgets/cli/client_utils.py +16 -5
  5. bec_widgets/cli/generate_cli.py +6 -3
  6. bec_widgets/cli/rpc_wigdet_handler.py +26 -0
  7. bec_widgets/cli/server.py +34 -9
  8. bec_widgets/examples/jupyter_console/jupyter_console_window.py +54 -2
  9. bec_widgets/examples/jupyter_console/jupyter_console_window.ui +26 -2
  10. bec_widgets/examples/jupyter_console/terminal_icon.png +0 -0
  11. bec_widgets/utils/__init__.py +1 -0
  12. bec_widgets/utils/bec_connector.py +11 -3
  13. bec_widgets/utils/layout_manager.py +117 -0
  14. bec_widgets/widgets/__init__.py +1 -0
  15. bec_widgets/widgets/dock/__init__.py +2 -0
  16. bec_widgets/widgets/dock/dock.py +269 -0
  17. bec_widgets/widgets/dock/dock_area.py +220 -0
  18. bec_widgets/widgets/figure/figure.py +33 -16
  19. bec_widgets/widgets/plots/__init__.py +1 -1
  20. bec_widgets/widgets/plots/image.py +5 -9
  21. bec_widgets/widgets/plots/motor_map.py +3 -3
  22. bec_widgets/widgets/plots/plot_base.py +4 -3
  23. bec_widgets/widgets/plots/waveform.py +6 -10
  24. {bec_widgets-0.51.0.dist-info → bec_widgets-0.52.0.dist-info}/METADATA +1 -1
  25. {bec_widgets-0.51.0.dist-info → bec_widgets-0.52.0.dist-info}/RECORD +36 -28
  26. tests/end-2-end/conftest.py +19 -4
  27. tests/end-2-end/test_bec_dock_rpc_e2e.py +145 -0
  28. tests/end-2-end/test_bec_figure_rpc_e2e.py +17 -17
  29. tests/end-2-end/test_rpc_register_e2e.py +3 -3
  30. tests/unit_tests/test_bec_dock.py +114 -0
  31. tests/unit_tests/test_bec_figure.py +20 -22
  32. tests/unit_tests/test_generate_cli_client.py +1 -1
  33. tests/unit_tests/test_waveform1d.py +1 -1
  34. {bec_widgets-0.51.0.dist-info → bec_widgets-0.52.0.dist-info}/LICENSE +0 -0
  35. {bec_widgets-0.51.0.dist-info → bec_widgets-0.52.0.dist-info}/WHEEL +0 -0
  36. {bec_widgets-0.51.0.dist-info → bec_widgets-0.52.0.dist-info}/top_level.txt +0 -0
@@ -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 BECFigureClientMixin, RPCBase, rpc_call
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 | WidgetConfig", replot_last_scan: "bool" = False):
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|WidgetConfig): Configuration settings.
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, BECFigureClientMixin):
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
- None
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
+ """
@@ -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 BECFigureClientMixin:
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: BECFigureClientMixin) -> None:
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
- command = [sys.executable, "-u", monitor_path, "--id", self._gui_id, "--config", config]
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
  )
@@ -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, BECFigureClientMixin
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__ == "BECFigure":
56
+ if cls.__name__ == "BECDockArea":
57
57
  self.content += f"""
58
- class {class_name}(RPCBase, BECFigureClientMixin):"""
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, gui_id: str = None, dispatcher: BECDispatcher = None, client=None, config=None
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.fig = BECFigure(gui_id=self.gui_id)
32
+ self.gui = gui_class(gui_id=self.gui_id)
26
33
  self.rpc_register = RPCRegister()
27
- self.rpc_register.add_rpc(self.fig)
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
- server = BECWidgetsCLIServer(gui_id=args.id, config=args.config)
135
- # server = BECWidgetsCLIServer(gui_id="test",config="awi-bec-dev-01:6379")
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
- fig = server.fig
138
- win.setCentralWidget(fig)
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
- print("Registered objects:", dict(self.register.list_all_connections()))
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="verticalLayout">
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="QWidget" name="glw" native="true"/>
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>
@@ -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 closeEvent(self, event):
152
- self.client.shutdown()
153
- super().closeEvent(event)
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)