1. Trang chủ
  2. » Công Nghệ Thông Tin

Concepts, Techniques, and Models of Computer Programming - Chapter 10 pps

26 244 0

Đang tải... (xem toàn văn)

Tài liệu hạn chế xem trước, để xem đầy đủ mời bạn chọn Tải xuống

THÔNG TIN TÀI LIỆU

Thông tin cơ bản

Định dạng
Số trang 26
Dung lượng 165,71 KB

Nội dung

Part III Specialized Computation Models Copyright c  2001-3 by P. Van Roy and S. Haridi. All rights reserved. Chapter 10 Graphical User Interface Programming “Nowadays the growth of a graphic image can be divided into two sharply defined phases. The process begins with the search for a visual form that will interpret as clearly as possible one’s train of thought. [ ] After this, to my great relief, there dawns the second phase, that is the making of the graphic print; for now the spirit can take its rest while the work is taken over by the hands.” – The Graphic Work of M.C. Escher, M.C. Escher (1898–1972) This chapter shows a particularly simple and powerful way to do graphical user interface (GUI) programming. We combine the declarative model together with the shared-state concurrent model in an approach that takes advantage of the good properties of each model. To introduce the approach, let us first summarize the existing approaches: • Purely procedural. The user interface is constructed by a sequence of graph- ics commands. These commands can be purely imperative, as in tcl/tk, object-oriented, as in the Java AWT (Abstract Window Toolkit) package or its extension, the Swing components, or even functional, as in Haskell fud- gets. The object-oriented or functional style is preferable to an imperative style because it is easier to structure the graphics commands. • Purely declarative. The user interface is constructed by choosing from a set of predefined possibilities. This is an example of descriptive declarativeness, as explained in Section 3.1. A well-known example is HTML (HyperText Markup Language), the formatting language used for Web pages. • Using an interface builder. The user interface is constructed manually by the developer, using a direct manipulation interface. A well-known example is Microsoft Visual Studio. Copyright c  2001-3 by P. Van Roy and S. Haridi. All rights reserved. 690 Graphical User Interface Programming The procedural approach is expressive (anything at all can be done at run time) but is complex to use. The declarative approach is easy to use (a few simple declarations suffice to create an interface) but lacks expressiveness. The interface builder approach is easy to use and gives immediate feedback on the interface, but it lacks expressiveness and the interface is hard to change at run time. None of these approaches is satisfactory. In our view, this is because each is limited to a single computation model. This chapter gives an approach to building graphical user interfaces (GUIs) that combines a declarative base together with a selected set of procedural con- cepts including objects and threads. We provide a user interface toolkit that is both expressive and easy to use. In the context of the book, this has two goals: • To present the ideas underlying a practical tool for GUI design that gives the user a high level of abstraction. It turns out that the combination of declarative and non-declarative (i.e., procedural) techniques is particularly appropriate for graphical user interface design. • To give a realistic example that shows the advantages of programming with concepts instead of programming in models. We start from the declarative programming techniques of Chapter 3 and add state and concurrency ex- actly where it is needed. This is a practical example of combining several computation models. To a first approximation, our user interface specifications are just data structures, which can be calculated at run time. The declarative model makes it easy to calculate with symbolic data structures such as records and lists. This means that we can easily define and manipulate quite sophisticated user interfaces. For example: • We build a context-sensitive clock widget, that changes its shape and pre- sentation depending on an external parameter, which is the window size. Other widgets and external parameters are just as easily programmed. • We show how to generate user interfaces quickly starting from program data. It requires just a few simple data structure manipulations. The ideas in this chapter are embodied in the QTk module, which is part of the Mozart system [65]. QTk (“Quick Tk”) is a full-featured GUI design tool based on the declarative approach [66, 67]. QTk is implemented as a front end to the tcl/tk graphics package. It has been used to build GUIs for real applications. All the examples we give can be run directly with QTk. This chapter gives most of the key ideas underlying QTk but only shows a small fraction of the available widgets. Structure of the chapter The chapter consists of four sections: Copyright c  2001-3 by P. Van Roy and S. Haridi. All rights reserved. 10.1 Basic concepts 691 • Section 10.1 introduces the basic concepts underlying declarative and pro- cedural approaches and how we propose to combine them. • Section 10.2 gives an introduction to the principles of QTk and how to use it to build user interfaces. • Section 10.3 gives four case studies to progressively illustrate different as- pects of the approach: a simple progress monitor, a calendar widget, the automatic generation of a user interface from a data set, and a context- sensitive clock. • Section 10.4 says a few words about how QTk is implemented. 10.1 Basic concepts What are the relative merits of the declarative and procedural approaches to specifying user interfaces? The trade-off is between manipulability and expres- siveness: • The declarative approach defines a set of possibilities for different attributes. The developer chooses among this set and defines a data structure that de- scribes the interface. A purely declarative approach makes it easy to formal- ly manipulate the user interface definitions, e.g., to translate raw data into a user interface or to change representations. However, the expressiveness is limited because it is only possible to express what the designers initially thought of. • The procedural approach gives a set of primitive operations and the ability to write programs with them. These programs construct the interface. A purely procedural approach has no limits on expressiveness, since in its general form it defines a full-fledged programming language. However, this makes it harder to do formal manipulations on the user interface definitions, i.e., to calculate the user interface. This trade-off is not a temporary state of affairs, to be solved by some ingenious new approach. It is a deep property of computation models. As a language becomes more expressive, its programs become less amenable to formal manipu- lation. This is illustrated by the Halting Problem. 1 However, this trade-off is not as bad as it seems on first glance. It is still possible to define a model that is both manipulable and expressive. We can do it by combining the declarative and procedural approaches. We use the declarative 1 Assume a language as expressive as a Turing machine, i.e., it is based on a general-purpose computer with potentially unbounded memory. Then it is impossible to write a program that, when given an input program, determines in finite time whether or not the input program will halt. Copyright c  2001-3 by P. Van Roy and S. Haridi. All rights reserved. 692 Graphical User Interface Programming approach in those areas where manipulability is important but a limited expres- siveness is sufficient. We use the procedural approach for those areas where expressiveness is essential. To be precise, for each window we define four parts declaratively: • The static structure of the window as a set of nested widgets, where a widget is a primitive component of a graphical user interface. • The widget types. • The initial states of the widgets. • The resize behavior of the window, i.e., how the widgets change size and relative position when the window size changes. We define two parts procedurally: • Procedures that are executed when external events happen. These proce- dures are called actions. Events are external activities that are detected by the window. • Objects that can be called to change the interface in various ways. These objects are called handlers. The complete definition of the interface is a nested record value with embedded procedures and objects. Since it is a record, all the declarative parts can be formally manipulated. Since it has procedures and objects, it can do arbitrary computations. When designing a graphical user interface, we recommend to use the declar- ative approach as the primary approach, and to supplement it with procedural aspects to increase expressiveness exactly where it is needed. There is a recent standard for Web design, Dynamic HTML, that also makes it possible to combine the declarative and procedural approaches [61]. It uses character strings instead of records for the declarative part. It is not as tightly integrated with a pro- gramming language as the approach of this chapter. At the time this book was written, the performance of the declarative part was not yet adequate to support the design approach we recommend. 10.2 Using the declarative/procedural approach As much of the interface as possible is defined declaratively as record values. Records are a good choice for two reasons: they are very general data structures and it is easy to calculate with them. The GUI consists of a set of widgets,where each widget is specified by a record. Specifying a GUI is done not by defining a new mini-language, but by using records in the existing language. Programming a complex GUI then becomes a simple matter of doing calculations with records and lists. Since both are strongly supported by the declarative model, these calculations are easy to specify and efficient. Copyright c  2001-3 by P. Van Roy and S. Haridi. All rights reserved. 10.2 Using the declarative/procedural approach 693 User interface description: record containing action procedures and unbound variables (for handler objects) Window on screen Build user interface Actions and handler objects (interpret description to create handler objects and event thread) Data to be displayed Calculate user interface description ApplicationHuman user Figure 10.1: Building the graphical user interface 10.2.1 Basic user interface elements The GUI model of this chapter has five basic elements: • Windows and widgets.Awindow isarectangularareaofthescreenthat contains a set of widgets arranged hierarchically according to a particular layout. A widget is a GUI primitive that is represented visually on the screen and that contains an interaction protocol, which defines its interactions with a human user. A widget is specified by a record, which gives its type, initial state, a reference to its handler, and some of the actions it can invoke (see below). An interaction protocol defines what information is displayed by the widget and what sequences of user commands and widget actions are acceptable. • Events and actions.Anevent is a well-defined discrete interaction by the external world on the user interface. An event is defined by its type, the time at which it occurs, and possibly some additional information (such as the mouse coordinates). Events are not seen directly by the program, but only indirectly by means of actions. An event can trigger the invocation of an action. An action is a procedure that is invoked when a particular event occurs. • Handlers.Ahandler is an object with which the program can control a widget. Each widget can have a corresponding handler. Copyright c  2001-3 by P. Van Roy and S. Haridi. All rights reserved. 694 Graphical User Interface Programming Figure 10.2: Simple text entry window 10.2.2 Building the graphical user interface Figure 10.1 shows how a graphical user interface is built. It starts with the data to be displayed. This data is manipulated to create a record data structure, the user interface description. This description defines the logical structure of the interface as a nested record. The record contains embedded action procedures and unbound variables which will become references to handler objects. The record is passed to a procedure QTk.build, which interprets it and builds the interface. QTk.build does two things. • It builds a window using its underlying graphics package. • It sets up an internal mechanism so that the application can interact with the window. For this, it creates one handler object per widget and one thread per window. It registers the action procedures with the widgets and the events they are triggered on. The action procedures are executed sequentially in the thread as window events arrive. An example The easiest way to see how this works is by means of an example. Here is a simple user interface description defined as a record: D=button(text:"Click this button") The record D defines a widget of button type and the content of the text field gives the initial text in the button. Other widgets follow the same conventions. The record label denotes the widget type, the field names denote widget param- eters, and the field contents denote either the parameters initial values or the procedural parts of the interface (actions or handlers). Some of the widgets are able to contain other widgets. By using these, the complete user interface is a nested record that defines all the widgets and their logical organization on the screen. For example, here is a simple interface for doing text entry (see Figure 10.2): D=td(lr(label(text:"Type your name:") entry(handle:H)) button(text:"Ok" action:proc {$} {W close} end)) Copyright c  2001-3 by P. Van Roy and S. Haridi. All rights reserved. 10.2 Using the declarative/procedural approach 695 fun {GetText A} HTDWin D=td(lr(label(text:A) entry(handle:H)) button(text:"Ok" action:proc {$} T={H get($)} {W close} end)) W={QTk.build D} {W show} {W wait} T end Figure 10.3: Function for doing text entry Figure 10.4: Windows generated with the lr and td widgets The td widget organizes its member widgets in top-down fashion. The lr widget is similar, but goes left to right. This example has one action, proc {$} {W close} end , and a handle, H, which we will explain later. At this point, both H and W are still unbound variables. Create the window by passing D to the QTk.build procedure: W={QTk.build D} This creates a window, a window object W that represents it, and a handler object H. Now we display the window: {W show} The user can type text in this window. At any time, the text in the window can be read by calling the handler H: T={H get($)} This is usually done when the window is closed. To make sure it is done when the window is closed, we can put it inside the action procedure. To complete this example, let us encapsulate the whole user interface in a function called GetText. Figure 10.3 shows the resulting code. Calling GetText will wait until the user types a line of text and then return the text: {Browse {GetText "Type your name:"}} Note that GetText does a {W wait} call to wait until the window is closed before returning. Leaving out this call will return immediately with an unbound variable that is bound later, when the user clicks the button. Copyright c  2001-3 by P. Van Roy and S. Haridi. All rights reserved. 696 Graphical User Interface Programming Figure 10.5: Window generated with newline and continue codes 10.2.3 Declarative geometry In addition to the widgets themselves, there are two other aspects of a window that are defined declaratively: the geometric arrangement of its widgets and the behavior of its widgets when the window is resized. We describe each in turn. The geometric arrangement of widgets is defined by means of three special widgets that can contain other widgets: • The lr and td widgets arrange their member widgets left-right or top-down. Figure 10.4 shows the two windows that are displayed with the following two commands: D=lr(label(text:"left") label(text:"center") label(text:"right")) W1={QTk.build D} {W1 show} E=td(label(text:"top") label(text:"center") label(text:"down")) W2={QTk.build E} {W2 show} • The placeholder widget defines a rectangular area in the window that can contain any other widget as long as the window exists. The placeholder’s content can be changed at any time during execution. A placeholder may be put inside a placeholder, to any level of recursion. In the following example, the window alternatively contains a label and a pushbutton: placeholder(handle:P) {P set(label(text:"Hello"))} {P set(button(text:"World"))} Calling {P set(D)} is almost the same as calling {QTk.build D}, i.e., it interprets the nested record D and creates handler objects, but the visible effect is limited to the rectangular area of the placeholder widget. Copyright c  2001-3 by P. Van Roy and S. Haridi. All rights reserved. [...]... the widget is expanded Otherwise, it takes up just its natural size For the top-down direction, the n and s values play the same roles (“north” and “south”) For example, the description: lr(label(text:"Name" glue:w) entry(glue:we) glue:nwe) gives the window of Figure 10. 7 10. 2.5 Dynamic behavior of widgets The dynamic behavior of widgets is defined by means of action procedures and handler objects Look... WW={QTk.wInfo width(P)} WH={QTk.wInfo height(P)} _#Handle={List.foldRInd Views fun {$ I W#H#Handle Min#CH} This=(W-WW)*(W-WW)+(H-WH)*(H-WH) in if W . instead of programming in models. We start from the declarative programming techniques of Chapter 3 and add state and concurrency ex- actly where it is needed. This is a practical example of combining. III Specialized Computation Models Copyright c  200 1-3 by P. Van Roy and S. Haridi. All rights reserved. Chapter 10 Graphical User Interface Programming “Nowadays the growth of a graphic image can. directly with QTk. This chapter gives most of the key ideas underlying QTk but only shows a small fraction of the available widgets. Structure of the chapter The chapter consists of four sections: Copyright c 

Ngày đăng: 14/08/2014, 10:22