簡體   English   中英

合並 PyQt5 中的插槽

[英]Consolidating slots in PyQt5

如何減少pyqtSlot()函數的數量,以便我不需要每個函數有兩個? 似乎應該有更好的方法來做到這一點,但我一直無法弄清楚。

該代碼采用兩個文件,在不同的線程上讀取每個文件,並將輸出打印到不同的QPlainTextEdit對象。

import sys
import time
import traceback
import pandas as pd

from PyQt5.QtWidgets import *
from PyQt5.QtCore import *

from compare_files_gui import Ui_MainWindow


class WorkerSignals(QObject):
    """Defines signals from running worker thread."""
    finished = pyqtSignal()
    error = pyqtSignal(tuple)
    result = pyqtSignal(str)
    progress = pyqtSignal(str)
    bar = pyqtSignal(int)


class Worker(QRunnable):
    """Worker thread."""
    def __init__(self, fn, *args, **kwargs):
        super(Worker, self).__init__()

        self.fn = fn
        self.args = args
        self.kwargs = kwargs
        self.signals = WorkerSignals()

        self.kwargs['progress_callback'] = self.signals.progress
        self.kwargs['pbar'] = self.signals.bar

    @pyqtSlot()
    def run(self):
        try:
            result = self.fn(*self.args, **self.kwargs)
        except:
            traceback.print_exc()
            exctype, value = sys.exc_info()[:2]
            self.signals.error.emit((exctype, value, traceback.format_exc()))
        else:
            self.signals.result.emit(result)
        finally:
            self.signals.finished.emit()


class MainWindow(QMainWindow):

    def __init__(self):
        QMainWindow.__init__(self)

        self.ui = Ui_MainWindow()
        self.ui.setupUi(self)

        self.ui.pushButton.clicked.connect(self.compare)
        self.ui.file1_progressBar.setValue(0)
        self.ui.file2_progressBar.setValue(0)

        self.ui.file1_lineEdit.setText('file1.csv')
        self.ui.file2_lineEdit.setText('file2.csv')
        self.file1 = self.ui.file1_lineEdit.text()
        self.file2 = self.ui.file2_lineEdit.text()

        self.threadpool = QThreadPool()

    ##### How can I consolidate the following slots so I don't need to
    ##### have 2, one for each console object?
    @pyqtSlot(str)
    def progress_fn1(self, n):
        self.ui.console1_plainTextEdit.appendPlainText(n)

    @pyqtSlot(str)
    def progress_fn2(self, n):
        self.ui.console2_plainTextEdit.appendPlainText(n)

    @pyqtSlot(str)
    def print_output1(self, s):
        self.ui.console1_plainTextEdit.appendPlainText(s)

    @pyqtSlot(str)
    def print_output2(self, s):
        self.ui.console2_plainTextEdit.appendPlainText(s)

    @pyqtSlot()
    def thread_complete1(self):
        self.ui.console1_plainTextEdit.appendPlainText('Processing complete!')

    @pyqtSlot()
    def thread_complete2(self):
        self.ui.console2_plainTextEdit.appendPlainText('Processing complete!')

    @pyqtSlot(int)
    def update_progress1(self, v):
        self.ui.file1_progressBar.setValue(v)

    @pyqtSlot(int)
    def update_progress2(self, v):
        self.ui.file2_progressBar.setValue(v)

    def compare(self):
        # files = [self.ui.file1_lineEdit.text(), self.ui.file2_lineEdit.text()]
        files = [self.file1, self.file2]

        # Start new thread for each file
        for i, file in enumerate(files, 1):
            worker = Worker(self.process_file, file)

            #### Is there a better way to do this?
            if i == 1:
                worker.signals.progress.connect(self.progress_fn1)
                worker.signals.result.connect(self.print_output1)
                worker.signals.finished.connect(self.thread_complete1)
                worker.signals.bar.connect(self.update_progress1)
            elif i == 2:
                worker.signals.progress.connect(self.progress_fn2)
                worker.signals.result.connect(self.print_output2)
                worker.signals.finished.connect(self.thread_complete2)
                worker.signals.bar.connect(self.update_progress2)
            else:
                pass

            # Execute thread
            self.threadpool.start(worker)

    def process_file(self, file, pbar, progress_callback):
        """Process file and emit signals."""
        t0 = time.time()
        progress_callback.emit(f'Processing {file}')

        df = pd.read_csv(file, header=None, names=['col'])

        num = len(df.index)

        for i, (index, row) in enumerate(df.iterrows(), 1):
            progress_callback.emit('  ' + row['col'])
            pbar.emit(int(i*100/num))
            time.sleep(0.25)

        t1 = time.time()

        return f'Time to complete: {round(t1-t0, 3)} s'


