2

ファイルからデータを抽出するための GUI を開発しました。このプロセスには長い時間がかかります。プログラムが (subprocess.Popen を使用して) 外部ファイルを処理している間、メイン GUI は (予想どおり) 反応しません。プログラムが動作していることをユーザーに知らせるために、プログレスバーが振動するメッセージをユーザーに追加して、そこにハングアップしたかったのです。プログレス バーを QDialog として実装し、長いプロセスを呼び出す前にそれを呼び出しました。[dialog.exec_()] メソッドを使用してプログレス バーを呼び出すと、プログラムはダイアログの結果である応答を待ちます。ダイアログを閉じると、表示したい進行状況メッセージなしでプログラムが続行されます。[dialog.show()] メソッドを使用すると、プログレス バーがあるはずの空白のウィンドウしか表示されません。
これについて複数のバリエーションを試しましたが、成功しませんでした。

私は使っている:

Python 2.7.2
PyQt4: 4.9.1
Windows 7

問題を示すサンプル コードを次に示します。dialog.exec() と dialog.show() を切り替えて、問題のデモを行います。


import sys
import time
from PyQt4 import QtCore, QtGui
from PyQt4.QtCore import pyqtSignature

try:
    _fromUtf8 = QtCore.QString.fromUtf8
except AttributeError:
    _fromUtf8 = lambda s: s

class LongProcess(QtGui.QMainWindow):
    def __init__(self, parent=None):
        super(LongProcess, self).__init__(parent)
        self.setup(self)

    def setup(self, MainWindow):
        MainWindow.setObjectName(_fromUtf8("MainWindow"))
        MainWindow.resize(255, 208)
        MainWindow.setMinimumSize(QtCore.QSize(300, 208))
        MainWindow.setMaximumSize(QtCore.QSize(300, 208))
        self.centralwidget = QtGui.QWidget(MainWindow)
        self.centralwidget.setObjectName(_fromUtf8("centralwidget"))
        self.verticalLayout = QtGui.QVBoxLayout(self.centralwidget)
        self.verticalLayout.setObjectName(_fromUtf8("verticalLayout"))
        self.textEdit = QtGui.QTextEdit(self.centralwidget)
        self.textEdit.setObjectName(_fromUtf8("textEdit"))
        self.verticalLayout.addWidget(self.textEdit)
        self.pushButton = QtGui.QPushButton(self.centralwidget)
        self.pushButton.setObjectName(_fromUtf8("pushButton"))
        self.verticalLayout.addWidget(self.pushButton)
        MainWindow.setCentralWidget(self.centralwidget)

        MainWindow.setWindowTitle(QtGui.QApplication.translate("MainWindow", "MainWindow", None, QtGui.QApplication.UnicodeUTF8))
        self.textEdit.setHtml(QtGui.QApplication.translate("MainWindow", "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.0//EN\" \"http://www.w3.org/TR/REC-html40/strict.dtd\">\n"
                                                           "<html><head><meta name=\"qrichtext\" content=\"1\" /><style type=\"text/css\">\n"
                                                           "p, li { white-space: pre-wrap; }\n"
                                                           "</style></head><body style=\" font-family:\'MS Shell Dlg 2\'; font-size:8.25pt; font-weight:400; font-style:normal;\">\n"
                                                           "<p style=\" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;\"><span style=\" font-size:8pt;\">We want to run a long process in the background (calling a subprocess) and use an oscillating progress bar to show that the process is running. </span></p>\n"
                                                           "<p style=\"-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-size:8pt;\"><br /></p>\n"
                                                           "<p style=\" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;\"><span style=\" font-size:8pt;\">When long process ends, we will close the oscillating progress bar. </span></p></body></html>", None, QtGui.QApplication.UnicodeUTF8))
        self.pushButton.setText(QtGui.QApplication.translate("MainWindow", "Run Long Process", None, QtGui.QApplication.UnicodeUTF8))
        QtCore.QMetaObject.connectSlotsByName(MainWindow)

    @pyqtSignature("")
    def on_pushButton_clicked(self):
        '''
            Simulate some long process to be performed.
            Before Long Process starts, show an oscillating progress bar to
            indiate process is running in background. 
        '''
        dialog = QtGui.QDialog()    
        progressBar = Ui_porcessProgress()
        progressBar.setupUi(dialog)
        dialog.show()
#        dialog.exec_()

        start = time.time()
        diff = 0
        while diff < 10:
            end = time.time()
            diff = end - start
            print (diff)

class Ui_porcessProgress(object):
    def setupUi(self, porcessProgress):
        porcessProgress.setObjectName(_fromUtf8("porcessProgress"))
        porcessProgress.setWindowModality(QtCore.Qt.ApplicationModal)
        porcessProgress.resize(329, 81)
        porcessProgress.setMinimumSize(QtCore.QSize(329, 81))
        porcessProgress.setMaximumSize(QtCore.QSize(329, 81))
        icon = QtGui.QIcon()
        icon.addPixmap(QtGui.QPixmap(_fromUtf8(":/WFT/wftlogo2.png")), QtGui.QIcon.Normal, QtGui.QIcon.Off)
        porcessProgress.setWindowIcon(icon)
        porcessProgress.setModal(True)
        self.verticalLayout = QtGui.QVBoxLayout(porcessProgress)
        self.verticalLayout.setObjectName(_fromUtf8("verticalLayout"))
        self.label = QtGui.QLabel(porcessProgress)
        self.label.setObjectName(_fromUtf8("label"))
        self.verticalLayout.addWidget(self.label)
        self.porcessProgressBar = QtGui.QProgressBar(porcessProgress)
        self.porcessProgressBar.setMaximum(0)
        self.porcessProgressBar.setProperty("value", 10)
        self.porcessProgressBar.setTextVisible(False)
        self.porcessProgressBar.setObjectName(_fromUtf8("porcessProgressBar"))
        self.verticalLayout.addWidget(self.porcessProgressBar)
        porcessProgress.setWindowTitle(QtGui.QApplication.translate("porcessProgress", "Please wait...", None, QtGui.QApplication.UnicodeUTF8))
        self.label.setText(QtGui.QApplication.translate("porcessProgress", "Time History data extraction from ODB file in progress...", None, QtGui.QApplication.UnicodeUTF8))
        QtCore.QMetaObject.connectSlotsByName(porcessProgress)
