在PyQt中使用自定义小部件?

时间:2013-12-12 09:23:48

标签: python multithreading qt python-3.x pyqt

我跟着this example(PyQT4)在PyQT5中创建了一个“自定义小部件”,最后得到了以下代码:

progress.py

from PyQt5 import QtCore, QtGui, QtWidgets
from PyQt5.QtWidgets import (QApplication,QMainWindow)

class Ui_Form(QMainWindow):
    def __init__(self, name, parent=None):
        super(Ui_Form,self).__init__(parent)
        #Form.setObjectName("Form")
        self.resize(619, 202)
        self.formLayoutWidget = QtWidgets.QWidget()
        self.formLayoutWidget.setGeometry(QtCore.QRect(0, 0, 621, 201))
        self.formLayoutWidget.setObjectName("formLayoutWidget")
        self.formLayout = QtWidgets.QFormLayout(self.formLayoutWidget)
        self.formLayout.setContentsMargins(0, 0, 0, 0)
        self.formLayout.setObjectName("formLayout")
        self.progressBar = QtWidgets.QProgressBar(self.formLayoutWidget)
        self.progressBar.setProperty("value", 24)
        self.progressBar.setObjectName("progressBar")
        self.formLayout.setWidget(2, QtWidgets.QFormLayout.SpanningRole, self.progressBar)
        self.graphicsView = QtWidgets.QGraphicsView(self.formLayoutWidget)
        self.graphicsView.setObjectName("graphicsView")
        self.formLayout.setWidget(1, QtWidgets.QFormLayout.LabelRole, self.graphicsView)
        self.label_Title = QtWidgets.QLabel(self.formLayoutWidget)
        font = QtGui.QFont()
        font.setFamily("Verdana")
        font.setPointSize(22)
        font.setBold(True)
        font.setWeight(75)
        self.label_Title.setFont(font)
        self.label_Title.setObjectName("label_Title")
        self.formLayout.setWidget(0, QtWidgets.QFormLayout.SpanningRole, self.label_Title)
        self.timer = QtCore.QBasicTimer()
        self.step = 0
        self.timer.start(100,self)

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


        def timerEvent(self, e):
            if self.step>= 100:
                self.timer.stop()
                return
            self.step = self.step + 1
            self.progressBar.setValue(self.step)   

    def retranslateUi(self):
        _translate = QtCore.QCoreApplication.translate
        self.setWindowTitle(_translate("Form", "Form"))
        self.label_Title.setText(_translate("Form", "TextLabel"))

new_ui.py

import sys
from PyQt5 import QtCore, QtGui, QtWidgets

class Ui_MainWindow(object):
    def setupUi(self, MainWindow):
        MainWindow.setObjectName("MainWindow")
        MainWindow.resize(742, 538)
        self.centralwidget = QtWidgets.QWidget(MainWindow)
        self.centralwidget.setObjectName("centralwidget")
        self.pushButton_Start = QtWidgets.QPushButton(self.centralwidget)
        self.pushButton_Start.setGeometry(QtCore.QRect(630, 20, 91, 31))
        self.pushButton_Start.setObjectName("pushButton_Start")
        self.lineEdit_URL = QtWidgets.QLineEdit(self.centralwidget)
        self.lineEdit_URL.setGeometry(QtCore.QRect(20, 20, 601, 31))
        self.lineEdit_URL.setObjectName("lineEdit_URL")
        self.label = QtWidgets.QLabel(self.centralwidget)
        self.label.setGeometry(QtCore.QRect(20, 460, 46, 13))
        self.label.setObjectName("label")
        self.label_status = QtWidgets.QLabel(self.centralwidget)
        self.label_status.setGeometry(QtCore.QRect(73, 460, 651, 16))
        self.label_status.setObjectName("label_status")
        self.listWidget = QtWidgets.QListWidget(self.centralwidget)
        self.listWidget.setGeometry(QtCore.QRect(20, 60, 701, 391))
        self.listWidget.setObjectName("listWidget")
        #MainWindow.setCentralWidget(self.centralwidget)
        self.menubar = QtWidgets.QMenuBar(MainWindow)
        self.menubar.setGeometry(QtCore.QRect(0, 0, 742, 21))
        self.menubar.setObjectName("menubar")
        #MainWindow.setMenuBar(self.menubar)
        self.statusbar = QtWidgets.QStatusBar(MainWindow)
        self.statusbar.setObjectName("statusbar")
        #MainWindow.setStatusBar(self.statusbar)

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


    def retranslateUi(self, MainWindow):
        _translate = QtCore.QCoreApplication.translate
        MainWindow.setWindowTitle(_translate("MainWindow", "My Downloader"))
        self.pushButton_Start.setText(_translate("MainWindow", "Start"))
        self.label.setText(_translate("MainWindow", "Status :"))
        self.label_status.setText(_translate("MainWindow", "Ideal..."))




if __name__ == "__main__":
    import sys
    app = QtWidgets.QApplication(sys.argv)
    MainWindow = QtWidgets.QMainWindow()
    ui = Ui_MainWindow()
    ui.setupUi(MainWindow)
    MainWindow.show()
    sys.exit(app.exec_())

