gui在处理过程中没有响应?

Posted

技术标签:

【中文标题】gui在处理过程中没有响应?【英文标题】:gui not responding during process? 【发布时间】:2018-11-20 10:20:12 【问题描述】:

我有 sql 查询。我的按钮与 sql 连接 - 这需要很长时间。在此期间,我的 GUI 没有响应 - 是否可以让它响应?

QtCore.QCoreApplication.processEvents() 不起作用。

QApp.py

​​>
# -- coding: utf-8 --

from PyQt5 import QtGui, QtWidgets, QtCore
import qdarkstyle
import pyodbc

class MainWindow(QtWidgets.QMainWindow):
    def __init__(self, parent=None):
        super(MainWindow,self).__init__(parent)

        self.main_frame()
        self.center() #center frame
        self.layout_init() #widgets layout

    def main_frame(self):
        ### actions on meenubar
        exitAct = QtWidgets.QAction('&Exit', self)
        exitAct.setShortcut('Ctrl+Q')
        exitAct.setStatusTip('Exit application')
        exitAct.triggered.connect(self.close)
        self.statusBar()

        moreinfo = QtWidgets.QAction('&Help',self)
        moreinfo.setStatusTip('More information')
        moreinfo.triggered.connect(self.information)
        self.statusBar()

        ### menubar
        menubar = self.menuBar()
        fileMenu = menubar.addMenu('&File')
        fileMenu.addAction(exitAct)
        fileMenu = menubar.addMenu('&Help')
        fileMenu.addAction(moreinfo)


        ### basic geometry and color
        self.setWindowTitle('Villain')
        self.setWindowIcon(QtGui.QIcon('dc1.png'))
        self.setStyleSheet((qdarkstyle.load_stylesheet_pyqt5()))


    def layout_init(self):
        self.grid = QtWidgets.QGridLayout()
        central_widget = QtWidgets.QWidget()
        self.setCentralWidget(central_widget)

        ### widgets
        self.tablewidget = QtWidgets.QTableWidget()
        self.tablewidget.setColumnCount(4)
        self.tablewidget.setHorizontalHeaderLabels(["FileNameTransformed", "OrderItemCode", "Imported", "Row"])
        self.tablewidget.horizontalHeader().setDefaultAlignment(QtCore.Qt.AlignLeft | QtCore.Qt.AlignVCenter)
        self.tablewidget.horizontalHeader().setStretchLastSection(True)
        self.tablewidget.resizeColumnsToContents()
        self.tablewidget.setSelectionMode(QtWidgets.QAbstractItemView.SingleSelection)
        self.tablewidget.setEditTriggers(QtWidgets.QAbstractItemView.NoEditTriggers)
        #self.tablewidget.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOn)

        self.textbox = QtWidgets.QLineEdit()
        self.textbox.setPlaceholderText('IMEI')
        self.textbox.setEnabled(True)
        regexp = QtCore.QRegExp('^(0\d+|[1-9][0-9]+)$') #IMEI = only int
        self.textbox.setValidator(QtGui.QRegExpValidator(regexp))

        self.pb = QtWidgets.QPushButton(self.tr("Run process"))
        self.pb.setDisabled(True)
        self.textbox.textChanged.connect(self.disableButton)
        self.pb.clicked.connect(self.on_clicked_pb)


        self.clearbutton = QtWidgets.QPushButton(self.tr("Clear all"))
        self.clearbutton.setDisabled(True)
        self.clearbutton.clicked.connect(self.on_clicked_clear)

        ### make vidgets alive
        self.centralWidget().setLayout(self.grid)
        self.grid.addWidget(self.textbox)
        self.grid.addWidget(self.tablewidget)
        self.grid.addWidget(self.pb)
        self.grid.addWidget(self.clearbutton)


    ### center main window
    def center(self):
        qr = self.frameGeometry()
        cp = QtWidgets.QDesktopWidget().availableGeometry().center()
        qr.moveCenter(cp)
        self.move(qr.topLeft())



    def information(self):
        QtWidgets.QMessageBox.information(self,'Information','Version: 1.19.11.18\n'\
                                                             'This is the prototype of the application\n\n'\
                                                             'Please, contact karol.chojnowski@digitalcaregroup.com for comments and suggestions\n\n'\
                                          'Digital Care - Data Processing Team')

    def disableButton(self):
        if len(self.textbox.text())> 0:
            self.pb.setDisabled(False)
            self.clearbutton.setDisabled(False)
        else:
            self.pb.setDisabled(True)
            self.clearbutton.setDisabled(True)


    ### run process button
    @QtCore.pyqtSlot()
    def on_clicked_pb(self):
        if len(self.textbox.text()) == 0:
            pass
        else:
            self.sql_query()


    ### clear all
    @QtCore.pyqtSlot()
    def on_clicked_clear(self):
        if len(self.textbox.text())> 0:
            self.textbox.clear()
            self.tablewidget.setRowCount(0)
            self.tablewidget.setColumnWidth(3, 200)

    def setCredentials(self, credentials):
        self._credentials = credentials

    def sql_query(self):
        ser = "10.96.6.14"
        base = "PROD_WAREX2"
        username, pwd = self._credentials

        QtCore.QCoreApplication.processEvents()
        try:
            self.connection = pyodbc.connect(driver='SQL Server', server=ser, database=base,
                         user=username, password=pwd)
            cursor = self.connection.cursor()
            self.res = cursor.execute(""" SELECT FI.FileNameTransformed,
                FI.OrderItemCode,
                FIR.Imported,
                FR.Row
                FROM [FileRows] AS FR
                JOIN [FileImportRows] AS FIR ON FR.RowId = FIR.RowId
                JOIN [FileImports] AS FI ON FIR.FileImportId = FI.Id 
                WHERE FR.Row LIKE ? """, ('%' + self.textbox.text() + '%'))
            if not cursor.rowcount:
                QtWidgets.QMessageBox.information(self, 'IMEI', "No items found")
                cursor.close()
                pass
            else:
                self.tablewidget.setRowCount(0)
                for row, form in enumerate(self.res):
                    self.tablewidget.insertRow(row)
                    for column, item in enumerate(form):
                        newitem = QtWidgets.QTableWidgetItem(str(item))
                        self.tablewidget.setItem(row, column, newitem)
                cursor.close()
                self.table_performance()
                self.tablewidget.sortItems(0, order=QtCore.Qt.DescendingOrder)
        except:
            QtWidgets.QMessageBox.warning(self, 'Error', "Something went wrong\n\n"\
                                          "Contact karol.chojnowski@digitalcaregroup.com")
            QtWidgets.QMessageBox.setStyleSheet((qdarkstyle.load_stylesheet_pyqt5()))


    def table_performance(self):
        self.tablewidget.resizeColumnsToContents()
        self.tablewidget.setColumnWidth(3, 2500)
        self.tablewidget.setHorizontalScrollMode(QtWidgets.QAbstractItemView.ScrollPerPixel)

