I have been struggling with a problem for about a month. I have used ChatGPT-4o during this time, but it hasn't provided a satisfactory solution. The issue is as follows:
When a user clicks on a part of a curve, the segmented part should be draggable up and down, with the y-axis values updating accordingly as the mouse moves. However, the problem arises when I drag the segment curve: the axis does not match the curve. Specifically, while the y-axis moves with the curve, the values of the curve no longer align with the axis, and they change inconsistently.
Here is a part of my code. Note that "self.p1" or "self.ui.pw" is a PlotWidget object defined in a UI file. We added all curves and view boxes to this main object. The code is written in Python and uses PyQt5. I used three functions to define these parts:
ok_on_segments(self): This function adds all segments into the "nv" view box and then adds it to "self.ui.pw". on_curve_selected(self, curve_name): When the user clicks on a segment, this function is triggered. For each selected curve, I create a view box, set the exact range, enable dragging by mouse, fix all other curves, and add the new view box to the "self.ui.pw" object. Despite this setup, the y-axis values do not maintain alignment with the dragged curve segment.
Here is the relevant part of my code:
def ok_on_segments(self):
#creat style for curves
global colors, curve_to_vb_map, num_curves
if self.split_btn_flag == 1:
curve_name_dict = dict()
curve_name_dict_temp = dict()
self.p1.removeItem(self.curve_3)
self.p2.removeItem(self.curve_1)
colors[num_import % len(colors)]
color = colors[0]
width = 2
pen = pg.mkPen(color, width=width)
#set data on a split curves
for ind in range(self.segments_window.listWidget_complete.count()):
list_item = self.segments_window.listWidget_complete.item(ind)
if ind!=0:
if list_item.flags() & Qt.ItemIsUserCheckable:
if list_item.checkState() == Qt.Checked:
if ind+1<=self.segments_window.listWidget_complete.count():
#convert s to min
x = []
try:
for item in self.data_dict[ind-1][f"step_{ind}"]["time"]:
x.append(item/60)
except:
pass
y = [x/self.sampleMass for x in self.data_dict[ind-1][f"step_{ind}["dsc"]]
z = self.data_dict[ind-1][f"step_{ind}"]["temp"]
custom_pen,cl = self.apply_custom_style_DDSC(pen)
self.custom_pen_list_dsc.append(cl)
curve_name_dict[f'curve_{self.counter_name}'] = pg.PlotCurveItem(x, y,pen=custom_pen)
curve_name_dict[f'curve_{self.counter_name}'].setClickable(True)
num_import_ = 1
curves_dict[num_curves] = {'curve_name':f'curve_{self.counter_name}' , 'curve_label':'DSC' }
num_curves = num_curves + 1
num_import_ =str(num_import)
self.textItem = pg.TextItem(text=f"{num_import_}.{ind}", color=color, anchor=(0, 0), border=pg.mkPen(color=color), fill=pg.mkBrush(None))
last_point = x[-1], y[-1]
self.textItem.setPos(last_point[0], last_point[1])
# Create a new view box and add items
self.p1.addItem(self.textItem)
self.counter_name += 1
self.curve_name_dict = curve_name_dict
nv = self.create_new_viewbox('dsc','l')
for i in range(self.counter_name):
try:
nv.addItem(curve_name_dict[f'curve_{i}'])
except:
pass
self.ui.pw.addItem(nv)
nv.setZValue(1)
self.curve_name_dict = curve_name_dict
for curve_name, curve in self.curve_name_dict.items():
curve.sigClicked.connect(partial(self.on_curve_selected, curve_name))
def on_curve_selected(self, curve_name): print(curve_name) for i in range(len(list(self.curve_name_dict.keys()))): if list(self.curve_name_dict.keys())[i]==curve_name: print(i) color_number = self.custom_pen_list_dsc[i] break y_min = 100 y_max = 0 for items in self.curve_name_dict.values(): if np.min(items.yData)<y_min: y_min = np.min(items.yData) if np.max(items.yData)>y_max: y_max = np.max(items.yData)
dict_=self.curve_name_dict
new_vbox = self.create_new_viewbox('DSC','l',color_number)
if curve_name != self.current_name:
curve = dict_[curve_name]
new_vbox.addItem(dict_[curve_name])
new_vbox.setZValue(1)
self.ui.pw.addItem(new_vbox)
self.ui.pw.setMouseEnabled(False)
new_vbox.setRange(yRange=(y_min, y_max))
for k in dict_.keys():
if k!=curve_name:
fixed_curve = FixedPlotDataItem(dict_[k].xData,dict_[k].yData)
self.ui.pw.addItem(fixed_curve)
self.current_viewbox = new_vbox
self.current_name = curve_name
def create_new_viewbox(self, label,pos,color='black'): new_viewbox = YAxisOnlyViewBox()
# new_viewbox.setMouseMode(new_viewbox.RectMode)
if pos == "l":
self.p1.scene().addItem(new_viewbox)
self.p1.showAxis('left')
self.p1.getAxis('left').linkToView(new_viewbox)
new_viewbox.setXLink(self.p1)
self.configure_axis(self.p1.getAxis('left'), label,color)
class YAxisOnlyViewBox(pg.ViewBox): def init(self,*args, **kwargs): super().init(*args, **kwargs) # self.setMouseMode(self.RectMode) self.draggableItem = None self.dragOffset = None self.setMouseEnabled(x=False,y=True)
class FixedPlotDataItem(pg.PlotDataItem): def mouseDragEvent(self, ev): # Ignore mouse drag events to disable dragging pass
I tried adding one view box for each segment and then clearing the other view boxes. Additionally, I experimented with using PlotWidget items and subplots. I also tried using mouse events instead of sigClicked.