bec-widgets 2.10.0__py3-none-any.whl → 2.10.2__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.
@@ -14,10 +14,15 @@ jobs:
14
14
 
15
15
  - name: Run black and isort
16
16
  run: |
17
- pip install black isort
18
- pip install -e .[dev]
17
+ pip install uv
18
+ uv pip install --system black isort
19
+ uv pip install --system -e .[dev]
19
20
  black --check --diff --color .
20
21
  isort --check --diff ./
22
+
23
+ - name: Check for disallowed imports from PySide
24
+ run: '! grep -re "from PySide6\." bec_widgets/ | grep -v -e "PySide6.QtDesigner" -e "PySide6.scripts"'
25
+
21
26
  Pylint:
22
27
  runs-on: ubuntu-latest
23
28
 
CHANGELOG.md CHANGED
@@ -1,6 +1,32 @@
1
1
  # CHANGELOG
2
2
 
3
3
 
4
+ ## v2.10.2 (2025-06-03)
5
+
6
+ ### Bug Fixes
7
+
8
+ - Remove unnecessary PySide imports
9
+ ([`1df6c19`](https://github.com/bec-project/bec_widgets/commit/1df6c1925b6ec88df8d7a1a5a79a5ddc6b1161b5))
10
+
11
+ ### Continuous Integration
12
+
13
+ - Check for disallowed imports from PySide
14
+ ([`6b939ac`](https://github.com/bec-project/bec_widgets/commit/6b939ac34d01cdbb0e8e32a0bd4e56cad032e75b))
15
+
16
+
17
+ ## v2.10.1 (2025-06-02)
18
+
19
+ ### Bug Fixes
20
+
21
+ - **console**: Qt console widget deleted
22
+ ([`cd4e90a`](https://github.com/bec-project/bec_widgets/commit/cd4e90a79fcdbc96f4ec23db22375d05a48731db))
23
+
24
+ ### Build System
25
+
26
+ - Pyte removed from dependencies
27
+ ([`a64cf0d`](https://github.com/bec-project/bec_widgets/commit/a64cf0dd871c1419e02d3803c74cc45966baac19))
28
+
29
+
4
30
  ## v2.10.0 (2025-06-02)
5
31
 
6
32
  ### Bug Fixes
PKG-INFO CHANGED
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: bec_widgets
3
- Version: 2.10.0
3
+ Version: 2.10.2
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
@@ -17,7 +17,6 @@ Requires-Dist: isort>=5.13.2,~=5.13
17
17
  Requires-Dist: pydantic~=2.0
18
18
  Requires-Dist: pyqtgraph~=0.13
19
19
  Requires-Dist: pyside6~=6.8.2
20
- Requires-Dist: pyte
21
20
  Requires-Dist: qtconsole>=5.5.1,~=5.5
22
21
  Requires-Dist: qtpy~=2.4
23
22
  Provides-Extra: dev
@@ -1,5 +1,5 @@
1
1
  from bec_lib.logger import bec_logger
2
- from PySide6.QtGui import QCloseEvent
2
+ from qtpy.QtGui import QCloseEvent
3
3
  from qtpy.QtWidgets import QDialog, QDialogButtonBox, QHBoxLayout, QPushButton, QVBoxLayout, QWidget
4
4
 
5
5
  from bec_widgets.utils.error_popups import SafeSlot
@@ -9,7 +9,7 @@ from bec_widgets.utils.plugin_utils import get_custom_classes
9
9
  logger = bec_logger.logger
10
10
 
11
11
  if PYSIDE6:
12
- from PySide6.QtUiTools import QUiLoader
12
+ from qtpy.QtUiTools import QUiLoader
13
13
 
14
14
  class CustomUiLoader(QUiLoader):
15
15
  def __init__(self, baseinstance, custom_widgets: dict | None = None):
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: bec_widgets
3
- Version: 2.10.0
3
+ Version: 2.10.2
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
@@ -17,7 +17,6 @@ Requires-Dist: isort>=5.13.2,~=5.13
17
17
  Requires-Dist: pydantic~=2.0
18
18
  Requires-Dist: pyqtgraph~=0.13
19
19
  Requires-Dist: pyside6~=6.8.2
20
- Requires-Dist: pyte
21
20
  Requires-Dist: qtconsole>=5.5.1,~=5.5
22
21
  Requires-Dist: qtpy~=2.4
23
22
  Provides-Extra: dev
@@ -2,11 +2,11 @@
2
2
  .gitlab-ci.yml,sha256=1nMYldzVk0tFkBWYTcUjumOrdSADASheWOAc0kOFDYs,9509
3
3
  .pylintrc,sha256=eeY8YwSI74oFfq6IYIbCqnx3Vk8ZncKaatv96n_Y8Rs,18544
4
4
  .readthedocs.yaml,sha256=ivqg3HTaOxNbEW3bzWh9MXAkrekuGoNdj0Mj3SdRYuw,639
5
- CHANGELOG.md,sha256=ZCoCGrGicUMSpl9R4w6AlvmPh0TeualolLm0VyLRFX0,292161
5
+ CHANGELOG.md,sha256=kLqgy6w0RZAc0p-MpRNLaWNxn5HJuoRkrurPJ6P4mOc,292878
6
6
  LICENSE,sha256=Daeiu871NcAp8uYi4eB_qHgvypG-HX0ioRQyQxFwjeg,1531
7
- PKG-INFO,sha256=Smer6N4138nsW9L0c5DAvG64Wioj9ku14Joj42MfAMs,1274
7
+ PKG-INFO,sha256=zmW1jybpTovUqp1f3Ag7vvvHALNCC6cgZ5vpo6xtQwg,1254
8
8
  README.md,sha256=oY5Jc1uXehRASuwUJ0umin2vfkFh7tHF-LLruHTaQx0,3560
9
- pyproject.toml,sha256=9G1m-vohVa87nvTCSVSYhLCV-Vm8XlkWFmZXseyMXlo,2903
9
+ pyproject.toml,sha256=aoMeS7K08mG5d7iEmQo_Sx8Y_o6xOxN5Qx2blQt7vbY,2835
10
10
  .git_hooks/pre-commit,sha256=n3RofIZHJl8zfJJIUomcMyYGFi_rwq4CC19z0snz3FI,286
11
11
  .github/pull_request_template.md,sha256=F_cJXzooWMFgMGtLK-7KeGcQt0B4AYFse5oN0zQ9p6g,801
12
12
  .github/ISSUE_TEMPLATE/bug_report.yml,sha256=WdRnt7HGxvsIBLzhkaOWNfg8IJQYa_oV9_F08Ym6znQ,1081
@@ -18,7 +18,7 @@ pyproject.toml,sha256=9G1m-vohVa87nvTCSVSYhLCV-Vm8XlkWFmZXseyMXlo,2903
18
18
  .github/workflows/check_pr.yml,sha256=jKMtYBJTNRAdw_MAS4JzdKVSoNrv8nxUQMqobsYFAXw,903
19
19
  .github/workflows/ci.yml,sha256=OVZt0UxN4wQInuMucuQcKsvHDiz27sLeuQRskxjtkY0,1863
20
20
  .github/workflows/end2end-conda.yml,sha256=yTH-CS8xxQ7kMo4BDpWwOeef1xmsV6gyrgqnFPHTo30,2278
21
- .github/workflows/formatter.yml,sha256=CdUVrRxWyVes9y-Ez3jiCJXqANPJtl6kfpGQxeWMhcQ,1694
21
+ .github/workflows/formatter.yml,sha256=CrYpMfUaZmFFvCk8sK-_7dTtngVxob0Cb1PaJZAZYWw,1918
22
22
  .github/workflows/generate-cli-check.yml,sha256=b6TcK8F5Hy0sFjgXpk0w3BO9eMDZw9WysTl3P7zEPuQ,1742
23
23
  .github/workflows/pytest-matrix.yml,sha256=0gL5wNPJKJF1JapqstlYNYiJ44ko05uaTD7epa7smVw,1834
24
24
  .github/workflows/pytest.yml,sha256=hYOB7XK_79MaiELaTH7zDT-WRw-pRDe4mHyB_WfcGDc,1747
@@ -91,11 +91,11 @@ bec_widgets/utils/round_frame.py,sha256=SLtoPi8sfJvjfK7G_a4_sRBMGivF5fTHNwMLq93-
91
91
  bec_widgets/utils/rpc_decorator.py,sha256=pIvtqySQLnuS7l2Ti_UAe4WX7CRivZnsE5ZdKAihxh0,479
92
92
  bec_widgets/utils/rpc_server.py,sha256=6FqSsjndXwLoL5d-zIHHLMg_U3GyK0aSk5gc1833UGw,10311
93
93
  bec_widgets/utils/serialization.py,sha256=_SO8q0ylC0MWckT9yTbCtF0QNsRoT6ysL8WnN8-GQ-U,1174
94
- bec_widgets/utils/settings_dialog.py,sha256=1z6noC9k6BSIGAw12DvHHiW3LmvPquaGMjWSe5wdmek,4307
94
+ bec_widgets/utils/settings_dialog.py,sha256=jSR6s1DW8KMbvCTM4gab2ESgHja9KTOHU079oOUzW7w,4304
95
95
  bec_widgets/utils/side_panel.py,sha256=enxcQOwJOMfD3MxgPA9mQSKSX_F6Omy2zePncYDW1FA,14193
96
96
  bec_widgets/utils/thread_checker.py,sha256=rDNuA3X6KQyA7JPb67mccTg0z8YkInynLAENQDQpbuE,1607
97
97
  bec_widgets/utils/toolbar.py,sha256=nQM2h5WqTmQDxlU4GMk6OyRJr-0_4VAKSbug7yOUwWY,40769
98
- bec_widgets/utils/ui_loader.py,sha256=E1tAprb5Vqc3gttANwuCaAx07paGMH6ezGbEDbb1-kE,5002
98
+ bec_widgets/utils/ui_loader.py,sha256=c3F1lC1FlRpJ_kUloPE5D8Lo3pbXuj9KgtKEY0DEz34,4999
99
99
  bec_widgets/utils/validator_delegate.py,sha256=Emj1WF6W8Ke1ruBWUfmHdVJpmOSPezuOt4zvQTay_44,442
100
100
  bec_widgets/utils/widget_io.py,sha256=afR7i59fw2WrEQCmkH5dwhz1uNjfMPnJTIhE8NWVBsU,21552
101
101
  bec_widgets/utils/widget_state_manager.py,sha256=tzrxVmnGa6IHSEdeh-R68aQ934BsuS9nLQbwYQ78J90,7651
@@ -219,11 +219,6 @@ bec_widgets/widgets/dap/lmfit_dialog/lmfit_dialog_vertical.ui,sha256=BVm0Xydqbgm
219
219
  bec_widgets/widgets/dap/lmfit_dialog/register_lm_fit_dialog.py,sha256=7tB1gsvv310_kVuKf2u4EdSR4F1posm7QCrWH5Kih-Q,480
220
220
  bec_widgets/widgets/editors/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
221
221
  bec_widgets/widgets/editors/dict_backed_table.py,sha256=u8n5diNaKY-UBb5vkpDSRtSI7w7g1096g37co8nxA8A,6422
222
- bec_widgets/widgets/editors/console/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
223
- bec_widgets/widgets/editors/console/console.py,sha256=OB5vvqpYNhUGWzn5Dx5rR1n0rQulef5Yv-by5RGjr2w,29323
224
- bec_widgets/widgets/editors/console/console.pyproject,sha256=JcoDuZG03g1Bxkd3Aipo7jjLexujfbibIZqXHIgLSWc,26
225
- bec_widgets/widgets/editors/console/console_plugin.py,sha256=EvFTruYDVHiS4pHIwZnuEvJhS9eQoktuB_k5mcPuEts,1357
226
- bec_widgets/widgets/editors/console/register_console.py,sha256=zoF-i3R9sRGzb85sdoxVunebYOfOD53fkCELTPtrFRc,471
227
222
  bec_widgets/widgets/editors/jupyter_console/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
228
223
  bec_widgets/widgets/editors/jupyter_console/jupyter_console.py,sha256=-e7HQOECeH5eDrJYh4BFIzRL78LDkooU4otabyN0aX4,2343
229
224
  bec_widgets/widgets/editors/scan_metadata/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -408,8 +403,8 @@ bec_widgets/widgets/utility/visual/dark_mode_button/dark_mode_button.py,sha256=O
408
403
  bec_widgets/widgets/utility/visual/dark_mode_button/dark_mode_button.pyproject,sha256=Lbi9zb6HNlIq14k6hlzR-oz6PIFShBuF7QxE6d87d64,34
409
404
  bec_widgets/widgets/utility/visual/dark_mode_button/dark_mode_button_plugin.py,sha256=CzChz2SSETYsR8-36meqWnsXCT-FIy_J_xeU5coWDY8,1350
410
405
  bec_widgets/widgets/utility/visual/dark_mode_button/register_dark_mode_button.py,sha256=rMpZ1CaoucwobgPj1FuKTnt07W82bV1GaSYdoqcdMb8,521
411
- bec_widgets-2.10.0.dist-info/METADATA,sha256=Smer6N4138nsW9L0c5DAvG64Wioj9ku14Joj42MfAMs,1274
412
- bec_widgets-2.10.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
413
- bec_widgets-2.10.0.dist-info/entry_points.txt,sha256=dItMzmwA1wizJ1Itx15qnfJ0ZzKVYFLVJ1voxT7K7D4,214
414
- bec_widgets-2.10.0.dist-info/licenses/LICENSE,sha256=Daeiu871NcAp8uYi4eB_qHgvypG-HX0ioRQyQxFwjeg,1531
415
- bec_widgets-2.10.0.dist-info/RECORD,,
406
+ bec_widgets-2.10.2.dist-info/METADATA,sha256=zmW1jybpTovUqp1f3Ag7vvvHALNCC6cgZ5vpo6xtQwg,1254
407
+ bec_widgets-2.10.2.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
408
+ bec_widgets-2.10.2.dist-info/entry_points.txt,sha256=dItMzmwA1wizJ1Itx15qnfJ0ZzKVYFLVJ1voxT7K7D4,214
409
+ bec_widgets-2.10.2.dist-info/licenses/LICENSE,sha256=Daeiu871NcAp8uYi4eB_qHgvypG-HX0ioRQyQxFwjeg,1531
410
+ bec_widgets-2.10.2.dist-info/RECORD,,
pyproject.toml CHANGED
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "bec_widgets"
7
- version = "2.10.0"
7
+ version = "2.10.2"
8
8
  description = "BEC Widgets"
9
9
  requires-python = ">=3.10"
10
10
  classifiers = [
@@ -21,7 +21,6 @@ dependencies = [
21
21
  "pydantic~=2.0",
22
22
  "pyqtgraph~=0.13",
23
23
  "PySide6~=6.8.2",
24
- "pyte", # needed for vt100 console
25
24
  "qtconsole~=5.5, >=5.5.1", # needed for jupyter console
26
25
  "qtpy~=2.4",
27
26
  ]
File without changes
@@ -1,870 +0,0 @@
1
- """
2
- BECConsole is a Qt widget that runs a Bash shell.
3
-
4
- BECConsole VT100 emulation is powered by Pyte,
5
- (https://github.com/selectel/pyte).
6
- """
7
-
8
- import collections
9
- import fcntl
10
- import html
11
- import os
12
- import pty
13
- import re
14
- import signal
15
- import sys
16
- import time
17
-
18
- import pyte
19
- from pygments.token import Token
20
- from pyte.screens import History
21
- from qtpy import QtCore, QtGui, QtWidgets
22
- from qtpy.QtCore import Property as pyqtProperty
23
- from qtpy.QtCore import QSize, QSocketNotifier, Qt, QTimer
24
- from qtpy.QtCore import Signal as pyqtSignal
25
- from qtpy.QtGui import QClipboard, QColor, QPalette, QTextCursor
26
- from qtpy.QtWidgets import QApplication, QHBoxLayout, QScrollBar, QSizePolicy
27
-
28
- from bec_widgets.utils.error_popups import SafeSlot as Slot
29
-
30
- ansi_colors = {
31
- "black": "#000000",
32
- "red": "#CD0000",
33
- "green": "#00CD00",
34
- "brown": "#996633", # Brown, replacing the yellow
35
- "blue": "#0000EE",
36
- "magenta": "#CD00CD",
37
- "cyan": "#00CDCD",
38
- "white": "#E5E5E5",
39
- "brightblack": "#7F7F7F",
40
- "brightred": "#FF0000",
41
- "brightgreen": "#00FF00",
42
- "brightyellow": "#FFFF00",
43
- "brightblue": "#5C5CFF",
44
- "brightmagenta": "#FF00FF",
45
- "brightcyan": "#00FFFF",
46
- "brightwhite": "#FFFFFF",
47
- }
48
-
49
- control_keys_mapping = {
50
- QtCore.Qt.Key_A: b"\x01", # Ctrl-A
51
- QtCore.Qt.Key_B: b"\x02", # Ctrl-B
52
- QtCore.Qt.Key_C: b"\x03", # Ctrl-C
53
- QtCore.Qt.Key_D: b"\x04", # Ctrl-D
54
- QtCore.Qt.Key_E: b"\x05", # Ctrl-E
55
- QtCore.Qt.Key_F: b"\x06", # Ctrl-F
56
- QtCore.Qt.Key_G: b"\x07", # Ctrl-G (Bell)
57
- QtCore.Qt.Key_H: b"\x08", # Ctrl-H (Backspace)
58
- QtCore.Qt.Key_I: b"\x09", # Ctrl-I (Tab)
59
- QtCore.Qt.Key_J: b"\x0a", # Ctrl-J (Line Feed)
60
- QtCore.Qt.Key_K: b"\x0b", # Ctrl-K (Vertical Tab)
61
- QtCore.Qt.Key_L: b"\x0c", # Ctrl-L (Form Feed)
62
- QtCore.Qt.Key_M: b"\x0d", # Ctrl-M (Carriage Return)
63
- QtCore.Qt.Key_N: b"\x0e", # Ctrl-N
64
- QtCore.Qt.Key_O: b"\x0f", # Ctrl-O
65
- QtCore.Qt.Key_P: b"\x10", # Ctrl-P
66
- QtCore.Qt.Key_Q: b"\x11", # Ctrl-Q
67
- QtCore.Qt.Key_R: b"\x12", # Ctrl-R
68
- QtCore.Qt.Key_S: b"\x13", # Ctrl-S
69
- QtCore.Qt.Key_T: b"\x14", # Ctrl-T
70
- QtCore.Qt.Key_U: b"\x15", # Ctrl-U
71
- QtCore.Qt.Key_V: b"\x16", # Ctrl-V
72
- QtCore.Qt.Key_W: b"\x17", # Ctrl-W
73
- QtCore.Qt.Key_X: b"\x18", # Ctrl-X
74
- QtCore.Qt.Key_Y: b"\x19", # Ctrl-Y
75
- QtCore.Qt.Key_Z: b"\x1a", # Ctrl-Z
76
- QtCore.Qt.Key_Escape: b"\x1b", # Ctrl-Escape
77
- QtCore.Qt.Key_Backslash: b"\x1c", # Ctrl-\
78
- QtCore.Qt.Key_Underscore: b"\x1f", # Ctrl-_
79
- }
80
-
81
- normal_keys_mapping = {
82
- QtCore.Qt.Key_Return: b"\n",
83
- QtCore.Qt.Key_Space: b" ",
84
- QtCore.Qt.Key_Enter: b"\n",
85
- QtCore.Qt.Key_Tab: b"\t",
86
- QtCore.Qt.Key_Backspace: b"\x08",
87
- QtCore.Qt.Key_Home: b"\x47",
88
- QtCore.Qt.Key_End: b"\x4f",
89
- QtCore.Qt.Key_Left: b"\x02",
90
- QtCore.Qt.Key_Up: b"\x10",
91
- QtCore.Qt.Key_Right: b"\x06",
92
- QtCore.Qt.Key_Down: b"\x0e",
93
- QtCore.Qt.Key_PageUp: b"\x49",
94
- QtCore.Qt.Key_PageDown: b"\x51",
95
- QtCore.Qt.Key_F1: b"\x1b\x31",
96
- QtCore.Qt.Key_F2: b"\x1b\x32",
97
- QtCore.Qt.Key_F3: b"\x1b\x33",
98
- QtCore.Qt.Key_F4: b"\x1b\x34",
99
- QtCore.Qt.Key_F5: b"\x1b\x35",
100
- QtCore.Qt.Key_F6: b"\x1b\x36",
101
- QtCore.Qt.Key_F7: b"\x1b\x37",
102
- QtCore.Qt.Key_F8: b"\x1b\x38",
103
- QtCore.Qt.Key_F9: b"\x1b\x39",
104
- QtCore.Qt.Key_F10: b"\x1b\x30",
105
- QtCore.Qt.Key_F11: b"\x45",
106
- QtCore.Qt.Key_F12: b"\x46",
107
- }
108
-
109
-
110
- def QtKeyToAscii(event):
111
- """
112
- Convert the Qt key event to the corresponding ASCII sequence for
113
- the terminal. This works fine for standard alphanumerical characters, but
114
- most other characters require terminal specific control sequences.
115
-
116
- The conversion below works for TERM="linux" terminals.
117
- """
118
- if sys.platform == "darwin":
119
- # special case for MacOS
120
- # /!\ Qt maps ControlModifier to CMD
121
- # CMD-C, CMD-V for copy/paste
122
- # CTRL-C and other modifiers -> key mapping
123
- if event.modifiers() == QtCore.Qt.MetaModifier:
124
- if event.key() == Qt.Key_Backspace:
125
- return control_keys_mapping.get(Qt.Key_W)
126
- return control_keys_mapping.get(event.key())
127
- elif event.modifiers() == QtCore.Qt.ControlModifier:
128
- if event.key() == Qt.Key_C:
129
- # copy
130
- return "copy"
131
- elif event.key() == Qt.Key_V:
132
- # paste
133
- return "paste"
134
- return None
135
- else:
136
- return normal_keys_mapping.get(event.key(), event.text().encode("utf8"))
137
- if event.modifiers() == QtCore.Qt.ControlModifier:
138
- return control_keys_mapping.get(event.key())
139
- else:
140
- return normal_keys_mapping.get(event.key(), event.text().encode("utf8"))
141
-
142
-
143
- class Screen(pyte.HistoryScreen):
144
- def __init__(self, stdin_fd, cols, rows, historyLength):
145
- super().__init__(cols, rows, historyLength, ratio=1 / rows)
146
- self._fd = stdin_fd
147
-
148
- def write_process_input(self, data):
149
- """Response to CPR request (for example),
150
- this can be for other requests
151
- """
152
- try:
153
- os.write(self._fd, data.encode("utf-8"))
154
- except (IOError, OSError):
155
- pass
156
-
157
- def resize(self, lines, columns):
158
- lines = lines or self.lines
159
- columns = columns or self.columns
160
-
161
- if lines == self.lines and columns == self.columns:
162
- return # No changes.
163
-
164
- self.dirty.clear()
165
- self.dirty.update(range(lines))
166
-
167
- self.save_cursor()
168
- if lines < self.lines:
169
- if lines <= self.cursor.y:
170
- nlines_to_move_up = self.lines - lines
171
- for i in range(nlines_to_move_up):
172
- line = self.buffer[i] # .pop(0)
173
- self.history.top.append(line)
174
- self.cursor_position(0, 0)
175
- self.delete_lines(nlines_to_move_up)
176
- self.restore_cursor()
177
- self.cursor.y -= nlines_to_move_up
178
- else:
179
- self.restore_cursor()
180
-
181
- self.lines, self.columns = lines, columns
182
- self.history = History(
183
- self.history.top,
184
- self.history.bottom,
185
- 1 / self.lines,
186
- self.history.size,
187
- self.history.position,
188
- )
189
- self.set_margins()
190
-
191
-
192
- class Backend(QtCore.QObject):
193
- """
194
- Poll Bash.
195
-
196
- This class will run as a qsocketnotifier (started in ``_TerminalWidget``) and poll the
197
- file descriptor of the Bash terminal.
198
- """
199
-
200
- # Signals to communicate with ``_TerminalWidget``.
201
- dataReady = pyqtSignal(object)
202
- processExited = pyqtSignal()
203
-
204
- def __init__(self, fd, cols, rows):
205
- super().__init__()
206
-
207
- # File descriptor that connects to Bash process.
208
- self.fd = fd
209
-
210
- # Setup Pyte (hard coded display size for now).
211
- self.screen = Screen(self.fd, cols, rows, 10000)
212
- self.stream = pyte.ByteStream()
213
- self.stream.attach(self.screen)
214
-
215
- self.notifier = QSocketNotifier(fd, QSocketNotifier.Read)
216
- self.notifier.activated.connect(self._fd_readable)
217
-
218
- def _fd_readable(self):
219
- """
220
- Poll the Bash output, run it through Pyte, and notify
221
- """
222
- # Read the shell output until the file descriptor is closed.
223
- try:
224
- out = os.read(self.fd, 2**16)
225
- except OSError:
226
- self.processExited.emit()
227
- self.notifier.setEnabled(False)
228
- return
229
-
230
- # Feed output into Pyte's state machine and send the new screen
231
- # output to the GUI
232
- self.stream.feed(out)
233
- self.dataReady.emit(self.screen)
234
-
235
-
236
- class BECConsole(QtWidgets.QWidget):
237
- """Container widget for the terminal text area"""
238
-
239
- PLUGIN = True
240
- ICON_NAME = "terminal"
241
-
242
- prompt = pyqtSignal(bool)
243
-
244
- def __init__(self, parent=None, cols=132):
245
- super().__init__(parent)
246
-
247
- self.term = _TerminalWidget(self, cols, rows=43)
248
- self.term.prompt.connect(self.prompt) # forward signal from term to this widget
249
-
250
- self.scroll_bar = QScrollBar(Qt.Vertical, self)
251
- # self.scroll_bar.hide()
252
- layout = QHBoxLayout(self)
253
- layout.addWidget(self.term)
254
- layout.addWidget(self.scroll_bar)
255
- layout.setAlignment(Qt.AlignLeft | Qt.AlignTop)
256
- layout.setContentsMargins(0, 0, 0, 0)
257
- self.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Expanding)
258
-
259
- pal = QPalette()
260
- self.set_bgcolor(pal.window().color())
261
- self.set_fgcolor(pal.windowText().color())
262
- self.term.set_scroll_bar(self.scroll_bar)
263
- self.set_cmd("bec --nogui")
264
-
265
- self._check_designer_timer = QTimer()
266
- self._check_designer_timer.timeout.connect(self.check_designer)
267
- self._check_designer_timer.start(1000)
268
-
269
- def minimumSizeHint(self):
270
- size = self.term.sizeHint()
271
- size.setWidth(size.width() + self.scroll_bar.width())
272
- return size
273
-
274
- def sizeHint(self):
275
- return self.minimumSizeHint()
276
-
277
- def check_designer(self, calls={"n": 0}):
278
- calls["n"] += 1
279
- if self.term.fd is not None:
280
- # already started
281
- self._check_designer_timer.stop()
282
- elif self.window().windowTitle().endswith("[Preview]"):
283
- # assuming Designer preview -> start
284
- self._check_designer_timer.stop()
285
- self.term.start()
286
- elif calls["n"] >= 3:
287
- # assuming not in Designer -> stop checking
288
- self._check_designer_timer.stop()
289
-
290
- def get_rows(self):
291
- return self.term.rows
292
-
293
- def set_rows(self, rows):
294
- self.term.rows = rows
295
- self.adjustSize()
296
- self.updateGeometry()
297
-
298
- def get_cols(self):
299
- return self.term.cols
300
-
301
- def set_cols(self, cols):
302
- self.term.cols = cols
303
- self.adjustSize()
304
- self.updateGeometry()
305
-
306
- def get_bgcolor(self):
307
- return QColor.fromString(self.term.bg_color)
308
-
309
- def set_bgcolor(self, color):
310
- self.term.bg_color = color.name(QColor.HexRgb)
311
-
312
- def get_fgcolor(self):
313
- return QColor.fromString(self.term.fg_color)
314
-
315
- def set_fgcolor(self, color):
316
- self.term.fg_color = color.name(QColor.HexRgb)
317
-
318
- def get_cmd(self):
319
- return self.term._cmd
320
-
321
- def set_cmd(self, cmd):
322
- self.term._cmd = cmd
323
- if self.term.fd is None:
324
- # not started yet
325
- self.term.clear()
326
- self.term.appendHtml(f"<h2>BEC Console - {repr(cmd)}</h2>")
327
-
328
- def start(self, deactivate_ctrl_d=True):
329
- self.term.start(deactivate_ctrl_d=deactivate_ctrl_d)
330
-
331
- def push(self, text, hit_return=False):
332
- """Push some text to the terminal"""
333
- return self.term.push(text, hit_return=hit_return)
334
-
335
- def execute_command(self, command):
336
- self.push(command, hit_return=True)
337
-
338
- def set_prompt_tokens(self, *tokens):
339
- """Prepare regexp to identify prompt, based on tokens
340
-
341
- Tokens are returned from get_ipython().prompts.in_prompt_tokens()
342
- """
343
- regex_parts = []
344
- for token_type, token_value in tokens:
345
- if token_type == Token.PromptNum: # Handle dynamic prompt number
346
- regex_parts.append(r"[\d\?]+") # Match one or more digits or '?'
347
- else:
348
- # Escape other prompt parts (e.g., "In [", "]: ")
349
- if not token_value:
350
- regex_parts.append(".+?") # arbitrary string
351
- else:
352
- regex_parts.append(re.escape(token_value))
353
-
354
- # Combine into a single regex
355
- prompt_pattern = "".join(regex_parts)
356
- self.term._prompt_re = re.compile(prompt_pattern + r"\s*$")
357
-
358
- def terminate(self, timeout=10):
359
- self.term.stop(timeout=timeout)
360
-
361
- def send_ctrl_c(self, timeout=None):
362
- self.term.send_ctrl_c(timeout)
363
-
364
- cols = pyqtProperty(int, get_cols, set_cols)
365
- rows = pyqtProperty(int, get_rows, set_rows)
366
- bgcolor = pyqtProperty(QColor, get_bgcolor, set_bgcolor)
367
- fgcolor = pyqtProperty(QColor, get_fgcolor, set_fgcolor)
368
- cmd = pyqtProperty(str, get_cmd, set_cmd)
369
-
370
-
371
- class _TerminalWidget(QtWidgets.QPlainTextEdit):
372
- """
373
- Start ``Backend`` process and render Pyte output as text.
374
- """
375
-
376
- prompt = pyqtSignal(bool)
377
-
378
- def __init__(self, parent, cols=125, rows=50, **kwargs):
379
- # regexp to match prompt
380
- self._prompt_re = None
381
- # last prompt
382
- self._prompt_str = None
383
- # process pid
384
- self.pid = None
385
- # file descriptor to communicate with the subprocess
386
- self.fd = None
387
- self.backend = None
388
- # command to execute
389
- self._cmd = ""
390
- # should ctrl-d be deactivated ? (prevent Python exit)
391
- self._deactivate_ctrl_d = False
392
-
393
- # Default colors
394
- pal = QPalette()
395
- self._fg_color = pal.text().color().name()
396
- self._bg_color = pal.base().color().name()
397
-
398
- # Specify the terminal size in terms of lines and columns.
399
- self._rows = rows
400
- self._cols = cols
401
- self.output = collections.deque()
402
-
403
- super().__init__(parent)
404
-
405
- self.setSizePolicy(QSizePolicy.MinimumExpanding, QSizePolicy.Expanding)
406
-
407
- # Disable default scrollbars (we use our own, to be set via .set_scroll_bar())
408
- self.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
409
- self.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
410
- self.scroll_bar = None
411
-
412
- # Use Monospace fonts and disable line wrapping.
413
- self.setFont(QtGui.QFont("Courier", 9))
414
- self.setFont(QtGui.QFont("Monospace"))
415
- self.setLineWrapMode(QtWidgets.QPlainTextEdit.NoWrap)
416
- fmt = QtGui.QFontMetrics(self.font())
417
- char_width = fmt.width("w")
418
- self.setCursorWidth(char_width)
419
-
420
- self.adjustSize()
421
- self.updateGeometry()
422
- self.update_stylesheet()
423
-
424
- @property
425
- def bg_color(self):
426
- return self._bg_color
427
-
428
- @bg_color.setter
429
- def bg_color(self, hexcolor):
430
- self._bg_color = hexcolor
431
- self.update_stylesheet()
432
-
433
- @property
434
- def fg_color(self):
435
- return self._fg_color
436
-
437
- @fg_color.setter
438
- def fg_color(self, hexcolor):
439
- self._fg_color = hexcolor
440
- self.update_stylesheet()
441
-
442
- def update_stylesheet(self):
443
- self.setStyleSheet(
444
- f"QPlainTextEdit {{ border: 0; color: {self._fg_color}; background-color: {self._bg_color}; }} "
445
- )
446
-
447
- @property
448
- def rows(self):
449
- return self._rows
450
-
451
- @rows.setter
452
- def rows(self, rows: int):
453
- if self.backend is None:
454
- # not initialized yet, ok to change
455
- self._rows = rows
456
- self.adjustSize()
457
- self.updateGeometry()
458
- else:
459
- raise RuntimeError("Cannot change rows after console is started.")
460
-
461
- @property
462
- def cols(self):
463
- return self._cols
464
-
465
- @cols.setter
466
- def cols(self, cols: int):
467
- if self.fd is None:
468
- # not initialized yet, ok to change
469
- self._cols = cols
470
- self.adjustSize()
471
- self.updateGeometry()
472
- else:
473
- raise RuntimeError("Cannot change cols after console is started.")
474
-
475
- def start(self, deactivate_ctrl_d: bool = False):
476
- self._deactivate_ctrl_d = deactivate_ctrl_d
477
-
478
- self.update_term_size()
479
-
480
- # Start the Bash process
481
- self.pid, self.fd = self.fork_shell()
482
-
483
- if self.fd:
484
- # Create the ``Backend`` object
485
- self.backend = Backend(self.fd, self.cols, self.rows)
486
- self.backend.dataReady.connect(self.data_ready)
487
- self.backend.processExited.connect(self.process_exited)
488
- else:
489
- self.process_exited()
490
-
491
- def process_exited(self):
492
- self.fd = None
493
- self.clear()
494
- self.appendHtml(f"<br><h2>{repr(self._cmd)} - Process exited.</h2>")
495
- self.setReadOnly(True)
496
-
497
- def send_ctrl_c(self, wait_prompt=True, timeout=None):
498
- """Send CTRL-C to the process
499
-
500
- If wait_prompt=True (default), wait for a new prompt after CTRL-C
501
- If no prompt is displayed after 'timeout' seconds, TimeoutError is raised
502
- """
503
- os.kill(self.pid, signal.SIGINT)
504
- if wait_prompt:
505
- timeout_error = False
506
- if timeout:
507
-
508
- def set_timeout_error():
509
- nonlocal timeout_error
510
- timeout_error = True
511
-
512
- timeout_timer = QTimer()
513
- timeout_timer.singleShot(timeout * 1000, set_timeout_error)
514
- while self._prompt_str is None:
515
- QApplication.instance().process_events()
516
- if timeout_error:
517
- raise TimeoutError(
518
- f"CTRL-C: could not get back to prompt after {timeout} seconds."
519
- )
520
-
521
- def _is_running(self):
522
- if os.waitpid(self.pid, os.WNOHANG) == (0, 0):
523
- return True
524
- return False
525
-
526
- def stop(self, kill=True, timeout=None):
527
- """Stop the running process
528
-
529
- SIGTERM is the default signal for terminating processes.
530
-
531
- If kill=True (default), SIGKILL will be sent if the process does not exit after timeout
532
- """
533
- # try to exit gracefully
534
- os.kill(self.pid, signal.SIGTERM)
535
-
536
- # wait until process is truly dead
537
- t0 = time.perf_counter()
538
- while self._is_running():
539
- time.sleep(1)
540
- if timeout is not None and time.perf_counter() - t0 > timeout:
541
- # still alive after 'timeout' seconds
542
- if kill:
543
- # send SIGKILL and make a last check in loop
544
- os.kill(self.pid, signal.SIGKILL)
545
- kill = False
546
- else:
547
- # still running after timeout...
548
- raise TimeoutError(
549
- f"Could not terminate process with pid: {self.pid} within timeout"
550
- )
551
- self.process_exited()
552
-
553
- def data_ready(self, screen):
554
- """Handle new screen: redraw, set scroll bar max and slider, move cursor to its position
555
-
556
- This method is triggered via a signal from ``Backend``.
557
- """
558
- self.redraw_screen()
559
- self.adjust_scroll_bar()
560
- self.move_cursor()
561
-
562
- def minimumSizeHint(self):
563
- """Return minimum size for current cols and rows"""
564
- fmt = QtGui.QFontMetrics(self.font())
565
- char_width = fmt.width("w")
566
- char_height = fmt.height()
567
- width = char_width * self.cols
568
- height = char_height * self.rows
569
- return QSize(width, height)
570
-
571
- def sizeHint(self):
572
- return self.minimumSizeHint()
573
-
574
- def set_scroll_bar(self, scroll_bar):
575
- self.scroll_bar = scroll_bar
576
- self.scroll_bar.setMinimum(0)
577
- self.scroll_bar.valueChanged.connect(self.scroll_value_change)
578
-
579
- def scroll_value_change(self, value, old={"value": -1}):
580
- if self.backend is None:
581
- return
582
- if old["value"] == -1:
583
- old["value"] = self.scroll_bar.maximum()
584
- if value <= old["value"]:
585
- # scroll up
586
- # value is number of lines from the start
587
- nlines = old["value"] - value
588
- # history ratio gives prev_page == 1 line
589
- for i in range(nlines):
590
- self.backend.screen.prev_page()
591
- else:
592
- # scroll down
593
- nlines = value - old["value"]
594
- for i in range(nlines):
595
- self.backend.screen.next_page()
596
- old["value"] = value
597
- self.redraw_screen()
598
-
599
- def adjust_scroll_bar(self):
600
- sb = self.scroll_bar
601
- sb.valueChanged.disconnect(self.scroll_value_change)
602
- tmp = len(self.backend.screen.history.top) + len(self.backend.screen.history.bottom)
603
- sb.setMaximum(tmp if tmp > 0 else 0)
604
- sb.setSliderPosition(tmp if tmp > 0 else 0)
605
- # if tmp > 0:
606
- # # show scrollbar, but delayed - prevent recursion with widget size change
607
- # QTimer.singleShot(0, scrollbar.show)
608
- # else:
609
- # QTimer.singleShot(0, scrollbar.hide)
610
- sb.valueChanged.connect(self.scroll_value_change)
611
-
612
- def write(self, data):
613
- try:
614
- os.write(self.fd, data)
615
- except (IOError, OSError):
616
- self.process_exited()
617
-
618
- @Slot(object)
619
- def keyPressEvent(self, event):
620
- """
621
- Redirect all keystrokes to the terminal process.
622
- """
623
- if self.fd is None:
624
- # not started
625
- return
626
- # Convert the Qt key to the correct ASCII code.
627
- if (
628
- self._deactivate_ctrl_d
629
- and event.modifiers() == QtCore.Qt.ControlModifier
630
- and event.key() == QtCore.Qt.Key_D
631
- ):
632
- return None
633
-
634
- code = QtKeyToAscii(event)
635
- if code == "copy":
636
- # MacOS only: CMD-C handling
637
- self.copy()
638
- elif code == "paste":
639
- # MacOS only: CMD-V handling
640
- self._push_clipboard()
641
- elif code is not None:
642
- self.write(code)
643
-
644
- def push(self, text, hit_return=False):
645
- """
646
- Write 'text' to terminal
647
- """
648
- self.write(text.encode("utf-8"))
649
- if hit_return:
650
- self.write(b"\n")
651
-
652
- def contextMenuEvent(self, event):
653
- if self.fd is None:
654
- return
655
- menu = self.createStandardContextMenu()
656
- for action in menu.actions():
657
- # remove all actions except copy and paste
658
- if "opy" in action.text():
659
- # redefine text without shortcut
660
- # since it probably clashes with control codes (like CTRL-C etc)
661
- action.setText("Copy")
662
- continue
663
- if "aste" in action.text():
664
- # redefine text without shortcut
665
- action.setText("Paste")
666
- # paste -> have to insert with self.push
667
- action.triggered.connect(self._push_clipboard)
668
- continue
669
- menu.removeAction(action)
670
- menu.exec_(event.globalPos())
671
-
672
- def _push_clipboard(self):
673
- clipboard = QApplication.instance().clipboard()
674
- self.push(clipboard.text())
675
-
676
- def move_cursor(self):
677
- textCursor = self.textCursor()
678
- textCursor.setPosition(0)
679
- textCursor.movePosition(
680
- QTextCursor.Down, QTextCursor.MoveAnchor, self.backend.screen.cursor.y
681
- )
682
- textCursor.movePosition(
683
- QTextCursor.Right, QTextCursor.MoveAnchor, self.backend.screen.cursor.x
684
- )
685
- self.setTextCursor(textCursor)
686
-
687
- def mouseReleaseEvent(self, event):
688
- if self.fd is None:
689
- return
690
- if event.button() == Qt.MiddleButton:
691
- # push primary selection buffer ("mouse clipboard") to terminal
692
- clipboard = QApplication.instance().clipboard()
693
- if clipboard.supportsSelection():
694
- self.push(clipboard.text(QClipboard.Selection))
695
- return None
696
- elif event.button() == Qt.LeftButton:
697
- # left button click
698
- textCursor = self.textCursor()
699
- if textCursor.selectedText():
700
- # mouse was used to select text -> nothing to do
701
- pass
702
- else:
703
- # a simple 'click', move scrollbar to end
704
- self.scroll_bar.setSliderPosition(self.scroll_bar.maximum())
705
- self.move_cursor()
706
- return None
707
- return super().mouseReleaseEvent(event)
708
-
709
- def redraw_screen(self):
710
- """
711
- Render the screen as formatted text into the widget.
712
- """
713
- screen = self.backend.screen
714
-
715
- # Clear the widget
716
- if screen.dirty:
717
- self.clear()
718
- while len(self.output) < (max(screen.dirty) + 1):
719
- self.output.append("")
720
- while len(self.output) > (max(screen.dirty) + 1):
721
- self.output.pop()
722
-
723
- # Prepare the HTML output
724
- for line_no in screen.dirty:
725
- line = text = ""
726
- style = old_style = ""
727
- old_idx = 0
728
- for idx, ch in screen.buffer[line_no].items():
729
- text += " " * (idx - old_idx - 1)
730
- old_idx = idx
731
- style = f"{'background-color:%s;' % ansi_colors.get(ch.bg, ansi_colors['black']) if ch.bg!='default' else ''}{'color:%s;' % ansi_colors.get(ch.fg, ansi_colors['white']) if ch.fg!='default' else ''}{'font-weight:bold;' if ch.bold else ''}{'font-style:italic;' if ch.italics else ''}"
732
- if style != old_style:
733
- if old_style:
734
- line += f"<span style={repr(old_style)}>{html.escape(text, quote=True)}</span>"
735
- else:
736
- line += html.escape(text, quote=True)
737
- text = ""
738
- old_style = style
739
- text += ch.data
740
- if style:
741
- line += f"<span style={repr(style)}>{html.escape(text, quote=True)}</span>"
742
- else:
743
- line += html.escape(text, quote=True)
744
- # do a check at the cursor position:
745
- # it is possible x pos > output line length,
746
- # for example if last escape codes are "cursor forward" past end of text,
747
- # like IPython does for "..." prompt (in a block, like "for" loop or "while" for example)
748
- # In this case, cursor is at 12 but last text output is at 8 -> insert spaces
749
- if line_no == screen.cursor.y:
750
- llen = len(screen.buffer[line_no])
751
- if llen < screen.cursor.x:
752
- line += " " * (screen.cursor.x - llen)
753
- self.output[line_no] = line
754
- # fill the text area with HTML contents in one go
755
- self.appendHtml(f"<pre>{chr(10).join(self.output)}</pre>")
756
-
757
- if self._prompt_re is not None:
758
- text_buf = self.toPlainText()
759
- prompt = self._prompt_re.search(text_buf)
760
- if prompt is None:
761
- if self._prompt_str:
762
- self.prompt.emit(False)
763
- self._prompt_str = None
764
- else:
765
- prompt_str = prompt.string.rstrip()
766
- if prompt_str != self._prompt_str:
767
- self._prompt_str = prompt_str
768
- self.prompt.emit(True)
769
-
770
- # did updates, all clean
771
- screen.dirty.clear()
772
-
773
- def update_term_size(self):
774
- fmt = QtGui.QFontMetrics(self.font())
775
- char_width = fmt.width("w")
776
- char_height = fmt.height()
777
- self._cols = int(self.width() / char_width)
778
- self._rows = int(self.height() / char_height)
779
-
780
- def resizeEvent(self, event):
781
- self.update_term_size()
782
- if self.fd:
783
- self.backend.screen.resize(self._rows, self._cols)
784
- self.redraw_screen()
785
- self.adjust_scroll_bar()
786
- self.move_cursor()
787
-
788
- def wheelEvent(self, event):
789
- if not self.fd:
790
- return
791
- y = event.angleDelta().y()
792
- if y > 0:
793
- self.backend.screen.prev_page()
794
- else:
795
- self.backend.screen.next_page()
796
- self.redraw_screen()
797
-
798
- def fork_shell(self):
799
- """
800
- Fork the current process and execute bec in shell.
801
- """
802
- try:
803
- pid, fd = pty.fork()
804
- except (IOError, OSError):
805
- return False
806
- if pid == 0:
807
- try:
808
- ls = os.environ["LANG"].split(".")
809
- except KeyError:
810
- ls = []
811
- if len(ls) < 2:
812
- ls = ["en_US", "UTF-8"]
813
- os.putenv("COLUMNS", str(self.cols))
814
- os.putenv("LINES", str(self.rows))
815
- os.putenv("TERM", "linux")
816
- os.putenv("LANG", ls[0] + ".UTF-8")
817
- if not self._cmd:
818
- self._cmd = os.environ["SHELL"]
819
- cmd = self._cmd
820
- if isinstance(cmd, str):
821
- cmd = cmd.split()
822
- try:
823
- os.execvp(cmd[0], cmd)
824
- except (IOError, OSError):
825
- pass
826
- os._exit(0)
827
- else:
828
- # We are in the parent process.
829
- # Set file control
830
- fcntl.fcntl(fd, fcntl.F_SETFL, os.O_NONBLOCK)
831
- return pid, fd
832
-
833
-
834
- if __name__ == "__main__":
835
- import os
836
- import sys
837
-
838
- from qtpy import QtGui, QtWidgets
839
-
840
- # Create the Qt application and console.
841
- app = QtWidgets.QApplication([])
842
- mainwin = QtWidgets.QMainWindow()
843
- title = "BECConsole"
844
- mainwin.setWindowTitle(title)
845
-
846
- console = BECConsole(mainwin)
847
- mainwin.setCentralWidget(console)
848
-
849
- def check_prompt(at_prompt):
850
- if at_prompt:
851
- print("NEW PROMPT")
852
- else:
853
- print("EXECUTING SOMETHING...")
854
-
855
- console.set_prompt_tokens(
856
- (Token.OutPromptNum, "•"),
857
- (Token.Prompt, ""), # will match arbitrary string,
858
- (Token.Prompt, " ["),
859
- (Token.PromptNum, "3"),
860
- (Token.Prompt, "/"),
861
- (Token.PromptNum, "1"),
862
- (Token.Prompt, "] "),
863
- (Token.Prompt, "❯❯"),
864
- )
865
- console.prompt.connect(check_prompt)
866
- console.start()
867
-
868
- # Show widget and launch Qt's event loop.
869
- mainwin.show()
870
- sys.exit(app.exec_())
@@ -1 +0,0 @@
1
- {'files': ['console.py']}
@@ -1,58 +0,0 @@
1
- # Copyright (C) 2022 The Qt Company Ltd.
2
- # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
3
- import os
4
-
5
- from qtpy.QtDesigner import QDesignerCustomWidgetInterface
6
-
7
- import bec_widgets
8
- from bec_widgets.utils.bec_designer import designer_material_icon
9
- from bec_widgets.widgets.editors.console.console import BECConsole
10
-
11
- DOM_XML = """
12
- <ui language='c++'>
13
- <widget class='BECConsole' name='bec_console'>
14
- </widget>
15
- </ui>
16
- """
17
-
18
- MODULE_PATH = os.path.dirname(bec_widgets.__file__)
19
-
20
-
21
- class BECConsolePlugin(QDesignerCustomWidgetInterface): # pragma: no cover
22
- def __init__(self):
23
- super().__init__()
24
- self._form_editor = None
25
-
26
- def createWidget(self, parent):
27
- t = BECConsole(parent)
28
- return t
29
-
30
- def domXml(self):
31
- return DOM_XML
32
-
33
- def group(self):
34
- return "BEC Console"
35
-
36
- def icon(self):
37
- return designer_material_icon(BECConsole.ICON_NAME)
38
-
39
- def includeFile(self):
40
- return "bec_console"
41
-
42
- def initialize(self, form_editor):
43
- self._form_editor = form_editor
44
-
45
- def isContainer(self):
46
- return False
47
-
48
- def isInitialized(self):
49
- return self._form_editor is not None
50
-
51
- def name(self):
52
- return "BECConsole"
53
-
54
- def toolTip(self):
55
- return "A terminal-like vt100 widget."
56
-
57
- def whatsThis(self):
58
- return self.toolTip()
@@ -1,15 +0,0 @@
1
- def main(): # pragma: no cover
2
- from qtpy import PYSIDE6
3
-
4
- if not PYSIDE6:
5
- print("PYSIDE6 is not available in the environment. Cannot patch designer.")
6
- return
7
- from PySide6.QtDesigner import QPyDesignerCustomWidgetCollection
8
-
9
- from bec_widgets.widgets.editors.console.console_plugin import BECConsolePlugin
10
-
11
- QPyDesignerCustomWidgetCollection.addCustomWidget(BECConsolePlugin())
12
-
13
-
14
- if __name__ == "__main__": # pragma: no cover
15
- main()