简体   繁体   中英

PyQt5 and MySQL GUI Program Crashing

So, I made a GUI using PyQt5 for a database form that allows you to perform different operations on the database. There are four buttons: Select, Insert, Delete, General View and Clear. Clear simply wipes/clears out the displayed data in the table of the GUI.

Each of select, insert, delete and general view, when pressed, are supposed to run their respected functions to which they are connected. Within the functions, they connect to the database, run the queries and display the result in the table.

However, when I run the GUI and press a button such as General View for example, the program stops running and crashes and I get no tracebacks or error reports in the IDLE Shell too.

My code is below:

from PyQt5 import QtCore, QtGui, QtWidgets
import mysql.connector as mc
from PyQt5.QtWidgets import QTableWidgetItem

class Ui_MainWindow(object):
    def setupUi(self, MainWindow):
        MainWindow.setObjectName("MainWindow")
        MainWindow.resize(538, 487)
        self.centralwidget = QtWidgets.QWidget(MainWindow)
        self.centralwidget.setObjectName("centralwidget")
        self.verticalLayout = QtWidgets.QVBoxLayout(self.centralwidget)
        self.verticalLayout.setObjectName("verticalLayout")
        self.gridLayout_2 = QtWidgets.QGridLayout()
        self.gridLayout_2.setObjectName("gridLayout_2")
        self.lineCMS = QtWidgets.QLineEdit(self.centralwidget)
        self.lineCMS.setObjectName("lineCMS")
        self.gridLayout_2.addWidget(self.lineCMS, 0, 1, 1, 1)
        self.lineRoom = QtWidgets.QLineEdit(self.centralwidget)
        self.lineRoom.setObjectName("lineRoom")
        self.gridLayout_2.addWidget(self.lineRoom, 3, 1, 1, 1)
        self.CMS = QtWidgets.QLabel(self.centralwidget)
        self.CMS.setObjectName("CMS")
        self.gridLayout_2.addWidget(self.CMS, 0, 0, 1, 1)
        self.lineName = QtWidgets.QLineEdit(self.centralwidget)
        self.lineName.setObjectName("lineName")
        self.gridLayout_2.addWidget(self.lineName, 1, 1, 1, 1)
        self.Room = QtWidgets.QLabel(self.centralwidget)
        self.Room.setObjectName("Room")
        self.gridLayout_2.addWidget(self.Room, 3, 0, 1, 1)
        self.Department = QtWidgets.QLabel(self.centralwidget)
        self.Department.setObjectName("Department")
        self.gridLayout_2.addWidget(self.Department, 2, 0, 1, 1)
        self.Name = QtWidgets.QLabel(self.centralwidget)
        self.Name.setObjectName("Name")
        self.gridLayout_2.addWidget(self.Name, 1, 0, 1, 1)
        self.lineDepartment = QtWidgets.QLineEdit(self.centralwidget)
        self.lineDepartment.setObjectName("lineDepartment")
        self.gridLayout_2.addWidget(self.lineDepartment, 2, 1, 1, 1)
        self.lineDOB = QtWidgets.QLineEdit(self.centralwidget)
        self.lineDOB.setObjectName("lineDOB")
        self.gridLayout_2.addWidget(self.lineDOB, 4, 1, 1, 1)
        self.DOB = QtWidgets.QLabel(self.centralwidget)
        self.DOB.setObjectName("DOB")
        self.gridLayout_2.addWidget(self.DOB, 4, 0, 1, 1)
        self.verticalLayout.addLayout(self.gridLayout_2)
        self.horizontalLayout = QtWidgets.QHBoxLayout()
        self.horizontalLayout.setObjectName("horizontalLayout")
        self.Select = QtWidgets.QPushButton(self.centralwidget)
        self.Select.setObjectName("Select")
        self.horizontalLayout.addWidget(self.Select)
        self.line = QtWidgets.QFrame(self.centralwidget)
        self.line.setFrameShape(QtWidgets.QFrame.VLine)
        self.line.setFrameShadow(QtWidgets.QFrame.Sunken)
        self.line.setObjectName("line")
        self.horizontalLayout.addWidget(self.line)
        self.Insert = QtWidgets.QPushButton(self.centralwidget)
        self.Insert.setObjectName("Insert")
        self.horizontalLayout.addWidget(self.Insert)
        self.line_2 = QtWidgets.QFrame(self.centralwidget)
        self.line_2.setFrameShape(QtWidgets.QFrame.VLine)
        self.line_2.setFrameShadow(QtWidgets.QFrame.Sunken)
        self.line_2.setObjectName("line_2")
        self.horizontalLayout.addWidget(self.line_2)
        self.Delete = QtWidgets.QPushButton(self.centralwidget)
        self.Delete.setObjectName("Delete")
        self.horizontalLayout.addWidget(self.Delete)
        self.line_3 = QtWidgets.QFrame(self.centralwidget)
        self.line_3.setFrameShape(QtWidgets.QFrame.VLine)
        self.line_3.setFrameShadow(QtWidgets.QFrame.Sunken)
        self.line_3.setObjectName("line_3")
        self.horizontalLayout.addWidget(self.line_3)
        self.Clear = QtWidgets.QPushButton(self.centralwidget)
        self.Clear.setObjectName("Clear")
        self.horizontalLayout.addWidget(self.Clear)
        self.verticalLayout.addLayout(self.horizontalLayout)
        self.table = QtWidgets.QTableWidget(self.centralwidget)
        self.table.setObjectName("table")
        self.table.setColumnCount(5)
        self.table.setRowCount(0)
        item = QtWidgets.QTableWidgetItem()
        self.table.setHorizontalHeaderItem(0, item)
        item = QtWidgets.QTableWidgetItem()
        self.table.setHorizontalHeaderItem(1, item)
        item = QtWidgets.QTableWidgetItem()
        self.table.setHorizontalHeaderItem(2, item)
        item = QtWidgets.QTableWidgetItem()
        self.table.setHorizontalHeaderItem(3, item)
        item = QtWidgets.QTableWidgetItem()
        self.table.setHorizontalHeaderItem(4, item)
        self.verticalLayout.addWidget(self.table)
        self.horizontalLayout_2 = QtWidgets.QHBoxLayout()
        self.horizontalLayout_2.setObjectName("horizontalLayout_2")
        self.verticalLayout.addLayout(self.horizontalLayout_2)
        self.generalView = QtWidgets.QPushButton(self.centralwidget)
        self.generalView.setObjectName("generalView")
        self.verticalLayout.addWidget(self.generalView)
        MainWindow.setCentralWidget(self.centralwidget)
        self.menubar = QtWidgets.QMenuBar(MainWindow)
        self.menubar.setGeometry(QtCore.QRect(0, 0, 538, 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)
        self.Select.clicked.connect(self.select)
        self.Insert.clicked.connect(self.insert)
        self.Delete.clicked.connect(self.delete)
        self.generalView.clicked.connect(self.view)
        self.Clear.clicked.connect(self.clear)
    def retranslateUi(self, MainWindow):
        _translate = QtCore.QCoreApplication.translate
        MainWindow.setWindowTitle(_translate("MainWindow", "Student"))
        self.CMS.setText(_translate("MainWindow", "CMS"))
        self.Room.setText(_translate("MainWindow", "Room No."))
        self.Department.setText(_translate("MainWindow", "Department"))
        self.Name.setText(_translate("MainWindow", "Name"))
        self.DOB.setText(_translate("MainWindow", "Date of Birth"))
        self.Select.setText(_translate("MainWindow", "SELECT"))
        self.Insert.setText(_translate("MainWindow", "INSERT"))
        self.Delete.setText(_translate("MainWindow", "DELETE"))
        self.Clear.setText(_translate("MainWindow", "CLEAR"))
        item = self.table.horizontalHeaderItem(0)
        item.setText(_translate("MainWindow", "CMS"))
        item = self.table.horizontalHeaderItem(1)
        item.setText(_translate("MainWindow", "Name"))
        item = self.table.horizontalHeaderItem(2)
        item.setText(_translate("MainWindow", "Department"))
        item = self.table.horizontalHeaderItem(3)
        item.setText(_translate("MainWindow", "Room No."))
        item = self.table.horizontalHeaderItem(4)
        item.setText(_translate("MainWindow", "Date of Birth"))
        self.generalView.setText(_translate("MainWindow", "GENERAL VIEW"))
        
    def select():
        try:
            self.table.setRowCount(0)
            QCMS = self.lineCMS.text() + "%"
            QDepartment = self.lineDepartment.text() + "%"
            QName = self.lineName.text() + "%"
            QRoom = self.lineRoom.text() + "%"
            QDOB = self.lineDOB.text() + "%"

            mydb = mc.connect(host="localhost",user="root",password="",database="sakila")
            mycursor = mydb.cursor()
            selectQuery = "SELECT * FROM Student WHERE CMS like '{}' and Department like '{}' and Name like '{}' and RoomNo like '{}' and DateOfBirth like '{}'".format(QCMS, QDepartment, QName, QRoom, QDOB)
            mycursor.execute(selectQuery)
            queryResult = mycursor.fetchall()
            for row_number, row_data in enumerate(queryResult):
                self.table.insertRow(row_number)
                for column_number, data in enumerate(row_data):
                    self.table.setItem(row_number,column_number,QTableWidgetItem(str(data)))
            
        except mc.Error as e:
            print("Error!")

    def insert():
        try:
            self.table.setRowCount(0)
            QCMS = self.lineCMS.text() + "%"
            QDepartment = self.lineDepartment.text() + "%"
            QName = self.lineName.text() + "%"
            QRoom = self.lineRoom.text() + "%"
            QDOB = self.lineDOB.text() + "%"

            mydb = mc.connect(host="localhost",user="root",password="",database="sakila")
            mycursor = mydb.cursor()
            insertQuery = "INSERT INTO Student Values({}, '{}', '{}', {}, '{}')".format(QCMS, QName, QDepartment, QRoom, QDOB)
            triggerQuery = "SELECT * FROM Student WHERE CMS like '{}' and Department like '{}' and Name like '{}' and RoomNo like '{}' and DateOfBirth like '{}'".format(QCMS, QDepartment, QName, QRoom, QDOB)
            mycursor.execute(insertQuery)
            insertResult = mycursor.fetchall()
            mycursor.execute(triggerQuery)
            triggerResult = mycursor.fetchall()
            for row_number, row_data in enumerate(triggerResult):
                self.table.insertRow(row_number)
                for column_number, data in enumerate(row_data):
                    self.table.setItem(row_number,column_number,QTableWidgetItem(str(data)))
            
        except mc.Error as e:
            print("Error!")

    def delete():
        try:
            self.table.setRowCount(0)
            QCMS = self.lineCMS.text() + "%"
            QDepartment = self.lineDepartment.text() + "%"
            QName = self.lineName.text() + "%"
            QRoom = self.lineRoom.text() + "%"
            QDOB = self.lineDOB.text() + "%"

            mydb = mc.connect(host="localhost",user="root",password="",database="sakila")
            mycursor = mydb.cursor()
            deleteQuery = "DELETE FROM Student WHERE CMS like '{}' and Department like '{}' and Name like '{}' and RoomNo like '{}' and DateOfBirth like '{}'".format(QCMS, QDepartment, QName, QRoom, QDOB)
            triggerQuery = "SELECT * FROM Student WHERE CMS like '{}' and Department like '{}' and Name like '{}' and RoomNo like '{}' and DateOfBirth like '{}'".format(QCMS, QDepartment, QName, QRoom, QDOB)
            mycursor.execute(deleteQuery)
            deleteResult = mycursor.fetchall()
            mycursor.execute(triggerQuery)
            triggerResult = mycursor.fetchall()
            for row_number, row_data in enumerate(triggerResult):
                self.table.insertRow(row_number)
                for column_number, data in enumerate(row_data):
                    self.table.setItem(row_number,column_number,QTableWidgetItem(str(data)))
            
        except mc.Error as e:
            print("Error!")

    def view():
        try:
            self.table.setRowCount(0)
            

            mydb = mc.connect(host="localhost",user="root",password="",database="sakila")
            mycursor = mydb.cursor()
            query = "SELECT * FROM Student"
            mycursor.execute(query)
            queryResult = mycursor.fetchall()
            
            
            for row_number, row_data in enumerate(queryResult):
                self.table.insertRow(row_number)
                for column_number, data in enumerate(row_data):
                    self.table.setItem(row_number,column_number,QTableWidgetItem(str(data)))
            
        except mc.Error as e:
            print("Error!")


    def clear():
        self.table.setRowCount(0)
    


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_())

