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
@@ -0,0 +1,117 @@
1
+ from collections import OrderedDict
2
+ from typing import Literal
3
+
4
+ from qtpy.QtWidgets import QGridLayout, QWidget
5
+
6
+
7
+ class GridLayoutManager:
8
+ """
9
+ GridLayoutManager class is used to manage widgets in a QGridLayout and extend its functionality.
10
+
11
+ The GridLayoutManager class provides methods to add, move, and check the position of widgets in a QGridLayout.
12
+ It also provides a method to get the positions of all widgets in the layout.
13
+
14
+ Args:
15
+ layout(QGridLayout): The layout to manage.
16
+ """
17
+
18
+ def __init__(self, layout: QGridLayout):
19
+ self.layout = layout
20
+
21
+ def is_position_occupied(self, row: int, col: int) -> bool:
22
+ """
23
+ Check if the position in the layout is occupied by a widget.
24
+ Args:
25
+ row(int): The row to check.
26
+ col(int): The column to check.
27
+
28
+ Returns:
29
+ bool: True if the position is occupied, False otherwise.
30
+ """
31
+ for i in range(self.layout.count()):
32
+ widget_row, widget_col, _, _ = self.layout.getItemPosition(i)
33
+ if widget_row == row and widget_col == col:
34
+ return True
35
+ return False
36
+
37
+ def shift_widgets(
38
+ self,
39
+ direction: Literal["down", "up", "left", "right"] = "down",
40
+ start_row: int = 0,
41
+ start_col: int = 0,
42
+ ):
43
+ """
44
+ Shift widgets in the layout in the specified direction starting from the specified position.
45
+ Args:
46
+ direction(str): The direction to shift the widgets. Can be "down", "up", "left", or "right".
47
+ start_row(int): The row to start shifting from. Default is 0.
48
+ start_col(int): The column to start shifting from. Default is 0.
49
+ """
50
+ for i in reversed(range(self.layout.count())):
51
+ widget_item = self.layout.itemAt(i)
52
+ widget = widget_item.widget()
53
+ row, col, rowspan, colspan = self.layout.getItemPosition(i)
54
+ if direction == "down" and row >= start_row:
55
+ self.layout.addWidget(widget, row + 1, col, rowspan, colspan)
56
+ elif direction == "up" and row > start_row:
57
+ self.layout.addWidget(widget, row - 1, col, rowspan, colspan)
58
+ elif direction == "right" and col >= start_col:
59
+ self.layout.addWidget(widget, row, col + 1, rowspan, colspan)
60
+ elif direction == "left" and col > start_col:
61
+ self.layout.addWidget(widget, row, col - 1, rowspan, colspan)
62
+
63
+ def move_widget(self, widget: QWidget, new_row: int, new_col: int):
64
+ """
65
+ Move a widget to a new position in the layout.
66
+ Args:
67
+ widget(QWidget): The widget to move.
68
+ new_row(int): The new row to move the widget to.
69
+ new_col(int): The new column to move the widget to.
70
+ """
71
+ self.layout.removeWidget(widget)
72
+ self.layout.addWidget(widget, new_row, new_col)
73
+
74
+ def add_widget(
75
+ self,
76
+ widget: QWidget,
77
+ row=None,
78
+ col=0,
79
+ rowspan=1,
80
+ colspan=1,
81
+ shift: Literal["down", "up", "left", "right"] = "down",
82
+ ):
83
+ """
84
+ Add a widget to the layout at the specified position.
85
+ Args:
86
+ widget(QWidget): The widget to add.
87
+ row(int): The row to add the widget to. If None, the widget will be added to the next available row.
88
+ col(int): The column to add the widget to. Default is 0.
89
+ rowspan(int): The number of rows the widget will span. Default is 1.
90
+ colspan(int): The number of columns the widget will span. Default is 1.
91
+ shift(str): The direction to shift the widgets if the position is occupied. Can be "down", "up", "left", or "right".
92
+ """
93
+ if row is None:
94
+ row = self.layout.rowCount()
95
+ if self.is_position_occupied(row, col):
96
+ self.shift_widgets(shift, start_row=row)
97
+ self.layout.addWidget(widget, row, col, rowspan, colspan)
98
+
99
+ def get_widgets_positions(self) -> dict:
100
+ """
101
+ Get the positions of all widgets in the layout.
102
+ Returns:
103
+ dict: A dictionary with the positions of the widgets in the layout.
104
+
105
+ """
106
+ positions = []
107
+ for i in range(self.layout.count()):
108
+ widget_item = self.layout.itemAt(i)
109
+ widget = widget_item.widget()
110
+ if widget:
111
+ position = self.layout.getItemPosition(i)
112
+ positions.append((position, widget))
113
+ positions.sort(key=lambda x: (x[0][0], x[0][1], x[0][2], x[0][3]))
114
+ ordered_positions = OrderedDict()
115
+ for pos, widget in positions:
116
+ ordered_positions[pos] = widget
117
+ return ordered_positions
@@ -1,3 +1,4 @@
1
+ from .dock import BECDock, BECDockArea
1
2
  from .figure import BECFigure, FigureConfig
