views:

258

answers:

4

Started my first Python pet project using PyGTK. Though it is a really powerful GUI toolkit and looks excellent, I have some pet peeves. So I thought about transitioning to something else, as it's not yet too extensive. Had a look around on SO and python documentation, but didn't get a good overview.

What's nice about PyGTK:

  • Glade files
  • self.signal_autoconnect({...})
  • self.get_widget() as _getattr_

This is bugging me however:

  • manual gobject.idle_add(lambda: ... and False)
  • no standard functionality to save application/window states
  • TreeView needs array building
  • widget.get_selection().get_selected(), model.get_value(iter, liststore_index)

TreeView: Because this is the main interface element, it's the most distracting. Basically my application builds a list of dictionaries to be displayed name=column+row=>value. To display it using GTK there needs to be a manual conversion process, ordering, typecasts. This seems a lot of overhead, and I wished for something more object-oriented here. PyGtk has many abstractions atop gtk+ but still seems rather low-levelish. I'd prefer to pass my dict as-is and have columns pre-defined somehow. (GtkBuilder can predefine TreeView columns, but this doesn't solve the data representation overhead.)

When I get a mousclick on my TreeView list, I also have to convert everything back into my application data structures. And it's also irksome that PyGTK doesn't wrap gtk+ calls with gobject.idle itself, if run from a non-main thread. Right now there is a lot of GUI code that I believe shouldn't be necessary, or could be rationalized away.

? So, are there maybe additional wrappers on top of PyGTK. Or which other toolkit supports simpler interfaces for displaying a Grid / TreeView. I've read a lot about wxPython being everyones favourite, but it's less mature on Linux. And PyQT seems to be mostly the same abstraction level as PyGTK. Haven't used TkInter much so don't know about if it has simpler interfaces, but it anyway looks unattractive. As does PyFLTK. PyJamas sounds fascinating, but is already too far out (Desktop application).

.

So, GUI toolkit with dict -> Grid display. Which would you pick?

.

Just as exhibit, this is my current TreeView mapping function. Sort of works, but I would rather have something standard:

    #-- fill a treeview
    #
    # Adds treeviewcolumns/cellrenderers and liststore from a data dictionary.
    # Its datamap and the table contents can be supplied in one or two steps.
    # When new data gets applied, the columns aren't recreated.
    #
    # The columns are created according to the datamap, which describes cell
    # mapping and layout. Columns can have multiple cellrenderers, but usually
    # there is a direct mapping to a data source key from entries.
    #
    # datamap = [  #  title   width    dict-key    type,  renderer,  attrs  
    #               ["Name",   150,  ["titlerow",   str,    "text",    {} ]  ],
    #               [False,     0,   ["interndat",  int,     None,     {} ]  ],
    #               ["Desc",   200,  ["descriptn",  str,    "text",    {} ],  ["icon",str,"pixbuf",{}]  ],
    #
    # An according entries list then would contain a dictionary for each row:
    #   entries = [ {"titlerow":"first", "interndat":123}, {"titlerow":"..."}, ]
    # Keys not mentioned in the datamap get ignored, and defaults are applied
    # for missing cols. All values must already be in the correct type however.
    #
    @staticmethod
    def columns(widget, datamap=[], entries=[], pix_entry=False):

        # create treeviewcolumns?
        if (not widget.get_column(0)):
            # loop through titles
            datapos = 0
            for n_col,desc in enumerate(datamap):

                # check for title
                if (type(desc[0]) != str):
                    datapos += 1  # if there is none, this is just an undisplayed data column
                    continue
                # new tvcolumn
                col = gtk.TreeViewColumn(desc[0])  # title
                col.set_resizable(True)
                # width
                if (desc[1] > 0):
                    col.set_sizing(gtk.TREE_VIEW_COLUMN_FIXED)
                    col.set_fixed_width(desc[1])

                # loop through cells
                for var in xrange(2, len(desc)):
                    cell = desc[var]
                    # cell renderer
                    if (cell[2] == "pixbuf"):
                        rend = gtk.CellRendererPixbuf()  # img cell
                        if (cell[1] == str):
                            cell[3]["stock_id"] = datapos  # for stock icons
                            expand = False
                        else:
                            pix_entry = datapos
                            cell[3]["pixbuf"] = datapos
                    else:
                        rend = gtk.CellRendererText()    # text cell
                        cell[3]["text"] = datapos
                        col.set_sort_column_id(datapos)  # only on textual cells

                    # attach cell to column
                    col.pack_end(rend, expand=cell[3].get("expand",True))
                    # apply attributes
                    for attr,val in cell[3].iteritems():
                        col.add_attribute(rend, attr, val)
                    # next
                    datapos += 1

                # add column to treeview
                widget.append_column(col)
            # finalize widget
            widget.set_search_column(2)   #??
            widget.set_reorderable(True)

        # add data?
        if (entries):
            #- expand datamap            
            vartypes = []  #(str, str, bool, str, int, int, gtk.gdk.Pixbuf, str, int)
            rowmap = []    #["title", "desc", "bookmarked", "name", "count", "max", "img", ...]
            if (not rowmap):
                for desc in datamap:
                    for var in xrange(2, len(desc)):
                        vartypes.append(desc[var][3])  # content types
                        rowmap.append(desc[var][0])    # dict{} column keys in entries[] list
            # create gtk array storage
            ls = gtk.ListStore(*vartypes)   # could be a TreeStore, too

            # prepare for missing values, and special variable types
            defaults = {
                str: "",
                unicode: u"",
                bool: False,
                int: 0,
                gtk.gdk.Pixbuf: gtk.gdk.pixbuf_new_from_data("\0\0\0\0",gtk.gdk.COLORSPACE_RGB,True,8,1,1,4)
            }
            if gtk.gdk.Pixbuf in vartypes:
                pix_entry = vartypes.index(gtk.gdk.Pixbuf) 

            # sort data into gtk liststore array
            for row in entries:
                # generate ordered list from dictionary, using rowmap association
                row = [   row.get( skey , defaults[vartypes[i]] )   for i,skey   in enumerate(rowmap)   ]

                # autotransform string -> gtk image object
                if (pix_entry and type(row[pix_entry]) == str):
                    row[pix_entry] = gtk.gdk.pixbuf_new_from_file(row[pix_entry])

                # add
                ls.append(row)   # had to be adapted for real TreeStore (would require additional input for grouping/level/parents)

            # apply array to widget
            widget.set_model(ls)
            return ls

        pass
+1  A: 

I would suggest taking a look at wxPython. I found it really easy to pick up and very powerful too although I'd have to admit I've not done a lot with Treeviews myself.

wxWidgets calls the equivalent control a wxTreeCtrl

[Edit] The wxDataViewTreeCtrl might actually be of more use in your case.

Jon Cage
How does that answer his question? While it's been a little while since I've used these two libraries, my feeling was that if anything, PyGTK is higher-level than wxPython. I don't see anything in the wxTreeCtrl docs to suggest you can, e.g., just pass a dictionary and be done with it.
Ken
It's not as helpful as I first though. I'm sure I'd seen an example on the wxPython website doing something like this but now I can't find it :-/
Jon Cage
I'll investigate this anyhow. WX uses GTK as backend, so at least can't be a visual downgrade. But since it's a compat wrapper, it's probably common denominator abstraction level. But then, I'm mostly interested in the TreeView API. Just haven't seen the relevant doc yet...
mario
+2  A: 

Try Kiwi, maybe? Especially with its ObjectList.

Johannes Sasongko
Have to buy some upvotes. And something to beat myself up. Actually I had read about Kiwi before, but somehow overlooked it at implementation time. That's really what I wanted. Just have to check out if you can get the ObjectLists content back as-is in a signal callback.
mario
+1  A: 

I hadn't come across Kiwi before. Thanks, Johannes Sasongko.

Here are some more tooklits that I keep bookmarked. Some of these are wrappers around other toolkits (GTK, wxWidgets) while others stand alone:

(I've included a few that were already mentioned for the sake of others who come across this post. I would have posted this as a comment, but it's a bit too long.)

Forest
Excellent points in your list. I've seen Dabo before, which seemed seriously higher level and especially provides a data view above a treeview/grid. However, couldn't find a real API documentation and so I'm unsure if the UI components can be used independently from database sources.
mario
And AVC sounds even better! "AVC is a multiplatform, fully automatic, live connection among graphical interface widgets and application variables for the python language."
mario
+1  A: 

See also pygtkhelpers' ObjectList

ychaouche