The chapter so far has described where scripts are stored, and when scripts are executed. We now move on to describing how scripts can manipulate objects in forms and reports.
In line with the object orientation of Python as a language, all Rekall objects - KBForm, KBBlock, etc. - are represented as Python objects. Hence, when an event function is invoked with its associated object as the first argument to the event function, that first argument is a Python object which represents the Rekall object. And, just as Python provides object inheritance, the Python objects which represent Rekall objects have an exactly corresponding inheritance. Hence, since a KBField object is a special case of a KBItem, so the KBField Python object inherits all the methods applicable to the KBItem Python object.
The remainder of this section should be read with the Rekall object structure described earler. Each of the following sections describes the more common methods applicable to each Rekall object. See the appendices for complete lists of all methods.
KBObject MethodsThe following methods apply to KBObjects. Note that some methods, for instance the enable and visibility methods, are redefined for KBItems since an KBItem may display multiple controls.
This method enables or disables the object, in the normal sense in which buttons and such are enabled or disabled.
The code below, which is a KBBlock onCurrent event, disables a button for the first record.
def eventFunc (block, qrow) : prevButton = block.getNamedCtrl("PrevButton") prevButton.setEnabled (qrow > 0) |
This method returns true if the control is enabled.
This method shows or hides the object, in the normal sense in which buttons and such are shown or hidden.
def eventFunc (block, qrow) : prevButton = block.getNamedCtrl("PrevButton") prevButton.setVisible (qrow > 0) |
This method returns true if the object is visible.
This method returns the name of the object, as set in the object's properties.
This method returns the value of a named property (attribute) of the object. The name is a name as specified in the previous chapter (eg., the X-position property is named x). getName() is actually equivalent to getAttr("name").
Returns the object's width in pixels.
Returns the object's height in pixels.
This method resizes the object to width x height pixels. Note that if the object is a KBItem then all the controls displayed by the KBItem will be resized.
Returns the KBBlock in which the object is embedded. Note that if the embedding block is actually a KBForm or KBReport then the result is a KBForm or KBReport respectively.
This method is the key to locating controls; given the name of a control it locates the control relative to the object on which it is invoked. The most common usage is to locate a control inside a block; the example below could be used on a block On Display event to clear a field.
def eventFunc (block, qrow) : ctrl = block.getNamedCtrl ("Password") control.setValue (qrow, "") |
However, the name argument to getNamedCtrl can be an arbitrary path, with the / character as separator, in which case the object tree is traversed. For instance, getNamedCtrl("block1/control12") would locate an object named block1 inside the object to which the method is applied, and then locate control12 within that object. When used this way, each step other than the last should return a block or container object.
In addition, you can use .. to move up the object tree. For instance, the following event code could be associated with a button On Click event, in which case it will disable the button named another in the same block (or container) as this button:
def eventFunc (button) : button.getNamedCtrl("../another").setEnabled(0) |
If the name starts with the / character then rather than starting at the object to which the method is applied, the location operation will start with the topmost object, that is, the form or block. But, beware, if you use a name like block2//control34 then the // will go to the topmost object; although the use of .. and a leading / is analagous to file system names, the // usage differs.
To make debugging easier, if the second argument is false (this argument is optional and defaults to false), and the location operation fails at some point, then a dialog is shown. This shows a tree of all objects in the form or report, along with the name argument. The object tree is expanded as far as the object on which the getNamedCtrl method was invoked. You can then either fail the operation (the method returns with the result None) or select an object and continue with that object being returned.
The screenshot below shows the dialog, and the following code (which can be attached to a button On Click event) will display the name of the selected object.
def eventFunc (button) : ctrl = button.getNamedCtrl ("../noWhere/noCtrl") if ctrl != None : RekallMain.messageBox (ctrl.getName()) |
Returns the KBForm in which the object is embedded, or None if the object is actually in a KBReport. Note that this is distinct from getBlock() which will only return a KBForm if the object is embedded in the top-most KBBlock of a form.
This method returns a string describing the last error which occured on the object. It can be used after specific methods (such as the KBForm executeCopier method) which set an error message.
KBItem MethodsThe following methods apply to KBItems. Note that the row argument identifies a query row number, that is it is a row index into the data which is displayed in the block in which the KBItem is embedded.
This method sets the data control which currently displays the row query row to the specified value. For instance, if a block is displaying 5 rows of data, which are the 11th through 15th rows of the query, then setValue(12,"Hello") will set the second displayed row.
If the specified row is not currently displayed, then nothing is updated.
Please note that in this release of Rekall, the value must be a string.
def eventFunc (block, qrow) : qty = block.getNamedCtrl("Quantity").getValue(qrow) ; cost = block.getNamedCtrl("Cost" ).getValue(qrow) ; block.getNamedCtrl("Amount").setValue(qrow, `int(qty) * int(cost)`) |
This method retrieves the value currently displayed in the control corresponding to the row query row. For instance, if a block is displaying 5 rows of data, which are the 11th through 15th rows of the query, then getValue(12) will get the value from the second displayed row.
If the specified row is not currently displayed, then the result null.
This method enables or disables the control corresponding to the row query row, in the normal sense in which text fields and such are enabled or disabled.
The code below, which is a KBBlock onDisplay event, disables a salary field if it contains the boss's salary, so that the wages department cannot change it. Ha! typical.
def eventFunc (block, qrow) : minion = block.getNamedCtrl("Name").getValue(qrow) != "TheBoss" block.getNamedCtrl("Salary").setEnabled (qrow, minion) if not minion : RekallMain.messageBox ("The Boss's salary is fixed!") |
This method returns true if the control corresponding to the row query row is enabled.
This method shows or hides the control corresponding to the row query row, in the normal sense in which buttons and such are shown or hidden.
This method returns true if the control corresponding to the row query row is visible.
Containers MethodsThe following methods apply to KBBlocks and KBContainers. Note that when used on a KBContainer, the method in effect operates on the KBBlock in which the KBContainer is embedded. As for KBItems, the row argument identifies a query row number.
This method returns the number of rows or data which the block has retrieved from the server database. For instance, if the block gets data directly from a table, and there were no SQL where conditions, then the value will be equal to the number of rows in the table.
The following example, an event function for the KBBlock postSync event, totals up and displays stock quantity.
def eventFunc (block) : total = 0 for row in range (0, block.getNumRows()) : value = block.getRowValue("Stock", row) if value != None : total = total + int(value) RekallMain.messageBox \ ( 'There are " + `total` + " items in total", Total Quantity of All Products" ) |
This method returns the current query row number.
Focus is moved to a control which is displaying data from the specified query row. If necessary, the block will scroll through its data to bring such a row into view.
Name should be the name of a data control which is embedded in the block. Provided that such a control exists, then the result is the data value from the row query row corresponding to the control.
Note that this is not neccessarily the value displayed. Either the specified row may not be displayed at all (ie., it is outside the range of rows currently displayed by the block), or the user may have edited the value displayed but not yet saved it. If it is neccessary to ensure that the displayed value is kept correct then the script must also update the control.
See the example above under the getNumRows() method.
KBButton Methods
This method sets the button text. For example, to change the button text when a button is clicked, using the onClick button event:
def eventFunc (button) : button.setText ("Button Clicked") |
This method sets the label text.
Tabber and Tabber Page MethodsThe following two methods apply to tabber pages, although they can also affect the tabber in which the page exists.
This method can be used to enable or disable a page. If the page is disabled then all control inside the page are also disabled. The tab which is associated with the page is also enabled or disabled.
This method makes the page to which it is applied, that is, it is made visible (and hides all other pages), and the associated tab becomes the current tab. This is equivalent to the user clicking the tab.
Note that this method acts independantly of the setEnabled method, so a page can be made current even if it is not enabled.
KBForm MethodsThis method can be used to open a named form. The first argument is the name of a form; the second (which is optional) should be a dictionary of (name, value) pairs, which are passed as parameters to the form. See the chapter on executing forms and reports with paramaters for more details.
The example below is attached to the Clients button of MainForm in the RekallDemo database. It prompts the user for a filter to select clients to display. See chapter 6 for more details.
def eventFunc (button) : text = RekallMain.promptBox \ ( "Enter pattern or leave empty for all", "", "Select companies" ) if text == None : return if text != "" : text = "Company like '" + text + "'" button.getForm().openForm ( 'Client', {'Filter' : text} ) |
There are three possible return conditions from openForm. If there was an error, then the error will be displayed before the call returns, and the result will be zero. If the form was closed normally, then the results is also zero.
The third case arrises if the form was executed modally, and was closed via the form.closeForm(rc) (see below) method, with a non-zero argument. In this case the return value from openForm is a python dictionary, where the keys are the names of the data controls in the form (actually, the path name from the top of the form down to the control; so, if a control named Name is embedded in a block named Person, then the key will be Person.Name), and the values are the values shown in the controls when the form was closed. This allows the use of modal forms as modal dialog boxes.
The third key argument, which is also optional, can be used to provide parent/child linkage between forms much as exists between a form and a subform. If a value is passed as this argument, and the Child property is set in the top-level block in the form to be opened, then the query generated by the top level block in that form will select only those rows where the Child column value matches the argument value. For instance, to open a form such that only records whose Sex column contains F, set the Child property to Sex, and open the form with:
def eventFunc (button) : button.getForm().openForm ('People', {}, 'F') |
This method can be used to open a named report. The first argument is the name of a report; the second (which is optional) should be a dictionary of (name, value) pairs, which are passed as parameters to the report. See the chapter on executing forms and reports with paramaters for more details. The third key argument is used in the same manned as in the openForm method.
This method can be used to execute a named copier, the name of which is passed as the argument. The return value is the number of rows copied, or negative on an error (in which case the lastError method can be used to get an error message).
def eventFunc (button) : form = button.getForm() rows = form.executeCopier('ClientsAsXML') if rows < 0 : RekallMain.messageBox (form.lastError()) else : RekallMain.messageBox ('Exported ' + `rows` + ' client records to "/tmp/clients.xml"') |
This method opens a form whose XML definition is passed as the first argument.
See the example earlier in this chapter.
This method opens a report whose XML definition is passed as the first argument.
Calling this method closes the form. The argument should be a number. See the openForm method above for a description of the use of this value.