This toolkit is exploratory, to try out new ideas and see what works and what doesn't. I built it to explore how to fix some of the more grievous bugs and misfeatures I've been discovering in GTK. The toolkit is a wrapper toolkit over pygtk, not a replacement for it. This means that the components largely exist to clean up and compose together various lower-level GTK widgets, and so when you build an application with the toolkit, at the end what you have is not something formed from the components in the toolkit but rather something formed with raw GTK components. There are disadvantages to this approach but one advantage is that it lets me freely interoperate with GTK widgets as I see fit. One side effect is that, for the moment, there's no hierarchy among the toolkit components since they share rather little.
It'll quickly become obvious that though I'm an experienced coder (Java, Lisp, NewtonScript, Objective C) I'm a total Python newbie. The kit reveals my idiosyncratic style, Lisp tendencies, and misinformed approach to learning Python as I built the toolkit.
You'll also note that the toolkit is extremely rudimentary. The idea (for now) is to provide a few functions which are very common, and assume you can dip into GTK to get the remaining functionality if you need it. The big item not in the toolkit: unhooking and removing widgets.
Last, I'm playing with some style features in GTK, so if things look funky, consider commenting out the "STYLE FIXES" section of the library.
Here's some principles behind the toolkit.
- Object Persistence All stateful widgets (Split, Slider, Spinner, Button, Text, etc.) are optionally persistent, meaning that their state is preserved after the application is quit and restarted. This is done by calling loadPersistenceData at the beginning of your program, assigning each of your desired stateful widgets a unique name, and then calling savePersistenceData at the end of your program. I've tried to handle database corruption gracefully, but we'll see.
- Simplification A number of components do a lot of things automatically so you don't have to type so much as you do in GTK. A small example: all widgets automatically show themselves except for MainWindow.
- Composition and Consolidation GTK is broken out way too much. Many of the toolkit's components construct compositions of underlying GTK widgets to provide common functionality in a single component.
- Hooks Instead of using signals, many widgets provide certain methods which you may override to receive information. For example, to hook into MainWindow's willClose() method to print out a message, you could do any of the following:
-
class MyMainWindow(MainWindow):
def willClose(self): print "I am closing!"
window = MyMainWindow()
-
def doThePrintThing(str): print str
window = MainWindow()
window.willClose = lambda : doThePrintThing("I am closing!")
-
def doAnotherPrintThing(): print "I am closing!"
window = MainWindow()
window.willClose = doAnotherPrintThing
As a lisp guy, I myself am partial to #2. Sadly, Python's lambda expression mechanism is horribly broken compared to a real functional language like Lisp: you can only provide a single expression to the lambda, rather than treat it (as it should be) as a fully-formed anonymous function with closure. I'm very dissapointed in Guido here.
Another Python ugliness arises in the fact that you can create per-instance functions (as we're doing in #2 and #3), but you can't make per-instance methods, as you can in a good dynamic OO language like, say, NewtonScript. Guido, shame on you.
-
- Definition by Construction Most GUI widgets are created once and then left alone by the developer. This is doubly true for persistent widgets. Thus here my constructors tend to (optionally) accept every feature under the sun, and in many cases set them in a way that cannot be changed later. I'll get to adding more setters and getters later on if the toolkit evolves.
- Access to Lower-level GTK These are cover component classes, not widgets in themselves. Thus you'll probably need to access GTK at some point here and there. To do this, the toolkit provides various items:
- getBasicComponent() will return the GTK widget that the toolkit component had wrapped (or the outermost of several composed GTK widgets used by the component).
- getUnderlying...()Various methods of this type will return specific GTK widget elements in that composistion as appropriate.
- The Wrap clas is a generic wrapper for any GTK widget, allowing you to use that widget in conjunction with these toolkit components.
- Bug Fixes in GTK and maemo I'm flabbergasted at the number of bugs I've come across while working on just these few cover widget classes; I have not ever dealt with a toolkit as buggy and ill-considered. Among the major widgets with major bugs or awful design that had (or have) to be modified: gtk.Paned, gtk.Image, gtk.Spinbutton (horrible), gtk.Scale (horrible), gtk.TreeView (nine errors already reported), gtk.ToolBar, hildon.ControlBar, gtk.Notebook, gtk.SplitView. And that's just so far in the toolkit contruction; I expect to find much more. :-(
Implemented Components
In all cases, I've indicated things I have yet to do, and also GTK bugs that the component had to work around. If it's in bugs but not to-do, the work-around has been successful.Persistence Facility
(functions)Provides basic object state persistence across application invocations. Osso supposedly provides such functionality, but it is broken beyond repair: it doesn't survive power-cycling (!); it's not automatic in widgets, but rather puts the burden on the developer; and it's strongly limited in its memory capacity, being stored, for no apparent reason, in RAM. This toolkit fixes all that, making widget state persistence automatic but optional, saving data to flash, and allowing any arbitrary storage.
Class Wrap
A generic holder for (typically) a gtk.Widget.
Class Strut
An empty widget which exists solely to enforce a minimum width or height. The defaults are all 0.
Class Pack
(composition of gtk.Alignment and gtk.Frame)A container widget which allows you to specify the location of its contained wiget, and to optionally stretch it in the X or Y dimension. Pack can also take a text string which appears as the label of an optional frame around the container.
GTK Bugs and Misfeatures.
- gtk.Frame's y_align property does nothing -- thus you can't change the y-dimension position of the frame label; and hildon's default position of the label is screwed up, with descenders colliding with the frame.
Class MainWindow
(composition of hildon.Window and gtk.Alignment)A container widget which represents the primary, full-size window of an application; the contained widget may be aligned as is done in the Pack class. The window is automatically a member of the hildon.Program. Automatically quits the application when the close box is closed.
To Do.
- hildon.Window has huge borders which look bad and are a gross waste of space. I'm still struggling to figure out how (or if) I can reduce them.
- A reference count for MainWindows so that the app is quit only when all of them are closed.
- For no good reason, hildon.Window's menu title bar is actually a composition of two strings: the window "title" and some other "title", typically the python file name! I need to figure out how to allow the user to set that other "title", rather than having it be a "." as it is now.
Class Box
(composition of gtk.VBox or gtk.HBox and optional gtk.Frame)A container widget into which you can add many widgets in either a horizontal (default) or vertical row. A Box may also have a text string, which appears as the label of a frame around the box.
GTK Bugs and Misfeatures.
- GTK doesn't seem to have a way to specify padding on one but not the other side of a widget (or likewise top but not bottom), reducing gtk.HBox's functionality by a considerable amount.
- gtk.Frame's y_align property does nothing -- thus you can't change the y-dimension position of the frame label; and hildon's default position of the label is screwed up, with descenders colliding with the frame.
Class Split
(composition of gtk.HPaned or gtk.VPaned and optional gtk.Frame)A container widget into which you can add two widgets in either a horizontal (default) or vertical row, with a divider-bar between them.
Persistence. Split's divider bar location is persistent.
GTK Bugs and Misfeatures.
- GTK doesn't have a hook for determining that the slider bar has been moved.
- GTK won't properly set the slider bar location until after gtk.main() has been called! Holy crap! I had to do an ugly hack for that one.
- gtk.Frame's y_align property does nothing -- thus you can't change the y-dimension position of the frame label; and hildon's default position of the label is screwed up, with descenders colliding with the frame.
Class Image
A widget which displays an image. The image may be specified as a gtk Stock image (one of the constants in http://www.pygtk.org/docs/pygtk/gtk-stock-items.html ), or loaded from a file with the given fileName, or given as an array of Strings representing XPM Image data.
GTK Bugs and Misfeatures.
- gtk.image isn't unified; it's just an amalgam of different storage formats: you have to extract the data in the same exact format that you inserted it into the image. This makes for a lot of ugly if/elif/elif/else nonsense.
To Do.
- Maybe include pixmap in addition to pixbuf.
- Need to allow more control over icon size.
Class Label
(composition of gtk.Label, gtk.Image, and gtk.HBox)A widget which displays a read-only textual label with an optional Image. The text can be right or left justified and the image moves as appropriate. The text can take up mutiple lines and may be specified as wrappable to the label width.
GTK Bugs and Misfeatures.
- Because you can't specify padding on on but not the other side of a widget, it's nontrivial to create a spacer between the text and the image, much less one which comes and goes depending on whether or not an image is provided. At present I'm using a Strut.
Class Text
(composition of gtk.HBox, gtk.TextView, and gtk.ScrolledWindow)A widget which displays read/write multiple-line, scrollable text. and may wrap around if so specified. Like Label, Text may also have an image.
Persistence. Both Text's text information and scroll position are persistent, and you can turn off persistence in either one independently.
GTK Bugs and Misfeatures.
- gtk.TextView is extremely slow (several seconds for a few paragraphs) at figuring its wrapping.
- gtk.TextView in combination with a gtk.ScrolledWindow and a gtk.HPaned (probably the single most common combination!) propagates repeated resize rules to the gtk.HPaned as the gtk.TextView changes its wrapping. This in turn causes gtk.TextView to change its wrapping AGAIN. This goes on until the system has converged (if it can).
Class Slider
(composition of gtk.Scale with an optional attached gtk.Label)A horizontal or vertical sliding widget which permits the user to choose from a set of numerical or other values. You can specify the location of the text label or provide a separate label of your own for it to update. Slider has three modes: first, it can slide smoothly among floating-point values from a minimum to a maximum; second, it can move by increments between a minimum and a maximum; third, it can slide from choice to choice in a provided list. You may also override a method to further customize display of information -- perhaps to create a logarithmic scale.
Persistence. Slider's current numerical value is persistent.
GTK Bugs and Misfeatures.
- Slider does not use hildon.Controlbar because of serious bugs (I've reported some) in Controlbar's resizing and its incapability of using real-valued sliding.
- Amazingly, gtk.Scale can only set ticks in powers of ten -- thus you can't make ticks of the form 0, 2, 4, 6, etc.
- gtk.Scale cannot provide arbitrarily-named ticks.
- gtk.Scale does not properly round floating-point values.
- gtk.Scale does not give you the ability to specify the width of your label (!).
- gtk.Scale improperly ties the precision of your slider's position to the precision of the displayed label.
- I wound up having to write my own truncator: python cannot truncate numbers when printing to strings. Instead, it rounds them (very wrong behavior for a slider).
- If gtk.Scale.set_draw_value() is false, then gtk.Scale.set_digits() has no effect.
Class Button
A pushbutton with text and/or icon images. The Button has some N number of states, and can display different text/images for each state, and also depending on whether or not the Button is presently being pushed or not. You can remove the background and the border.
Persistence. Button's current state is persistent.
GTK Bugs and Misfeatures.
- Button's displacement is a style property. Stupidly, GTK does not give you any way to set style properties programmatically.
- GTK buttons cannot distinguish between RELIEF_NORMAL and RELIEF_HALF
- GTK buttons cannot easily remove images
- GTK buttons cannot have their border completely removed -- if you click on them the border comes back. Eliminating focus on click has no effect.
- GTK buttons do not resize to properly fit around their subsidiary component. You are forced to hard-code a size.
- GTK buttons can EITHER have text OR an image but not both.
To Do.
- Figure out a way to optionally eliminate displacement.
- Figure out how to allow total removal of the border.
Class Spinner
(composition of gtk.Label, gtk.Button, and gtk.HBox)A replacement for gtk.SpinButton and hildon.NumberEditor. Spinner provides increase, decrease, and reset buttons. Unlike hildon.NumberEditor, the behavior of the increase and decrease buttons can be customized: typically either we add/subtract or multiply/divide (for scaling, for example). And there is not necessarily a minimum or maximum. The reset button resets to the original value. Unlike gtk.SpinButton, these buttons are large and easy to access. You can completely customize the display of the number, even including choosing strings from an array.
Persistence. Spinner's current value is persistent.
GTK Bugs and Misfeatures.
- gtk.SpinButton is very close to unusable. It has poorly-drawn, tiny increase/decrease buttons totally inapproprate for a PDA.
- hildon.NumberEditor requires a minimum and maximum value for no reason.
- gtk.SpinButton and hildon.NumberEditor cannot customize their display.
Class Notebook
(composition of gtk.Notebook and optional gtk.Frame)A cover for gtk.Notebook.
Persistence. Notebook's current displayed tab is persistent.
GTK Bugs and Misfeatures.
- Notebook does not propagate the 'destroy' signal to its children.
- gtk.Frame's y_align property does nothing -- thus you can't change the y-dimension position of the frame label; and hildon's default position of the label is screwed up, with descenders colliding with the frame.
Class Table
(composition of gtk.Table and optional gtk.Frame)A simplified cover for gtk.Table.
GTK Bugs and Misfeatures.
- gtk.Table does not dynamically increase the number of rows or columns as you add data.
- gtk.Frame's y_align property does nothing -- thus you can't change the y-dimension position of the frame label; and hildon's default position of the label is screwed up, with descenders colliding with the frame.
Class TextField
(composition of gtk.ComboBox and optional gtk.Image)A generally one-line text field entry which maintains a list of previously entered items so you can choose them from a pop-up list instead of re-entering them. The developer can also specify items in the list as well. Has an optional Image like Label and Text do.
Persistence. TextField's current text value and history of entered items (in the pop-up list) are all persistent.
GTK Bugs and Misfeatures.
- ComboBoxes do not copy arrays passed to them: thus if you give a ComboBox an empty array and give another the same empty array, Bad Things happen. This is of course totally undocumented.
Soon-to-Be Implemented Components
Class TreeView
(likely a composition of gtk.TreeView and gtk.ScrolledWindow)Under Development. A wrapper for gtk.TreeView which fixes a large number of bugs. We'll include the ability for the user to customize what columns are being displayed, what order they're displayed, and to always be able to change the column widths.
Persistence. TreeView's current configuration, including its hierarchy hide/show values and column settings, will all be persistent.
GTK Bugs and Misfeatures.
- Quite a swarm of GTK bugs. See here for some I've reported.
Class ScrollView
A wrapper for gtk.ScrollView
Persistence. ScrollView's current scrolled values will be persistent.
Class Window
A movable, resizable, miniaturizable window which floats over the main window.
Persistence. Window's current location and size are persistent.
GTK Bugs and Misfeatures.
- hildon doesn't allow movable or resizable windows.
Class List
A scrollable list of options.
Persistence. The current selected item(s) in the list, and its scroll position, will be persistent.
Class MenuItem
Under Development. A menu item. Can take an image to provide to Toolbar (see below).
Persistence. MenuItem's state, if it's a checkbox menu item, will be persistent.
Class Menu
Under development. A menu or submenu. Can be popped attached to a Button and popped up next to it, or attached to a MainWindow either as a per-window menu or as a per-application menu.
Class Toolbar
Under Development. A replacement for gtk.Toolbar which is totally user-customizable. My current strategy is to pop up a dialog box which lets the user assign any menu item to a button on the toolbar, and to delete or rearrange buttons as he prefers. The user should also be able to assign whole menus to buttons on the toolbar so if you click on that button the menu pops up. The application will have control of the left portion of the ToolBar to force certain items to be there always if necessary. The Toolbar will also have much larger icons and closer-spaced buttons. I might look into a way for the Toolbar to be called forth or made to go away using a button press (perhaps holding down the screen-maximize button). I'll also try to get rid of some of the wasted border space, but I suspect that's not immediately possible given certain style settings.
Persistence. The current toolbar settings will be persistent.
GTK Bugs and Misfeatures.
- gtk.Toolbar is totally uncustomizable.
- hildon's icons are horrible and tiny and must be replaced.