【发布时间】:2021-07-29 17:40:39
【问题描述】:
我在尝试按顺序使用 PyQt 和 QThreads 遍历表中的每一行时遇到了困难。我已经简化了下面的代码(我的原始代码涉及执行 SQL 查询的线程)
下面的代码创建了一个按钮和一个三行三列的表格。按下启动按钮时,将从表中的第一行读取文本并将其传递给线程。在线程中(具有模拟延迟),字符串被反转并传递回主线程。接下来,将反转的字符串传递回另一个线程实例,并将字符串返回到原始顺序并更新表。计划是遍历每一行
我的问题是我的代码似乎首先对所有行执行所有反向字符串操作,然后对所有行执行所有字符串撤消操作,然后将更新转储到最后的表中
我希望对第一行执行相反的操作,然后在同一行执行撤消操作,然后再转到表中的下一行,同时更新表中的更改
这是结果表的视图:
代码如下:
import sys
import time
from datetime import datetime
from PyQt5.QtWidgets import QMainWindow, QApplication, QWidget, QTableWidget, QTableWidgetItem, QVBoxLayout, QPushButton, QHBoxLayout
from PyQt5.QtCore import pyqtSignal, QObject, QThread
from PyQt5 import QtGui
import functools
class WorkerSignals(QObject):
finished = pyqtSignal()
error = pyqtSignal(str)
result = pyqtSignal(object)
class Worker(QObject):
def __init__(self, string_list):
super(Worker, self).__init__()
self.signals = WorkerSignals() # Create an instance of our signals class.
self.string_list = string_list
def run(self):
for string in self.string_list:
time.sleep(1)
self.signals.result.emit(string[::-1])
self.signals.finished.emit()
class MainWindow(QMainWindow):
def __init__(self):
QMainWindow.__init__(self)
self.setWindowTitle('Testing ')
central = QWidget(self)
self.setCentralWidget(central)
mainLayout = QHBoxLayout(central)
buttonLayout = QVBoxLayout()
mainLayout.addLayout(buttonLayout)
button1 = QPushButton('Launch') # Add launch button
buttonLayout.addWidget(button1)
tableLayout = QVBoxLayout()
mainLayout.addLayout(tableLayout)
self.tableWidget = QTableWidget(self.centralWidget()) # Add table
self.tableWidget.setRowCount(3)
self.tableWidget.setColumnCount(3)
self.tableWidget.setItem(0, 0, QTableWidgetItem("Test A"))
self.tableWidget.setItem(0, 1, QTableWidgetItem(""))
self.tableWidget.setItem(0, 2, QTableWidgetItem(""))
self.tableWidget.setItem(1, 0, QTableWidgetItem("Test B"))
self.tableWidget.setItem(1, 1, QTableWidgetItem(""))
self.tableWidget.setItem(1, 2, QTableWidgetItem(""))
self.tableWidget.setItem(2, 0, QTableWidgetItem("Test C"))
self.tableWidget.setItem(2, 1, QTableWidgetItem(""))
self.tableWidget.setItem(2, 2, QTableWidgetItem(""))
tableLayout.addWidget(self.tableWidget)
self.show()
button1.clicked.connect(self.start_tests) # Trigger start_tests method on button clicked
def call_worker(self, my_string, fn, row):
""" Prep worker thread request in main thread"""
self.my_string = my_string
self.fn = fn
self.row = row
self.thread = QThread(self)
self.worker = Worker([self.my_string])
self.worker.moveToThread(self.thread)
self.thread.started.connect(self.worker.run)
self.worker.signals.result.connect(functools.partial(self.fn, self.row))
self.worker.signals.finished.connect(self.worker.deleteLater)
self.thread.finished.connect(self.thread.deleteLater)
self.thread.start()
self.thread.quit()
self.thread.wait()
return self.thread
def start_tests(self):
self.launch_time = datetime.now().strftime('%Y-%m-%d__%H-%M-%S')
print(self.launch_time)
all_rows = self.tableWidget.rowCount()
all_rows = [i for i in range(0, all_rows)]
"""Iterate through each row in table to perform a couple of actions, by calling a thread to run first to
reverse a string, then to pass to another method to undo the reverse in another thread, update the table row
then proceed to the next row in the table"""
for row in all_rows:
self.tableWidget.setItem(row, 1, QTableWidgetItem('Queued'))
self.tableWidget.item(row, 1).setBackground(QtGui.QColor.fromRgb(255, 255, 0))
self.test_str = (self.tableWidget.item(row, 0)).text()
# First thread call
self.call_worker(self.test_str, self.undo_changes, row)
def undo_changes(self, row, reverse_str):
self.row = row
self.reverse_str = reverse_str
print(f'String reversed to: {self.reverse_str}')
self.tableWidget.setItem(self.row, 1, QTableWidgetItem('Passed'))
self.tableWidget.item(self.row, 1).setBackground(QtGui.QColor.fromRgb(0, 255, 255))
# Second thread call
self.call_worker(self.reverse_str, self.add_to_third_column, self.row)
def add_to_third_column(self, row, orig_string):
self.row = row
self.orig_string = orig_string
print(f'String RETURNED to: {self.orig_string}')
# update table row
self.tableWidget.setItem(self.row, 2, QTableWidgetItem(self.orig_string))
self.tableWidget.item(self.row, 2).setBackground(QtGui.QColor.fromRgb(255, 0, 255))
if __name__ == '__main__':
app = QApplication(sys.argv)
window = MainWindow()
app.exec_()
这是我目前得到的打印输出:
String reversed to: A tseT
String reversed to: B tseT
String reversed to: C tseT
String RETURNED to: Test A
String RETURNED to: Test B
String RETURNED to: Test C
这是我在每行之后更新表格时希望得到的:
String reversed to: A tseT
String RETURNED to: Test A #Then update table
String reversed to: B tseT
String RETURNED to: Test B #Then update table
String reversed to: C tseT
String RETURNED to: Test C #Then update table
感谢您的宝贵时间。
【问题讨论】:
-
请注意,
Worker已经继承自 QObject,不需要单独的信号对象。此外,如果您在线程上调用wait,您实际上是在使线程的点无效,因为该函数在线程完成之前不会返回(因此是您的结果)。
标签: python multithreading pyqt pyqt5