主.py:

恶棍.py

​​>
# -- coding: utf-8 --

import sys
from PyQt5 import QtWidgets,QtGui
from QLogin import LoginDialog
from QApp import MainWindow
import os


def resource_path(relative_path):
if hasattr(sys, '_MEIPASS'):
    return os.path.join(sys._MEIPASS, relative_path)
return os.path.join(os.path.abspath("."), relative_path)


if __name__ == '__main__':

app = QtWidgets.QApplication(sys.argv)

login = LoginDialog()
login.setWindowIcon(QtGui.QIcon(resource_path('dc1.png')))

if login.exec_() != QtWidgets.QDialog.Accepted:
    sys.exit(-1)

window = MainWindow()
window.setWindowIcon(QtGui.QIcon(resource_path('dc1.png')))
window.setGeometry(500, 150, 800, 500)
window.setCredentials(login.credentials()) # <----
window.show()
sys.exit(app.exec_())

【问题讨论】:

【参考方案1】:

processEvents() 的使用在大多数情况下意味着糟糕的设计。如果你有一个繁重的任务,不要在主线程中执行它,在另一个线程中执行它并通过信号或QMetaObject::invokeMethod()将必要的数据发送到主线程(最后一个选项将被使用,因为它不是需要如此多的联系)。

另一方面,您应该只添加一次样式表,如果有新的小部件,它们将作为样式表的基础。

# -- coding: utf-8 --
import threading
from PyQt5 import QtGui, QtWidgets, QtCore
import qdarkstyle
import pyodbc

