Using Python 3.8, Matplotlib 3.3.2, embedding a figure canvas in a GTK3 GUI.
Problem Statement:
I have a program with a GTK3 GUI, and hundreds of variables in a dropdown menu, each with x/y data to plot. I'm attempting to add a NavigationToolbar below my figure so the user can zoom in and out. This works on the initial figure, but when the user selects a different variable, and the line's data is updated with axis.lines[0].set_data(x,y)
, the Reset Original View button on the toolbar resets to the bounds of the first data set, instead of the new data set (assuming the user zoomed in on the first data set).
Is there some way to tell a NavigationToolbar that the plotted data in a figure has changed, so resetting the view works properly?
Sample Code:
import gi
gi.require_version('Gtk', '3.0')
from gi.repository import Gtk
from matplotlib.backends.backend_gtk3agg import FigureCanvasGTK3Agg as FigureCanvas
from matplotlib.backends.backend_gtk3 import NavigationToolbar2GTK3 as NavigationToolbar
import matplotlib.pyplot as plt
import numpy as np
class PlotDemo(Gtk.Window):
def __init__(self):
Gtk.Window.__init__(self, title="Navigation Demo")
self.set_border_width(4)
self.set_default_size(850, 650)
self.box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL)
self.add(self.box)
buttonBox = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL)
self.box.add(buttonBox)
sineButton = Gtk.Button(label='SIN')
sineButton.connect('clicked', self.plotSin)
buttonBox.add(sineButton)
cosineButton = Gtk.Button(label='COS')
cosineButton.connect('clicked', self.plotCos)
buttonBox.add(cosineButton)
self.figure, self.axis = plt.subplots()
self.time = np.arange(0.0, 3.0, 0.01)
self.sin = 100*np.sin(2*np.pi*self.time)
self.cos = np.cos(2*np.pi*self.time)
self.axis.plot(self.time, self.sin)
self.axis.set_xlim(0, 3)
self.canvas = FigureCanvas(self.figure)
self.canvas.set_size_request(800, 600)
self.canvas.draw()
self.navigation = NavigationToolbar(self.canvas, self)
self.box.add(self.canvas)
self.box.add(self.navigation)
self.show_all()
def plotSin(self, _unused_widget):
self.update(self.time, self.sin)
def plotCos(self, _unused_widget):
self.update(self.time, self.cos)
def update(self, x, y):
self.axis.lines[0].set_data(x, y)
self.autoScaleY()
def autoScaleY(self):
self.axis.relim()
self.axis.autoscale(axis='y')
self.canvas.draw()
win = PlotDemo()
win.connect("destroy", Gtk.main_quit)
win.show_all()
Gtk.main()
Steps to Reproduce Issue:
1. Run script. Plotted SIN data should be visible.
2. Click "Zoom to rectangle" button on toolbar
3. Zoom in on the SIN plot
4. Click "Reset original view" button on toolbar
5. Click "COS" button at top
6. Initial view should be correct, since I auto-scale the Y-Axis
7. Zoom (optional), and then click "Reset original view" button again
Problem: Step 7 zooms back out to the original Y-Axis limits [-100, 100]
Desired Behavior: Reset view to appropriate limits for the new data set [-1, 1]
I guess I was a bit too quick to post. With a little more digging, I found a solution.
NavigationToolbar2GTK3
inherits from NavigationToolbar2
, which has an update()
method that clears the navigation stack, which is what the home()
method refers to.
In the sample code, changing my update() method to this solved it:
def update(self, x, y):
self.axis.lines[0].set_data(x, y)
self.autoScaleY()
self.navigation.update()