[英]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_())
為了解決這個問題,我將與每個文件關聯的QPlainTextEdit
和QProgressBar
對象傳遞給線程,然后從線程發出這些對象,以便我可以確定要打印到哪個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.