python - 如何在 PyQT 中使用 QThread

标签 python multithreading csv pyqt5 pyside2

我的代码遇到了很多麻烦 - 具体来说,实现某种信号/槽/线程。我对此很陌生,我在网上阅读的内容并没有太大帮助。

无论如何,我使用 PyQT Designer 制作了一个简单的 GUI。事情是这样的:

from PySide2 import QtCore, QtGui, QtWidgets

class Ui_Dialog(object):
    def setupUi(self, Dialog):
        Dialog.setObjectName("Dialog")
        Dialog.resize(687, 514)
        self.browse_btn = QtWidgets.QPushButton(Dialog)
        self.browse_btn.setGeometry(QtCore.QRect(20, 440, 61, 27))
        self.browse_btn.setObjectName("browse_btn")
        self.play_btn = QtWidgets.QPushButton(Dialog)
        self.play_btn.setGeometry(QtCore.QRect(100, 440, 61, 27))
        self.play_btn.setObjectName("play_btn")
        self.cur_scene_header = QtWidgets.QTextEdit(Dialog)
        self.cur_scene_header.setGeometry(QtCore.QRect(20, 20, 191, 21))
        self.cur_scene_header.setObjectName("cur_scene_header")
        self.prev_scene_header = QtWidgets.QTextEdit(Dialog)
        self.prev_scene_header.setGeometry(QtCore.QRect(20, 90, 191, 21))
        self.prev_scene_header.setObjectName("prev_scene_header")
        self.pause_btn = QtWidgets.QPushButton(Dialog)
        self.pause_btn.setGeometry(QtCore.QRect(180, 440, 61, 27))
        self.pause_btn.setObjectName("pause_btn")
        self.reset_btn = QtWidgets.QPushButton(Dialog)
        self.reset_btn.setGeometry(QtCore.QRect(260, 440, 61, 27))
        self.reset_btn.setObjectName("reset_btn")
        self.prev_scenes = QtWidgets.QListWidget(Dialog)
        self.prev_scenes.setGeometry(QtCore.QRect(20, 120, 331, 301))
        self.prev_scenes.setObjectName("prev_scenes")
        self.current_scene = QtWidgets.QListWidget(Dialog)
        self.current_scene.setGeometry(QtCore.QRect(20, 50, 331, 31))
        self.current_scene.setObjectName("current_scene")

        self.retranslateUi(Dialog)
        QtCore.QMetaObject.connectSlotsByName(Dialog)

    def retranslateUi(self, Dialog):
        Dialog.setWindowTitle(QtWidgets.QApplication.translate("Dialog", "Dialog", None, -1))
        self.browse_btn.setText(QtWidgets.QApplication.translate("Dialog", "Browse", None, -1))
        self.play_btn.setText(QtWidgets.QApplication.translate("Dialog", "Play", None, -1))
        self.cur_scene_header.setHtml(QtWidgets.QApplication.translate("Dialog", "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.0//EN\" \"http://www.w3.org/TR/REC-html40/strict.dtd\">\n"
"<html><head><meta name=\"qrichtext\" content=\"1\" /><style type=\"text/css\">\n"
"p, li { white-space: pre-wrap; }\n"
"</style></head><body style=\" font-family:\'Sans Serif\'; font-size:9pt; font-weight:400; font-style:normal;\">\n"
"<p style=\" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;\">Current Scene</p></body></html>", None, -1))
        self.prev_scene_header.setHtml(QtWidgets.QApplication.translate("Dialog", "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.0//EN\" \"http://www.w3.org/TR/REC-html40/strict.dtd\">\n"
"<html><head><meta name=\"qrichtext\" content=\"1\" /><style type=\"text/css\">\n"
"p, li { white-space: pre-wrap; }\n"
"</style></head><body style=\" font-family:\'Sans Serif\'; font-size:9pt; font-weight:400; font-style:normal;\">\n"
"<p style=\" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;\">Previous Scenes</p></body></html>", None, -1))
        self.pause_btn.setText(QtWidgets.QApplication.translate("Dialog", "Pause", None, -1))
        self.reset_btn.setText(QtWidgets.QApplication.translate("Dialog", "Reset", None, -1))

那里没有什么真正重要的。有四个按钮:浏览、播放、重置和暂停。我还没有实现重置和暂停按钮,但稍后我会实现。

有两个QWidgetList。第一个“curr_scene”(当前场景)显示一个字符串。第二个“prev_scenes”是先前场景的列表(过去的 current_scenes)。

当用户单击“浏览”按钮时,他们可以使用 TKinter filedialog 模块选择文件(csv 文件)。选择后,程序将加载 csv 文件的内容。接下来我想要发生的是,它转到(csv 文件的)第一行,将该文本放入 current_scene 列表中,然后等待五秒,将该行发送到 previous_scene 列表,然后从 csv 文件中取出下一行,将其放入 current_scene 中,依此类推,直到到达最后一行。