Can't think of anything related to the situation I am in right now.

My best guess is it's because you aren't using any threads in this program.

When you click a button, the main event loop that is processing the GUI events (mouse events, window updating) is locked up because it's working on your code for the button. This makes the GUI appear to be frozen or crashed (not responding) because the event loop is busy with something else.

There's some concepts needed to use PyQt5, Threading and Signals and Slots are two of the core concepts.

A Thread is code that runs in parallel with your main code, and shares the same code space so they have access to similar variables.

Signals and Slots are a way to pass information between the main thread and other threads that are being run.

In your code you will want to create a thread class like this:

class ViewThread(QThread):
    # This is a signal when it is emitted it will send along data of type 'list' 
    query_finished_signal = pyqtSignal(list)
    
    def __init__(self):
        super().__init__()

    def run(self):
        # Here you would put the code to execute your SQL and 
        # get the data returned from the SQL fetch statement put in a 
        # list of lists called query_results.
        # NOTE: You'll need to make the instances of your SQL cursor here so 
        # everything is within the scope of this thread class.
        
        # Here we emit the signal, which will emit query_results to our 
        # connected slot.
        self.query_finished_signal.emit(query_results)
        self.finished.emit() 

Then in your Ui_MainWindow class you'll make your view method kick off the thread instead and then have a new method that I called set_view, which does only the operations the main event loop is responsible for updating.

