如何修复 PyQt5 GUI 冻结
Posted
技术标签:
【中文标题】如何修复 PyQt5 GUI 冻结【英文标题】:How to fix PyQt5 GUI freezing 【发布时间】:2020-05-17 12:34:49 【问题描述】:我使用 QtDesigner 为我的 python 应用程序创建了一个简单的 GUI。当我尝试运行我的代码时,它可以正常工作,但我现在的问题是,当我尝试使用 GUI 上的转换按钮运行我的代码时,界面冻结或无响应,并且只有在完成执行代码后才会响应。现在我的问题是如何解决这个问题?
这是我的代码:
# -*- coding: utf-8 -*-
# Form implementation generated from reading ui file 'GUI.ui'
#
# Created by: PyQt5 UI code generator 5.13.2
#
# WARNING! All changes made in this file will be lost!
from PyQt5 import QtCore, QtGui, QtWidgets
from PyQt5.QtWidgets import QFileDialog, QMessageBox
import sys
import excel2img
import openpyxl as xl
class Ui_MainWindow(object):
def setupUi(self, MainWindow):
MainWindow.setObjectName("MainWindow")
MainWindow.resize(361, 303)
self.centralwidget = QtWidgets.QWidget(MainWindow)
self.centralwidget.setObjectName("centralwidget")
self.btn_exit = QtWidgets.QPushButton(self.centralwidget)
self.btn_exit.setGeometry(QtCore.QRect(270, 240, 75, 23))
self.btn_exit.setObjectName("btn_exit")
self.btn_openfile = QtWidgets.QPushButton(self.centralwidget)
self.btn_openfile.setGeometry(QtCore.QRect(40, 30, 75, 23))
self.btn_openfile.setObjectName("btn_openfile")
self.btn_convert = QtWidgets.QPushButton(self.centralwidget)
self.btn_convert.setGeometry(QtCore.QRect(40, 60, 75, 23))
self.btn_convert.setObjectName("btn_convert")
#self.btn_send = QtWidgets.QPushButton(self.centralwidget)
#self.btn_send.setGeometry(QtCore.QRect(40, 90, 75, 23))
#self.btn_send.setObjectName("btn_send")
MainWindow.setCentralWidget(self.centralwidget)
self.menubar = QtWidgets.QMenuBar(MainWindow)
self.menubar.setGeometry(QtCore.QRect(0, 0, 361, 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)
#Widgets
self.btn_openfile.clicked.connect(self.openfile)
self.btn_exit.clicked.connect(self.exit)
self.btn_convert.clicked.connect(self.convert)
#Widgets
def retranslateUi(self, MainWindow):
_translate = QtCore.QCoreApplication.translate
MainWindow.setWindowTitle(_translate("MainWindow", "MainWindow"))
self.btn_exit.setText(_translate("MainWindow", "Exit"))
self.btn_openfile.setText(_translate("MainWindow", "Open File"))
self.btn_convert.setText(_translate("MainWindow", "Convert File"))
#self.btn_send.setText(_translate("MainWindow", "Send Payroll"))
#My Functons
fileName = ''
@classmethod
def openfile(cls):
fname = QFileDialog.getOpenFileName(None, "Open File", "", "Excel files (*.xlsx *xls)")
cls.fileName = fname[0]
def exit(self):
quit_msg = "Are you sure you want to exit the program?"
reply = QMessageBox.question(None, 'Prompt',
quit_msg, QMessageBox.Yes | QMessageBox.No)
if reply == QMessageBox.Yes:
sys.exit()
def convert(self):
wb = xl.load_workbook(self.fileName, read_only=True)
ws = wb.active
a, b = 2, 2
x, y = 1, 44
z, w = 1, 44
max_row = ws.max_row
temp = 0
loop_num = 0
while temp < max_row:
temp += 52
loop_num += 1
print(loop_num)
for i in range(loop_num * 2):
if i % 2 == 0:
cell = "Sheet1!A:F".format(x,y)
ccell = 'B'.format(a)
var = ws[ccell].value
a += 52
x += 52
y += 52
else:
cell = "Sheet1!H:M".format(z,w)
ccell = 'I'.format(b)
var = ws[ccell].value
b += 52
z += 52
w += 52
name = '.png'.format(var)
excel2img.export_img(self.fileName, name, "", cell )
print('generating image '.format(i + 1))
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_())
【问题讨论】:
不要修改 Qt Designer 生成的代码,而是创建另一个继承自相应小部件的类并使用初始类来填充它。 【参考方案1】:首先你不应该修改Qt Designer生成的类,所以在应用我的解决方案之前你必须重新生成.py,所以你必须再次使用pyuic:
pyuic5 your_ui.ui -o design.py -x
考虑到上述情况,问题在于您有一个耗时的任务阻塞了事件循环,从而阻止它完成其工作,例如对用户事件做出反应。一个可能的解决方案是使用线程:
import sys
import threading
from PyQt5 import QtCore, QtGui, QtWidgets
import excel2img
import openpyxl as xl
from design import Ui_MainWindow
def task(fileName):
wb = xl.load_workbook(fileName, read_only=True)
ws = wb.active
a, b = 2, 2
x, y = 1, 44
z, w = 1, 44
max_row = ws.max_row
temp = 0
loop_num = 0
while temp < max_row:
temp += 52
loop_num += 1
print(loop_num)
for i in range(loop_num * 2):
if i % 2 == 0:
cell = "Sheet1!A:F".format(x, y)
ccell = "B".format(a)
var = ws[ccell].value
a += 52
x += 52
y += 52
else:
cell = "Sheet1!H:M".format(z, w)
ccell = "I".format(b)
var = ws[ccell].value
b += 52
z += 52
w += 52
name = ".png".format(var)
excel2img.export_img(fileName, name, "", cell)
print("generating image ".format(i + 1))
class MainWindow(QtWidgets.Ui_MainWindow, Ui_MainWindow):
def __init__(self, parent=None):
super().__init__(parent)
self.setupUi(self)
# Widgets
self.btn_openfile.clicked.connect(self.openfile)
self.btn_exit.clicked.connect(self.exit)
self.btn_convert.clicked.connect(self.convert)
fileName = ""
@classmethod
def openfile(cls):
cls.fileName, _ = QtWidgets.QFileDialog.getOpenFileName(
None, "Open File", "", "Excel files (*.xlsx *xls)"
)
def exit(self):
quit_msg = "Are you sure you want to exit the program?"
reply = QtWidgets.QMessageBox.question(
None,
"Prompt",
quit_msg,
QtWidgets.QMessageBox.Yes | QtWidgets.QMessageBox.No,
)
if reply == QtWidgets.QMessageBox.Yes:
sys.exit()
def convert(self):
threading.Thread(target=task, args=(self.fileName,), daemon=True).start()
if __name__ == "__main__":
app = QtWidgets.QApplication(sys.argv)
MainWindow = QtWidgets.QMainWindow()
ui = Ui_MainWindow()
ui.setupUi(MainWindow)
MainWindow.show()
sys.exit(app.exec_())
【讨论】:
【参考方案2】:您应该在 for 循环中调用 QtCore.QCoreApplication.processEvents() 以使 Qt 的事件循环继续传入事件(来自键盘或鼠标)
希望我有帮助
【讨论】:
感谢您的回答。我可以通过添加您提到的代码来稍微移动 GUI,但它看起来仍然冻结,我不希望这种情况发生,有什么办法吗?以上是关于如何修复 PyQt5 GUI 冻结的主要内容,如果未能解决你的问题,请参考以下文章
PyQt5 在不冻结 GUI 的情况下绘制数据的最佳方法 [关闭]
在 PyQt5 中通过命令行调用外部程序时,有没有办法阻止 GUI 冻结?