PyQtで動画表示するサンプルプログラム

# Pythonで動画を表示するGUIを作ってみた
PyQt5とOpenCV for Pythonを使って、昔作ったプログラムコードを発掘したので、備忘録として載せておきます。

```python

"""
@author : koharite

機能：
動画ファイルを表示する。
正解枠情報を指定すると、フレームに重畳して表示する。
"""

import sys
import cv2

from PyQt5.QtWidgets import (QMainWindow, QApplication, QWidget, QHBoxLayout, QLabel, QPushButton, QAction, QStyle, QFileDialog)
from PyQt5.QtGui import(QIcon, QImage, QPixmap, QPainter, QColor)
from PyQt5.QtCore import (pyqtSlot, QTimer, Qt)


# メインウィンドウの構成
class MainWindow(QMainWindow):

    # メインウィンドウの初期値を設定
    def __init__(self):
        # ウィンドウのステータス
        super().__init__()
        self.title = 'Movie Viewer for PDET'
        self.left = 10
        self.top = 10
        self.width = 1280
        self.height = 960

        # 動画のステータス
        self.framePos = 0
        #self.image = QImage()
        self.image = None
        self.moviePlayFlg = False
        self.imgWidth = 0
        self.imgHeight = 0
        self.frameRate = 0

        self.initUI()

    # 初期化処理をまとめておく
    def initUI(self):
        # メインウィンドウのタイトルを設定する
        self.setWindowTitle(self.title)
        # メインウィンドウの初期位置
        self.setGeometry(self.left, self.top, self.width, self.height)
        hbox = QHBoxLayout(self)

        # メインウィンドウのメニューを設定する
        mainMenu = self.menuBar()
        fileMenu = mainMenu.addMenu('File')
        editMenu = mainMenu.addMenu('Edit')
        optionMenu = mainMenu.addMenu('Option')
        helpMenu = mainMenu.addMenu('Help')

        # ファイルを開くメニューを設定する
        fileOpenButton = QAction(self.style().standardIcon(getattr(QStyle, 'SP_FileDialogStart')), 'File Open', self)
        fileOpenButton.setShortcut('Ctrl+O')
        fileOpenButton.triggered.connect(self.openFileDialog)
        fileMenu.addAction(fileOpenButton)

        # アプリの終了メニューを設定する
        exitButton = QAction(self.style().standardIcon(getattr(QStyle, 'SP_DialogCloseButton')), 'Exit', self)
        exitButton.setShortcut('Ctrl+Q')
        exitButton.setStatusTip('Exit application')
        exitButton.triggered.connect(self.close)
        fileMenu.addAction(exitButton)

        # メインウィンドウに部品を配置する
        # 画像描画領域を設定する
        #self.showFrameLabel = QLabel(self)
        #self.painter = QPainter()
        #self.showFramePixmap = QPixmap()
        #self.painter.drawPixmap(0,0, self.imgWidth, self.imgHeight, self.showFramePixmap)
        #self.showFrameLabel.setPixmap(self.showFramePixmap)
        #self.resize(self.showFramePixmap.width(), self.showFramePixmap.height())

        #hbox.addWidget(self.showFrameLabel)
        self.setLayout(hbox)

        # 動画の再生ボタンを設定する
        moviePlayBtn = QPushButton(self.style().standardIcon(getattr(QStyle, 'SP_MediaPlay')), 'Play', self)
        moviePlayBtn.move(20, self.height-50)
        moviePlayBtn.clicked.connect(self.moviePlay)

        # 動画の停止ボタンを設定する
        movieStopBtn = QPushButton(self.style().standardIcon(getattr(QStyle, 'SP_MediaStop')), 'Stop', self)
        movieStopBtn.move(120, self.height-50)
        movieStopBtn.clicked.connect(self.movieStop)

        # 描画更新用タイマー
        #self.updateTimer = QTimer(self)
        #self.updateTimer.timeout.connect(self.showNextFrame)
        ##self.updateTimer.start(self)

        # メインウィンドウの表示
        self.show()

    def openFileDialog(self):
        options = QFileDialog.Options()
        #options |= QFileDialog.DontUseNativeDialog
        inputFileName, _ = QFileDialog.getOpenFileName(self, 'Open File', '', 'Movie files(*.avi *.wmv)', options=options)
        # デバッグ用にステータスバーにファイル名を表示する
        #self.statusBar().showMessage(inputFileName[0])
        self.statusBar().showMessage(inputFileName)
        print(inputFileName)

        # OpenCVで動画を読み込む
        self.video = cv2.VideoCapture(inputFileName)
        # デバッグ
        print('OpenCV movie read success.')
        # フレーム数を取得
        self.frameNum = self.video.get(cv2.CAP_PROP_FRAME_COUNT)
        # デバッグ
        print('movie frameNum: ', str(self.frameNum))
        # フレームレートを取得
        self.frameRate = self.video.get(cv2.CAP_PROP_FPS)
        # デバッグ
        print('movie frameRate: ', str(self.frameRate))

        # 最初のフレームを表示する
        self.framePos = 0
        self.video.set(cv2.CAP_PROP_POS_FRAMES, self.framePos)
        ret, frame = self.video.read()
        # デバッグ
        print('openCV current frame read')
        # 再生フレームをOpenCV形式からPyQtのQImageに変換する
        self.image = self.openCV2Qimage(frame)
        # デバッグ
        print('convert openCV to QImage')
        self.imgWidth = self.image.width()
        self.imgHeith = self.image.height()
        # 表示
        # デバッグ
        print('movie properties read success')
        #self.painter.drawPixmap(0, 0, self.imgWidth, self.imgHeight, self.image)

    def moviePlay(self):
        self.moviePlayFlg = True
        self.updateTimer.start((1/self.frameRate) * 1000)

    def movieStop(self):
        self.moviePlayFlg = False
        self.updateTimer.stop()

    def showNextFrame(self):
        if self.moviePlayFlg == False:
            return

        self.framePos += 1
        # OpenCVで動画の再生フレーム位置を設定する
        self.video.set(cv2.CAP_PROP_POS_FRAMES, self.framePos)
        ret, frame = self.video.read()
        # 再生フレームをOpenCV形式からPyQtのQImageに変換する
        self.image = self.openCV2Qimage(frame)
        #self.imgWidth = self.image.width()
        #self.imgHeith  self.image.height()

    def paintEvent(self, event):
        # デバッグ用
        print('paintEvent Start')
        painter = QPainter()
        painter.begin(self)
        painter.setPen(QColor('#FFFFFF'))
        painter.setBrush(Qt.white)
        painter.drawRect(event.rect())
        # デバッグ用
        print('painter tool set')

        if self.image == None:
            return

        #image = QtGui.QImage('./sample01.jpg')
        #x = (self.width() - self.image.width()) / 2
        #y = (self.height() - self.image.height()) / 2

        #painter.drawImage(x, y, image)
        painter.drawPixmap(0, 0, self.ImgWidth, self.imgHeight, self.image)
        painter.end()



    def openCV2Qimage(self, cvImage):
        height, width, channel = cvImage.shape
        bytesPerLine = channel * width
        cvImageRGB = cv2.cvtColor(cvImage, cv2.COLOR_BGR2RGB)
        image = QImage(cvImageRGB, width, height, bytesPerLine, QImage.Format_RGB888)
        #image = QImage(cvImage, width, height, bytesPerLine, QImage.Format_RGB888)
        #image = image.rgbSwapped()

        return image



# アプリの実行
if __name__ == '__main__':
    app = QApplication(sys.argv)
    ex = MainWindow()
    sys.exit(app.exec_())


```