Drawing a Wooden Cabinet in a GTK Application

I have a question for you GTK and/or WebKit folks.

For Ubuntu Accomplishments we would like to take this:

…and make it look more like a wooden trophy cabinet, like this:

Currently the icons in the client are displayed using a gtk.IconView, but I am not sure how we could accomplish the wooden cabinet look.

Some questions:

  • Can we draw images behind the gtk.IconView? This would be the preferred approach as we then get all the resizing, sorting, and other benefits of the gtk.IconView.
  • Would WebKit be a better option? If so, we would need to be able to click on icons in the view, fill the window when the window resizes etc. Is there an easy way to do this in WebKit (I would prefer if we didn’t need to reimplement an Icon View in WebKit if possible).
  • Any other ideas? (switching to another widget toolkit is not an option).

Any input would be greatly appreciated. :-)


  • https://launchpad.net/~jdelarocque hal

    Hello, i’m not a very experimented developer but i’ve done something similar a few days ago. You could connect the ‘expose-event’ signal of your window to a function using cairo like this one : def expose (widget, event, bg_source):     cr = widget.window.cairo_create()     surface = cairo.ImageSurface.create_from_png(bg_source)     cr.set_source_surface(surface)     cr.paint()

    to set the background of your window, in which you can have an iconview. This is much faster than loading webkit with the python webkit module (also easier from my point of view).

  • Anonymous

     Thanks for the reply, Hal.

    Would I be able to set the background of the IconView instead of the Window (so that the expose-event of the Window is used to draw, but I draw the IconView instead)? I ask as if I draw behind the window, I suspect the tiling of the image will be out due to the toolbars.

    In your code I don’t see how I specify where the cairo instance is painted. Where do I specify the Window/IconView?


  • Andre

     I’m used to program using GTK, so correct me if I’m wrong.

    If I remember correctly, every widget is a window. So you should call expose passing your IconView as the widget parameter. This will set the bg only for the IconView, and not for the whole window as you fear.

  • Anonymous

    You know what ? Don’t

    Skeuomorphism  (or however it’s spelled) is something I personally see as a real step backwards for the industry Do not follow apple, please. A computer is virtual, and it doesn’t need to try and emulate “real life” I have way more respect for original design such as ICS, sadly defunct MeeGo or even, yes, the reimagining Windows is going through right now than anything coming out of Cupertino (other than hardware) lately

    But then again, this is totally a personal point of view, and I understand the appeal, I just can’t see the beauty and the advantages such a design could have

    Aaaaaaaand now, on the topic of this blog post, I’m afraid I can’t be of any help

  • Anonymous

    I would like to see how it is possible to achieve maximum bling, but without any compromise of the accessibility of the application, orca should just think it is an iconview. This kind of thing is where people start building custom non-standard widgets that break accessibility for a bit of eye candy, but I don’t think it is necessary to compromise in that way.

  • Anonymous

     Thanks for the feedback, but the goal here is to make the system as attractive as possible; this doesn’t have to be a wooden look, but I think some kind of design would be preferably (possibly the ubuntu.com style dot design).

    I will leave the design recommendations to people with an eye for great design…I just want to know how to implement it. :-)

  • Anonymous

     Agreed; I am not willing to compromise accessibility…this is why I would prefer to stick with the IconView.

  • Anonymous

     We’re on the same page then, Ubuntu dot design is a win in my book ;)

  • Mathias Hasselmann

    In theory it should be sufficient to load a custom CSS file with some content like this:

        TrophyinfoWindow GtkIconView { background: url(‘/usr/share/backgrounds/Langelinie_Allé_by_SirPecanGum.jpg’);    }

    Oddly GtkIconView only accepts plain colors for its backgrounds, but no gradients or images. Investigating.

  • https://launchpad.net/~jdelarocque hal

    As Andre said, IconView inherits from Window, so it seems possible to connect iconview’s expose-event to the expose function i wrote. But it doesn’t work (just tested it, returns no error but doesn’t work…). Furthermore when i connect the expose-event of a container widget, like a scrolled-window, it’s actually the background of the parent window (i mean window, of type gtk.Window, an not parent widget inheriting from window) which is changed. So the method i suggested does not work (or so it seems).

    To answer your question, this line : cr = widget.window.cairo_create() creates a cairo instance in the parent window (so the window is where the cairo instance is painted), but as explained, it doesn’t seem to work when i use this function specifying an iconview (while it works with a window).

    You may also try to set a gtk.pixmap as the widgets background using gtk.Style but i didn’t really explore this method…

    If you want to take a look at the code (but this solution doesn’t work as is, it just shows how it works for the window):

    !/usr/bin/env python

    -- coding:utf-8 --

    import gtk, cairo

    class win(gtk.Window):     def main_quit(self, window):         gtk.main_quit()

        def expose (self, widget, event, bg_source):         cr = widget.window.cairo_create()         surface = cairo.ImageSurface.create_from_png(bg_source)         cr.set_source_surface(surface)         cr.paint()

        def init(self):         window = gtk.Window()         window.set_app_paintable(True)         window.set_border_width(10) # just to see the background         window.connect(‘expose-event’, self.expose, “pic_0.png”)         window.connect(“destroy”, self.main_quit)

            iconview=gtk.IconView()         iconview.set_app_paintable(True)         iconview.connect(‘expose-event’, self.expose, “pic_1.png”)

            box=gtk.VBox()         box.pack_start(iconview)

            window.add(box)         window.show_all()

    def main():     app = win()     gtk.main()

    if name == ‘main‘:     main()

  • guest_x

    You’re still using old PyGTK/GTK+2. PyGObject/GTK+3 does not have “expose-event”. It’s called “draw” and has a Cairo context directly as parameter.

    Here you can update your knowledge: http://python-gtk-3-tutorial.readthedocs.org/en/latest/index.html

  • guest_x

    from gi.repository import Gtk, Gdk

    css = Gtk.CssProvider() css.load_from_data(“”"     .transparent { background-color: rgba(0, 0, 0, 0); }     .shelf { background-image: url(‘shelf.png’); } “”") Gtk.StyleContext.add_provider_for_screen(Gdk.Screen.get_default(), css,     Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION)

    icon_view.get_style_context().add_class(‘transparent’) window.get_style_context().add_class(‘shelf’)

  • Mathias Hasselmann

    Something like this works (but don’t ask for reason):

      TrophyinfoWindow GtkScrolledWindow { background: url(“/tmp/shelf.png”); }   TrophyinfoWindow GtkIconView { background: url(“/tmp/empty.gif”); }

    That “empty.gif” is used for rendering icon view cells. So you also could use it to give some custom background to each cell. Oddly background repeat modes also get ignored. Seems that CSS support isn’t that robust yet… :-(

    Guess you also want to know how to load the CSS, do it like this:

      p = Gtk.CssProvider()   p.load_from_path(‘/tmp/test.css’)   widget.get_style_context().add_provider(p)

  • http://www.dylanmccall.com/ dylan-m

    I like the idea of doing something more visually interesting! The Accomplishments app would definitely be more interesting to me if it felt less like a system and more like a lovable, organic thing :)

    I don’t think this is a problem you should solve with GTK, though. Pretty well the entire toolkit is going to be fighting against you, insisting on doing exactly what you don’t want to do here. Sure, you can get that background image, and pretty icons, but without some serious fiddling it’ll still feel like icons in a grid over an image.

    Have you looked at MX or Clutter? You can keep GTK for things that are better expressed with GTK, and just use them for the trophy display. These things do a pretty good job fitting together, as far as I can tell. Otherwise, yeah, I think Webkit would be a nice approach. It’s nice enough to deal with, and you’ll definitely get some flexibility.

    You might find it liberating once you have a base set up for looser, shinier graphics, anyway. Yes, you’ll have to do some extra work, and then more extra work for accessibility, but you won’t need to conform to as many rules, either.

  • http://www.jorgecastro.org/ Jorge Castro

    Your reply made me chuckle. 

    It’s come full circle. I used to have this view; I want a consistent UI, not winamp skins for every app, how terrible! It’s kind of horrible to sacrifice usability for this kind of junk, all apps should just conform to the OS look and feel, or you suck!

    The Unfortunate bummer is that Linux apps (GTK apps in particular) never evolved passed this 90′s look and feel, so out of the box compared to other platforms we don’t look so nice.  

    I think the title of this blog ends up being misleading. I’m going to guess that jono doesn’t really wants to draw wooden shelves for his app, I think that’s the symptom. What (I am guessing) he really wants is for his app to not look like crap. Look at the subtle shadows around the books and shelves. It looks nice

    Look at the apps on iOS, Windows Phone/Metro, ICS, and other platforms. The native widgets look nice, even the most immature apps can look decent right out of the box. They might be like their equivalent linux apps and be crashy garbage, but other than the rare exception, they look and feel pretty nice. For some reason our default toolkit’s idea is nothing but battleship grey with tons of padding, like it’s been for the past decade.

    And we wonder why Firefox, Chrome, Mp3 players, and other apps choose to do their own custom things, and people flame them for being “inconsistent” with the rest of the platform when really they’re just trying to not look and feel so horrible. 

    GTK3 stuff looks nice. The indicators in GNOME Shell look sweet, the Shell overlay looks great, the Unity stuff looks great too (like the translucency on the dock and Dash), but IMO we have a long way to go to make ootb great-looking apps a no brainer for app authors. 

    At the end of the day it’s dumb that app authors even have to care about this.

  • Stephen Griffiths

    I think you may have the wrong approach all together.

    This may all be done easier/better with CSS theming no matter what choice you make. I reckon you can just load a CSS class to get things working.

    Your real issue with using (GTK+) CSS theming won’t be the background it will be: a) determining if the theming will hold for the major theming engines (it should do). b) deciding what to do with the text, loose text on a wooden book shelf isn’t classy c) finding the best approach may end up being using a Gtk.TreeView using the right renderers (possibly custom) and the new layout classes that were introduced

    Any chance you could post links to the bug/design, the project location, wiki pages whatever you have going.  I might be interested in coding up the widget for you as I have experience and enjoy doing it!  (I’d start now however I have Uni work I should be doing)

  • Anon

     So attractiveness is more important than usability? The proposal just emulates real life, instead of making use of the powers of a computer interface.

  • Peter Nel

    My comments are not directly related to the question of Jono’s post…

    Being an iBook app user myself, I appreciate the graphical look of the shelf. The presentation of books in one’s home serves a similar sense of accomplishment – both in showing off what one has read and as a reminder of what must still be read. The virtual shelf app comes as close to that as i guess is possible on a screen. Ubuntu accomplishments have parallels with this idea and also its real-world counterpart of a trophy case.

    Concerning usability. One problem with a shelf-only presentation in iBooks is that when you have more than 20 books, it becomes more difficult to find any particular one. That’s when I use list-view (top-right icon next to ‘Edit’) to display a sorted list of books. This could apply similarly to the accomplishments app.

    Another suggestion I have is to have each accomplishment have a bit more unique look, or logo variation, or perhaps some color or picture symbolizing the accomplishment – similar to game console achievements… if people are going to respond or be motivated by these “achievements” (as opposed to merely the intrinsic reward of the achievement itself, as it has always been and perhaps should be) like gamers are with game-achievements, then the full psychology of this feature should be exploited. Perhaps we could also include novel achievements like installing games or apps, or maybe even provide a framework for in-game/-app achievements… just some ideas that might be aimed at bridging the gap between self-serving achievers and contributory accomplishments, perhaps. It could also motivate app developers to hook into this mechanism.

    Finally, for those objecting to the proposed shelf-like look of the app. Consider its purpose – it’s exactly to display accomplishments (icons). It really has no other use than that, and I think it should do that as well as possible. Further, the audience to whom it’s aimed at: likely a kind of player that have not been playing this game for long – perhaps a new class of user that really care about trophies, recognition, acknowledgement or status.

  • DavidW

    Please no.

  • Random832

    The iBooks screenshot shown here doesn’t look like an actual bookshelf, it looks like an icon/tile view with a wood grain texture. It’s not even rendered in perspective, and who has their books all facing forward (who has a shelf shallow enough to look good that way) with space between them? Who has only one size of book (I am left wondering how iBooks handles covers with different aspect ratios)?

  • Familie Gotwig

    Why dont you look how gcstar does it ??? http://media.cdn.ubuntu-de.org/wiki/attachments/31/40/gcstar_cover.png 

  • Leopoldo Pena

    Best Blind Guardian performance ever!!!!!! ;)

  • Familie Gotwig

    What does that mean :O ?

  • Wood Working Projects

     Well I definitely enjoyed reading it. This article provided by you is very helpful for accurate planning.

  • Wolf Vollprecht

    Hey guys,

    It’s a pity nobody could give a good answer here, because I was searching for something very similar. After two hours or so more I found a solution (because GTK-CSS with repeat: repeat; didn’t work for me):

    Source Code: #!/usr/bin/env pythonfrom gi.repository import Gtk, Gdk, GdkPixbufimport cairo

    def draw_background( widget, context): pb = GdkPixbuf.Pixbuf.new_from_file(‘bg.png’) surface = cairo.ImageSurface.create_from_png(‘bg.png’) sp = cairo.SurfacePattern(surface) sp.set_extend(cairo.EXTEND_REPEAT) context.set_source(sp) context.paint()

    window = Gtk.Window()window.set_title(‘Drawing Test’)window.set_size_request(640,480)

    window.connect(‘destroy’,Gtk.main_quit)hbbox = Gtk.HButtonBox()window.add(hbbox)hbbox.connect(‘draw’, draw_background)button = Gtk.Button(‘Press Me!’)hbbox.pack_start(button, True, False, 10) window.show_all() Gtk.main()

    You could then arrange a pattern that tiles and becomes the bookshelf in the end :)

  • Wolf Vollprecht

    Obviously discus messed up the code, here is the gist: https://gist.github.com/3470841