A New Megawidget Library

There have been lots of Mega-widget libraries written at different points in time, and all of them have their merits. I am currently thinking of writing a new mega-widget library which would be a re-write of all the BWidget code into a SNIT framework. I think the BWidget widgets are great. I just think making them [some word or phrase appears missing here??] leaves something to be desired.

Does anyone think this is worth the time and effort?

How would you go about writing a new mega-widget library?

What widgets would you include?

My hope is that this discussion could bring about a mega-widget library (and possibly a framework) which could become the benchmark for Tk. I want this to be the widget library you can't live without (currently BWidget for me). How would you do it?


DC 2004-03-22 - I've submitted a TIP to include a megawidget package in the core. This package will not consist of an actual framework for defining widgets but will instead include several helper functions that a megawidget package can use to control the basic functions of megawidgets. The commands provided by the TIP are:

::megawidget::class <widgetClass>

    Define a new megawidget class.

::megawidget::commands <widgetClass> ?command ...?

    Define a list of subcommands available to widgets of <widgetClass>.

::megawidget::options <widgetClass> ?optionList ...?

    Define the options available to widgets of <widgetClass>

    An optionList is a list describing the option:

    <type> <option> <dbClass> <dbName> <defVale>

    <type> can be:
        boolean, int, double, string, enum, color, font, bitmap,
        border, relief, cursor, justify, anchor, synonym, pixels,
        window

::megawidget::create <widgetName> <widgetClass> <commandName>

    Create a new megawidget of class <widgetClass> from
    <widgetName>.  <widgetName> must already exist as a widget,
    and its widget command will be renamed to <commandName>
    and replaced by a new command that will handle the defined
    subcommands for the widget class.

::megawidget::cget <pathName> <option>

    Get the value of a megawidget option for <pathName>.  This
    function is also called as a result of '<pathName> cget'.

::megawidget::configure <pathName> ?option? ?value option value ...?

    Configure or query option values for the megawidget <pathName>.
    This function is also called as a result of '<pathName> configure'.

Everything else is defined by the author of the megawidget package. These commands are merely meant to speed up the common functions that all megawidget packages must reproduce (like cget and configure) by implementing versions of them in C. If this becomes a core package, megawidget writers can count on at least these basic functions being available in a Tcl release and can use them to write widgets that are much faster than just pure-Tcl alternatives.

escargo 27 May 2005 - This might be a late comment here, but I thought I should inject this: As long as you are providing for ways to define things, you should also provide a way to inspect things. E.g., if there is a ::megawidget::class <widgetClass> there should be a ::megawidget::info classes ?pattern? that returns the list of (programmer-declared) classes. Similarly for all the other defining operations. These are useful for extension writers and for test writers as well.


DC 2004-02-26 - After a day of coding and some preliminary testing, I have the following to report.

I have rewritten the mega-widget functionality of SNIT into a smaller library (about 13k) from scratch. I didn't start with SNIT itself, just the functionality and syntax. Why didn't I just use SNIT? Because some people didn't want to depend on an external library, and SNIT was actually SLOWER than BWidgets.

....

NEM Sorry to interrupt, but surely the argument "some people didn't want to depend on an external library" is not a reason to write another external library? I'm really slightly confused as to what the difference is between snit and your code - surely neither is in the core, and so both are external libraries. snit is in tcllib, however. Wouldn't it be better to concentrate on submitting improvements to the snit maintainers?

DC 2004-03-03 - Well, the point of all of this was to, in fact, develop an extension that could be merged into the core with a TIP. I'm pretty sure that the inclusion of SNIT in the core is not gonna' happen. I developed the new framework as a proof-of-concept and modeled the syntax after SNIT because I liked it. That's all. 0-]

NEM 4Mar04 - OK. I'd quite like to see a framework for creating widgets go into the core. However, getting an object system into the core is going to be hard. Firstly, itcl has already been officially bundled with the core (ok, noone has bothered to do the work, so it could be argued that that TIP has lapsed) -- I imagine a number of people might start saying "use itk/iwidgets, or at the very least, use itcl". Secondly, if we're going to put an object system into Tk (I assume that's what you're proposing), then why not put it into Tcl, and take care of all those object-like things which all behave slightly differently (interps, channels, fonts, etc etc)? I made a list of them all once, and it was quite long, and almost all of them worked in different ways (e.g. should [fconfigure $chan -options ..] be [$chan configure -options...]?). I think this would be a good thing, from a consistency point of view, but it comes back to the heated debate of which object system to "bless" with core inclusion.