def __init__(self):

    # vvv keep everything else just add this vvv
    
    #Create an instance of the custom QThread
    self.view_thread = ViewThread()
    
    #This is where we connect the signal, when it is emitted it will send the data to the method  self.set_view
    self.view_thread.query_finished_signal.connect(self.set_view) 

def view(self):
    try:      
        self.view_thread.start() 
    except mc.Error as e:
        print("Error!") 
        
def set_view(self, queryResult=None): #queryResult is a slot which will contain the data emitted from the signal connected to this method
    if queryResult:
        self.table.setRowCount(0)
        for row_number, row_data in enumerate(queryResult):
            self.table.insertRow(row_number)
            for column_number, data in enumerate(row_data):
                self.table.setItem(row_number,column_number,QTableWidgetItem(str(data)))

The point of all this is so your event loop only needs to update the table with the data it's given which is relatively fast, but it doesn't need to freeze the GUI while you go and collect the information. Another note, GUI widgets cannot be altered by threads, so doing something like self.lineedit.setText("Hello") would not work in a thread, it needs to be done by the main thread.

NOTE: This code is meant to be an example only, not to be copy and pasted as working code.

I would suggest in your current GUI, click a button and if the GUI freezes up just wait it out and see if it eventually responds again. That's how you'll know you need to implement this solution. I hope this helps:)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM