2017-05-30 3 views
0

Comment faire pour qu'une fenêtre PyQt5 se ferme automatiquement au bout de 30 secondes et que la fenêtre continue à réagir à l'interaction?Comment fermer automatiquement la fenêtre PyQt/PySide à l'aide d'un fil?

Je crée un thread qui dort pendant 30 secondes, puis il appelle la fonction close() de la fenêtre. En ce moment, le code se bloque à self.close():

Exception in thread Thread-1: 
Traceback (most recent call last): 
    File "C:\Users\fredrik\.conda\envs\pysidedev_py27\lib\threading.py", line 801, in __bootstrap_inner 
    self.run() 
    File "C:\Users\fredrik\.conda\envs\pysidedev_py27\lib\threading.py", line 754, in run 
    self.__target(*self.__args, **self.__kwargs) 
    File "C:\Users\fredrik\Desktop\timer.py", line 47, in <lambda> 
    my_win.execute_function_threaded(func=lambda: my_win.auto_close(n=3)) 
    File "C:\Users\fredrik\Desktop\timer.py", line 36, in auto_close 
    self.close() # hangs 
RuntimeError: Internal C++ object (MyWindow) already deleted. 

J'ai essayé aussi de déplacer le filetage de l'objet de la fenêtre mais je rencontre toujours un blocage sur window.close().

Qu'est-ce que je fais mal?

Le code doit fonctionner avec Python 2.7 et 3.5.

import sys 
import time 
from threading import Thread 

try: 
    from PyQt5 import QtWidgets 
except ImportError: 
    try: 
     from PySide2 import QtWidgets 
    except ImportError: 
     try: 
      from PyQt4 import QtGui as QtWidgets 
     except ImportError: 
      try: 
       from PySide import QtGui as QtWidgets 
      except ImportError: 
       print('giving up!') 


class MyWindow(QtWidgets.QMainWindow): 
    """Auto-closing window""" 
    def __init__(self, parent=None): 
     super(MyWindow, self).__init__(parent) 

    def closeEvent(self, event): 
     """Delete object when closed""" 
     self.deleteLater() 

    def auto_close(self, n): 
     """Close self in n seconds""" 
     print('going to sleep') 
     for i in range(n): 
      print('sleeping...') 
      time.sleep(1) 
     print('done sleeping!') 
     self.close() # hangs 

    def execute_function_threaded(self, func): 
     """Run given function in thread""" 
     self.t = Thread(target=func) 
     self.t.start() 
     print('thread started') 

app = QtWidgets.QApplication(sys.argv) 
my_win = MyWindow() 
my_win.show() 
my_win.execute_function_threaded(func=lambda: my_win.auto_close(n=3)) 
sys.exit(app.exec_()) 

S'il vous plaît noter que les importations de liaison Qt sont juste fait comme ça pour rendre ce code courir plus facile sur votre fin;)

+0

J'ai testé le code et il ne bloque pas le widget, vous pourriez commenter que vous avez, la version de PyQt, la version de Python – eyllanesc

+0

@eyllanesc Pas sûr de ce que vous voulez dire, la fenêtre se bloque ici de mon côté de la version Python ou de la liaison Qt/version avec laquelle je suis en train de tester. Par exemple j'ai essayé Python 2.7/PySide, Python 3.5/PyQt5 où la fenêtre se bloque. – fredrik

Répondre

0

Essayez le code suivant au lieu de my_win.execute_function_threaded appel:

class MyWindow(QtWidgets.QMainWindow): 
    """Auto-closing window""" 
    def __init__(self, parent=None): 
     super(MyWindow, self).__init__(parent) 
     QtCore.QTimer.singleShot(30000, self.close) 
0

La raison pour laquelle le code d'origine ne fonctionne pas est qu'il est interdit d'appeler les méthodes de l'interface graphique Qt à partir de threads secondaires (les méthodes Qt ne sont pas sécurisées pour les threads). Il existe plusieurs solutions à ce, comme:

  • utilisant un QTimer (puisque QTimer ne pas utiliser les threads, et juste des files d'attente un événement futur dans la boucle d'événement Qt qui existe dans le thread principal, cette solution devrait résoudre les erreurs de segmentation - voir la réponse par @ingvar)

  • en utilisant un QThread et un signal Qt personnalisé. Les signaux Qt sont thread-safe, vous pouvez donc connecter un signal dans un thread secondaire à l'emplacement close dans le thread principal. Ensuite, vous pouvez émettre le signal à partir du thread secondaire après 30 secondes pour déclencher la fermeture.

L'approche que vous devriez prendre dépend de votre objectif final (et combien d'un exemple « jouet » c'est), mais pour l'instant je ne vois aucune raison d'avoir un fil secondaire. Vous devriez juste aller avec la réponse de @ ingvar :)

P.S. Dans tous les cas, utiliser des threads python avec Qt est une mauvaise idée. S'en tenir à QThread sauf si le thread est complètement indépendant de l'interface graphique.