现在发生的事情就是我想要的 - 只是它没有显示。它没有显示在 GUI 上发生的情况。我只能使用 prev_scene 来显示文本,或者使用 current_scene 来显示文本,而不能同时使用两者,这是通过使用 repaint 来完成的。

我知道我必须使用线程。但我真的不知道该怎么办。能给我看看么?这是程序其余部分的代码:

import sys
import PySide2
from PySide2.QtCore import *
from PySide2.QtWidgets import *
import pull_csv_data
import main
import time
from tkinter import filedialog

class MainDialog(QWidget, main.Ui_Dialog):

    def __init__(self, parent = None):
        super(MainDialog, self).__init__(parent)
        self.setupUi(self)

        self.connect(self.browse_btn, SIGNAL("clicked()"), self.browse_for_file)
        self.list = []
        self.connect(self.play_btn, SIGNAL("clicked()"), self.list_items)
        self.csv_path = ""
        self.file_watch = QFileSystemWatcher()

    def browse_for_file(self):
        if not self.list:
            self.csv_path = filedialog.askopenfilename()
            self.list = pull_csv_data.pull_data(self.csv_path)
            self.update_watcher()

    def update_watcher(self):
        self.file_watch.addPath(self.csv_path)
        self.file_watch.connect(self.file_watch, SIGNAL('fileChanged(QString)'), self.update_list)

    def update_list(self):
        print("here1")
        old_list = self.list
        self.list = pull_csv_data.pull_data(self.csv_path)
        print("here2")
        if len(old_list) < len(self.list):
            for i in range(len(old_list), len(self.list)):
                item = QListWidgetItem(self.list[i][0])
                self.prev_scenes.addItem(item)
                print("here6")
        else:
            self.list = pull_csv_data.pull_data(self.csv_path)
            self.prev_scenes.clear()
            for i in self.list:
                item = QListWidgetItem(i[0])
                self.prev_scenes.addItem(item)

    def list_items(self):
        if self.prev_scenes.count() == 0:
            for i in self.list:
                item = QListWidgetItem(i[0])
                self.update_current(item)

    def update_current(self, item):
        self.current_scene.addItem(item)
        time.sleep(0.5)
        self.update_prev(item)
        self.current_scene.clear()


    def update_prev(self, item):
        print("hello")
        self.prev_scenes.addItem(item)
        self.prev_scenes.repaint()
        self.prev_scenes.repaint()



app = QApplication(sys.argv)
form = MainDialog()
form.show()
sys.exit(app.exec_()) 

这是 pull_csv_data:

def pull_data(file_path):
    king = []
    with open(file_path) as csvDataFile:
        csvReader = csv.reader(csvDataFile)
        for row in csvReader:
            king.append(row)
    return king

最佳答案

不建议组合执行相同任务的库,以及可以像 tkinter 和 Qt 一样被阻止的更糟糕的库,并且两者都会创建内部主循环。 Qt 是一个库,它具有用于许多功能的组件,并且还具有用于选择文件的库:QFileDialog.getOpenFileName()

def browse_for_file(self):
    if not self.list:
        self.csv_path, _ = QFileDialog.getOpenFileName()
        self.list = pull_csv_data.pull_data(self.csv_path)
        self.update_watcher()

另一件不应该做的事情是执行阻塞任务,例如 sleep(),这些任务不会让 GUI 工作,每个 GUI 库都提出了不必使用这些函数的选项,在我们的例子中, QEvenLoop 的组合使用 QTimer 是正确的选择 此外,您无法将一个 QListWidget 中的项目分配给另一个 QListWidget,您必须首先使用 takeItem() 将其从一个 QListWidget 中删除。并将其分配给另一个:

def update_current(self, item):
    self.current_scene.addItem(item)
    loop = QEventLoop()
    QTimer.singleShot(500, loop.quit)
    loop.exec_()
    it = self.current_scene.takeItem(0)
    self.update_prev(it)

def update_prev(self, item):
    self.prev_scenes.addItem(item)

关于python - 如何在 PyQT 中使用 QThread,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/47275135/

相关文章:

java - Thread.join() 未按预期工作

c# - 处理 Task.Run 中的异常

c - 从多个线程写入一个数组

python - 用 Pandas 创建空的 csv 文件

python - django prefetch_related 是否应该与 GenericRelation 一起使用

python - 遍历 not None 和 not empty 目录

csv - 从 Tableau Server 导出为交叉表时如何删除 Tableau 工作表中的隐藏字段?

php - 将 CSV 文件直接导入 MySQL 数据库

python - vim: 无法加载库 libpython

python - 在这种情况下有更好的方法使用 numpy 吗?