【问题标题】:gui not responding during process?gui在处理过程中没有响应?
【发布时间】: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_())

【问题讨论】:

    标签: python python-3.x pyqt pyqt5 qtablewidget


    【解决方案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 你用过我最后的代码吗?我已经纠正了这些错误。
    猜你喜欢
    • 1970-01-01
    • 2012-05-05
    • 1970-01-01
    • 2013-06-16
    • 2014-04-22
    • 1970-01-01
    • 1970-01-01
    • 2018-09-03
    相关资源
    最近更新 更多