Now, you could argue that this is simply a megawidget framework for Tk - no more, no less. However, I'm pretty sure these questions will be raised. If you can bear the arguments that will probably ensue, then more power to you.

....

With my new library in place, I took the ButtonBox widget from BWidgets and rewrote it in SNIT. The same exact code written for SNIT worked in the new library with no modifications. I then tested each implementation to get some rough benchmarks. All I did was create and destroy the button box 10000 times. This basically creates a frame widget and sets up the default options in each environment. And, for the record, the new framework creates a single ::megawidget namespace, and everything else is created under that, and it has the added bonus of handling options and error messages correctly so that a resulting mega-widget behaves exactly like a standard widget.

Here are the results (tested on a 2.4G Pentium with 512M RAM). Take them as you will:

    SNIT

    time "BBox .b; destroy .b" 10000
    822 microseconds per iteration

    BWidget

    time "ButtonBox .b; destroy .b" 10000
    570 microseconds per iteration

    New Megawidget Framework

    time "BBox .b; destroy .b" 10000
    360 microseconds per iteration

RS 2004-02-24 - First of all, "in contrast to Megahertz and Megabytes, a megawidget is not worth a million widgets". BWidget offers some popular things, like ComboBox, NoteBook etc., but custom "megawidgets" are often required. So make it easy on composing things - and best don't waste namespace names like BWidget does... For one-off usage, I often just pack or grid two or more things into a frame. Well, if it's the simplest thing that works...


DC 2004-02-25 - I don't see making a mega-widget framework without using namespaces. That's pretty much the clearest way to store information about a widget. And if it happens that we have a new namespace for each widget created, what's wrong with that? It will be destroyed when the widget is.

I did some preliminary testing by porting the BWidget ButtonBox to SNIT. The process was extremely painless, and I had the whole widget ported to a SNIT framework in about 10 minutes. This shows promise, as the whole BWidget library could be moved to SNIT with very little effort.

The only thing that might stop me is that when testing the two implementations, I found the BWidget actually faster. Not by much, but faster. About 500 microseconds per iteration faster. Anyone care to comment on that?


RS Namespaces are well and fine, but BWidget (and any successor) should claim a single ::BWidget namespace, and create child namespaces in that. Currently, after "package require BWidget; noteBook .n", the following are created:

 ::GlobalVar ::Widget ::DynamicHelp ::BWidget ::BWIDGET ::ArrowButton ::NoteBook

which is a bit much for me


Bryan Oakley 2004-02-25 - if it were me, I'd not base the widgets on snit. Snit is wonderful, but I personally believe megawidgets would be more universally accepted if they didn't depend on an external library.

As for namespaces, I think there should be a single toplevel namespace (eg: ::megawidget) with additional namespaces below that (eg: ::megawidget::combobox, ::megawidget::notebook, etc). Why? Good top-level namespaces are already being used by some widgets, and placing them all under an umbrella namespace will limit name clashes.

I've also found it convenient to create a namespace per widget but that's just my coding style. I tend to write code like this (not exactly, but this captures the spirit):

    proc mywidget {name args} {
        namespace eval ::mywidget::instance::$name {
            variable options 
            variable widgets
        }
        <create the widget...>
        interp alias {} $name {} ::mywidget::proxy $name
    }
    proc ::mywidget::proxy {name command args} {
        if {$command eq "configure"} {
            eval ::mywidget::configure $name $args
        }
    }
    proc ::mywidget::configure {name args} {
        variable ::mywidget::instance::${name}::options
        variable ::mywidget::instance::${name}::widgets
        ....
        $widgets(frame) configure -background $options(-background)
        ....
    }

I don't know if that's the most efficient way, but it works well in practice and means I don't need a large object system.

And yes, I know that snit is a wonderful mechanism to implement all the details of namespaces, instance variables, etc. I still find standalone widgets preferable to something that has external dependencies on an object system I might not otherwise require.

