In my PyGTK application, I am asking a user to find a file so that operations can be performed on it. The application asks the user for the file, and relays that filename to the necessary methods. Unfortunately, when calling the gtk.dispose()
method on that dialog, it just hangs there until the method being called upon to perform the file-IO is complete. I have even tried placing the file manipulations inside of another thread, but that did not have any effect.
My indented purpose is to have the program display a dialog box to the user informing them that the file they selected for manipulation is taking place. With the current implementation, the dialog appears after the gtk.FileChooserDialog
is disposed.
Below is my co开发者_如何学Cde:
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()
Please note that this code is not yet completed, for instance, it does not yet properly handle the user not selecting a file and canceling the operation.
EDIT: Upon further investigation, there appears to be a threading issue with PyGTK. The problem is occurring in the while True
loop. I replaced that code with a time.sleep(15)
, and similarly, the file select dialog will pause. This is quite odd behavior, and everything should operate inside of a different thread. I guess the question now is to find out how to place the File Selection dialog inside of it's own thread.
There's probably no need to perform the file operations in a separate thread, since you're not really doing anything in this thread while the file operations are running -- just busy-waiting. And that brings me to why the code doesn't work: GUI updates are processed within the GTK main loop. But the whole time while you're waiting for the file thread to finish, the GTK main loop isn't executing, because it's stuck waiting for your performFileManipulation
function to end.
What you need to do is perform iterations of the GTK main loop during your while True
loop. That looks like this:
while True:
theFileOperationThread.join(0.1)
if theFileOperationThread.isAlive():
while gtk.events_pending():
gtk.main_iteration(block=False)
else:
break
But again, I would consider just doing the file operations in this thread, it seems redundant to start another thread and then do nothing while it's running.
Mixing threads and GTK apps (from what I recall) tends to produce weird results.
The problem is that even though you call gtk.dispose, you're probably calling the methods directly, which blocks the next iteration of gtk.mainloop.
What you need to do is create another function to do the file processing and call it from a callback funciton:
def doFileStuff(filename):
with open(filename, 'r') as f:
for line in f:
#do something
return False # On success
And then change this function:
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)
That should at least let you close the dialog, and I think it will launch the file processing stuff in the background. If not it will at least give you somewhere to launch your new thread from.
HTH
精彩评论