PyQt : Manage two MainWindow
Question: i'm trying to manage two MainWindow (MainWindow & AccessWindow) with PyQt for
my RFID ACCESS CONTROL Project. I want to show the first MainWindow all time
(Endless Loop). Then, i want to hide it and show the second MainWindow when
the RFID Reader (who's working on "auto-reading mode") read an RFID Tag. so in
the main python program i have a pseudo "do while" loop (while True: and break
with a condition) to read on serial port the data provided by the reader. Then
i check a DB.. It's not important. So the trigger event is "when the reader
read something). I got some help from another forum and now i have this:

    
    
    # -*- coding: utf-8 -*-
    from PyQt4 import QtCore, QtGui
    import sys, pyodbc, serial
    import os
    import time
    
    #Variables
    Code_Zone = "d"
    
    class MainWindow(QtGui.QWidget):
        def __init__(self, main):
            super(MainWindow, self).__init__()
            self.main = main
            self.grid = QtGui.QGridLayout(self)
            self.welcome = QtGui.QLabel("WELCOME", self)
            self.grid.addWidget(self.welcome, 2, 2, 1, 5)
    
    class AccessWindow(QtGui.QWidget):
        def __init__(self):
            super(AccessWindow, self).__init__()
            self.setMinimumSize(150, 50)
            self.grid = QtGui.QGridLayout(self)
            self.label = QtGui.QLabel(self)
            self.grid.addWidget(self.label, 1, 1, 1, 1)
    
    class Main(object):
        def __init__(self):
            self.accueil = MainWindow(self)
            self.accueil.show()
            self.access = AccessWindow()
    
        def wait(self):
            # RFID READER ENDLESS LOOP
            while 1:
    
                global EPC_Code
                ser = serial.Serial(port='COM6', baudrate=115200)    
                a = ser.read(19).encode('hex')
                if (len(a)==38):
                    EPC_Code = a[14:] 
                    print ('EPC is : ' + EPC_Code)
                    break
                else:
                    continue
            ser.close()
    
    
            self.on_event(EPC_Code)
    
        def on_event(self, data):
            def refresh():
                self.toggle_widget(False)
                self.wait()
            # vérification des données 
            EPC_Code = data
            sql_command = "[Get_Access_RFID] @Code_RFID = '"+EPC_Code+"', @Code_Zone = '"+Code_Zone+"'"  #  STORED PROCEDURE
            db_cursor.execute(sql_command)
            rows = db_cursor.fetchone()
            result= str(rows[0])
            print ("result = " + str(result))
            if result == "True":
                # si OK
                self.access.label.setText('ACCESS GRANTED')
            else:
                # si pas OK
                self.access.label.setText('ACCESS DENIED')
            self.toggle_widget(True)
            QtCore.QTimer.singleShot(2000, refresh)
    
        def toggle_widget(self, b):
            self.accueil.setVisible(not b)
            self.access.setVisible(b)
    
    if __name__=='__main__':
        cnxn = """DRIVER={SQL Server};SERVER=***;PORT=***;UID=***;PWD=***;DATABASE=***"""
        db_connection = pyodbc.connect(cnxn)
        db_cursor = db_connection.cursor()
        print ('Connected TO DB & READY')
        app = QtGui.QApplication(sys.argv)
        main = Main()
        main.wait()
        sys.exit(app.exec_())
    

and now my problem is that the text of the first window doesn't appear when i
run the program but the text of the second window appear when i keep my badge
near the RFID Reader.


Answer: Instead of two `MainWindow`, create one. As content, create two classes which
extend `QtGui.QWidget` called `MainView` and `AccessView`. Instead of
replacing the window, just put the correct view into the window. That way, you
can swap views without opening/closing windows.

If you use a layout, then the window will resize to fit the view.

The next problem is that you block the UI thread which means Qt can't handle
events (like the "paint UI" event). To fix this, you must move the RFID
handling code in a background thread. You can emit signals from this
background thread to update the UI.

Note: You must not call UI code from a thread!! Just emit signals. PyQt's main
loop will see them and process them.

Related:

  * <https://joplaete.wordpress.com/2010/07/21/threading-with-pyqt4/>
  * [Updating GUI elements in MultiThreaded PyQT](http://stackoverflow.com/questions/9957195/updating-gui-elements-in-multithreaded-pyqt), especially the second example using signals. The first example is broken (calling `addItem()` from a thread) is not allowed)

