Monday 28 November 2011

Creating a Treeview in Glade

Preface: This was one of the first articles I wrote for my blog but never published it. I can't say why but I didn't. It's quite old and it's a little embarrassing. I figure I may as well post it though since it might just help another Glade/GTK+ newbie out there!


Disclaimer: This article assumes you have an understanding of how to implement the Model/View/Controller methodology of a GTK+ treeview. It is also intended for GTK+ 2.xx but may still be applicable to GTK+ 3.xx.

It seems like every day I program I am reminded about how much more I have to learn. Some days, like the name of my blog suggests, I feel like I have no idea what I'm doing. Or more appropriately, what I'm doing wrong. Take for example, a few nights several months ago.

I was continuing the process of converting the user interface for Vocab Builder from a C source code implementation to a gtk-builder xml user interface via Glade. Glade makes designing and maintaining a user interface a dream when it's behaving properly. I am using the latest version of Glade (3.8.0 at the time of writing) on Ubuntu 10.10 Maverick Meerkat and I have experienced a few problems. One of them is a real show-stopper. Literally. Somehow the widget tree gets corrupted and isn't displayed properly. Some widgets are just blank lines where the widget should be or are a garbled mess. If that happens, and you click on the place where the widget SHOULD be, Glade crashes. No warnings, just straight back to the desktop. All unsaved work is lost. Good times! The only work-around is to not click on the spot and instead find the widget (if you can) in your UI and click on it, hoping all the while that it's a visible widget because if its a vbox or adjustment widget you might be out of luck. A work around is to restart Glade. Closing the UI file and re-opening it may fix the issue too, I don't know.

Creating more than just the treeview widget seems to be a relatively new feature to Glade which might explain some of its oddities. First of all, how you add columns isn't exactly clear. Unlike other widgets, there's no treeview subcategory in the tools section where you can click on a category and easily add it to the treeview as a child widget. No sirree, you have to right-click on the treeview widget and select Edit. Okay, fine. Not a big deal. An odd design but whatever. How about a cell renderer? There is absolutely no visual clue that tells you how to add one. What you have to do is go into the Hierarchy tab, where you add new columns, and right-click on each column and select Add. Again, it sort of makes sense but with how easy it is to implement other widgets you would think there would be some kind of a clue like a button or tooltip which would indicate how you add a renderer. At least the Hierarchy tab had an Add button that let you know you could add something to it. View and controller done. What about the model?

Well, there's a property for the treeview where you can select the model to attach to it. It pops up a handy window that allows you to either select a tree model you've already created or, if you haven't created one yet, you can click New and Glade will create one for you. By default, Glade gives you a ListStore. This is where my problems began. I didn't clue in that it was a ListStore that was created by default. To be fair, Glade does call the model ListStore1 or something like that. I just never picked up on it. So I edited the model and put in the values I needed to store in the model. Uh-oh, what's this? Another stumbling block? Where's the string type? There MUST be a string type...Nope, can't find one! As it turns out, while a string is TYPE_STRING in GDK in Glade its called a gchararray. Yes, a string is a character array. So...why change what its called? Why not gstring? Oh...maybe that's why? G-string? Naw...must be another reason. Maybe just to piss me off? Either way, off I went and re-ran Vocab Builder to see if my changes were working. Yup, the treeview headers are clickable and the sort indicators look like they work. It does almost everything I want sans a few options I haven't implemented, yet. However, when selecting New in the Editor the ability to sort by columns disappears. What the heck? What's going on? After hours of trying to solve the issue, nothing. Bah! "I'll fix it tomorrow."

Here it is tomorrow the following day and I'm still not sure what's going on with the treeview so I start converting the Builder interface to get my mind off the Editor. Turns out I use a treeview in the preferences window, too. I can't escape these darned treeviews! Well, I decide to take another stab at it. I go through all the steps, forgetting how to do some of them and getting frustrated again, but I muddle through it. In my original code, I simply removed the old model and created a new one whenever I needed to update the treeview because it would always be new data. However, I realized that I could do away with all that if I just cleared the old treeview. Hrm...why isn't it working? It says it's not a TreeStore...I stared blankly at the screen in consternation. Ya, right, it's not. Its a ListStore. Ohhhh....what an idiot I am! I quickly convert my functions to use that of a ListStore and viola! Everything works as intended! Fantastic! Now, what if I want a TreeStore?

Luckily, this fix was relatively easy to figure out. Rather than have Glade create a default model for me I can just create the TreeStore model first and then attach it to the treeview. Easy. Fixed. Wish I'd known that yesterday the day before after spending hours trying to figure it out...

Addendum: If there turns out to be some interest in an actual step-by-step example of the process of creating a treeview in Glade/GTK+ I'd be more than happy to do so.

1 comment:

  1. Have you used gtkmm? I am having a hard time appending data to a TreeView that I created in glade with a liststore. I even added data in glade and it shows up when run the program, but when I try using code to append new data to the liststore/tree, that's where I am stuck.

    ReplyDelete