※当サイトではアフィリエイト広告を利用しています。

GUI開発

【PyQt】ウィンドウ上に表示させたグラフを保存する方法

こんにちは、タナカです。

この記事では、PyQtでウィンドウ上に表示させたグラフを保存する方法を説明します。

以前、【PyQt】四角で囲った範囲の画像の輝度値を確認するという記事でヒストグラムを作成しました。そのような場合、ヒストグラムを作成するのはいいけど、グラフを画像として保存できた方が汎用性があったりします。

そこで、簡単なグラフを描画し保存ボタン押すことで、簡単にグラフを保存できるようにしたいと思います。

この記事の内容
  • ウィンドウ上のグラフの保存方法がわかる
  • 保存ボタンの実装方法がわかる

1. グラフを保存するGUIの確認

まず初めにアウトプットの確認をします。グラフを描画するウィンドウを作成し、その下にplotボタンとsaveボタンを設置しています。

plotボタンを押すとランダムな数字が適当にプロットされ、そのプロットされたグラフをsaveボタンを押すことで保存する場合を考えます。

graph-output-image

2. グラフを保存するためのウィジェットを実装する

GUIはウィジェットと呼ばれるもので構成されています。

ウィジェットとはグラフを設置するウィンドウやボタンのことを言います。それぞれのウィジェットの位置関係を図で示すとこのようになります。

グラフを描画するウィンドウとボタンを配置するレイアウトが垂直に配置されるようにしています。

具体的にはQVBoxLayoutと呼ばれるクラスを用いており、QVのVはVerticalを意味しています。背景の青色で示したものがQVBoxLayoutになります。

class GraphWindow(QWidget):
    def __init__(self):
        super(GraphWindow, self).__init__()
        # layout
        self.graph_window_layout = QVBoxLayout()

        # set layout
        self.setLayout(self.graph_window_layout)

        # graph_window
        self.fig = plt.Figure()
        self.fig_canvas = FigureCanvas(self.fig)
        self.graph_window_layout.addWidget(self.fig_canvas, 5)
        self.axes = self.fig.add_subplot(111)

        # button layout
        self.button_layout = QHBoxLayout()
        self.graph_window_layout.addLayout(self.button_layout, 1)

 

self.fig_canvasがグラフを描画するためのウィンドウになります。それをaddWidgetで先ほどのレイアウトに設置しています。

さらにボタンを設置するためのレイアウトを作り、そのレイアウト自体をQVBoxLayoutに追加しています。

このボタンを設置するレイアウトはQHBoxLayoutクラスを使っており、水平方向にウィジェットを設置できるものになります。背景のオレンジで示したものがQHBoxLayoutになります。

# graph random plot button
self.plot_button = QPushButton('plot')
self.button_layout.addWidget(self.plot_button, alignment = QtCore.Qt.AlignRight)

# graph save button
self.save_button = QPushButton('save')
self.button_layout.addWidget(self.save_button, alignment = QtCore.Qt.AlignLeft)

 

ボタンを設置するためのレイアウトにそれぞれ、プロットボタンと保存ボタンを追加することでアウトプットの確認で示したウィンドウを作成することができます。

3. 保存ボタンに機能を追加する

今のままでは、プロットボタンも保存ボタンもただのボタンです。押しても何も反応しません。

ボタンが押されたときに、関数が実行されるようにする必要があります。関数とはボタンが押されたと同時に実行される機能をまとめたものです。

実際にボタンが押されたときに実行される関数はこちらになります。

def saveGraph(self):
    file_name = QFileDialog.getSaveFileName(self, 'save')[0]
    if len(file_name) == 0:
        return
    file_name = str(pathlib.Path(file_name).with_suffix(".png"))
    self.fig.savefig(file_name)

def randomPlot(self):
    self.axes.clear()
    data = [random.random()*10 for i in range(10)]
    self.axes.plot(data)
    self.fig.canvas.draw()

 

グラフを保存する関数とランダムにプロットする関数となります。

この作成した関数とボタンを紐づけるコードがこちらになります。このコードはコンストラクタ内に記述するものです。

self.plot_button.clicked.connect(self.randomPlot)
self.save_button.clicked.connect(self.saveGraph)

 

普段関数を使う場合は、self.randomPlot()というように「()」を記述するのですが、機能として紐づける際は、「()」がつきませんのでご注意ください。

実行した結果がこちらになります。

save-demo

まとめ

PyQtで作成したグラフを保存する方法を紹介しました。

グラフを描画して、保存するまでがセットで実装できると汎用性がありそうですね。

最後に今回紹介したコードを載せておきます。

from PyQt5.QtWidgets import QApplication, QMainWindow, QWidget, QFileDialog, QPushButton, QVBoxLayout, QHBoxLayout
from PyQt5 import QtGui
from PyQt5 import QtCore

import matplotlib as mpl
mpl.use("Qt5Agg")
from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas
import matplotlib.pyplot as plt


import sys
import os
import numpy as np
import cv2
import pathlib
import random

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

        # 初期値
        self.setGeometry(0, 0, 700, 700)

        self.setCentralWidget(GraphWindow())

class GraphWindow(QWidget):
    def __init__(self):
        super(GraphWindow, self).__init__()
        # layout
        self.graph_window_layout = QVBoxLayout()

        # set layout
        self.setLayout(self.graph_window_layout)

        # graph_window
        self.fig = plt.Figure()
        self.fig_canvas = FigureCanvas(self.fig)
        self.graph_window_layout.addWidget(self.fig_canvas, 5)
        self.axes = self.fig.add_subplot(111)

        # button layout
        self.button_layout = QHBoxLayout()
        self.graph_window_layout.addLayout(self.button_layout, 1)

        # graph random plot button
        self.plot_button = QPushButton('plot')
        self.button_layout.addWidget(self.plot_button, alignment = QtCore.Qt.AlignRight)

        # graph save button
        self.save_button = QPushButton('save')
        self.button_layout.addWidget(self.save_button, alignment = QtCore.Qt.AlignLeft)

        self.plot_button.clicked.connect(self.randomPlot)
        self.save_button.clicked.connect(self.saveGraph)


    def saveGraph(self):
        file_name = QFileDialog.getSaveFileName(self, 'save')[0]
        if len(file_name) == 0:
            return
        file_name = str(pathlib.Path(file_name).with_suffix(".png"))
        self.fig.savefig(file_name)

    def randomPlot(self):
        self.axes.clear()
        data = [random.random()*10 for i in range(10)]
        self.axes.plot(data)
        self.fig.canvas.draw()

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

main()