Previous Next Table of Contents

5. Programming Interface

5.1 Overview

All exchanges between the package and the user application are made through TCL arrays that hold all data and parameters. There is one such array for every screen. The array name is not significant except that it will be used for the screen title. We often use the table name, but this is not mandatory.

The basic idea is that you set values in the array and then call "sqlscreen arrayname" to create the screen.

The values set in the array define what tables/columns will be used, how the screen will look like, etc... You can also define callback functions which will be called before and after the database accesses, to give you an opportunity for checking what's happening, possibly modify values, or block the operation if something is wrong.

In the following, we shall use the example of a database named "orderdb", with a table named "customers", with columns named "custid", "custname", and "custfirstname", and a table named "orders" with "orderid" and "ordercustid".

sqlscreens stores the values for the field corresponding to a column as "arrayname(sqlsc_column_value)" (Ex: customer(sqlsc_custid_value)). This makes collisions of other entries with your column names unlikely. You can access these variables to retrieve values into your application, and also to modify them (before an insert for example, if the user input needs processing, or if some values are automatically generated by the application).

The first release used to store the values as arrayname(column). If you have written code based on this, I would suggest that you modify it. If you do not want or can not, you can set the global variable "sqlsc_names_compat_old" to 1 before the first call to get a compatible behaviour (this will go away in the near future).

The following paragraphs describe the function of the different array entries, beginning with the most basic and frequently used, then the different callback functions that you can use.

I am sorry for the many naming inconsistencies (like using or not the sqlsc prefix for array entries), this came over time and would just be too much work to change.

When you're finished with the screen, you can call sqlscreendelete arrayname to cleanup and release all resources (array, windows, database connections). Most applications will exit instead. sqlscreendelete is mostly useful in case you want to recreate the screen with different options (most options can't be changed once a screen is created).

5.2 Initialization and termination

sqlscreen

To create an sqlscreen, you set values inside a TCL array (See the following section: Interface array entries ), then call :


sqlscreen yourarrayname
 

to create the screen. Note that sqlscreen will create and pack the screen's window, but not its parents, so that the screen will not be necessarily visible at this point. Ex:


frame .f
set myarray(window) .f.scr
... set other fields
sqlscreen myarray
# screen still not visible 
pack .f     #screen appears
 

This can be useful if you do not want the screen to be visible at all times: you can use 'pack ' and 'pack forget' to make it appear and disappear as you wish.

sqlscreen optionnally takes a second parameter. If the value is 'h', the fields will be arranged horizontally instead of vertically. There are other ways to do this(see columns) , but it can still be useful in some cases.

sqlscreendelete

This procedure will destroy all resources associated with an sqlscreen (windows, database connections and the array itself). Call it as:


sqlscreendelete arrayname
 

5.3 Interface array entries

General parameters

window

This defines the TK frame name where the screen will be created. Example:


frame .f1 
set customer(window) .f1.cust
 

or just the following to create the window in the top one:


set customer(window) .cust
 

This entry must be a valid TK window name: for exemple it cannot start with an upper case character.

The window must not exist before calling sqlscreen, which will create it. Its parents must exist.

database

This defines the database name.


set customer(database) orderdb
 

sqlcpasswd, sqlschost, sqlscuser

These define the user name, host and password for the connection to the database server. These are all optional.

table

This defines the table name. Example:


set customer(table) customers
 

It is also possible to display fields from several tables in one screen:


set custorder(table) {customers orders}
 

If you are using several tables, you'll also need a join clause (see the following paragraph), and you will not be able to modify data through the screen. (You CAN update several tables in one application, but each table will need a separate screen, and the screen links will be through cascaded queries, not join clauses - See Linking screens ).

joinclause

In case fields from several tables are displayed in a screen, sqlscreens needs to know how to join the tables when performing a SELECT. This is defined by the joinclause array entry. Exemple:


set custorder(joinclause) \ 
       {customers.custid = orders.ordercustid}
 

columns

This is a list to define the columns that you want included. If it is not set, sqlscreen will query the database for all the column names in the table, and build the screen with the result.

Example for specifying the column names:


set customer(columns) {custid custname}
 

If several tables are used, it may be necessary to qualify the column names if they are not unique:


set custorder(columns) {customers.custid customers.custname...}
 

By default, all fields will be displayed in one column. You can get them to be displayed in one line by calling sqlscreen as "sqlscreen arrayname h" instead of "sqlscreen arrayname". You can also insert "line breaks" by inserting newline caracters in the column list, like:


set arrayname(columns) { 
    host "\n" 
    user db "\n"
    select_priv insert_priv update_priv "\n"
    delete_priv create_priv drop_priv 
}
 

The field positions will be arranged by the grid geometry manager. In lines with less fields, the last field (and only the last) spans the remaining columns. You'll probably need several tries to get it right (at least I do).

Screen attributes

queryonly

If this is set, the screen will not have "add" and "update" buttons, you will only be able to select data. Example:


set customer(queryonly) ""
 

The value has no importance, just setting the array entry (even to "no") creates a screen for query only.

