0

バックで多くの計算を行い、同時に進行状況バーを表示する GUI プログラムを作成しました。完了すると、新しい画面に結果が表示されます。

次に、計算の前に別のインターフェイスを作成して、ユーザーが最後の計算結果を使用するかどうかを選択できるようにして、計算をスキップしたいと考えています。

計算に接続するボタンと前回の計算結果ファイルを選択するコンボボックスのある画面を作りました。

しかし、ボタンをクリックしても何も起こりませんでした。そして約 10 秒 (計算時間) 後に、結果画面がポップアップ表示されます。したがって、プログレスバー画面をスキップしました。なんで?

これは元のプログラムの一部です。

import sys
import configparser
import getpass
import telnetlib
import time
import subprocess
from datetime import *

from log_tracker import *
from PyQt4.QtCore import *
from PyQt4.QtGui import *
from PyQt4 import QtCore
from PyQt4 import QtGui

class Task_Checker(QMainWindow):

    def __init__(self):
        super(Task_Checker, self).__init__()

        config = configparser.RawConfigParser()
        config.read('profile.cfg')
        self.log_path = config.get('Config', 'log_path')
        self.log_prefix = config.get('Config', 'log_prefix')
        self.log_suffix = config.get('Config', 'log_suffix')

        self.initUI()

    def check_production(self):
        self.log_tracker = Log_Tracker(self)
        self.log_tracker.tick.connect(self.pbar.setValue)
        self.log_tracker.parseConfig()
        self.log_tracker.connectDb()
        self.log_tracker.trackLog()



    def initUI(self):
        self.resize(1400, 768)
        self.center()

        self.statusBar().showMessage('Checking Production Programs')

        self.wait_message = QLabel('Checking Production Programs')
        self.wait_message.setAlignment(Qt.Alignment(Qt.AlignHCenter))
        self.pbar = QProgressBar(self)
        self.pbar.setMinimum(0)
        self.pbar.setMaximum(100)

        vbox = QVBoxLayout()
        vbox.addWidget(self.wait_message)
        vbox.addWidget(self.pbar)
        tmpWidget2 = QWidget()
        tmpWidget2.setLayout(vbox)
        self.setCentralWidget(tmpWidget2)
        self.show()

        self.check_production()

        self.pbar.hide()
        self.statusBar().showMessage('Processing Information')

        self.tabs = QTabWidget()        
        mua_table = self.processInfo('MUA')
        bps_table = self.processInfo('BPS')
        obdua_table = self.processInfo('OBDUA')
        sua_table = self.processInfo('SUA')
        ngr_ftp_table = self.processInfo('NGR_FTP')
        bpspdfbill_table = self.processInfo('BpsPdfBill')
        disk_space_table = self.processInfo('Disk_Space')        
        self.tabs.addTab(mua_table, 'MUA')
        self.tabs.addTab(bps_table, 'BPS')
        self.tabs.addTab(obdua_table, 'ODBUA')
        self.tabs.addTab(sua_table, 'SUA')
        self.tabs.addTab(ngr_ftp_table, 'NGR_FTP')
        self.tabs.addTab(bpspdfbill_table, 'BpsPdfBill')
        self.tabs.addTab(disk_space_table, 'Disk_Space')
        self.setCentralWidget(self.tabs)

        self.statusBar().showMessage('Ready')

        self.setWindowTitle('Task Checker')    
        self.show()

    def center(self):

        qr = self.frameGeometry()
        cp = QDesktopWidget().availableGeometry().center()
        qr.moveCenter(cp)
        self.move(qr.topLeft())



    def processInfo(self, project_name):
        ...the processing...

プログレスバーの前に新しい画面を追加するために (元々は計算をすぐにロードします)、initUI() にいくつかの変更を加え、計算部分を新しいサブルーチン checkProd() に移動してから、ボタンで接続します。 :

import sys
import configparser
import getpass
import telnetlib
import time
import subprocess
from datetime import *

from log_tracker import *
from PyQt4.QtCore import *
from PyQt4.QtGui import *
from PyQt4 import QtCore
from PyQt4 import QtGui

