Ever used Pygtk’s filechooser widget and wanted to have Back, Forward and Up navigation buttons to go with it? The filechooser widgets acts weird sometimes, for instance when you open a new folder, the folder_changed signal is received twice, sometimes once which is very inconsistent and difficult to work with. Spent hours Googling (to Google means to search for something on the internet – Oxford dictionary – soon) a solution in vain so I finally decided to put my brain to work. If you’re reading this post then chances are that you’re either looking for a solution or just a big fan of my blog
The script below assumes you’re designing a GTK GUI with Python as your scripting language, have a filechooser widget placed somewhere on it, and want to add some navigation buttons (basically back, forward and up, and maybe reload – just like a file browser) and need some script to get them to work. Here are a few things you’ll need before we proceed.
A stack class:
#!/usr/bin/env python
class Stack:
def __init__(self):
self.data = []
self.size = 0
def push(self,item):
self.data.append(item)
self.size = self.size + 1
def pop(self):
if self.size > 0:
self.size = self.size - 1
return self.data[self.size]
def is_empty(self):
return self.size == 0
def is_full(self):
return self.size == len(self.data)
def top(self):
return self.data[self.size-1]
def item_at(self, pos):
return self.data[pos-1]
def count(self):
return self.size
def clear(self):
self.data = []
self.size = 0
def elements(self):
#displays all elements in the stack
for x in range(0, self.size):
print self.data[x]
- with the stack class in hand and ready to go, you’ll need to create 2 stacks (each an instance of the stack class) one for back history, and the other for the forward history (in my example I called them back_stack and fwd_stack respectively).
- note that in the code below, the filechooser widget is called, well, “filechooser”.
guru = gtk.glade.XML("your_glade_file.glade")
class WidgetsWrapper:
current_folder = guru.get_widget("filechooser").get_current_folder()
back_stack = stack.Stack()
fwd_stack = stack.Stack()
back_size = 0 #used to determine when folder_changed occurs.
#if not == back_stack.count() that means back button was clicked.
def __init__(self):
signalDic = {"on_filechooser_current_folder_changed" : self.folder_changed,
"on_btnReload_clicked" : self.reload_dir,
"on_btnBack_clicked" : self.goBack,
"on_btnForward_clicked" : self.goFwd,
"on_btnUp_clicked" : self.goUp}
guru.signal_autoconnect(signalDic)
#code to show the main window goes here;
#ie. guru.get_widget("main_window").show(), something so.
def folder_changed(self, widget):
filechooser = guru.get_widget("filechooser")
if WidgetsWrapper.back_size == 0 or
WidgetsWrapper.back_stack.count() == WidgetsWrapper.back_size:
#the line below makes sure the current folder doesn't get added to
#back_stack after folder is changed.
if WidgetsWrapper.current_folder <> filechooser.get_current_folder():
WidgetsWrapper.back_stack.push(WidgetsWrapper.current_folder)
guru.get_widget("btnBack").set_sensitive(True)
#should fwd_stack be cleared?
if WidgetsWrapper.fwd_stack.count() > 0 and
(WidgetsWrapper.back_stack.count() > WidgetsWrapper.back_size):
WidgetsWrapper.fwd_stack.clear()
WidgetsWrapper.back_size = WidgetsWrapper.back_stack.count()
WidgetsWrapper.current_folder = filechooser.get_current_folder()
#set button sensitivity
#back button
if WidgetsWrapper.back_stack.count() == 0:
guru.get_widget("btnBack").set_sensitive(False)
#forward button
if WidgetsWrapper.fwd_stack.count() == 0:
guru.get_widget("btnForward").set_sensitive(False)
#up button
if os.path.dirname(WidgetsWrapper.current_folder) == WidgetsWrapper.current_folder:
guru.get_widget("btnUp").set_sensitive(False)
else:
guru.get_widget("btnUp").set_sensitive(True)
def goBack(self, widget):
cnt = WidgetsWrapper.back_stack.count()
if cnt > 0:
WidgetsWrapper.current_folder = guru.get_widget("filechooser").get_current_folder()
setdir = WidgetsWrapper.back_stack.pop()
WidgetsWrapper.fwd_stack.push(WidgetsWrapper.current_folder)
try:
guru.get_widget("filechooser").set_current_folder(setdir)
guru.get_widget("btnForward").set_sensitive(True)
except:
pass
else:
guru.get_widget("btnBack").set_sensitive(False)
def goFwd(self, widget):
cnt = WidgetsWrapper.fwd_stack.count()
if cnt > 0:
setdir = WidgetsWrapper.fwd_stack.pop()
WidgetsWrapper.back_stack.push(str(guru.get_widget("filechooser").get_current_folder()))
try:
guru.get_widget("filechooser").set_current_folder(setdir)
guru.get_widget("btnBack").set_sensitive(True)
WidgetsWrapper.back_size = -1 #avoid folder_changed evaluation
except:
pass
else:
guru.get_widget("btnForward").set_sensitive(False)
def goUp(self, widget):
WidgetsWrapper.current_folder = guru.get_widget("filechooser").get_current_folder()
WidgetsWrapper.back_stack.push(WidgetsWrapper.current_folder)
try:
x = guru.get_widget("filechooser")
x.set_current_folder(os.path.dirname(WidgetsWrapper.current_folder))
guru.get_widget("btnBack").set_sensitive(True)
WidgetsWrapper.back_size = -1 #avoid folder_changed evaluation
WidgetsWrapper.fwd_stack.clear()
except:
pass
def reload_dir(*args):
x = guru.get_widget("filechooser")
x.set_current_folder(guru.get_widget("filechooser").get_current_folder())
def print_stacks(*args):
print ""
print "back_stack elements:"
print "...................."
WidgetsWrapper.back_stack.elements()
print ""
print "count: ", WidgetsWrapper.back_stack.count()
print "fwd_stack elements:"
print "..................."
WidgetsWrapper.fwd_stack.elements()
print ""
The code could use some improvement but bottom line is that it works. Just be sure to change the string in line 1 to the name of your glade file. The print_stacks function simply displays the current contents of the 2 stacks. Use it for debugging.
Hope this helps someone.