allowdelete

If this entry is set, and queryonly is not set a Delete button will be created.

notitle

Suppresses the screen title. This spares a little space if your screen is crowded.

nobuttons

If this is set, no buttons will be created in this particular screen. Note that this does not change what you can do in the screen, because the keyboard shortcuts are still available.

Field attributes

Column type and length

The sqlsc_colname_len and sqlsc_colname_type entries (like sqlsc_custid_len, sqlsc_custid_type) are normally created by the package, you do not need to set them. For character columns, you can set sqlsc_colname_len if you want the entry field to be of a size different from the column width (for example if the column is very wide). Example:


set customer(sqlsc_custname_len) 20
 

would create a 20 characters field even if custname is actually 100 characters wide. This does not constrain what you can enter because TK fields can scroll.

Beware: sqlscreens does NOT check that the input can fit in the database column. This can produce unexpected truncation.

autopercent

The autopercentboth, autopercentleft, autopercentright lists can be set for character columns where you want '%' to be automatically added before a query (all char field queries are done with the LIKE operator). Example:


set customer(autopercentright) {custname}
 

would let you query by entering just the beginning of the name, without having to reach for the shift key to type '%'.

texts

This is a list of columns (typically text blobs) that should be displayed in multiline text widgets. Each entry is a triplet listing the column name, and the width and height of the text widget. Ex:


set product(texts) {{description 20 70} {notice 10 70}}
 

sqlscreens will handle quoting and unquoting the blob contents.

There is an exemple of texts use in the wines.tcl sample application.

choices

This list defines columns where entries should come from a menu instead of being free form. It is very useful, but the interface could be nicer.

The choices entry is a list. There are two list elements for every column. The first element is the column name, the second element the name for the list of possible values. For example:


set customer(choices) {
    custtype custtypelist 
    custsex custsexlist 
}
 

Would specify that the custtype and custsex columns will have values coming from custtypelist and custsexlist. These lists would typically have been created beforehand (possibly by querying another table). The list of values can in turn be of two types: either a simple list or a list of pairs.

A simple list lists the possible values (would you believe this ?). Ex:


set custtypelist {normal distributor internal}
 

normal, distributor and internal will be both displayed on the screen and used for querying or updating the database.

In a list of pairs, each pair defines the value that should be shown and the value that should be entered in the database. Example:


set custsexlist {{unknown 0} {female 1} {male 2}}
 

With this list, the menu would display unknown, male, female, but the values used for the database would be 0, 1, 2.

Note that when using MySQL, a choice menu will be automatically generated for 'enum' columns. You can still set your own list, which will override the automatically built one. This can be useful if the displayed values are different from the stored ones.

There are exemples of use (both automatic and explicit) in the wines.tclsample program.

ordercols

This list defines column names that will be added in an ORDER BY clause each time a query is run. It has the format of a normal ORDER BY column list. Ex:


set customer(ordercols) "custid desc, custname"
 

updateindex

This defines a column name (or a list of column names) that will be used in the WHERE clause of an UPDATE statement. It should provide a way to uniquely identify a row.

If neither columns nor updateindex are set before calling sqlscreen, sqlscreenswill try to make up an updateindex by using a serial column or primary key if one is found.

If columns is set, updateindex is automatically generated only if the primary key is completely included in the column list.

If no updateindex list finally exists, the screen will have no update button.

See Sql generation for a more complete discussion of update row selection.

upshiftcols

This is a list of fields for which values should be automatically changed to upper case before inserting or querying. This is very useful with INFORMIX which is case-sensitive, not very useful with MySQL. Example:


set customer(upshiftcols) {custname custfirstname}
 

noentry

This is a list of columns for which data entry is forbidden. They are displayed differently, and will not allow typing. This is sometimes useful for fields that should only be updated by the program or on which searching is forbidden.

nodisplay

This is a list of columns for which no fields will be shown. The corresponding values are present in the array. This is used for fields which link several screens, or which the application wants to use, but which don't need to be displayed.

Auxiliary list window

list_columns, list_window

If list_columns and list_window are set, sqlscreen will create an auxiliary list for the screen, in the specified window. The list screen will display one line for each result row, the data displayed will be taken from the list_columns columns. This is useful to get a compact display of a query's results.

Clicking on a line in the list with mouse button 1 will display the corresponding row in the main screen.

This capability will only be available if an updateindex list has been defined for the screen (either implicitely or explicitely), see the updateindex section. The updateindex columns must be part of list_columns, so that we can uniquely link back from the list to the detail screen.

Example:


set customers(list_columns) {custname custid} 
set customers(updateindex) custid 
toplevel .custlist 
set customers(list_window) .custlist
 

list_colwidths:

Sqlscreens will try its best to compute appropriate column widths for the list and to align the columns. You may force specific values for the column widths by specifying the list_colwidths entry, as a list specifying the width in characters for each column. Exemple:


set customers(list_colwidths) {40 5}
 

The widths must be specified in the same order as the columns in list_columns.

list_lineproc:

