PySide6Plot 0.0.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- PySide6Plot/__init__.py +187 -0
- PySide6Plot/compoents/__init__.py +3 -0
- PySide6Plot/compoents/average_line.py +232 -0
- PySide6Plot/compoents/draw_line.py +624 -0
- PySide6Plot/compoents/frame_recorder.py +213 -0
- PySide6Plot/compoents/zoom_move.py +160 -0
- PySide6Plot/libs/__init__.py +3 -0
- PySide6Plot/libs/constant.py +19 -0
- PySide6Plot/libs/data_handler.py +230 -0
- PySide6Plot/libs/helpers.py +401 -0
- PySide6Plot/libs/plot_item.py +233 -0
- PySide6Plot/libs/style.py +119 -0
- PySide6Plot/resources/icons/ChevronLeft_black.svg +44 -0
- PySide6Plot/resources/icons/ChevronLeft_white.svg +44 -0
- PySide6Plot/resources/qss/dark/navigation_view_interface.qss +16 -0
- PySide6Plot/resources/qss/light/navigation_view_interface.qss +16 -0
- PySide6Plot/widgets/__init__.py +3 -0
- PySide6Plot/widgets/colorful_toggle_button.py +131 -0
- PySide6Plot/widgets/fluent_scroller.py +450 -0
- PySide6Plot/widgets/line_card.py +138 -0
- PySide6Plot/widgets/navigation_widget.py +49 -0
- PySide6Plot/widgets/q_plot_widget.py +750 -0
- PySide6Plot/widgets/removable_table.py +259 -0
- PySide6Plot/widgets/transparent_Line_edit.py +69 -0
- PySide6Plot/widgets/transparent_selector.py +208 -0
- PySide6Plot/widgets/value_select_box.py +369 -0
- PySide6Plot/widgets/zoom_bar.py +136 -0
- pyside6plot-0.0.1.dist-info/METADATA +37 -0
- pyside6plot-0.0.1.dist-info/RECORD +31 -0
- pyside6plot-0.0.1.dist-info/WHEEL +5 -0
- pyside6plot-0.0.1.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,369 @@
|
|
|
1
|
+
from PySide6.QtGui import QDoubleValidator, QIntValidator
|
|
2
|
+
from qfluentwidgets import (
|
|
3
|
+
MessageBox,
|
|
4
|
+
EditableComboBox,
|
|
5
|
+
MessageBoxBase,
|
|
6
|
+
SubtitleLabel,
|
|
7
|
+
LineEdit,
|
|
8
|
+
FluentIcon,
|
|
9
|
+
RoundMenu,
|
|
10
|
+
Action,
|
|
11
|
+
DropDownToolButton,
|
|
12
|
+
TableWidget,
|
|
13
|
+
CommandBarView,
|
|
14
|
+
Flyout,
|
|
15
|
+
FlyoutAnimationType,
|
|
16
|
+
)
|
|
17
|
+
from PySide6.QtWidgets import QHBoxLayout, QTableWidgetItem, QCompleter
|
|
18
|
+
from PySide6.QtGui import QCursor
|
|
19
|
+
from PySide6.QtCore import Signal
|
|
20
|
+
from .transparent_selector import TransparentColorSelector
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
class ValueSelectBox(MessageBoxBase):
|
|
24
|
+
"""Custom message box"""
|
|
25
|
+
|
|
26
|
+
def __init__(self, parent=None, title="Set aspect ratio", allowed_min=None, allowed_max=None, current=None):
|
|
27
|
+
super().__init__(parent)
|
|
28
|
+
if allowed_min is not None and allowed_max is not None:
|
|
29
|
+
if allowed_min > allowed_max:
|
|
30
|
+
raise ValueError("allowed_min must be less than allowed_max")
|
|
31
|
+
# print(allowed_min,allowed_max,current)
|
|
32
|
+
|
|
33
|
+
self.title_label = SubtitleLabel(title, self)
|
|
34
|
+
self.viewLayout.addWidget(self.title_label)
|
|
35
|
+
|
|
36
|
+
validator = QDoubleValidator(allowed_min, allowed_max, 2)
|
|
37
|
+
self.value_edit = LineEdit(self)
|
|
38
|
+
self.value_edit.setValidator(validator)
|
|
39
|
+
self.value_edit.setClearButtonEnabled(True)
|
|
40
|
+
self.value_edit.textChanged.connect(self.__validate_value)
|
|
41
|
+
if current is not None:
|
|
42
|
+
self.__set_value(current)
|
|
43
|
+
else:
|
|
44
|
+
self.yesButton.setDisabled(True)
|
|
45
|
+
if allowed_min is not None and allowed_max is not None:
|
|
46
|
+
self.value_edit.setPlaceholderText(f"{allowed_min:.2f}~{allowed_max:.2f}")
|
|
47
|
+
elif allowed_min is not None and allowed_max is None:
|
|
48
|
+
self.value_edit.setPlaceholderText(f">{allowed_min:.2f}")
|
|
49
|
+
elif allowed_min is None and allowed_max is not None:
|
|
50
|
+
self.value_edit.setPlaceholderText(f"<{allowed_max:.2f}")
|
|
51
|
+
else:
|
|
52
|
+
self.value_edit.setPlaceholderText("input value here")
|
|
53
|
+
|
|
54
|
+
if allowed_min is not None or allowed_max is not None or current is not None:
|
|
55
|
+
self.tag_button = DropDownToolButton(self)
|
|
56
|
+
self.tag_button.setIcon(FluentIcon.TAG)
|
|
57
|
+
self.tag_menu = RoundMenu(self)
|
|
58
|
+
if current is not None:
|
|
59
|
+
current_action = Action(f"current value ({current:.2f})")
|
|
60
|
+
current_action.triggered.connect(lambda: self.__set_value(current))
|
|
61
|
+
self.tag_menu.addAction(current_action)
|
|
62
|
+
if allowed_min is not None:
|
|
63
|
+
min_action = Action(f"min value ({allowed_min:.2f})")
|
|
64
|
+
min_action.triggered.connect(lambda: self.__set_value(allowed_min))
|
|
65
|
+
self.tag_menu.addAction(min_action)
|
|
66
|
+
if allowed_max is not None:
|
|
67
|
+
max_action = Action(f"max value ({allowed_max:.2f})")
|
|
68
|
+
max_action.triggered.connect(lambda: self.__set_value(allowed_max))
|
|
69
|
+
self.tag_menu.addAction(max_action)
|
|
70
|
+
self.tag_button.setMenu(self.tag_menu)
|
|
71
|
+
Hlayout = QHBoxLayout()
|
|
72
|
+
Hlayout.addWidget(self.value_edit)
|
|
73
|
+
Hlayout.addWidget(self.tag_button)
|
|
74
|
+
self.viewLayout.addLayout(Hlayout)
|
|
75
|
+
else:
|
|
76
|
+
self.viewLayout.addLayout(self.value_edit)
|
|
77
|
+
|
|
78
|
+
# change the text of button
|
|
79
|
+
self.yesButton.setText("Confirm")
|
|
80
|
+
self.cancelButton.setText("Cancel")
|
|
81
|
+
|
|
82
|
+
self.widget.setMinimumWidth(400)
|
|
83
|
+
|
|
84
|
+
# self.hideYesButton()
|
|
85
|
+
|
|
86
|
+
def __set_value(self, value):
|
|
87
|
+
self.value_edit.setText(f"{value:.2f}")
|
|
88
|
+
self.__validate_value("")
|
|
89
|
+
|
|
90
|
+
def __validate_value(self, text):
|
|
91
|
+
self.yesButton.setEnabled(self.value_edit.hasAcceptableInput())
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
class StrSelectBox(MessageBoxBase):
|
|
95
|
+
"""Custom message box"""
|
|
96
|
+
|
|
97
|
+
def __init__(self, parent=None, title="Set text", default_str=None):
|
|
98
|
+
super().__init__(parent)
|
|
99
|
+
self.title_label = SubtitleLabel(title, self)
|
|
100
|
+
self.viewLayout.addWidget(self.title_label)
|
|
101
|
+
self.value_edit = LineEdit(self)
|
|
102
|
+
self.value_edit.setClearButtonEnabled(True)
|
|
103
|
+
if default_str is not None:
|
|
104
|
+
self.value_edit.setText(default_str)
|
|
105
|
+
else:
|
|
106
|
+
self.yesButton.setDisabled(True)
|
|
107
|
+
self.viewLayout.addWidget(self.value_edit)
|
|
108
|
+
# change the text of button
|
|
109
|
+
self.yesButton.setText("Confirm")
|
|
110
|
+
self.cancelButton.setText("Cancel")
|
|
111
|
+
self.widget.setMinimumWidth(400)
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
class LimitedStrSelectBox(StrSelectBox):
|
|
115
|
+
def __init__(self, parent=None, title="Set text", allowed_strs=None):
|
|
116
|
+
super().__init__(parent, title, allowed_strs[-1])
|
|
117
|
+
self.allowed_strs = allowed_strs
|
|
118
|
+
self.value_edit.textChanged.connect(self.__validate_value)
|
|
119
|
+
|
|
120
|
+
def __validate_value(self, text):
|
|
121
|
+
self.yesButton.setEnabled(self.value_edit.text() in self.allowed_strs)
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
class ItemSelectBox(MessageBoxBase):
|
|
125
|
+
"""Custom message box"""
|
|
126
|
+
|
|
127
|
+
def __init__(self, parent=None, title="Set text", items=None, current_index=-1):
|
|
128
|
+
super().__init__(parent)
|
|
129
|
+
self.items = items
|
|
130
|
+
self.title_label = SubtitleLabel(title, self)
|
|
131
|
+
self.viewLayout.addWidget(self.title_label)
|
|
132
|
+
self.com_box = EditableComboBox(self)
|
|
133
|
+
self.com_box.addItems(items)
|
|
134
|
+
self.com_box.setCurrentIndex(current_index)
|
|
135
|
+
self.completer = QCompleter(items, self)
|
|
136
|
+
self.com_box.setCompleter(self.completer)
|
|
137
|
+
self.com_box.currentTextChanged.connect(self.__validate_value)
|
|
138
|
+
self.viewLayout.addWidget(self.com_box)
|
|
139
|
+
# change the text of button
|
|
140
|
+
self.yesButton.setText("Confirm")
|
|
141
|
+
self.cancelButton.setText("Cancel")
|
|
142
|
+
self.widget.setMinimumWidth(400)
|
|
143
|
+
|
|
144
|
+
def __validate_value(self):
|
|
145
|
+
self.yesButton.setEnabled(self.com_box.currentText() in self.items)
|
|
146
|
+
|
|
147
|
+
def current_text(self):
|
|
148
|
+
return self.com_box.currentText()
|
|
149
|
+
|
|
150
|
+
|
|
151
|
+
class RemovableTableWidget(TableWidget):
|
|
152
|
+
selected = Signal(QTableWidgetItem)
|
|
153
|
+
|
|
154
|
+
def __init__(self, parent=None, show_row_index=False, resize_columns_to_contents=True):
|
|
155
|
+
super().__init__(parent)
|
|
156
|
+
self.setBorderVisible(True)
|
|
157
|
+
self.setBorderRadius(8)
|
|
158
|
+
self.setWordWrap(False)
|
|
159
|
+
self.itemClicked.connect(self.__on_item_clicked)
|
|
160
|
+
self.current_item = None
|
|
161
|
+
self.show_row_index = show_row_index
|
|
162
|
+
self.resize_columns_to_contents = resize_columns_to_contents
|
|
163
|
+
|
|
164
|
+
def __on_item_clicked(self, item):
|
|
165
|
+
self.current_item = item
|
|
166
|
+
self.command_bar = CommandBarView(self)
|
|
167
|
+
select_action = Action(FluentIcon.ACCEPT, "Select")
|
|
168
|
+
select_action.triggered.connect(self.on_item_selected_clicked)
|
|
169
|
+
self.command_bar.addAction(select_action)
|
|
170
|
+
edit_action = Action(FluentIcon.EDIT, "Rename")
|
|
171
|
+
edit_action.triggered.connect(self.on_edit_row_clicked)
|
|
172
|
+
self.command_bar.addAction(edit_action)
|
|
173
|
+
if item.row() > 0:
|
|
174
|
+
move_up_action = Action(FluentIcon.UP, "Move up")
|
|
175
|
+
move_up_action.triggered.connect(self.on_move_up_row_clicked)
|
|
176
|
+
self.command_bar.addAction(move_up_action)
|
|
177
|
+
if item.row() < self.rowCount() - 1:
|
|
178
|
+
move_down_action = Action(FluentIcon.DOWN, "Move down")
|
|
179
|
+
move_down_action.triggered.connect(self.on_move_down_row_clicked)
|
|
180
|
+
self.command_bar.addAction(move_down_action)
|
|
181
|
+
delete_action = Action(FluentIcon.DELETE, "Delete")
|
|
182
|
+
delete_action.triggered.connect(self.on_delete_row_clicked)
|
|
183
|
+
self.command_bar.addAction(delete_action)
|
|
184
|
+
self.command_bar.resizeToSuitableWidth()
|
|
185
|
+
Flyout.make(self.command_bar, QCursor.pos(), self, FlyoutAnimationType.FADE_IN)
|
|
186
|
+
|
|
187
|
+
def on_delete_row_clicked(self):
|
|
188
|
+
if self.current_item is not None:
|
|
189
|
+
self.removeRow(self.current_item.row())
|
|
190
|
+
self.current_item = None
|
|
191
|
+
self.clearSelection()
|
|
192
|
+
self.command_bar.close()
|
|
193
|
+
|
|
194
|
+
def on_move_up_row_clicked(self):
|
|
195
|
+
if self.current_item is not None:
|
|
196
|
+
for j in range(self.columnCount()):
|
|
197
|
+
temple = self.item(self.current_item.row(), j).text()
|
|
198
|
+
self.item(self.current_item.row(), j).setText(self.item(self.current_item.row() - 1, j).text())
|
|
199
|
+
self.item(self.current_item.row() - 1, j).setText(temple)
|
|
200
|
+
self.current_item = None
|
|
201
|
+
self.clearSelection()
|
|
202
|
+
self.command_bar.close()
|
|
203
|
+
|
|
204
|
+
def on_move_down_row_clicked(self):
|
|
205
|
+
if self.current_item is not None:
|
|
206
|
+
for j in range(self.columnCount()):
|
|
207
|
+
temple = self.item(self.current_item.row(), j).text()
|
|
208
|
+
self.item(self.current_item.row(), j).setText(self.item(self.current_item.row() + 1, j).text())
|
|
209
|
+
self.item(self.current_item.row() + 1, j).setText(temple)
|
|
210
|
+
self.current_item = None
|
|
211
|
+
self.clearSelection()
|
|
212
|
+
self.command_bar.close()
|
|
213
|
+
|
|
214
|
+
def on_item_selected_clicked(self):
|
|
215
|
+
if self.current_item is not None:
|
|
216
|
+
self.selected.emit(self.item(self.current_item.row(), 0))
|
|
217
|
+
self.command_bar.close()
|
|
218
|
+
|
|
219
|
+
def on_edit_row_clicked(self):
|
|
220
|
+
if self.current_item is not None:
|
|
221
|
+
self.editItem(self.item(self.current_item.row(), 0))
|
|
222
|
+
self.command_bar.close()
|
|
223
|
+
|
|
224
|
+
def collect_items(self):
|
|
225
|
+
items = []
|
|
226
|
+
for i in range(self.rowCount()):
|
|
227
|
+
item = []
|
|
228
|
+
for j in range(self.columnCount()):
|
|
229
|
+
item.append(self.item(i, j).text())
|
|
230
|
+
items.append(item)
|
|
231
|
+
return items
|
|
232
|
+
|
|
233
|
+
def set_data(self, data, labels=None):
|
|
234
|
+
num_rows = len(data)
|
|
235
|
+
num_cols = len(data[0])
|
|
236
|
+
self.setRowCount(num_rows)
|
|
237
|
+
self.setColumnCount(num_cols)
|
|
238
|
+
for i, songInfo in enumerate(data):
|
|
239
|
+
for j in range(num_cols):
|
|
240
|
+
self.setItem(i, j, QTableWidgetItem(songInfo[j]))
|
|
241
|
+
if not self.show_row_index:
|
|
242
|
+
self.verticalHeader().hide()
|
|
243
|
+
if self.resize_columns_to_contents:
|
|
244
|
+
self.resizeColumnsToContents()
|
|
245
|
+
if labels is not None:
|
|
246
|
+
self.setHorizontalHeaderLabels(labels)
|
|
247
|
+
|
|
248
|
+
|
|
249
|
+
class ItemEditBox(MessageBoxBase):
|
|
250
|
+
"""Custom message box"""
|
|
251
|
+
|
|
252
|
+
def __init__(
|
|
253
|
+
self, parent=None, title="Set text", min_width=400, show_row_index=False, resize_columns_to_contents=True
|
|
254
|
+
):
|
|
255
|
+
super().__init__(parent)
|
|
256
|
+
self.title_label = SubtitleLabel(title, self)
|
|
257
|
+
self.viewLayout.addWidget(self.title_label)
|
|
258
|
+
self.yesButton.setText("Confirm")
|
|
259
|
+
self.cancelButton.setText("Cancel")
|
|
260
|
+
self.tableView = RemovableTableWidget(self, show_row_index, resize_columns_to_contents)
|
|
261
|
+
self.viewLayout.addWidget(self.tableView)
|
|
262
|
+
self.widget.setMinimumWidth(min_width)
|
|
263
|
+
self.selected_row = None
|
|
264
|
+
self.tableView.selected.connect(self.on_item_selected)
|
|
265
|
+
|
|
266
|
+
def set_data(self, data, labels):
|
|
267
|
+
self.data = data
|
|
268
|
+
self.tableView.set_data(data, labels)
|
|
269
|
+
|
|
270
|
+
def on_item_selected(self, item):
|
|
271
|
+
self.accept()
|
|
272
|
+
self.selected_row = item.row()
|
|
273
|
+
self.accepted.emit()
|
|
274
|
+
|
|
275
|
+
def accept(self) -> None:
|
|
276
|
+
self.data = self.tableView.collect_items()
|
|
277
|
+
return super().accept()
|
|
278
|
+
|
|
279
|
+
|
|
280
|
+
class NewAverageLineBox(MessageBoxBase):
|
|
281
|
+
"""Custom message box"""
|
|
282
|
+
|
|
283
|
+
def __init__(self, num_data, existed_values, parent=None):
|
|
284
|
+
super().__init__(parent)
|
|
285
|
+
self.num_data = num_data
|
|
286
|
+
self.existed_values = existed_values
|
|
287
|
+
self.title_label = SubtitleLabel("Creative new average line", self)
|
|
288
|
+
self.viewLayout.addWidget(self.title_label)
|
|
289
|
+
self.yesButton.setText("Confirm")
|
|
290
|
+
self.cancelButton.setText("Cancel")
|
|
291
|
+
self.yesButton.setDisabled(True)
|
|
292
|
+
self.value_edit = LineEdit(self)
|
|
293
|
+
self.value_edit.setClearButtonEnabled(True)
|
|
294
|
+
self.value_edit.textChanged.connect(self.__validate_value)
|
|
295
|
+
self.value_edit.setPlaceholderText("average days")
|
|
296
|
+
self.value_edit.setValidator(QIntValidator(1, num_data, self.value_edit))
|
|
297
|
+
self.text_color_layout = QHBoxLayout()
|
|
298
|
+
self.text_color_layout.addWidget(self.value_edit)
|
|
299
|
+
self.color_selector = TransparentColorSelector(parent=self)
|
|
300
|
+
self.color_selector.setFixedHeight(35)
|
|
301
|
+
self.text_color_layout.addWidget(self.color_selector)
|
|
302
|
+
self.viewLayout.addLayout(self.text_color_layout)
|
|
303
|
+
|
|
304
|
+
def __validate_value(self, text):
|
|
305
|
+
if self.value_edit.hasAcceptableInput():
|
|
306
|
+
if int(text) in self.existed_values:
|
|
307
|
+
self.yesButton.setDisabled(True)
|
|
308
|
+
self.title_label.setText("Average Line existed")
|
|
309
|
+
else:
|
|
310
|
+
self.yesButton.setDisabled(False)
|
|
311
|
+
self.title_label.setText("Creative new average line")
|
|
312
|
+
|
|
313
|
+
|
|
314
|
+
def select_value(parent=None, title="Set aspect ratio", allowed_min=0.1, allowed_max=100, current=50):
|
|
315
|
+
"""Custom message box"""
|
|
316
|
+
msg = ValueSelectBox(parent, title, allowed_min, allowed_max, current)
|
|
317
|
+
if msg.exec():
|
|
318
|
+
return float(msg.value_edit.text())
|
|
319
|
+
else:
|
|
320
|
+
return None
|
|
321
|
+
|
|
322
|
+
|
|
323
|
+
def select_str(parent=None, title="Select str", default_str=None):
|
|
324
|
+
"""Custom message box"""
|
|
325
|
+
msg = StrSelectBox(parent, title, default_str)
|
|
326
|
+
if msg.exec():
|
|
327
|
+
return msg.value_edit.text()
|
|
328
|
+
else:
|
|
329
|
+
return None
|
|
330
|
+
|
|
331
|
+
|
|
332
|
+
def select_limited_str(parent=None, title="Select str", allowed_strs=None):
|
|
333
|
+
"""Custom message box"""
|
|
334
|
+
msg = LimitedStrSelectBox(parent, title, allowed_strs)
|
|
335
|
+
if msg.exec():
|
|
336
|
+
return msg.value_edit.text()
|
|
337
|
+
else:
|
|
338
|
+
return None
|
|
339
|
+
|
|
340
|
+
|
|
341
|
+
def edit_items(
|
|
342
|
+
parent, items, label, title="Edit item", min_width=400, show_row_index=False, resize_columns_to_contents=True
|
|
343
|
+
):
|
|
344
|
+
"""Custom message box"""
|
|
345
|
+
msg = ItemEditBox(parent, title, min_width, show_row_index, resize_columns_to_contents)
|
|
346
|
+
if len(label) != len(items[0]):
|
|
347
|
+
raise ValueError("label must have the same length as items")
|
|
348
|
+
msg.set_data(items, label)
|
|
349
|
+
if msg.exec():
|
|
350
|
+
return msg.data, msg.selected_row
|
|
351
|
+
else:
|
|
352
|
+
return None
|
|
353
|
+
|
|
354
|
+
|
|
355
|
+
def select_item(parent, items, title="Select item", current_index=-1):
|
|
356
|
+
"""Custom message box"""
|
|
357
|
+
msg = ItemSelectBox(parent, title, items, current_index)
|
|
358
|
+
if msg.exec():
|
|
359
|
+
return msg.current_text()
|
|
360
|
+
else:
|
|
361
|
+
return None
|
|
362
|
+
|
|
363
|
+
|
|
364
|
+
def confirmation_dialog(parent, title, content):
|
|
365
|
+
w = MessageBox(title, content, parent)
|
|
366
|
+
if w.exec():
|
|
367
|
+
return True
|
|
368
|
+
else:
|
|
369
|
+
return False
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
from PySide6.QtCore import Qt
|
|
2
|
+
from PySide6.QtWidgets import QGraphicsOpacityEffect, QHBoxLayout
|
|
3
|
+
from PySide6.QtCore import QPropertyAnimation
|
|
4
|
+
from qfluentwidgets import SimpleCardWidget, ToolButton, Slider, FluentIcon
|
|
5
|
+
import math
|
|
6
|
+
from ..libs.helpers import limit_in_range
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class ZoomBar(SimpleCardWidget):
|
|
10
|
+
def __init__(self, parent=None, use_opacity_effect=True) -> None:
|
|
11
|
+
super().__init__(parent=parent)
|
|
12
|
+
self.zoom_slider = Slider(orientation=Qt.Orientation.Horizontal, parent=self)
|
|
13
|
+
self.zoom_slider.setMinimum(0)
|
|
14
|
+
self.zoom_slider.setMaximum(100)
|
|
15
|
+
self.zoom_slider.setValue(100)
|
|
16
|
+
self.zoom_slider.valueChanged.connect(self.__on_zoom_slider_moved)
|
|
17
|
+
self.zoom_in_button = ToolButton(FluentIcon.ZOOM_IN, parent=self)
|
|
18
|
+
self.zoom_in_button.clicked.connect(self.__on_zoom_in_button_clicked)
|
|
19
|
+
self.zoom_out_button = ToolButton(FluentIcon.ZOOM_OUT, parent=self)
|
|
20
|
+
self.zoom_out_button.clicked.connect(self.__on_zoom_out_button_clicked)
|
|
21
|
+
self.zoom_tool_layout = QHBoxLayout(self)
|
|
22
|
+
self.zoom_tool_layout.setContentsMargins(5, 5, 5, 5)
|
|
23
|
+
self.zoom_tool_layout.setSpacing(5)
|
|
24
|
+
self.zoom_tool_layout.addWidget(self.zoom_out_button)
|
|
25
|
+
self.zoom_tool_layout.addWidget(self.zoom_slider)
|
|
26
|
+
self.zoom_tool_layout.addWidget(self.zoom_in_button)
|
|
27
|
+
if use_opacity_effect:
|
|
28
|
+
self.opacity_effect = QGraphicsOpacityEffect(self)
|
|
29
|
+
self.opacity_ani = QPropertyAnimation(self.opacity_effect, b"opacity", self)
|
|
30
|
+
self.opacity_effect.setOpacity(0.1)
|
|
31
|
+
self.setGraphicsEffect(self.opacity_effect)
|
|
32
|
+
self.value_min = 0
|
|
33
|
+
self.value_max = 100
|
|
34
|
+
self.current_value = 100
|
|
35
|
+
self.move_from_update = False
|
|
36
|
+
self.zoom_coef = 10 # a constant to control the zoom speed. Do NOT change it.
|
|
37
|
+
self.e_constant = math.exp(self.zoom_coef) # a constant to control the zoom speed
|
|
38
|
+
self.__init_config_variable()
|
|
39
|
+
self.update_widget()
|
|
40
|
+
|
|
41
|
+
def __init_config_variable(self):
|
|
42
|
+
self.zoom_out_factor = 1.1
|
|
43
|
+
self.zoom_in_factor = 0.9
|
|
44
|
+
|
|
45
|
+
def enterEvent(self, e):
|
|
46
|
+
if hasattr(self, "opacity_ani"):
|
|
47
|
+
self.opacity_ani.setEndValue(0.8)
|
|
48
|
+
self.opacity_ani.setDuration(150)
|
|
49
|
+
self.opacity_ani.start()
|
|
50
|
+
return super().enterEvent(e)
|
|
51
|
+
|
|
52
|
+
def leaveEvent(self, e):
|
|
53
|
+
if hasattr(self, "opacity_ani"):
|
|
54
|
+
self.opacity_ani.setEndValue(0.1)
|
|
55
|
+
self.opacity_ani.setDuration(150)
|
|
56
|
+
self.opacity_ani.start()
|
|
57
|
+
return super().leaveEvent(e)
|
|
58
|
+
|
|
59
|
+
def __on_zoom_slider_moved(self):
|
|
60
|
+
"""
|
|
61
|
+
Callback function for when the zoom slider is moved.
|
|
62
|
+
Adjusts the x range and updates the plot, zoom slider, and scrollbars accordingly.
|
|
63
|
+
"""
|
|
64
|
+
if not self.move_from_update:
|
|
65
|
+
z_value = (100 - self.zoom_slider.value()) / self.zoom_coef
|
|
66
|
+
value = (math.exp(z_value) - 1) * (self.value_max - self.value_min) / (self.e_constant - 1) + self.value_min
|
|
67
|
+
self.update_widget(value)
|
|
68
|
+
self.apply_value_func(value)
|
|
69
|
+
|
|
70
|
+
def __on_zoom_out_button_clicked(self):
|
|
71
|
+
"""
|
|
72
|
+
Handles the click event of the zoom out button.
|
|
73
|
+
Zooms out the x-axis range by a factor of zoom_out_factor, updates the plot,
|
|
74
|
+
and updates the zoom slider, x scroller, y scroller, and zoom buttons.
|
|
75
|
+
"""
|
|
76
|
+
value = self.current_value * self.zoom_out_factor
|
|
77
|
+
value = min(self.value_max, value)
|
|
78
|
+
self.update_widget(value)
|
|
79
|
+
self.apply_value_func(value)
|
|
80
|
+
|
|
81
|
+
def __on_zoom_in_button_clicked(self):
|
|
82
|
+
"""
|
|
83
|
+
Handles the click event of the zoom in button.
|
|
84
|
+
Zooms in the x-axis range by a factor of zoom_in_factor, updates the plot,
|
|
85
|
+
and updates the zoom slider, x scroller, y scroller, and zoom buttons.
|
|
86
|
+
"""
|
|
87
|
+
value = self.current_value * self.zoom_in_factor
|
|
88
|
+
value = max(self.value_min, value)
|
|
89
|
+
self.update_widget(value)
|
|
90
|
+
self.apply_value_func(value)
|
|
91
|
+
|
|
92
|
+
def update_min_max_value(self, min_v=None, max_v=None):
|
|
93
|
+
if min_v is not None:
|
|
94
|
+
self.value_min = min_v
|
|
95
|
+
if max_v is not None:
|
|
96
|
+
self.value_max = max_v
|
|
97
|
+
self.update_widget()
|
|
98
|
+
|
|
99
|
+
def update_widget(self, value=None):
|
|
100
|
+
run = False
|
|
101
|
+
if value is None:
|
|
102
|
+
run = True
|
|
103
|
+
else:
|
|
104
|
+
value = limit_in_range(value, self.value_min, self.value_max)
|
|
105
|
+
if value != self.current_value:
|
|
106
|
+
self.current_value = value
|
|
107
|
+
run = True
|
|
108
|
+
if run:
|
|
109
|
+
self.move_from_update = True
|
|
110
|
+
self.zoom_slider.setValue(
|
|
111
|
+
100
|
|
112
|
+
- int(
|
|
113
|
+
math.log(
|
|
114
|
+
(self.current_value - self.value_min)
|
|
115
|
+
* (self.e_constant - 1)
|
|
116
|
+
/ (self.value_max - self.value_min)
|
|
117
|
+
+ 1
|
|
118
|
+
)
|
|
119
|
+
* self.zoom_coef
|
|
120
|
+
)
|
|
121
|
+
)
|
|
122
|
+
self.move_from_update = False
|
|
123
|
+
if self.current_value == self.value_min:
|
|
124
|
+
self.zoom_in_button.setEnabled(False)
|
|
125
|
+
self.zoom_out_button.setEnabled(True)
|
|
126
|
+
elif self.current_value == self.value_max:
|
|
127
|
+
self.zoom_out_button.setEnabled(False)
|
|
128
|
+
self.zoom_in_button.setEnabled(True)
|
|
129
|
+
else:
|
|
130
|
+
if not self.zoom_out_button.isEnabled():
|
|
131
|
+
self.zoom_out_button.setEnabled(True)
|
|
132
|
+
if not self.zoom_in_button.isEnabled():
|
|
133
|
+
self.zoom_in_button.setEnabled(True)
|
|
134
|
+
|
|
135
|
+
def apply_value_func(self, value):
|
|
136
|
+
raise NotImplementedError()
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: PySide6Plot
|
|
3
|
+
Version: 0.0.1
|
|
4
|
+
Summary: Scientific/Financial plot utilities with PySide6+pyqtgraph.
|
|
5
|
+
Author-email: YQ Cui <qianyun210603@hotmail.com>
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Keywords: visualization,plotting,pyside6,pyqtgraph,financial,scientific
|
|
8
|
+
Classifier: Development Status :: 2 - Pre-Alpha
|
|
9
|
+
Classifier: Operating System :: POSIX :: Linux
|
|
10
|
+
Classifier: Operating System :: Microsoft :: Windows
|
|
11
|
+
Classifier: Programming Language :: Python :: 3
|
|
12
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
14
|
+
Classifier: Topic :: Scientific/Engineering :: Visualization
|
|
15
|
+
Classifier: Programming Language :: Python :: Implementation :: CPython
|
|
16
|
+
Requires-Python: >=3.11
|
|
17
|
+
Description-Content-Type: text/markdown
|
|
18
|
+
Requires-Dist: numpy
|
|
19
|
+
Requires-Dist: pandas
|
|
20
|
+
Requires-Dist: pyaml>=23.10.0
|
|
21
|
+
Requires-Dist: PySide6!=6.9.1,>=6.9.0; python_version >= "3.12"
|
|
22
|
+
Requires-Dist: PySide6>=6.9.0; python_version <= "3.12"
|
|
23
|
+
Requires-Dist: pyqtgraph>=0.13.3
|
|
24
|
+
Requires-Dist: PySide6-Fluent-Widgets[full]>=1.5.1
|
|
25
|
+
Provides-Extra: dev
|
|
26
|
+
Requires-Dist: ruff>=0.0.263; extra == "dev"
|
|
27
|
+
Requires-Dist: setuptools_scm>=8; extra == "dev"
|
|
28
|
+
Requires-Dist: setuptools>=64; extra == "dev"
|
|
29
|
+
Requires-Dist: wheel; extra == "dev"
|
|
30
|
+
|
|
31
|
+
<h1 align="center">
|
|
32
|
+
<br>PySide6Plot<br>
|
|
33
|
+
</h1>
|
|
34
|
+
|
|
35
|
+
-----
|
|
36
|
+
|
|
37
|
+
A PySide6-based data visualization tool.
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
PySide6Plot/__init__.py,sha256=oVFoILVz4pNi4rGXlwJwvX08dU5jRyOKA02EE8OKX40,8503
|
|
2
|
+
PySide6Plot/compoents/__init__.py,sha256=_zQCVB18dbAZE8nnaXL6BB6gaP5yS_nkvfuF89WzbZg,47
|
|
3
|
+
PySide6Plot/compoents/average_line.py,sha256=nUBfCkjcoj7IH3FjHb5iXZC7B_2d_WpNnf319YZzl98,9630
|
|
4
|
+
PySide6Plot/compoents/draw_line.py,sha256=qauE_lwn1F0OKZtMifM_vcuBmQVFOiRbBPLFSKQ1MfQ,27674
|
|
5
|
+
PySide6Plot/compoents/frame_recorder.py,sha256=Lf-8TTRmlG8j36I21W8GqyYj9hTyBHaeFi4_GWdaTTg,8935
|
|
6
|
+
PySide6Plot/compoents/zoom_move.py,sha256=Bj2aSwGW441E_PAJQlr92V4oZOv8pI3oYSEVwg5Tp_c,6496
|
|
7
|
+
PySide6Plot/libs/__init__.py,sha256=cdBp-GgK1QVNMz8F1Fv0qF_qiJB05WPIONqbh7rCr78,57
|
|
8
|
+
PySide6Plot/libs/constant.py,sha256=2ydv7tADEKzLAJEDKJ73LUP6oCulmKtF84IQTmJ-OXs,493
|
|
9
|
+
PySide6Plot/libs/data_handler.py,sha256=mEyiU2ns6cF31b6vdwVqYrNu4Dilb5YEtQjsNsayRVg,7738
|
|
10
|
+
PySide6Plot/libs/helpers.py,sha256=Wd93UNbtDyAdqNY9kDu7OfMSn-YecNOO6cEX78EjmyE,15441
|
|
11
|
+
PySide6Plot/libs/plot_item.py,sha256=R8w_wZ1ppGJ4vhlIbHbIDRvzhBxDg3YRE-e7zrvXN4E,7848
|
|
12
|
+
PySide6Plot/libs/style.py,sha256=dBxt0RhEgnYHVO4-Jwb-0MDdAuJp642rUkzKU2LG3jk,4218
|
|
13
|
+
PySide6Plot/resources/icons/ChevronLeft_black.svg,sha256=kQ5d1FGLKuFWNrPPZ7rJjjb5cHJBGi2tne24JjZODTs,1659
|
|
14
|
+
PySide6Plot/resources/icons/ChevronLeft_white.svg,sha256=KFyk40zbDOT5wRNmr4YqN-BU8lQU31ITv5RIAUSV4CM,1659
|
|
15
|
+
PySide6Plot/resources/qss/dark/navigation_view_interface.qss,sha256=DtAjYvoXVIpVIIYBBD2xXggXn3pqTBQKwM8umvTT0X4,353
|
|
16
|
+
PySide6Plot/resources/qss/light/navigation_view_interface.qss,sha256=g8lANztEM-5q9EqkswK6NfQgOzA7B7HddPaCfzKOKIg,346
|
|
17
|
+
PySide6Plot/widgets/__init__.py,sha256=1MQ5d1nRLjI8wCpCaK3wBq_GJ0xWqXqBIuYFps9MhrM,46
|
|
18
|
+
PySide6Plot/widgets/colorful_toggle_button.py,sha256=J4w61zPt8bwnYjfEgvXs5aAFexLURc2ADjpt1hA7UTs,4637
|
|
19
|
+
PySide6Plot/widgets/fluent_scroller.py,sha256=oAXifVuceOTPhG-mqJ0EcZjfuU7KDlEyJBJadHrkpm4,15026
|
|
20
|
+
PySide6Plot/widgets/line_card.py,sha256=_LAhp3fODOWDUStlioQFTF7dXEdHNpZhbooUUqjQm9A,4886
|
|
21
|
+
PySide6Plot/widgets/navigation_widget.py,sha256=RoSdIukzj5gcXqCX3dgeQL4k2hTh6KE23EWZK77-1i4,1992
|
|
22
|
+
PySide6Plot/widgets/q_plot_widget.py,sha256=y36qHeKY3_0h2_xJ3DWam03hKkAOT6IzScGoe9Wb8Cw,30241
|
|
23
|
+
PySide6Plot/widgets/removable_table.py,sha256=QMBfEczuOPKTRVd4CGA1ca3Gv0obHbIHzWBMDTvw8FY,10059
|
|
24
|
+
PySide6Plot/widgets/transparent_Line_edit.py,sha256=7T7XdsxaGe1IKhLNlaU_-eWbI87H0abDqaxduQSxQak,2480
|
|
25
|
+
PySide6Plot/widgets/transparent_selector.py,sha256=Uu5nY0vYo2uR4WIFoApcUKDJV7N-Bvwl6kBhUPhorTE,7528
|
|
26
|
+
PySide6Plot/widgets/value_select_box.py,sha256=ckvGRNzjPX-TWY0acTknO0_CE9rnXIHFzMQ6JHrAs3E,14678
|
|
27
|
+
PySide6Plot/widgets/zoom_bar.py,sha256=o8VUyj759B_ww7U5mpUc7TJH9D7hy-l9tuf7LBtsBSM,5839
|
|
28
|
+
pyside6plot-0.0.1.dist-info/METADATA,sha256=_7MINfmy4LWZDy5iCYVk4lv5G0BM_qcHKYTNutlvJFA,1373
|
|
29
|
+
pyside6plot-0.0.1.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
30
|
+
pyside6plot-0.0.1.dist-info/top_level.txt,sha256=gGfJuwJhDwCA8bGQzXl-K5O14xWcoVVX7QkK0dUoeSo,12
|
|
31
|
+
pyside6plot-0.0.1.dist-info/RECORD,,
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
PySide6Plot
|