DC 2004-02-25 - Well, the idea is that whatever we end up with will become part of a core mega-widget library that can ship with the core and be depended-on to be there. I don't see any problem with SNIT being a candidate for that. If I write a mega-widget library from scratch, it's probably going to resemble SNIT in a lot of ways. Albeit, a lot smaller.

As of this writing, I've already gotten most of what I need done in about 11k of code. This mimics almost all of the SNIT functionality that we would need for a mega-widget library. Of course, someone will have an objection to distributing an extra 11k (probably 20k by the time I'm done) with the core. I just think we need mega-widget support as part of the core.


Isn't the new 'ensemble' stuff in Tcl 8.5 supposed to make all this easier? Shouldn't we be using that? If it's not quite adequate, shouldn't we extend it until it is? I also think a megawidget library would get more acceptance if it didn't rely on any specific object-model (of which Tcl's has a dozen or so, by now). Vince


DC 2004-02-25 - Well, that's what this page is for. What are the requirements of a mega-widget framework? I'm willing to do the leg work and make the library once we (those of us who develop mega-widgets, not just use them) decide what is required. We've all said that some amount of code at the core level is necessary to truly implement this kind of thing (IE: Adding -class options to all widgets, etc...), but I want to get something going now.

Maybe the ensemble code could be used. Is there a working patch of it anywhere? (Vince adds that the ensemble stuff is in Tcl 8.5 already -- the TIP is final, just read the namespace ensemble documentation)

I definitely like the idea of a single namespace and everything else is a child of that. That's kind of what got me started on a lot of this. Once the framework is in place, I plan to port BWidgets to the new framework as a starting point. My hope is that the framework itself would be distributed with the core, while the widget sets would be separate.


Peter Newman 2004-02-26: I don't know whether we need a new megawidget collection or not. But what we do need is inheritance and reusability. By that I mean that Tk widgets should be designed in such a way that there is a standard and well-documented method of adding new features, or replace existing features, of existing (core or complying,) widgets.

Take the button widget for example. Heaps of people have complained about the fact that you can't set a text only button's width/height in pixels (only in characters/lines). But rather than designing a new button from the ground up (as BWidgets and many other megawidget sets have done), a Tcl programmer should be able to correct this by either; adding a new (say) -pwidth/-pheight option to, or; replacing the existing -width/-height options of, the existing Tk button.

I realise that there are hacks to do this with current Tcl/Tk. But really it shouldn't be a hack. It should be a standard feature of Tk. Hopefully the "frameworks" and "object-oriented extensions" being talked about above, will include and make possible this sort of thing.

For example, we should be able to go:-

 button .mybutton -label {Hello World} ;
 pack .mybutton ;

 proc MyPixelWidthOptionHandler { ...whatever... } { ...whatever... } ;
 proc MyWidthOptionHandler { ...whatever... } { ...whatever... } ;
 .mybutton optionadd -pwidth MyPixelWidthOptionHandler ;
 .mybutton optionreplace -width MyWidthOptionHandler ;

In my opinion, coming to an agreement on a megawidget framework, then proceeding to generate a common set of megawidgets on that framework, provides the community with at least two things:

  • a source for useful widgets without having to code and debug them
  • source code examples for cases when the useful widgets do not quite meet the specifications

I'm uncertain whether a purely script based framework will scale appropriately; I keep remembering the rewrite of Tix from script to C, the struggles that the incr tcl community had with performance of incr widgets, etc.


