2

編集:

私の問題の実例を含めるためにこれを書き直すことにしました。これはかなり長いですが、将来多くの人にとって役立つことを願っています.

import sys

from matplotlib.backends.backend_qt4agg import FigureCanvasQTAgg as FigureCanvas
from matplotlib.figure import Figure
from PyQt4.QtGui import *
from PyQt4.QtCore import *

class MainWindow(QMainWindow):
    def __init__(self):
        super(MainWindow, self).__init__()

        self.setGeometry(100, 100, 640, 480)
        showButton = QPushButton('Show')

        toolbarShowButton = self.addToolBar('Show button toolbar')

        toolbarShowButton.addWidget(showButton)

        self.connect(showButton, SIGNAL('clicked()'), self.showButtonClicked)
        self.graphLabel = GraphCanvas(self);
        self.setCentralWidget(self.graphLabel)

    def showButtonClicked(self):  
        self.graphLabel.drawGraph()

    def resizeEvent(self, event):
        try:
            self.graphLabel.setFig()
        except AttributeError:
            pass

class GraphCanvas(FigureCanvas):

    def __init__(self, parent=None, width=5, height=4, dpi=100):

        self.fig = Figure(figsize=(width, height), dpi=dpi)

        self.axes = self.fig.add_subplot(111)

        FigureCanvas.__init__(self, self.fig)
        self.setParent(parent)

        FigureCanvas.setSizePolicy(self,
                                   QSizePolicy.Expanding,
                                   QSizePolicy.Expanding)
        FigureCanvas.updateGeometry(self)

        self.background = None

    def drawGraph(self):
        self.axes.cla()
        self.someplot = self.axes.plot(range(1,5), range(1,5))
        self.redVert, = self.axes.plot(None, None, 'r--')
        self.greenVert, = self.axes.plot(None, None, 'g--')
        self.yellowVert, = self.axes.plot(None, None, 'y--')

        self.verticalLines = (self.redVert, self.greenVert, self.yellowVert)

        self.fig.canvas.mpl_connect('motion_notify_event', self.onMove)

        self.draw()
        self.background = self.fig.canvas.copy_from_bbox(self.axes.bbox)

    def onMove(self, event):

        # cursor moves on the canvas
        if event.inaxes:

            # restore the clean background
            self.fig.canvas.restore_region(self.background)
            ymin, ymax = self.axes.get_ylim()
            x = event.xdata - 1

            # draw each vertical line
            for line in self.verticalLines:
                line.set_xdata((x,))
                line.set_ydata((ymin, ymax))
                self.axes.draw_artist(line)
                x += 1

            self.fig.canvas.blit(self.axes.bbox)

    def setFig(self):
        '''
        Draws the canvas again after the main window
        has been resized.
        '''

        try:
            # hide all vertical lines
            for line in self.verticalLines:
                line.set_visible(False)

        except AttributeError:
            pass

        else:
            # draw canvas again and capture the background
            self.draw()
            self.background = self.fig.canvas.copy_from_bbox(self.axes.bbox)

            # set all vertical lines visible again
            for line in self.verticalLines:
                line.set_visible(True)

def main():
    app = QApplication(sys.argv)
    mainWindow = MainWindow()
    mainWindow.show()
    sys.exit(app.exec_())

if __name__ == '__main__': main()

コードの説明

QMainWindow「表示」ボタンのあるツールバーを備えた基本があります。メイン ウィンドウは、Figure のキャンバスも作成しmatplotlib、中央のウィジェットに設定します。

ユーザーが「表示」ボタンを押すと、 のdrawGraph()メソッドを呼び出すことによっていくつかのデータが表示されGraphCanvasます。実際のプログラムでは、ユーザーがボタンをクリックする前に表示するように選択した内容に応じてデータが変化します。このメソッドresizeEvent()は基本的に、新しい中央ウィジェットのサイズに対応するために Figure を再度描画します。

このdrawGraph()メソッドは 4 つのプロットを作成し、最初のプロットにはデータがあるため、表示されます。最後の 2 行は Figure を描画し、静的な背景を変数 に保存しますself.background

ユーザーがキャンバス上でマウスを動かすと、最初に背景が読み込まれます。フィギュアの描画を高速化するために、静的な背景を保存してロードしたかったのです。その後、最後の 3 つのプロットはデータを動的に取得し、マウス カーソルで移動する 3 つの垂直線として表示されます。

問題

1)「表示」ボタンを押し続けると、図が徐々に遅くなります。50回くらい叩いてマウスを動かしてみると、縦の線がずいぶん遅れているのがわかります。実際の図にもっと動的なプロットや注釈などが含まれている場合、数回クリックしただけでプログラムが使用できなくなります。

