PyQt connect inside for loop vs. separate calls results in different behavior

两盒软妹~` 提交于 2019-11-27 07:32:07

问题


I'm building a plotting UI that lets a user generate multiple plots from loaded data sets. As part of this the user can add marker lines to their plots to examine (x, y) values by moving those marker lines across the plot.

The functions below work fine if the markers are added to each plot separately (i.e. add_marker to Plot1, Plot2, etc. separately via the elif block of code). However, if the "add to all" option is selected, resulting in the usage of the for loop block of code in the add_marker function all of the markers end up being children of the last plotItem in the list (plot_objects).

If I check the marker objects as the loop iterates when the add_marker function is called the objects, and their parents, are distinct. However, if I check the parents in the update_marker_vals function the parent for markers 1 and 2 are incorrect for all but the last plot in the list.

Not sure what's going on here, but I assume it has something to do with the two sigDragged.connect statements, seeing as everything else seems fine before then.

Code:

def add_marker(self):
        name = self.markersPlot_dropdown.currentText()

        if name == "All":
            for plot_name, plt in self.plot_objects.items():
                x_val = (plt.viewRange()[0][0]+plt.viewRange()[0][1])/2
                marker_one = plt.addLine(x=x_val*0.5, pen=pg.mkPen('g', width=2.0), movable=True)
                marker_two = plt.addLine(x=x_val*1.5, pen=pg.mkPen('c', width=2.0), movable=True)
                marker_one.sigDragged.connect(lambda: self.update_marker_vals(marker_one, "Marker One"))
                marker_two.sigDragged.connect(lambda: self.update_marker_vals(marker_two, "Marker Two"))

                self.plot_markers[plot_name] = {"Marker One": marker_one, "Marker Two:": marker_two}

        elif name:
            plt = self.plot_objects[name]
            x_val = (plt.viewRange()[0][0]+plt.viewRange()[0][1])/2
            marker_one = plt.addLine(x=x_val*0.5, pen=pg.mkPen('g', width=2.0), movable=True)
            marker_two = plt.addLine(x=x_val*1.5, pen=pg.mkPen('c', width=2.0), movable=True)
            marker_one.sigDragged.connect(lambda: self.update_marker_vals(marker_one, "Marker One"))
            marker_two.sigDragged.connect(lambda: self.update_marker_vals(marker_two, "Marker Two"))

            self.plot_markers[name] = {"Marker One": marker_one, "Marker Two:": marker_two}

    def update_marker_vals(self, marker, marker_num):
        plot_item = marker.parentItem().parentItem().parentItem()
        plot_name = list(self.plot_objects.keys())[list(self.plot_objects.values()).index(plot_item)]
        sampling_divisor = self.selected_curve[plot_name].getData()[0][1] - \
                           self.selected_curve[plot_name].getData()[0][0]
        index = int(marker.getXPos() / sampling_divisor)
        x_val = self.selected_curve[plot_name].getData()[0][index]
        y_val = self.selected_curve[plot_name].getData()[1][index]
        if marker_num == "Marker One":
            print(plot_name)
            print("Marker One, :" + str(index))
            print(x_val, y_val)

        elif marker_num == "Marker Two":
            print(plot_name)
            print("Marker Two, :" + str(index))
            print(x_val, y_val) 

Edit:

On a side note, as a solution I can separate this function out into two functions - one function that creates the two markers and then another function that takes the input from the QComboBox and creates one set of markers for a specific plot or creates markers for all the plots available. This works, and is my current solution, but I'm still curious as to what the issue is with the above code.


回答1:


When you connect a signal to a lambda function, the contents of the lambda function are evaluated when the signal is emitted, not when the signal is connected. As such, the variables you use (marker_one and marker_two) always point to the objects created in the last iteration of the loop.

One simple solution is to explicitly pass in marker_one and marker_two as default arguments to variables of the same name, in the signature of the lambda function:

lambda marker_one=marker_one: self.update_marker_vals(marker_one, "Marker One")
lambda marker_two=marker_two: self.update_marker_vals(marker_two, "Marker Two")

There are several useful answers relating to a very similar problem here, specifically the answer by ekhumoro, if you would like to know more (my answer to that question my also be of use, although ekhumoro's solution is cleaner)



来源:https://stackoverflow.com/questions/27208706/pyqt-connect-inside-for-loop-vs-separate-calls-results-in-different-behavior

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!