Table of Contents
A skeleton framework for generating the basic MineSight® Grail Panel Application (GPA). The framework is an abstraction of all the mechanics of an application that uses the panels/tree idiom.
Currently it allows you to customize the menu behavior, event handling, and loading and saving of application specifications. The details for performing these extensions/overrides are provided in this document.
In the future, as the GPanelApp gains more features, there will be more items that will be exposed for customization.
The idea was to construct a somewhat flexible framework for working with the 'panel/tree' concept. Initially the panel/tree concept was designed for the medstyle-to-python-style procedures within the compass package. However, as the panel/tree concept was being stretched to other domains (for example, IP), we found that there were serious coupling between logic that was compass specific and logic that was GUI specific. The GPanelApp is really only the basic GUI specifications that lacks problem-domain specific logic.
The frameworks goals are,
- Flexible enough to allow one to customize as much as one wanted.
- Abstract enough to hide as much of the magic-low-level window logic as possible.
Now to create your own version, which is no longer dependent on compass-centric concepts--you would just inherit and extend/override the components that you want in your application.
By "extend/override" I mean that you really have two ways to customize your own GPanelApp. Either you can extend the existing behavior to define some added features, or you can override the default behavior as you please to define your application better. See the following sections for more information on extending/overriding the GPanelApp class.
For starters lets assume that you don't really care about what the framework does, how it operates, etc. You just want to stuff a bunch of panels into the framework, and start prototyping, and you'll work out the finer details of your application later.
The basic GPanelApp comes with a very ruff sketch of a default implementation, one that lacks any convincing menu structure or behavior. Except the GPanelApp knows how to exit the application really well.
An example of using the GPanelApp framework straight out of the box would be as follows,
The GPanelApp requires at least these four steps to start execution.
- The initialization process, goes and starts up the Tkinter environment.
- The build process starts to construct the widgets. The build you can safely assume that all the application's widgets have been constructed.
- The show attribute tells the application to show itself to the user.
- The mainloop call starts the event handler.
That is all there is to it if you don't want to bother defining menu structures, or any of your own fancy event handling.
By application specifications, we are referring to all that information that you would like to be persistent from run-to-run, regardless if the user decided to save it or not. Anything from, last files used, to last window position, to where the pane separator is specified.
There are two functions that you have to override/extend to take advantage of this behavior. They are loadappspec and saveappspec.
Lets define the loadappspec for our custom application, assuming that we already have a WinRegistry (see grail.misc.winregistry) object called 'registry' pre-configured,
Its important to note that you have to define at least three specifications specs, meaning, that if you decide to override the loadappspec, you should be prepared to handle the following specs,
'wm_state'
This is fed into the root windows wm_state() method, it indicates if the window should be minimized, maximized, etc... Need to return a string value.
'geometry'
This is the standard Tkinter windows geometry and is sent to the root's geometry() method. Need to return a string value.
'navigatorwidth'
This is the width of the navigator window, and is used to configure where the middle window separator goes. Needs to also be a string value.
If you don't really care about these specs, and you just want to charge ahead and define your own, then you could do something like this when overriding the loadappspec attribute,
The GPanelApp object's loadappspec will provide reasonable default values when requested. This would be an example of extending the default implementation, you just hook in and do what you like, then let the default behavior take over for everything else.
Now for saving specs, you would override the saveappspec function. The default is just an empty function, so essentially, the default doesn't save anything. If we want to save specs we need to do it ourselves. As an example, lets consider saving of the three mandatory specs, again assuming that we have a WinRegistry object already pre-configured and called 'registry',
That is all there is to it. With the registry object, the saving becomes rather trivial: the spec becomes the key, and the value is what is tossed under that key.
Off course, you may have your own means of saving this specification information. Maybe you want it to go to a file or to go across the internet. It doesn't really matter, you just need to define it in the saveappspec() function.
So your application wants to take a few more arguments upon initialization? How to do this is best explained with an example, lets say we want our app to take one additional variable, for worldwide consistency, we will call it 'foo',
There that's all there is to it. Now the same methodology can be applied to any of the methods, where you just want to extend the behavior, but still execute the default code provided. For example, consider the build function,
The following classes are defined within this module.
Defines the folder structure.
Note
Folders can be nested inside the contents supplied to a GFolder. In other words you can do the following,
And you would get the following 'tree' structure,
[Root] + [FOLDER1] + MyPanel1 + [FOLDER2]
An item, that contains a panel and a name.
This is a basic part of the panel/folder configuration scheme used in the GPanelApp widget. It represents the panel's information (state, name, etc...).
Some further points,
Specifying a rtv with an IntegerRTV object overrides the state variable.
If you do not define a name, then there will be a search for the name attribute on the panelclass, specifically for NAME. This is also known as 'auto-naming'. For example,
class MyPanel(GPanel):
NAME = "My sample panel"
mypanelitem = GItem(panel=MyPanel)Will set the mypanelitem name to be "My sample panel". The above is equivalent to,
class MyPanel(GPanel):
pass
mypanelitem = GPanel(panel=MyPanel, name="My sample panel.")Sometimes its better to have the name of the panel, and the panel definition lie closer together as in the first example.
Translates a panel configuration into user HTML documentation. Mainly for internal usage only.
Show help for all the panels.
This generates two distinct sections, one is a table of contents that mimics the panelconfiguration and links to the further documentation within the second part of the help.
Allows you to build and customize a 'folder/panel' type of application.
Defines how the menu is going to be layed out. Part of build.
To generate your own menu structure, override this method, and make sure a GMenuBar menus keyword is stored into 'self.menus'.
This is the default implementation used for an example,
Note
Although this may seem odd, the 'self.menus' attribute must be defined with the menubar information if you decide to override this method.
If you do not, you will most likely get an AttributeError referring to there not being a 'menus' attribute.
Returns window specification information. This provide a means to perform persistence on what ever you want for your application window.
When this was first built, there were three specs that must be satisfied if you decide to override this method. They are,
- 'geometry' (can default to: GPanelApp.DEFAULT_GEOMETRY)
- 'wm_state' (can default to: GPanelApp.DEFAULT_WM_STATE)
- 'navigatorwidth' (can default to: GPanelApp.DEFAULT_NAVIGATOR_WIDTH)
If you don't want to define these, and are comfortable with the defaults, just try extending this method like so,
Calling the GPanelApp object's loadspec method will handle all the default conditions.
This function should always return a string.
Forwards to the root window (the application) starts event loop.
Your execution thread will return from this function call when the user destroys the window (via some 'exit' event). Or if the destroy method is called.
Generated when an error is written sys.stderr.
This gets called when an error is sent to sys.stderr. Default behavior is to write the message to the statusbar via statusbarwrite.
Opposite of loading! This saves the 'spec' with the given value. In this case don't have to handle the 'geometry', 'wm_state', and 'navigatorwidth' spec's, because the default implementation is to completely ignore them (no saving).
Typically this method is called during the savedialog method.
Saves all pertinent values to via the saveappspec method. This method should be called prior to calling destroy to maintain dialog persistence.
Extend this method if you wish to save other values.
Requests that a particular panel be shown.
Warning
This assumes that the build has occurred, otherwise the GPanelApp will fail horribly.