被告知时,PyGTK窗口没有隐藏

时间:2010-07-08 12:58:51

标签: python linux multithreading gtk pygtk

在我的PyGTK应用程序中,我要求用户查找文件,以便可以对其执行操作。应用程序向用户询问该文件,并将该文件名转发给必要的方法。不幸的是,当在该对话框上调用gtk.dispose()方法时,它只会挂起,直到被调用的方法执行文件IO完成。我甚至尝试将文件操作放在另一个线程中,但这没有任何影响。

我缩进的目的是让程序向用户显示一个对话框,通知他们他们选择进行操作的文件正在进行中。在当前实现的情况下,在 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)替换了该代码,同样,文件选择对话框将暂停。这是非常奇怪的行为,一切都应该在不同的线程内运行。我想现在的问题是找出如何将文件选择对话框放在它自己的线程中。

2 个答案:

答案 0 :(得分:2)

可能没有必要在单独的线程中执行文件操作,因为在文件操作运行时你没有真正在这个线程中做任何事情 - 只是忙着等待。这让我想到了为什么代码不起作用:GUI更新在GTK主循环中处理。但是在你等待文件线程完成的整个过程中,GTK主循环没有执行,因为它等待你的performFileManipulation函数结束。

您需要做的是在while True循环期间执行GTK主循环的迭代。看起来像这样:

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

但是,再次,我会考虑在这个线程中执行文件操作,启动另一个线程然后在它运行时什么都不做似乎是多余的。

答案 1 :(得分:1)

混合线程和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)

这应该至少让你关闭对话框,我认为它将在后台启动文件处理。如果没有,它至少会让你从某个地方启动你的新线程。

HTH