Re: Plotting to a matplotlib / pyplot window does not work

Matthias Brennwald <[email protected]>
Newsgroups gmane.comp.python.wxpython
Message-ID <[email protected]>
On Mo, Jul 20, 2020 at 23:02, Andrea Gavana <[email protected]> 
wrote:
> A relatively straightforward solution is to embed your plot in a 
> wxPython window and update the plot from a thread when new data 
> becomes available.

Ok, I revised my code accordingly (see attached example), and it works 
a lot better. However, if the plot is updated every time after adding a 
new data value, the GUI locks up if the rate of incoming data becomes 
too high.

I worked around this by setting a flag that is TRUE while the plot is 
being updated, and the code calls the updating of the plot only if this 
flag is FALSE (this is also in the attached example). This works ok, 
but my gut feeling is that there is a better / cleaner / more elegant 
way of avoiding the GUI lockup.

Thoughts, ideas, suggestions?

-- 
You received this message because you are subscribed to the Google Groups "wxPython-users" group.
To unsubscribe from this group and stop receiving emails from it, send an email to [email protected].
To view this discussion on the web visit https://groups.google.com/d/msgid/wxpython-users/LZ1XDQ.HRWQ1OF4INFR%40gmail.com.
wx_pyplot_minimal_example_2 (text/x-python3, 4 KB)
#!/usr/bin/env python3


# Example showing how I cannot make pyplot work to plot data in a pyplot window
# (1) Show GUI with a button to start running a measurement instrument
# (2) Once the button is pressed, run the measurement instrument:
#	- Get a new data value every second
# 	- After getting a new value, plot all data in a pyplot window
#       - The GUI locks up if the data rate is too high, and the data plotting is called too often (idle_time less than 0.1 in the measurement_thread)
#
# The problem is that the pyplot window does not seem to show up on the screen.

import time
import random
from threading import Thread
import wx
from matplotlib.backends.backend_wxagg import FigureCanvasWxAgg as FigureCanvas
from matplotlib.figure import Figure


# measurement instrument that takes measurement data and plots data in a pyplot window
class instrument:

	def __init__(self):
		self.values = []                               # container list of the measurement values
		self.plot_frame = instrument_plot_frame()      # frame for plotting measurement data recorded with the instrument
		self.plotting_in_progress = False
		
	def read_value(self):
		# get a (fake) measurement value
		print('Getting a new data value...')
		val = random.random()                          # determine a (fake) data value
		self.values.append(val)                        # append the value to list of previous values
		return val
	
	def plot_data(self):
		print('Plotting the data (' + str(len(self.values)) + ' data points)...')
		self.plotting_in_progress = True
		self.plot_frame.Show(True)
		self.plot_frame.axes.cla()                     # clear plot data
		self.plot_frame.axes.plot(self.values)         # plot all data
		self.plot_frame.canvas.draw()
		self.plot_frame.Refresh()
		self.plotting_in_progress = False

# plot window (part of the instrument class)
class instrument_plot_frame(wx.Frame):
	def __init__(self):
		wx.Frame.__init__(self, None, -1, 'Instrument data')
		self.figure = Figure()                                           # create matplotlib figure
		self.axes = self.figure.add_subplot(111)                         # create figure axes
		self.canvas = FigureCanvas(self, -1, self.figure)                # FigureCanvasWxAgg
		self.sizer = wx.BoxSizer(wx.VERTICAL)
		self.sizer.Add(self.canvas, 1, wx.LEFT | wx.TOP | wx.EXPAND)
		self.SetSizer(self.sizer)
		self.Fit()


# Thread that gets measurement data from the instrument, with logging to pyplot window
class measurement_thread(Thread):

	def __init__(self, main_window):
		Thread.__init__(self)
		self.main_window = main_window                 # for communication with the main_window
		self.idle_time = 0.01                          # idle time between instrument readings

	def run(self):
		# get data from instrument and plot the data in pyplot window
		while True:
			self.main_window.instrument.read_value()                       # get a measurement value
			if not self.main_window.instrument.plotting_in_progress:
				wx.CallAfter( self.main_window.instrument.plot_data )  # update the pyplot figure <-- *** THIS NEEDS TO BE IN wx.CallAfter TO AVOID CRASHING ***
			time.sleep(self.idle_time)                                     # wait a bit for the next measurement


# main window:
class MyFrame(wx.Frame):
  
	def __init__(self):
	
		# define GUI:
		wx.Frame.__init__(self, None, title="Instrument Control", size=(300,150))
		panel = wx.Panel(self)
		button = wx.Button(panel, label='Start measurements')
		button.Bind(wx.EVT_BUTTON, self.on_button_press)
		sizer = wx.BoxSizer(wx.VERTICAL)
		sizer.Add(button, 1, wx.ALL | wx.CENTER, 25)
		panel.SetSizer(sizer)    
		
		# measurement instrument, and thread for measurements:
		self.instrument = instrument()
		self.measurement_thread = measurement_thread(self)
		
		# show the window:
		self.Show()

	def on_button_press(self, event):
        	print('Starting the measurement thread...')
        	self.measurement_thread.start() # start the measurement thread


# main:
if __name__ == '__main__':
	app = wx.App()
	frame = MyFrame()
	app.MainLoop()
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.