2022-01-28

PySide6: QProgressDialog と QThread

PySide (Qt for Python) は、Qt(キュート)の Python バインディングで、GUI などを構築するためのクロスプラットフォームなライブラリです。配布ライセンスは LGPL で公開されています。最新のバージョンは Qt6 に対応した PySide6(記事執筆時点で 6.2.2.1)です。

大きなファイルを読み込むときや、時間がかかる処理をしている時、GUI が反応しなくなります。自分用にちゃちゃっと作る GUI プログラムではそれでも我慢できますが、他の人も利用するようになったら、ちとダサいです。プログラムが固まると苦情を受けること必至です。ひと手間かけてスレッドで処理を分ければいいのですが、結局のところ、処理が終わるまでは他の処理をできなくして、進捗だけ表示させておけば良い場合が多いです。

そんなとき、QProgressDialog を利用すれば、進捗ダイアログで表示させ modal にしておけば、進捗を表示する上、親ウィンドウの操作ができなくなって便利です。

本記事では、下記の OS 環境を使用しています。

Fedora Linux 35 Workstation x86_64

まずは、ボタンをクリックしてダミーのタスクを実行し、その進捗を表示させるサンプルです。

qt_progressdialog.py の実行例
qt_progressdialog.py
#!/usr/bin/env python
# coding: utf-8
import sys
import time
from PySide6.QtWidgets import (
QApplication,
QMainWindow,
QProgressDialog,
QPushButton,
)
from PySide6.QtCore import (
QThread,
Signal, Qt,
)
class Example(QMainWindow):
def __init__(self, parent=None):
super().__init__(parent)
self.init_ui()
self.setWindowTitle('ProgressDialog & Thread')
self.resize(300, 100)
def init_ui(self):
but = QPushButton('START')
but.clicked.connect(self.task_start)
self.setCentralWidget(but)
def task_start(self):
button = self.sender()
button.setEnabled(False)
progress = QProgressDialog(labelText='Working...', parent=self)
progress.setWindowModality(Qt.WindowModal)
progress.setCancelButton(None)
progress.setWindowTitle('status')
progress.show()
task = TaskThread(self)
task.progressChanged.connect(progress.setValue)
task.start()
task.progressCompleted.connect(lambda: self.task_end(button, progress))
def task_end(self, button, progress):
button.setEnabled(True)
progress.cancel()
class TaskThread(QThread):
progressChanged = Signal(int)
progressCompleted = Signal()
def run(self):
for progress in range(0, 101):
time.sleep(0.1)
self.progressChanged.emit(progress)
time.sleep(0.5)
self.progressCompleted.emit()
self.progressChanged.emit(0)
self.exit(0)
def main():
app = QApplication(sys.argv)
ex = Example()
ex.show()
sys.exit(app.exec())
if __name__ == '__main__':
main()

一方、進捗を測る数字を取れない場合もあります。そんなときは、QProgressDialog の setRange メソッドで (0, 0) としておけば、進捗に関係なく進捗バーが動いているようになります。

qt_progressdialog_2.py の実行例
qt_progressdialog_2.py
#!/usr/bin/env python
# coding: utf-8
import sys
import time
from PySide6.QtWidgets import (
QApplication,
QMainWindow,
QProgressDialog,
QPushButton,
)
from PySide6.QtCore import (
QThread,
Signal, Qt,
)
class Example(QMainWindow):
def __init__(self, parent=None):
super().__init__(parent)
self.init_ui()
self.setWindowTitle('ProgressDialog & Thread (2)')
self.resize(300, 100)
def init_ui(self):
but = QPushButton('START')
but.clicked.connect(self.task_start)
self.setCentralWidget(but)
def task_start(self, button):
button = self.sender()
button.setEnabled(False)
progress = QProgressDialog(labelText='Working...', parent=self)
progress.setWindowModality(Qt.WindowModal)
progress.setCancelButton(None)
progress.setRange(0, 0)
progress.setWindowTitle('status')
progress.show()
task = TaskThread(self)
task.start()
task.progressCompleted.connect(lambda: self.task_end(button, progress))
def task_end(self, button, progress):
button.setEnabled(True)
progress.cancel()
class TaskThread(QThread):
progressCompleted = Signal()
def run(self):
for progress in range(0, 101):
time.sleep(0.1)
time.sleep(0.5)
self.progressCompleted.emit()
self.exit(0)
def main():
app = QApplication(sys.argv)
ex = Example()
ex.show()
sys.exit(app.exec())
if __name__ == '__main__':
main()

QProgressDialog のダイアログ画面では、デフォルトでキャンセルボタンが表示されます。今回は良いサンプルを用意できなかったので、表示しないようにしてしまいましたが、サンプルを工夫できれば、あらためて紹介します。

参考サイト

  1. QProgressDialog — Qt for Python
  2. QThread — Qt for Python

 

ブログランキング・にほんブログ村へ bitWalk's - にほんブログ村 にほんブログ村 IT技術ブログ Linuxへ
にほんブログ村

0 件のコメント: