QuLab 2.11.8__py3-none-any.whl → 2.11.10__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.
- qulab/executor/analyze.py +1 -0
- qulab/executor/cli.py +180 -42
- qulab/executor/schedule.py +17 -6
- qulab/executor/template.py +6 -1
- qulab/executor/utils.py +16 -2
- qulab/monitor/__init__.py +1 -1
- qulab/monitor/__main__.py +31 -3
- qulab/monitor/config.py +55 -30
- qulab/monitor/dataset.py +145 -38
- qulab/monitor/event_queue.py +98 -25
- qulab/monitor/mainwindow.py +165 -131
- qulab/monitor/monitor.py +220 -30
- qulab/monitor/ploter.py +98 -73
- qulab/monitor/qt_compat.py +30 -1
- qulab/monitor/toolbar.py +152 -121
- qulab/utils.py +16 -17
- qulab/version.py +1 -1
- {qulab-2.11.8.dist-info → qulab-2.11.10.dist-info}/METADATA +2 -1
- {qulab-2.11.8.dist-info → qulab-2.11.10.dist-info}/RECORD +23 -23
- {qulab-2.11.8.dist-info → qulab-2.11.10.dist-info}/WHEEL +1 -1
- {qulab-2.11.8.dist-info → qulab-2.11.10.dist-info}/entry_points.txt +0 -0
- {qulab-2.11.8.dist-info → qulab-2.11.10.dist-info}/licenses/LICENSE +0 -0
- {qulab-2.11.8.dist-info → qulab-2.11.10.dist-info}/top_level.txt +0 -0
qulab/monitor/toolbar.py
CHANGED
@@ -1,15 +1,36 @@
|
|
1
|
+
"""
|
2
|
+
QuLab Monitor Toolbar Module
|
3
|
+
|
4
|
+
This module implements the toolbar interface for the QuLab monitor application.
|
5
|
+
The toolbar provides controls for:
|
6
|
+
- Switching between point and trace data modes
|
7
|
+
- Configuring plot formats and transformations
|
8
|
+
- Managing axis linking and scaling
|
9
|
+
- Controlling auto-range and data clearing
|
10
|
+
"""
|
11
|
+
|
1
12
|
import itertools
|
2
13
|
import re
|
3
|
-
from typing import Callable
|
14
|
+
from typing import Callable, List, Tuple
|
4
15
|
|
5
|
-
from .config import
|
16
|
+
from .config import STYLE, TRANSFORM_NAMES
|
6
17
|
from .qt_compat import AlignRight, QtWidgets
|
7
18
|
|
8
19
|
|
9
|
-
def matched_xy_pairs(patterns: str,
|
20
|
+
def matched_xy_pairs(patterns: str, column_names: list[str]) -> list[tuple[str, str]]:
|
21
|
+
"""
|
22
|
+
Find matching X-Y column pairs based on pattern strings.
|
23
|
+
|
24
|
+
Args:
|
25
|
+
patterns: Semicolon-separated patterns for matching column pairs
|
26
|
+
column_names: List of available column names
|
27
|
+
|
28
|
+
Returns:
|
29
|
+
List of matched (x, y) column name pairs
|
30
|
+
"""
|
10
31
|
patterns = patterns.replace(" ", "").split(";")
|
11
32
|
pairs = []
|
12
|
-
for x, y in itertools.product(
|
33
|
+
for x, y in itertools.product(column_names, repeat=2):
|
13
34
|
test = f"{x},{y}"
|
14
35
|
for pattern in patterns:
|
15
36
|
r = re.match(pattern, test)
|
@@ -20,69 +41,82 @@ def matched_xy_pairs(patterns: str, lst: list[str]) -> list[tuple[str, str]]:
|
|
20
41
|
|
21
42
|
|
22
43
|
class FormatCombo(QtWidgets.QComboBox):
|
44
|
+
"""Base class for format selection combo boxes."""
|
23
45
|
|
24
46
|
def __init__(self):
|
25
47
|
super().__init__()
|
26
48
|
self.on_change_callable = None
|
27
49
|
|
28
|
-
def set_on_change_event_action(self, callback: Callable[[], None]):
|
50
|
+
def set_on_change_event_action(self, callback: Callable[[], None]) -> None:
|
51
|
+
"""Set the callback function for when the selection changes."""
|
29
52
|
self.on_change_callable = callback
|
30
53
|
self.activated.connect(callback)
|
31
54
|
|
32
|
-
def set_idx(self, idx: int):
|
55
|
+
def set_idx(self, idx: int) -> None:
|
56
|
+
"""Set the current index and trigger the change callback."""
|
33
57
|
self.setCurrentIndex(idx)
|
34
|
-
if
|
58
|
+
if callable(self.on_change_callable):
|
35
59
|
self.on_change_callable()
|
36
60
|
|
37
61
|
|
38
62
|
class XFormatCombo(FormatCombo):
|
63
|
+
"""Combo box for X-axis data transformation selection."""
|
39
64
|
|
40
65
|
def __init__(self):
|
41
66
|
super().__init__()
|
42
|
-
|
43
|
-
|
44
|
-
self.addItem("mag")
|
45
|
-
self.addItem("phase")
|
67
|
+
for transform in ["real", "imag", "mag", "phase"]:
|
68
|
+
self.addItem(transform)
|
46
69
|
|
47
70
|
|
48
71
|
class YFormatCombo(FormatCombo):
|
72
|
+
"""Combo box for Y-axis data transformation selection."""
|
49
73
|
|
50
74
|
def __init__(self):
|
51
75
|
super().__init__()
|
52
|
-
|
53
|
-
|
54
|
-
self.addItem("real")
|
55
|
-
self.addItem("imag")
|
76
|
+
for transform in ["mag", "phase", "real", "imag"]:
|
77
|
+
self.addItem(transform)
|
56
78
|
|
57
79
|
|
58
80
|
class LineEdit(QtWidgets.QLineEdit):
|
81
|
+
"""Custom line edit with change event support."""
|
59
82
|
|
60
|
-
def set_on_change_event_action(self, callback: Callable[[], None]):
|
83
|
+
def set_on_change_event_action(self, callback: Callable[[], None]) -> None:
|
84
|
+
"""Set the callback function for when editing is finished."""
|
61
85
|
self.on_change_callable = callback
|
62
86
|
self.editingFinished.connect(callback)
|
63
87
|
|
64
|
-
def set_text(self,
|
65
|
-
|
66
|
-
|
88
|
+
def set_text(self, text: str) -> None:
|
89
|
+
"""Set the text and trigger the change callback."""
|
90
|
+
self.setText(text)
|
91
|
+
if callable(self.on_change_callable):
|
67
92
|
self.on_change_callable()
|
68
93
|
|
69
94
|
|
70
|
-
class SelectionBundle
|
95
|
+
class SelectionBundle:
|
96
|
+
"""
|
97
|
+
Bundle of UI controls for plot configuration.
|
98
|
+
|
99
|
+
This class groups together the controls for:
|
100
|
+
- X-Y pair selection
|
101
|
+
- Data transformations
|
102
|
+
- Axis scaling
|
103
|
+
- Axis linking
|
104
|
+
"""
|
71
105
|
|
72
106
|
def __init__(self):
|
73
|
-
|
74
|
-
#
|
75
|
-
self.fx = XFormatCombo()
|
76
|
-
self.fy = YFormatCombo()
|
77
|
-
self.lx = QtWidgets.QCheckBox("logX")
|
78
|
-
self.ly = QtWidgets.QCheckBox("logY")
|
79
|
-
self.linkx = QtWidgets.QCheckBox("ShareX")
|
80
|
-
self.linky = QtWidgets.QCheckBox("ShareY")
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
107
|
+
# Plot configuration controls
|
108
|
+
self.stx = LineEdit() # X-Y pair selection text
|
109
|
+
self.fx = XFormatCombo() # X-axis transformation
|
110
|
+
self.fy = YFormatCombo() # Y-axis transformation
|
111
|
+
self.lx = QtWidgets.QCheckBox("logX") # X-axis log scale
|
112
|
+
self.ly = QtWidgets.QCheckBox("logY") # Y-axis log scale
|
113
|
+
self.linkx = QtWidgets.QCheckBox("ShareX") # X-axis linking
|
114
|
+
self.linky = QtWidgets.QCheckBox("ShareY") # Y-axis linking
|
115
|
+
|
116
|
+
def set_on_change_event_actions(self, on_text_edited: Callable[[], None],
|
117
|
+
on_format_changed: Callable[[], None],
|
118
|
+
on_log_scale_marker_changed: Callable[[], None]) -> None:
|
119
|
+
"""Set callback functions for various control changes."""
|
86
120
|
self.stx.set_on_change_event_action(on_text_edited)
|
87
121
|
self.fx.set_on_change_event_action(on_format_changed)
|
88
122
|
self.fy.set_on_change_event_action(on_format_changed)
|
@@ -91,23 +125,18 @@ class SelectionBundle():
|
|
91
125
|
self.linkx.toggled.connect(on_log_scale_marker_changed)
|
92
126
|
self.linky.toggled.connect(on_log_scale_marker_changed)
|
93
127
|
|
94
|
-
def
|
95
|
-
|
96
|
-
self.fx.
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
layout.addWidget(self.
|
106
|
-
i += 2
|
107
|
-
layout.addWidget(self.fx, 0, i)
|
108
|
-
i += 2
|
109
|
-
layout.addWidget(self.fy, 0, i)
|
110
|
-
i += 2
|
128
|
+
def remove_from_layout(self) -> None:
|
129
|
+
"""Remove all controls from their parent layout."""
|
130
|
+
for widget in [self.stx, self.fx, self.fy, self.lx, self.ly,
|
131
|
+
self.linkx, self.linky]:
|
132
|
+
widget.setParent(None)
|
133
|
+
|
134
|
+
def add_to_layout(self, layout: QtWidgets.QGridLayout) -> None:
|
135
|
+
"""Add all controls to the specified layout."""
|
136
|
+
# Column positions for controls
|
137
|
+
layout.addWidget(self.stx, 0, 3)
|
138
|
+
layout.addWidget(self.fx, 0, 5)
|
139
|
+
layout.addWidget(self.fy, 0, 7)
|
111
140
|
layout.addWidget(self.lx, 0, 10)
|
112
141
|
layout.addWidget(self.ly, 0, 11)
|
113
142
|
layout.addWidget(self.linkx, 0, 12)
|
@@ -115,46 +144,46 @@ class SelectionBundle():
|
|
115
144
|
|
116
145
|
|
117
146
|
class ToolBar(QtWidgets.QWidget):
|
147
|
+
"""
|
148
|
+
Main toolbar widget for the monitor application.
|
149
|
+
|
150
|
+
This widget provides controls for:
|
151
|
+
- Switching between point and trace data modes
|
152
|
+
- Configuring plot formats and transformations
|
153
|
+
- Managing axis linking and scaling
|
154
|
+
- Controlling auto-range and data clearing
|
155
|
+
"""
|
118
156
|
|
119
157
|
def __init__(self):
|
120
158
|
super().__init__()
|
159
|
+
self.setStyleSheet(STYLE)
|
121
160
|
|
122
|
-
#
|
123
|
-
#
|
124
|
-
self.mode = 'P'
|
125
|
-
self.setStyleSheet(style)
|
126
|
-
|
161
|
+
# Mode selection
|
162
|
+
self.mode = 'P' # 'P' for points, 'T' for traces
|
127
163
|
self.pb = QtWidgets.QRadioButton('Points')
|
164
|
+
self.tb = QtWidgets.QRadioButton('Traces')
|
128
165
|
self.pb.setChecked(True)
|
129
166
|
self.pb.toggled.connect(self.toggle_mode)
|
130
|
-
|
131
|
-
# button for Traces
|
132
|
-
self.tb = QtWidgets.QRadioButton('Traces')
|
133
167
|
self.tb.toggled.connect(self.toggle_mode)
|
134
168
|
|
135
|
-
#
|
169
|
+
# Labels
|
136
170
|
self.ytxt_lb = QtWidgets.QLabel("(X,Y)")
|
137
|
-
self.ytxt_lb.setAlignment(AlignRight)
|
138
171
|
self.fx_lb = QtWidgets.QLabel("fx")
|
139
|
-
self.fx_lb.setAlignment(AlignRight)
|
140
172
|
self.fy_lb = QtWidgets.QLabel("fy")
|
173
|
+
self.ytxt_lb.setAlignment(AlignRight)
|
174
|
+
self.fx_lb.setAlignment(AlignRight)
|
141
175
|
self.fy_lb.setAlignment(AlignRight)
|
142
176
|
|
143
|
-
#
|
144
|
-
ps = SelectionBundle()
|
145
|
-
|
146
|
-
|
177
|
+
# Selection bundles for points and traces
|
178
|
+
self.ps = SelectionBundle()
|
179
|
+
self.ts = SelectionBundle()
|
180
|
+
self.ps.set_on_change_event_actions(
|
181
|
+
self.text_edited, self.generate_transform, self.link_edited)
|
182
|
+
self.ts.set_on_change_event_actions(
|
183
|
+
self.text_edited, self.generate_transform, self.link_edited)
|
147
184
|
|
148
|
-
|
149
|
-
|
150
|
-
self.link_edited)
|
151
|
-
|
152
|
-
# connections :
|
153
|
-
self.ps = ps
|
154
|
-
self.ts = ts
|
155
|
-
|
156
|
-
# plot format configures
|
157
|
-
self.xypairs = []
|
185
|
+
# State flags
|
186
|
+
self.xypairs: List[Tuple[str, str]] = []
|
158
187
|
self.xypairs_dirty = True
|
159
188
|
self.fx = None
|
160
189
|
self.fy = None
|
@@ -162,25 +191,27 @@ class ToolBar(QtWidgets.QWidget):
|
|
162
191
|
self.link_dirty = True
|
163
192
|
self.lx = False
|
164
193
|
self.ly = False
|
165
|
-
|
194
|
+
self.CR_flag = False
|
195
|
+
|
196
|
+
# Layout setup
|
166
197
|
self.layout = QtWidgets.QGridLayout()
|
167
198
|
self.layout.setContentsMargins(0, 0, 0, 0)
|
168
199
|
self.setLayout(self.layout)
|
169
200
|
|
201
|
+
# Add basic controls
|
170
202
|
self.layout.addWidget(self.pb, 0, 0)
|
171
203
|
self.layout.addWidget(self.tb, 0, 1)
|
172
|
-
|
173
204
|
self.layout.addWidget(self.ytxt_lb, 0, 2)
|
174
205
|
self.layout.addWidget(self.fx_lb, 0, 4)
|
175
206
|
self.layout.addWidget(self.fy_lb, 0, 6)
|
176
207
|
|
208
|
+
# Add action buttons
|
177
209
|
self.AR = QtWidgets.QPushButton("AR")
|
178
|
-
self.AR.setMaximumWidth(30)
|
179
|
-
self.AR.setToolTip("Auto Range")
|
180
210
|
self.CR = QtWidgets.QPushButton("CLR")
|
211
|
+
self.AR.setMaximumWidth(30)
|
181
212
|
self.CR.setMaximumWidth(30)
|
182
|
-
self.
|
183
|
-
self.
|
213
|
+
self.AR.setToolTip("Auto Range")
|
214
|
+
self.CR.setToolTip("Clear History Plots")
|
184
215
|
self.layout.addWidget(self.AR, 0, 8)
|
185
216
|
self.layout.addWidget(self.CR, 0, 9)
|
186
217
|
|
@@ -188,6 +219,7 @@ class ToolBar(QtWidgets.QWidget):
|
|
188
219
|
|
189
220
|
@property
|
190
221
|
def column_names(self) -> list[str]:
|
222
|
+
"""Get column names for the current mode."""
|
191
223
|
return {
|
192
224
|
"P": self.mainwindow.point_data_box.column_names,
|
193
225
|
"T": self.mainwindow.trace_data_box.column_names
|
@@ -195,71 +227,70 @@ class ToolBar(QtWidgets.QWidget):
|
|
195
227
|
|
196
228
|
@property
|
197
229
|
def selections(self) -> SelectionBundle:
|
230
|
+
"""Get the selection bundle for the current mode."""
|
198
231
|
return {"P": self.ps, "T": self.ts}[self.mode]
|
199
232
|
|
200
|
-
def set_trace_text(self, text: str):
|
233
|
+
def set_trace_text(self, text: str) -> None:
|
234
|
+
"""Set the X-Y pair text for trace mode."""
|
201
235
|
self.ts.stx.set_text(text)
|
202
236
|
|
203
|
-
def set_point_text(self, text: str):
|
237
|
+
def set_point_text(self, text: str) -> None:
|
238
|
+
"""Set the X-Y pair text for point mode."""
|
204
239
|
self.ps.stx.set_text(text)
|
205
240
|
|
206
|
-
def sharexy(self):
|
207
|
-
|
208
|
-
|
241
|
+
def sharexy(self) -> tuple[bool, bool]:
|
242
|
+
"""Get the current axis sharing state."""
|
243
|
+
return (self.selections.linkx.isChecked(),
|
244
|
+
self.selections.linky.isChecked())
|
209
245
|
|
210
|
-
def set_mainwindow(self, mainwindow):
|
246
|
+
def set_mainwindow(self, mainwindow) -> None:
|
247
|
+
"""Connect the toolbar to the main window."""
|
211
248
|
self.mainwindow = mainwindow
|
212
249
|
self.AR.clicked.connect(self.mainwindow.all_enable_auto_range)
|
213
|
-
self.CR.clicked.connect(self.
|
250
|
+
self.CR.clicked.connect(self.clear_history)
|
214
251
|
|
215
|
-
def
|
216
|
-
|
217
|
-
|
218
|
-
def CR_action(self):
|
252
|
+
def clear_history(self) -> None:
|
253
|
+
"""Set flag to clear plot history."""
|
219
254
|
self.CR_flag = True
|
220
255
|
|
221
|
-
def refresh_layout(self):
|
256
|
+
def refresh_layout(self) -> None:
|
257
|
+
"""Update the layout based on current mode."""
|
222
258
|
if self.mode == 'P':
|
223
|
-
self.ts.
|
224
|
-
self.ps.
|
259
|
+
self.ts.remove_from_layout()
|
260
|
+
self.ps.add_to_layout(self.layout)
|
225
261
|
elif self.mode == 'T':
|
226
|
-
self.ps.
|
227
|
-
self.ts.
|
228
|
-
|
229
|
-
def toggle_mode(self):
|
230
|
-
|
231
|
-
|
232
|
-
elif (self.tb.isChecked()):
|
233
|
-
self.mode = 'T'
|
262
|
+
self.ps.remove_from_layout()
|
263
|
+
self.ts.add_to_layout(self.layout)
|
264
|
+
|
265
|
+
def toggle_mode(self) -> None:
|
266
|
+
"""Handle mode toggle between points and traces."""
|
267
|
+
self.mode = 'P' if self.pb.isChecked() else 'T'
|
234
268
|
self.refresh_layout()
|
235
269
|
self.refresh_comb()
|
236
270
|
|
237
|
-
def refresh_comb(self
|
238
|
-
|
239
|
-
self.
|
240
|
-
|
271
|
+
def refresh_comb(self) -> None:
|
272
|
+
"""Refresh all combo boxes and update tooltips."""
|
273
|
+
self.generate_transform()
|
274
|
+
self.text_edited()
|
241
275
|
self.ytxt_lb.setToolTip(str(self.column_names))
|
242
276
|
|
243
|
-
def link_edited(self):
|
244
|
-
|
277
|
+
def link_edited(self) -> None:
|
278
|
+
"""Handle changes to axis linking."""
|
245
279
|
self.link_dirty = True
|
246
280
|
|
247
|
-
def
|
281
|
+
def generate_transform(self) -> None:
|
282
|
+
"""Update transformation settings."""
|
248
283
|
self.fx = self.selections.fx.currentText()
|
249
284
|
self.fy = self.selections.fy.currentText()
|
250
285
|
self.lx = self.selections.lx.isChecked()
|
251
286
|
self.ly = self.selections.ly.isChecked()
|
252
287
|
self.xyfm_dirty = True
|
253
|
-
#self.show_info() ;
|
254
288
|
|
255
|
-
def
|
256
|
-
|
257
|
-
|
258
|
-
if
|
259
|
-
self.
|
289
|
+
def text_edited(self) -> None:
|
290
|
+
"""Handle changes to X-Y pair text."""
|
291
|
+
text = self.selections.stx.text()
|
292
|
+
if not text:
|
293
|
+
self.xypairs = []
|
260
294
|
else:
|
261
|
-
|
262
|
-
|
263
|
-
self.xypairs_dirty = True
|
264
|
-
break
|
265
|
-
self.xypairs = new_pairs
|
295
|
+
self.xypairs = matched_xy_pairs(text, self.column_names)
|
296
|
+
self.xypairs_dirty = True
|
qulab/utils.py
CHANGED
@@ -24,7 +24,7 @@ def combined_env(extra_paths=None):
|
|
24
24
|
return env
|
25
25
|
|
26
26
|
|
27
|
-
def run_detached(
|
27
|
+
def run_detached(script, env=None):
|
28
28
|
"""
|
29
29
|
启动可执行文件并完全分离(优先用 tmux/screen),无需额外终端窗口
|
30
30
|
支持 Windows、Linux 和 macOS
|
@@ -33,26 +33,26 @@ def run_detached(executable_path, env=None):
|
|
33
33
|
env = combined_env()
|
34
34
|
try:
|
35
35
|
if sys.platform == 'win32' or not _unix_detach_with_tmux_or_screen(
|
36
|
-
|
36
|
+
script, env):
|
37
37
|
# 回退到带终端窗口的方案
|
38
|
-
run_detached_with_terminal(
|
38
|
+
run_detached_with_terminal(script, env)
|
39
39
|
|
40
40
|
except Exception as e:
|
41
41
|
click.echo(f"启动失败: {e}")
|
42
42
|
sys.exit(1)
|
43
43
|
|
44
44
|
|
45
|
-
def _windows_start(
|
45
|
+
def _windows_start(script, env):
|
46
46
|
"""Windows 弹窗启动方案"""
|
47
|
-
subprocess.Popen(f'start cmd /k "{
|
47
|
+
subprocess.Popen(f'start cmd /k "{script}"',
|
48
48
|
shell=True,
|
49
49
|
env=env,
|
50
50
|
creationflags=subprocess.CREATE_NEW_PROCESS_GROUP)
|
51
51
|
|
52
52
|
|
53
|
-
def _unix_detach_with_tmux_or_screen(
|
53
|
+
def _unix_detach_with_tmux_or_screen(script, env):
|
54
54
|
"""Unix 后台分离方案(无窗口)"""
|
55
|
-
safe_path = shlex.quote(
|
55
|
+
safe_path = shlex.quote(script)
|
56
56
|
session_name = f"qulab_{int(time.time())}"
|
57
57
|
|
58
58
|
# 尝试 tmux
|
@@ -63,7 +63,7 @@ def _unix_detach_with_tmux_or_screen(executable_path, env):
|
|
63
63
|
"-d",
|
64
64
|
"-s",
|
65
65
|
session_name,
|
66
|
-
|
66
|
+
script + " ; tmux wait-for -S finished", # 等待命令结束
|
67
67
|
";",
|
68
68
|
"tmux",
|
69
69
|
"wait-for",
|
@@ -79,7 +79,7 @@ def _unix_detach_with_tmux_or_screen(executable_path, env):
|
|
79
79
|
|
80
80
|
# 尝试 screen
|
81
81
|
elif _check_command_exists("screen", env):
|
82
|
-
command = ["screen", "-dmS", session_name,
|
82
|
+
command = ["screen", "-dmS", session_name, script]
|
83
83
|
subprocess.Popen(command, env=env, start_new_session=True)
|
84
84
|
click.echo(f"已启动 screen 会话: {session_name}")
|
85
85
|
click.echo(f"你可以使用 `screen -r {session_name}` 来查看输出")
|
@@ -88,28 +88,27 @@ def _unix_detach_with_tmux_or_screen(executable_path, env):
|
|
88
88
|
return False
|
89
89
|
|
90
90
|
|
91
|
-
def run_detached_with_terminal(
|
91
|
+
def run_detached_with_terminal(script, env=None):
|
92
92
|
"""回退到带终端窗口的方案"""
|
93
93
|
if env is None:
|
94
94
|
env = combined_env()
|
95
95
|
|
96
96
|
if sys.platform == 'win32':
|
97
|
-
_windows_start(
|
97
|
+
_windows_start(script, env)
|
98
98
|
elif sys.platform == 'darwin':
|
99
|
-
#
|
100
|
-
|
101
|
-
script = f'tell app "Terminal" to do script "{executable_path}"'
|
99
|
+
# script=shlex.quote(script)
|
100
|
+
script = f'tell app "Terminal" to do script "{script}"'
|
102
101
|
subprocess.Popen(["osascript", "-e", script],
|
103
102
|
env=env,
|
104
103
|
start_new_session=True)
|
105
104
|
else:
|
106
105
|
try:
|
107
106
|
subprocess.Popen(
|
108
|
-
["gnome-terminal", "--", "sh", "-c",
|
107
|
+
["gnome-terminal", "--", "sh", "-c", script],
|
109
108
|
env=env,
|
110
109
|
start_new_session=True)
|
111
110
|
except FileNotFoundError:
|
112
|
-
subprocess.Popen(["xterm", "-e",
|
111
|
+
subprocess.Popen(["xterm", "-e", script],
|
113
112
|
env=env,
|
114
113
|
start_new_session=True)
|
115
114
|
|
@@ -127,4 +126,4 @@ def _check_command_exists(cmd, env):
|
|
127
126
|
|
128
127
|
# 示例用法
|
129
128
|
if __name__ == '__main__':
|
130
|
-
run_detached("/path/to/your/program")
|
129
|
+
run_detached("/path/to/your/program --option1=1 --option2=2 arg1 arg2")
|
qulab/version.py
CHANGED
@@ -1 +1 @@
|
|
1
|
-
__version__ = "2.11.
|
1
|
+
__version__ = "2.11.10"
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: QuLab
|
3
|
-
Version: 2.11.
|
3
|
+
Version: 2.11.10
|
4
4
|
Summary: contral instruments and manage data
|
5
5
|
Author-email: feihoo87 <feihoo87@gmail.com>
|
6
6
|
Maintainer-email: feihoo87 <feihoo87@gmail.com>
|
@@ -49,6 +49,7 @@ Requires-Dist: wath>=1.1.6
|
|
49
49
|
Requires-Dist: waveforms>=1.9.4
|
50
50
|
Requires-Dist: rich>=14.0.0
|
51
51
|
Provides-Extra: full
|
52
|
+
Requires-Dist: paramiko>=3.5.1; extra == "full"
|
52
53
|
Requires-Dist: uvloop>=0.19.0; extra == "full"
|
53
54
|
Dynamic: license-file
|
54
55
|
|
@@ -1,31 +1,31 @@
|
|
1
1
|
qulab/__init__.py,sha256=hmf6R3jiM5NtJY1mSptogYnH5DMTR2dTzlXykqLxQzg,2027
|
2
2
|
qulab/__main__.py,sha256=fjaRSL_uUjNIzBGNgjlGswb9TJ2VD5qnkZHW3hItrD4,68
|
3
3
|
qulab/typing.py,sha256=vg62sGqxuD9CI5677ejlzAmf2fVdAESZCQjAE_xSxPg,69
|
4
|
-
qulab/utils.py,sha256=
|
5
|
-
qulab/version.py,sha256=
|
4
|
+
qulab/utils.py,sha256=B_8QdY9OMY7WU-F200v93BDJXJpQcKAHihnOXeEvv_w,3966
|
5
|
+
qulab/version.py,sha256=xB08zidrdg5aQlDWwe_lBjpJMKhdnVRGdNtR9CzvjcY,23
|
6
6
|
qulab/cli/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
7
7
|
qulab/cli/commands.py,sha256=ywKmwbGNBCVASx8OsgrRttaLz9ogwY38ZdKj7I-tdJ4,813
|
8
8
|
qulab/cli/config.py,sha256=tZPSBLbf2x_Brb2UBuA1bsni8nC8WOPPGyWIi8m7j1I,5459
|
9
9
|
qulab/cli/decorators.py,sha256=oImteZVnDPPWdyhJ4kzf2KYGJLON7VsKGBvZadWLQZo,621
|
10
10
|
qulab/executor/__init__.py,sha256=LosPzOMaljSZY1thy_Fxtbrgq7uubJszMABEB7oM7tU,101
|
11
|
-
qulab/executor/analyze.py,sha256=
|
12
|
-
qulab/executor/cli.py,sha256=
|
11
|
+
qulab/executor/analyze.py,sha256=VYTfiOYJe5gDbtGA4wOkrQhhu55iIrRVByx-gBLUUm4,5672
|
12
|
+
qulab/executor/cli.py,sha256=cMhx_oOWsvYWRi3XyaDbNKQ8B3o7nMZ-D3Eic9WJ-Bk,25505
|
13
13
|
qulab/executor/load.py,sha256=eMlzOrrn8GpbP3J3uY5JJ8iO7tL5B1DWP1z2qiGyNhU,20385
|
14
14
|
qulab/executor/registry.py,sha256=gym9F5FIDY5eV-cSCZsP99wC4l-6jkx9VMjJMaPOLaQ,4730
|
15
|
-
qulab/executor/schedule.py,sha256=
|
15
|
+
qulab/executor/schedule.py,sha256=cDFM_tIMvxHLxb-tBA6fEPF9u-4LcgnQDxbaquZIQKc,21727
|
16
16
|
qulab/executor/storage.py,sha256=8K73KGLAVgchJdtd4rKHXkr1CQOJORWH-Gi57w8IYsw,21081
|
17
|
-
qulab/executor/template.py,sha256=
|
18
|
-
qulab/executor/utils.py,sha256=
|
19
|
-
qulab/monitor/__init__.py,sha256=
|
20
|
-
qulab/monitor/__main__.py,sha256=
|
21
|
-
qulab/monitor/config.py,sha256=
|
22
|
-
qulab/monitor/dataset.py,sha256=
|
23
|
-
qulab/monitor/event_queue.py,sha256=
|
24
|
-
qulab/monitor/mainwindow.py,sha256=
|
25
|
-
qulab/monitor/monitor.py,sha256=
|
26
|
-
qulab/monitor/ploter.py,sha256=
|
27
|
-
qulab/monitor/qt_compat.py,sha256=
|
28
|
-
qulab/monitor/toolbar.py,sha256=
|
17
|
+
qulab/executor/template.py,sha256=D8D7_B__R8wh1oOXk7CKZveRqDix4dNeUjefbfN6m9w,10720
|
18
|
+
qulab/executor/utils.py,sha256=YSUEYjyYTJTjiJmd-l5GTEnSyD6bqEdNoWhivEAfGZA,7112
|
19
|
+
qulab/monitor/__init__.py,sha256=FNYMIUr8ycRM4XmsAGpRK-A-SQsJZlcRWghXcGTgAi0,44
|
20
|
+
qulab/monitor/__main__.py,sha256=Txy4d7vgVYUWwiHxX3uwtsi1ewde-MaiSC_K0ePoPiM,1372
|
21
|
+
qulab/monitor/config.py,sha256=lMMRHy6wnVgMHiRMQqev5lj8mbF2g29TLrK82dwp_2k,1950
|
22
|
+
qulab/monitor/dataset.py,sha256=9ctuod8SE0yRfWABC2dyosDHwjr0higR4lTP4n4usJk,6152
|
23
|
+
qulab/monitor/event_queue.py,sha256=rfD9qC8miT96myBcfERJfzi7-wotzEiq2ld-4e277tk,4615
|
24
|
+
qulab/monitor/mainwindow.py,sha256=c4anPDroq_ik8wbc300bgSmrnM9mcz2NxM-IaJKPkao,9969
|
25
|
+
qulab/monitor/monitor.py,sha256=RwmjKJZVTyfDs_bYigh576zwUG_crgxKhbly2rWFFbE,9604
|
26
|
+
qulab/monitor/ploter.py,sha256=Fl63M7kz9dcnmDYoXZxcvehd_oZRFpSofXEzRsVEDd8,4799
|
27
|
+
qulab/monitor/qt_compat.py,sha256=f45fGhmHiHBxaFeaajRs1NnzFHH1qnXzU5Sgv6-s7MI,1648
|
28
|
+
qulab/monitor/toolbar.py,sha256=T_Snin6N3NpzTkSVMVTfKsbXyyv5Tj6hJEbXvTefEqY,10339
|
29
29
|
qulab/scan/__init__.py,sha256=Z88CzGsvr1VdKbF6aYgQv3j-fsEkKmqvpvX4LlzOM98,87
|
30
30
|
qulab/scan/curd.py,sha256=tiJ0oo2DqirK2fpAAAA-6OFAjAuKG5W21nLqfLvTLyQ,6895
|
31
31
|
qulab/scan/models.py,sha256=JFofv-RH0gpS3--M6izXiQg7iGkS9a_em2DwhS5kTTk,17626
|
@@ -96,9 +96,9 @@ qulab/visualization/plot_seq.py,sha256=UWTS6p9nfX_7B8ehcYo6UnSTUCjkBsNU9jiOeW2ca
|
|
96
96
|
qulab/visualization/qdat.py,sha256=ZeevBYWkzbww4xZnsjHhw7wRorJCBzbG0iEu-XQB4EA,5735
|
97
97
|
qulab/visualization/rot3d.py,sha256=CuLfG1jw1032HNNZxzC0OWtNzKsbj2e2WRYhMWDgvMQ,1321
|
98
98
|
qulab/visualization/widgets.py,sha256=6KkiTyQ8J-ei70LbPQZAK35wjktY47w2IveOa682ftA,3180
|
99
|
-
qulab-2.11.
|
100
|
-
qulab-2.11.
|
101
|
-
qulab-2.11.
|
102
|
-
qulab-2.11.
|
103
|
-
qulab-2.11.
|
104
|
-
qulab-2.11.
|
99
|
+
qulab-2.11.10.dist-info/licenses/LICENSE,sha256=PRzIKxZtpQcH7whTG6Egvzl1A0BvnSf30tmR2X2KrpA,1065
|
100
|
+
qulab-2.11.10.dist-info/METADATA,sha256=vgFOZFLgcFe7je7WZiaP9_T1AMQq5LjIE_s6pIdeCGU,3945
|
101
|
+
qulab-2.11.10.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
102
|
+
qulab-2.11.10.dist-info/entry_points.txt,sha256=b0v1GXOwmxY-nCCsPN_rHZZvY9CtTbWqrGj8u1m8yHo,45
|
103
|
+
qulab-2.11.10.dist-info/top_level.txt,sha256=3T886LbAsbvjonu_TDdmgxKYUn939BVTRPxPl9r4cEg,6
|
104
|
+
qulab-2.11.10.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|