2010-07-08 14 views
1

Dans mon application PyGTK, je demande à un utilisateur de trouver un fichier afin que des opérations puissent y être effectuées. L'application demande à l'utilisateur le fichier et relaie ce nom de fichier aux méthodes nécessaires. Malheureusement, lorsque vous appelez la méthode gtk.dispose() dans cette boîte de dialogue, elle reste bloquée jusqu'à ce que la méthode appelée pour exécuter le fichier-IO soit terminée. J'ai même essayé de placer les manipulations de fichier à l'intérieur d'un autre thread, mais cela n'a eu aucun effet.PyGTK Fenêtre ne se cachant pas quand on lui dit

Mon but indenté est que le programme affiche une boîte de dialogue à l'utilisateur pour l'informer que le fichier qu'il a sélectionné pour la manipulation a lieu. Avec la mise en œuvre actuelle, la boîte de dialogue apparaît après le gtk.FileChooserDialog est disposé.

est mon code ci-dessous:

def performFileManipulation(self, widget, data=None): 
     # Create the file chooser dialog: 
     dialog = gtk.FileChooserDialog("Open..", None, gtk.FILE_CHOOSER_ACTION_OPEN, (gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL, gtk.STOCK_OPEN, gtk.RESPONSE_OK)) 
     dialog.set_default_response(gtk.RESPONSE_OK) 

     # Display the file selector and obtain the response back 
     response = dialog.run() 

     # If the user selected a file, then get the filename: 
     if response == gtk.RESPONSE_OK: 
      dataLocation = dialog.get_filename() 

     # If the file was not chosen, then just close the window: 
     else: 
      print "Closed, no files selected" # Just for now 

     ########## Problem Area ########## 
     # The dialog is told to get destroyed, however, it hangs here in an 
     # unresponsive state until the the file-manipulations performed in a new thread 
     # below are completed. Then, the status dialog (declared below) is displayed. 
     dialog.destroy() # Close the dialog. 

     ## Show a dialog informing the user that the file manipulation is taking place: 
     statusDialog = gtk.Dialog("Performing File Operations...", parent=None, flags=0, buttons=None) 
     statusLabel = gtk.Label("Performing File Operations.\nPlease wait...") 
     statusLabel.set_justify(gtk.JUSTIFY_CENTER) 
     statusLabel.show() 
     statusDialog.vbox.pack_start(statusLabel, True, True, 0) 
     statusDialog.set_default_size(350, 150) 
     statusDialog.show() 

     # Create the thread to perform the file conversion: 
     errorBucket = Queue.Queue()    # Make a bucket to catch all errors that may occur: 
     theFileOperationThread = doStuffToTheFile(dataLocation, errorBucket)  # Declare the thread object. 

     ## Perform the file operations: 
     theFileOperationThread.start()   # Begin the thread 

     # Check on the thread. See if it's still running: 
     while True: 
      theFileOperationThread.join(0.1) 
      if theFileOperationThread.isAlive(): 
       continue 
      else: 
       break 

     # Check if there was an error in the bucket: 
     try: 
      errorFound = errorBucket.get(False) 

     # If no errors were found, then the copy was successful! 
     except Queue.Empty: 
      pass 

     # There was an error in the bucket! Alert the user 
     else: 
      print errorFound 

     statusDialog.destroy() 

S'il vous plaît noter que ce code ne soit pas encore terminée, par exemple, il n'a pas encore gérer correctement l'utilisateur ne pas sélectionner un fichier et l'annulation de l'opération.

EDIT: Lors d'une étude plus approfondie, il semble y avoir un problème de threading avec PyGTK. Le problème se produit dans la boucle while True. J'ai remplacé ce code par un time.sleep(15), et de même, la boîte de dialogue de sélection de fichier mettra en pause. C'est un comportement assez étrange, et tout devrait fonctionner à l'intérieur d'un thread différent. Je suppose que la question est maintenant de savoir comment placer la boîte de dialogue de sélection de fichier dans son propre thread.

Répondre

1

Le mélange de threads et d'applications GTK (d'après ce que je me souviens) a tendance à produire des résultats bizarres.

