How do I keep my app updated with data from my mysql database?

1

I have read a lot, researched in several pages but I have not been able to find an answer to my question.
I have an application made with PyQt, this same app uploads data to a mysql database and downloads them when you indicate it. But the application works with several clients and I'm trying to find a way to keep each client updated with the data in the database.

Try to make a QThread that every time is making a query to see if there is new data but it hangs for some reason (I am new to threads).

class Example(QThread):
    def __init__(self)
        self.bool = True

    def start:
        self.bool = True
        while self.bool:
            print("hola mundo!")

    def stop:
        self.bool = False

It's an example of how I'm using threads, the issue is that when I call Example (). start () I hang all the rest of the application, it freezes and only prints.

    
asked by Christopher Vivar Vivar 23.05.2017 в 01:30
source

2 answers

2

According to the developers of Qt it is not recommended to derive directly of QThread and overwrite their methods exactly as you are doing. Although this practice is quite widespread, there are reasons to avoid it as much as possible. There are many alternatives but, perhaps the most direct, is to create a subclass of QObject that will act as a worker and use the moveToThread method.

Remember that you can and should use signals to communicate threads and that you should never directly modify a widget from a thread . The graphical interface should only be drawn and modified from the main thread. If a component of the interface needs to be updated from a secondary thread, signals or any other method that is thread-safe (such as queues) will be used.

On the other hand, remember that QThread does not run on an independent thread itself, only its run method does it . QThread is just a container, you have to be careful with this, any blocking method not directly called by run will block the interface.

An example of a complete application using Qthread without using a subclass of it:

import sys
import time
from PyQt5.QtWidgets import QApplication, QPushButton,  QMainWindow,  QLabel
from PyQt5.QtCore import  QObject,  QThread,  QMutex,  pyqtSignal,  QRect

class Worker(QObject):
    candado = QMutex()
    resultado = pyqtSignal(str)
    iniciado = pyqtSignal()
    terminado = pyqtSignal(bool)

    def __init__(self):
        super(Worker,  self).__init__()
        self._trabajando = False
        self._abortar = False

    def iniciar(self):
        self.candado.lock()
        self._trabajando = True
        self._abortar = False
        self.candado.unlock()
        self.iniciado.emit()

    def abortar(self):
        self.candado.lock()
        if self._trabajando:
            self._abortar = True
        self.candado.unlock()

    def procesar(self):
        for n in range(60):
            self.candado.lock()
            abortar = self._abortar
            self.candado.unlock()
            self.resultado.emit('Contador desde hilo: {}'.format(n) )
            time.sleep(0.5)
            if abortar:
                self.resultado.emit('Trabajo en hilo abortado')
                break
        else:
            self.resultado.emit('Trabajo completado')

        self.candado.lock()
        self._trabajando = False
        self.candado.unlock()
        self.terminado.emit(True)


class App(QMainWindow):
    def __init__(self):
        super().__init__()
        self.title = 'Ejemplo QThread'
        self.left = 100
        self.top = 100
        self.width = 400
        self.height = 300
        self.setWindowTitle(self.title)
        self.setGeometry(self.left, self.top, self.width, self.height)

        boton_iniciar = QPushButton('Iniciar', self)
        boton_iniciar.move(100,120) 
        boton_iniciar.clicked.connect(self.iniciar_hilo)

        boton_detener = QPushButton('Detener', self)
        boton_detener.move(210,120) 
        boton_detener.clicked.connect(self.detener_hilo)

        self.etiqueta = QLabel(self)
        self.etiqueta.setGeometry(QRect(140, 180,  400, 50))
        self.etiqueta.setText('-------------------------')

        self.hilo = QThread()
        self.worker = Worker()
        self.worker.moveToThread(self.hilo)
        self.worker.resultado.connect(self.actualizar_etiqueta)
        self.worker.iniciado.connect(self.hilo.start)
        self.hilo.started.connect(self.worker.procesar)
        self.worker.terminado.connect(self.hilo.quit)

    def iniciar_hilo(self):
        self.worker.iniciar()

    def detener_hilo(self):
        self.worker.abortar()

    def actualizar_etiqueta(self,  datos):
        self.etiqueta.setText(datos)


if __name__ == '__main__':
    app = QApplication(sys.argv)
    ventana = App()
    ventana.show()
    sys.exit(app.exec_())

The application shows an interface with two buttons and a label. One button allows you to throw a thread, the other stops it. The label shows the information processed in the thread.

The process that runs on the thread is just a simple example, a counter from 0 to 59 with half a second of waiting between each number.

The process can be stopped at any time. Qt5 (pyqt5) is used but the idea is valid for Qt4 (pyqt4), you just have to change the import in principle:

import sys
import time
from PyQt4.QtGui import QApplication, QPushButton,  QMainWindow,  QLabel
from PyQt4.QtCore import  QObject,  QThread,  QMutex,  pyqtSignal,  QRect

A padlock is used to modify and access the control variables in the thread. Possibly it is not necessary in this case in which, if I am not mistaken, they are atomic operations. However, it is not bad practice to protect variables, memory, files, etc. shared between threads.

Capture of the app worked:

    
answered by 23.05.2017 / 23:22
source
1

Basically you need the run method and __init__ , the basic architecture of a class inherited from QThread should be more or less like this:

class mythread(QtCore.QThread):
    def __init__(self,parent:
        QtCore.QThread.__init__(self,parent) 

    def run(self):
        # Lógica del thread
        self.emit(QtCore.SIGNAL("mythread.start()"),"arrancamos")
        pass

To invoke it from the interface class

self.t=mythread(self)

And to connect the signals of the thread and we can process them, we connect the signal to any method, for example: thread_start

QtCore.QObject.connect(self.t, QtCore.SIGNAL("mythread.start()"), self.thread_start)

And obviously, you have to create the thread_start method in the interface class, and process the messages, to inform them, or move a progress bar

    
answered by 23.05.2017 в 16:17