简体   繁体   中英

Direct calls to function to update matplotlib figure works, calling it repeatedly from a loop only shows final result

Background: I am developing GUI for analyzing experimental imaging data. I have a viewing window (ie, the matplotlib figure) where I overlay manually selected data points (if any) and optical values (if normalization has occurred) on top of a background image. Below the axis I have a QScrollBar that I can use to manually move to different time points in the data. I initialize it like so:

self.movie_scroll_obj.valueChanged.connect(self.update_axes)

With the relevant part of the associated function looking like the following:

def update_axes(self):
        # Determine if data is prepped or unprepped
        data = self.data_filt
        # UPDATE THE OPTICAL IMAGE AXIS
        # Clear axis for update
        self.mpl_canvas.axes.cla()
        # Update the UI with an image off the top of the stack
        self.mpl_canvas.axes.imshow(self.data[0], cmap='gray')
        # Match the matplotlib figure background color to the GUI
        self.mpl_canvas.fig.patch.set_facecolor(self.bkgd_color)
        # If normalized, overlay the potential values
        if self.norm_flag == 1:
            # Get the current value of the movie slider
            sig_id = self.movie_scroll_obj.value()
            # Create the transparency mask
            mask = ~self.mask
            thresh = self.data_filt[sig_id, :, :] > 0.3
            transp = mask == thresh
            transp = transp.astype(float)
            # Overlay the voltage on the background image
            self.mpl_canvas.axes.imshow(self.data_filt[sig_id, :, :],
                                        alpha=transp, vmin=0, vmax=1,
                                        cmap='jet')
        # Plot the select signal points
        for cnt, ind in enumerate(self.signal_coord):
            if self.signal_toggle[cnt] == 0:
                continue
            else:
                self.mpl_canvas.axes.scatter(
                    ind[0], ind[1], color=self.cnames[cnt])
        # Tighten the border on the figure
        self.mpl_canvas.fig.tight_layout()
        self.mpl_canvas.draw()

The overlay of normalized data occurs within the IF statement in the above block of code. When I interact with the QScrollBar object it works exactly as intended. However, I have a play button that I want to be able to click and have the plot update like a movie from whatever the current scrollbar value is. I also want the string on the button to change to "Stop Movie" and have a subsequent click stop the movie. To accomplish this I have connected the pushbutton to the following code:

def play_toggle(self, event):
        # Grab the button string
        button_str = self.play_movie_button.text()
        if button_str == 'Play Movie':
            # Update the button string
            self.play_movie_button.setText('Stop Movie')
            # time.sleep(1)
            # Run the play movie function
            self.play_movie()
        else:
            # Update the play movie boolean
            self.play_bool = 0
            # Update the button string
            self.play_movie_button.setText('Play Movie')

Which calls this function:

def play_movie(self):
        # Set the play boolean to true
        self.play_bool = 1
        # Grab the current value of the movie scroll bar
        cur_val = self.movie_scroll_obj.value()
        # Grab the maximum value of the movie scroll bar
        max_val = self.movie_scroll_obj.maximum()
        # Being updating the scroll bar value and the movie window
        # while self.play_bool == 1:
        for n in np.arange(cur_val+5, cur_val+30, 5):
            # Check to make sure you haven't reached the end of the signal
            # if self.movie_scroll_obj.value() < max_val:
            # Update the slider value
            if self.play_bool == 1:
                self.movie_scroll_obj.setValue(n)
                plt.pause(1)
            # If button is hit again, break loop
            else:
                break

Because I used the "valueChanged" signal for the QScrollBar, updating its value in this function calls the update_axes() function. The current iteration is the biproduct of a handful of hours scouring the Internet for solutions to the two following issues:

  1. ) The reason I have limited the np.arange call in the for loop (and that I'm using a for loop rather than a while loop) is because if I let it run its course without a built in stop point the GUI crashes and I get a "SpyderKernelApp WARNING No such comm" error.

  2. ) The figure does not update with each iteration through the loop. It only updates at the end of the short for loop I currently have implemented.

I am currently developing this using Spyder 4.1.4, Python 3.8, and Qt Designer (using Qt 5.11.1). I'll include a picture of the GUI on the off chance it helps folks orient themselves. The code is currently up on GitHub, but I would need to push my current version and fix a *.yml if anyone decided they were that invested in rescuing me. Happy to provide additional code or access to code as needed.

Image of GUI with visualized data present.:
具有可视化数据的 GUI 图像。

A potential reason for the figure not updating:

def play_movie(self):
    [...]
    self.movie_scroll_obj.setValue(n)

This only changes the value of the move_scroll_obj. You are expecting that to call self.update_axes, since you connected it via

self.movie_scroll_obj.valueChanged.connect(self.update_axes)

However, since you are in the play_movie function, all these connect-updates are postponed until the play_movie function returns to the main event loop, ie until the loop finishes. Hence you only see the update at the end of function.

This is potentially also the reason for the spyder crash, as with a while loop you would indefinitely fill up the event-queue.

Directly implementing the function call into the loop should fix this behaviour:

def play_movie(self):
    [...]
    self.movie_scroll_obj.setValue(n)
    self.update_axes()

The self.movie_scroll_obj.valueChanged.connect gets redundant at this point and should be deleted (elsewise the event-queue would still be filled up during the loop).

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM