Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Zoom In-Out not behaving as expected in QT backend example #17352

Closed
@precesseur

Description

@precesseur
The example in https://matplotlib.org/3.1.1/gallery/user_interfaces/embedding_in_qt_sgskip.html or its 3.2.1 version show two FigureCanvas each one alongside their own NavigationToolbar2Qt. Now the first (top) is purely static, i.e. it is one permanent waveform, so when you zoom in and then out everything seems normal. The second one is dynamic with a plot which is changed every one tenth of seconds (a sinusoid which then shifts along the time axis); it is then redraw so fast that there is no point or actually no way to zoom in and out with the second navigation bar.

The interesting thing happens when you want to simulate what would be the first application of the navigation bar and 'dynamic' drawing: the use of a FigureCanvas inserted in a pyQt UI for plotting diverse static results coming at a very slow pace, possibly after unblocking by the user which inspects in sequence these results.
This can simply be simulated by changing the delay od 100ms into 10s to have the time to zoom in, possibly several time, and zooming out or even hitting the 'home' button supposed to get you back to the original figure (no zoom).

Well .. that does not work! Actually the y axis is lost or scrambled between previous views.

Bug report

Bug summary

The y axis is lost or scrambled between previous views when zooming out and/or going back to original plot (of the 10 seconds interval)

Code for reproduction
In the original example just change the delay from 100ms to 10s (and be patient before the first dynamic plot), then zoom in once or twice and zoom out or hit 'home'

#
#
-----
        self._static_ax.plot(t, np.tan(t), ".")

        self._dynamic_ax = dynamic_canvas.figure.subplots()
        self._timer = dynamic_canvas.new_timer(
            10000, [(self._update_canvas, (), {})])
        self._timer.start()

    def _update_canvas(self):
-------

And here is my proposed solution. With comments after 'PLR'. But beware I am not a specialist of Matplotlib! It is just that I think the Figure, and its axis, have to be reinitialized for each new plot, so I save the only thing stable which is the Canvas, and work from top to bottom rather than in the original proposal which (apparently) go back from the grand child (axis?) to its parent (the Figure?) and to its grand parent (the Canvas). But it is how I understand it and I am not sure.
Proposed solution


import sys
import time
import numpy as np
import matplotlib
matplotlib.use("Qt5Agg", warn=True)
from matplotlib.backends.qt_compat import QtCore, QtWidgets, is_pyqt5
if is_pyqt5():
    from matplotlib.backends.backend_qt5agg import (
        FigureCanvas, NavigationToolbar2QT as NavigationToolbar)
else:
    from matplotlib.backends.backend_qt4agg import (
        FigureCanvas, NavigationToolbar2QT as NavigationToolbar)
from matplotlib.figure import Figure

class ApplicationWindow(QtWidgets.QMainWindow):
    def __init__(self):
        super().__init__()
        self._main = QtWidgets.QWidget()
        self.setCentralWidget(self._main)
        layout = QtWidgets.QVBoxLayout(self._main)

        static_canvas = FigureCanvas(Figure(figsize=(5, 3)))
        layout.addWidget(static_canvas)
        self.addToolBar(NavigationToolbar(static_canvas, self))

        
        dynamic_canvas = FigureCanvas(Figure(figsize=(5, 3)))

        #PLR: save the main object, with its figure, its subplots and axis
        self._dynamic_canvas = dynamic_canvas
     
        layout.addWidget(dynamic_canvas)
        self.addToolBar(QtCore.Qt.BottomToolBarArea,
                        NavigationToolbar(dynamic_canvas, self))

        self._static_ax = static_canvas.figure.subplots()
        t = np.linspace(0, 10, 501)
        self._static_ax.plot(t, np.tan(t), ".")
        print(matplotlib.get_backend())  
        
             
        #PLR:  ax must be reinitialized for each new figure
        #self._dynamic_ax = dynamic_canvas.figure.subplots()
        self._timer = dynamic_canvas.new_timer(
            10000, [(self._update_canvas, (), {})])
        self._timer.start()
        
        

    def _update_canvas(self):
       # self._dynamic_ax.clear()
        t = np.linspace(0, 10, 101)
       
        # PLR, clear the old figure, regenerate the axis and
        # plot and draw
        self._dynamic_canvas.figure.clear()
        ax = self._dynamic_canvas.figure.add_subplot(111)
       
        # Shift the sinusoid as a function of time.
        ax.plot(t, np.sin(t + time.time()))

        self._dynamic_canvas.draw()


if __name__ == "__main__":
    qapp = QtWidgets.QApplication(sys.argv)
    app = ApplicationWindow()
    print(matplotlib.get_backend())
    app.show()
    qapp.exec_()

... But here at least it works as expected when zooming in and out or home.

Matplotlib version

  • Operating system: Kubuntu 18.04
  • Matplotlib version: 3.1.1 or 3.2.1
  • Matplotlib backend (print(matplotlib.get_backend())):Qt5Agg
  • Python version: 3.6.9 from the OS
  • Jupyter version (if applicable):
  • Other libraries: numpy

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions