我跟着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)
答案 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_())