賢明な人なら、なぜこの速度低下が起こっているのかわかるかもしれませんが、以前に読み込まれた図はメモリのどこかに保持されており、新しく作成された図の下に描画される可能性があると思います。そして、スタックはますます大きくなり続けています。

2)プログラム起動直後の図が表示されます。大したことではありませんが、ボタンがクリックされるまで空白の領域があればいいのにと思います。

試した解決策

私が試したのは、これらの 2 行をdef __init__(self)ofclass MainWindowからに移動したことdef showButtonClicked(self)です。

self.graphLabel = GraphCanvas(self);
self.setCentralWidget(self.graphLabel)

したがって、次のようになります。

def showButtonClicked(self):  
    self.graphLabel = GraphCanvas(self);
    self.setCentralWidget(self.graphLabel)
    self.graphLabel.drawGraph()

そのため、ボタンが押された後にのみ図を作成しました。これで速度低下の問題は解決しますが、別の問題が生じます。「表示」ボタンを押してキャンバス上でマウスを動かすと、保存された Figure の背景が読み込まれますが、元のサイズは 5 x 4 インチで、ごちゃごちゃしています。なので、背景は基本的にフィギュアを描いた時のサイズではなく、作った時のサイズで保存しました。

ただし、ウィンドウのサイズを変更すると、すべてがうまく機能します。しかし、次に「表示」ボタンをクリックすると、問題が再発し、ウィンドウのサイズを再度変更する必要があります。

必要なもの

これを流動的に動作させ、「表示」ボタンが何回クリックされても正常に見えるようにする必要があります。また、「表示」ボタンが初めてクリックされるまで図が表示されず、その時点からプログラムが閉じられるまで表示されることをお勧めします。

「表示」ボタンがクリックされたときにウィンドウのサイズを1ピクセル変更するなど、いくつかのハックが私に来ましたが、それは正しいアプローチではありませんか?

どんなアイデアや提案も大歓迎です。ありがとうございました。

4

1 に答える 1

1

より良い解決策が見つかるまで、問題の適切な解決策を見つけました。

私が試した解決策が混乱を引き起こした理由は、 のインスタンスGraphCanvasが作成されて として設定されるとQCentralWidget、がインスタンスのサイズ (この場合は 500*400) にQCentralWidget縮小され、そのサイズの が保存されるためです。ただし、図自体は使用可能なスペース全体を使用します。GraphCanvasBbox

を作成してGraphCanvasを として設定するとQCentralWidget、ウィジェットは、GraphCanvas作成されたメソッド (およびそのすべての親) の実行が完了するまで、インスタンスのサイズを使用します。その後、両者が並びます。

メソッドでキャンバスを作成する場合、メソッドではとのサイズが一致し、適切なサイズが使用される__init__ため、混乱は発生しません。ただし、 で作成すると、メソッドが終了するまでは「間違った」サイズになります。drawGraphQCentralWidgetGraphCanvasbboxshowButtonClickbbox

QCentralWidgetまた、 の__init__メソッドでを作成するとQMainWindow、 のサイズが で設定したウィンドウ全体のサイズに一致しますself.setGeometry()。メソッドが終了QCentralWidgetすると、ウィンドウに収まるように のサイズが計算され、通常は小さくなります。

この問題を解決するためQWidgetに、__init__メソッドにダミーを作成し、それを として追加することにしましQCentralWidgetた。次に、showButtonClickedメソッドで の幅と高さを取得し、保存した幅と高さを使用してQCentralWidgetを作成します。GraphCanvasそうすれば、最初からサイズが一致します。

したがって、関連するコードは次のとおりです。

class MainWindow(QMainWindow):
    def __init__(self):
        super(MainWindow, self).__init__()

        self.setGeometry(100, 100, 640, 480)
        showButton = QPushButton('Show')

        toolbarShowButton = self.addToolBar('Show button toolbar')

        toolbarShowButton.addWidget(showButton)
        self.connect(showButton, SIGNAL('clicked()'), self.showButtonClicked)

        # dummy QWidget
        tempWidget = QWidget()
        self.setCentralWidget(tempWidget)

    def showButtonClicked(self):

        width = self.centralWidget().width()
        height = self.centralWidget().height()

        # convert to float (python 2) to prevent
        # flooring in the following divisions
        dpi = float(100)

        # create the canvas and replace the central widget
        self.graphLabel = GraphCanvas(self, width=width/dpi, height=height/dpi, dpi=dpi);
        self.setCentralWidget(self.graphLabel)

        self.graphLabel.drawGraph()
于 2013-08-02T07:41:43.200 に答える