2010-03-20 14 views
6

Je rencontre un problème, où je souhaite exécuter plusieurs fonctions de ligne de commande à partir d'un programme python en utilisant une interface graphique. Je ne sais pas si mon problème est spécifique à PyQt4 ou s'il s'agit de ma mauvaise utilisation du code python.Étiquette dans PyQt4 GUI ne pas mettre à jour avec chaque boucle de FOR boucle

Ce que je souhaite faire est d'avoir une étiquette sur ma GUI changer sa valeur de texte pour informer l'utilisateur de la commande qui est en cours d'exécution. Mon problème cependant, se pose lorsque j'exécute plusieurs commandes en utilisant une boucle pour. Je voudrais que l'étiquette se mette à jour avec chaque boucle, cependant, le programme ne met pas à jour l'étiquette GUI avec chaque boucle, à la place, elle ne se met à jour qu'une fois la boucle complète cela a été exécuté. J'utilise PyQt4 pour mon environnement GUI. Et j'ai établi que la variable de texte pour l'étiquette est en effet mise à jour avec chaque boucle, mais, elle n'apparaît pas réellement visuellement dans l'interface graphique.

Existe-t-il un moyen de forcer l'étiquette à se mettre à jour? J'ai essayé la mise à jour() et repaint() méthodes dans la boucle, mais ils ne font aucune différence.

J'apprécierais vraiment toute aide. Merci.

Ronny.

Voici le code que je utilise:

# -*- coding: utf-8 -*- 
import sys, os 
from PyQt4 import QtGui, QtCore 
Gui = QtGui 
Core = QtCore 

# ================================================== CREATE WINDOW OBJECT CLASS 
class Win(Gui.QWidget): 
    def __init__(self, parent = None): 
     Gui.QWidget.__init__(self, parent) 

     # --------------------------------------------------- SETUP PLAY BUTTON 
     self.but1 = Gui.QPushButton("Run Commands",self) 
     self.but1.setGeometry(10,10, 200, 100) 

     # -------------------------------------------------------- SETUP LABELS 
     self.label1 = Gui.QLabel("No Commands running", self) 
     self.label1.move(10, 120) 

     # ------------------------------------------------------- SETUP ACTIONS 
     self.connect(self.but1, Core.SIGNAL("clicked()"), runCommands) 


# ======================================================= RUN COMMAND FUNCTION 
def runCommands(): 
    for i in commands: 
     win.label1.setText(i)  # Make label display the command being run 
     print win.label1.text()  # This shows that the value is actually 
            # changing with every loop, but its just not 
            # being reflected in the GUI label 
     os.system(i) 

# ======================================================================== MAIN 

# ------------------------------------------------------ THE TERMINAL COMMANDS 
com1 = "espeak 'senntence 1'" 
com2 = "espeak 'senntence 2'" 
com3 = "espeak 'senntence 3'" 
com4 = "espeak 'senntence 4'" 
com5 = "espeak 'senntence 5'" 
commands = (com1, com2, com3, com4, com5) 

# --------------------------------------------------- SETUP THE GUI ENVIRONMENT 
app = Gui.QApplication(sys.argv) 
win = Win() 
win.show() 

sys.exit(app.exec_()) 

Répondre

10

Le label est mis à jour tous les droits, mais l'interface graphique est pas redessiné avant la fin de la boucle.

Voici ce que vous pouvez faire à ce sujet:

  • Déplacez votre boucle de longue durée à un fil secondaire, le dessin de l'interface graphique se passe dans le thread principal.

  • Appelez app.processEvents() dans votre boucle. Cela permet à Qt de traiter les événements et de redessiner l'interface graphique.

  • Cassez la boucle et le laisser tourner en utilisant un QTimer avec un délai d'attente de 0.

L'utilisation d'un fil est la meilleure option, mais implique un peu plus de travail que simplement appeler processEvents . Le faire avec une minuterie est à l'ancienne et n'est plus recommandé. (voir la documentation)

+0

Merci beaucoup! J'ai trouvé cette application.processEvents() a seulement changé l'étiquette après que chaque commande ait été complétée, ce qui était trop tard, et a également ignoré quelques commandes. Qu'est-ce que le travail a été la création d'un nouvel objet QThread avec une méthode d'exécution, et l'appel de la méthode d'exécution lorsque le bouton est enfoncé. Est-ce que c'est ce que vous vouliez dire? Voici le code que j'ai utilisé. Je n'ai jamais appris à filer avant, alors dites-moi s'il vous plaît si je l'ai appliqué imprudemment. RunCommands de classe (Core.QThread): Run def (auto): pour i dans les commandes: win.label1.setText (i) os.system (i) – Ronny

+0

Ohh, bon sang, le commentaire na pas imprimer avec les nouvelles lignes et onglets que j'avais entrés: ( – Ronny

+0

@Ronny: Cela semble à peu près juste, mais vous devriez appeler 'start()' au lieu de 'run()'. –

2

Vous avez une incompréhension de base sur le fonctionnement d'une telle interface graphique. Une interface graphique Qt doit s'exécuter dans une boucle d'événements qui lui est propre. Votre boucle s'exécute à la place, et l'interface graphique ne peut pas faire son travail entre les exécutions de votre boucle. En d'autres termes, pendant que votre boucle for est en cours d'exécution, le code de l'interface graphique n'obtient pas l'heure du processeur et ne se met pas à jour.

Vous pouvez configurer un temporisateur avec un événement et exécuter votre code dans les gestionnaires de cet événement pendant un certain temps - cela résoudra votre problème.

+1

Merci pour l'explication dans le 1er paragraphe de votre réponse. Cela me permet maintenant de mieux comprendre ce qui se passe et de mieux comprendre le raisonnement derrière la réponse de Georg. Cependant, je ne comprends pas la solution que vous proposez. Je suis un débutant en programmation. Peut-être qu'un simple aperçu du code qui serait impliqué serait utile. Même si j'ai maintenant trouvé une solution qui semble fonctionner, je serais très curieux d'apprendre votre alternative proposée aussi bien. – Ronny

+0

@Ronny: en savoir plus sur QTimer et regarder quelques exemples de son utilisation - Je pense qu'il deviendra clair pour vous –