writing clean GTK by subclassing
05:29 pm
Suppose you have an application that uses a lot of GtkFileChoosers. If someone chooses a file, you want your program to remember the directory they were last in next time they open a file chooser.

Simple you think, you'll just set the current folder before you show the chooser and get the current folder after the user has made their selection. Of course, this means you'll have to put that code around every GtkFileChooser instance.

The solution is subclassing.

By creating a subclass, we can override methods like the constructor and run() to hook in our directory save and restore code. We can also set default properties that would otherwise be required for every instance. The method for subclassing an object depends on the language you're using, but is possible even in C. For this example, I'll use Python:
class MyFileChooserDialog (gtk.FileChooserDialog):
	CWD = None

	def __init__ (self, *args, **kwargs):
		super (MyFileChooserDialog, self).__init__ (*args, **kwargs)

		# set some defaults
		self.set_default_response (gtk.RESPONSE_OK)
		self.set_property ('do-overwrite-confirmation', True)

		# set the working directory if available
		if MyFileChooserDialog.CWD is not None:
			self.set_current_folder (MyFileChooserDialog.CWD)
	
	def run (self):
		response = super (MyFileChooserDialog, self).run ()

		# get the working directory if the user clicked ok
		if response == gtk.RESPONSE_OK:
			MyFileChooserDialog.CWD = self.get_current_folder ()

		return response

gobject.type_register (MyFileChooserDialog)
The class being created is MyFileChooserDialog, which inherits from GtkFileChooserDialog. It has a class-global variable called CWD. The constructor accepts all arguments it is called with, and passes them unmodified to the parent's constructor. It then sets some default properties and finally, sets the current folder for the instance to the global variable CWD.

The run() method also immediately calls its parent's method. It then checks the response code, if the user didn't cancel the action, we store the instance's current folder in the global CWD and then return the value from our parent. All other methods from the parent work as usual, allowing us to transparently replace all instances of gtk.FileChooserDialog with MyFileChooserDialog.

New methods, not implemented in the parent, can also be added to the class. Or depending on your language, new polymorphic methods to support additional data types could be implemented. Python doesn't have true polymorphism, but say we wanted to extend the functionality of add_filter() to make it easier to use in our app:
	def add_filter (self, filter, *args):
		if isinstance (filter, gtk.FileFilter):
			super (MyFileChooserDialog, self).add_filter (filter)
		elif isinstance (filter, MyFileChooserFilterEnum):
			pass # do something with an enum
		else:
			super (MyFileChooserDialog, self).add_filter (create_filter (filter, *args))

def create_filter (name, *patterns):
	filter = gtk.FileFilter ()
	filter.set_name (name)
	for pattern in patterns: filter.add_pattern (pattern)
	return filter
Allowing us to do something like dialog.add_filter ("X Files", "*.x") to create the GtkFileFilter on the fly.

Subclassing allows us to extend the functionality of widgets in reusable way.

Play Along At Home: feel free to port this example into your language of choice, comment with text or a link!
(posted on Wednesday February 6th, 2008 at 05:29 pm — 17 comments)

Livejournal

Navigation

Related Links

Syndication

RSS 2.0 Atom FOAF

Planetarium

Web Presence

Hacking Life   UCC   GNOME

Contact Me

License

Creative Commons License