class MainWindow(QtWidgets.QMainWindow):
    def __init__(self, parent=None):
        super(MainWindow,self).__init__(parent)

        self.main_frame()
        self.center() #center frame
        self.layout_init() #widgets layout

    def main_frame(self):
        ### actions on meenubar
        exitAct = QtWidgets.QAction('&Exit', self, shortcut='Ctrl+Q', statusTip='application')
        exitAct.triggered.connect(self.close)

        moreinfo = QtWidgets.QAction('&Help',self, statusTip='More information')
        moreinfo.triggered.connect(self.information)

        ### menubar
        menubar = self.menuBar()
        fileMenu = menubar.addMenu('&File')
        fileMenu.addAction(exitAct)
        fileMenu = menubar.addMenu('&Help')
        fileMenu.addAction(moreinfo)

        ### basic geometry and color
        self.setWindowTitle('Villain')
        self.setWindowIcon(QtGui.QIcon('dc1.png'))
        self.setStyleSheet((qdarkstyle.load_stylesheet_pyqt5()))

    def layout_init(self):
        central_widget = QtWidgets.QWidget()
        self.setCentralWidget(central_widget)

        ### widgets
        self.tablewidget = QtWidgets.QTableWidget()
        self.tablewidget.setColumnCount(4)
        self.tablewidget.setHorizontalHeaderLabels(["FileNameTransformed", "OrderItemCode", "Imported", "Row"])
        self.tablewidget.horizontalHeader().setDefaultAlignment(QtCore.Qt.AlignLeft | QtCore.Qt.AlignVCenter)
        self.tablewidget.horizontalHeader().setStretchLastSection(True)
        self.tablewidget.resizeColumnsToContents()
        self.tablewidget.setSelectionMode(QtWidgets.QAbstractItemView.SingleSelection)
        self.tablewidget.setEditTriggers(QtWidgets.QAbstractItemView.NoEditTriggers)
        #self.tablewidget.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOn)

        self.textbox = QtWidgets.QLineEdit()
        self.textbox.setPlaceholderText('IMEI')
        self.textbox.setEnabled(True)
        regexp = QtCore.QRegExp('^(0\d+|[1-9][0-9]+)$') #IMEI = only int
        self.textbox.setValidator(QtGui.QRegExpValidator(regexp))

        self.pb = QtWidgets.QPushButton(self.tr("Run process"))
        self.pb.setDisabled(True)
        self.textbox.textChanged.connect(self.disableButton)
        self.pb.clicked.connect(self.on_clicked_pb)

        self.clearbutton = QtWidgets.QPushButton(self.tr("Clear all"))
        self.clearbutton.setDisabled(True)
        self.clearbutton.clicked.connect(self.on_clicked_clear)

        ### make vidgets alive
        grid = QtWidgets.QGridLayout(central_widget)
        grid.addWidget(self.textbox)
        grid.addWidget(self.tablewidget)
        grid.addWidget(self.pb)
        grid.addWidget(self.clearbutton)

        self.table_performance()

    ### center main window
    def center(self):
        qr = self.frameGeometry()
        cp = QtWidgets.QDesktopWidget().availableGeometry().center()
        qr.moveCenter(cp)
        self.move(qr.topLeft())

    def information(self):
        QtWidgets.QMessageBox.information(self,'Information','Version: 1.19.11.18\n'\
                                                             'This is the prototype of the application\n\n'\
                                                             'Please, contact karol.chojnowski@digitalcaregroup.com for comments and suggestions\n\n'\
                                          'Digital Care - Data Processing Team')


    @QtCore.pyqtSlot()
    def disableButton(self):
        val = bool(self.textbox.text())
        self.pb.setDisabled(not val)
        self.clearbutton.setDisabled(not val)

    ### run process button
    @QtCore.pyqtSlot()
    def on_clicked_pb(self):
        if self.textbox.text():
            threading.Thread(target=self.sql_query, daemon=True).start()
    ### clear all
    @QtCore.pyqtSlot()
    def on_clicked_clear(self):
        if self.textbox.text():
            self.textbox.clear()
            self.tablewidget.setRowCount(0)
            self.tablewidget.setColumnWidth(3, 200)

    def setCredentials(self, credentials):
        self._credentials = credentials

    def table_performance(self):
        self.tablewidget.resizeColumnsToContents()
        self.tablewidget.setColumnWidth(3, 2500)
        self.tablewidget.setHorizontalScrollMode(QtWidgets.QAbstractItemView.ScrollPerPixel)

    @QtCore.pyqtSlot(str, str)
    def show_warning(self, title, msg):
        QtWidgets.QMessageBox.information(self, title, msg)

    @QtCore.pyqtSlot()
    def clear_items(self):
        self.tablewidget.setRowCount(0)

    @QtCore.pyqtSlot(int, int, str)
    def add_item(self, row, column, val):
        if row >= self.tablewidget.rowCount():
            self.tablewidget.insertRow(self.tablewidget.rowCount())
        newitem = QtWidgets.QTableWidgetItem(val)
        self.tablewidget.setItem(row, column, newitem)

    @QtCore.pyqtSlot()
    def sort_items(self):
        self.tablewidget.sortItems(0, order=QtCore.Qt.DescendingOrder)

    def sql_query(self):
        ser = "10.96.6.14"
        base = "PROD_WAREX2"
        username, pwd = self._credentials
        try:
            connection = pyodbc.connect(driver='SQL Server', server=ser, database=base,
                         user=username, password=pwd)
            cursor = connection.cursor()
            res = cursor.execute(""" SELECT FI.FileNameTransformed,
                FI.OrderItemCode,
                FIR.Imported,
                FR.Row
                FROM [FileRows] AS FR
                JOIN [FileImportRows] AS FIR ON FR.RowId = FIR.RowId
                JOIN [FileImports] AS FI ON FIR.FileImportId = FI.Id 
                WHERE FR.Row LIKE ? """, ('%' + self.textbox.text() + '%'))
            if not cursor.rowcount:
                QtCore.QMetaObject.invokeMethod(self, "show_warning", 
                    QtCore.Qt.QueuedConnection, 
                    QtCore.Q_ARG(str, 'IMEI'), QtCore.Q_ARG(str, "No items found"))
            else:
                QtCore.QMetaObject.invokeMethod(self, "clear_items", QtCore.Qt.QueuedConnection)
                QtCore.QThread.msleep(10)
                for row, form in enumerate(res):
                    for column, item in enumerate(form):
                        QtCore.QMetaObject.invokeMethod(self, "add_item", 
                            QtCore.Qt.QueuedConnection,
                            QtCore.Q_ARG(int, row), QtCore.Q_ARG(int, column), QtCore.Q_ARG(str, str(item)))
                        QtCore.QThread.msleep(10)
                QtCore.QMetaObject.invokeMethod(self, "sort_items", QtCore.Qt.QueuedConnection)
            cursor.close()
        except:
            QtCore.QMetaObject.invokeMethod(self, "show_warning", 
                    QtCore.Qt.QueuedConnection, 
                    QtCore.Q_ARG(str, 'Error'), QtCore.Q_ARG(str, "Something went wrong\n\n"\
                                          "Contact karol.chojnowski@digitalcaregroup.com"))