#
#------------------------------------------------------------------------------ 
#
def main():
    app = QtGui.QApplication(sys.argv)
    mainWindow = LongProcess()
    mainWindow.show()
    sys.exit(app.exec_())
#
#------------------------------------------------------------------------------ 
#
if __name__ == "__main__":
    main()
4

4 に答える 4

2

これは、この問題を修正するためのちょっといんちきな方法です。

これを正しく行うには、whileループをQThread内に配置し、Signalとslotを使用してQProgressbarを更新する必要があります。

しかし、これで今のところ問題は問題なく修正されます。

不足している部分は、

  1. progressBarの最大値を設定しますsetMaximum(value)
  2. ProgressBar setValue(value)を更新するコードを追加します
  3. QApplication.processEvents()を使用してイベントプロセスを適用します

クイックフィックス:

#Add QApplication on top #################
from PyQt4.QtGui import QApplication
##########################################
def on_pushButton_clicked(self):
        '''
            Simulate some long process to be performed.
            Before Long Process starts, show an oscillating progress bar to
            indiate process is running in background. 
        '''
        dialog = QtGui.QDialog()    
        progressBar = Ui_porcessProgress()
        progressBar.setupUi(dialog)
        dialog.show()
        #dialog.exec_()

        start = time.time()
        diff = 0
        while diff < 10:
            end = time.time()
            diff = end - start


            ################################################### 
            #add routine to update progressBar value
            progressBar.porcessProgressBar.setValue(diff)
            QApplication.processEvents()
            #add routine to update progressBar value 
            ###################################################


            print (diff)

class Ui_porcessProgress(object):
    def setupUi(self, porcessProgress):
        porcessProgress.setObjectName(_fromUtf8("porcessProgress"))
        porcessProgress.setWindowModality(QtCore.Qt.ApplicationModal)
        porcessProgress.resize(329, 81)
        porcessProgress.setMinimumSize(QtCore.QSize(329, 81))
        porcessProgress.setMaximumSize(QtCore.QSize(329, 81))
        icon = QtGui.QIcon()
        icon.addPixmap(QtGui.QPixmap(_fromUtf8(":/WFT/wftlogo2.png")), QtGui.QIcon.Normal, QtGui.QIcon.Off)
        porcessProgress.setWindowIcon(icon)
        porcessProgress.setModal(True)
        self.verticalLayout = QtGui.QVBoxLayout(porcessProgress)
        self.verticalLayout.setObjectName(_fromUtf8("verticalLayout"))
        self.label = QtGui.QLabel(porcessProgress)
        self.label.setObjectName(_fromUtf8("label"))
        self.verticalLayout.addWidget(self.label)
        self.porcessProgressBar = QtGui.QProgressBar(porcessProgress)

        ###########################################
        ####Set max value of progress bar
        self.porcessProgressBar.setMaximum(10)
        ############################################

        self.porcessProgressBar.setProperty("value", 10)
        self.porcessProgressBar.setTextVisible(False)
        self.porcessProgressBar.setObjectName(_fromUtf8("porcessProgressBar"))
        self.verticalLayout.addWidget(self.porcessProgressBar)
        porcessProgress.setWindowTitle(QtGui.QApplication.translate("porcessProgress", "Please wait...", None, QtGui.QApplication.UnicodeUTF8))
        self.label.setText(QtGui.QApplication.translate("porcessProgress", "Time History data extraction from ODB file in progress...", None, QtGui.QApplication.UnicodeUTF8))
        QtCore.QMetaObject.connectSlotsByName(porcessProgress)
#
#------------------------------------------------------------------------------ 
于 2012-05-14T17:39:53.943 に答える
0

メイン スレッドを介してループされる ProgressBar は、GUI を実行しているスレッドとまったく同じスレッドをロックするため、LongProcess が終了するまで更新は表示されません。長いプロセスを実行している部分をスレッド化し、メインスレッドに報告する必要があります。このSOの質問を見てください

于 2012-05-14T17:17:09.410 に答える
0

GUI の更新を行うには、ボタン スロットから戻る必要があります。そのスロットに座っていると、UI スレッドをブロックしているため、実際の (目に見える) ウィジェットのセットアップは表示されません。

私は Python に詳しくありませんが、バックグラウンドでプロセスを実行する (UI をブロックしない) Qt の方法は、QProcess. QProcess インスタンスには、子プロセスの状態変化を通知するシグナルと、入力および出力ストリームを処理するメソッドがあります。あなたはそれを調べたいと思うかもしれません、それはより多くの柔軟性を可能にします.
(特に、スレッド化をまったく処理する必要はありません。これは、IMO としては非常に良いことです。)

于 2012-05-14T17:17:28.803 に答える
0

メソッドで行うことはすべてon_pushButton_clickedgui スレッドで実行されるため、gui がフリーズします。に移動するだけQThreadです。そしてこれを読んでください。

于 2012-05-14T17:17:43.677 に答える