From b86e35e4d2d6c89461f396654eb08ba54577bade Mon Sep 17 00:00:00 2001 From: Doron Behar Date: Thu, 18 Jul 2024 11:58:04 +0300 Subject: [PATCH 1/2] Qt embedding example: Separate drawing and data retrieval timers In the previous version of this example, if the timer interval would have been simply decreased to 1ms, the GUI could have appeared as stuck on some platforms / (slow) machines, because the event loop didn't find the time to respond to external events such as the user dragging the window etc. This change does 3 things: - Puts the data to plot in the self.{x,y}data attributes. - Separates the timer that updates the self.{x,y}data from the timer that updates the canvas - Explains much better the reasoning for the timers' intervals choices in comments, as well as explaining why the timers are attributed to self, although they are not used by other methods of the class. - Use the asynchronous draw_idle() function to further guarantee that the drawing won't be blocking. Co-authored-by: Elliott Sales de Andrade --- .../user_interfaces/embedding_in_qt_sgskip.py | 34 ++++++++++++++----- 1 file changed, 25 insertions(+), 9 deletions(-) diff --git a/galleries/examples/user_interfaces/embedding_in_qt_sgskip.py b/galleries/examples/user_interfaces/embedding_in_qt_sgskip.py index b79f582a65e4..56f076d3d338 100644 --- a/galleries/examples/user_interfaces/embedding_in_qt_sgskip.py +++ b/galleries/examples/user_interfaces/embedding_in_qt_sgskip.py @@ -44,18 +44,34 @@ def __init__(self): self._static_ax.plot(t, np.tan(t), ".") self._dynamic_ax = dynamic_canvas.figure.subplots() - t = np.linspace(0, 10, 101) # Set up a Line2D. - self._line, = self._dynamic_ax.plot(t, np.sin(t + time.time())) - self._timer = dynamic_canvas.new_timer(50) - self._timer.add_callback(self._update_canvas) - self._timer.start() + self.xdata = np.linspace(0, 10, 101) + self._update_data() + self._line, = self._dynamic_ax.plot(self.xdata, self.ydata) + # The below two timers must be attributes of self, so that the garbage + # collector won't clean them after we finish with __init__... - def _update_canvas(self): - t = np.linspace(0, 10, 101) + # The data retrieval may be fast as possible (Using QRunnable could be + # even faster). + self.data_timer = dynamic_canvas.new_timer(1) + self.data_timer.add_callback(self._update_data) + self.data_timer.start() + # Drawing at 50Hz should be fast enough for the GUI to feel smooth, and + # not too fast for the GUI to be overloaded with events that need to be + # processed while the GUI element is changed. + self.drawing_timer = dynamic_canvas.new_timer(20) + self.drawing_timer.add_callback(self._update_canvas) + self.drawing_timer.start() + + def _update_data(self): # Shift the sinusoid as a function of time. - self._line.set_data(t, np.sin(t + time.time())) - self._line.figure.canvas.draw() + self.ydata = np.sin(self.xdata + time.time()) + + def _update_canvas(self): + self._line.set_data(self.xdata, self.ydata) + # It should be safe to use the synchronous draw() method for most drawing + # frequencies, but it is safer to use draw_idle(). + self._line.figure.canvas.draw_idle() if __name__ == "__main__": From a46ecad34240487861bdf36980eb79c9adb850bd Mon Sep 17 00:00:00 2001 From: Tim Hoffmann <2836374+timhoffm@users.noreply.github.com> Date: Mon, 5 Aug 2024 11:46:08 +0200 Subject: [PATCH 2/2] Rename _update_data to _update_ydata --- .../examples/user_interfaces/embedding_in_qt_sgskip.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/galleries/examples/user_interfaces/embedding_in_qt_sgskip.py b/galleries/examples/user_interfaces/embedding_in_qt_sgskip.py index 56f076d3d338..854ae798e284 100644 --- a/galleries/examples/user_interfaces/embedding_in_qt_sgskip.py +++ b/galleries/examples/user_interfaces/embedding_in_qt_sgskip.py @@ -46,7 +46,7 @@ def __init__(self): self._dynamic_ax = dynamic_canvas.figure.subplots() # Set up a Line2D. self.xdata = np.linspace(0, 10, 101) - self._update_data() + self._update_ydata() self._line, = self._dynamic_ax.plot(self.xdata, self.ydata) # The below two timers must be attributes of self, so that the garbage # collector won't clean them after we finish with __init__... @@ -54,7 +54,7 @@ def __init__(self): # The data retrieval may be fast as possible (Using QRunnable could be # even faster). self.data_timer = dynamic_canvas.new_timer(1) - self.data_timer.add_callback(self._update_data) + self.data_timer.add_callback(self._update_ydata) self.data_timer.start() # Drawing at 50Hz should be fast enough for the GUI to feel smooth, and # not too fast for the GUI to be overloaded with events that need to be @@ -63,7 +63,7 @@ def __init__(self): self.drawing_timer.add_callback(self._update_canvas) self.drawing_timer.start() - def _update_data(self): + def _update_ydata(self): # Shift the sinusoid as a function of time. self.ydata = np.sin(self.xdata + time.time())