【讨论】:

嗯,好像不能完全正常工作。线程有效,但是当我调用sql_query 方法时它有效,然后它使用pycharm 信息Process finished with exit code -1073740791 (0xC0000409) 关闭程序似乎问题出在写入表?在我的主线程中,单击任何内容都会停止工作 sql_query? 我找到并更正了它:self.tablewidget.insertRow(self.tablewidget.rowCount()) 但只有第一列,其余为空。错误:QBasicTimer::start: QBasicTimer can only be used with threads started with QThread QBasicTimer::start: QBasicTimer can only be used with threads started with QThread 哦,我太快了:QMetaObject::invokeMethod: No such method MainWindow::show_warning(QString,QString) Candidates are: show_warning(int) QMetaObject::invokeMethod: No such method MainWindow::show_warning(QString,QString) Candidates are: show_warning(int) Exception in thread Thread-1: Traceback (most recent call last): File "C:\Users\kchojnowski\Documents\Python_projects\GUI Natalia\QApp.py", line 187, in sql_query QtCore.Q_ARG(str, 'IMEI'), QtCore.Q_ARG(str, "No items found")) RuntimeError: QMetaObject.invokeMethod() call failed @Karol357 你用过我最后的代码吗?我已经纠正了这些错误。

以上是关于gui在处理过程中没有响应?的主要内容,如果未能解决你的问题,请参考以下文章

使用 QThread 两次单击按钮 2 后 Pyqt GUI 突然没有响应

错误1053:服务没有及时响应启动或控制请求

经验分享响应式网站项目实操过程中的那些事儿

Python Qt GUI设计入门信号与槽函数

asp.net core启动源码以及监听,到处理请求响应的过程

当阻塞任务与 GUI 相关时,如何保持 PyQt GUI 响应?