Окно PyGTK не скрывается, когда ему говорят

В моем приложении PyGTK я прошу пользователя найти файл, чтобы с ним можно было выполнять операции. Приложение запрашивает у пользователя файл и передает это имя файла необходимым методам. К сожалению, при вызове метода gtk.dispose() в этом диалоговом окне он просто зависает до тех пор, пока метод, вызываемый для выполнения файлового ввода-вывода, не будет завершен. Я даже пытался поместить манипуляции с файлами в другой поток, но это не дало никакого эффекта.

Моя отступная цель состоит в том, чтобы программа отображала диалоговое окно для пользователя, информирующего его о том, что файл, который он выбрал для манипуляции, имеет место. В текущей реализации диалоговое окно появляется после удаления gtk.FileChooserDialog.

Ниже мой код:

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()

Обратите внимание, что этот код еще не завершен, например, он еще неправильно обрабатывает пользователя, не выбравшего файл и отменившего операцию.

РЕДАКТИРОВАТЬ: При дальнейшем расследовании возникла проблема с потоками в PyGTK. Проблема возникает в цикле while True. Я заменил этот код на time.sleep(15), и аналогичным образом диалоговое окно выбора файла приостановится. Это довольно странное поведение, и все должно работать внутри другого потока. Я предполагаю, что теперь вопрос заключается в том, чтобы выяснить, как разместить диалоговое окно выбора файла внутри его собственного потока.


person Phanto    schedule 08.07.2010    source источник


Ответы (2)


Смешивание потоков и приложений GTK (насколько я помню) приводит к странным результатам.

Проблема в том, что хотя вы вызываете gtk.dispose, вы, вероятно, вызываете методы напрямую, что блокирует следующую итерацию gtk.mainloop.

Что вам нужно сделать, так это создать еще одну функцию для обработки файла и вызвать ее из функции обратного вызова:

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

А затем измените эту функцию:

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)

Это должно, по крайней мере, позволить вам закрыть диалоговое окно, и я думаю, что оно запустит обработку файлов в фоновом режиме. Если нет, то, по крайней мере, это даст вам возможность запустить новый поток.

ХТН

person Wayne Werner    schedule 08.07.2010
comment
Я попробую это, как только у меня будет шанс. - person Phanto; 08.07.2010
comment
Я использую смесь метода gobject.timeout_add(), и что ptomato предложил удалить нить. Это делает всплывающее окно. Однако, когда я использую простой обратный вызов, как я узнаю, когда метод doFileStuff() завершит обработку? Метод gobject.timeout_add() возвращает целочисленный идентификатор источника события. Сделает ли это метод gobject.signal_query()? - person Phanto; 09.07.2010
comment
Я так думаю: pygtk.org/pygtk2reference/ говорит, что кортеж будет содержать the GType of the return from the signal callback function. Я предполагаю, что он вернет None (или эквивалент GType), если обратный вызов не завершен. - person Wayne Werner; 09.07.2010

Вероятно, нет необходимости выполнять операции с файлами в отдельном потоке, поскольку вы ничего не делаете в этом потоке, пока выполняются операции с файлами, а просто ждете. И это подводит меня к тому, почему код не работает: обновления GUI обрабатываются в основном цикле GTK. Но все время, пока вы ожидаете завершения файлового потока, основной цикл GTK не выполняется, потому что он застрял в ожидании завершения вашей функции performFileManipulation.

Что вам нужно сделать, так это выполнить итерации основного цикла GTK во время вашего цикла while True. Это выглядит так:

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

Но опять же, я бы подумал о том, чтобы просто выполнять операции с файлами в этом потоке, кажется излишним запускать другой поток, а затем ничего не делать во время его работы.

person ptomato    schedule 09.07.2010
comment
Вы предложили удалить нить, и использование gobject.timeout_add(), предложенного Уэйном, исправило проблему. Спасибо. - person Phanto; 09.07.2010
comment
Я не думаю, что вам вообще нужен тайм-аут - тайм-аут в GObject - это просто еще один способ запуска другого потока. Вы можете просто вызвать функцию операций с файлами в этом потоке и обязательно время от времени вызывать пару gtk.events_pending() и gtk.main_iteration() во время операции с файлом. - person ptomato; 09.07.2010
comment
И если это невозможно, то в этом случае функция простоя будет лучше, чем тайм-аут. - person ptomato; 09.07.2010