class Task_Checker(QMainWindow):

    def __init__(self):
        super(Task_Checker, self).__init__()

        config = configparser.RawConfigParser()
        config.read('profile.cfg')
        self.log_path = config.get('Config', 'log_path')
        self.log_prefix = config.get('Config', 'log_prefix')
        self.log_suffix = config.get('Config', 'log_suffix')

        self.initUI()

    def check_production(self):
        self.log_tracker = Log_Tracker(self)
        self.log_tracker.tick.connect(self.pbar.setValue)
        self.log_tracker.parseConfig()
        self.log_tracker.connectDb()
        self.log_tracker.trackLog()



    def initUI(self):
        self.resize(1400, 768)
        self.center()

        btn_check = QPushButton('Check Lastest Status', self)
        btn_check.setToolTip('Click this if you want to check the lastest status in production')

        combo = QComboBox()
        dirlist = os.listdir(self.log_path)
        for f in dirlist:
            combo.addItem(f)

        QtCore.QObject.connect(btn_check, QtCore.SIGNAL('clicked()'), self.checkProd)

        hbox = QHBoxLayout()
        hbox.addWidget(btn_check)
        hbox.addWidget(combo)
        tmpWidget = QWidget()
        tmpWidget.setLayout(hbox)        
        self.setCentralWidget(tmpWidget)
        self.show()

    def checkProd(self):
        self.statusBar().showMessage('Checking Production Programs')

        self.wait_message = QLabel('Checking Production Programs')
        self.wait_message.setAlignment(Qt.Alignment(Qt.AlignHCenter))
        self.pbar = QProgressBar(self)
        self.pbar.setMinimum(0)
        self.pbar.setMaximum(100)

        vbox = QVBoxLayout()
        vbox.addWidget(self.wait_message)
        vbox.addWidget(self.pbar)
        tmpWidget2 = QWidget()
        tmpWidget2.setLayout(vbox)
        self.setCentralWidget(tmpWidget2)
        self.show()


        self.check_production()

        self.pbar.hide()
        self.statusBar().showMessage('Processing Information')

        self.tabs = QTabWidget()        
        mua_table = self.processInfo('MUA')
        bps_table = self.processInfo('BPS')
        obdua_table = self.processInfo('OBDUA')
        sua_table = self.processInfo('SUA')
        ngr_ftp_table = self.processInfo('NGR_FTP')
        bpspdfbill_table = self.processInfo('BpsPdfBill')
        disk_space_table = self.processInfo('Disk_Space')        
        self.tabs.addTab(mua_table, 'MUA')
        self.tabs.addTab(bps_table, 'BPS')
        self.tabs.addTab(obdua_table, 'ODBUA')
        self.tabs.addTab(sua_table, 'SUA')
        self.tabs.addTab(ngr_ftp_table, 'NGR_FTP')
        self.tabs.addTab(bpspdfbill_table, 'BpsPdfBill')
        self.tabs.addTab(disk_space_table, 'Disk_Space')

        self.setCentralWidget(self.tabs)

        self.statusBar().showMessage('Ready')

        self.setWindowTitle('Task Checker')    
        self.show()


    def center(self):

        qr = self.frameGeometry()
        cp = QDesktopWidget().availableGeometry().center()
        qr.moveCenter(cp)
        self.move(qr.topLeft())



    def processInfo(self, project_name):
        ...the processing...
4

1 に答える 1

0

これは読むべき不必要なコードがたくさんありますが、私がなんとか解析したことは、メイン スレッド内の別のウィジェットも応答性を維持することを期待しながら、メイン スレッド内で大量の計算を行っているということです。

これは、PyQt4 (およびおそらくメイン スレッド イベント ループを使用するほとんどの GUI フレームワーク) を使用する場合によくある落とし穴です。問題は、アプリを起動すると、メイン イベント ループが、GUI から処理する新しいイベントを常にポーリングすることです。すべての操作が完了するまでに非常に短い時間がかかるか、定期的に制御を放棄してイベントループに戻ることを期待しています。現在、あなたの計算はメイン スレッドのすべての可用性を占有しており、進行状況ウィンドウが実行しようとしているものはすべて、イベント ループがそれを取得する機会を待っているキューにバックアップしているだけです。

これに対する簡単な答えは次のとおりです。別のQThreadで重い計算を行い、シグナルを介してメインスレッドに通信します。これには、あなたの側で少し改造が必要になります。どのクラスの init にも重いものを入れるべきではありません。しかし、2 番目の例では、計算をボタンにアタッチすることで、これに対処することができました。よいスタート。ボタンは実際には別のスレッドで計算を開始する必要があるため、メインスレッドをブロックしません。

Qt4.7 スレッド化の基本

GUI スレッドとワーカー スレッド
前述のように、各プログラムは起動時に 1 つのスレッドを持ちます。このスレッドは「メイン スレッド」と呼ばれます (Qt アプリケーションでは「GUI スレッド」とも呼ばれます)。Qt GUI はこのスレッドで実行する必要があります。すべてのウィジェットといくつかの関連クラス (QPixmap など) は、セカンダリ スレッドでは機能しません。セカンダリ スレッドは、メイン スレッドから処理作業をオフロードするために使用されるため、一般に「ワーカー スレッド」と呼ばれます。
...スレッドの
使用 スレッド
には基本的に 2 つの使用例があり
ます。 1. マルチコア プロセッサを使用して処理を高速化します。
2. 長期にわたる処理をオフロードするか、他のスレッドへの呼び出しをブロックすることにより、GUI スレッドまたはその他のタイム クリティカルなスレッドの応答性を維持します。

結果を確認したいだけの場合の簡単な修正として、QtGui.QApplication.processEvents()計算メソッドから定期的に呼び出すことができます。これにより、イベントループが保留中の操作をフラッシュできることがよくあります。そうすることで、進行状況ウィジェットのようなものが実際に機能します。基本的に、あなたがしていることは、イベントループを手動で「ポンピング」することです。ただし、これは最善の方法ではありません。通常、軽量のワンオフ用に予約されています。最高のパフォーマンスが必要な場合は、スレッドに移動します。

于 2012-07-07T05:30:56.960 に答える