DC 2004-02-26 - Well, the whole thing could certainly use some assistance from C. This is where the Tk core needs to have some features to support the making of mega-widgets. Obviously, any widget which is found to be extremely useful and in need of speed will eventually get converted to C (see: panedwindow, spinbox, labelframe), but that doesn't mean that Tcl developers shouldn't have the ability to develop their own widgets too. I, personally, hate writing things in C unless I really, really have to. Mostly because I can get it done really quickly in Tcl (that's why I use the language), and secondly because I don't have to recompile and then compile on every platform I want to support.

For this project to be truly useful, it needs support on both the C and the Tcl side. I'm looking to make a library of mega-widgets which can be distributed with the core (or at the very least, as the defacto standard). These widgets, over time, will most likely get moved into the core and be written in C as the need arises. The problem is that we don't often have the resources to make this happen as quickly as it is sometimes necessary.

Remember, 8.5 is supposed to be a revitalization of Tk. This whole idea is meant to support that effort.

Vince very much agrees with these last comments. This suggests that any new megawidget package should be written as, perhaps, a composition of two things:

  • a wrapper around Tk to emulate the stuff we'd like in C in the future (whatever that may be -- user data in widgets, better focus handling, etc)
  • a new megawidget package which makes use of the wrapper's functionality

WHD 2004-03-03: Some thoughts about Snit and the dependence of megawidgets on external libraries: de facto, any set of megawidgets is going to rely on some kind of megawidget framework. Users of the megawidgets shouldn't have to know about that framework--but surely it should be available for those who'd like to use it?

That said, I hate using packages that depend on other packages. But there's no reason why a megawidget set couldn't include a private copy of Snit, slightly modified to load into the megawidget set's namespace. Snit's implemented in such a way that two such copies could happily coexist.

It doesn't surprise me that Snit's slower than BWidget; it adds quite a lot of sugar, and sugar can be expensive. And to date, I've not had time or energy to do any amount of performance tuning--especially since I hope to reimplement Snit using ensembles when 8.5 is released.

A question for DC: Does your partial reimplementation of Snit pass the Snit test suite? There are many, many little details, especially regarding widget destruction, that are quite tricky to get right. But if it does, and it's faster, I'm not at all averse to updating Snit to use your techniques.

Also, you say your version produces widgets which look more like built-in widgets than Snit does--could you send me a list of the specifics?


DC 2004-03-03 - My original plan was to just create a new megawidget library that was BWidgets written in SNIT. Upon testing, SNIT was way slower than BWidgets, so I started off on a quest to create a framework that was both faster than BWidgets and syntactically like SNIT (since, as I've said, I really like it).

The "widgets look more like built-in widgets" comment was basically adding more "sugar." The widgets created in the framework I have now return proper error messages as well as handle shorthand commands and options. This is something lacking in both BWidgets and SNIT widgets. Both would choke if I tried to do:

    ButtonBox .b
    .b conf -def 0 ; ## shorthand for .b configure -default 0

Because neither one can pick up shorthand subcommands or options. The error messages were another thing that bothered me. When I'm testing, I like to be able to see all of the available subcommands of a widget. Neither BWidgets or SNIT offer error messages that are helpful in this regard.

(WHD: Snit doesn't, because (in the presence of "delegate method *") it doesn't know what the available subcommands are.)

I just added more "sugar." 0-]

After having played around with it more, I'm still not happy with the results. The object creation is as fast as BWidgets (which still ain't that great when compared with widgets written in C), but the configure and cget methods are much, much slower. Now, we're talking 100 microseconds here, which ain't much, but it still bugs me. Especially since the Tk core widgets can handle a configure or cget request in about 2 microseconds.

So, I'm now working on a C / Tcl combination where the extension will use Tcl's built-in functions for handling options and subcommands at the C level while the bulk of the code will still be pure Tcl. We'll see how it goes. 0-]


I am thinking building an OO extension agnostic framework (that is to say, a megawidget framework which doesn't care whether you are using snit, incr tk, etc.) would be the most valuable thing; otherwise, the ego/territorial/pick your favorite term issues overwhelm the technical issues.


Anonymous comment on 27 May 2005: I suggest that the megawidget framework being developed be as much like Tk as possible, so that the merging of it into Tk move as rapidly as possible. This means that coding style, comment style, variable and function naming all match the Tk styles. Test suites and docs need to develop. And taking a discussion of the goals and status of this work to the Tcl core mailing list (see TCT) would be very useful at this point, since you don't want to spend weeks on this effort only to have basic concerns from the TCT slow down the process.

Lars H: Hasn't DC already done that? See TIP 180 , Add a Megawidget Support Core Package. (As I recall it that TIP got stuck in Itcl quagmire -- there's already a decision to make Itcl a core package, so any proposal that makes Itcl less necessary has to be attacked in a show of support for Itcl -- or something.)