When displaying the list, Sqlscreens will alternate the line's background between white and light grey to facilitate reading. If defined, the list_lineproc procedure will be called for each displayed line, with parameters allowing it to change the line's display (for exemple, this would allow showing special rows needing attention in red). Exemple:


set customers(list_lineproc) custlineproc
 

list_lineproc will then be called for each line with 3 parameters:

  1. The name of the TK text window where the line is displayed.
  2. The TK text tag name for the area associated with the line.
  3. The list of column values for this line.

list_lineproc can then test one or several entries in the value list, and use the window and tag names to set attributes. The following exemple sets the ugly colors in the wine list according to the bottle count (from wines.tcl):


proc setlinecolor {w tag res} { 
    # Get the bottle count from the value list
    set botcnt [lindex $res 0] 
    # Set the background color accordingly
    switch $botcnt { 
      1 {$w tag configure $tag -background red} 
      2 {$w tag configure $tag -background orange} 
      3 {$w tag configure $tag -background yellow} 
      default {$w tag configure $tag -background green} 
    } 
}
 

Miscellaneous

hdl

The package uses this entry to store the database handle.

initfocus

This is the name of the window where the focus should go when the screen is reset. This can be useful for repetitive entry when you don't want to use the mouse.

tabcolserial

If there is a serial or auto_increment column, sqlscreen sets its name in there.

If the screen allows insertion, but this field is either not displayed or not modifiable (noentry), the value will be automatically reset to null before performing an insert, which will allow inserting a record by first querying for (and probably modifying) another one.

If the field is modifiable by the user, no special action will be taken.

If the beforeinsert procedure is defined for the screen, any modification is performed before calling it, to allow for a local value allocation scheme.

querynum

This is the select result handle.

sqlscshowstmts

You can set this variable to 1 or 0 to print the SQL statements to stdout (or SQLSCLOG) when they are executed. This is not an array element but a global variable.

sqlsc_colname_valsaved

The package uses these entries to save the database values when a query is performed. This is used to compute the UPDATE statements (See the Sql generation section).

5.4 Linking screens

It is possible to link two screens so that a change in one screen will update the other one. This can be done in two ways.

sqlmasterslave

This links the first screen to the second one so that a query in the first will run a query in the second. Example:


sqlmasterslave customer custid order ordercust
 

would link the customer and order screens so that the order screen is reset, the ordercust field is set to the value of the custid field and a query is run every time a query is run in customer.

Things are set up so that it is possible to have reciprocal links without creating an infinite loop. Example:


sqlmasterslave customer custid order ordercustsqlmasterslave order ordercust customer custid 
 

is ok and would both show a customer's orders after querying in the customer screen and an order's customer after querying in the orders screen.

sqlslavemaster

This second type of link is used to just update a column in the target screen, without running a query there. It is useful to set the join column values. Example:


sqlslavemaster customer custid order ordercust
 

could be used to set the ordercust field by querying customer, typically while entering orders.

5.5 Controlling the number of button sets

Each screen in an application normally has a set of control buttons. It is sometimes useful to use only one set of buttons for several screens. This is done with the sqcommonbuttons routine. sqcommonbutons will create a TK frame with a set of control buttons inside. This set of buttons will not be linked to a particular screen, but will apply to the screen which has the current keyboard focus. Example:


sqcommonbuttons .f1.buttons
 

Will create the .f1.buttons frame and buttons inside there.

It is possible to create several sets of buttons (useful when there are several top level frames in the application) by calling sqcommonbuttons several times. Any of these sets will control the screen which currently has the keyboard focus.

In practice, this facility has not proved very useful because it is to easy to make mistake about where the current keyboard focus actually is.

It would be quite easy to use completely custom buttons for an application by setting the "usecommonbuttons" variable, and creating custom buttons with appropriate callbacks (look at the sqcommonbuttons code in sqlscreens.tcl).

5.6 Callback routines:

The following callback routine names can be defined in the array:

Example:


set customer(beforeinsert) checkcustfields
 

The different routines will be invoked in the following manner:

For beforexxx routines:


routinename optype arrayname
 

For afterxxx routines:


routinename optype txt arrayname
 

Where optype defines what's happening (like beforeinsert, afternext, etc...), arrayname is the affected screen's array name, and txt is the SQL text for afterxxx routines. We can't pass the text to the beforexxx routines, because they may be responsible to modify some field values that will affect the statement !

beforeinsert, afterinsert, beforeupdate, afterupdate, beforedelete andafterdelete will be called before and after inserting or updating data.

beforequery and afterquery will be called before and after doing a select, and afterquery will also be called after the user fetches the next record, rewinds the query, or resets the screen.

If one of the beforexxx routines returns anything but 0 , the operation will be canceled (not run).

5.7 Visible internal interfaces

In some cases it may be useful to start a database operation by a program call (as opposed to a button press by the user). This is easily feasible by calling the following routines. They all take the array name as sole argument, and use the values that are currently stored/displayed in the screen.

In all cases, the effect will be exactly the same as the corresponding button press.

5.8 Small utility routines


Previous Next Table of Contents