QuLab 2.0.1__cp311-cp311-macosx_10_9_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 (82) hide show
  1. QuLab-2.0.1.dist-info/LICENSE +21 -0
  2. QuLab-2.0.1.dist-info/METADATA +95 -0
  3. QuLab-2.0.1.dist-info/RECORD +82 -0
  4. QuLab-2.0.1.dist-info/WHEEL +5 -0
  5. QuLab-2.0.1.dist-info/entry_points.txt +2 -0
  6. QuLab-2.0.1.dist-info/top_level.txt +1 -0
  7. qulab/__init__.py +1 -0
  8. qulab/__main__.py +24 -0
  9. qulab/fun.cpython-311-darwin.so +0 -0
  10. qulab/monitor/__init__.py +1 -0
  11. qulab/monitor/__main__.py +8 -0
  12. qulab/monitor/config.py +41 -0
  13. qulab/monitor/dataset.py +77 -0
  14. qulab/monitor/event_queue.py +54 -0
  15. qulab/monitor/mainwindow.py +234 -0
  16. qulab/monitor/monitor.py +93 -0
  17. qulab/monitor/ploter.py +123 -0
  18. qulab/monitor/qt_compat.py +16 -0
  19. qulab/monitor/toolbar.py +265 -0
  20. qulab/scan/__init__.py +4 -0
  21. qulab/scan/base.py +548 -0
  22. qulab/scan/dataset.py +0 -0
  23. qulab/scan/expression.py +472 -0
  24. qulab/scan/optimize.py +0 -0
  25. qulab/scan/scanner.py +270 -0
  26. qulab/scan/transforms.py +16 -0
  27. qulab/scan/utils.py +37 -0
  28. qulab/storage/__init__.py +0 -0
  29. qulab/storage/__main__.py +51 -0
  30. qulab/storage/backend/__init__.py +0 -0
  31. qulab/storage/backend/redis.py +204 -0
  32. qulab/storage/base_dataset.py +352 -0
  33. qulab/storage/chunk.py +60 -0
  34. qulab/storage/dataset.py +127 -0
  35. qulab/storage/file.py +273 -0
  36. qulab/storage/models/__init__.py +22 -0
  37. qulab/storage/models/base.py +4 -0
  38. qulab/storage/models/config.py +28 -0
  39. qulab/storage/models/file.py +89 -0
  40. qulab/storage/models/ipy.py +58 -0
  41. qulab/storage/models/models.py +88 -0
  42. qulab/storage/models/record.py +161 -0
  43. qulab/storage/models/report.py +22 -0
  44. qulab/storage/models/tag.py +93 -0
  45. qulab/storage/storage.py +95 -0
  46. qulab/sys/__init__.py +0 -0
  47. qulab/sys/chat.py +688 -0
  48. qulab/sys/device/__init__.py +3 -0
  49. qulab/sys/device/basedevice.py +221 -0
  50. qulab/sys/device/loader.py +86 -0
  51. qulab/sys/device/utils.py +46 -0
  52. qulab/sys/drivers/FakeInstrument.py +52 -0
  53. qulab/sys/drivers/__init__.py +0 -0
  54. qulab/sys/ipy_events.py +125 -0
  55. qulab/sys/net/__init__.py +0 -0
  56. qulab/sys/net/bencoder.py +205 -0
  57. qulab/sys/net/cli.py +169 -0
  58. qulab/sys/net/dhcp.py +543 -0
  59. qulab/sys/net/dhcpd.py +176 -0
  60. qulab/sys/net/kad.py +1142 -0
  61. qulab/sys/net/kcp.py +192 -0
  62. qulab/sys/net/nginx.py +192 -0
  63. qulab/sys/progress.py +190 -0
  64. qulab/sys/rpc/__init__.py +0 -0
  65. qulab/sys/rpc/client.py +0 -0
  66. qulab/sys/rpc/exceptions.py +96 -0
  67. qulab/sys/rpc/msgpack.py +1052 -0
  68. qulab/sys/rpc/msgpack.pyi +41 -0
  69. qulab/sys/rpc/rpc.py +412 -0
  70. qulab/sys/rpc/serialize.py +139 -0
  71. qulab/sys/rpc/server.py +29 -0
  72. qulab/sys/rpc/socket.py +29 -0
  73. qulab/sys/rpc/utils.py +25 -0
  74. qulab/sys/rpc/worker.py +0 -0
  75. qulab/version.py +1 -0
  76. qulab/visualization/__init__.py +188 -0
  77. qulab/visualization/__main__.py +71 -0
  78. qulab/visualization/_autoplot.py +457 -0
  79. qulab/visualization/plot_layout.py +408 -0
  80. qulab/visualization/plot_seq.py +90 -0
  81. qulab/visualization/qdat.py +152 -0
  82. qulab/visualization/widgets.py +86 -0