Le problème est que même si vous appelez gtk.dispose, vous appelez probablement les méthodes directement, ce qui bloque l'itération suivante de gtk.mainloop.

Ce que vous devez faire est de créer une autre fonction pour effectuer le traitement de fichiers et l'appeler à partir d'un funciton de rappel:

def doFileStuff(filename): 
    with open(filename, 'r') as f: 
     for line in f: 
      #do something 
    return False # On success 

Et puis changer cette fonction:

def performFileManipulation(self, widget, data=None): 
     # Create the file chooser dialog: 
     dialog = gtk.FileChooserDialog("Open..", 
             None, 
             gtk.FILE_CHOOSER_ACTION_OPEN, 
             (gtk.STOCK_CANCEL, 
             gtk.RESPONSE_CANCEL, 
             gtk.STOCK_OPEN, gtk.RESPONSE_OK)) 
     dialog.set_default_response(gtk.RESPONSE_OK) 

     # Display the file selector and obtain the response back 
     response = dialog.run() 

     # If the user selected a file, then get the filename: 
     if response == gtk.RESPONSE_OK: 
      dataLocation = dialog.get_filename() 

     # If the file was not chosen, then just close the window: 
     else: 
      print "Closed, no files selected" # Just for now 

     # You'll need to import gobject 
     gobject.timeout_add(100, doFileStuff, dataLocation) 

Cela devrait au moins laissez-vous fermer la boîte de dialogue, et je pense qu'il va lancer le traitement de fichiers en arrière-plan. Si ce n'est pas le cas, il vous donnera au moins un endroit où lancer votre nouveau fil.

HTH

+0

Je vais essayer dès que j'en aurai l'occasion. – Phanto

+0

J'utilise un mélange de la méthode 'gobject.timeout_add()', et de ce que ptomato a suggéré de supprimer le thread. Cela fait apparaître le popup. Cependant, lorsque j'utilise un simple rappel, comment savoir quand la méthode 'doFileStuff()' est terminée? La méthode 'gobject.timeout_add()' renvoie un ID entier de la source de l'événement. Est-ce que la méthode 'gobject.signal_query()' le ferait? – Phanto

+0

Je pense: http://pygtk.org/pygtk2reference/gobject-functions.html#function-gobject--signal-query2 dit que le tuple contiendra 'le GType du retour de la fonction de rappel de signal'. Je suppose qu'il retournera None (ou équivalent de GType) si le callback n'est pas fini. –

2

Il n'y a probablement pas besoin d'effectuer les opérations de fichiers dans un thread séparé, puisque vous n'êtes pas vraiment faire quelque chose dans ce fil alors que les opérations de fichiers sont en cours d'exécution - juste attente occupé. Et cela m'amène à comprendre pourquoi le code ne fonctionne pas: les mises à jour de l'interface graphique sont traitées dans la boucle principale de GTK. Mais tout le temps que vous attendez la fin du thread de fichier, la boucle principale GTK ne s'exécute pas, car elle est bloquée en attente de la fin de la fonction performFileManipulation.

Ce que vous devez faire est d'effectuer des itérations de la boucle principale de GTK pendant votre boucle while True. Cela ressemble à ceci:

while True: 
    theFileOperationThread.join(0.1) 
    if theFileOperationThread.isAlive(): 
     while gtk.events_pending(): 
      gtk.main_iteration(block=False) 
    else: 
     break 

Mais encore une fois, je considère simplement faire les opérations de fichiers dans ce fil, il semble inutile de commencer un autre thread et ne rien faire pendant qu'il est en cours d'exécution.

+0

Vous suggestion de supprimer le fil, et en utilisant le 'gobject.timeout_add()' suggéré par Wayne a corrigé le problème. Je vous remercie. – Phanto

+0

Je ne pense pas que vous ayez besoin du timeout non plus - un timeout dans GObject est juste une autre façon d'exécuter un autre thread. Vous pouvez simplement appeler votre fonction d'opérations de fichiers dans ce thread, et assurez-vous d'appeler la paire 'gtk.events_pending()' et 'gtk.main_iteration()' de temps en temps pendant votre opération de fichier. – ptomato

+0

Et si ce n'est pas possible, alors une fonction inactive serait meilleure qu'un timeout dans ce cas. – ptomato