Structure Hiding Pros and Cons

George Peter Staplin: It has occured to me that much of Tk is about hiding the structure of a widget or gadget. This greatly reduces the stress of using the toolkit, but it can become limiting.

The canvas is a great example of hiding structure. If say you want to create a polygon with a red color it's as simple as:

 pack [canvas .c] -fill both -expand 1
 .c create polygon 10 10 150 150 200 200 130 100 -fill red

Tk is hiding many aspects of the structure of that object you just created. For instance there is a graphics context/GC, an XColor allocated, information about how to redraw it, etc. The canvas takes care of redrawing it when an Expose or Configure event occurs without us telling it to.

Now obviously if we just created objects this way it would be limiting and we would run out of memory. So, the canvas returns a unique id for each object and that can be considered a name for our structure. We can delete the object and the hidden structure behind it.

The concept of a tag (which may have originated in Ezd) was introduced to allow having an easier method of referring to an object than the tag returned by the creation. The tag also allows classifying objects.

This all sounds great, but then you decide "I want to manipulate Object to do X." Usually this means writing a procedure that manipulates the tag/object via the coords or scale subcommands. As the amount of changes you want to make to the object increases your number of global or namespace procedures grows. Then you may decide in the future "I want to create another instance of this." So, then you create two or more widgets and probably redesign some of your work to restructure the code to work in this manner. The canvas and your procedures are not a unit. Changes made in one part may adversely affect another. The end result is that the code may be more difficult to understand. Then you may wish to inherit certain aspects of your object manipulation. This would of course make it even more complex. These problems have been solved years ago with Smalltalk and other systems.

On Drawing Gradients on a Canvas I wrote a simple procedure that uses the canvas. It ends up consuming a lot of memory if used for random colors in a loop. I also found that it was much slower than in C, because of each item being an object. I didn't really want or need each line to be an object. An obvious yet-limited solution is to use a photo image, but alas they are even slower due to the structure being hidden and the design of the photo subsystem.

An obvious solution is to write the drawing code in C. This introduces yet another problem, because the Xlib emulation is incomplete and often functions are empty on non-X platforms. It's not documented what works and what doesn't (though I'm documenting some of it now for a future man page). Another would be to use Tk_CreateItemType providing all of structure there. But that's a lot to do just for drawing simple lines for a gradient. Another approach is to write a widget that draws gradients and then embed that, but that has problems because you can't draw on top of window items. You could do so however if the GC was exposed to allow you to enable IncludeInferiors drawing.

Years ago I wrote a program that would draw a canvas on the X root/background window. I found that the structure for the canvas wasn't exposed. I couldn't directly access the memory for the Pixmap etc. I solved this problem by including a private header file from the canvas with my program. This didn't work with future releases. There wasn't really a way around it unless I added the feature to Tk. What I was doing was considered inappropriate, but there wasn't another way unless I patched Tk.

As the years have gone by I've noticed several useful extension for Tk that patch the Tk sources, because a structure isn't exposed. Some extensions, such as BLT even replace core widgets IIRC. As Tk is updated of course they break, and they are often a pain to compile.

What do we gain by encapsulating structures and hiding memory? Interfaces may be simpler. Extensions may clash less.

Sometimes having access to a structure is better than none at all.

In my Smalltick Widgets with Inheritance I expose more of the internals of the widget. The drawing of a gradient is easier because more structure is exposed. I expect users of my classes to use the internals for interesting things I hadn't thought of. I still have public methods named with the prefix '-' but I don't hide the more powerful methods that the public methods may use.


PWQ 28 Jun 04, I think one has to accept that when push comes to shove you are forced to drop to C to do the work. This is just the fundamental structure of the language.

The above sentiment is seen expressed in lots of different areas with TCL and the question should be asked (why does this occur?) and someone should answer (hint TCT).

In the case put above, I don't personally believe that the main issue of access to underlying structures per se, rather the fact that any core item in TCL that is extensible is only so at the C level.

Consider the following:

   .canvas create rect ... -fill {solid blue}
   .canvas create rect ... -fill {gradient lightblue darkblue}
   .canvas create rect ... -fill {{solid blue} {starburst topleft}}
   .canvas register fill myfill -command hyperfill -pixels list
 or
   .canvas register fill myfill -command hyperfill -pixels binary
   .canvas create rect ... -fill {myfill green}


 proc hyperfill {widget id bbox pixels args} {

        set newpixels [list]

        foreach point $pixels {
                ...
        }
        set newpixels
 }

Another weakness of the canvas widget is the inability to group items. This would be a useful ammendment along with the ability to merge items into one composite item to reduce the number of items the canvas must process.

The only comment I would make about hidden data structures, is that I see it as a failing that the internals of TCL don't use Tcl_Objs to implement the structures. This would allow them being exposed to the TCL level in callbacks and passed as opaque values.

Another burning question is Why does everyone go it alone and implement their own extensions in a vacuum rather then as joint projects.

I put it that the reason is they way the TCT manages change. It does not foster and coordinate changes outside of its own scope. Leaving everyone to fend for themselves. The TCT by definition is concerned only with the CORE, and extensions can burn in hell. If your extension needs core modifications then hope that you can suck up to a TCT member to have it implemented.


Category Discussion