bec-widgets 0.62.0__py3-none-any.whl → 0.63.1__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.
CHANGELOG.md CHANGED
@@ -2,6 +2,41 @@
2
2
 
3
3
 
4
4
 
5
+ ## v0.63.1 (2024-06-13)
6
+
7
+ ### Fix
8
+
9
+ * fix: just terminate the remote process in close() instead of communicating
10
+
11
+ The proper finalization sequence will be executed by the remote process
12
+ on SIGTERM ([`9263f8e`](https://gitlab.psi.ch/bec/bec_widgets/-/commit/9263f8ef5c17ae7a007a1a564baf787b39061756))
13
+
14
+
15
+ ## v0.63.0 (2024-06-13)
16
+
17
+ ### Documentation
18
+
19
+ * docs: add documentation ([`bc709c4`](https://gitlab.psi.ch/bec/bec_widgets/-/commit/bc709c4184c985d4e721f9ea7d1b3dad5e9153a7))
20
+
21
+ ### Feature
22
+
23
+ * feat: add textbox widget ([`d9d4e3c`](https://gitlab.psi.ch/bec/bec_widgets/-/commit/d9d4e3c9bf73ab2a5629c2867b50fc91e69489ec))
24
+
25
+ ### Refactor
26
+
27
+ * refactor: add pydantic config, add change_theme ([`6b8432f`](https://gitlab.psi.ch/bec/bec_widgets/-/commit/6b8432f5b20a71175a3537b5f6832b76e3b67d73))
28
+
29
+ ### Test
30
+
31
+ * test: add test for text box ([`b49462a`](https://gitlab.psi.ch/bec/bec_widgets/-/commit/b49462abeb186e56bac79d2ef0b0add1ef28a1a5))
32
+
33
+ ### Unknown
34
+
35
+ * Revert "feat: implement non-polling, interruptible waiting of gui instruction response with timeout"
36
+
37
+ This reverts commit abc6caa2d0b6141dfbe1f3d025f78ae14deddcb3 ([`fe04dd8`](https://gitlab.psi.ch/bec/bec_widgets/-/commit/fe04dd80e59a0e74f7fdea603e0642707ecc7c2a))
38
+
39
+
5
40
  ## v0.62.0 (2024-06-12)
6
41
 
7
42
  ### Feature
@@ -135,31 +170,3 @@
135
170
 
136
171
 
137
172
  ## v0.57.6 (2024-06-06)
138
-
139
- ### Fix
140
-
141
- * fix(bar): docstrings extended ([`edb1775`](https://gitlab.psi.ch/bec/bec_widgets/-/commit/edb1775967c3ff0723d0edad2b764f1ffc832b7c))
142
-
143
-
144
- ## v0.57.5 (2024-06-06)
145
-
146
- ### Documentation
147
-
148
- * docs(figure): docs adjusted to be compatible with new signature ([`c037b87`](https://gitlab.psi.ch/bec/bec_widgets/-/commit/c037b87675af91b26e8c7c60e76622d4ed4cf5d5))
149
-
150
- ### Fix
151
-
152
- * fix(waveform): added .plot method with the same signature as BECFigure.plot ([`8479caf`](https://gitlab.psi.ch/bec/bec_widgets/-/commit/8479caf53a7325788ca264e5bd9aee01f1d4c5a0))
153
-
154
- * fix(plot_base): .plot removed from plot_base.py, because there is no use case for it ([`82e2c89`](https://gitlab.psi.ch/bec/bec_widgets/-/commit/82e2c898d2e26f786b2d481f85c647472675e75b))
155
-
156
- ### Refactor
157
-
158
- * refactor(figure): logic for .add_image and .image consolidated; logic for .add_plot and .plot consolidated ([`52bc322`](https://gitlab.psi.ch/bec/bec_widgets/-/commit/52bc322b2b8d3ef92ff3480e61bddaf32464f976))
159
-
160
-
161
- ## v0.57.4 (2024-06-06)
162
-
163
- ### Fix
164
-
165
- * fix(docks): set_title do update dock internal _name now ([`15cbc21`](https://gitlab.psi.ch/bec/bec_widgets/-/commit/15cbc21e5bb3cf85f5822d44a2b3665b5aa2f346))
PKG-INFO CHANGED
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: bec_widgets
3
- Version: 0.62.0
3
+ Version: 0.63.1
4
4
  Summary: BEC Widgets
5
5
  Project-URL: Bug Tracker, https://gitlab.psi.ch/bec/bec_widgets/issues
6
6
  Project-URL: Homepage, https://gitlab.psi.ch/bec/bec_widgets
bec_widgets/cli/client.py CHANGED
@@ -17,6 +17,7 @@ class Widgets(str, enum.Enum):
17
17
  BECDockArea = "BECDockArea"
18
18
  BECFigure = "BECFigure"
19
19
  SpiralProgressBar = "SpiralProgressBar"
20
+ TextBox = "TextBox"
20
21
  WebsiteWidget = "WebsiteWidget"
21
22
 
22
23
 
@@ -1897,6 +1898,48 @@ class SpiralProgressBar(RPCBase):
1897
1898
  """
1898
1899
 
1899
1900
 
1901
+ class StopButton(RPCBase):
1902
+ @property
1903
+ @rpc_call
1904
+ def config_dict(self) -> "dict":
1905
+ """
1906
+ Get the configuration of the widget.
1907
+
1908
+ Returns:
1909
+ dict: The configuration of the widget.
1910
+ """
1911
+
1912
+ @rpc_call
1913
+ def get_all_rpc(self) -> "dict":
1914
+ """
1915
+ Get all registered RPC objects.
1916
+ """
1917
+
1918
+
1919
+ class TextBox(RPCBase):
1920
+ @rpc_call
1921
+ def set_color(self, background_color: str, font_color: str) -> None:
1922
+ """
1923
+ Set the background color of the Widget.
1924
+
1925
+ Args:
1926
+ background_color (str): The color to set the background in HEX.
1927
+ font_color (str): The color to set the font in HEX.
1928
+ """
1929
+
1930
+ @rpc_call
1931
+ def set_text(self, text: str) -> None:
1932
+ """
1933
+ Set the text of the Widget
1934
+ """
1935
+
1936
+ @rpc_call
1937
+ def set_font_size(self, size: int) -> None:
1938
+ """
1939
+ Set the font size of the text in the Widget.
1940
+ """
1941
+
1942
+
1900
1943
  class WebsiteWidget(RPCBase):
1901
1944
  @rpc_call
1902
1945
  def set_url(self, url: str) -> None:
@@ -14,7 +14,7 @@ from typing import TYPE_CHECKING
14
14
 
15
15
  from bec_lib.endpoints import MessageEndpoints
16
16
  from bec_lib.utils.import_utils import isinstance_based_on_class_name, lazy_import, lazy_import_from
17
- from qtpy.QtCore import QEventLoop, QSocketNotifier, QTimer
17
+ from qtpy.QtCore import QCoreApplication
18
18
 
19
19
  import bec_widgets.cli.client as client
20
20
  from bec_widgets.cli.auto_updates import AutoUpdates
@@ -24,8 +24,6 @@ if TYPE_CHECKING:
24
24
 
25
25
  from bec_widgets.cli.client import BECDockArea, BECFigure
26
26
 
27
- from bec_lib.serialization import MsgpackSerialization
28
-
29
27
  messages = lazy_import("bec_lib.messages")
30
28
  # from bec_lib.connector import MessageObject
31
29
  MessageObject = lazy_import_from("bec_lib.connector", ("MessageObject",))
@@ -176,16 +174,11 @@ class BECGuiClientMixin:
176
174
  """
177
175
  Close the figure.
178
176
  """
179
- if self._process is None:
180
- return
181
- if self.gui_is_alive():
182
- self._run_rpc("close", (), wait_for_rpc_response=True)
183
- else:
184
- self._run_rpc("close", (), wait_for_rpc_response=False)
185
- self._process.terminate()
186
- self._process_output_processing_thread.join()
187
- self._process = None
188
177
  self._client.shutdown()
178
+ if self._process:
179
+ self._process.terminate()
180
+ self._process_output_processing_thread.join()
181
+ self._process = None
189
182
 
190
183
  def print_log(self) -> None:
191
184
  """
@@ -207,48 +200,6 @@ class RPCResponseTimeoutError(Exception):
207
200
  )
208
201
 
209
202
 
210
- class QtRedisMessageWaiter:
211
- def __init__(self, redis_connector, message_to_wait):
212
- self.ev_loop = QEventLoop()
213
- self.response = None
214
- self.connector = redis_connector
215
- self.message_to_wait = message_to_wait
216
- self.pubsub = redis_connector._redis_conn.pubsub()
217
- self.pubsub.subscribe(self.message_to_wait.endpoint)
218
- fd = self.pubsub.connection._sock.fileno()
219
- self.notifier = QSocketNotifier(fd, QSocketNotifier.Read)
220
- self.notifier.activated.connect(self._pubsub_readable)
221
-
222
- def _msg_received(self, msg_obj):
223
- self.response = msg_obj.value
224
- self.ev_loop.quit()
225
-
226
- def wait(self, timeout=1):
227
- timer = QTimer()
228
- timer.singleShot(timeout * 1000, self.ev_loop.quit)
229
- self.ev_loop.exec_()
230
- timer.stop()
231
- self.notifier.setEnabled(False)
232
- self.pubsub.close()
233
- return self.response
234
-
235
- def _pubsub_readable(self, fd):
236
- while True:
237
- msg = self.pubsub.get_message()
238
- if msg:
239
- if msg["type"] == "subscribe":
240
- # get_message buffers, so we may already have the answer
241
- # let's check...
242
- continue
243
- else:
244
- break
245
- else:
246
- return
247
- channel = msg["channel"].decode()
248
- msg = MessageObject(topic=channel, value=MsgpackSerialization.loads(msg["data"]))
249
- self.connector._execute_callback(self._msg_received, msg, {})
250
-
251
-
252
203
  class RPCBase:
253
204
  def __init__(self, gui_id: str = None, config: dict = None, parent=None) -> None:
254
205
  self._client = BECDispatcher().client
@@ -275,7 +226,7 @@ class RPCBase:
275
226
  parent = parent._parent
276
227
  return parent
277
228
 
278
- def _run_rpc(self, method, *args, wait_for_rpc_response=True, timeout=3, **kwargs):
229
+ def _run_rpc(self, method, *args, wait_for_rpc_response=True, **kwargs):
279
230
  """
280
231
  Run the RPC call.
281
232
 
@@ -297,24 +248,16 @@ class RPCBase:
297
248
 
298
249
  # pylint: disable=protected-access
299
250
  receiver = self._root._gui_id
300
- if wait_for_rpc_response:
301
- redis_msg = QtRedisMessageWaiter(
302
- self._client.connector, MessageEndpoints.gui_instruction_response(request_id)
303
- )
304
-
305
251
  self._client.connector.set_and_publish(MessageEndpoints.gui_instructions(receiver), rpc_msg)
306
252
 
307
- if wait_for_rpc_response:
308
- response = redis_msg.wait(timeout)
309
-
310
- if response is None:
311
- raise RPCResponseTimeoutError(request_id, timeout)
312
-
313
- # get class name
314
- if not response.accepted:
315
- raise ValueError(response.message["error"])
316
- msg_result = response.message.get("result")
317
- return self._create_widget_from_msg_result(msg_result)
253
+ if not wait_for_rpc_response:
254
+ return None
255
+ response = self._wait_for_response(request_id)
256
+ # get class name
257
+ if not response.content["accepted"]:
258
+ raise ValueError(response.content["message"]["error"])
259
+ msg_result = response.content["message"].get("result")
260
+ return self._create_widget_from_msg_result(msg_result)
318
261
 
319
262
  def _create_widget_from_msg_result(self, msg_result):
320
263
  if msg_result is None:
@@ -337,6 +280,30 @@ class RPCBase:
337
280
  return cls(parent=self, **msg_result)
338
281
  return msg_result
339
282
 
283
+ def _wait_for_response(self, request_id: str, timeout: int = 5):
284
+ """
285
+ Wait for the response from the server.
286
+
287
+ Args:
288
+ request_id(str): The request ID.
289
+ timeout(int): The timeout in seconds.
290
+
291
+ Returns:
292
+ The response from the server.
293
+ """
294
+ start_time = time.time()
295
+ response = None
296
+
297
+ while response is None and self.gui_is_alive() and (time.time() - start_time) < timeout:
298
+ response = self._client.connector.get(
299
+ MessageEndpoints.gui_instruction_response(request_id)
300
+ )
301
+ QCoreApplication.processEvents() # keep UI responsive (and execute signals/slots)
302
+ if response is None and (time.time() - start_time) >= timeout:
303
+ raise RPCResponseTimeoutError(request_id, timeout)
304
+
305
+ return response
306
+
340
307
  def gui_is_alive(self):
341
308
  """
342
309
  Check if the GUI is alive.
@@ -1,6 +1,7 @@
1
1
  from bec_widgets.utils import BECConnector
2
2
  from bec_widgets.widgets.figure import BECFigure
3
3
  from bec_widgets.widgets.spiral_progress_bar.spiral_progress_bar import SpiralProgressBar
4
+ from bec_widgets.widgets.text_box.text_box import TextBox
4
5
  from bec_widgets.widgets.website.website import WebsiteWidget
5
6
 
6
7
 
@@ -11,6 +12,7 @@ class RPCWidgetHandler:
11
12
  "BECFigure": BECFigure,
12
13
  "SpiralProgressBar": SpiralProgressBar,
13
14
  "Website": WebsiteWidget,
15
+ "TextBox": TextBox,
14
16
  }
15
17
 
16
18
  @staticmethod
@@ -136,18 +136,17 @@ if __name__ == "__main__": # pragma: no cover
136
136
 
137
137
  module_path = os.path.dirname(bec_widgets.__file__)
138
138
 
139
+ bec_dispatcher = BECDispatcher()
140
+ client = bec_dispatcher.client
141
+ client.start()
142
+
139
143
  app = QApplication(sys.argv)
140
144
  app.setApplicationName("Jupyter Console")
141
145
  app.setApplicationDisplayName("Jupyter Console")
142
- qdarktheme.setup_theme("auto")
146
+ # qdarktheme.setup_theme("auto")
143
147
  icon = QIcon()
144
148
  icon.addFile(os.path.join(module_path, "assets", "terminal_icon.png"), size=QSize(48, 48))
145
149
  app.setWindowIcon(icon)
146
-
147
- bec_dispatcher = BECDispatcher()
148
- client = bec_dispatcher.client
149
- client.start()
150
-
151
150
  win = JupyterConsoleWindow()
152
151
  win.show()
153
152
 
@@ -9,7 +9,7 @@ import redis
9
9
  from bec_lib.client import BECClient
10
10
  from bec_lib.redis_connector import MessageObject, RedisConnector
11
11
  from bec_lib.service_config import ServiceConfig
12
- from qtpy.QtCore import QCoreApplication, QObject
12
+ from qtpy.QtCore import QObject
13
13
  from qtpy.QtCore import Signal as pyqtSignal
14
14
 
15
15
  if TYPE_CHECKING:
@@ -71,7 +71,6 @@ class BECDispatcher:
71
71
 
72
72
  _instance = None
73
73
  _initialized = False
74
- qapp = None
75
74
 
76
75
  def __new__(cls, client=None, config: str = None, *args, **kwargs):
77
76
  if cls._instance is None:
@@ -83,9 +82,6 @@ class BECDispatcher:
83
82
  if self._initialized:
84
83
  return
85
84
 
86
- if not QCoreApplication.instance():
87
- BECDispatcher.qapp = QCoreApplication([])
88
-
89
85
  self._slots = collections.defaultdict(set)
90
86
  self.client = client
91
87
 
File without changes
@@ -0,0 +1,127 @@
1
+ import re
2
+
3
+ from pydantic import Field, field_validator
4
+ from qtpy.QtWidgets import QTextEdit
5
+
6
+ from bec_widgets.utils.bec_connector import BECConnector, ConnectionConfig
7
+ from bec_widgets.utils.colors import Colors
8
+
9
+
10
+ class TextBoxConfig(ConnectionConfig):
11
+
12
+ theme: str = Field("dark", description="The theme of the figure widget.")
13
+ font_color: str = Field("#FFF", description="The font color of the text")
14
+ background_color: str = Field("#000", description="The background color of the widget.")
15
+ font_size: int = Field(16, description="The font size of the text in the widget.")
16
+ text: str = Field("", description="The text to display in the widget.")
17
+
18
+ @classmethod
19
+ @field_validator("theme")
20
+ def validate_theme(cls, v):
21
+ """Validate the theme of the figure widget."""
22
+ if v not in ["dark", "light"]:
23
+ raise ValueError("Theme must be either 'dark' or 'light'")
24
+ return v
25
+
26
+ _validate_font_color = field_validator("font_color")(Colors.validate_color)
27
+ _validate_background_color = field_validator("background_color")(Colors.validate_color)
28
+
29
+
30
+ class TextBox(BECConnector, QTextEdit):
31
+
32
+ USER_ACCESS = ["set_color", "set_text", "set_font_size"]
33
+
34
+ def __init__(self, text: str = "", parent=None, client=None, config=None, gui_id=None):
35
+ if config is None:
36
+ config = TextBoxConfig(widget_class=self.__class__.__name__)
37
+ else:
38
+ if isinstance(config, dict):
39
+ config = TextBoxConfig(**config)
40
+ self.config = config
41
+ super().__init__(client=client, config=config, gui_id=gui_id)
42
+ QTextEdit.__init__(self, parent=parent)
43
+
44
+ self.config = config
45
+ self.setReadOnly(True)
46
+ self.setGeometry(self.rect())
47
+ self.set_color(self.config.background_color, self.config.font_color)
48
+ if not text:
49
+ text = "<h1>Welcome to the BEC Widget TextBox</h1><p>A widget that allows user to display text in plain and HTML format.</p><p>This is an example of displaying HTML text.</p>"
50
+ self.set_text(text)
51
+
52
+ def change_theme(self) -> None:
53
+ """
54
+ Change the theme of the figure widget.
55
+ """
56
+ if self.config.theme == "dark":
57
+ theme = "light"
58
+ font_color = "#000"
59
+ background_color = "#FFF"
60
+ else:
61
+ theme = "dark"
62
+ font_color = "#FFF"
63
+ background_color = "#000"
64
+ self.config.theme = theme
65
+ self.set_color(background_color, font_color)
66
+
67
+ def set_color(self, background_color: str, font_color: str) -> None:
68
+ """Set the background color of the widget.
69
+
70
+ Args:
71
+ background_color (str): The color to set the background in HEX.
72
+ font_color (str): The color to set the font in HEX.
73
+
74
+ """
75
+ self.config.background_color = background_color
76
+ self.config.font_color = font_color
77
+ self._update_stylesheet()
78
+
79
+ def set_font_size(self, size: int) -> None:
80
+ """Set the font size of the text in the widget.
81
+
82
+ Args:
83
+ size (int): The font size to set.
84
+ """
85
+ self.config.font_size = size
86
+ self._update_stylesheet()
87
+
88
+ def _update_stylesheet(self):
89
+ """Update the stylesheet of the widget."""
90
+ self.setStyleSheet(
91
+ f"background-color: {self.config.background_color}; color: {self.config.font_color}; font-size: {self.config.font_size}px"
92
+ )
93
+
94
+ def set_text(self, text: str) -> None:
95
+ """Set the text of the widget.
96
+
97
+ Args:
98
+ text (str): The text to set.
99
+ """
100
+ if self.is_html(text):
101
+ self.setHtml(text)
102
+ else:
103
+ self.setPlainText(text)
104
+ self.config.text = text
105
+
106
+ def is_html(self, text: str) -> bool:
107
+ """Check if the text contains HTML tags.
108
+
109
+ Args:
110
+ text (str): The text to check.
111
+
112
+ Returns:
113
+ bool: True if the text contains HTML tags, False otherwise.
114
+ """
115
+ return bool(re.search(r"<[a-zA-Z/][^>]*>", text))
116
+
117
+
118
+ if __name__ == "__main__":
119
+ import sys
120
+
121
+ from qtpy.QtWidgets import QApplication
122
+
123
+ app = QApplication(sys.argv)
124
+
125
+ widget = TextBox()
126
+ widget.show()
127
+ sys.exit(app.exec())
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: bec_widgets
3
- Version: 0.62.0
3
+ Version: 0.63.1
4
4
  Summary: BEC Widgets
5
5
  Project-URL: Bug Tracker, https://gitlab.psi.ch/bec/bec_widgets/issues
6
6
  Project-URL: Homepage, https://gitlab.psi.ch/bec/bec_widgets
@@ -2,11 +2,11 @@
2
2
  .gitlab-ci.yml,sha256=3PU2LONUl10zIOD4UCPQgA6vVBtniabww1MQkTMce7I,7995
3
3
  .pylintrc,sha256=OstrgmEyP0smNFBKoIN5_26-UmNZgMHnbjvAWX0UrLs,18535
4
4
  .readthedocs.yaml,sha256=aSOc277LqXcsTI6lgvm_JY80lMlr69GbPKgivua2cS0,603
5
- CHANGELOG.md,sha256=TUs5Jd_fGHQ6IUvMRmlJlJZTW3dBC8hTsnJ-D8_lzdo,6881
5
+ CHANGELOG.md,sha256=2guAV_VW9TfxaJIOGzjUg0l968nM8kwFLtXNUkop-jY,6936
6
6
  LICENSE,sha256=YRKe85CBRyP7UpEAWwU8_qSIyuy5-l_9C-HKg5Qm8MQ,1511
7
- PKG-INFO,sha256=QoubvfbHPjKGZLAQaC7bj2h7AdPFa8rk6Jj4dMgQ--o,1302
7
+ PKG-INFO,sha256=MNvEiOQ0tVlzDg9MrZ4Jq1HjSIb0l2Lg14-2kN9IgqQ,1302
8
8
  README.md,sha256=y4jB6wvArS7N8_iTbKWnSM_oRAqLA2GqgzUR-FMh5sU,2645
9
- pyproject.toml,sha256=p5YYYOClz-NdVPtwURPrkDjO6EJmjEOt8H-axu7tyvc,2115
9
+ pyproject.toml,sha256=oJZwUnqVMwN3MDK_gvSWFIVyGvKEidp-NyAgJwB_LUQ,2115
10
10
  .git_hooks/pre-commit,sha256=n3RofIZHJl8zfJJIUomcMyYGFi_rwq4CC19z0snz3FI,286
11
11
  .gitlab/issue_templates/bug_report_template.md,sha256=gAuyEwl7XlnebBrkiJ9AqffSNOywmr8vygUFWKTuQeI,386
12
12
  .gitlab/issue_templates/documentation_update_template.md,sha256=FHLdb3TS_D9aL4CYZCjyXSulbaW5mrN2CmwTaeLPbNw,860
@@ -17,22 +17,22 @@ bec_widgets/assets/bec_widgets_icon.png,sha256=K8dgGwIjalDh9PRHUsSQBqgdX7a00nM3i
17
17
  bec_widgets/assets/terminal_icon.png,sha256=bJl7Tft4Fi2uxvuXI8o14uMHnI9eAWKSU2uftXCH9ws,3889
18
18
  bec_widgets/cli/__init__.py,sha256=d0Q6Fn44e7wFfLabDOBxpcJ1DPKWlFunGYDUBmO-4hA,22
19
19
  bec_widgets/cli/auto_updates.py,sha256=DyBV3HnjMSH-cvVkYNcDiYKVf0Xut4Qy2qGQqkW47Bw,4833
20
- bec_widgets/cli/client.py,sha256=6mzmRe_pjuz-3CoBJZkxaa05VzAmPLi_cDJ3uljmw8Y,54363
21
- bec_widgets/cli/client_utils.py,sha256=z85Q0QWtBUf4A1X3ovWgOExKI8qVzIR2hE-lPWEOdpY,11645
20
+ bec_widgets/cli/client.py,sha256=PkKPeR4KJjJP-DOPTq3yYJIuWlwXLHjTyZunCYSsU1I,55332
21
+ bec_widgets/cli/client_utils.py,sha256=tGfe3OKCDxgPHWKbJsM8myHooDwKNa-e5bjqGrtwwLg,10464
22
22
  bec_widgets/cli/generate_cli.py,sha256=DIaGz7nhwef3ebIaP4LtiUC3q7MoM1swJ_e0SgAO2jo,6901
23
23
  bec_widgets/cli/rpc_register.py,sha256=QxXUZu5XNg00Yf5O3UHWOXg3-f_pzKjjoZYMOa-MOJc,2216
24
- bec_widgets/cli/rpc_wigdet_handler.py,sha256=VRLujiICU_Gq0mO1Hr15YwjlxU7OcAzqeoM8QHqSubM,1032
24
+ bec_widgets/cli/rpc_wigdet_handler.py,sha256=1oE2TSbwQdfLEaZiscyDX2eExHsenp2BF5Lwy8PE6LA,1118
25
25
  bec_widgets/cli/server.py,sha256=rsj31Vsx6ayThNe4PQelQFahGjYXFZjfrNyB2fnm6Ro,5737
26
26
  bec_widgets/examples/__init__.py,sha256=WWQ0cu7m8sA4Ehy-DWdTIqSISjaHsbxhsNmNrMnhDZU,202
27
27
  bec_widgets/examples/jupyter_console/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
28
- bec_widgets/examples/jupyter_console/jupyter_console_window.py,sha256=AKIlwcOMBJ8UAIBEhPipc9lDwA25UmmBDEYAfC0L7kU,5338
28
+ bec_widgets/examples/jupyter_console/jupyter_console_window.py,sha256=lViKNC3TOh6_-SztcKaRnYkf-V_EQ9T1cuyORskVX6c,5339
29
29
  bec_widgets/examples/jupyter_console/jupyter_console_window.ui,sha256=2A2mNTUMZBYygz8K4qWzrcjnNqZBMVyeHm26iLZVRWI,1473
30
30
  bec_widgets/examples/motor_movement/__init__.py,sha256=LzPJkxLAxOsZCbXR-fRCPmeYobp7Yqds6tDxW4W1gSw,214
31
31
  bec_widgets/examples/motor_movement/motor_control_compilations.py,sha256=8rpA7a2xVZTDMrx7YQIj3IJew78J1gcVMkHvloS0U_Q,9055
32
32
  bec_widgets/examples/motor_movement/motor_controller.ui,sha256=83XX6NGILwntoUIghvzWnMuGf80O8khK3SduVKTAEFM,29105
33
33
  bec_widgets/utils/__init__.py,sha256=B7OZ2ArjyFaGNh4XYIbk49agnYCz704ltuFSalLCjSA,481
34
34
  bec_widgets/utils/bec_connector.py,sha256=RxHJNF7JjtY5pRbTMu2eQTiRXvoyJ53QuTYxHjZba38,5357
35
- bec_widgets/utils/bec_dispatcher.py,sha256=3zzf7LXCWtxiroUuSdObGexuYfRoq0QY1i3hJ7GmbBE,5726
35
+ bec_widgets/utils/bec_dispatcher.py,sha256=nLdcj2u4dy8-ZR03XioCzr7hBg9Wq4Kw58OU6sDorT4,5593
36
36
  bec_widgets/utils/bec_table.py,sha256=nA2b8ukSeUfquFMAxGrUVOqdrzMoDYD6O_4EYbOG2zk,717
37
37
  bec_widgets/utils/colors.py,sha256=GYSDe0ZxsJSwxvuy-yG2BH17qlf_Sjq8dhDcyp9IhBI,8532
38
38
  bec_widgets/utils/container_utils.py,sha256=m3VUyAYmSWkEwApP9tBvKxPYVtc2kHw4toxIpMryJy4,1495
@@ -87,6 +87,8 @@ bec_widgets/widgets/scan_control/scan_control.py,sha256=B5n2U2iVtTCY3Tx93JyBqzGC
87
87
  bec_widgets/widgets/spiral_progress_bar/__init__.py,sha256=4efbtcqCToMIw5bkQrTzy2TzuBCXvlhuUPh1bYC_Yzg,51
88
88
  bec_widgets/widgets/spiral_progress_bar/ring.py,sha256=7i5oKpW8eUQGvLyKce2-2rlaGDVLec__DoWp6hfJlRw,10524
89
89
  bec_widgets/widgets/spiral_progress_bar/spiral_progress_bar.py,sha256=cMi4g7zNTLrkkzZ9ChiIBTaqigDCYwzrgA2iRmq9dUI,24050
90
+ bec_widgets/widgets/text_box/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
91
+ bec_widgets/widgets/text_box/text_box.py,sha256=kykQ_Zcxh8IGcPEP5-oGGQwoZEpY9vhxRIM8TY8kTYg,4240
90
92
  bec_widgets/widgets/toolbar/__init__.py,sha256=d-TP4_cr_VbpwreMM4ePnfZ5YXsEPQ45ibEf75nuGoE,36
91
93
  bec_widgets/widgets/toolbar/toolbar.py,sha256=e0zCD_0q7K4NVhrzD8001Qvfxt-VhqHTgofchS9NgCM,5125
92
94
  bec_widgets/widgets/website/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -127,9 +129,10 @@ docs/user/widgets/motor.gif,sha256=FtaWdRHx4UZaGJPpq8LNhMMgX4PFcAB6IZ93JCMEh_w,2
127
129
  docs/user/widgets/progress_bar.gif,sha256=5jh0Zw2BBGPuNxszV1DBLJCb4_6glIRX-U2ABjnsK2k,5263592
128
130
  docs/user/widgets/scatter_2D.gif,sha256=yHpsuAUseMafJjI_J5BcOhmE3nu9VFn_Xm9XHzJaH5I,13188862
129
131
  docs/user/widgets/spiral_progress_bar.md,sha256=QTgUDIl6XPuK_HwSfB6sNijZ4bss26biDg6B_mJ8Pxk,2208
132
+ docs/user/widgets/text_box.md,sha256=i40AxKJP0PxrYW0x0up1VIovPFvemsaZoosGjOn4iZE,931
130
133
  docs/user/widgets/w1D.gif,sha256=tuHbleJpl6bJFNNC2OdndF5LF7IyfvlkFCMGZajrQPs,622773
131
134
  docs/user/widgets/website.md,sha256=wfudAupdtHX-Sfritg0xMWXZLLczJ4XwMLNWvu6ww-w,705
132
- docs/user/widgets/widgets.md,sha256=EfrElcZVh9jHt_0kIXdDtc7z3ErYGqwmVMxC_AHSGU0,370
135
+ docs/user/widgets/widgets.md,sha256=NzRfrgd4LWmZHa2Cs_1G59LeY5uAlFdy5aP00AtGAjk,380
133
136
  tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
134
137
  tests/end-2-end/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
135
138
  tests/end-2-end/conftest.py,sha256=taLqiYVzOhJjMre5ypgQjB7wzSXP4soKANW3XfAjems,1773
@@ -154,6 +157,7 @@ tests/unit_tests/test_rpc_register.py,sha256=hECjZEimd440mwRrO0rg7L3PKN7__3DgjmE
154
157
  tests/unit_tests/test_scan_control.py,sha256=7dtGpE0g4FqUhhQeCkyJl-9o7NH3DFZJgEaqDmBYbBc,7551
155
158
  tests/unit_tests/test_spiral_progress_bar.py,sha256=yak3N9-TmEM3lQZPSROL4cAx9mior__se1XADlMScks,12418
156
159
  tests/unit_tests/test_stop_button.py,sha256=hOoWO0emkvd5bR_EExxCnKsiZgXKqf_uIGTwzWLxhDw,704
160
+ tests/unit_tests/test_text_box_widget.py,sha256=cT0uEHt_6d-FwST0A_wE9sFW9E3F_nJbKhuBAeU4yHg,1862
157
161
  tests/unit_tests/test_waveform1d.py,sha256=j9-CCE0BkFVI3Gnv8pjV1gc9HwA5PYG0_ox1oZ60F6w,15272
158
162
  tests/unit_tests/test_website_widget.py,sha256=fBADIJJBAHU4Ro7u95kdemFVNv196UOcuO9oLHuHt8A,761
159
163
  tests/unit_tests/test_widget_io.py,sha256=FeL3ZYSBQnRt6jxj8VGYw1cmcicRQyHKleahw7XIyR0,3475
@@ -163,8 +167,8 @@ tests/unit_tests/test_configs/config_device_no_entry.yaml,sha256=hdvue9KLc_kfNzG
163
167
  tests/unit_tests/test_configs/config_scan.yaml,sha256=vo484BbWOjA_e-h6bTjSV9k7QaQHrlAvx-z8wtY-P4E,1915
164
168
  tests/unit_tests/test_msgs/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
165
169
  tests/unit_tests/test_msgs/available_scans_message.py,sha256=m_z97hIrjHXXMa2Ex-UvsPmTxOYXfjxyJaGkIY6StTY,46532
166
- bec_widgets-0.62.0.dist-info/METADATA,sha256=QoubvfbHPjKGZLAQaC7bj2h7AdPFa8rk6Jj4dMgQ--o,1302
167
- bec_widgets-0.62.0.dist-info/WHEEL,sha256=zEMcRr9Kr03x1ozGwg5v9NQBKn3kndp6LSoSlVg-jhU,87
168
- bec_widgets-0.62.0.dist-info/entry_points.txt,sha256=80s2YKCNziN2ROUYbpDRyEmiejMf_dshmiYCdN7qNsU,70
169
- bec_widgets-0.62.0.dist-info/licenses/LICENSE,sha256=YRKe85CBRyP7UpEAWwU8_qSIyuy5-l_9C-HKg5Qm8MQ,1511
170
- bec_widgets-0.62.0.dist-info/RECORD,,
170
+ bec_widgets-0.63.1.dist-info/METADATA,sha256=MNvEiOQ0tVlzDg9MrZ4Jq1HjSIb0l2Lg14-2kN9IgqQ,1302
171
+ bec_widgets-0.63.1.dist-info/WHEEL,sha256=zEMcRr9Kr03x1ozGwg5v9NQBKn3kndp6LSoSlVg-jhU,87
172
+ bec_widgets-0.63.1.dist-info/entry_points.txt,sha256=80s2YKCNziN2ROUYbpDRyEmiejMf_dshmiYCdN7qNsU,70
173
+ bec_widgets-0.63.1.dist-info/licenses/LICENSE,sha256=YRKe85CBRyP7UpEAWwU8_qSIyuy5-l_9C-HKg5Qm8MQ,1511
174
+ bec_widgets-0.63.1.dist-info/RECORD,,
@@ -0,0 +1,33 @@
1
+ (user.widgets.text_box)=
2
+ # [Text Box Widget](/api_reference/_autosummary/bec_widgets.cli.client.TextBoxWidget)
3
+ **Purpose:**
4
+
5
+ The Text Box Widget is a widget that allows you to display text within the BEC GUI. The widget can be used to display plain text or HTML text.
6
+
7
+ **Key Features:**
8
+
9
+ - set the text to display.
10
+ - automatically detects if the text is plain text or HTML text.
11
+ - set background color and font color.
12
+
13
+ **Code example:**
14
+
15
+ The following code snipped demonstrates how to create a `TextBox` widget using BEC Widgets within BEC.
16
+ ```python
17
+ text_box = gui.add_dock().add_widget("TextBox")
18
+ # set the text to display
19
+ text_box.set_text("Hello, World!")
20
+ # set the background color and font color
21
+ text_box.set_color(backgroud_color="#FFF", font_color="#000")
22
+ # set the text to display as HTML
23
+ text_box.set_text("<h1>Welcome to BEC Widgets</h1><p>This is an example of displaying <strong>HTML</strong> text.</p>")
24
+ ```
25
+
26
+
27
+
28
+
29
+
30
+
31
+
32
+
33
+
@@ -12,6 +12,7 @@ bec_figure/
12
12
  spiral_progress_bar/
13
13
  website/
14
14
  buttons/
15
+ text_box/
15
16
  ```
16
17
 
17
18
 
pyproject.toml CHANGED
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "bec_widgets"
7
- version = "0.62.0"
7
+ version = "0.63.1"
8
8
  description = "BEC Widgets"
9
9
  requires-python = ">=3.10"
10
10
  classifiers = [
@@ -0,0 +1,55 @@
1
+ import re
2
+ from unittest import mock
3
+
4
+ import pytest
5
+
6
+ from bec_widgets.widgets.text_box.text_box import TextBox
7
+
8
+ from .client_mocks import mocked_client
9
+
10
+
11
+ @pytest.fixture
12
+ def text_box_widget(qtbot, mocked_client):
13
+ widget = TextBox(client=mocked_client)
14
+ qtbot.addWidget(widget)
15
+ qtbot.waitExposed(widget)
16
+ yield widget
17
+ widget.close()
18
+
19
+
20
+ def test_textbox_widget(text_box_widget):
21
+ """Test the TextBox widget."""
22
+ text = "Hello World!"
23
+ text_box_widget.set_text(text)
24
+ assert text_box_widget.toPlainText() == text
25
+
26
+ text_box_widget.set_color("#FFDDC1", "#123456")
27
+ text_box_widget.set_font_size(20)
28
+ assert (
29
+ text_box_widget.styleSheet() == "background-color: #FFDDC1; color: #123456; font-size: 20px"
30
+ )
31
+ text_box_widget.set_color("white", "blue")
32
+ text_box_widget.set_font_size(14)
33
+ assert text_box_widget.styleSheet() == "background-color: white; color: blue; font-size: 14px"
34
+ text = "<h1>Welcome to PyQt6</h1><p>This is an example of displaying <strong>HTML</strong> text.</p>"
35
+ with mock.patch.object(text_box_widget, "setHtml") as mocked_set_html:
36
+ text_box_widget.set_text(text)
37
+ assert mocked_set_html.call_count == 1
38
+ assert mocked_set_html.call_args == mock.call(text)
39
+
40
+
41
+ def test_textbox_change_theme(text_box_widget):
42
+ """Test change theme functionaility"""
43
+ # Default is dark theme
44
+ text_box_widget.change_theme()
45
+ assert text_box_widget.config.theme == "light"
46
+ assert (
47
+ text_box_widget.styleSheet()
48
+ == f"background-color: #FFF; color: #000; font-size: {text_box_widget.config.font_size}px"
49
+ )
50
+ text_box_widget.change_theme()
51
+ assert text_box_widget.config.theme == "dark"
52
+ assert (
53
+ text_box_widget.styleSheet()
54
+ == f"background-color: #000; color: #FFF; font-size: {text_box_widget.config.font_size}px"
55
+ )