2
3
  from .monitor import BECMonitor
3
4
  from .motor_control import (
@@ -0,0 +1,2 @@
1
+ from .dock import BECDock
2
+ from .dock_area import BECDockArea
@@ -0,0 +1,269 @@
1
+ from __future__ import annotations
2
+
3
+ from typing import TYPE_CHECKING, Literal, Optional
4
+
5
+ from pydantic import Field
6
+ from pyqtgraph.dockarea import Dock
7
+
8
+ from bec_widgets.cli.rpc_wigdet_handler import RPCWidgetHandler
9
+ from bec_widgets.utils import BECConnector, ConnectionConfig, GridLayoutManager
10
+
11
+ if TYPE_CHECKING:
12
+ from qtpy.QtWidgets import QWidget
13
+
14
+ from bec_widgets.widgets import BECDockArea
15
+
16
+
17
+ class DockConfig(ConnectionConfig):
18
+ widgets: dict[str, ConnectionConfig] = Field({}, description="The widgets in the dock.")
19
+ position: Literal["bottom", "top", "left", "right", "above", "below"] = Field(
20
+ "bottom", description="The position of the dock."
21
+ )
22
+ parent_dock_area: Optional[str] = Field(
23
+ None, description="The GUI ID of parent dock area of the dock."
24
+ )
25
+
26
+
27
+ class BECDock(BECConnector, Dock):
28
+ USER_ACCESS = [
29
+ "rpc_id",
30
+ "widget_list",
31
+ "show_title_bar",
32
+ "hide_title_bar",
33
+ "get_widgets_positions",
34
+ "set_title",
35
+ "add_widget_bec",
36
+ "list_eligible_widgets",
37
+ "move_widget",
38
+ "remove_widget",
39
+ "remove",
40
+ "attach",
41
+ "detach",
42
+ ]
43
+
44
+ def __init__(
45
+ self,
46
+ parent: QWidget | None = None,
47
+ parent_dock_area: BECDockArea | None = None,
48
+ config: DockConfig | None = None,
49
+ name: str | None = None,
50
+ client=None,
51
+ gui_id: str | None = None,
52
+ **kwargs,
53
+ ) -> None:
54
+ if config is None:
55
+ config = DockConfig(
56
+ widget_class=self.__class__.__name__, parent_dock_area=parent_dock_area.gui_id
57
+ )
58
+ else:
59
+ if isinstance(config, dict):
60
+ config = DockConfig(**config)
61
+ self.config = config
62
+ super().__init__(client=client, config=config, gui_id=gui_id)
63
+ Dock.__init__(self, name=name, **kwargs)
64
+
65
+ self.parent_dock_area = parent_dock_area
66
+
67
+ # Layout Manager
68
+ self.layout_manager = GridLayoutManager(self.layout)
69
+
70
+ def dropEvent(self, event):
71
+ source = event.source()
72
+ old_area = source.area
73
+ self.setOrientation("horizontal", force=True)
74
+ super().dropEvent(event)
75
+ if old_area in self.parent_dock_area.tempAreas and old_area != self.parent_dock_area:
76
+ self.parent_dock_area.removeTempArea(old_area)
77
+
78
+ def float(self):
79
+ """
80
+ Float the dock.
81
+ Overwrites the default pyqtgraph dock float.
82
+ """
83
+
84
+ # need to check if the dock is temporary and if it is the only dock in the area
85
+ # fixes bug in pyqtgraph detaching
86
+ if self.area.temporary == True and len(self.area.docks) <= 1:
87
+ return
88
+ elif self.area.temporary == True and len(self.area.docks) > 1:
89
+ self.area.docks.pop(self.name(), None)
90
+ super().float()
91
+ else:
92
+ super().float()
93
+
94
+ @property
95
+ def widget_list(self) -> list:
96
+ """
97
+ Get the widgets in the dock.
98
+
99
+ Returns:
100
+ widgets(list): The widgets in the dock.
101
+ """
102
+ return self.widgets
103
+
104
+ @widget_list.setter
105
+ def widget_list(self, value: list):
106
+ self.widgets = value
107
+
108
+ def hide_title_bar(self):
109
+ """
110
+ Hide the title bar of the dock.
111
+ """
112
+ # self.hideTitleBar() #TODO pyqtgraph looks bugged ATM, doing my implementation
113
+ self.label.hide()
114
+ self.labelHidden = True
115
+
116
+ def show_title_bar(self):
117
+ """
118
+ Hide the title bar of the dock.
119
+ """
120
+ # self.showTitleBar() #TODO pyqtgraph looks bugged ATM, doing my implementation
121
+ self.label.show()
122
+ self.labelHidden = False
123
+
124
+ def set_title(self, title: str):
125
+ """
126
+ Set the title of the dock.
127
+
128
+ Args:
129
+ title(str): The title of the dock.
130
+ """
131
+ self.parent_dock_area.docks[title] = self.parent_dock_area.docks.pop(self.name())
132
+ self.setTitle(title)
133
+
134
+ def get_widgets_positions(self) -> dict:
135
+ """
136
+ Get the positions of the widgets in the dock.
137
+
138
+ Returns:
139
+ dict: The positions of the widgets in the dock as dict -> {(row, col, rowspan, colspan):widget}
140
+ """
141
+ return self.layout_manager.get_widgets_positions()
142
+
143
+ def list_eligible_widgets(
144
+ self,
145
+ ) -> list: # TODO can be moved to some util mixin like container class for rpc widgets
146
+ """
147
+ List all widgets that can be added to the dock.
148
+
149
+ Returns:
150
+ list: The list of eligible widgets.
151
+ """
152
+ return list(RPCWidgetHandler.widget_classes.keys())
153
+
154
+ def add_widget_bec(
155
+ self,
156
+ widget_type: str,
157
+ row=None,
158
+ col=0,
159
+ rowspan=1,
160
+ colspan=1,
161
+ shift: Literal["down", "up", "left", "right"] = "down",
162
+ ):
163
+ """
164
+ Add a widget to the dock.
165
+
166
+ Args:
167
+ widget_type(str): The widget to add. Only BEC RPC widgets from RPCWidgetHandler are allowed.
168
+ row(int): The row to add the widget to. If None, the widget will be added to the next available row.
169
+ col(int): The column to add the widget to.
170
+ rowspan(int): The number of rows the widget should span.
171
+ colspan(int): The number of columns the widget should span.
172
+ shift(Literal["down", "up", "left", "right"]): The direction to shift the widgets if the position is occupied.
173
+ """
174
+ if row is None:
175
+ row = self.layout.rowCount()
176
+
177
+ if self.layout_manager.is_position_occupied(row, col):
178
+ self.layout_manager.shift_widgets(shift, start_row=row)
179
+
180
+ widget = RPCWidgetHandler.create_widget(widget_type)
181
+ self.addWidget(widget, row=row, col=col, rowspan=rowspan, colspan=colspan)
182
+
183
+ return widget
184
+
185
+ def add_widget(
186
+ self,
187
+ widget: QWidget,
188
+ row=None,
189
+ col=0,
190
+ rowspan=1,
191
+ colspan=1,
192
+ shift: Literal["down", "up", "left", "right"] = "down",
193
+ ):
194
+ """
195
+ Add a widget to the dock.
196
+
197
+ Args:
198
+ widget(QWidget): The widget to add.
199
+ row(int): The row to add the widget to. If None, the widget will be added to the next available row.
200
+ col(int): The column to add the widget to.
201
+ rowspan(int): The number of rows the widget should span.
202
+ colspan(int): The number of columns the widget should span.
203
+ shift(Literal["down", "up", "left", "right"]): The direction to shift the widgets if the position is occupied.
204
+ """
205
+ if row is None:
206
+ row = self.layout.rowCount()
207
+
208
+ if self.layout_manager.is_position_occupied(row, col):
209
+ self.layout_manager.shift_widgets(shift, start_row=row)
210
+
211
+ self.addWidget(widget, row=row, col=col, rowspan=rowspan, colspan=colspan)
212
+
213
+ def move_widget(self, widget: QWidget, new_row: int, new_col: int):
214
+ """
215
+ Move a widget to a new position in the layout.
216
+
217
+ Args:
218
+ widget(QWidget): The widget to move.
219
+ new_row(int): The new row to move the widget to.
220
+ new_col(int): The new column to move the widget to.
221
+ """
222
+ self.layout_manager.move_widget(widget, new_row, new_col)
223
+
224
+ def attach(self):
225
+ """
226
+ Attach the dock to the parent dock area.
227
+ """
228
+ self.parent_dock_area.removeTempArea(self.area)
229
+
230
+ def detach(self):
231
+ """
232
+ Detach the dock from the parent dock area.
233
+ """
234
+ self.float()
235
+
236
+ def remove_widget(self, widget_rpc_id: str):
237
+ """
238
+ Remove a widget from the dock.
239
+
240
+ Args:
241
+ widget_rpc_id(str): The ID of the widget to remove.
242
+ """
243
+ widget = self.rpc_register.get_rpc_by_id(widget_rpc_id)
244
+ self.layout.removeWidget(widget)
245
+ widget.close()
246
+
247
+ def remove(self):
248
+ """
249
+ Remove the dock from the parent dock area.
250
+ """
251
+ # self.cleanup()
252
+ self.parent_dock_area.remove_dock(self.name())
253
+
254
+ def cleanup(self):
255
+ """
256
+ Clean up the dock, including all its widgets.
257
+ """
258
+ for widget in self.widgets:
259
+ if hasattr(widget, "cleanup"):
260
+ widget.cleanup()
261
+ super().cleanup()
262
+
263
+ def close(self):
264
+ """
265
+ Close the dock area and cleanup.
266
+ Has to be implemented to overwrite pyqtgraph event accept in Container close.
267
+ """
268
+ self.cleanup()
269
+ super().close()
@@ -0,0 +1,220 @@
1
+ from __future__ import annotations
2
+
3
+ from typing import Literal, Optional
4
+ from weakref import WeakValueDictionary
5
+
6
+ from pydantic import Field
7
+ from pyqtgraph.dockarea.DockArea import DockArea
8
+ from qtpy.QtCore import Qt
9
+ from qtpy.QtGui import QPainter, QPaintEvent
10
+ from qtpy.QtWidgets import QWidget
11
+
12
+ from bec_widgets.utils import BECConnector, ConnectionConfig, WidgetContainerUtils
13
+
14
+ from .dock import BECDock, DockConfig
15
+
16
+
17
+ class DockAreaConfig(ConnectionConfig):
18
+ docks: dict[str, DockConfig] = Field({}, description="The docks in the dock area.")
19
+
20
+
21
+ class BECDockArea(BECConnector, DockArea):
22
+ USER_ACCESS = [
23
+ "panels",
24
+ "save_state",
25
+ "remove_dock",
26
+ "restore_state",
27
+ "add_dock",
28
+ "clear_all",
29
+ "detach_dock",
30
+ "attach_all",
31
+ "get_all_rpc",
32
+ ]
33
+
34
+ def __init__(
35
+ self,
36
+ parent: QWidget | None = None,
37
+ config: DockAreaConfig | None = None,
38
+ client=None,
39
+ gui_id: str = None,
40
+ ) -> None:
41
+ if config is None:
42
+ config = DockAreaConfig(widget_class=self.__class__.__name__)
43
+ else:
44
+ if isinstance(config, dict):
45
+ config = DockAreaConfig(**config)
46
+ self.config = config
47
+ super().__init__(client=client, config=config, gui_id=gui_id)
48
+ DockArea.__init__(self, parent=parent)
49
+
50
+ self._instructions_visible = True
51
+
52
+ def paintEvent(self, event: QPaintEvent):
53
+ super().paintEvent(event)
54
+ if self._instructions_visible:
55
+ painter = QPainter(self)
56
+ painter.drawText(self.rect(), Qt.AlignCenter, "Add docks using 'add_dock' method")
57
+
58
+ @property
59
+ def panels(self) -> dict:
60
+ """
61
+ Get the docks in the dock area.
62
+ Returns:
63
+ dock_dict(dict): The docks in the dock area.
64
+ """
65
+ return dict(self.docks)
66
+
67
+ @panels.setter
68
+ def panels(self, value: dict):
69
+
70
+ self.docks = WeakValueDictionary(value)
71
+
72
+ def restore_state(
73
+ self,
74
+ state: dict = None,
75
+ missing: Literal["ignore", "error"] = "ignore",
76
+ extra="bottom",
77
+ ):
78
+ """
79
+ Restore the state of the dock area. If no state is provided, the last state is restored.
80
+ Args:
81
+ state(dict): The state to restore.
82
+ missing(Literal['ignore','error']): What to do if a dock is missing.
83
+ 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.
84
+ """
85
+ if state is None:
86
+ state = self._last_state
87
+ self.restoreState(state, missing=missing, extra=extra)
88
+
89
+ def save_state(self) -> dict:
90
+ """
91
+ Save the state of the dock area.
92
+ Returns:
93
+ dict: The state of the dock area.
94
+ """
95
+ self._last_state = self.saveState()
96
+ return self._last_state
97
+
98
+ def remove_dock(self, name: str):
99
+ """
100
+ Remove a dock by name and ensure it is properly closed and cleaned up.
101
+ Args:
102
+ name(str): The name of the dock to remove.
103
+ """
104
+ dock = self.docks.pop(name, None)
105
+ if dock:
106
+ dock.close()
107
+ if len(self.docks) <= 1:
108
+ for dock in self.docks.values():
109
+ dock.hide_title_bar()
110
+
111
+ else:
112
+ raise ValueError(f"Dock with name {name} does not exist.")
113
+
114
+ def add_dock(
115
+ self,
116
+ name: str = None,
117
+ position: Literal["bottom", "top", "left", "right", "above", "below"] = None,
118
+ relative_to: BECDock | None = None,
119
+ closable: bool = False,
120
+ prefix: str = "dock",
121
+ widget: str | QWidget | None = None,
122
+ row: int = None,
123
+ col: int = 0,
124
+ rowspan: int = 1,
125
+ colspan: int = 1,
126
+ ) -> BECDock:
127
+ """
128
+ Add a dock to the dock area. Dock has QGridLayout as layout manager by default.
129
+
130
+ Args:
131
+ name(str): The name of the dock to be displayed and for further references. Has to be unique.
132
+ position(Literal["bottom", "top", "left", "right", "above", "below"]): The position of the dock.
133
+ relative_to(BECDock): The dock to which the new dock should be added relative to.
134
+ closable(bool): Whether the dock is closable.
135
+ prefix(str): The prefix for the dock name if no name is provided.
136
+ widget(str|QWidget|None): The widget to be added to the dock. While using RPC, only BEC RPC widgets from RPCWidgetHandler are allowed.
137
+ row(int): The row of the added widget.
138
+ col(int): The column of the added widget.
139
+ rowspan(int): The rowspan of the added widget.
140
+ colspan(int): The colspan of the added widget.
141
+ Returns:
142
+ BECDock: The created dock.
143
+ """
144
+ if name is None:
145
+ name = WidgetContainerUtils.generate_unique_widget_id(
146
+ container=self.docks, prefix=prefix
147
+ )
148
+
149
+ if name in set(self.docks.keys()):
150
+ raise ValueError(f"Dock with name {name} already exists.")
151
+
152
+ if position is None:
153
+ position = "bottom"
154
+
155
+ dock = BECDock(name=name, parent_dock_area=self, closable=closable)
156
+ dock.config.position = position
157
+ self.config.docks[name] = dock.config
158
+
159
+ self.addDock(dock=dock, position=position, relativeTo=relative_to)
160
+
161
+ if len(self.docks) <= 1:
162
+ dock.hide_title_bar()
163
+ elif len(self.docks) > 1:
164
+ for dock in self.docks.values():
165
+ dock.show_title_bar()
166
+
167
+ if widget is not None and isinstance(widget, str):
168
+ dock.add_widget_bec(
169
+ widget_type=widget, row=row, col=col, rowspan=rowspan, colspan=colspan
170
+ )
171
+ elif widget is not None and isinstance(widget, QWidget):
172
+ dock.addWidget(widget, row=row, col=col, rowspan=rowspan, colspan=colspan)
173
+ if self._instructions_visible:
174
+ self._instructions_visible = False
175
+ self.update()
176
+ return dock
177
+
178
+ def detach_dock(self, dock_name: str) -> BECDock:
179
+ """
180
+ Undock a dock from the dock area.
181
+ Args:
182
+ dock_name(str): The dock to undock.
183
+
184
+ Returns:
185
+ BECDock: The undocked dock.
186
+ """
187
+ dock = self.docks[dock_name]
188
+ self.floatDock(dock)
189
+ return dock
190
+
191
+ def attach_all(self):
192
+ """
193
+ Return all floating docks to the dock area.
194
+ """
195
+ while self.tempAreas:
196
+ for temp_area in self.tempAreas:
197
+ self.removeTempArea(temp_area)
198
+
199
+ def clear_all(self):
200
+ """
201
+ Close all docks and remove all temp areas.
202
+ """
203
+ self.attach_all()
204
+ for dock in dict(self.docks).values():
205
+ dock.remove()
206
+
207
+ def cleanup(self):
208
+ """
209
+ Cleanup the dock area.
210
+ """
211
+ self.clear_all()
212
+ super().cleanup()
213
+
214
+ def close(self):
215
+ """
216
+ Close the dock area and cleanup.
217
+ Has to be implemented to overwrite pyqtgraph event accept in Container close.
218
+ """
219
+ self.cleanup()
220
+ super().close()