% % ISOTAS'96 paper % % Author: Erick Gallesio [eg@unice.fr] % Creation date: 20-Jun-1995 11:54 % Last file update: 1-Dec-1995 14:57 % \documentstyle[alltt,psfig]{llncs} \begin{document} % Command definitions \newcommand{\figsize}{\small} \newcommand{\stk}{\mbox{\sc STk}} \newcommand{\stklos}{\mbox{\sc STklos}} \newcommand{\Indextt}[1]{{\tt{#1}}\index{#1}} \newcommand{\Index}[1]{{#1}\index{#1}} \newcommand{\rrrr}{{\em R$^{4}\!RS$}} \newcommand{\codesize}{\small} \newenvironment{Code}{\begin{quote}\begin{minipage}{12cm}\codesize}{\end{minipage}\end{quote}} \title{Designing a Meta Object Protocol to Wrap a Standard Graphical Toolkit} \author{Erick Gallesio} \institute {Universit\'e de Nice~~-~~Sophia~Antipolis \\ I3S/CNRS - ESSI\\ Route des Colles - B.P. 145\\ 06903 Sophia-Antipolis Cedex - FRANCE\\ Email: eg@unice.fr} \maketitle \begin{abstract} This paper presents a graphical package which relies on the Tk toolkit and the Scheme programming language. The Tk package is a widely used graphical toolkit built upon the Tcl scripting language. Tcl was not designed as a general purpose programming language and its usage for large-scale software development is generally not suitable. To improve the programming level of the Tk toolkit, we have defined {\stklos}, a Scheme language with a CLOS-like object system. This alternative language has been used to embody the standard Tk widgets in a clean hierarchy of classes, which is presented here. The {\stklos} object system implementation is based on a Meta Object Protocol; this protocol and its usage for accessing the Tk toolkit in an efficient way are also presented here. \end{abstract} \section{Motivations} The Tk package \cite{Ouster-book} is a widely used graphical toolkit which provides a large set of widgets such as buttons, scrollbars, menus or text editors. With these high level widgets, one can build rather complex interfaces with little effort and without coping with the usual intricacies needed when programming under the X window system {\cite{X11}}. The Tk toolkit relies on a small interpretative scripting language named Tcl (Tool Command Language)~\cite{Ouster-Tcl}, a string based language with a shell-like syntax. Tcl is a small scripting language. To easily embed the Tcl interpreter in application programs, some usual programming languages capabilities have been set aside by its author. In particular, Tcl has a poor set of data structures reduced to character strings and associative arrays. It provides numbers, which are simulated with strings and which are, consequently, slow. In fact, a proper usage of Tcl consists in writing small scripts to {\em glue} large application components written in C or C++. However, experience shows that people are reluctant to use it in this way, and that they often prefer to write applications with a single programming language. Tk is indeed an application with an embedded Tcl interpreter~\cite{Ouster-Tk}. The interpretative nature of Tcl provides Tk a simple and attractive interface to develop simple graphical programs. However, the easiness afforded by Tcl for the design of small interfaces is misleading and it often encourages people to start heavy developments with this language. But, writing large applications with a language which lacks ways of structuring data tends to be more and more painful as the program grows. We think that this kind of usage is beyond the scope of a language such as Tcl, and we have tried to propose a solution for a better using of the Tk toolkit. In order to improve the Tk toolkit for large applications, we decided to replace Tcl by a conventional programming language. Furthermore, the {\em substitute} language must fulfill some important requirements; it must be \begin{itemize} \item {\bf high level} and provides useful data types such as structures, arrays, lists or strings \item {\bf small} enough for allowing to embed it in applications (as Tcl) \item {\bf efficient} so that most applications can be written entirely in this language without having to resort to C or C++ programming. \item easily {\bf extensible} so that user can investigate several interesting programming paradigms ({\em e.g.} objects, prototypes, actors,~\ldots) \item already {\bf defined}. This point is, of course, not mandatory, but we think that it is preferable to use an existing programming language, if possible, rather than defining a new one. \end{itemize} Scheme \cite{R4RS} is a Lisp dialect which satisfies quite well the previous points. It is a statically scoped language with a clear and simple semantic. Moreover, Scheme procedures are first class objects able to capture their creation environment. This language feature is important since it allows us to envision the coding of interfaces {\em callbacks} in a clean way. In this framework, we have defined {\stk}~{\cite{Gallesio93-1}, a graphical package based on Tk toolkit where the Tcl language as been replaced by a Scheme interpreter. {\stk} is a small and efficient Scheme interpreter. As Tcl, it is small enough to be used simply as a {\em glue language} which can be embedded in an existing application. Furthermore, the solid basis provided by the Scheme language affords the tools necessary for writing, and maintaining medium size Graphical User Interfaces (GUI). Nevertheless, we think the expressive power of Scheme is not sufficient to envisage its use for large-scale software development. In particular, the lack of an object mechanism increases the programming complexity of large applications. {\stklos}, the object extension of {\stk}, has been defined to alleviate this problem. This extension provides meta classes, multiple inheritance and generic functions {\em \`a la} CLOS~\cite{CLOS,CLtL2} or Dylan~\cite{Dylan}. {\stklos} has also been used to embody the predefined Tk widgets in a hierarchy of classes. Usage of these classes simplifies the core Tk usage by providing an homogeneous access to widget options and by hiding the Tk widgets low level idiosyncrasies. Moreover, as expected, usage of objects facilitates code reuse and definition of new widgets classes. Finally, we think that the object orientation of {\stklos}, as well as the solid basis of the Scheme programming language, afford therefore the tools necessary to envision writing, and maintaining, complex GUI. The rest of this paper is divided in three sections. The next section presents the {\stk} package and its object system. Wrapping the standard Tk widgets in {\stklos} classes and the influence of this integration in interfaces programming are described in section 3. {\stklos} implementation relies on a MOP (Meta Object Protocol), in the spirit of the one defined for CLOS~\cite{AMOP}. Section 4 presents this protocol and how it has been used to integrate the Tk standard widgets in the Scheme world. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% \section{Presentation of {\stklos}} Programming with {\stk} can be done at two distinct levels. The first level uses only the standard Scheme constructs and is quite classical. The second level gives access to {\stklos}, the object oriented extension of {\stk}. Of course, both levels can be used at the same time in a single program. However, most of the time, one will use the higher level, resorting to the lower one for specific purposes only. \subsection{\stk: the Basic Layer} Starting a session with {\stk} brings the user in the basic layer which gives access to an extended Scheme interpreter able to handle the Tk toolkit. With a little set of rewriting rules from the original Tcl/Tk library, and the Tk manual pages close at hand, one can easily build a {\stk} program using the Tk toolkit. Creation of new widgets (button, label, canvas, \ldots) is done by special {\stk} primitives procedures. For instance, creating a new button can be done as followed \begin{quote}\figsize \begin{verbatim} (button '.b) \end{verbatim} \end{quote} Tk uses a very special way to name widgets: a widget name is a kind of pathname which reflects its position in the graphical hierarchy of widgets. In this example, the name of the newly created button is ``{\tt .b}''. This pathname states that ``{\tt b}'' is a son of ``{\tt .}'', the root window. Note that the name of the widget must be {\em quoted} due to the Scheme evaluation mechanism. Calling a widget creation primitive, such as {\tt button}, builds a new Scheme object which is called a {\em Tk-command}. This object, which is considered as a new basic type by the {\stk} interpreter, is automatically stored in a variable whose name is the symbol given to the creation function ({\tt .b} in this case). A Tk-command is a special kind of function which is generally used, as in Tcl/Tk, to customize its associated widget. For instance, the expression\label{configure} \begin{quote}\figsize \begin{verbatim} (.b 'configure :text "Hello, world" :border 3) \end{verbatim} \end{quote} \noindent allows us to set the text and background options of the~{\tt .b} button. Of course, as in Tcl/Tk, parameters can be passed at widget creation time, and the previous button creation and initialization could have been done in a single expression, such as \begin{quote}\figsize \begin{verbatim} (button '.b :text "Hello, world" :border 3) \end{verbatim} \end{quote} \noindent Tk proposes a general purpose binding mechanism to associate a handlers to an external event ({\em e.g.} a key press or a mouse action). An event handler is automatically triggered by the library when the given event occurs. In Tcl, an event handler is a string which is evaluated at the global level, whereas in {\stk} it is a Scheme closure. The following expression adds a new event handler to the {\tt .b} button when the third mouse button is depressed over it: \begin{quote}\figsize \begin{verbatim} (bind .b "" (let ((count 0)) (lambda () (set! count (+ count 1)) (format #t "# of button press: ~A~%" count)))) \end{verbatim} \end{quote} This simple example shows that {\stk} handlers are cleaner than Tcl ones: the standard Scheme lexical scoping allows a handler to have its own private global variables (as {\tt count} here); on the other hand, a Tcl handler is a flat string unable to carry an environment. Even if closures afford a better expressive power for writing event handlers than Tcl strings, programming an interface resorting only to the constructions of the basic layer becomes rapidly tedious. In fact, the {\stk} basic layer can be considered as a kind of {\em assembly language} for interfaces programming and we will see in section~\ref{reification} how it can be used for the {\em reification} of the Tk widgets in {\stklos} classes. %%%%%%%%%%%%%%%%%%%% \subsection{\stk: the Object Layer} {\stklos}, the object extension of {\stk}, is close to the CLOS system~\cite{CLOS}; it is briefly introduced in this section. Note that we consider only the language aspects of {\stklos} here and we forget its use for integrating the Tk toolkit for a while. Definition of a new class is done with the {\tt define-class} macro. For instance, \begin{quote}\figsize \begin{verbatim} (define-class Point () ((x :init-keyword :x :accessor x-of) (y :init-keyword :y :accessor y-of))) \end{verbatim} \end{quote} defines the characteristics of a point. Two slots are declared here: {\tt x} and {\tt y}. \noindent Creation of new instances is done with the {\tt make} constructor: \begin{quote}\figsize \begin{verbatim} (define p (make Point :x 10 :y 20)) \end{verbatim} \end{quote} \noindent The evaluation of the preceding form builds a new point and initializes its slots {\tt x} and {\tt y} with the values 10 and 20. Slot content can be accessed by the two basic primitives {\tt slot-ref} and {\tt slot-set!}. These primitives are low level primitives and users often prefer to use accessors, since they generally lead to a clearer code. For instance, getting the value of the {\tt y} slot of {\tt p} could be done in the following way: \begin{quote}\figsize \begin{alltt} (y-of p) {\em ; or (slot-ref p 'y)} \end{alltt} \end{quote} \noindent since the {\tt y-of} accessor has been defined for slot {\tt y}. This slot can be set by the generalized {\tt set!}\label{set!} form, as illustrated by the following example: \begin{quote}\figsize \begin{alltt} (set! (y-of p) 1) {\em ; or (slot-set! p 'y 1)} \end{alltt} \end{quote} \noindent Now, we can define the {\tt Rectangle} class which inherits from the {\tt Point} class: \begin{quote}\figsize \begin{verbatim} (define-class Rectangle (Point) ((width :init-keyword :width :accessor width-of) (height :init-keyword :height :accessor height-of))) \end{verbatim} \end{quote} \noindent The instances of this class have four slots ({\tt x}, {\tt y}, {\tt width} and {\tt height}). Methods\footnote{In {\stklos}\cite{Gallesio95-1a}, the execution of a method rely on a subset of the CLOS {\em generic functions} mechanism (only primary methods are supported and the methods combination cannot be changed).} defined for instances of the {\tt Point} class can also be used for instances of the {\tt Rectangle} class. For example, the {\tt x} coordinate of a {\tt Rectangle} can be accessed with the accessor method {\tt x-of} defined before. Previous class definition represents rectangles with a reference point, a width and a height. This representation for rectangles is, most of the time, convenient but we sometimes need a representation using the coordinates of two opposite corners. In that case, {\em virtual slots} can be used\label{virtual-slot}. A virtual slot is a slot which is defined as a normal slot but whose allocation is declared as {\tt :virtual}. Such a slot has a null allocation and its reading (resp. writing) provokes the execution of a getter (resp. setter) function which must be provided by the user within the class definition. The getter and setter functions are defined with the {\tt :slot-ref} and {\tt :slot-set!} options. Here is another writing of the {\tt Rectangle} class using virtual slots: \begin{quote}\figsize %\begin{minipage}{12.2cm} \begin{alltt} (define-class Rectangle (Point) ((width :init-keyword :width :accessor width-of) (height :init-keyword :height :accessor height-of) \end{alltt} %\end{minipage} %\begin{minipage}{12.2cm} \begin{alltt} (x2 :init-keyword :x2 :accessor x2-of :allocation :virtual :slot-ref (lambda (obj) (+ (x-of obj) (width-of obj))) :slot-set! (lambda (obj val) (set! (width-of obj) (- val (x-of obj))))) \end{alltt} %\end{minipage} %\begin{minipage}{12.2cm} \begin{alltt} (y2 :init-keyword :y2 :accessor y2-of :allocation :virtual :slot-ref (lambda (obj) (+ (y-of obj) (height-of obj))) :slot-set! (lambda (obj val) (set! (height-of obj) (- val (y-of obj))))))) \end{alltt} %\end{minipage} \end{quote} In this new definition of {\tt Rectangle}, {\tt x2} and {\tt y2} are virtual slots. The getter and setter associated functions are lambda expressions which compute or set their value depending on other slots value. Note that a virtual slot accessor closure can change the value of standard slots in order to keep the system coherent. Since virtual slots do not imply memory allocation, they could easily be simulated with classical accessor methods. But, declaring a slot as virtual allows introspecting functions to ``see'' it as a standard slot. On the contrary, using a couple of methods to simulate such a slot would hide it to these functions. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% \section{Integration of Tk widgets} \label{reification} \subsection{The Class Hierarchy} This section presents how the standard Tk widgets have been embodied in {\stklos} classes. Each graphical object defined in the Tk toolkit such as menu, label or button is represented by a {\stklos} class. The corresponding classes constitute a hierarchy which is briefly described here. First, all the classes share a unique ancestor: the {\tt } class\footnote{End users will not have to use direct instances of the {\tt } class (all classes whose name begins with the ``Tk-'' prefix are abstract classes which should not be instanced; they correspond to the {\em implementation specific} classes of \cite{Kickzales:oopsla92}).}. This class defines a set of slots necessary to establish a communication between the Scheme and Tk worlds. In particular, two slots are defined in this class\footnote{The actual implementation is more complex, but to make easier the reading of this paper, we have simplified the definition of classes, and hence the class hierarchy.}: \begin{itemize} \item The {\tt parent}\label{parent-slot} slot contains a reference to the object which (graphically) includes the current object. \item The {\tt Id} slot contains a reference to the low level {\stk} {\em Tk-command} which implements the {\stklos} widget. This {\em Tk-command}, which is different for each class, is created during {\stklos} instance initialization. This slot establishes the link between the {\stk} and the {\stklos} layers and guarantees, by keeping a reference to the low level widget, a protection against GC recovery. \end{itemize} The next level of the class hierarchy defines a fork with two branches: the {\tt } class and {\tt } class. Instances of the former class are classical widgets such as buttons or menus whereas instances of the later are objects contained in a canvas\footnote{The canvas widget afforded by the Tk library allows 2D structured drawing.} such as lines, ovals or rectangles. Both kind of Tk objects are directly implemented as {\stklos} classes in a one-to-one relationship. A partial view of the {\stklos} hierarchy is shown in Fig.~1. Here are some important points: \begin{figure} %\centerline{\epsfig{file= hierarchy.eps, clip=, width=13cm,height=13cm}} \centerline{\psfig{file= hierarchy.eps,width=12.0cm}} \caption[]{\em A partial view of the {\stklos} hierarchy} \end{figure} \begin{itemize} \item In Tk, interface widgets ({\em e.g.} buttons) are first class objects, but canvas items ({\em e.g.} rectangles) can be accessed only through their containing canvas. Thus, actions on widgets or canvas items must be done in different ways. Accessing a canvas item option requires two references: one to the canvas which contains it and one to its identification (a number) in this canvas. In order to make canvas items first class objects, the class {\tt } defines the extra slot {\tt Cid} which contains the Tk identification number associated to the item. \item The hierarchical view of Tk widgets permits a better apprehension of the Tk toolkit, even if there is no notion of inheritance in standard Tk. According to Fig.~1, a {\em button} can be seen as a reactive {\em label}. As a consequence, the methods in charge of the look of a label or button text (font, foreground color, \ldots) can be gathered in the {\tt