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:
) 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.
) 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.
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.