QTcl programming
Introduction
QTcl is a Tcl extension module that provides a Tcl interface to the Qt widget set. This means that you can create Qt applications written in Tcl. When the QTcl module is loaded into a Tcl environment such as Geocap, it adds several commands to the set of built-in Tcl commands. These commands are all geared towards creating a graphical user-interface (GUI) to your application.
This documentation gives an introduction to how QTcl works and contains a substantial amount of example scripts, almost all of which are complete programs that can be executed as-is. The reader is encouraged to use and modify these example scripts according to his own needs.
The reader should be familiar with the basics of Tcl programming.
A good way to start reading this documentation is by skimming through the different QTcl Widgets. This gives an immediate visual presentation of what QTcl is about.
In this section:
Typographical Conventions
This documentation follows certain typographical conventions that serve as a visual cue for grouping terms and describing syntax.
Upper case is used to denote objects, like QWidget. This is not done for links. The corresponding command is written out using lower-case: qwidget.
Command arguments are written like this:
qspinbox min max [cmds]
This means that min and max are entities that should be replaced by actual values, followed by an optional command.
General
Being a Tcl module, the basic syntax of QTcl is just that of Tcl. However, the commands introduced by QTcl have some common characteristics that make the commands easier to use and remember.
The central philosophy of QTcl is to keep the number of command arguments to a minimum. When necessary, additional functionality is provided by separate commands. This prevents the programmer from repeatedly typing advanced arguments when they are not needed.
All QTcl commands begin with the letter 'q' to distinguish them from other Tcl commands. Also, commands and arguments are lower case. Statements follow the syntactical convention in Tcl by having a command followed by arguments :
command argument1 argument2 ...
Most QTcl commands fall into one of two categories :
- Commands that create widgets
- Commands that modify widgets.
The syntax for widget-creational commands are always as follows :
widgetname argument1 argument2 ...
Below is an example of how to create a spinbox with range 1 to 10 :
qspinbox 1 10
Commands that modify existing widgets take the following form :
command [targetwidget] argument1 argument2 ...
The targetwidget argument is provided when using explicit syntax, otherwise the current widget (implicit syntax) is used. The following example demonstrates both versions :
# Explicit syntax set widget [qwidget] qsetwindowtitle $widget "My Window" ; # Explicit target widget argument ... # Implicit syntax qwidget qsetwindowtitle "My Window" ; # Using the current widget
Explicit and Implicit Syntax
QTcl employs two different syntactical conventions : Explicit and Implicit. Both achieve the same task, and the commands are the same. The only difference is that a command using an implicit syntax will locate both the target widget and layout from the QTcl state whereas an explicitly issued command will provide these as conventional arguments.
The following example shows how these syntactical methods differ. Below are two scripts that each create a window with a qlineedit. A qlineedit is a single line text editor, the textual contents of which can be set interactively or programmatically.
set w [qwidget] ; # Sets 'w' to be current widget qsetwindowtitle "Implicit" ; # Uses current widget w # Use implicit syntax for layout # This will be the current layout during evaluation of the following block qvboxlayout { qlineedit ; # Lineedit becomes current widget qsettext "Hello" ; # Uses current widget (qlineedit) } qshow $w
Most of the commands in the above script operate on the current widget and current layout. While it has the advantage of concise syntax, the programmer is required to keep track of the QTcl state throughout the execution of the program.
We will now rewrite this script to use explicit syntax. Recall from the Command Substitution section in the Tcl programming documentation how square brackets can be used to evaluate statements that return a value, and, how that value can be assigned to a variable for later use. Explicit syntax relies heavily on command substitution to create and return widgets that are subsequently used as command arguments. Below is our lineedit script which has been rewritten using explicit syntax :
set w [qwidget] qsetwindowtitle $w "Explicit" ; # w is an argument this time # Create layout object for w and assign to variable 'layout' set layout [qvboxlayout $w] # Create lineedit and add to layout set lineedit [qlineedit ] qaddwidget $layout $lineedit qsettext $lineedit "Hello" qshow $w
A characteristic feature of the script above is how widgets are created using command substitution and then assigned to a variable. The variable is then used in commands such as qsettext to show the target widget which to modify.
Explicit syntax can be viewed as slightly more long winded than its implicit counterpart. However, it renders a precise picture of the workings of the script, and does not rely on "invisible" objects in the QTcl state. The key advantage, however, is that widgets are handled just like regular Tcl variables and can be managed accordingly - widgets can then be sent as arguments to Tcl procedures, and stored in data structures like lists etc.
Callback Functions
A particular type of argument is the callback function. A callback function is a group of statements assigned to a widget for later execution. This execution takes place when the widget receives a window event, for instance a user pushing a button. This way the application program may respond to various interactive events received by the menu interface.
The callback function is always a single argument, and it is always a list or a string. This is illustrated in the following example, which creates three buttons that when pushed display an information dialog. Although the string or list that constitutes the callback function has several components, they are passed to QTcl as a single string or list variable. This makes it possible to store a callback function as a single variable.
The callback function is always the last argument of a command.
# Callback function as a string qpushbutton "My button" "qinformation Information pushed" # Callback function as a list qpushbutton "My button" { qinformation Information pushed } # Callback function as a list spanning several lines qpushbutton "My button" { qinformation Information pushed qinformation Information "pushed again" }
While it often makes no difference whether the callback function is a list or a string, it does matter if the function contains variables. This is because variables are resolved immediately when using strings. When using braces, the variables will not be resolved until later, if at all. See Evaluation of Variables for examples.
The declaration of a list may be spread across several lines. This is accomplished by separating the elements with a newline. This is useful if the callback function contains several statements:
qpushbutton "My button" { qinformation Title "First line" qinformation Title "Second line" }
The formal notation for a callback command used in this manual is "cmds ..." :
qradiobutton [ref:text] [ref:cmds ...]
This shows that a QPushButton may take a final callback command argument. See more examples here.
Scopes and Blocks
A list can be spread across several lines by separating the elements with a new line. Such a list constitutes a block. While a block is technically just a list, the term block will be used to emphasize that the list is expected to have a non-trivial composition.
Blocks in QTcl are usually callback function blocks or groups of widgets in automatic layouts. An in-lined callback command is a block following the widget declaration which contains the complete command, rather than just a procedure call. Automatic layout is performed in blocks following the layout command.
Callback Function Blocks
A callback function block is a list spread across several lines and is assigned as a callback function to a widget. While functionally equivalent to a one-line list, it is sometimes convenient to spread the list across several lines if it contains several statements:
# Callback function as a block qpushbutton "OK" { qinformation "This is the first line" qinformation "This is the secondline" } # Callback function as a one-line list qpushbutton "OK" { qinformation "This is a one-liner" }
During program execution the area inside a block is referred to as a scope. A block is therefore a physical property of the program text, while scope refers to a state of execution at run-time. Scopes are important when using implicit syntax because they determine the scope of the current widget or current layout.
Automatic Layout
For the convenience of the programmer QTcl can perform automatic layout of widgets. This works well in simple scenarios, but is seldom applicable when using container widgets that perform their own layout. Meanwhile, automatic layout is achieved by grouping the widgets in a block following the layout declaration. Below is a simple case :
set w [qwidget ] qsetwindowtitle "Auto Layout" qhboxlayout { qlabel "Name : " qlineedit } qshow $w
Life time of Widgets
A widget exists as long as it exists in the computers memory, regardless of whether it is visible or not. When a widget-creating command such as qpushbutton is invoked, a button object is created in memory, but the button is not visible yet. To make the button visible invoke the command qshow, which always draws the widget in question. The widget may then become hidden or closed for a variety of reasons, such as the user invoking the qclose command or pushing the windows close button. The button still remains in memory, however, and is redrawn automatically by the window system or explicitly by commands such as qshow or qraise.
Sooner or later the widget will be erased from memory as well. It is then said to be deleted. Most widgets are deleted as a result of their parent widget being deleted. Child widgets are deleted recursively. This particular behavior is a built-in feature of QTcl. It relieves the programmer from manually deleting all widgets, as well as avoiding memory leaks. A widget may also be deleted explicitly using the qdelete command, although this is rarely necessary.
Sometimes it is desired that the widget should be deleted from memory when it is closed. This is achieved through the use of the deleteonclose attribute. Flags are only accepted by the QWidget and QMainWindow widgets.
Parents and Children
Widgets are usually laid out in a hierarchical manner. A window contains widgets that also contain widgets and so on. When one widget contains another they are said to have a parent-child relationship. The containing widget is the parent widget of the children that it embeds.
All widgets have one parent and possibly some children. An exception is toplevel windows, which do not need a parent. The parent-child relationship between two widgets determines their physical position relative to each other as well as their lifetime. A child widget is destroyed when its parent is destroyed. If the child widget itself has children, they in turn will be destroyed as well.
Widgets are usually created without parents. The parent-child relationship is established when the widget is added to another widget. Adding a widget takes place during layout management, or when a widget is added to a container widget.
The next example shows a qwidget having a qlabel and qlineedit as children:
set w [qwidget] qsetwindowtitle "Parent" set layout [qhboxlayout $w] qaddwidget $layout [qlabel "Name : "] qaddwidget $layout [qlineedit] qshow $w
The qlabel and qlineedit are made children of their containing widget through the qaddwidget command, where they are added to the parent widgets layout.
Layout Management
Introduction
A layout manager determines the placement, or positioning of widgets. Adding a widget to a layout will reparent the widget. Its new parent will be the widget owning the layout.
There are three layout managers in QTcl. They are the qvboxlayout, the qhboxlayout, and the qgridlayout. The QVBoxLayout and QHBoxLayout stack widgets vertically or horizontally inside their parent. The QGridLayout arranges widgets in a row-column grid. Which layout manager to use is usually determined by the constraints imposed by the nature of the menu, although one sometimes has a choice between using a gridlayout or a boxlayout.
Layout management can be performed using explicit or implicit syntax. We will now look at each of these in turn.
Layout using Explicit Syntax
Using explicit syntax the programmer can manipulate the layout manager directly. This way one can achieve fine-grained control as well as being able to nest layouts to accommodate complex layout requirements. The key operations involved in explicit layout management are as follows :
- Creating the layout manager using the qgridlayout, qvboxlayout, or qhboxlayout commands.
- Using qaddwidget to add widgets or nested layouts to the existing layout manager. All operations relating to the layout manager will involve a reference to the layout object.
Here is an example illustrating handling of a layout object :
set w [qwidget] qsetwindowtitle "Explicit Layout" # Create layout for w set layout [qhboxlayout $w] qsetspacing $layout 10 qsetmargin $layout 10 # Create a label and a lineedit - add to layout set label [qlabel "Name : "] qaddwidget $layout $label set lineedit [qlineedit] qaddwidget $layout $lineedit qshow $w
Layout using Implicit Syntax
While the explicit syntax allows for a precise handling of the layout manager, there are simple scenarios where this requirement can be relaxed. In such cases the implicit syntax might be more convenient. The defining ability of implicit syntax is that the widgets are automatically added to the current layout. Braces '{ }' are used to delimit the widgets to be placed. The layout is said to be the current layout throughout the block, and all widgets will thereafter be subjected to automatic layout. Following is an example:
set w [qwidget] qsetwindowtitle "Implicit Layout" # Create layout for w, the active widget # This will be the current layout inside the '\{}' qhboxlayout { # These operate on the current layout qsetspacing 10 qsetmargin 10 # Create a label and a lineedit - automatically added to layout qlabel "Name : " qlineedit } qshow $w
The final look is, of course, identical to the explicit version.
Nested Layouts
We have seen how the qgridlayout and qhboxlayout arrange widgets in a grid or in a linear manner. Most menus and dialogs have a more complex arrangement, often featuring groups of widgets laid out differently from the main flow of the menu. This can be accomplished by assigning the group widgets to a separate layout, and then adding that layout to the main, outer layout. This leads to the layouts being nested inside each other. The following example shows two buttons - OK and Cancel - arranged in a local horizontal layout inside the main vertical layout:
set w [qwidget] qsetwindowtitle "Nested Layouts" # Main vertical layout for the toplevel window set mainlayout [qvboxlayout $w] set label [qlabel "<html>This label is added <em>directly</em> to the vertical layout</html>"] qaddwidget $mainlayout $label # Add OK and Cancel buttons in a separate horizontal layout # Add this layout to main layout set buttonlayout [qhboxlayout ] qaddlayout $mainlayout $buttonlayout set ok_button [qpushbutton OK] qaddwidget $buttonlayout $ok_button set cancel_button [qpushbutton Cancel] qaddwidget $buttonlayout $cancel_button qshow $w
Notice the use of the qaddlayout command.