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.
@@ -0,0 +1,624 @@
1
+ from PySide6.QtCore import Qt, Signal, QObject, QEvent
2
+ from pyqtgraph import PlotCurveItem
3
+ import pyqtgraph as pg
4
+ import numpy as np
5
+ from qfluentwidgets import CommandBar, setFont, FluentIcon, Action
6
+ from ..widgets.transparent_selector import (
7
+ TransparentColorSelector,
8
+ TransparentDashTypeSelector,
9
+ TransparentLineWidthSelector,
10
+ )
11
+ from ..widgets.q_plot_widget import QPlotWidget
12
+ from ..widgets.value_select_box import confirmation_dialog
13
+ from ..widgets.removable_table import RemovableTable
14
+ from ..widgets.line_card import LineCard
15
+ from ..widgets.transparent_Line_edit import TransparentLineEdit
16
+ from ..libs.constant import ZOOM_MODEL, YLOC_MODEL
17
+
18
+
19
+ class DrawLineCommandBar(CommandBar):
20
+ """
21
+ Command bar for drawing lines.
22
+ """
23
+
24
+ def __init__(self, parent=None, show_text_editor=True):
25
+ """
26
+ Initialize the DrawLineCommandBar.
27
+
28
+ Args:
29
+ parent: The parent widget. Defaults to None.
30
+ show_text_editor: Flag to show the text editor. Defaults to True.
31
+ """
32
+ super().__init__(parent)
33
+ self.setToolButtonStyle(Qt.ToolButtonStyle.ToolButtonIconOnly)
34
+ # set up editor
35
+ if show_text_editor:
36
+ self.line_name_editor = TransparentLineEdit(parent=self)
37
+ self.line_name_editor.line_edit.setClearButtonEnabled(True)
38
+ self.line_name_editor.line_edit.setPlaceholderText("Line name")
39
+ self.line_name_editor.setFixedWidth(140)
40
+ self.line_name_editor.setFixedHeight(34)
41
+ self.addWidget(self.line_name_editor)
42
+ # set up selectors
43
+ self.line_color_selector = TransparentColorSelector(parent=self)
44
+ self.line_color_selector.setFixedWidth(140)
45
+ self.line_dash_type_selector = TransparentDashTypeSelector(parent=self)
46
+ self.line_width_selector = TransparentLineWidthSelector(parent=self)
47
+ for selector in [
48
+ self.line_color_selector,
49
+ self.line_dash_type_selector,
50
+ self.line_width_selector,
51
+ ]:
52
+ selector.setFixedHeight(34)
53
+ setFont(selector, 12)
54
+ self.addWidget(selector)
55
+ self.addSeparator()
56
+ # set up buttons
57
+ self.draw_line_cancel_action = Action(FluentIcon.CANCEL, "Cancel", parent=self)
58
+ self.draw_line_accept_action = Action(FluentIcon.SAVE, "Save", parent=self)
59
+ self.draw_line_close_action = Action(FluentIcon.CLOSE, "Close", parent=self)
60
+ self.draw_line_delete_action = Action(FluentIcon.DELETE, "Delete", parent=self)
61
+ for action_i in [
62
+ self.draw_line_cancel_action,
63
+ self.draw_line_delete_action,
64
+ self.draw_line_accept_action,
65
+ self.draw_line_close_action,
66
+ ]:
67
+ self.addAction(action_i)
68
+ if action_i is self.draw_line_delete_action:
69
+ self.addSeparator()
70
+
71
+
72
+ class DrawnLineTable(RemovableTable):
73
+ """
74
+ A table widget for displaying and editing drawn lines.
75
+ """
76
+
77
+ def __init__(self, parent=None):
78
+ super().__init__(
79
+ parent=parent,
80
+ show_row_index=False,
81
+ resize_columns_to_contents=False,
82
+ show_move_up_down_buttons=False,
83
+ show_delete_button=True,
84
+ show_rename_button=True,
85
+ accept_name="Edit",
86
+ accept_icon=FluentIcon.EDIT,
87
+ link_default_slot=False,
88
+ )
89
+ self.set_header_labels(["Name", "Line"])
90
+ self.setColumnWidth(0, 100)
91
+ self.setColumnWidth(1, 200)
92
+ # self.setFixedWidth(300)
93
+
94
+
95
+ class DrawLineComponent(QObject):
96
+ """
97
+ A component for drawing lines on a plot widget.
98
+ """
99
+
100
+ sigLineRemoved = Signal(int)
101
+
102
+ def __init__(self, plot_widget: QPlotWidget, parent=None) -> None:
103
+ """
104
+ Initializes a DrawLineComponent object.
105
+
106
+ Args:
107
+ plot_widget (QPlotWidget): The plot widget on which the lines will be drawn.
108
+ parent (QObject, optional): The parent object. Defaults to None.
109
+ """
110
+ super().__init__(parent=parent)
111
+ self.parent = parent
112
+ self.custom_lines = []
113
+ self.current_custom_line = None
114
+ self.is_current_line_new = True
115
+ self.num_new_added_points = 0
116
+ self.clicked_from_line = False
117
+ self.dynamic_line = None
118
+ self.menu_action = Action(FluentIcon.PENCIL_INK, "Draw line")
119
+ self.plot_widget = plot_widget
120
+ self.command_bar = DrawLineCommandBar(parent=parent, show_text_editor=True)
121
+ self.command_bar.hide()
122
+ self.line_table = DrawnLineTable(parent=parent)
123
+ self.line_table.hide()
124
+ self.__init_connections()
125
+
126
+ def __init_connections(self):
127
+ """
128
+ Initializes the signal-slot connections for the DrawLineComponent object.
129
+ """
130
+ self.menu_action.triggered.connect(self.add_new_line)
131
+ self.command_bar.draw_line_accept_action.triggered.connect(self.__on_draw_line_accept_clicked)
132
+ self.command_bar.draw_line_cancel_action.triggered.connect(self.__on_draw_line_cancel_clicked)
133
+ self.command_bar.draw_line_close_action.triggered.connect(self.__on_draw_line_close_clicked)
134
+
135
+ self.command_bar.draw_line_delete_action.triggered.connect(self.remove_current_line)
136
+ self.line_table.sigDeleteClicked.connect(self.remove_saved_line)
137
+ self.sigLineRemoved.connect(lambda x: self.__deactivate_command_bar())
138
+ self.sigLineRemoved.connect(self.line_table.delete_row_data)
139
+
140
+ self.line_table.sigAcceptClicked.connect(self.__on_line_table_accept_clicked)
141
+ self.line_table.sigRowClicked.connect(self.jump_to_line)
142
+ self.line_table.sigNameEdited.connect(self.__on_line_table_name_changed)
143
+
144
+ self.command_bar.line_color_selector.item_selected.connect(self.__on_line_color_selector_selected)
145
+ self.command_bar.line_dash_type_selector.item_selected.connect(self.__on_line_dash_type_selector_selected)
146
+ self.command_bar.line_width_selector.item_selected.connect(self.__on_line_width_selector_selected)
147
+ self.command_bar.line_name_editor.sigTextEditFinished.connect(self.__on_line_name_editor_text_changed)
148
+ self.plot_widget_clicked_slot = pg.SignalProxy(
149
+ self.plot_widget.scene().sigMouseClicked, rateLimit=50, slot=self.__on_plot_widget_mouse_clicked
150
+ )
151
+ self.plot_widget_mouse_move_slot = pg.SignalProxy(
152
+ self.plot_widget.scene().sigMouseMoved, rateLimit=50, slot=self.__on_plot_widget_mouse_moved
153
+ )
154
+ self.plot_widget_mouse_leave_slot = pg.SignalProxy(
155
+ self.plot_widget.sigMouseLeaved, rateLimit=50, slot=self.__on_plot_widget_mouse_leaved
156
+ )
157
+ self.plot_widget.installEventFilter(self)
158
+
159
+ def __activate_command_bar(self):
160
+ """
161
+ Activates the line command bar for the current custom line.
162
+
163
+ Raises:
164
+ Exception: If there is no current line.
165
+ """
166
+ if self.current_custom_line is None:
167
+ raise Exception("No current line")
168
+ self.is_current_line_new = self.current_custom_line not in self.custom_lines
169
+ if self.is_current_line_new:
170
+ self.command_bar.draw_line_delete_action.setEnabled(False)
171
+ else:
172
+ self.command_bar.draw_line_delete_action.setEnabled(True)
173
+ self.command_bar.line_name_editor.set_text(self.current_custom_line["name"])
174
+ self.command_bar.line_color_selector.set_item(self.current_custom_line["color"])
175
+ self.command_bar.line_width_selector.set_item(self.current_custom_line["width"])
176
+ self.command_bar.line_dash_type_selector.set_item(self.current_custom_line["dash"])
177
+ self.command_bar.draw_line_cancel_action.setEnabled(False)
178
+ self.command_bar.draw_line_accept_action.setEnabled(False)
179
+ self.command_bar.show()
180
+
181
+ def __deactivate_command_bar(self):
182
+ """
183
+ Deactivates the line command bar and performs necessary cleanup.
184
+
185
+ This method hides the draw line command bar, removes the dynamic line from the plot widget,
186
+ resets the current custom line, and resets the number of new added points. It also adjusts
187
+ the position of the zoom card.
188
+
189
+ Note: The resize event doesn't work here for an unknown reason.
190
+
191
+ Returns:
192
+ None
193
+ """
194
+ self.command_bar.hide()
195
+ self.current_custom_line = None
196
+ if self.dynamic_line is not None:
197
+ self.plot_widget.remove_item(self.dynamic_line)
198
+ self.dynamic_line = None
199
+ self.num_new_added_points = 0
200
+
201
+ def __on_draw_line_cancel_clicked(self):
202
+ """
203
+ Callback method for handling the click event of the 'Cancel' button in the draw line functionality.
204
+ Removes the last point from the current custom line, updates the data, and disables the accept and cancel buttons if no new points are added.
205
+ """
206
+ xs, ys = self.current_custom_line["line_item"].getData()
207
+ self.current_custom_line["line_item"].setData(x=xs[0:-1], y=ys[0:-1])
208
+ self.num_new_added_points -= 1
209
+ if self.num_new_added_points == 0:
210
+ self.command_bar.draw_line_cancel_action.setEnabled(False)
211
+ self.command_bar.draw_line_accept_action.setEnabled(False)
212
+
213
+ def __on_draw_line_accept_clicked(self):
214
+ """
215
+ Callback method for the 'Accept' button click event in the draw line functionality.
216
+ Updates the properties of the current custom line based on the selected color, width, and dash type.
217
+ If the current line is new, it appends it to the list of custom lines.
218
+ Deactivates the line command bar.
219
+ """
220
+ self.current_custom_line["name"] = self.command_bar.line_name_editor.get_text()
221
+ self.current_custom_line["color"] = self.command_bar.line_color_selector.current_item
222
+ self.current_custom_line["width"] = self.command_bar.line_width_selector.current_item
223
+ self.current_custom_line["dash"] = self.command_bar.line_dash_type_selector.current_item
224
+ if self.is_current_line_new:
225
+ self.current_custom_line["line_item"].setClickable(True)
226
+ self.current_custom_line["line_item"].sigClicked.connect(self.__on_new_line_clicked)
227
+ self.custom_lines.append(self.current_custom_line)
228
+ self.line_table.add_row_data(
229
+ [
230
+ self.current_custom_line["name"],
231
+ LineCard(
232
+ line_color=self.current_custom_line["color"],
233
+ line_width=self.current_custom_line["width"],
234
+ dash_type=self.current_custom_line["dash"],
235
+ ),
236
+ ]
237
+ )
238
+ else:
239
+ row_index = self.custom_lines.index(self.current_custom_line)
240
+ self.line_table.set_row_data_item(row_index, 0, self.current_custom_line["name"])
241
+ self.line_table.set_row_data_item(
242
+ row_index,
243
+ 1,
244
+ LineCard(
245
+ line_color=self.current_custom_line["color"],
246
+ line_width=self.current_custom_line["width"],
247
+ dash_type=self.current_custom_line["dash"],
248
+ ),
249
+ )
250
+ self.__deactivate_command_bar()
251
+
252
+ def __on_draw_line_close_clicked(self):
253
+ """
254
+ Handles the event when the "Close" button is clicked in the draw line mode.
255
+
256
+ If the current line is new and has unsaved points, a confirmation dialog is shown to ask if the user wants to leave without saving.
257
+ If the user chooses to leave, the current line is removed from the plot widget.
258
+ If the current line is not new but has unsaved points, a confirmation dialog is shown to ask if the user wants to leave without saving.
259
+ If the user chooses to leave, the last added points are removed from the current line.
260
+ The line command bar is deactivated after the operation.
261
+
262
+ Returns:
263
+ True if the operation is successful, False otherwise.
264
+ """
265
+ if self.is_current_line_new and self.num_new_added_points > 0:
266
+ if not confirmation_dialog(
267
+ self.parent.window(),
268
+ "Current line is not saved",
269
+ "The line you are editing now is not saved yet. Do you still want to leave?",
270
+ ):
271
+ return False
272
+ self.plot_widget.remove_item(self.current_custom_line["line_item"])
273
+ elif self.num_new_added_points > 0:
274
+ if not confirmation_dialog(
275
+ self.parent.window(),
276
+ "Current line is not saved",
277
+ "The line you are editing now is not saved yet. Do you still want to leave?",
278
+ ):
279
+ return False
280
+ xs, ys = self.current_custom_line["line_item"].getData()
281
+ self.current_custom_line["line_item"].setData(
282
+ x=xs[0 : -1 * self.num_new_added_points], y=ys[0 : -1 * self.num_new_added_points]
283
+ )
284
+ self.__deactivate_command_bar()
285
+ return True
286
+
287
+ def __on_new_line_clicked(self, line):
288
+ """
289
+ Event handler for when a new line is clicked.
290
+
291
+ Args:
292
+ line: The line that was clicked.
293
+
294
+ Returns:
295
+ False if the current line is not saved and the user chooses not to edit another line, True otherwise.
296
+ """
297
+ self.clicked_from_line = True
298
+
299
+ if self.current_custom_line is not None:
300
+ if not self.__on_draw_line_close_clicked():
301
+ return False
302
+ for line_item in self.custom_lines:
303
+ if line_item["line_item"] is line:
304
+ self.current_custom_line = line_item
305
+ self.__activate_command_bar()
306
+ break
307
+
308
+ def __on_line_name_editor_text_changed(self):
309
+ """
310
+ Callback method for handling the text changed event of the line name editor.
311
+ """
312
+ pass
313
+ current_text = self.command_bar.line_name_editor.line_edit.text()
314
+ if current_text == "":
315
+ self.command_bar.line_name_editor.set_text(self.current_custom_line["name"])
316
+ elif current_text != self.current_custom_line["name"]:
317
+ xs, ys = self.current_custom_line["line_item"].getData()
318
+ if len(xs) > 0:
319
+ self.command_bar.draw_line_accept_action.setEnabled(True)
320
+
321
+ def __on_line_color_selector_selected(self, item):
322
+ """
323
+ Callback method triggered when a line color is selected in the color selector.
324
+
325
+ Args:
326
+ item: The selected item from the color selector.
327
+
328
+ """
329
+ if self.current_custom_line["color"] != self.command_bar.line_color_selector.get_value(item):
330
+ self.current_custom_line["line_item"].setPen(
331
+ pg.mkPen(
332
+ self.command_bar.line_color_selector.get_value(item),
333
+ dash=self.command_bar.line_dash_type_selector.current_item,
334
+ width=self.command_bar.line_width_selector.current_item,
335
+ )
336
+ )
337
+ self.current_custom_line["color"] = self.command_bar.line_color_selector.get_value(item)
338
+ if self.dynamic_line is not None:
339
+ self.dynamic_line.setPen(
340
+ pg.mkPen(
341
+ self.command_bar.line_color_selector.get_value(item),
342
+ dash=self.command_bar.line_dash_type_selector.current_item,
343
+ width=self.command_bar.line_width_selector.current_item,
344
+ )
345
+ )
346
+ xs, ys = self.current_custom_line["line_item"].getData()
347
+ if len(xs) > 0:
348
+ self.command_bar.draw_line_accept_action.setEnabled(True)
349
+
350
+ def __on_line_width_selector_selected(self, item):
351
+ """
352
+ Callback function for the line width selector.
353
+
354
+ Args:
355
+ item: The selected item from the line width selector.
356
+
357
+ """
358
+ if self.current_custom_line["width"] != self.command_bar.line_width_selector.get_value(item):
359
+ self.current_custom_line["line_item"].setPen(
360
+ pg.mkPen(
361
+ self.command_bar.line_color_selector.current_item,
362
+ dash=self.command_bar.line_dash_type_selector.current_item,
363
+ width=self.command_bar.line_width_selector.get_value(item),
364
+ )
365
+ )
366
+ self.current_custom_line["width"] = self.command_bar.line_width_selector.get_value(item)
367
+ if self.dynamic_line is not None:
368
+ self.dynamic_line.setPen(
369
+ pg.mkPen(
370
+ self.command_bar.line_color_selector.current_item,
371
+ dash=self.command_bar.line_dash_type_selector.current_item,
372
+ width=self.command_bar.line_width_selector.get_value(item),
373
+ )
374
+ )
375
+ xs, ys = self.current_custom_line["line_item"].getData()
376
+ if len(xs) > 0:
377
+ self.command_bar.draw_line_accept_action.setEnabled(True)
378
+
379
+ def __on_line_dash_type_selector_selected(self, item):
380
+ """
381
+ Callback method triggered when a line dash type is selected in the line dash type selector.
382
+
383
+ Args:
384
+ item: The selected line dash type item.
385
+
386
+ """
387
+ if self.current_custom_line["dash"] != self.command_bar.line_dash_type_selector.get_value(item):
388
+ self.current_custom_line["line_item"].setPen(
389
+ pg.mkPen(
390
+ self.command_bar.line_color_selector.current_item,
391
+ dash=self.command_bar.line_dash_type_selector.get_value(item),
392
+ width=self.command_bar.line_width_selector.current_item,
393
+ )
394
+ )
395
+ self.current_custom_line["dash"] = self.command_bar.line_dash_type_selector.get_value(item)
396
+ if self.dynamic_line is not None:
397
+ self.dynamic_line.setPen(
398
+ pg.mkPen(
399
+ self.command_bar.line_color_selector.current_item,
400
+ dash=self.command_bar.line_dash_type_selector.get_value(item),
401
+ width=self.command_bar.line_width_selector.current_item,
402
+ )
403
+ )
404
+ xs, ys = self.current_custom_line["line_item"].getData()
405
+ if len(xs) > 0:
406
+ self.command_bar.draw_line_accept_action.setEnabled(True)
407
+
408
+ def __on_line_table_name_changed(self, row_index, new_name):
409
+ """
410
+ Callback method triggered when the name of a line in the line table is changed.
411
+
412
+ Args:
413
+ row_index: The index of the row in the line table.
414
+ new_name: The new name of the line.
415
+ """
416
+ if new_name == "":
417
+ self.line_table.set_row_data_item(row_index, 0, self.custom_lines[row_index]["name"])
418
+ else:
419
+ self.custom_lines[row_index]["name"] = new_name
420
+ if self.custom_lines[row_index] == self.current_custom_line and self.command_bar.isVisible():
421
+ self.command_bar.line_name_editor.set_text(new_name)
422
+
423
+ def __on_line_table_accept_clicked(self, row_index):
424
+ """
425
+ Handles the event when the accept button is clicked in the line table.
426
+
427
+ Args:
428
+ row_index (int): The index of the selected row in the line table.
429
+ """
430
+ if self.jump_to_line(row_index):
431
+ self.current_custom_line = self.custom_lines[row_index]
432
+ self.__activate_command_bar()
433
+
434
+ def __on_plot_widget_mouse_moved(self, event):
435
+ """
436
+ Handle the mouse moved event on the plot widget.
437
+
438
+ Args:
439
+ event: The mouse moved event.
440
+ """
441
+ pos = event[0]
442
+ mouse_point = self.plot_widget.plotItem.vb.mapSceneToView(pos)
443
+ if self.current_custom_line is not None:
444
+ line_item = self.current_custom_line["line_item"]
445
+ xs, ys = line_item.getData()
446
+ if len(xs) > 0:
447
+ if self.dynamic_line is None:
448
+ self.dynamic_line = PlotCurveItem(
449
+ pen=pg.mkPen(
450
+ self.current_custom_line["color"],
451
+ dash=self.current_custom_line["dash"],
452
+ width=self.current_custom_line["width"],
453
+ ),
454
+ clickable=False,
455
+ )
456
+ self.plot_widget.add_item(self.dynamic_line)
457
+ self.dynamic_line.updateData(
458
+ x=np.array([xs[-1], mouse_point.x()]), y=np.array([ys[-1], mouse_point.y()])
459
+ )
460
+
461
+ def __on_plot_widget_mouse_clicked(self, event=None):
462
+ """
463
+ Handle the mouse click event on the plotter.
464
+ Add a new point to the current custom line if the left button is clicked.
465
+
466
+ Parameters:
467
+ - event (QMouseEvent): The mouse click event.
468
+
469
+ """
470
+ if self.current_custom_line is not None and event[0].button() == Qt.MouseButton.LeftButton:
471
+ if not self.clicked_from_line:
472
+ mouse_point = self.plot_widget.plotItem.vb.mapSceneToView(event[0].scenePos())
473
+ if self.plot_widget.viewRect().contains(mouse_point):
474
+ xs, ys = self.current_custom_line["line_item"].getData()
475
+ if len(xs) == 0:
476
+ xs = np.array([mouse_point.x()])
477
+ ys = np.array([mouse_point.y()])
478
+ else:
479
+ xs = np.append(xs, mouse_point.x())
480
+ ys = np.append(ys, mouse_point.y())
481
+ self.__on_plot_widget_mouse_leaved(None)
482
+ self.current_custom_line["line_item"].setData(x=xs, y=ys)
483
+ self.command_bar.draw_line_cancel_action.setEnabled(True)
484
+ self.command_bar.draw_line_accept_action.setEnabled(True)
485
+ self.num_new_added_points += 1
486
+ # print(self.current_custom_line["line_item"].getData())
487
+ else:
488
+ self.clicked_from_line = False
489
+
490
+ def __on_plot_widget_mouse_leaved(self, event):
491
+ """
492
+ Removes the dynamic line from the plot widget when the mouse leaves.
493
+
494
+ Parameters:
495
+ - event: The mouse leave event.
496
+
497
+ """
498
+ if self.dynamic_line is not None:
499
+ self.plot_widget.remove_item(self.dynamic_line)
500
+ self.dynamic_line = None
501
+
502
+ def eventFilter(self, a0: QObject, a1: QEvent) -> bool:
503
+ """
504
+ Filters events for the specified object.
505
+
506
+ Args:
507
+ a0 (QObject): The object to filter events for.
508
+ a1 (QEvent): The event to be filtered.
509
+
510
+ Returns:
511
+ bool: True if the event was filtered and should be ignored, False otherwise.
512
+ """
513
+ if a0 == self.plot_widget and a1.type() == QEvent.Type.KeyPress and self.command_bar.isVisible():
514
+ if (
515
+ a1.key() in (Qt.Key.Key_Enter, Qt.Key.Key_Return)
516
+ and self.command_bar.draw_line_accept_action.isEnabled()
517
+ ):
518
+ self.__on_draw_line_accept_clicked()
519
+ return False
520
+ elif a1.key() == Qt.Key.Key_Escape:
521
+ self.__on_draw_line_close_clicked()
522
+ return False
523
+ return super().eventFilter(a0, a1)
524
+
525
+ def get_widget(self):
526
+ """
527
+ Returns the command bar and line table as a tuple.
528
+
529
+ Returns:
530
+ tuple: A tuple containing the command bar and line table widgets.
531
+ """
532
+ return self.command_bar, self.line_table
533
+
534
+ def add_new_line(self):
535
+ """
536
+ Adds a new line to the plot.
537
+
538
+ If there is a current custom line being edited and it has unsaved changes, a confirmation dialog is displayed.
539
+ If the user chooses to add a new line, the current line is closed and a new line is created with default settings.
540
+ The new line is added to the plot and the line command bar is activated.
541
+ """
542
+ if self.current_custom_line is not None:
543
+ if not self.__on_draw_line_close_clicked():
544
+ return False
545
+ line_item_now = PlotCurveItem(
546
+ pen=pg.mkPen(
547
+ self.command_bar.line_color_selector.default_item,
548
+ dash=self.command_bar.line_dash_type_selector.default_item,
549
+ width=self.command_bar.line_width_selector.default_item,
550
+ ),
551
+ clickable=False,
552
+ )
553
+ self.current_custom_line = {
554
+ "name": "Line " + str(len(self.custom_lines) + 1),
555
+ "line_item": line_item_now,
556
+ "color": self.command_bar.line_color_selector.default_item,
557
+ "width": self.command_bar.line_width_selector.default_item,
558
+ "dash": self.command_bar.line_dash_type_selector.default_item,
559
+ }
560
+ self.__activate_command_bar()
561
+ self.plot_widget.add_item(line_item_now)
562
+
563
+ def remove_current_line(self):
564
+ """
565
+ Removes the current line from the list of custom lines.
566
+
567
+ Raises:
568
+ Exception: If the current line is new.
569
+
570
+ Returns:
571
+ bool: True if the line is successfully removed, False otherwise.
572
+ """
573
+ if self.is_current_line_new:
574
+ raise Exception("Current line is new")
575
+ row_index = self.custom_lines.index(self.current_custom_line)
576
+ return self.remove_saved_line(row_index)
577
+
578
+ def remove_saved_line(self, row_index):
579
+ """
580
+ Removes a saved line from the plot.
581
+
582
+ Args:
583
+ row_index (int): The index of the line to be removed.
584
+
585
+ Returns:
586
+ bool: True if the line was successfully removed, False otherwise.
587
+ """
588
+ if not confirmation_dialog(
589
+ self.parent.window(), "Delete confirmation", "Are you sure to delete the current line?"
590
+ ):
591
+ return False
592
+ else:
593
+ self.plot_widget.remove_item(self.custom_lines[row_index]["line_item"])
594
+ self.custom_lines.pop(row_index)
595
+ self.sigLineRemoved.emit(row_index)
596
+ return True
597
+
598
+ def jump_to_line(self, row_index):
599
+ """
600
+ Jumps to the specified line in the plot.
601
+
602
+ Args:
603
+ row_index (int): The index of the line to jump to.
604
+
605
+ Returns:
606
+ bool: True if the jump was successful, False otherwise.
607
+ """
608
+ if self.current_custom_line is not None:
609
+ if self.current_custom_line == self.custom_lines[row_index]:
610
+ return True
611
+ if not self.__on_draw_line_close_clicked():
612
+ return False
613
+
614
+ bounding_rect = self.custom_lines[row_index]["line_item"].boundingRect()
615
+ x_range = bounding_rect.width()
616
+ x_loc = bounding_rect.x()
617
+ y_loc = bounding_rect.y()
618
+
619
+ self.plot_widget.update_plot(x_loc=x_loc, x_range=x_range)
620
+
621
+ if self.plot_widget.zoom_model != ZOOM_MODEL.AUTO_RANGE and self.plot_widget.y_loc_model == YLOC_MODEL.FREE:
622
+ self.plot_widget.move_y_loc(y_loc)
623
+
624
+ return True