main.py

import sys
import threading
import logging
from PyQt5 import QtCore, QtGui, QtWidgets
import json
import urllib.request
from new_ui import Ui_MainWindow
import progress
import random

class MyForm(QtWidgets.QDialog):
  def __init__(self, parent=None):
    super(MyForm, self).__init__(parent)
    self.ui = Ui_MainWindow()
    self.ui.setupUi(self)
    self.ui.pushButton_Start.clicked.connect(self.thread_start)

  def thread_start(self):
    p = threading.Thread(name='worker', target=self.start_download)
    #Do UI updates
    self.ui.label_status.setText('Fetching information...')
    #self.listWidget.addItem(prgstr)
    p.start()

  def start_download(self):

        ............
        code to fetch information from web
        ............

        #itm = self.ui.listWidget.addItem(json_data['entries'][0]['title'])
        self.ui.label_status.setText(json_data['entries'][0]['title'])
        item = QtWidgets.QListWidgetItem(self.ui.listWidget)
        item_widget = progress.Ui_Form("It works")
        item.setSizeHint(item_widget.sizeHint())
        self.ui.listWidget.addItem(item)
        self.ui.listWidget.setItemWidget(item,item_widget)


if __name__ == "__main__":
    import sys
    app = QtWidgets.QApplication(sys.argv)
    myapp = MyForm()
    myapp.show()
    sys.exit(app.exec_())

以上运行没有任何错误,但以下代码无法添加任何列表项(自定义小部件),label_status正确填充检索到的数据 - 我在这里缺少什么?

    self.ui.label_status.setText(json_data['entries'][0]['title'])
    item = QtWidgets.QListWidgetItem(self.ui.listWidget)
    item_widget = progress.Ui_Form("It works")
    item.setSizeHint(item_widget.sizeHint())
    self.ui.listWidget.addItem(item)
    self.ui.listWidget.setItemWidget(item,item_widget)

1 个答案:

答案 0 :(得分:0)

您应该只在主GUI线程中的中更新GUI元素。

工作线程应该在完成时发出信号,或者可能定期发出信号,以便可以处理挂起的GUI事件(即通过调用qApp.processEvents())。

如果工作线程生成数据,请使用Queue,以便可以从主线程安全地访问它。

<强>更新

以下是如何将QThread与worker对象一起使用的基本示例(仅限python3)。 GUI线程和工作线程之间的所有通信都是使用信号和插槽完成的(有关详细信息,请参阅Qt文档中的“Signals and Slots Across Threads”。)

import urllib.request
from PyQt4 import QtCore, QtGui

class Worker(QtCore.QObject):
    finished = QtCore.pyqtSignal()
    error = QtCore.pyqtSignal(object)
    dataReady = QtCore.pyqtSignal(object)

    @QtCore.pyqtSlot(str)
    def process(self, url):
        try:
            response = urllib.request.urlopen(url, timeout=20)
            try:
                self.dataReady.emit(response.read())
            finally:
                response.close()
        except urllib.error.URLError as exception:
            self.error.emit(exception.reason)
        except BaseException as exception:
            self.error.emit(str(exception))
        finally:
            self.finished.emit()

class Window(QtGui.QWidget):
    downloadRequest = QtCore.pyqtSignal(str)

    def __init__(self):
        QtGui.QWidget.__init__(self)
        self.viewer = QtGui.QPlainTextEdit(self)
        self.edit = QtGui.QLineEdit(self)
        self.edit.setFocus()
        self.button = QtGui.QPushButton('Download', self)
        self.button.clicked.connect(self.handleButton)
        layout = QtGui.QVBoxLayout(self)
        layout.addWidget(self.viewer)
        layout.addWidget(self.edit)
        layout.addWidget(self.button)
        # worker must not have a parent
        self._worker = Worker()
        self._thread = QtCore.QThread(self)
        self._worker.moveToThread(self._thread)
        self._worker.finished.connect(self._thread.quit)
        self._worker.error.connect(self.handleError)
        self._worker.dataReady.connect(self.handleDataReady)
        self.downloadRequest.connect(self._worker.process)

    def handleButton(self):
        url = self.edit.text().strip()
        if url and not self._thread.isRunning():
            self.viewer.clear()
            self._thread.start()
            # safely communicate with worker via signal
            self.downloadRequest.emit(url)

    def handleError(self, reason):
        self.viewer.clear()
        print('ERROR:', reason)

    def handleDataReady(self, data):
        self.viewer.setPlainText(data.decode('utf-8'))

    def closeEvent(self, event):
        if self._thread.isRunning():
            # Qt will complain if thread is still
            # running, so quit it before closing
            self._thread.quit()
            self._thread.wait()
        QtGui.QWidget.closeEvent(self, event)

if __name__ == '__main__':

    import sys
    app = QtGui.QApplication(sys.argv)
    window = Window()
    window.edit.setText('http://stackoverflow.com')
    window.setGeometry(500, 300, 300, 300)
    window.show()
    sys.exit(app.exec_())