if __name__ == '__main__':
    app = QApplication(sys.argv)
    main = MainWindow()
    main.show()
    sys.exit(app.exec_())

為了解決這個問題,我將與每個文件關聯的QPlainTextEditQProgressBar對象傳遞給線程,然后從線程發出這些對象,以便我可以確定要打印到哪個QPlainTextEdit 從我在線閱讀的內容來看,我認為將 UI 對象傳遞給后台線程並不是最佳實踐,但它適用於這個簡單的應用程序,而且我還沒有找到更好的方法來做到這一點。

import sys
import time
import traceback
import pandas as pd

from PyQt5.QtWidgets import *
from PyQt5.QtCore import *

from compare_files_gui import Ui_MainWindow


class WorkerSignals(QObject):
    """Defines signals from running worker thread."""
    finished = pyqtSignal(object)
    error = pyqtSignal(tuple)
    result = pyqtSignal(object, str)
    progress = pyqtSignal(object, str)
    bar = pyqtSignal(object, int)


class Worker(QRunnable):
    """Worker thread."""
    def __init__(self, fn, *args, **kwargs):
        super(Worker, self).__init__()

        # Store constructor arguments
        self.fn = fn
        self.args = args
        self.kwargs = kwargs
        self.signals = WorkerSignals()

        # Add callbacks to kwargs
        self.kwargs['progress_callback'] = self.signals.progress
        self.kwargs['pbar_callback'] = self.signals.bar

    @pyqtSlot()
    def run(self):
        try:
            console, result = self.fn(*self.args, **self.kwargs)
        except:
            traceback.print_exc()
            exctype, value = sys.exc_info()[:2]
            self.signals.error.emit((exctype, value, traceback.format_exc()))
        else:
            self.signals.result.emit(console, result)
        finally:
            self.signals.finished.emit(console)


class MainWindow(QMainWindow):

    def __init__(self):
        QMainWindow.__init__(self)

        self.ui = Ui_MainWindow()
        self.ui.setupUi(self)

        # Initialize progress bars
        self.ui.file1_progressBar.setValue(0)
        self.ui.file2_progressBar.setValue(0)

        # Connect widgets to slots
        self.ui.pushButton.clicked.connect(self.compare)

        # Create instance of QThreadPool
        self.threadpool = QThreadPool()


    @pyqtSlot(object, str)
    def progress_fn(self, console, n):
        """Print progress string to specific QPlainTextEdit object."""
        console.appendPlainText(n)

    @pyqtSlot(object, str)
    def print_output(self, console, s):
        """Print result string to specific QPlainTextEdit object."""
        console.appendPlainText(s)

    @pyqtSlot(object)
    def thread_complete(self, console):
        """Print completion text to specific QPlainTextEdit object."""
        console.appendPlainText('Processing complete!')

    @pyqtSlot(object, int)
    def update_progress(self, pbar, v):
        """Set value of QProgressBar object."""
        pbar.setValue(v)

    def compare(self):
        """Send each file and associated UI objects to thread."""
        # Store files in list
        # files = [self.ui.file1_lineEdit.text(), self.ui.file2_lineEdit.text()]
        files = [self.file1, self.file2]

        # Store QPlainTextEdit and QProgressBar objects in list
        consoles = [self.ui.console1_plainTextEdit, self.ui.console2_plainTextEdit]
        pbars = [self.ui.file1_progressBar, self.ui.file2_progressBar]

        # Start new thread for each file
        for i, (file, console, pbar) in enumerate(zip(files, consoles, pbars), 1):
            worker = Worker(self.process_file, file, console, pbar)

            # Connect thread signals to slots in UI thread
            worker.signals.progress.connect(self.progress_fn)
            worker.signals.result.connect(self.print_output)
            worker.signals.finished.connect(self.thread_complete)
            worker.signals.bar.connect(self.update_progress)

            # Execute thread
            self.threadpool.start(worker)

    def process_file(self, file, console, pbar, pbar_callback, progress_callback):
        """Process file and emit signals."""
        t0 = time.time()

        progress_callback.emit(console, f'Processing {file}')

        # Read file into dataframe
        df = pd.read_csv(file, header=None, names=['col'])

        # Iterate over each row and emit value of column and progress
        num = len(df.index)
        for i, (index, row) in enumerate(df.iterrows(), 1):
            progress_callback.emit(console, '  ' + row['col'])
            pbar_callback.emit(pbar, int(i*100/num))

            # Slow down response
            time.sleep(0.25)

        t1 = time.time()

        # Return QPlainTextEdit object and string
        return console, f'Time to complete: {round(t1-t0, 3)} s'


if __name__ == '__main__':
    app = QApplication(sys.argv)
    main = MainWindow()
    main.show()
    sys.exit(app.exec_())

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM