PyQt5多线程:如何组合多个线程的输出

时间:2018-03-30 15:03:10

标签: multithreading pyqt5

我正在尝试使用包含多线程的PyQt5构建Python GUI。使用各种代码片段(主要从Stack Overflow获取),我提出了下面的代码,它将csv文件中的数据读入pandas数据帧并更新GUI(从QTimer触发)。

我想实现第二个线程,该线程从不同的csv文件和一个接受两个数据帧的函数读取,并在更新GUI之前对它们进行一些计算。我不确定如何在PyQt5的信号/插槽框架内做到这一点。任何帮助深表感谢!

import time
import traceback
import sys
import pandas as pd
import numpy as np
from PyQt5.QtWidgets import (QWidget, QTabWidget, QGridLayout, QApplication, QMainWindow, QStatusBar, QTableView)
from PyQt5.QtCore import (Qt, QTimer, QAbstractTableModel, QThread, QVariant, QObject, QRect, pyqtSlot, pyqtSignal)


class PandasModel(QAbstractTableModel):
    """
    Class to populate a table view with a pandas dataframe
    """
    def __init__(self, data, parent=None):
        QAbstractTableModel.__init__(self, parent)
        self._data = np.array(data.values)
        self._cols = data.columns
        self.r, self.c = np.shape(self._data)

    def rowCount(self, parent=None):
        return self.r

    def columnCount(self, parent=None):
        return self.c

    def data(self, index, role=Qt.DisplayRole):
        if index.isValid():
            if role == Qt.DisplayRole:
                return self._data[index.row(), index.column()]
        return None

    def headerData(self, p_int, orientation, role):
        if role == Qt.DisplayRole:
            try:
                if orientation == Qt.Horizontal:
                    return self._cols[p_int]
                elif orientation == Qt.Vertical:
                    return p_int
            except(IndexError, ):
                return QVariant()
        else:
            return QVariant()


class WorkerSignals(QObject):
    """
    Defines the signals available from a running worker thread.
    Supported signals are:
    finished: No data
    error: `tuple` (exctype, value, traceback.format_exc() )
    result: `object` data returned from processing, anything
    progress: `int` indicating % progress
    """
    finished = pyqtSignal()
    error = pyqtSignal(tuple)
    result = pyqtSignal(object)
    progress = pyqtSignal(int)


class Worker(QObject):
    """
    Worker thread
    Inherits from QRunnable to handler worker thread setup, signals and wrap-up.
    :param callback: The function callback to run on this worker thread. Supplied args and
                     kwargs will be passed through to the runner.
    :type callback: function
    :param args: Arguments to pass to the callback function
    :param kwargs: Keywords to pass to the callback function

    """

    def __init__(self, fn, *args, **kwargs):
        super(Worker, self).__init__()
        # Store constructor arguments (re-used for processing)
        self.fn = fn
        self.args = args
        self.kwargs = kwargs
        self.signals = WorkerSignals()

        # Add the callback to our kwargs
        # kwargs['progress_callback'] = self.signals.progress

    @pyqtSlot()
    def run(self):
        """
        Initialise the runner function with passed args, kwargs.
        """

        # Retrieve args/kwargs here; and fire processing using them
        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)  # Return the result of the processing
        finally:
            self.signals.finished.emit()  # Done


class MyTabWidget(QWidget):

    def __init__(self, parent):
        super(QWidget, self).__init__(parent)
        self.layout = QGridLayout(self)

        # Initialize tab screen
        self.tabs = QTabWidget()
        self.tab1 = QWidget()
        self.tabs.resize(300, 200)

        # Add tabs
        self.tabs.addTab(self.tab1, "Tab 1")

        # Create tabs
        self.tab1.layout = QGridLayout(self)
        self.tab1.setLayout(self.tab1.layout)

        # Create table widgets
        self.table_widget1 = MyTableWidget(self)

        # Add tables to tabs
        self.tab1.layout.addWidget(self.table_widget1)

        # Add tabs to widget
        self.layout.addWidget(self.tabs)
        self.setLayout(self.layout)


class MyTableWidget(QWidget):

    def __init__(self, parent):
        super(QWidget, self).__init__(parent)
        self.layout = QGridLayout(self)

        # Initialize tables
        self.table = QTableView()

        # Add tabs to widget
        self.layout.addWidget(self.table)
        self.setLayout(self.layout)


class MainWindow(QMainWindow):
    def __init__(self, *args, **kwargs):
        super(MainWindow, self).__init__(*args, **kwargs)
        # Create a central Widgets
        self.central_widget = QWidget()
        # Create tab Widgets
        self.tab_widget = MyTabWidget(self)
        # Create a Layout for the central Widget
        self.central_layout = QGridLayout()
        # Set the Layout
        self.central_widget.setLayout(self.central_layout)
        # Set the Widget
        self.setCentralWidget(self.central_widget)
        self.central_layout.addWidget(self.tab_widget)
        self.setGeometry(QRect(0, 100, 2000, 1500))
        self.setWindowTitle('CSV viewer')

        self.status_bar = QStatusBar()
        self.setStatusBar(self.status_bar)

        self.proxy = None
        self.model = None
        self.show()

        # Setup worker thread
        self.worker_thread = QThread(self)
        self.worker = Worker(self.read_csv)
        self.worker.moveToThread(self.worker_thread)
        self.worker_thread.started.connect(self.worker.run)

        # Connect worker signals to appropriate ports
        self.worker.signals.result.connect(self.process_csv)
        self.worker.signals.finished.connect(self.worker_thread.quit)
        self.worker.signals.finished.connect(self.print_refresh_time)

        # Setup timer to repetitively trigger start of worker thread e.g. every 5000ms
        self.timer = QTimer()
        self.timer.timeout.connect(self.worker_thread.start)
        self.timer.start(5000)

    def process_csv(self, df):
        self.model = PandasModel(df)
        self.tab_widget.table_widget1.table.setModel(self.model)
        self.tab_widget.table_widget1.table.resizeColumnsToContents()

    def print_refresh_time(self):
        self.status_bar.showMessage('Last updated: ' + time.strftime("%d/%m/%Y %H:%M:%S", time.localtime()))

    @staticmethod
    def read_csv():
        df = pd.read_csv("C:\\Users\\Brian\\Desktop\\E0.csv")
        return df


def main():
    app = QApplication(sys.argv)
    window = MainWindow()
    app.exec_()


if __name__ == '__main__':
    main()

0 个答案:

没有答案