QuLab 2.4.0__cp312-cp312-macosx_10_13_universal2.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (97) hide show
  1. QuLab-2.4.0.dist-info/LICENSE +21 -0
  2. QuLab-2.4.0.dist-info/METADATA +105 -0
  3. QuLab-2.4.0.dist-info/RECORD +97 -0
  4. QuLab-2.4.0.dist-info/WHEEL +5 -0
  5. QuLab-2.4.0.dist-info/entry_points.txt +2 -0
  6. QuLab-2.4.0.dist-info/top_level.txt +1 -0
  7. qulab/__init__.py +3 -0
  8. qulab/__main__.py +30 -0
  9. qulab/dicttree.py +511 -0
  10. qulab/executor/__init__.py +5 -0
  11. qulab/executor/__main__.py +89 -0
  12. qulab/executor/load.py +202 -0
  13. qulab/executor/schedule.py +223 -0
  14. qulab/executor/storage.py +143 -0
  15. qulab/executor/transform.py +90 -0
  16. qulab/executor/utils.py +107 -0
  17. qulab/fun.cpython-312-darwin.so +0 -0
  18. qulab/monitor/__init__.py +1 -0
  19. qulab/monitor/__main__.py +8 -0
  20. qulab/monitor/config.py +41 -0
  21. qulab/monitor/dataset.py +77 -0
  22. qulab/monitor/event_queue.py +54 -0
  23. qulab/monitor/mainwindow.py +234 -0
  24. qulab/monitor/monitor.py +93 -0
  25. qulab/monitor/ploter.py +123 -0
  26. qulab/monitor/qt_compat.py +16 -0
  27. qulab/monitor/toolbar.py +265 -0
  28. qulab/scan/__init__.py +3 -0
  29. qulab/scan/curd.py +221 -0
  30. qulab/scan/expression.py +646 -0
  31. qulab/scan/models.py +554 -0
  32. qulab/scan/optimize.py +76 -0
  33. qulab/scan/query.py +374 -0
  34. qulab/scan/record.py +603 -0
  35. qulab/scan/scan.py +1166 -0
  36. qulab/scan/server.py +533 -0
  37. qulab/scan/space.py +213 -0
  38. qulab/scan/utils.py +229 -0
  39. qulab/storage/__init__.py +0 -0
  40. qulab/storage/__main__.py +51 -0
  41. qulab/storage/backend/__init__.py +0 -0
  42. qulab/storage/backend/redis.py +204 -0
  43. qulab/storage/base_dataset.py +352 -0
  44. qulab/storage/chunk.py +60 -0
  45. qulab/storage/dataset.py +127 -0
  46. qulab/storage/file.py +273 -0
  47. qulab/storage/models/__init__.py +22 -0
  48. qulab/storage/models/base.py +4 -0
  49. qulab/storage/models/config.py +28 -0
  50. qulab/storage/models/file.py +89 -0
  51. qulab/storage/models/ipy.py +58 -0
  52. qulab/storage/models/models.py +88 -0
  53. qulab/storage/models/record.py +161 -0
  54. qulab/storage/models/report.py +22 -0
  55. qulab/storage/models/tag.py +93 -0
  56. qulab/storage/storage.py +95 -0
  57. qulab/sys/__init__.py +0 -0
  58. qulab/sys/chat.py +688 -0
  59. qulab/sys/device/__init__.py +3 -0
  60. qulab/sys/device/basedevice.py +229 -0
  61. qulab/sys/device/loader.py +86 -0
  62. qulab/sys/device/utils.py +79 -0
  63. qulab/sys/drivers/FakeInstrument.py +52 -0
  64. qulab/sys/drivers/__init__.py +0 -0
  65. qulab/sys/ipy_events.py +125 -0
  66. qulab/sys/net/__init__.py +0 -0
  67. qulab/sys/net/bencoder.py +205 -0
  68. qulab/sys/net/cli.py +169 -0
  69. qulab/sys/net/dhcp.py +543 -0
  70. qulab/sys/net/dhcpd.py +176 -0
  71. qulab/sys/net/kad.py +1142 -0
  72. qulab/sys/net/kcp.py +192 -0
  73. qulab/sys/net/nginx.py +192 -0
  74. qulab/sys/progress.py +190 -0
  75. qulab/sys/rpc/__init__.py +0 -0
  76. qulab/sys/rpc/client.py +0 -0
  77. qulab/sys/rpc/exceptions.py +96 -0
  78. qulab/sys/rpc/msgpack.py +1052 -0
  79. qulab/sys/rpc/msgpack.pyi +41 -0
  80. qulab/sys/rpc/router.py +35 -0
  81. qulab/sys/rpc/rpc.py +412 -0
  82. qulab/sys/rpc/serialize.py +139 -0
  83. qulab/sys/rpc/server.py +29 -0
  84. qulab/sys/rpc/socket.py +29 -0
  85. qulab/sys/rpc/utils.py +25 -0
  86. qulab/sys/rpc/worker.py +0 -0
  87. qulab/sys/rpc/zmq_socket.py +220 -0
  88. qulab/version.py +1 -0
  89. qulab/visualization/__init__.py +188 -0
  90. qulab/visualization/__main__.py +71 -0
  91. qulab/visualization/_autoplot.py +464 -0
  92. qulab/visualization/plot_circ.py +319 -0
  93. qulab/visualization/plot_layout.py +408 -0
  94. qulab/visualization/plot_seq.py +242 -0
  95. qulab/visualization/qdat.py +152 -0
  96. qulab/visualization/rot3d.py +23 -0
  97. qulab/visualization/widgets.py +86 -0
@@ -0,0 +1,107 @@
1
+ from pathlib import Path
2
+
3
+ from .load import load_workflow
4
+
5
+
6
+ class Node:
7
+
8
+ def __init__(self, name: str):
9
+ self.name = name
10
+ self.dependents = []
11
+
12
+
13
+ class Tree:
14
+
15
+ def __init__(self):
16
+ self.nodes = {}
17
+ self.heads = []
18
+
19
+ def add_node(self, node: str):
20
+ self.nodes[node] = Node(node)
21
+
22
+
23
+ def dependent_tree(node: str, code_path: str | Path) -> dict[str, list[str]]:
24
+ '''
25
+ Returns a dict of nodes and their dependents.
26
+ '''
27
+ tree = {}
28
+ for n in load_workflow(node, code_path).depends()[0]:
29
+ tree[n] = dependent_tree(n, code_path)
30
+ return tree
31
+
32
+
33
+ def workflow_template(deps: list[str]) -> str:
34
+ return f"""
35
+ from loguru import logger
36
+
37
+ import numpy as np
38
+
39
+
40
+ # 多长时间应该检查一次校准实验,单位是秒。
41
+ __timeout__ = 200
42
+
43
+ def depends():
44
+ return [{deps!r}]
45
+
46
+
47
+ def calibrate():
48
+ logger.info(f"run {{__name__}}")
49
+
50
+ # calibrate 是一个完整的校准实验,如power Rabi,Ramsey等。
51
+ # 你需要足够的扫描点,以使得后续的 analyze 可以拟合出合适的参数。
52
+
53
+ # 这里只是一个示例,实际上你需要在这里写上你的校准代码。
54
+ x = np.linspace(0, 2*np.pi, 101)
55
+ y = []
56
+ for i in x:
57
+ y.append(np.sin(i))
58
+
59
+ return x, y
60
+
61
+
62
+ def analyze(*args, history):
63
+ import random
64
+
65
+ # 完整校准后的状态有两种:OK 和 Bad,分别对应校准成功和校准失败。
66
+ # 校准失败是指出现坏数据,无法简单通过重新运行本次校准解决,需要
67
+ # 检查前置步骤。
68
+ state = random.choice(['OK', 'Bad'])
69
+
70
+ # 参数是一个字典,包含了本次校准得到的参数,后续会更新到config表中。
71
+ parameters = {{'gate.R.Q1.params.amp':1}}
72
+
73
+ # 其他信息可以是任何可序列化的内容,你可以将你想要记录的信息放在这里。
74
+ # 下次校准分析时,这些信息也会在 history 参数中一起传入,帮助你在下
75
+ # 次分析时对比参考。
76
+ other_infomation = {{}}
77
+
78
+ return state, parameters, other_infomation
79
+
80
+
81
+ def check():
82
+ logger.info(f"check {{__name__}}")
83
+
84
+ # check 是一个快速检查实验,用于检查校准是否过时。
85
+ # 你只需要少数扫描点,让后续的 check_analyze 知道参数是否漂移,数据
86
+ # 坏没坏就够了,不要求拟合。
87
+
88
+ # 这里只是一个示例,实际上你需要在这里写上你的检查代码。
89
+ x = np.linspace(0, 2*np.pi, 5)
90
+ y = []
91
+ for i in x:
92
+ y.append(np.sin(i))
93
+
94
+ return x, y
95
+
96
+
97
+ def check_analyze(*args, history):
98
+ import random
99
+
100
+ # 状态有三种:Outdated, OK, Bad,分别对应过时、正常、坏数据。
101
+ # Outdated 是指数据过时,即参数漂了,需要重新校准。
102
+ # OK 是指数据正常,参数也没漂,不用重新校准。
103
+ # Bad 是指数据坏了,无法校准,需要检查前置步骤。
104
+ state = random.choice(['Outdated', 'OK', 'Bad'])
105
+
106
+ return state, {{}}, {{}}
107
+ """
Binary file
@@ -0,0 +1 @@
1
+ from .monitor import Monitor, get_monitor
@@ -0,0 +1,8 @@
1
+ import click
2
+
3
+ from .monitor import Monitor
4
+
5
+
6
+ @click.command(name='monitor')
7
+ def main():
8
+ pass
@@ -0,0 +1,41 @@
1
+ import numpy as np
2
+
3
+ style = '''
4
+ QWidget {
5
+ font: medium Ubuntu;
6
+ background-color: #011F2F;
7
+ font-size: 16px;
8
+ font-size: 16px;
9
+ color:#FFFFFF;
10
+ }
11
+ '''
12
+ #
13
+ Nroll = 6
14
+
15
+ # Format of the data.
16
+ forms = {
17
+ "mag": lambda w, ang: np.abs(w),
18
+ "phase": lambda w, ang: np.angle(w),
19
+ "real": lambda w, ang: np.real(w),
20
+ "imag": lambda w, ang: np.imag(w),
21
+ "rot": lambda w, ang: np.real(np.exp(1j * ang) * np.array(w))
22
+ }
23
+ form_keys = list(forms.keys())
24
+ #
25
+ COL_SEL = (0, 0, 0)
26
+ COL_UNSEL = (6, 6, 8)
27
+ #
28
+
29
+ defualt_colors = [
30
+ (200, 0, 0),
31
+ (55, 100, 180),
32
+ (40, 80, 150),
33
+ (30, 50, 110),
34
+ (25, 40, 70),
35
+ (25, 30, 50),
36
+ ]
37
+
38
+ widths = [3, 2, 2, 2, 1, 1]
39
+ SymSize = [5, 0, 0, 0, 0, 0]
40
+ ridx = list(range(Nroll))
41
+ ridx.reverse()
@@ -0,0 +1,77 @@
1
+ from collections import defaultdict, deque
2
+
3
+ from .config import Nroll
4
+
5
+ PAUSE_TIME = 1
6
+
7
+ Number = int | float | complex
8
+
9
+
10
+ def remove_duplicates(input_list: list[str]) -> list[str]:
11
+ """
12
+ Remove duplicates from a list of strings, keeping the order of the elements.
13
+ """
14
+ return list(dict.fromkeys(input_list))
15
+
16
+
17
+ class Dataset():
18
+
19
+ def __init__(self):
20
+ self.column_names = []
21
+ self.box = deque(maxlen=Nroll)
22
+ self.dirty = True
23
+
24
+ def clear(self):
25
+ self.box.clear()
26
+
27
+ def clear_history(self):
28
+ o = self.box.popleft()
29
+ self.box.clear()
30
+ self.box.appendleft(o)
31
+
32
+ def set_column_names(self, column_names: list[str]):
33
+ column_names = remove_duplicates(column_names)
34
+ if column_names != self.column_names:
35
+ self.clear()
36
+ self.column_names = column_names
37
+
38
+ def get_data(self, step: int, xname: str,
39
+ yname: str) -> tuple[list[Number], list[Number]]:
40
+ try:
41
+ b = self.box[step]
42
+ except IndexError:
43
+ return [], []
44
+ return b[xname], b[yname]
45
+
46
+ def append(self, dataframe: list[Number] | list[list[Number]]):
47
+ if not dataframe:
48
+ return
49
+ try:
50
+ iter(dataframe[0]) # test if dataframe is a list of list
51
+ self._append_traces(dataframe)
52
+ except TypeError:
53
+ self._append_points(dataframe)
54
+
55
+ def roll(self):
56
+ self.box.appendleft(defaultdict(list))
57
+
58
+ def _append_points(self, points: list[Number]):
59
+ self.dirty = True
60
+ assert (len(points) == len(
61
+ self.column_names)), (f"-PointDataBox\n"
62
+ f"-ap\n"
63
+ f"-Length Must be same\n"
64
+ f"the column_names : {self.column_names}\n"
65
+ f"given data : {points}")
66
+ for name, p in zip(self.column_names, points):
67
+ self.box[0][name].append(p)
68
+
69
+ def _append_traces(self, traces: list[list[Number]]):
70
+ self.dirty = True
71
+ assert (len(traces) == len(
72
+ self.column_names)), (f"-TraceDataBox\n"
73
+ f"-at\n"
74
+ f"-Length Must be same\n"
75
+ f"the column_names : {self.column_names}\n"
76
+ f"given data : {traces}")
77
+ self.box.appendleft(dict(zip(self.column_names, traces)))
@@ -0,0 +1,54 @@
1
+ import warnings
2
+ from multiprocessing import Queue
3
+
4
+ from .dataset import Dataset
5
+ from .toolbar import ToolBar
6
+
7
+
8
+ class EventQueue():
9
+
10
+ def __init__(self, queue: Queue, toolbar: ToolBar, point_dataset: Dataset,
11
+ trace_dataset: Dataset):
12
+ self.toolbar = toolbar
13
+ self.queue = queue
14
+ self.point_dataset = point_dataset
15
+ self.trace_dataset = trace_dataset
16
+
17
+ def flush(self):
18
+ while (not self.queue.empty()):
19
+ words = self.queue.get()
20
+ try:
21
+ assert (isinstance(
22
+ words, tuple)), "QueueHandler - fifo Content must be tuple"
23
+ assert (
24
+ len(words) == 2
25
+ ), "QueueHandler -the tuple must be like (\"command_type\" , \"DATA\")"
26
+ cmd, data = words
27
+ assert (isinstance(
28
+ cmd, str)), "QueueHandler - the command should be a string"
29
+ except AssertionError as e:
30
+ warnings.warn(e.args[0])
31
+ continue
32
+
33
+ self.handle(cmd, data)
34
+
35
+ def handle(self, cmd, data):
36
+ match cmd:
37
+ case "PN":
38
+ self.point_dataset.set_column_names(data)
39
+ self.toolbar.refresh_comb()
40
+ case "TN":
41
+ self.trace_dataset.set_column_names(data)
42
+ self.toolbar.refresh_comb()
43
+ case "PD":
44
+ self.point_dataset.append(data)
45
+ case "TD":
46
+ self.trace_dataset.append(data)
47
+ case "ROLL":
48
+ self.point_dataset.roll()
49
+ case "PXY":
50
+ self.toolbar.set_point_text(data)
51
+ case "TXY":
52
+ self.toolbar.set_trace_text(data)
53
+ case _:
54
+ warnings.warn(f"QueueHandler - unknown command : {cmd}")
@@ -0,0 +1,234 @@
1
+ from multiprocessing import Queue
2
+ from typing import Literal
3
+
4
+ from .config import forms, ridx, style
5
+ from .dataset import Dataset
6
+ from .event_queue import EventQueue
7
+ from .ploter import PlotWidget
8
+ from .qt_compat import (BottomDockWidgetArea, QtCore, QtWidgets,
9
+ ScrollBarAlwaysOff, ScrollBarAlwaysOn,
10
+ TopDockWidgetArea)
11
+ from .toolbar import ToolBar
12
+
13
+
14
+ class MainWindow(QtWidgets.QMainWindow):
15
+
16
+ def __init__(self,
17
+ queue: Queue,
18
+ ncols=3,
19
+ plot_minimum_height=350,
20
+ plot_colors: list[tuple[int, int, int]] | None = None):
21
+ super().__init__()
22
+ self.ncols = ncols
23
+ self.need_reshuffled = False
24
+ self.plot_minimum_height = plot_minimum_height
25
+ self.plot_widgets: list[PlotWidget] = []
26
+ self.plot_colors = plot_colors
27
+ self.toolbar = ToolBar()
28
+ self.trace_data_box = Dataset()
29
+ self.point_data_box = Dataset()
30
+ self.queue = EventQueue(queue, self.toolbar, self.point_data_box,
31
+ self.trace_data_box)
32
+
33
+ self.init_ui()
34
+
35
+ self.timer = QtCore.QTimer()
36
+ self.timer.timeout.connect(self.update)
37
+ self.timer.start(250)
38
+
39
+ def init_ui(self):
40
+ self.setStyleSheet(style)
41
+ self.setMinimumHeight(500)
42
+ self.setMinimumWidth(700)
43
+ self.scroll = QtWidgets.QScrollArea(
44
+ ) # Scroll Area which contains the widgets, set as the centralWidget
45
+ self.widget = QtWidgets.QWidget(
46
+ ) # Widget that contains the collection of Vertical Box
47
+ self.layout = QtWidgets.QGridLayout()
48
+ self.widget.setLayout(self.layout)
49
+
50
+ #Scroll Area Properties
51
+ #self.setCorner(Qt.TopSection, Qt.TopDockWidgetArea);
52
+ self.scroll.setVerticalScrollBarPolicy(ScrollBarAlwaysOn)
53
+ self.scroll.setHorizontalScrollBarPolicy(ScrollBarAlwaysOff)
54
+ self.scroll.setWidgetResizable(True)
55
+ self.scroll.setWidget(self.widget)
56
+ self.setCentralWidget(self.scroll)
57
+
58
+ self.dock = QtWidgets.QDockWidget(self)
59
+ self.dock.setAllowedAreas(TopDockWidgetArea | BottomDockWidgetArea)
60
+ self.addDockWidget(TopDockWidgetArea, self.dock)
61
+ self.dock.setFloating(False)
62
+ self.dock.setWidget(self.toolbar)
63
+ self.dock.setFeatures(QtWidgets.QDockWidget.DockWidgetMovable)
64
+ #self.tabifyDockWidget(self.dock,None);
65
+ #self.addDockWidget(self.dock);
66
+ #self.setStatusBar(self.toolbar);
67
+ #self.layout.addWidget(self.toolbar , 0 , 0 , 1, self.ncol);
68
+
69
+ self.setWindowTitle('Scroll multi view')
70
+ self.show()
71
+ self.toolbar.set_mainwindow(self)
72
+ self.toolbar.pb.setChecked(True)
73
+
74
+ @property
75
+ def mode(self) -> Literal["P", "T"]:
76
+ return self.toolbar.mode
77
+
78
+ @property
79
+ def dataset(self) -> Dataset:
80
+ return {"P": self.point_data_box, "T": self.trace_data_box}[self.mode]
81
+
82
+ def set_ncols(self, x: int):
83
+ x = max(1, min(10, int(x)))
84
+ if (x != self.ncols):
85
+ self.need_reshuffled = True
86
+ self.ncols = x
87
+
88
+ def add_subplot(self):
89
+ n = len(self.plot_widgets)
90
+ pw = PlotWidget(self.plot_minimum_height, self.plot_colors)
91
+ self.plot_widgets.append(pw)
92
+ grid_row = n // self.ncols
93
+ grid_col = n % self.ncols
94
+ self.layout.addWidget(pw, grid_row + 1, grid_col)
95
+ return pw
96
+
97
+ def create_subplots(self, xy_pairs):
98
+ for xn, yn in xy_pairs:
99
+ pw = self.add_subplot()
100
+ pw.set_X_label(xn)
101
+ pw.set_Y_label(yn)
102
+ self.do_link()
103
+ self.all_enable_auto_range()
104
+
105
+ def clear_subplots(self):
106
+ for i in range(len(self.plot_widgets)):
107
+ self.layout.removeWidget(self.plot_widgets[i])
108
+ self.plot_widgets[i].setParent(None)
109
+ self.plot_widgets.clear()
110
+
111
+ def remove_plot(self, w: PlotWidget):
112
+ w.setParent(None)
113
+ self.plot_widgets.remove(w)
114
+ self.reshuffle()
115
+
116
+ def drop_last_plot(self, i_=-1):
117
+ # delete the one
118
+ i = int(i_)
119
+ if (i < len(self.plot_widgets)):
120
+ w = self.plot_widgets[i]
121
+ w.setParent(None)
122
+ del w
123
+ del self.plot_widgets[i]
124
+ self.reshuffle()
125
+
126
+ def reshuffle(self):
127
+ for idx, widget in enumerate(self.plot_widgets):
128
+ widget.setParent(None)
129
+ grid_row = idx // self.ncols
130
+ grid_col = idx % self.ncols
131
+ self.layout.addWidget(widget, grid_row + 1, grid_col)
132
+
133
+ def keyPressEvent(self, ev):
134
+ #print(ev.text());
135
+ tx = ev.text()
136
+ if (tx in ['_', '-']):
137
+ self.set_ncols(self.ncols - 1)
138
+ elif (tx in ['=', '+']):
139
+ self.set_ncols(self.ncols + 1)
140
+
141
+ def mouse_click(self):
142
+ pass
143
+
144
+ def do_link(self):
145
+ """
146
+ link the plot
147
+
148
+ share the same x or y axis
149
+ """
150
+ same_X = {}
151
+ xy_pairs = self.toolbar.xypairs
152
+ for idx, xyn in enumerate(xy_pairs):
153
+ xn, yn = xyn
154
+ if xn not in same_X:
155
+ same_X[xn] = []
156
+ same_X[xn].append(idx)
157
+
158
+ sharex, sharey = self.toolbar.sharexy()
159
+
160
+ s_A = not (sharex and sharey)
161
+ for x, yidxs in same_X.items():
162
+ pre_yidx = -1
163
+ for yidx in yidxs:
164
+ if (-1 != pre_yidx):
165
+ if (s_A):
166
+ self.plot_widgets[pre_yidx].plotItem.vb.setXLink(None)
167
+ self.plot_widgets[pre_yidx].plotItem.vb.setYLink(None)
168
+
169
+ if sharex:
170
+ self.plot_widgets[pre_yidx].plotItem.vb.setXLink(
171
+ self.plot_widgets[yidx].plotItem.vb)
172
+
173
+ if sharey:
174
+ self.plot_widgets[pre_yidx].plotItem.vb.setYLink(
175
+ self.plot_widgets[yidx].plotItem.vb)
176
+ pre_yidx = yidx
177
+
178
+ def all_auto_range(self):
179
+ for pw in self.plot_widgets:
180
+ pw.auto_range()
181
+
182
+ def all_enable_auto_range(self):
183
+ for pw in self.plot_widgets:
184
+ pw.enable_auto_range()
185
+
186
+ def update(self):
187
+ # update the queue
188
+ self.queue.flush()
189
+
190
+ rescale = False
191
+
192
+ # setup the xyfm
193
+ if (self.toolbar.xypairs_dirty):
194
+ self.clear_subplots()
195
+ self.create_subplots(self.toolbar.xypairs)
196
+ self.toolbar.xypairs_dirty = False
197
+ rescale = True
198
+
199
+ if (self.toolbar.link_dirty):
200
+ self.do_link()
201
+ self.toolbar.link_dirty = False
202
+
203
+ if (self.need_reshuffled):
204
+ self.need_reshuffled = False
205
+ self.reshuffle()
206
+
207
+ # checking the log space
208
+ if (self.toolbar.xyfm_dirty):
209
+ for pw in self.plot_widgets:
210
+ pw.plotItem.ctrl.logXCheck.setChecked(self.toolbar.lx)
211
+ pw.plotItem.ctrl.logYCheck.setChecked(self.toolbar.ly)
212
+
213
+ #update the plot
214
+ # if clear is set then do the clear :
215
+ if (self.toolbar.CR_flag):
216
+ self.toolbar.CR_flag = False
217
+ self.dataset.clear_history()
218
+ self.dataset.dirty = True
219
+
220
+ if (self.dataset.dirty or self.toolbar.xyfm_dirty or rescale):
221
+ self.dataset.dirty = False
222
+ self.xyfm_dirty = False
223
+ for pw in self.plot_widgets:
224
+
225
+ fx = forms[self.toolbar.fx]
226
+ fy = forms[self.toolbar.fy]
227
+ for i in ridx:
228
+ x, y = self.dataset.get_data(i, pw.xname, pw.yname)
229
+ l = min(len(x), len(y))
230
+ x, y = fx(x[:l], 0), fy(y[:l], 0)
231
+ pw.set_data(i, x, y)
232
+ pw.update()
233
+ if rescale:
234
+ pw.auto_range()
@@ -0,0 +1,93 @@
1
+ import multiprocessing as mp
2
+ import sys
3
+
4
+ # try:
5
+ # mp.set_start_method("spawn")
6
+ # except:
7
+ # pass
8
+
9
+
10
+ def main(queue: mp.Queue,
11
+ ncols: int = 4,
12
+ minimum_height: int = 400,
13
+ colors: list[tuple[int, int, int]] = []):
14
+ from .mainwindow import MainWindow
15
+ from .qt_compat import QtWidgets
16
+
17
+ app = QtWidgets.QApplication(sys.argv)
18
+ main = MainWindow(queue, ncols, minimum_height, colors)
19
+ sys.exit(app.exec())
20
+
21
+
22
+ class Monitor():
23
+
24
+ def __init__(self,
25
+ number_of_columns: int = 4,
26
+ minimum_height: int = 400,
27
+ colors: list[tuple[int, int, int]] = []):
28
+ self.colors = [tuple(color) for color in colors]
29
+ self.number_of_columns = number_of_columns
30
+ self.minimum_height = minimum_height
31
+ self.queue = mp.Queue(20)
32
+ self.process = None
33
+ self.start()
34
+
35
+ def start(self):
36
+ if self.process is not None and self.process.is_alive():
37
+ return
38
+ self.queue = mp.Queue(20)
39
+ self.process = mp.Process(target=main,
40
+ args=(self.queue, self.number_of_columns,
41
+ self.minimum_height, self.colors))
42
+ self.process.start()
43
+
44
+ def _put(self, w: tuple):
45
+ self.queue.put(w)
46
+
47
+ def roll(self):
48
+ self._put(("ROLL", None))
49
+
50
+ def set_column_names(self, *arg):
51
+ self._put(('PN', list(arg)))
52
+
53
+ def add_point(self, *arg):
54
+ self._put(('PD', list(arg)))
55
+
56
+ def set_plots(self, arg):
57
+ """
58
+ arg: str, like "(x,y)" or "(x1,y1);(x2,y2);"
59
+ """
60
+ self._put(('PXY', str(arg)))
61
+
62
+ def set_trace_column_names(self, *arg):
63
+ self._put(('TN', list(arg)))
64
+
65
+ def add_trace(self, *arg):
66
+ self._put(('TD', list(arg)))
67
+
68
+ def set_trace_plots(self, arg):
69
+ """
70
+ arg: str, like "(x,y)" or "(x1,y1);(x2,y2);"
71
+ """
72
+ self._put(('TXY', str(arg)))
73
+
74
+ def __del__(self):
75
+ try:
76
+ self.process.kill()
77
+ except:
78
+ pass
79
+
80
+ def is_alive(self):
81
+ return self.process.is_alive()
82
+
83
+
84
+ _monitor = None
85
+
86
+
87
+ def get_monitor(auto_open=True):
88
+ global _monitor
89
+
90
+ if auto_open and (_monitor is None or not _monitor.is_alive()):
91
+ _monitor = Monitor()
92
+
93
+ return _monitor