PyGTK窗口未按指示隐藏

1 投票
2 回答
904 浏览
提问于 2025-04-16 00:59

在我的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),结果文件选择对话框也会暂停。这种行为很奇怪,所有操作应该在不同的线程中进行。我现在的问题是,如何把文件选择对话框放在自己的线程里。

2 个回答

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

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

这样至少可以让你关闭对话框,我觉得它会在后台启动文件处理的工作。如果没有,它至少会给你一个地方来启动你的新线程。

希望这对你有帮助。

撰写回答