PyQt4 + matplotlib in a QScrollWidget

2019-02-28 15:08发布

问题:

I have matplotlib embedded in a PyQt4 app that I'm working on. The problem is when I dynamically add a subplot to the figure, the figures compress with every added subplot. I thought I could solve this by setting the figure to a QScrollArea but that doesn't work (as far as I can tell). Here's an example of what I thought would work

import os
os.environ['QT_API'] = 'pyside'

from PySide.QtGui   import *
from PySide.QtCore  import *

import matplotlib

from matplotlib.backends.backend_qt4agg import FigureCanvasQTAgg
from matplotlib.figure                  import Figure


class Canvas(FigureCanvasQTAgg):

    def __init__(self, parent=None):
        self.figure = Figure()
        super(Canvas, self).__init__(self.figure)

        ax = self.figure.add_subplot(1,1,1)
        ax.plot([1,2,3])
        self.draw()

    def add_subplot(self, data=[]):
        rows = len(self.figure.axes) + 1
        for index, axes in enumerate(self.figure.axes, start=1):
            axes.change_geometry(rows, 1, index)

        ax = self.figure.add_subplot(rows, 1, index+1)
        ax.plot(data)

        self.draw()

class Main(QWidget):

    def __init__(self, parent=None):
        super(Main, self).__init__(parent)

        self.canvas = QScrollArea(self)
        self.canvas.setWidget(Canvas(self))
        self.canvas.setWidgetResizable(True)

        for x in range(5):
            self.canvas.widget().add_subplot()

        layout = QVBoxLayout(self)
        layout.addWidget(self.canvas)


app = QApplication([])
main = Main()
main.show()
app.exec_()

Notice how all the graphs are smashed together to show then in the same visible space? I wan't have to scroll to see the other graphs. I'm not sure how to do this exactly.

Anyone know how to do this or another way of doing this?

回答1:

Two steps to sketch an idea to solve this:

Unset the resizing of the ScollArea to display scroll bars. Change the line:

self.canvas.setWidgetResizable(True)

to

self.canvas.setWidgetResizable(False)

Then when adding a subplot change the figure height, because the canvas will determine it's height by checking the size of the figure:

def add_subplot(self, data=[]):
    rows = len(self.figure.axes) + 1
    for index, axes in enumerate(self.figure.axes, start=1):
        axes.change_geometry(rows, 1, index)

    ax = self.figure.add_subplot(rows, 1, index+1)
    ax.plot(data)
    self.figure.set_figheight(self.figure.get_figheight()*1.25)
    self.draw()

In the Main you have to let PySide know, that the it has to resize the widget in the scroll area:

    for x in range(5):
        self.canvas.widget().add_subplot()
        self.canvas.widget().adjustSize()