@@ -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
@@ -0,0 +1,123 @@
1
+ from .config import COL_SEL, COL_UNSEL, SymSize, defualt_colors, ridx, widths
2
+ from .qt_compat import QtWidgets
3
+
4
+ # the plotting widget
5
+ try:
6
+ import pyqtgraph as pg
7
+ except:
8
+ raise ImportError("Please install pyqtgraph first")
9
+
10
+ try:
11
+ import pyperclip as pc
12
+ hasCliper = True
13
+ except:
14
+ hasCliper = False
15
+
16
+
17
+ class PlotWidget(pg.PlotWidget):
18
+
19
+ def __init__(self, minimum_height=300, colors=None):
20
+ self.XAxisLinked = False
21
+ self.YAxisLinked = False
22
+ if colors is None:
23
+ colors = defualt_colors
24
+ elif len(colors) < len(defualt_colors):
25
+ colors.extend(defualt_colors[len(colors):])
26
+ self.colors = colors
27
+ self.xname = ""
28
+ self.yname = ""
29
+ super().__init__()
30
+
31
+ self.setMinimumHeight(minimum_height)
32
+ self.showGrid(x=True, y=True)
33
+ self.setBackground(COL_UNSEL)
34
+
35
+ self.plotItem.vb.autoRange()
36
+
37
+ ## Labeling
38
+ self.XLabel = QtWidgets.QLabel(self)
39
+ self.XLabel.setText("X:")
40
+ self.XLabel.move(0, 5)
41
+ self.YLabel = QtWidgets.QLabel(self)
42
+ self.YLabel.setText("Y:")
43
+ self.YLabel.move(0, 35)
44
+
45
+ self.plots = {}
46
+ self.clippos1 = 0
47
+ self.clippos2 = 0
48
+ self.range_select = False
49
+ for i in ridx:
50
+ self.plots[i] = \
51
+ self.plot([],[] ,pen={"color":self.colors[i] ,"width":widths[i]} ,
52
+ symbolBrush = self.colors[i],
53
+ symbolPen = { "width":0 ,"color":self.colors[i] } ,
54
+ symbolSize =SymSize[i] ,
55
+ )
56
+ self.update()
57
+
58
+ def set_X_label(self, w):
59
+ self.xname = w
60
+ self.XLabel.setText(f"X:{w}")
61
+
62
+ def set_Y_label(self, w):
63
+ self.yname = w
64
+ self.YLabel.setText(f"Y:{w}")
65
+
66
+ def auto_range(self):
67
+ self.plotItem.vb.autoRange()
68
+
69
+ def enable_auto_range(self):
70
+ self.plotItem.vb.enableAutoRange()
71
+
72
+ def keyPressEvent(self, ev):
73
+ #print(ev.text());
74
+ tx = ev.text()
75
+ if ('f' == tx or 'F' == tx):
76
+ self.plotItem.vb.autoRange()
77
+ if ('a' == tx or 'A' == tx):
78
+ self.plotItem.vb.setAutoPan()
79
+ if ('r' == tx or 'R' == tx):
80
+ self.range_select = True
81
+ super().keyPressEvent(ev)
82
+
83
+ def keyReleaseEvent(self, ev):
84
+ #print(ev.text());
85
+ tx = ev.text()
86
+ if ('f' == tx or 'F' == tx):
87
+ self.plotItem.vb.autoRange()
88
+ if ('a' == tx or 'A' == tx):
89
+ self.plotItem.vb.setAutoPan()
90
+ if ('r' == tx or 'R' == tx):
91
+ self.range_select = False
92
+ super().keyReleaseEvent(ev)
93
+
94
+ def mousePressEvent(self, ev):
95
+ if (4 == ev.button()):
96
+ # print(ev.flags())
97
+ if (hasCliper):
98
+ # print("Mouse is pressed")
99
+ self.clippos1 = self.plotItem.vb.mapSceneToView(ev.pos()).x()
100
+ else:
101
+ super().mousePressEvent(ev)
102
+
103
+ def mouseReleaseEvent(self, ev):
104
+ if (4 == ev.button()):
105
+ p = ev.pos()
106
+ if (hasCliper):
107
+ # print("Mouse is released")
108
+ self.clippos2 = self.plotItem.vb.mapSceneToView(ev.pos()).x()
109
+ if (self.range_select):
110
+ pc.copy(f"{self.clippos1},{self.clippos2}")
111
+ else:
112
+ pc.copy(self.clippos2)
113
+ else:
114
+ super().mouseReleaseEvent(ev)
115
+
116
+ def update(self):
117
+ super().update()
118
+
119
+ def set_data(self, i, x, y):
120
+ self.plots[i].setData(x, y)
121
+
122
+ # def mouseDoubleClickEvent(self, ev):
123
+ # super().mouseDoubleClickEvent(ev)
@@ -0,0 +1,16 @@
1
+ from matplotlib.backends.qt_compat import QT_API, QtCore, QtWidgets
2
+
3
+ if QT_API in ['PySide6', 'PyQt6']:
4
+ AlignRight = QtCore.Qt.AlignmentFlag.AlignRight
5
+ BottomDockWidgetArea = QtCore.Qt.DockWidgetArea.BottomDockWidgetArea
6
+ ScrollBarAlwaysOn = QtCore.Qt.ScrollBarPolicy.ScrollBarAlwaysOn
7
+ ScrollBarAlwaysOff = QtCore.Qt.ScrollBarPolicy.ScrollBarAlwaysOff
8
+ TopDockWidgetArea = QtCore.Qt.DockWidgetArea.TopDockWidgetArea
9
+ elif QT_API in ['PyQt5', 'PySide2']:
10
+ AlignRight = QtCore.Qt.AlignRight
11
+ BottomDockWidgetArea = QtCore.Qt.BottomDockWidgetArea
12
+ ScrollBarAlwaysOn = QtCore.Qt.ScrollBarAlwaysOn
13
+ ScrollBarAlwaysOff = QtCore.Qt.ScrollBarAlwaysOff
14
+ TopDockWidgetArea = QtCore.Qt.TopDockWidgetArea
15
+ else:
16
+ raise AssertionError(f"Unexpected QT_API: {QT_API}")
@@ -0,0 +1,265 @@
1
+ import itertools
2
+ import re
3
+ from typing import Callable
4
+
5
+ from .config import style
6
+ from .qt_compat import AlignRight, QtWidgets
7
+
8
+
9
+ def matched_xy_pairs(patterns: str, lst: list[str]) -> list[tuple[str, str]]:
10
+ patterns = patterns.replace(" ", "").split(";")
11
+ pairs = []
12
+ for x, y in itertools.product(lst, repeat=2):
13
+ test = f"{x},{y}"
14
+ for pattern in patterns:
15
+ r = re.match(pattern, test)
16
+ if r and r.group(0) == test:
17
+ pairs.append((x, y))
18
+ break
19
+ return pairs
20
+
21
+
22
+ class FormatCombo(QtWidgets.QComboBox):
23
+
24
+ def __init__(self):
25
+ super().__init__()
26
+ self.on_change_callable = None
27
+
28
+ def set_on_change_event_action(self, callback: Callable[[], None]):
29
+ self.on_change_callable = callback
30
+ self.activated.connect(callback)
31
+
32
+ def set_idx(self, idx: int):
33
+ self.setCurrentIndex(idx)
34
+ if (callable(self.on_change_callable)):
35
+ self.on_change_callable()
36
+
37
+
38
+ class XFormatCombo(FormatCombo):
39
+
40
+ def __init__(self):
41
+ super().__init__()
42
+ self.addItem("real")
43
+ self.addItem("imag")
44
+ self.addItem("mag")
45
+ self.addItem("phase")
46
+
47
+
48
+ class YFormatCombo(FormatCombo):
49
+
50
+ def __init__(self):
51
+ super().__init__()
52
+ self.addItem("mag")
53
+ self.addItem("phase")
54
+ self.addItem("real")
55
+ self.addItem("imag")
56
+
57
+
58
+ class LineEdit(QtWidgets.QLineEdit):
59
+
60
+ def set_on_change_event_action(self, callback: Callable[[], None]):
61
+ self.on_change_callable = callback
62
+ self.editingFinished.connect(callback)
63
+
64
+ def set_text(self, w):
65
+ self.setText(w)
66
+ if (callable(self.on_change_callable)):
67
+ self.on_change_callable()
68
+
69
+
70
+ class SelectionBundle():
71
+
72
+ def __init__(self):
73
+ self.stx = LineEdit()
74
+ # select text
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
+ self.sels = []
82
+ # tuple enumeration
83
+
84
+ def set_on_change_event_actions(self, on_text_edited, on_format_changed,
85
+ on_log_scale_marker_changed):
86
+ self.stx.set_on_change_event_action(on_text_edited)
87
+ self.fx.set_on_change_event_action(on_format_changed)
88
+ self.fy.set_on_change_event_action(on_format_changed)
89
+ self.lx.toggled.connect(on_format_changed)
90
+ self.ly.toggled.connect(on_format_changed)
91
+ self.linkx.toggled.connect(on_log_scale_marker_changed)
92
+ self.linky.toggled.connect(on_log_scale_marker_changed)
93
+
94
+ def rm4l(self): # remove from layout
95
+ self.stx.setParent(None)
96
+ self.fx.setParent(None)
97
+ self.fy.setParent(None)
98
+ self.lx.setParent(None)
99
+ self.ly.setParent(None)
100
+ self.linkx.setParent(None)
101
+ self.linky.setParent(None)
102
+
103
+ def a2l(self, layout): # add to layout
104
+ i = 3
105
+ layout.addWidget(self.stx, 0, i)
106
+ i += 2
107
+ layout.addWidget(self.fx, 0, i)
108
+ i += 2
109
+ layout.addWidget(self.fy, 0, i)
110
+ i += 2
111
+ layout.addWidget(self.lx, 0, 10)
112
+ layout.addWidget(self.ly, 0, 11)
113
+ layout.addWidget(self.linkx, 0, 12)
114
+ layout.addWidget(self.linky, 0, 13)
115
+
116
+
117
+ class ToolBar(QtWidgets.QWidget):
118
+
119
+ def __init__(self):
120
+ super().__init__()
121
+
122
+ # the buttons
123
+ # button for points
124
+ self.mode = 'P'
125
+ self.setStyleSheet(style)
126
+
127
+ self.pb = QtWidgets.QRadioButton('Points')
128
+ self.pb.setChecked(True)
129
+ self.pb.toggled.connect(self.toggle_mode)
130
+
131
+ # button for Traces
132
+ self.tb = QtWidgets.QRadioButton('Traces')
133
+ self.tb.toggled.connect(self.toggle_mode)
134
+
135
+ # text labels
136
+ self.ytxt_lb = QtWidgets.QLabel("(X,Y)")
137
+ self.ytxt_lb.setAlignment(AlignRight)
138
+ self.fx_lb = QtWidgets.QLabel("fx")
139
+ self.fx_lb.setAlignment(AlignRight)
140
+ self.fy_lb = QtWidgets.QLabel("fy")
141
+ self.fy_lb.setAlignment(AlignRight)
142
+
143
+ # enumeration
144
+ ps = SelectionBundle()
145
+ ps.set_on_change_event_actions(self.textEdited, self.generateXYFM,
146
+ self.link_edited)
147
+
148
+ ts = SelectionBundle()
149
+ ts.set_on_change_event_actions(self.textEdited, self.generateXYFM,
150
+ self.link_edited)
151
+
152
+ # connections :
153
+ self.ps = ps
154
+ self.ts = ts
155
+
156
+ # plot format configures
157
+ self.xypairs = []
158
+ self.xypairs_dirty = True
159
+ self.fx = None
160
+ self.fy = None
161
+ self.xyfm_dirty = True
162
+ self.link_dirty = True
163
+ self.lx = False
164
+ self.ly = False
165
+ # setting layout
166
+ self.layout = QtWidgets.QGridLayout()
167
+ self.layout.setContentsMargins(0, 0, 0, 0)
168
+ self.setLayout(self.layout)
169
+
170
+ self.layout.addWidget(self.pb, 0, 0)
171
+ self.layout.addWidget(self.tb, 0, 1)
172
+
173
+ self.layout.addWidget(self.ytxt_lb, 0, 2)
174
+ self.layout.addWidget(self.fx_lb, 0, 4)
175
+ self.layout.addWidget(self.fy_lb, 0, 6)
176
+
177
+ self.AR = QtWidgets.QPushButton("AR")
178
+ self.AR.setMaximumWidth(30)
179
+ self.AR.setToolTip("Auto Range")
180
+ self.CR = QtWidgets.QPushButton("CLR")
181
+ self.CR.setMaximumWidth(30)
182
+ self.CR.setToolTip("Clearing History Plots")
183
+ self.CR_flag = False
184
+ self.layout.addWidget(self.AR, 0, 8)
185
+ self.layout.addWidget(self.CR, 0, 9)
186
+
187
+ self.refresh_layout()
188
+
189
+ @property
190
+ def column_names(self) -> list[str]:
191
+ return {
192
+ "P": self.mainwindow.point_data_box.column_names,
193
+ "T": self.mainwindow.trace_data_box.column_names
194
+ }[self.mode]
195
+
196
+ @property
197
+ def selections(self) -> SelectionBundle:
198
+ return {"P": self.ps, "T": self.ts}[self.mode]
199
+
200
+ def set_trace_text(self, text: str):
201
+ self.ts.stx.set_text(text)
202
+
203
+ def set_point_text(self, text: str):
204
+ self.ps.stx.set_text(text)
205
+
206
+ def sharexy(self):
207
+ return self.selections.linkx.isChecked(
208
+ ), self.selections.linky.isChecked()
209
+
210
+ def set_mainwindow(self, mainwindow):
211
+ self.mainwindow = mainwindow
212
+ self.AR.clicked.connect(self.mainwindow.all_enable_auto_range)
213
+ self.CR.clicked.connect(self.CR_action)
214
+
215
+ def AR_action(self):
216
+ self.mainwindow.enable_all_auto_range()
217
+
218
+ def CR_action(self):
219
+ self.CR_flag = True
220
+
221
+ def refresh_layout(self):
222
+ if self.mode == 'P':
223
+ self.ts.rm4l()
224
+ self.ps.a2l(self.layout)
225
+ elif self.mode == 'T':
226
+ self.ps.rm4l()
227
+ self.ts.a2l(self.layout)
228
+
229
+ def toggle_mode(self):
230
+ if (self.pb.isChecked()):
231
+ self.mode = 'P'
232
+ elif (self.tb.isChecked()):
233
+ self.mode = 'T'
234
+ self.refresh_layout()
235
+ self.refresh_comb()
236
+
237
+ def refresh_comb(self, ):
238
+ self.generateXYFM()
239
+ self.textEdited()
240
+ #set tooltips
241
+ self.ytxt_lb.setToolTip(str(self.column_names))
242
+
243
+ def link_edited(self):
244
+ # print("LinkEdited")
245
+ self.link_dirty = True
246
+
247
+ def generateXYFM(self):
248
+ self.fx = self.selections.fx.currentText()
249
+ self.fy = self.selections.fy.currentText()
250
+ self.lx = self.selections.lx.isChecked()
251
+ self.ly = self.selections.ly.isChecked()
252
+ self.xyfm_dirty = True
253
+ #self.show_info() ;
254
+
255
+ def textEdited(self):
256
+ new_pairs = matched_xy_pairs(self.selections.stx.text(),
257
+ self.column_names)
258
+ if (len(self.xypairs) != len(new_pairs)):
259
+ self.xypairs_dirty = True
260
+ else:
261
+ for i, xy in enumerate(new_pairs):
262
+ if (xy != self.xypairs[i]):
263
+ self.xypairs_dirty = True
264
+ break
265
+ self.xypairs = new_pairs
qulab/scan/__init__.py ADDED
@@ -0,0 +1,4 @@
1
+ from .base import (BaseOptimizer, Begin, End, OptimizerConfig, StepStatus,
2
+ Tracker, scan_iters)
3
+ from .expression import Expression, Symbol
4
+ from .scanner import Atom, Optimizer, OptimizeSpace, Scan