Published as part of the Ubuntu Community Learning Week
Custom widgets Notes
Plain Text version: http://dl.dropbox.com/u/123544/custom-widget-notes.txt
Introduction
- This is a collection of realisations i have had with regards my adventures creating custom widgets
- There are often many ways to accomplish similar ends
- there may be more correct ways to accomplish things than as presented here
- this will be a python/gtk/cairo -centric chat
So why would we want to use custom widgets?
- solve specific problems in interesting and (perhaps) more effective ways
- Add visual flare and identity to an application
- encourages experimentation with new ways of interacting with an app or providing feedback
- great way to get to know the nitty-gritty of toolkits
- If you are of the right disposition, they can be really fun to code and test
- If you get good at it, you can free yourself from the default toolkit and let your imagination run wild
What are the draw backs?
- Can be difficult to test, and get right!
- Many behaviours that may need implementing to reach the status quo
- Accessibility issues
- May not coexist well with different themes or the 'feel' of the surrounding GUI components
Before you start: get familiar with existing widgets
- Its best to research what existing widgets are capable of. this can save you a lot of time and if often an eye-opener
- Even if you conclude no gtk widget exists sufficient to your needs, its instructive to see how other widgets are structured, i.e. api and classes may hint at a good architecture for your own widget
- You are going to have to subclass a gtk widget at some point, which is best?
- Eventboxes or DrawingAreas or some other gtk widget?
- Be familiar with what a gtk.Widget provides for free so you dont go implementing unnecessary things
gtk.EventBox
- IMHO, Provides a great canvas for creating custom widgets
- receive events such as button-presses, button-releases, key-presses, focus-in... etc
- A lightweight starting point
- I am a fan of gtk.EventBox.set_visible_window(False)
- Your event box no longer draws its own background
Why is having no background good?
- You can still draw to the area allocated to your EventBox either with cairo or gtk.Style
- You can achieve a composited effect
- A parent container could have an unusual background, say a coloured gradient
- You may want to draw a non-rectangular shape to your EventBox on-top of a funky background
Lets see some code
Python Script: http://dl.dropbox.com/u/123544/custom-widget-1.py
- demonstrates drawing on a parent container
- drawing to your 'own' EventBox
- connecting to events
- button-press/release
- key-press/release
- focus-in/out
Fitting in: part 1 - gtk.Style
Python Script: http://dl.dropbox.com/u/123544/custom-widget-2.py
- gtk.Style provides a palette of colours derived from the current gtk-theme
- You can access these when drawing your widget or once your widget is realised
- gtk.Style also provides simple draw methods, handy ones include:
- paint_layout
- paint_box
- paint_flat_box
- paint_(v|h)line
- Caveat 1: Some themes do not implement all paint_* methods :(
- Caveat 2: Often to achieve the desired effect you need to supply the correct 'hint' to the paint method
- Caveat 3: Hint names can be difficult to track down, usually Google is your friend, failing that, gtk source code is a fall-back
- Caveat 4: Even with the correct hint things may not draw as expected ;(
- You may need an actual & realised widget to paint from ;O
- An example: Say you want to imitate the frame of a gtk.Viewport, achieve the correct colour etc (Note: python-like psuedo-code follows...)
- vbox_widget.style.paint_hline(*args, window=vbox_window, hint='viewport') = Fail
- the colour does not reliably match the colour of an actual viewport frame
- viewport_widget.style.paint_hline(*args, window=vbox_window, hint='viewport') = Success
- we needed to call the paint_hline method from an actual gtk.Viewport.style to achieve the intended result
"style-set" signal
- If you store colour information or font size, do it in a way such that colours and dimensions can be updated when the user changes themes
- You can achieve this by connecting to the "style-set" signal
Fitting in: part 2 - cairo and mask_surface
- Firstly, FYI: cairo rocks. Learn it well :-D
- using cairo and masking is a another effective way of doing fancy visuals that fit in with their surroundings
- Its tricky to explain but,
- With CairoContext.mask_surface(...) you can provide an image with an alpha channel ((semi-)transparent pixels)...
- and composit it with a colour derived from the gtk-theme
Cool! So we wrote a custom widget that looks great. What now?
Remember accessibility!
What is accessibility?
Python Script: http://dl.dropbox.com/u/123544/custom-widget-3.py
- Its additional contextual information about what your widget does and how it relates to other components around it
- Example: A visually impaired person may require assistance from a screen reader such as Orca. For Orca to provide meaningful assistance to our user, your widget needs provide information about itself to Orca.
The basics of implementing accessibility
- Starting with an existing gtk widget gets you a long way, nevertheless...
- Accessibility can be a tricky topic to get 100% right but there are some basics you should always cover...
- Firstly, install Orca and Accerciser from the repositories.
- Testing with these apps will give you a good indication as to how well your app/widget is doing accessibility-wise
- Read up on the atk toolkit, its the gtk of accessibility
- What is your widgets name and description?
- What is its role?
- How does it relate to other components?
Atk minima:
- a11y = my_widget.get_accessible()
- a11y.set_name('..') # this is different from gtk.Widget.set_name()
- a11y.set_description('..')
- a11y.set_role( atk.ROLE_* )
- a11y.set_relationship(..)
Thats all for now. Go wild and experiment with your widget ideas!!!!