1

First of all, I want to figure out how to check database status every second. so that the user will able to tell if the database is up or not without even clicking or triggering anything. I've read that this will create a problem as mentioned in the comments here

so here's my minimal reproducible example:

import sys
import os
import shiboken2
from PySide2 import QtCore, QtGui, QtWidgets
from PySide2.QtWidgets import QMainWindow, QFileDialog, QMessageBox, QWidget, QDialog, QProxyStyle
from sqlalchemy import create_engine, inspect

class MyWidget(QtWidgets.QWidget):
    def __init__(self):
        QtWidgets.QWidget.__init__(self)
        self.resize(200, 200)
        self.path = os.path.abspath(os.path.dirname(sys.argv[0]))
        self.button = QtWidgets.QPushButton("Open File")
        self.labelFile = QtWidgets.QLabel("empty")
        self.labelData = QtWidgets.QLabel("None")
        self.layout = QtWidgets.QVBoxLayout()
        self.layout.addWidget(self.button)
        self.layout.addWidget(self.labelFile)
        self.layout.addWidget(self.labelData)
        self.setLayout(self.layout)
        self.button.clicked.connect(self.open_file)
        self.process = None
        self.CreateEngine = CreateEngine(self)
        self.CreateEngine.result.connect(self.start_timer)
        self.CreateEngine.start()

    def open_file(self):
        x = QFileDialog.getOpenFileName(self,"Just To Spice This Code",self.path,"CSV Files (*.csv)")
        self.labelFile.setText(x[0]) #just to check that GUI doesn't freeze

    def start_timer(self,engine): #callback from CreateEngine
        self.timer = QtCore.QTimer(self)
        self.timer.timeout.connect(lambda: self.continuously_check(engine))
        self.timer.start(1000) #check connetion every second, as real-time as possible

    def continuously_check(self,engine): #this gonna get called every second, yes it isn't effective i know       
        self.process = CheckConnection(self,engine)
        self.process.result.connect(self.update_connection_label)
        self.process.start()

    def update_connection_label(self,x): #update connection status on GUI
        self.labelData.setText("DB Status: "+str(x))

    def closeEvent(self,event): #to handle QThread: Destroyed while thread is still running
        print("begin close event")
        if(self.process is not None):
            if(shiboken2.isValid(self.process)): #to check whether the object is deleted. ->
                self.process.wait()             #-> this will get messy when the DB connection is down 
                self.process.quit()            #-> (IMO):since i stack so many CheckConnection objects maybe?
        print("end close event")

class CreateEngine(QtCore.QThread): #creating engine on seperate thread so that it wont block GUI
    result = QtCore.Signal(object)
    def __init__(self, parent):
        QtCore.QThread.__init__(self, parent)
        self.engine = None
    def run(self):
        self.engine = create_engine('mysql+pymysql://{}:{}@{}:{}/{}'.format("root","","localhost","3306","adex_admin"))
        self.result.emit(self.engine)

class CheckConnection(QtCore.QThread): #constantly called every second, yes its not a good approach ->
    result = QtCore.Signal(str) #-> i wonder how to replace all this with something appropriate
    def __init__(self, parent,engine):
        QtCore.QThread.__init__(self, parent)
        self.engine = engine
    def run(self):
        try:
            self.engine.execute('SELECT 1').fetchall()
            self.result.emit("Connected")
        except:
            self.result.emit("Not Connected")
        self.deleteLater() #somehow this doesn't do it job very well. maybe blocked?
        #-> especially when the connection is busted. this thread gets stuck quite long to finish
if __name__ == "__main__":
    #idk why when you start this without connection it's running really slow on showing the status of DB
    #you must wait like 4 seconds until the connection status is showed up, which is really bad
    #but once it's live. it could read database status really fast
    app = QtWidgets.QApplication(sys.argv)
    widget = MyWidget()
    widget.show()
    sys.exit(app.exec_())

GUI UI I've created this example just to reproduce the same problem I'm facing in my real app. so the problem is that closeEvent takes too long to terminate the checking process and also blocking the GUI. The reason why I create 'closeEvent' is that I had this problem which produce [QThread: Destroyed while thread is still running] when the app is closed.

also, whenever the database isn't reachable it makes the QThread finishes way longer than it should unlike when the database is reachable. but we can retrieve the status pretty much like we want (every second of live DB Status). I also tried a silly approach like this

...
    def continuously_check(self,engine):
        self.process = CheckConnection(self,engine)
        self.process.result.connect(self.update_connection_label)
        self.process.finished.connect(lambda: QtCore.QTimer.singleShot(1000,self.continuously_check))
        self.process.start()
...

hoping that it won't keep creating objects before the thread even finished (ps: obviously this won't work). so what's the best approach when it comes to this? sorry for multiple problems at a time.

greendino
  • 416
  • 3
  • 17
  • so I've tried to do a "non-SQL" exception on purpose (e.g purposely creating error by giving undefined variable) but the thread handles it perfectly fine. so I think it is SQLAlchemy who blocks the thread exception handling – greendino Nov 05 '20 at 01:03

0 Answers0