|
From: Jeff T. <sjt...@ya...> - 2001-11-29 14:16:58
|
Here's the patch i mentioned last night (made against fresh cvs
trunk at 0830 EST today, 11/29). It is a modification
to PythonCard to support "modular, self-describing" widgets
(i.e., bundle the class, spec, and event bindings in a single file).
Much more detail below.
I hope this change (or something like it) can be added to PythonCard.
Most of my interesting ideas about PythonCard involve nonstandard or
composite widgets, which are difficult to quickly prototype, clone,
mod, ..., when i have to touch three different files.
Comments are welcome. I tried to make the changes as unobtrusive, and
obvious, as possible. If the code, or this rationale, are not lucid,
let me know. (I'll go get my asbestos suit from the dry cleaner).
I'm on the mailing list, so you don't have to cc: me.
How to apply the patch
----------------------
I finally created the patch by checking out another copy of PythonCard,
and doing a regular, local diff:
$ cd <myworkingdir>/PythonCardPrototype
$ diff -c -N ../../pythoncard-cvs/PythonCardPrototype . >
../mod-widgets.patch
I was able to patch it into a fresh PythonCardPrototype and run it:
$ cd <newdir>/PythonCardPrototype
$ patch < ../mod-widgets.patch
Then, make sure the new version is the only one on your PYTHONPATH,
and try the examples.
Note: it will be obvious that the idea is not fully fleshed out.
In particular, there are no awesome new modular widgets yet.
However, i only have a couple hours a week to play, and
i have been spending most of that just trying to keep up
with the cvs tree, rather than making progress on my apps.
I have not had a chance to test it on windows, but I don't think
there should be any issues.
Patch manifest:
---------------
modified files: (see change details, below)
spec.py
res.py
binding.py
new files:
WidgetLoader.py
NOTES.modular widgets (this file)
Refactoring PyCard to support modular widgets
---------------------------------------------
This is an experiment to support loading widgets that are not
part of the standard widget library. Each widget is self-contained.
The main inspiration for this work was how hard it was for me to
create a fairly simple composite widget that i wanted to reuse.
But, now that it works, there's a lot we can do with it.
For this experment, the widgets must be in the same directory
as the app (i.e., the widget path is [ "." ]). That will be easy
to change, to support local libraries, dynamic exchange of widgets,
dynamic creation of composite widgets, etc.
Examples
--------
The are two examples right now:
- "modular" extremely simple
- "halfassgrid" more interesting, but extremely immature
"modular" is a slight modification of the "minimal" app,
but uses a local MyTextField, instead of TextField. It really
just shows that it can be done, and may be a guide for starting
other widgets.
"halfassgrid" is the start of wrapping the wxGrid. But, it just
barely works. There's a ton of work to do on it. Again, it's really
just an example of how you would get started prototyping a wrapper.
I have a couple of others (including a pretty flexible VCR control),
but they need to bake some more.
Overview of changes
-------------------
Right now, a widget is composed of three main pieces:
- the main class typically wraps a wx widget
(usually found in widget.py)
- a "spec" metadata, including slots,
events it wants from toolkit, ...
(usually found in spec.py)
- event adapter translate events from toolkit to
the PythonCard event system
(usually found in wxPython_binding.py)
In PythonCard, these three pieces are found in three different files,
and it is somewhat of a hassle to add a new one.
This patch allows you to put all three pieces in a single .py file,
that is loaded on-demand.
This first change puts all three components in a single file,
and hacks the main application file (e.g., AudioNotesTool.py) to
"register" each component with its respective subsystem, *before*
creating the PythonCardApp.
Note that the _spec must now include a _class slot, in the
'info' section. It should point to the new widget class, e.g.
'info': {
'parent' : 'Widget',
'_class' : Grid,
...
Limitations/Disadvantages
-------------------------
1) The main disadvantage that i can think of is that there's no single
place you can go to get all specs, or even the complete list of
widgets you have available.
This limitation should be easily resolvable with a simple tool,
though, that either just grabs them and stuffs them in a single
file,
or generates some pretty pydoc-style html
2) More difficult to do a mass-refactor (like changing _getDelegate()
to _delegate).
3) I'm not sure I understand py2exe, so i don't know what will be
involved in ensuring that all necessary widgets are included
in a standalone app. It may require some sort of "rsrc analyzer"?
4) Does not do "recursive lazy-load", but should.
spec.py says that TextField must come before PasswordField.
If you lazy-load a widget whose parent is not yet loaded, i think
things will fall apart.
Should be fixable - just try again, with the parent. I'll
look into this
5) It's not clear that putting all event adapters in the file is
a great idea (same problem as what to do with I18N translation
files, i think). Eventually, the two should be split, and some way
added to look up or fetch adapters for the widget/toolkit
combination. It may be a non-issue though, if wx is the only kit.
Change Details
--------------
WidgetLoader.py
new file. tries to look up Widgets on special _widget_path
res.py
Spec.getEntry(): changed
if a ComponentSpec is not found the usual way,
try lazy-loading it using the WidgetLoader
Spec.tryLazyLoad(widgetModuleName): new
call WidgetLoader to try loading the
Spec.addEntry(componentSpec): new
ability to "register" new specs on-the-fly.
merges contents from parent
Spec.addEntryForSpec(specDict): new
wrapper around addEntry()
Spec._mergeContentsFromParentSpec(): new
does the same thing as mergeContents(), but
starts with ComponentSpecs versus dictionaries
widget.py
WidgeFactory.createWidget():
if widget class is not found the "normal" way,
can Spec entries to see if it has been lazy-loaded
binding.py
add an adapterRegistry: maps from <toolkit>_<className>
to a subclass of EventBinding
add registerAdapter()
EventBinding.__init__()
check the registry for adapters, before checking
the standard library of adapters
TODO
----
- unit tests
- no _spec in .py - fail gracefully
- no _eventBindings - make sure warning shows up in log
- no _eventBindings["key"] - make sure warning shows up in log
- make sure there's no "fault" the second time through
- need rsrc that creates two grids
- tool to gather all _specs into a single documentation set
- can add other metadata to the widget module:
- what icon to show on a palette, ...
__________________________________________________
Do You Yahoo!?
Yahoo! GeoCities - quick and easy web site hosting, just $8.95/month.
http://geocities.yahoo.com/ps/info1 |