Software Customisation Guide PDF
Software Customisation Guide PDF
Customisation Guide
AVEVA Solutions Ltd
Disclaimer
Information of a technical nature, and particulars of the product and its use, is given by AVEVA
Solutions Ltd and its subsidiaries without warranty. AVEVA Solutions Ltd and its subsidiaries disclaim
any and all warranties and conditions, expressed or implied, to the fullest extent permitted by law.
Neither the author nor AVEVA Solutions Ltd, or any of its subsidiaries, shall be liable to any person or
entity for any actions, claims, loss or damage arising from the use or possession of any information,
particulars, or errors in this publication, or any incorrect use of the product, whatsoever.
Copyright
Copyright and all other intellectual property rights in this manual and the associated software, and every
part of it (including source code, object code, any data contained in it, the manual and any other
documentation supplied with it) belongs to AVEVA Solutions Ltd or its subsidiaries.
All other rights are reserved to AVEVA Solutions Ltd and its subsidiaries. The information contained in
this document is commercially sensitive, and shall not be copied, reproduced, stored in a retrieval
system, or transmitted without the prior written permission of AVEVA Solutions Ltd Where such
permission is granted, it expressly requires that this Disclaimer and Copyright notice is prominently
displayed at the beginning of every copy that is made.
The manual and associated documentation may not be adapted, reproduced, or copied, in any material
or electronic form, without the prior written permission of AVEVA Solutions Ltd. The user may also not
reverse engineer, decompile, copy, or adapt the associated software. Neither the whole, nor part of the
product described in this publication may be incorporated into any third-party software, product,
machine, or system without the prior written permission of AVEVA Solutions Ltd, save as permitted by
law. Any such unauthorised action is strictly prohibited, and may give rise to civil liabilities and criminal
prosecution.
The AVEVA products described in this guide are to be installed and operated strictly in accordance with
the terms and conditions of the respective licence agreements, and in accordance with the relevant
User Documentation. Unauthorised or unlicensed use of the product is strictly prohibited.
AVEVA Solutions Ltd, High Cross, Madingley Road, Cambridge, CB3 0HB, United Kingdom
Trademarks
AVEVA and Tribon are registered trademarks of AVEVA Solutions Ltd or its subsidiaries. Unauthorised
use of the AVEVA or Tribon trademarks is strictly forbidden.
AVEVA product names are trademarks or registered trademarks of AVEVA Solutions Ltd or its
subsidiaries, registered in the UK, Europe and other countries (worldwide).
The copyright, trade mark rights, or other intellectual property rights in any other product, its name or
logo belongs to its respective owner.
Software Customisation Guide
Contents Page
Software
Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1:1
Customising a Graphical User Interface . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1:1
Serious Warning About Software Customisation . . . . . . . . . . . . . . . . . . . . . . . . 1:1
How to Use this Manual . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1:2
Hints on the Trying the Examples . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1:2
Minimising Problems for Future Upgrades . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1:2
Note for Users Familiar with OO Concepts . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1:4
Arrays . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6:1
Creating Array Variables . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6:1
Arrays of Arrays (Multi-dimensional Arrays). . . . . . . . . . . . . . . . . . . . . . . . . . . . 6:2
Array Methods . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6:2
Appending a New Element to an Existing Array. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6:3
Deleting an Array Element . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6:3
Deleting an Array . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6:3
Splitting a Text String into Array Elements. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6:3
Length of String Array Elements . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6:4
Sorting Arrays Using Array Methods . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6:4
Sorting Arrays using the VAR Command . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6:6
Subtotalling Arrays with the VAR Command . . . . . . . . . . . . . . . . . . . . . . . . . . . 6:9
Arrays of Objects . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6:10
DO Values with Arrays of Objects . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6:10
Block Evaluation And Arrays . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6:10
Using Block Evaluate to Sort an Array of Objects . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6:11
DO VALUES and DO INDICES with Arrays . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6:11
Macros . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7:1
PML Macros . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7:1
Naming and Running Macros. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7:1
Macros with Arguments . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7:1
Using Macros and Functions Together . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7:3
Synonyms in PML Macros and Functions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7:4
Forms . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15:1
Modules and Applications. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15:1
Application Window . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15:1
Current Document . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15:2
Defining a Form . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15:2
Form Attributes. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15:3
Form Type . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15:3
Menus . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 16:1
Menu Types and Rules . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 16:1
Hints and Tips for Using Menu Types . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 16:1
Core-Code Based Menus. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 16:2
Defining a Bar Menu Gadget . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 16:2
Defining a Menu Object . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 16:3
Window Menu. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 16:4
Online Help Menu. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 16:4
Popup Menus . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 16:5
Finding Who Popped up a Menu . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 16:6
Toggle Menus. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 16:6
Editing Bars and Menus . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 16:7
Inserting Menus into a Bar . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 16:7
Inserting New Menu Fields. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 16:8
Changing the State of Menufields . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 16:9
Implied Menu-field Groups . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 16:10
Creating Menus Dynamically . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 16:11
Frames . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 18:1
Types of Frame. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 18:1
Normal Frames. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 18:1
Tabset Frames . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 18:1
Toolbar Frames . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 18:2
PANEL Frames . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 18:2
Fold Up Panel Frames . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 18:2
1 Introduction
This manual describes how to use PML, the AVEVA Programmable Macro Language. You
should use it together with the Software Customisation Reference Manual.
You do not have to be a professional programmer to start to learn PML, although you may
find this manual difficult to follow unless you have some understanding of programming
concepts such as if statements and do loops. If you have no programming experience, you
should consider attending a PML Training Course. For details, contact your local support
office, whose address is given on the copyright page of this manual.
The current version of PML, sometimes referred to as PML2, may simply be seen as an
extension to the original PML1 facilities. However, the more powerful techniques that are
available mean that many programming tasks are carried out in different ways.
There are some tasks which are carried out more efficiently using PML 1 facilities. These
are described in this manual where necessary, you should also refer to the Database
Management Reference Manual.
To minimise this risk, it is most important that your in-house customisation policies constrain
any changes which you make to the Applications so that they retain maximum compatibility
with the standard product at all times.
Remember that AVEVA Ltd can give you full technical support only for products over which
it has control. We cannot guarantee to solve problems caused by software which you have
written yourself.
Warning: You should never modify the original files, so that you can always revert to
using these if things go wrong.
• The environment variable PMLLIB is a search path which must be set to the directory
or list of directories under which user-defined PML Functions, objects and forms are
stored. The directory specified can contain sub-directories which will automatically be
scanned by PML.
• When writing your own PML Application macros or functions, follow the styles and
conventions used in the AVEVA standard AppWare.
• Ensure that the names of any new forms which you create cannot conflict with the
names of any standard forms which may be used in the AVEVA Applications. You
should prefix the names of your forms and global variables with a suitable code to
distinguish them from AVEVA names. For example, a company XYZ could choose to
start all form and global variables names with the characters XYZ.
Icon naming
Icons are named without regard to any rules, except on odd occasions.
Actions Various
Applications Various
Assembly ADP AD
Constructs Various
Draft DRAFT
Splash Various
Warning: Changing the value of an argument inside a PML Functions or method will
change the value in the calling function or method.
Object Description
Object Description
BOOLEAN This is used for the result of logical expressions and holds
the value TRUE or FALSE
There are also system-defined variable types, such as POSITION and ORIENTATION in
PDMS.
The following commands define an object type FACTORY with two REAL members and a
STRING member:
define object FACTORY
member .Name is STRING
member .Workers is REAL
member .Output is REAL
endobject
Here is another example where the object type FACTORY is itself used for one of the data
members of another object type:
define object PRODUCT
member .Code is STRING
member .Total is REAL
member .Site is FACTORY
endobject
It is a good idea to use upper case for object-type names, and mixed upper and lower case
for variable names, the objects themselves. For example, a type might be WORKERS, and
a variable name might be NumberOfWorkers.
• Remember that within the methods of PML Objects and Forms you generally need only
local variables. Any values which are to be exported should be made available as
members or as method values or returned arguements.
• Only use PML Functions (necessarily global) for tasks which are genuinely globally
accessible, i.e. independant of a specific object or form, or which need to be shared by
several objects or forms.
CADCentre !!CADC
Draft !!CDR
Defaults !!DFLTS
Various Others
A variable may also be given a type without giving it an initial value, in which case it will
have the value UNSET:
!!Answer = REAL()
!Name = STRING()
!Grid = BOOLEAN()
!Lengths = ARRAY()
For more about unset variables, see UNSET Values and UNDEFINED Variables.
A PML Function returning a value may be used wherever an expression or PML Variable
can be used, for example, this call to the !Area function defined above:
!PartLength = 7
!PartWidth = 6
Note: You cannot switch to a different PDMS module if a PML Function is running. Use
EXIT to exit from the function if it has failed to complete.
Note: The!! signifies that the function is user-defined and that it is global - but !! does not
form part of the external filename. All user-defined functions are global and only one
may be defined per file.
Note: The define function must be the first line in the file and that its name and the file
name must correspond.
The ( ) parentheses after the name of a procedure or function must always be present —
even for procedures that do not need arguments:
define function !!Initialise()
!TotalWeight = 0
!!MaxWeight = 0
endfunction
call !!Initialise()
Although the call keyword is strictly speaking optional, its use is recommended with
procedures.
Note: As well as procedures, you can invoke a PML Function that has a return value using
call, in which case the function result value is discarded.
Note: The use of ANY should be the exception rather than the rule as it switches off
argument type checking - an important feature of PML Functions to help ensure
correct functioning of your PML.
In the case an argument of type ANY, a value of any type may be passed as the argument to
the function:
define function !!Print(!Argument is ANY)
$P $!Argument
endfunction
Where an argument of type ANY is used, you may need to find out its actual type before you
can do anything with it. The ObjectType() method can be used for this purpose:
define function !!AnyType(!Argument is ANY)
Type = !Argument.pmlobjectType()
if ( !Type EQ 'STRING' ) then
- - do something with a STRING
elseif ( !Type EQ 'REAL' ) then
- - do something with a REAL
elseif ( !Type EQ 'DBREF' ) then
- - do something with a DB Reference
else
- - do something with all other types or give an error
endif
endfunction
The Software Customisation Reference Manual contains a table of the object types supplied
as part of PML 2. For each object type, there is a list of the associated methods and
members.
For each object type, the table shows:
Result or Type For members, we use the heading Type; for methods, we
use the heading Result.
The type of the member or the result describes what kind of
object we are expecting to see as a member or result of the
method.
For example, the result of the method Cosine is a REAL
value.
Some methods do not return a value: these are shown as
NO RESULT.
Purpose This column tells you what the member or method does.
2.8.1 Example
This section explains how to use methods, using a STRING variable as an example.
Although the STRING is a simple object type, it has a large number of methods which can
be called.
For example, if you are interested in the length of the string value, look under the list in the
Software Customisation Reference Manual for STRING objects, and you will find a method
named Length.
This method returns a REAL value (the number of characters in the string), but has no
effect on the variable itself.
You can extract the number of characters in the string and store it in a new variable,
!Nchars, by calling the method as follows:
Notice the dot separator between the name of the variable and the name of the method.
Also note the ( ) brackets following the name of the method. The brackets are used to
enclose the arguments of the method, but they must be present even if there are no
arguments.
Other variable types are system-defined variables. Most of these have adopted the unset
string ‘Unset’. For example:
User-defined data types can also provide a String() method. These also support an
UNSET representation, and usually adopt the UNSET representation ‘Unset’.
Functions Methods
An UNDEFINED variable is one that does not exist. The existence of a variable may be
tested with these functions:
if ( Undefined(!!Y) ) then . . .
if ( Defined(!!Y) ) then
There is no equivalent method call. If the variable does not exist, attempting to call a method
would result in an error.
PML Functions and methods on objects (including forms) are the recommended way of
storing command sequences because:
• There is a check that they have been called with the right type of arguments.
• Arguments can return values.
• A PML Function or method can return a result of any type.
Most new AppWare code is written as methods on objects. PML Macros are explained
in Macros as they are the basis of the older AppWare code.
PML Macros are normally stored in a directory under the PDMSUI search-path.
PML Functions are automatically loaded from a directory under the PMLLIB search-path.
A comment may extend over several lines provided it is enclosed in the escape sequences
$( and $) .
$( A comment containing
Note: This is a different use of return from the command used to set return values of
variables.
3.3 Abbreviations
Many commands have a minimum abbreviation which can be used in place of the full
command.
For readability it is recommended that you use the full form of the command — note that it is
not less efficient than using the abbreviation.
PML keywords such as if, else and do have no abbreviations.
A number of other escape sequences will be described later in this manual. The important
point to note here that if you need the dollar character itself as part of a command, you will
need to double it:
$$
As the last character on a line, $ means that the next line is a continuation line.
For example:
$P This is an example of a much longer message that will
be $
output to the screen
This command scans all the files under the first directory in your PMLLIB path, and updates
the pml.index file.
If other users have added PML Files and updated the appropriate pml.index files, you can
access the new files by giving the command:
pml index
This command re-reads all the pml.index files in your search path without rebuilding
them.
When you are not running an AVEVA product, you can update the pml.index file in a given
directory by giving the command:
pmlscan directory_name
This command runs a utility supplied with AVEVA products.
Note: The commands pml rehash and pml index are a type of command known as
PML directives: they are used outside PML Files to direct PML itself to take certain
actions. More information about using PML directives with form definitions is given in
PML Directives
The following command scans all the files in all the directories in your PMLLIB path, and
updates the pml.index files.
pml rehash all
4 PML Expressions
Each expression has a type such as REAL, STRING or BOOLEAN. All the elements in an
expression must be of the correct type. For example:
is meaningless if !X is REAL and will result in an error. (But see Concatenation for using the
concatenation operator to convert different types to STRING.)
Operator Comments
FUNCTIONS
*/
+-
NOT
AND
OR
Operator Comments
Note: The operators EQ, NE, LT, GT, LE and GE are sometimes referred to as comparator
or relational operators; NOT, AND, and OR are sometimes referred to as Boolean
4.4 Concatenation
The & operator concatenates two STRING values (joins them end-to-end) to make a result
of type STRING. Values of any type are automatically converted to a STRING first:
5 Control Logic
There are four types of construct for implementing control logic within a PML Function or
Macro. These are:
• The if construct for conditional execution of commands.
• The do command for looping and the associated break and skip.
• The golabel for jumping to a line with a label.
• The handle construct for dealing with errors.
5.1 IF Construct
The full form of an if-construct is as follows:
if (!Word EQ 'Peanuts' OR !Word EQ 'Crisps') then!Snacks
= !Snacks + 1
!Meal = FALSE
elseif ( !Word EQ 'Soup') then
!Starters = !Starters + 1
!Meal = TRUE
elseif (!Word EQ 'Fruit' Or !Word EQ 'IceCream' ) then
!Puddings = !Puddings + 1
!Meal = TRUE
else
!MainCourse = !MainCourse + 1
!Meal = TRUE
endif
Each BOOLEAN expression, such as (!Word EQ 'Soup'), is examined in turn to see
whether it is TRUE or FALSE. As soon as an expression that is TRUE is encountered, the
following block of commands is executed.
Once a block of commands has been executed, everything else up to the endif is ignored.
The else command is optional. If it is included, you can be sure that exactly one command
block within the if-construct will be executed.
The elseif commands are also optional. Once one of the elseif expressions has been
found to be TRUE, any remaining elseif commands are ignored.
Thus the simplest form of the if-construct is:
if ( !Number LT 0 ) then
!Negative = TRUE
endif
You may not concatenate the commands into one line, so the following are not allowed:
if ( !TrueValue OR !UnsetValue)
if ( !FalseValue AND !UnsetValue)
ignore !UnsetValue if the value is not required to determine the outcome of the expression.
The same is true for PML Functions which have returned an error.
if ( TRUE ) BOOLEAN
if ( FALSE )
if ( T ) BOOLEAN
if ( F )
if ( YES ) BOOLEAN
if ( NO )
if ( Y ) BOOLEAN
The following do not return BOOLEAN values and are therefore invalid:
if ( 1 ) REAL
if ( 0 )
if ( ‘TRUE’ ) STRING
if ( ‘FALSE’ )
if ( ‘T’ ) STRING
if ( ‘F’ )
Variable = 1 REAL
if ($Variable)
For upward compatibility with PML1, STRING variables set to ‘TRUE’, ‘FALSE’, ‘YES’ or
‘NO’ or their single-letter abbreviations can be used in an if-test as long as they are
evaluated with a preceding $. For example:
There is a built-in PML Method and a function for converting a value to BOOLEAN:
!MyString = ‘TRUE’
if (!MyString.Boolean() ) then . . .
The Boolean conversion is as follows:
Code Result
5.2 DO Loops
A do-loop enables a series of commands to be repeated more than once. The number of
times the series is repeated is controlled by a counter.
Control is passed to the line following the enddo command after executing the commands
within the loop with the counter variable at its final value.
The full format of the do-loop is as follows:
do !x from 10 to 100 by 10
!Total = !Total + !X
enddo
The endo must be present at the end of the loop.
The !X , from, to and by are optional, therefore in its simplest form you may use:
do
commands block
enddo
This will loop forever unless something in the commands block stops the loop (see break
and golabel below)
Element Purpose
from Defines the value of the counter for the first time round the
loop.
If you do not give a value, it will default to 1.
to Defines the value of the counter for the final time round
the loop.
The default is infinity.
GOLABEL /FRED
The label name /FRED has a maximum length of 16 characters, excluding the / slash which
must be present. The next line to be executed will be the line following LABEL /FRED,
which could be before or after the GOLABEL command.
6 Arrays
An array subscript of zero is allowed but you are advised against using it as many of the
array facilities ignore array element zero.
An array subscript may be an expression of any complexity provided it evaluates to a
positive REAL result and may even include a call to a PML Function:
!Value = !MyArray[!A + (!B * !!MyFunction() ) + !C ]
PML Arrays may be heterogeneous. That is to say the elements of a PML array do not have
to be all of the same type. Array elements may even be user-defined objects.
Non-existent array elements — after the last set element of the array and the non-existent
elements in the gaps of a sparse array — are all UNDEFINED: the function Undefined()
will return TRUE and the function Defined() will return FALSE for all these subscripts.
Array methods are built-in functions for performing a variety of operations on the array.
These methods are invoked with a dot following the array name. The method name must be
followed by ( ) parentheses - even if the function has no arguments:
!Nelements = !MyArray.Size()
This method sets !Nelements to the number of elements currently in the array. This is an
example of a no-effect method which does not alter the array but returns a REAL result
which can be assigned to another variable.
Here is method which does change the array:
!MyArray.Clear()
This is an example of a method which modifies the array by deleting all the array elements
but produces no-result value, so there is nothing to assign to another variable.
There is a third kind of method which both changes the array and returns a result value.
Here the result is assigned to a variable:
!NewArray = !OldArray.RemoveFrom(5,10)
This is an example of a method result which modifies the array by removing 10 elements,
starting at element 5. NewArray value is a new array containing the 10 removed elements.
If not required, the result can be simply discarded by invoking the method as a command
and not assigning the result to a variable:
!OldArray.RemoveFrom(5,10)
Note: Always use an array method, if one is available, in preference to constructing a do-
loop as it is far more efficient.
The white-space is treated as a special case since consecutive white-spaces are treated as
a single delimiter and white-spaces at the start or finish of the string are ignored.
The result is to create the array-variable !ArrayOfFields (if it does not already exist) with
the element !FIELDS[1] set to '123', !FIELDS[2] set to '456', and !FIELDS[3] set to '789'.
To specify a different delimiter, specify the required (single) character as the argument to the
Split() method:
!Line = '123 ,456 ,,789'
!ArrayOfFields = !Line.split(',')
In this example, comma is used as the delimiter. !ArrayOfFields is created if it does not
already exist with the element !FIELDS[1] set to '123', !FIELDS[2] set to '456', !FIELDS[3]
created but set to zero length, and !FIELDS[4] set to '789'. Note that in this case, unlike the
white-space delimiter, consecutive occurrences of the comma define empty elements.
Note: The only way to set the special white-space delimiter is by default; that is, by not
specifying any delimiter as an argument to the Split() method. If a space is
specified explicitly as the delimiter (as ' '), it will behave in the same way as comma in
this example.
You can combine an array-append method with a text-string Split() method in a single
command to append the fields of a text string to the end of an existing array variable, thus:
!ArrayOfFields.AppendArray(!Line.Split())
Note: If the array contained elements that were not strings, these are ignored when
calculating the maximum width.
The sort is into ascending order and will be an ASCII sort on an array of STRING elements
and a NUMERIC sort on an array of REAL values.
The Sort() method returns the array itself as a list result, so it is possible to follow the call
to the Sort() method immediately with a call to the Invert() method, which will return a
descending sort:
!MyArray.Sort().Invert()
An alternative approach is an indirect sort using the method SortedIndices() which
returns a REAL array representing new index positions for the array elements in their sorted
positions:
!NewPositions = !MyArray.SortedIndices()
The array can be sorted by applying the new index values using the ReIndex() method:
!MyArray.ReIndex(!NewPositions)
More important, the index values in !NewPositions can be used to sort other arrays as well.
To use some simple examples, imagine we had the array !Animals that contained:
Index Animal
[1] Wombat
[2] Kangaroo
[3] Gnu
[4] Aardvark
[5] Antelope
The command:
!Animals.Sort ()
Would move the array elements so that they now appeared in the following order:
Index Animal
[1] Aardvark
[2] Antelope
[3] Gnu
[4] Kangaroo
[5] Wombat
Would create a new array !index representing the subscript values of the array elements in
!Animals then sort these index values (without altering the array !Animals). After the sort,
!index would look like this:
Index Subscript
[1] 4
[2] 5
[3] 3
[4] 2
[5] 1
The command:
!Index.Invert()
Would result in !index now looking like:
Index Subscript
[1] 1
[2] 2
[3] 3
[4] 5
[5] 4
Where a multi-level sort is required it is still necessary to use the older facilities of the VAR
command to perform the sort. Using a different example, look at the arrays !Car, !Colour
and !Year:
If you want to sort the arrays by car model, then on colour, and then on year, you would give
the command:
The sorted index values are put into the array !Index, (for clarity shown here with the rows
of the original arrays they are pointing to),
By default values are sorted ascending order using the ASCII character-codes. Other
options which may be supplied after each array name are:
You can also modify the array to eliminate empty or repeated elements:
Option Effect
To sort these arrays and identify the last occurrence of each group of the same car type, use
the LASTINGROUP option:
This would create !Index by sorting the values in the array !Car , but would also would also
create the array !Last:
[1] 1 1 Ö CHARIOT
[2] 2 Ö FORD
[3] 4 Ö FORD
[4] 5 Ö FORD
[5] 10 2 Ö FORD
[6] 3 3 Ö VAUXHALL
[7] 7 4 Ö ford
[8] 9 5 Ö vauxhall
[9] 6 Ö (Unset)
[10] 8 6 Ö (Unset)
We can then generate an array of subtotals for each type of car with the following command:
Take care that the expression does in fact return a value so that new array’s elements can
be created and assigned to it.
Alternatively, you may use an evaluation expression which does not return a result provided
you invoke the evaluation directly without trying to assign the result. For example, if you
have defined a !!Print() function the following is valid:
!Output = object
BLOCK('!!PRINT(!MyEmployees[!Evalindex].Name)')
!MyEmployees.Evaluate(!Output)
Note: In this release there is no object-oriented shorthand representing ‘this element’. It is
necessary to give the array name along with a subscript expression using
!Evalindex.
On exit from a do values loop PML destroys the loop counter variable.
With do indices the counter takes the value of each array subscript at which an array
element is stored:
!Pump[1] = ’Fred’
!Pump[20] = ’Tom’
!Pump[10] = ’Jim’
do !IN indices !Pump
!Value = !Pump[!IN]
SP Array Element $!N is $!Value
enddo
This will produce the following output:
Array Element 1 is Fred
Array Element 10 is Jim
Array Element 20 is Tom
7 Macros
In the simplest case, parameters are allocated positions in the command lines as macro
arguments by inserting escape codes of the form
$n
where n is an integer in the range 1 to 9. These arguments are then specified as part
of the command to run the macro. They follow the macro name, with spaces
separating the individual arguments.
For example, if a macro named beam.mac includes the command line
NEW BOX XLEN $1 YLEN $2 ZLEN $3
then the macro call
$M/BEAM.MAC 5000 200 300
will run the macro and will set the lengths defined as $1, $2, $3 to 5000, 200 and 300
respectively.
Arguments may be either values or text, but note that a space in a text string will be
interpreted as a separator between two different arguments.
Apostrophes in text arguments are treated as parts of the arguments, not as separators
between them. For example, if a demonstration macro arg.mac includes the lines:
$P First Argument is $1
$P Second Argument is $2
$P Third Argument is $3
and is called by the command
$M arg.mac ’Arg1a Arg1b’ ’Arg2’ ’Arg3’
the resulting output will be
First Argument is ’Arg1a’
Second Argument is ’Arg1b’
Third Argument is ’Arg2’
whereas the intended output was
First Argument is ’Arg1a Arg1b’
Second Argument is ’Arg2’
Third Argument is ’Arg3’
If you need to include spaces or newlines in an argument, you must enclose the argument
between the escape codes $< and $>.
The correct form for the preceding example is therefore
$M/arg.mac $<’Arg1a Arg1b’$> ’Arg2’ ’Arg3’
As an alternative, you may redefine the separator between arguments to be the escape
code
$,
instead of a space.
If you do this, you must end the argument list with the escape code
$.
Note: The full stop is part of the escape code, not punctuation.
To avoid possible errors, a default setting may be defined for each argument. If the
argument is omitted when the macro is called, the default setting will be used.
To define an argument’s default setting, use the command
$Dn = default_string
where n is the argument number (1-9) and default_string is any sequence of
characters ending in a newline.
The default setting may be specified at any point in the macro, but it will only be applied from
that point onwards. It is usually best, therefore, to define any defaults at the beginning of the
macro. If an argument has been specifically defined, a subsequent default specification is
ignored.
Most of this manual describes how to use PML 2 to create and customise Forms and
Menus. This chapter describes how to use PML within AVEVA products. Note that for tasks
such as defining Rules and Report Templates, you are restricted to the PML 1 expressions
package described in the Software Customisation Reference Manual, and it is also
sometimes necessary to use the VAR command.
Index Value
COMPOSE always returns an array with at least one element. The number of array
elements depends on the length of the text strings supplied and the width of each column.
Notice that all of the STRING array elements are space-padded to the same length.
Following the COMPOSE keyword is a list of column definitions. For each column, there is a
text string, such as |$!A| which evaluates to a text string, followed by the column layout
keywords in any order:
Keyword Effect
SPACES n Specifies the number spaces between this and the next
column.
!Item = !!CE
!!CE is of type DBREF so the new variable !Item will also be of type DBREF. The dot
notation can be used to access attributes and pseudo-attributes of a database element:
!Bore = !Item.bore
This form of access can also be used directly on the !!CE variable:
!Owner = !!CE.owner
It is also possible to follow references between DB elements using this mechanism:
!Rating = !!CE.cref.pspec.rating
Assigning a new reference to the !!CE variable makes the new reference the current
element by navigating to it in the database:
!!CE = !!CE.owner
P-points are accessed using the P-point number like an array subscript. For example, to
access the direction of P-point[1]:
!Array is the name of the array that will be created to contain the results of
(expression) for all the elements selected within select.
(expression) is the expression that will be carried out for all the elements that
match the select criteria.
select is the selection criteria (see above, and the relevant Reference
Manual for your product for details of selection criteria)
COUNTVAR is an optional command which allows you to record how often the
expression is calculated in Counter , which is increased by one
each time the expression is evaluated.
You can append the results of such an evaluation to an existing array using the APPEND
keyword. For example:
VAR !BOXES APPEND EVALUATE ( XLEN*YLEN ) FOR ALL BOXES
will add the values calculated from the expression for all BOXES to the (already existing)
array BOXES.
You can also overwrite elements in the array by specifying the first index in the array which
you want to be overwritten. The specified index, and the indexes following it, will be
overwritten by the results of the evaluation. For example:
VAR !BOXES[99] EVALUATE ( XLEN*YLEN ) FOR ALL BOXES
will place the result of the first evaluation for the selected elements at index 99, overwriting
any existing item, and the following results in the subsequent array elements.
Assignment using = , the assignment operator, usually does what you would expect, but a
detailed explanation of how the different data types are handled may be helpful.
9.1 Assignment
Assignment always makes a copy of the right-hand-side to replace what is on the left-hand-
side. The following command copies !Y into !X:
!X = !Y
If, for example, !Y is an array, this command will duplicate the entire array making a copy of
each of the array elements. The same is true if !Y is an OBJECT.
The technical term for this is deep copy. Following this copy
!X[1] = ‘New Value’
will change !X[1] but leave the original array !Y unchanged.
9.3 DB References
!!CE is a DB reference. Following the command:
!X = !!CE
!X is now a new reference to the same DB element, but the element itself has not been
copied.
!Value = !X.Attribute
!S = ‘Old Value’
!!ChangeString ( !S )
However, the following will result in a PML error message because the value passed to the
function as an argument is a CONSTANT STRING value which cannot be modified.
endhandle
$* This line is processed after the handle block
If (46,27) matches the error, PML processes the commands in that handle block instead of
outputting an error. Processing of the current PML Macro or function continues at the line
after the endhandle command.
Note: The keywords ANY and NONE which can optionally be used in place of a specific
error code.
If the line following a line which has caused an error is not a handle command, the outcome
depends on the current setting of onerror (see next section). The default setting is
onerror RETURN and the current macro or function is abandoned.
However, if in the calling PML Macro or function the next command to be processed is a
handle command, it now gets an opportunity of handling the error (and so on up the calling
PML).
You can also re-instate the error but suppress the alert using the command:
return error noalert
To generate a new error (or replace an error with your own error) plus an optional message,
use one of the following
return error 1
return error 1 ‘Your error message’
return error 1 NOALERT
To handle such an error there is a special form of the handle command:
handle 1
PML code to handle a user-defined error number
endhandle
Reading and writing files is greatly simplified by using the FILE object. This chapter
describes the methods most frequently used for reading and writing.
Methods are also provided for moving, copying and deleting files and extracting information
such as the pathname of a file, its size data and time last modified.
A FILE object may refer to a directory rather than a file and methods are provided for
navigating around the directory structure.
For a complete list of the methods available, refer to the Software Customisation Reference
Manual.
11.1.1 Example
This example reads pairs of numbers from file data, adds them together and writes the
answers to file RESULTS.
!Input = object FILE('DATA')
!Input.Open(‘READ’)
!Output = object FILE('RESULTS')
!Output.Open(‘WRITE’)
do
!Line = !Input.ReadRecord()
if (!Line.set()) then
!array = !Line.Split()
!Total = !Array[1].Real() + !Array[2].Real()
!Output.WriteRecord( !Total.String() )
else
break
endif
enddo
!Output.Close()
!Input.Close()
Note: With the ReadFile() method you may optionally specify the maximum number of
lines you are prepared to read and an error is raised if this number is exceeded. If not
specified, a limit of 10000 is imposed.
Fully developed PML code should - if required to - interact with the user by means of forms,
menus, and alert boxes. However, while you are still developing your PML code the facilities
described here might be useful.
The commands described here should normally only be typed interactively or included in a
PML File on a temporary basis. None of these commands should remain in fully developed
PML code.
Command Effect
13.1 Overview
As a user of AVEVA products, you will already be familiar with forms, and the gadgets
(buttons, textboxes, etc) on them.
In PML 2, forms are a type of object represented by a global variable — the form’s name, for
example !!EntryForm. This means that a form cannot have the same name as any other
object type, global variable, or any other form.
The form object owns a set of predefined member variables and built-in methods. In
addition, you can define your own members — form variables and form gadgets — and your
own form methods. All these will determine the content and functionality of the form.
Gadget objects are user-defined members of the form object. Form members are always
accessed using the dot notation, for example
!!EntryForm.TextField
Gadgets own a set of predefined member variables and built-in methods which determine
the content and functionality of the gadget. For example, the value of a text field gadget is
accessed by:
!!EntryForm.TextField.val.
Note that gadgets do not support user-defined member variables or user-defined gadget
methods.
Note: All the in-built members and methods of forms and gadget-types are listed in the
Software Customisation Reference Manual.
Callbacks are user-defined actions assigned to a form and its gadgets and that are
executed when the operator interacts with the form, for example, by clicking a mouse button
on a gadget.
The callbacks are supplied as text strings and may be any valid PML expression, PML
Function or method call, including any form, gadget or user defined object method.
Effectively, these callbacks determine the intelligence of the form.
Note: That total length of the name is limited to 256 characters even though the length
each individual components is limited to 64 characters. Within the form definition, the
members of the form should be referred to by using !This to replace the form name
part of the gadget name. For example:
!This.GadgetName
!This.GadgetName.val
Note: The obsolete convention of using an underscore to replace the form name has been
retained for compatibility with earlier versions.
The following example defines a small form, containing the text ‘Hello World’, and a
button labelled ‘Goodbye’, which removes the form when pressed:
setup form !!hello
paragraph .Message text 'Hello world'
button .bye 'Goodbye' OK
exit
Some points to note about the above definition and the form it creates are:
• there are no user-defined methods on this form, and so the setup form . . . exit
sequence is the complete form definition;
• the paragraph command adds a paragraph gadget to the form (a paragraph gadget is
just a piece of text displayed on the form). The name of the gadget is Message, and
the dot before the name indicates that the gadget is a member of the form. The text
itself is specified after the keyword TEXT.
• the button subcommand adds a button gadget named .bye. The text on the button
will be ‘Goodbye’. The keyword OK is a form control attribute that specifies that the
action of this button is to remove the form from the screen. (For more about form
control attributes, see Form Attributes.)
To display the form in this example, you can use the command:
show !!Hello
Note: The use of This to mean the current form. When the callback is executed,
!this.message.val = ‘Modified’
will set the value member of the gadget Message on this form to read Modified rather
than Hello world.
|!!OtherForm.Para.val = ‘Modified’|
to change the value of a paragraph gadget on another form named !!OtherForm.
Typically a callback will involve many commands and could be a complex piece of code in
its own right. In practise, the recommended way of defining a complicated callback is to use
a form method.
First though, we need to store our form definition in a file so that we can edit it at will.
Once a form has been displayed you can remove it from the screen using the command
hide !!formname
Note that if you show it again it will appear on the screen, but its definition is already known
to PML and so it will not be loaded.
It is possible to remove a form definition from PML using the command
kill !!formname
The form is then no longer known to PML until a new definition is loaded.
Note: Earlier AppWare used form definitions in macro files which had to be loaded explicitly
via $M path-name-to-file. This mechanism still operates for backwards
compatibility, but is strongly discouraged when writing new AppWare.
Note: Unlike the PML commands described in How Forms are Loaded and Displayed, PML
directives should not be included in callbacks, but are generally for command line
use.
You will need to use PML directives when you are developing new form definitions or
modifying existing ones. PML directives are commands of the form pml . . .
The table below gives some useful PML directives.
Command Effect
pml reload form !!formname When you edit an existing form while an
AVEVA product is running, you must use
this directive to reload the form definition
file.
See Developing PML Code for tracing commands that will be useful when you start to
develop PML code.
Note: This chapter looks at the callback in more detail. If you are reading this manual for
the first time then you may want to skip this chapter and return to it later.
Before you read this chapter you should have read and understood the Form Concepts
chapter, in which we saw that callbacks are user-defined actions which are assigned to a
form and its gadgets
They are supplied as text strings and may be any valid PML expression, PML Function or
method call, including any form, gadget or user-defined object method.
Most callbacks require more than a single command, so invoking a method or function (or
macro) is an essential requirement.
The advantage of using form methods as callbacks is that this keeps the whole form
definition in a single file. Forms defined in early versions of PML 2 used PML Functions as
callbacks. This is still valid and is sometimes even essential as you may need to manage a
group of forms; but mostly the callbacks on a form are specific to that form.
You should note that a method or function invocation is just a valid PML expression, but
such a powerful expression that it is worthy of its own section in the manual.
We have already used a form method as a callback in the revised !Hello form in Form
Concepts chapter:
setup form !!hello
title ‘Display Your Message’
paragraph .Message width 15 height 1
text .capture ‘Enter message’ width 15 is STRING
button .bye 'Goodbye' OK
exit
define method .hello()
--default constructor - set gadget default values
!this.message.val = ‘Hello world’
!this.capture.callback = ‘!this.message.val = !this.capture.val’
!this.Okcall = ‘!this.success()’
endmethod
define method .success()
!this.capture.val = ‘’
endmethod
The .success() method above could only deliver a fixed string ‘Hello again’ to the
message PARAGRAPH gadget. The great advantage of methods is that you can pass
variables as arguments to the method, so it can be used more generally, for example as the
callback to several gadgets.
define method .success( !output is GADGET, !message is STRING, !input
is GADGET )
output.val = !message
input.val = ‘’
endmethod
We have added three arguments, an output gadget, an input gadget and a message string
variable. This has made the method very general. We can still use it as the Okcall callback:
!this.Okcall = |!this.success( !this.message, ‘Hello again’,
!this.capture )|
When the OK button is pressed the Okcall action will invoke the success() method,
passing to it the message paragraph gadget as !output, the message text ‘Hello
again’ as !message and the text input field gadget capture as !input. The method will do
just what it did before.
However, we could use it differently. We could add another button gadget, Restart, to the
form:
button .restore 'Restart' callback |!this.success( !this.message,
‘Hello world’, !this.capture )|
When clicked, the Restart button‘s callback will execute and set the message paragraph
gadget to read ‘Hello world’ and clear the capture text field, thus restoring the form to
its state when it was displayed for the very first time.
If we invoked the success() method as:
!this.success( !this.capture, ‘Hello world’, !this.message )
it would set the value ‘Hello world’ into the capture text input field and clear the
contents of the message PARAGRAPH gadget. Not what you need here perhaps, but you
can see how versatile methods can be!
Note: The arguments to methods can be any valid PML object types, built in or user
defined.
14.3.1 Events
When the operator interacts with a GUI, an event occurs. For example, when the operator:
• types something into a field on a form;
• moves the cursor into a window;
• presses down a mouse button;
• moves the mouse with button down;
• lets the button up.
The events are queued in a time-ordered queue. The application software services this
queue: it gets the next event, determines the object of the event (for example, form,
gadget, menu) and the event type (for example, enter, leave, select, unselect, popup
etc), deduces appropriate actions and carries them out. When one event is completed,
the software looks for the next event.
• !action is the meta-event that occurred on the object and represents the action to be
carried out by the method.
The open callback is a string of the form:
'!this.MethodName('
Note: The open bracket '(', no arguments and no closing bracket. The callback is to an
open method or function.
exit
do !i from 1 to 10
enddo
this.choose.dtext = !fields
endmethod
endif
endmethod
• Note in the constructor method open() we have initialised the list so that field n will
display list field n. DTEXT is a shorthand for display-text, that is the text displayed in
the list field.
• Control is the method which manages interaction with the list. Note the open callback
defined in list choose.
• Note the use of $* for in-line comments
• Note the use of the printing command $p to print information to the system Request
channel. $!form replaces the variable !form with its current value as a string - this
only works for PML simple in-built scalars types REAL, STRING, BOOLEAN. $n in
$!form$n.$!name is a separator needed to separate $!form from the following ‘.’
which would otherwise be thought of as part of the variable name.
When a list field is clicked, the list’s callback will be examined and if it is an open
method or function, then the Forms and Menus software will supply the arguments
required to complete the function.
Thus in this case the actual callback string executed will be
|!this.control( !!Open.choose, ‘SELECT’ )|
Inside the control() method we branch on the value of !action and access the data of
!object (in this case our list). Finally we perform our application’s resultant action - in this
case just printing out all we know about the operator interaction using the $p command,
which for a selection of an un-highlighted list field will write:
Selected OPEN.CHOOSE LIST field 5, Dtext{list field 5}
Notice that the same method could be used to manage several or even all gadgets on our
form since the variable !object is a reference to a gadget object and is supplied as
!!Open.choose, the full name of the gadget including its form. This allows us to find
everything about that object from its in-built methods, including its gadget type (LIST,
TOGGLE, etc) and its owning form:
!type = !object.type()
!form = !object.owner()
All the in-built members and methods of forms and gadget types are listed in the Software
Customisation Reference Manual.
Note: The 2D and 3D VIEW objects supplied in AVEVA products, in particular those in
which Event-Driven Graphics are used, may have open PML Functions as
callbacks. However, open callbacks on these gadgets are not supported as a user-
definable facility at this release.
15 Forms
Before reading this section you should have read the Form Concepts chapter.
• The commands which create the gadgets on the form, and specify how they are
arranged.
• The definitions of any variables which are to be members of the form.
MAIN The form that will be swapped to as a Main form. These forms
are not usually displayed directly, but serve to provide gadgets
for the application’s toolbar and menus for the application’s
main menus.
DOCUMENT Resizable form usually with a view gadget, but no menu bar.
All document forms can be floated or un-floated using the
right-mouse popup menu in the form’s top border. When it is
floating, you can drag the form away from the MDI frame and
position it and resize it without constraint. This allows you to
drag the document form away to another screen of a multi-
screen configuration.
DIALOG This is the default type the form will assume if you give no type
when you set up the form. The default DIALOG form will be
non-resizable, floating, and non-docking. You can specify the
DOCKING attribute to allow the form to be docked within the
application frame. By default, a docking dialog is displayed
floating, and you can interactively dock it. When a dialog is
docked it will be resized to match the application frame edge
to which it is docked, and so is resizable by default. The
qualifiers LEFT, RIGHT, TOP, and BOTTOM, specify the edge
of the application frame to which the dialog form will be
docked when first displayed.
BLOCKINGDIALOG Normal form layout and content, but will block access to all
other forms while it is displayed.
Here are some examples of ways you can set up forms of different types:
However, the DOCK and ANCHOR attributes are mutually exclusive: setting the DOCK
attribute resets the ANCHOR to the default; setting the ANCHOR attribute resets DOCK to
none.
ALPHA and VIEW gadgets do not support DOCK or ANCHOR attributes. They do,
however, expand to fill their containers, so you can put them in a frame and set the frame’s
DOCK or ANCHOR attributes to get the behaviour you desire.
or directly by
!!MyForm.initcall = ‘This.InitCallBack()’
Note: The form initialisation callback must not attempt to display another form. You may
invoke an ALERT object but not otherwise seek any input from the user.
If the callback discovers an error so serious that the form cannot be displayed it can abort
the display of the form by returning an error. You can supply the text of an error message
that is to be presented in an error alert in place of the form:
define method .initcallback()
:
return error 1 'You do not have write access to this
database'
endmethod
If the initialisation callback has already caused an alert to be raised then you can prevent
the raising of a new error alert by using the NOALERT keyword:
define method .initcallback()
:
return error 1 NOALERT
endmethod
You can assign to the form’s CANCELCALL member using the command
CANCELCALL ‘CallbackString’
You can modify the CANCELcallback at any time using
!this.Cancelcall = ‘CallbackString’
See also Button Gadgets.
quitCall ‘!!quitMain( )’
. . .
exit
define method .myApplication( )
-- Constructor
!this.quitCall = ‘!!quitMain( )’
. . .
endmethod
Note: F&M does not display an alert for the returned error, it is merely for communication.
You don’t need a QUIT callback if you just want to allow the form to be hidden. For
DOCUMENT forms (MDI children) only, the callback must not display an alert as
this will cause some gadgets to malfunction afterwards.
otherwise not be destroyed. This may be necessary because PML global objects will survive
an application module switch, but may not be valid in the new module.
Notes:
1. The callback method MUST NOT carry out any modifications to the Gadgets belonging
to the form or to the Form itself (e.g. don't show or hide the form). Attempts to edit the
form or its gadgets may cause unwanted side effects or possible system errors.
2. Form callbacks designed for other Form events (e.g. CANCEL, INIT) are rarely suitable
as killing callbacks.
3. Restrict form and gadget operations to querying.
A form can be displayed as a free standing form, for example by show !!form free. It
then has no parent so it will not disappear when the form which caused it to be displayed is
hidden.
When one form causes another form to be displayed, such as when a button with the FORM
keyword is pressed or a gadget callback executes a show !!form command the result is a
child form.
A form can have many child forms (and grand-children…) but a child form has only one
parent - the form which caused the child form to be displayed. The nest of related forms is
called a Form Family.
The Form Family exists just as long as the forms are displayed. If a form is already on the
screen when it is shown, it is brought to the front of the display. If the child form is already in
a Form Family it is transferred to the new parent.
If the user presses the OK button of a parent form, the system in effect presses the OK
buttons on each of the child forms, 'youngest' first, invoking their OKCALL callbacks. The
parent form and all child-forms are hidden and the Form Family then ceases to exist.
If the user presses the CANCEL button or uses the window's CLOSE controls, the system
in effect presses the CANCEL buttons of each of the child forms, 'youngest' first, invoking
their CANCELALL callbacks, and all the forms in the Form Family are hidden.
The action of RESET and APPLY buttons does not affect family members.
Note: Earlier AppWare used form definitions in macro files which had to be loaded explicitly
via $m path-name. This mechanism still operates for backwards compatibility, but is
strongly discouraged when writing new AppWare.
show !!formname
used in the gadget’s callback. In either case the form becomes a child of the menu’s or
gadget’s owning form.
A form may be displayed free-standing, i.e. not as a child, by:
When a form is displayed as a child form then it is always positioned with respect to its
parent.
For a form shown from a MENU, its origin is at the origin of the parent. If the form is
displayed from a BUTTON or any other gadget, its origin is at the centre of the gadget.
When a form is shown as a free form for the first time then its default position is at the top
left-hand corner of the screen.
We strongly recommend that you allow the system to position forms whenever
possible.
You can force the screen position of free-standing forms using the following commands or
methods:
show !!MyForm Free At xr 0.3 yr 0.5
show !!MyForm Free Centred xr 0.5 yr 0.5
!!MyForm.show( 'At', 0.3, 0.5 )
!!MyForm.show( 'Cen', 0.5, 0.5 )
The At option puts the origin of the form at the specified position; alternatively the Cen
option puts the centre of the form at the given position. The co-ordinate values are fractions
of the screen width or height respectively and are referred to as screen co-ordinates.
For example:
show !!MyForm free At xr 0.25 yr 0.1
positions the origin of !!MyForm one quarter of the way across from the left edge of
the screen, and one tenth of the way down from the top.
!!MyForm.show( 'Cen', 0.5, 0.5 )
centres !!MyForm at the middle of the screen.
16 Menus
Menus are always members of forms but can be employed in various ways by the form and
its gadgets.
Menus come in two types: main menus and popup menus. You determine what type a menu
is when you create it. If you do not specify the type, then the system will try to infer its type
from the context of its first use.
For example, if the first action in a form definition is to define the menubar, then the system
will infer any menus referenced in the bar…exit sequence to be of type MAIN.
Or, as a second example, if a menu is assigned as the popup for the form or a gadget of the
form, then the system will infer the menu to be of type POPUP.
• The Choose option when picked will open the menu Menu1 as a pull-down (assuming
you have defined Menu1).
Note: That Menu1 need not exist when the bar is defined, it can be defined later in the form
definition, but it must exist before the Choose option is selected or an error alert will
be raised.
• The Window and Help options will open the Window and Help system menus
(described later in this chapter).
Within the form definition a menu object can be created using the form’s NewMenu method
or the menu sub-command. You can then use the menu’s Add(), InsertAfter(), and
InsertBefore() methods to add or insert named menu fields. A menu field can do one of
three things:
• Execute a callback.
• Display a form.
• Display a sub-menu.
You can also add a visual separator between fields.
Below is an example of a complete menu definition:
!menu = !this.newmenu( 'file', ‘main’ )
!menu.add( 'MENU', 'Send to', 'SendList', 'SendTo' )
!menu.add( 'SEPARATOR', 'saveGroup' )
!menu.add( 'CALLBACK', 'Save', '!this.SaveFile()', 'Save' )
!menu.add( 'FORM', 'Save asº', 'SaveFile', 'SaveAs' )
!menu.add( 'SEPARATOR' )
--core-code managed field for Explorer Addin, ticked.
--Note no Rtext needed
!menu.add( 'CORETOGGLE', 'Explorer', '', 'Expl' )
!menu.add( 'MENU', 'Pull-right1', 'Pull1')
--initialise toggle field as ticked (typically in the
constructor)
!menu.SetField( 'Expl', 'Selected', true )
This creates a new main menu menu with six fields and two separators between them. For
example:
• The SAVE field when picked will execute the callback command this.SaveFile().
• The Save as... field when picked will load and display the form !!SaveFile. By
convention, the text on a menu field leading to a form ends with three dots, which you
must include with the text displayed for the field.
• The SEPARATOR, usually a line, will appear after the previous field.
• The Pull-right1 field when picked will display the sub-menu !this.Pull1 to its right. A
menu field leading to a sub-menu ends with a > symbol: this is added automatically.
Field Description
When selected, this Help option displays a system-help pull-down menu that gives access
to the application help system. The fields are:
Field Description
Contents This displays the Help window so that you can find
the required topic from the hierarchical contents list.
Index This displays the Help window with the Index tab
selected, so that you can browse for the topic you
want to read about from the alphabetically-arranged
list. You can locate topics quickly by typing in the
first few letters of their title.
Search This displays the Help window with the Search tab
at the front so that you can find all topics containing
the keywords you specify.
You can access On Window help by pressing the F1 key while the form has keyboard
focus, or by including a Help button in the form’s definition.
Note: By convention, the help menu should be the last one defined for the menu bar, which
will ensure that it appears at the right-hand end of the menu bar.
endmethod
Note: How we use the PickedFieldName member of the menu object to obtain the last
picked field.
If you pick this menu field the callback method will print:
menu !!MyForm.Menu1 SELECT field: OnOff
Field Description
<FieldName> is the unique name for the menu within the bar.
For example:
setup form !!MyForm Dialog size 30 5
bar
-- adds a pulldown for menu1 labelled with <dtext>
!this.bar.Add( ‘<dtext>’, ‘menu1’ )
-- adds a window pulldown labelled with <dtext>
!this.bar.Add( ‘<dtext>’, ‘Window’ )
-- adds a help pulldown labelled with <dtext>
!bar.InsertAfter( ‘Window’, ‘<dtext>’, ‘Help’ )
...
exit
If you use the identifier ‘Window’ or ‘Help’ as the name of the menu, the system will
interpret them as system Window and Help menus, although they will still be displayed with
the string given in <dtext>.
Named menus and the methods that create them are discussed in more detail in the rest of
this section.
Field Description
Field Description
Note: The property names may optionally be truncated to the first three characters ‘ACT’,
‘VIS’, and ‘SEL’.
For example:
!bar = !!MyForm.bar
!menu = !!MyForm.Menu1
sets local variables !bar and !menu to be references to the bar gadget and menu
Menu1 of form !!MyForm.
Then
!bar.SetFieldProperty( 'Foo', ‘ACTive’, false)
will grey-out the menufield on bar that has the field-name “Foo”. And
!menu.SetFieldProperty ( 'Bar', ‘ACTive’, true)
will activate the menufield on Menu1 that has the field-name “Bar”.
You can use the same method to change the selected status of a toggle menu field.
Field Description
<PropertyName> is the name of the property you want to change in the named
field. The allowed values are:
‘ACTIVE’ - greyed in or out
VISIBLE’ - visible or invisible
‘SELECTED’ - selected or unselected (toggle type fields, only.
Specifically, this value cannot be used with bars)
Note: The property names may optionally be truncated to the first three characters ‘ACT’,
‘VIS’, and ‘SEL’.
For example:
!bar = !!MyForm.bar
sets local variable !bar to be a reference to the bar gadget of form !!MyForm.
Then
!isSet = !bar.FieldProperty( 'Foo', ‘ACT’)
will get the greyed-out status of the menufield on bar that has the field-name “Foo”.
You can use the same method to change the selected status of a toggle menu field.
will make the all the fields invisible for the group currently implied by the separator field
‘SaveGroup’, i.e. the fields SaveGroup, Save and SaveAs.
The combination of named SEPARATOR fields, insertion and field group visibility will be
useful for managing the sharing of menus between co-operating sub-applications. This
facility should be used with great care.
17 Form Layout
Typically gadgets are laid out onto the form from left to right and from top to bottom. It is
unnecessary and undesirable to specify absolute positions for gadgets; we recommend
defining the layout of each gadget relative to a predecessor. This allows the layout to be
edited simply, without having to calculate new positions.
At any time the form has maximum and minimum extremities XMIN, YMIN, XMAX, YMAX.
As new gadgets are added to it the form XMAX and YMAX extents grow to include the
gadget boxes.
Note: The grid width is the notional character width because fonts usually have
proportional spacing so different characters in the font typically have different widths.
Thus n grid units does not equate to n characters. Generally, you can get more than
n characters in n grid units unless the string has a high proportion of wide characters
e.g. capitals, numerals, w’s, m’s. It is important that you understand this concept
when you specify the width and height of gadgets.
The extremities of a gadget box on the form can be referenced by the gadget pseudo-
variables:
In their definition gadgets may be positioned at specific form coordinates form, using the AT
keyword followed by the X and Y grid coordinates.
Auto-placement uses the PATH, DISTANCE and ALIGNMENT keywords. These keywords
have the following meaning:
Keyword Description
ALIGNMENT specifies how the next gadget aligns to the previous one for
the current path.
Relative placement means specifying the new gadget position by explicit reference to the
extremities of a previous gadget.
For special cases it is also possible to position gadgets using explicit form coordinates. See
Absolute Gadget Positioning.
17.3 Auto-placement
The current path direction is set until you give a different PATH command.
.GADG1 .GADG2
HDISTANCE
VDISTANCE
.GADG3
PATH last D
next next next
last last
D D
PATH
next last next last next last
D D D
D D D
D D D
Combinations of path, distance and alignment commands allow you to set up correctly
aligned rows and columns of gadgets without the need to calculate any grid coordinates.
For example, the command sequence:
PATH right
HDIST 3.0
VALIGN bottom
list .Lis4 width 2 height 3 $* auto-placed
PATH up
HALIGN right
paragraph .Par5 width 3 height 3 $* auto-placed
.But1
.Par5
0 1 2 3 4 5 6 7 8 9 X
0
1
2
previously-created gadget used as
3
4 YMAX
5 YMAX+1
6
Y
XMIN new toggle gadget
0 1 2 3 4 5 6 7 8 9 X
0
1
2
existing gadget used as reference
3
4 YMAX
5 YMAX+1
6
Y
XMIN-2 XMIN new toggle gadget
Figure 17:6. Newly Placed Gadget
Note: The new gadget need not be adjacent to the referenced gadget(s). The same rules
apply even if there are other gadgets positioned between them. You will probably find
form design easier, however, if you add gadgets in two directions only (right and
down, say), rather than in a more random order
Y-direction the gadget’s origin is placed at the form’s current maximum depth and extends it
to include the gadget’s height. The result is:
0 1 2 3 4 5 6 7 8 9
0
1 Original YMAX FORM
2
3
4 BUTTON .OK gadget
5
6
7 New YMAX FORM
Figure 17:7. Positioning Relative to Form’s Extremities
0 1 2 3 4 5 6 7 8 9 X
0
1
2
3
4
5
6
7
Y
Figure 17:8. Positioning at a Known Location
Note: You can position gadgets anywhere on the grid, not only at the grid intersection
points.
17.7 AT Syntax
The AT syntax is used to define the position of a gadget’s origin within a form. The position
may be specified absolutely (in form layout grid units) or relative to the extremities of
existing gadgets, or relative to the size of the form and the gadget.
The rest of the Forms and Menus syntax is described in Software Customisation
Reference Manual: refer to that manual for more information, in particular about
conventions used in the syntax graphs. The AT syntax graph is the easiest way of
illustrating all the possible options, and so it is included here, as well as in the Software
Customisation Reference Manual.
where <FGREL>, shown below, sets the gadget position relative to another gadget or the
form’s extent. For example, it can be used to position a gadget half-way across the width of
a form:
>----+- <gname> -.
| |
+-- FORM ---|
| |
‘-----------+- * val --.
| |
+- + val --|
| |
+- - val --|
| |
+- val ----+--- + val * SIZE --.
| | |
| +-- - val * SIZE ---|
| | |
| +- - SIZE ---------|
| | |
| ‘-------------------|
| |
+-- SIZE ----------------------|
| |
‘------------------------------‘--->
Figure 17:10. Syntax Graph -: Placing a Gadget Half-Way across the Form
Below are some examples of how you can use the syntax:
Syntax Effect
AT XMAX FORM-SIZE YMAX XMAX FORM refers to the current right hand size
FORM-SIZE of the form at its current stage of definition (not its
final maximum extent). YMAX FORM refers to the
form’s current bottom extent. The -SIZE option
subtracts the size of the gadget being positioned
in the form. This example positions the gadget at
the extreme right-hand bottom edge of the form.
18 Frames
Frames are very special and versatile gadgets that are crucial for form definition, layout, and
run-time behaviour. This section gives an overview of frame gadgets. Their definition and
details are discussed in Gadgets and their Attributes.
Normal frames can be nested and as an inner frame expands, an outer frame will also
expand to accommodate it.
The FRAME has a Radio Group property which operates on the set of RTOGGLE gadgets
(radio buttons) owned directly by it.
To create a multi-paged tabbed form, you can define a dialog or document form with a
single TABSET frame that contains a set of tabbed page frames, with each page containing
the gadgets for the desired page of the form.
The form will automatically resize to take account of the largest tabbed page frame. There is
an example showing the definition of tabbed frames in Complex Form Layout
You cannot nest TABSET frames.
As a user, you will already be familiar with forms and their gadgets. The types of gadgets
that you can define on the body of the form are summarised below.
Refer to Figure 20:1.: Examples of different types of gadgets. for examples of the following:
Gadget Purpose
FRAME A container that groups other gadgets visually and logically. It also
acts as a radio group for all directly included RTOGGLE gadgets.
BUTTON Act as visual buttons and are pushed to achieve some action.
The buttons like OK and Apply, which appear on many forms, are
examples of a special type of button, which use form control
attributes to control the display of the form and whether it is
actioned.
OPTION Has a pull-down list of options associated with it, from which the
user can choose one only.
LIST Displays one or more lines of data from which the user can select
one or several. The list can be scrollable if there are too many
options to be displayed at once.
CONTAINER Allows the hosting of an external control inside a PML defined form.
DATABASE Used to display a list of database elements, from which the user can
SELECTOR choose.
Gadget Purpose
TEXTPANE An area where the user can type and edit multiple lines of text, or
cut and paste text from elsewhere on the screen.
For example:
Specific properties: the order of these commands generally matters. See the syntax
graphs in the Software Customisation Reference Manual for definitive information about
order.
Most gadget properties can be changed using the members and methods supported by
each gadget object.
Note: You cannot change a gadget’s geometry except for a VIEW gadget on a resizable
form.
The following methods are shared by all gadgets regardless of gadget type:
19.3.2 User Specifiable Tagwidth for TEXT, TOGGLE and OPTION Gadgets
TEXT, TOGGLE and OPTION gadgets support the Tagwidth syntax.
For example, the OPTION definition below:
option .ELLIST tagwid 7 |Element Id| width 4
The Tagwidth specifies the size of the gadget’s tag field in grid width units including any
padding space, regardless of the actual tag string.
The actual tag may have more characters than the declared Tagwidth and still fit in the tag
field (typically a mostly-lower-case string of 10 characters occupies only about 60-70% of
the 10 X notional character spaces). Similarly the actual strings may have fewer characters
than the declared Tagwidth and the extra space will be blank padded.
If the tag width is not explicitly given then it is assumed to be the number of characters in the
‘tagtext’ string multiplied by the horizontal grid size (the notional character width for the font).
Note: You can specify the tag width without specifying any tagtext at definition time; you
can add this at run time.
The Tagwidth is not needed for gadgets with an explicit area specification (width and
height, lines or length). FRAME, LIST, SELECTOR, TEXTPANE and PARAGRAPH can
always force an explicit width (excluding any padding).
For example, the following PARAGRAPH gadget definition displays the given string, which
has 14 characters without lots of unused space:
paragraph .oneline text ‘a single line!’ width 9
Gadgets with an optional TEXT, OPTION WIDTH and HEIGHT define the
in-line tag. data display space in grid units.
TOGGLE RTOGGLE
These settings exclude the tag
and any gadget glyph or
decoration.
Gadgets with no tag or Gadgets that never WIDTH and HEIGHT define the
with an integral (optional) display a tag: FRAME, (possibly implicit) enclosing box
tag. SLIDER, G2D, G3D, in grid units.
GM3D, PARA.
The relative gadget extent settings define the data display part of the gadget being defined
in terms of the data display part of the specified or implied gadget.
For the three categories defined above, WIDTH.GAD and HEIGHT.GAD have the following
meanings:
Gadgets with an optional TEXT, OPTION WIDTH and HEIGHT define the
in-line tag. (implicit) enclosing box
TOGGLE RTOGGLE
excluding any tag.
Boxed gadgets with LIST, SELECTOR, WIDTH and HEIGHT define the
optional tag above. TEXTPANE enclosing box excluding any
tag.
Gadgets with no tag or Gadgets that never WIDTH and HEIGHT define the
with an integral (optional) display a tag: FRAME, (possibly implicit) enclosing box
taG. SLIDER, G2D, G3D, including any tag.
GM3D, PARA.
!pixmap1 corresponds to the Un-selected state of the gadget, and pixmap2 corresponds to
the Selected state. Specifying !pixmap1 as the null string ' ', will leave the current Selecetd
pixmap unchanged.
PARAGRAPH gadgets only have one pixmap which is represented by the .VAL member,
and so can be directly set or changed using !this.myPara.val = '<pixmap-pathname>.
Notes:
1. The PML function !!PML.GetPathname( '<myPixmap>.png') returns the required
pixmap pathname for pixmaps held in the standars PDMS Appware pixmap directories.
2. It is recommended that when you define the gadget you set its size to encompass the
largest pixmap which you will later add. Failure to do this may give rise to unexpected
behaviour.
3. Historically you could add a third pixmap which was used when the gadget was de-
activated. This practice is no longer necessary as the gadget pixmapped is
automatically greyed-out on de-activation.
For example:
!!MyForm.Textfield.Setfocus()
20 Gadget Set
This chapter describes all the gadgets supported by PML. It covers their definition and use.
Note: In some of the examples the relative gadget positioning commands are indicated by
the AT keyword followed by three dots . . . See Relative Placement for details.
20.1 Examples
This section contains two examples, which illustrate much of the functionality described in
this manual.
This file layout1.pmlfrm, shown below, defines the form shown in Figure 20:1.:
Examples of different types of gadgets. Note that all the gadgets on the form are dummies:
there are no callbacks. The PATH commands control the layout: they are described in
Positioning Gadgets on a Defined Path.
-- PDMS Customisation User Guide
-- Form layout1 - Demonstrate simple form layout
setup form !!layout1 DIALOG
title 'Form !!Layout1'
-- defaults: path right, hdist 1.0, vdist 0.2
paragraph .Message text 'This form illustrates simple form
layout'
path down
frame .frame1 'Frame 1'
paragraph .Message1 text 'This frame contains toggle buttons'
toggle .Bold 'Bold '
path RIGHT
toggle .Italic 'Italic '
toggle .Underline 'Underline'
exit
PATH DOWN
frame .frame2 'Frame 2' at XMIN FORM
paragraph .Message2 text 'This frame contains a horizontal
group of radio buttons' width 20 lines 2
.Horizontal 'Alignment' HORIZONTAL
add tag 'Left ' select 'LEFT'
add tag 'Centre ' select 'CENTRE'
add tag 'Right ' select 'RIGHT'
exit
frame .frame3 'Frame 3' at XMIN FORM
paragraph .Message3 width 20 lines 2
.Vertical 'Alignment'
add tag 'Top ' select 'TOP'
add tag 'Middle ' select 'MIDDLE'
add tag 'Bottom ' select 'BOTTOM'
exit
button .CANCEL CANCEL
button .OK at XMAX FORM-SIZE YMAX FORM-SIZE OK
exit
define method .layout1()
-- Constructor
!this.Message3.val = 'This frame contains a vertical group of
radio buttons'
endmethod
The file layout2.pmlfrm, shown below, defines the form shown in Figure 20:3.: The
tabbed pages of a complex form. Within the form definition the TABSET frame is defined
and directly contains a frame gadget for each tabbed page. Note its ANCHOR ALL setting
which maintains the distance between each edge of the frame and the corresponding edge
of the form, when the form is resized by the user. This allows the frame to grow and shrink
without overwriting gadgets outside of it.
Each tabbed page frame contains the definition of all the gadgets belonging to the page.
Note the use of the DOCK FILL setting which allows each edge of the tabbed page frame to
stick to the corresponding edge of the TABSET frame so that they grow and shrink in
unison.
Alternatively, when you define a TABSET FRAME, you can specify its DOCK or ANCHOR
attributes to determine its resize behaviour within your form.
For each tabbed-page frame within the TABSET, it is no longer necessary to specify any
DOCK or ANCHOR attribute settings, and any that are specified will be ignored. Each
tabbed-page frame will always fill the available space in its TABSET parent (it exhibits
DOCK FILL behaviour).
The gadget ANCHOR attribute is used extensively to allow resizable gadgets to expand in
specific directions and not others for. It is also used by non-resizable gadgets, e.g.
BUTTONs, to allow them to move with edges of their containers and so avoid being overlaid
by resizing gadgets.
Note also, the extensive use of form methods to provide the intelligence of the form as
gadget callbacks. In particular the method listCallback(!list is GADGET, !event
is STRING), which just reports details of select and unselect evetns on list fields, has the
standard form of an open callback, and is used as such on the list gadgets LI1 and LI2, i.e.
!this.Li1.callback = |!this.listCallback(|. Open callbacks are described in
PML Open Callbacks.
-- PDMS Customisation User Guide
-- Form layout2 - Demonstrate complex form layout
setup form !!layout2 dialog docking
title 'Form !!layout2'
paragraph .Message width 40 lines 3
path DOWN
frame .Tabset TABSET 'tabset' anchor All
frame .page1 |Page 1| dock Fill
frame .frame4 'Frame 4'
paragraph .Message4 text 'This is an option
gadget' width 16
option .Colour 'Colour'width 5 tooltip'select
colour for paragraph'
exit
frame .frame6 'Frame 6'
halign right
paragraph .Message6 text 'These are right aligned
text gadgets' width 16 lines 2
text .Width 'Width ' width 5 is REAL
text .Height 'Height' width 5 is REAL
text .Area 'Area ' width 5 is REAL
halign left
button .b3 |area| tooltip'calculate the area'
exit
frame .frame5 'Frame 5' at Xmax.frame4+2 Ymin.frame4
anchor All
paragraph .Message5 text 'This is a multi-choice
list gadget' wid 20
list .Li1 'Select some of these' anchor all
MULTIPLE width 20 height 10
do !i from 1 to !dtext.size()
!rtext[!i] = 'row $!i'
enddo
!this.li2.rtext = !rtext
-- set callbacks
!this.b2.callback =
|!this.printListSelection(!this.li2)|
!this.li2.callback = |!this.listCallback(|
endmethod
define method .listCallback(!list is GADGET, !event is
STRING)
-- open callback to report on list events
-- can be used for any list gadget
!n = !list.pickedField
!sel = !list.dtext[!n]
!name = !list.fullname()
$P $!event callback on field $!n<$!sel> for list $!name
endmethod
define method .calcArea()
-- calculate the area
!area = !this.width.val * !this.height.val
!this.Area.val = !area
endmethod
define method .printListSelection(!list is GADGET)
-- report single-selection list gadget selection
-- can be used for any single-choice list
!sel = !list.selection('Dtext')
!num = !list.val
!name = !list.fullname()
$P -----------------------------------------------
$P selected field for list $!name
$P Field $!num: <$!sel>
$P -----------------------------------------------
endmethod
define method .printListSelections(!list is GADGET)
• Frame Size
During form definition, once a frame is positioned on the form the origin remains fixed but
the frame automatically expands downwards and to the right to contain any gadgets added
to it. You cannot add gadgets to it at negative coordinates relative to the frame’s origin. You
can optionally declare a minimum size for the frame. For example:
Frame frame1 ‘frame 1’ SIZE 10 20
This is relevant only for NORMAL and TABSET frames; for TOOLBAR frames, the gadgets
appear as a single row in the order defined i.e. gadget positions are ignored.
The radio group action only applies to FRAME gadgets of type NORMAL, PANEL,
FOLDUPPANEL.
You can add RTOGGLE to a FRAME with the usual positioning and layout commands.
The FRAME has a value member, VAL, which is the index of currently selected RTOGGLE
for the radio group. You can use this to change the selected RTOGGLE.
Similarly, you change the value of the FRAME by setting the VAL member of any of the
group’s RTOGGLEs to true.
Note that the FRAME group value may be set to zero, indicating that there is no selected
RTOGGLE. Similarly if the selected RTOGGLE value is set to false, then it becomes
deselected and the FRAME value will then be zero.
The default value for an RTOGGLE gadget is FALSE, and the default value for a FRAME
gadget is zero, i.e. no selected RTOGGLE.
• Frame Callbacks
The FRAME gadget can have an assigned callback, which is executed when the radio
group selection is changed, i.e. whenever the user selects an unselected radio-toggle. As
there is only a SELECT action supported, it can be either a simple callback or an open
callback.
The form definition below is a simple TV and radio station selector, shown above.
setup form !!FRGTest dialog noAlign
title |Select a program|
Frame .rg1 |TV and Radio|
path down
text .choice tagwid 6 |Selection:| width 12 is STRING
rToggle .rad1 tagwid 7 |BBC 1| States '' 'BBC 1'
path right
valign centre
rToggle .rad2 tagwid 7 |BBC 2| States '' 'BBC 2'
rToggle .rad3 tagwid 7 |Anglia| States '' 'Anglia'
rToggle .rad4 tagwid 7 |Chan 4| at xmin.rad1 ymax.rad1
States '' 'Chan 4'
rToggle .rad5 tagwid 7 |Radio:| States '' 'radio'
option .Radio width 10
exit
button .cancel |Cancel| at xmin form ymax form + 0.2 CANCEL
button .ok | OK | at xmax form - size OK
- set focus to button to ensure to ensure Windows does not
set it to first Rtoggle
!this.keyboardFocus = !this.ok
exit
Note: The form’s keyboard focus is initially placed on the OK button to prevent it being
assigned (by Windows) to the first RTOGGLE rad1 (effectively the first interactive
gadget on the form as the text field Selection is read-only)
The form constructor method assigns a simple callback, the form method
RGroupSelectionChanged(), to the frame rg1 (radio group). It then initialises the
gadget values and selects the second RTOGGLE as the default selection.
define method .FRGTest()
-- Constructor
-- Frame radiogroup with simple callback
!this.rg1.callback = '!this.RGroupSelectionChanged( )'
The default state is 'unfolded' and the EXPANDED property allows the user to initialise a
panel to unfolded (true) or folded (false).
When the panel expands or collapses, any gadgets which lie below the panel and between
(or partially between) the panel's horizontal limits will be moved down or up the form.
If the form's AutoScroll attribute is selected, then a scroll bar will automatically appear
whenever gadgets have been moved off the bottom of the form, so that the entire form is
always accessible.
• OnPopup - raised when the right mouse button is pressed while the cursor is over the
background of the control. Callbacks edit and show the PML popup menu.
• OnColour – raised when the user selects a colour from the standard Winforms colour
picker (as pictured), which is shown when the colour button is pressed. Callback prints
the RGB components of the selected colour.
The PML code to create and manage the form is given below. Purple is used to highlight the
PMLNet commands, the rest is just standard PML2. It includes rudimentary callback
methods to service the PMLNet controls events.
In your Appware you need an import command (to load the dll containing your
PMLNetControls)
import 'PMLNetTest'
This is usually done once only, probably in application start-up macro. You can have it in
your form, but you will then need to handle the error which will occur if the import is
executed more than once:
import 'PMLNetTest'
handle (1000,0)
-- PMLNetTest dll already loaded
endhandle
-- MyNetForm.pmlfrm: Test form hosting a PMLNet Control
setup form !!MyNetForm size 25 20 dialog dock right
using namespace 'Aveva.PDMS.PMLNetTest'
member .MyCtrl is MyNetControl
!this.FormTitle = 'My PMLNet Form'
path down
-- define PML Container to host .Net control
container .NET indent PMLNETControl dock fill
-- define PML Frame
frame .PMLFrame 'PML' dock top
paragraph .PMLMessage text 'This frame contains a PML toggle button' wid 25
toggle .PMLToggle 'PML '
exit
-- define PML popup menu
menu .PMLPopup popup
!this.PMLPopup.add( 'CALLBACK', 'Attributes', '!this.attributesMenu()' )
!this.PMLPopup.add( 'CALLBACK', 'More', '!this.moreMenu()' )
!this.PMLPopup.add( 'CALLBACK', 'Last', '!this.lastMenu()')
exit
----------------------
-- MYNETFORM Constructor
----------------------
define method .MyNetForm()
using namespace 'Aveva.PDMS.PMLNetTest'
-- create instance of .Net control
!this.MyCtrl = object MyNetControl()
• Textual Buttons
Toggle buttons will highlight when pressed. For example on toolbars they will turn from blue
to orange when pressed, and go back to blue again when un-pressed.
The syntax to select the new mode is toggle, which can be anywhere after the button
name and before the button control type or associated form, e.g.
Button .B1 TOGGLE pixmap /Unselected.png /Selected.png /
Inactive.png width 16 height 16 tooltip…
The button’s value-member !button.val is a BOOLEAN and reflects the button’s state,
having the value TRUE when the button is pressed and FALSE when it is not.
CANCEL Allows the user to decides not to proceed with the form.
The form nest’s CANCELCALL callbacks are run and the
nest is hidden. All gadget values are reset to their initial
settings or to the values established at the last APPLY.
The effect of OK and CANCEL on gadgets is more extensive if a form family is involved, as
described in Free Forms and Form Families.
Examples:
button .Ok AT . . . 'OK' CALLBACK '!!MyOkFunction()' OK
button .Apply 'Apply' CALLBACK '!!MyApplyFunction()' APPLY
button .Cancel 'Cancel' CALLBACK '!!MyCancelFunction()' CANCEL
button .reset AT . . . 'Reset' RESET
button .help AT . . . 'Help' HELP
The value of a toggle gadget is set and used via the .val member which is a BOOLEAN
value:
!!MyForm. Italic.val = TRUE
if ( !!MyForm.GridOn.val ) then
...
else
...
endif
The default value for a toggle is FALSE.
• RToggle Callbacks
The RTOGGLE gadget can have an assigned callback, which is executed whenever its
selected status is changed. When the group selection is changed, by the user clicking an
unselected radio button, the current button (if any) is unselected and the new button is
selected. An open callback (PML function or form method) is necessary as the events
UNSELECT and SELECT need to be reported.
The PML code below shows a modification to our example form, which makes use of open
callbacks on the RTOGGLEs instead of a simple callback on the FRAME radio group. The
Constructor and the RgroupSelectionChanged methods are modified accordingly.
Note: The behaviour of the two versions is identical. Both mechanisms are equally valid,
and are provided to minimise the work required in replacing the RGROUP and
(deprecated) RADIO gadgets.
this.rad5.callback = '!this.RGroupSelectionChanged('
-- Radio choices
!this.rad5.setTooltip(|select your Radio option|)
!radio[1] = 'Q103'
!radio[2] = 'Hereward'
!radio[3] = 'Cambridge'
!radio[4] = 'ClassicFM'
!radio[5] = 'Caroline'
!this.Radio.dtext = !radio
!this.Radio.setTooltip(|change your Radio option|)
!this.Radio.callback = '!this.selectProgram(!this.rad5)'
-- set initial value
!this.rad2.val = true
!this.RGroupSelectionChanged( !this.rad2,'SELECT' )
endmethod
define method .RGroupSelectionChanged( !rtog is GADGET,
!event is STRING )
-- Service specified radio-toggle events
if( !event eq 'UNSELECT' ) then
-- Do some application actions
!this.choice.clear()
elseif( !event eq 'SELECT' ) then
!value = !rtog.onValue
-- Do some application actions
if( !value eq 'radio' ) then
!value = !this.Radio.selection('dtext')
endif
!this.choice.val = !value
endif
endmethod
!ColourArray[3]='Red'
!ColourArray[4]='Green'
!ColourArray[5]='Blue'
!This.Layout2.Colour.Dtext=!ColourArray
Other examples:
option .Colour 'Colour:' AT . . . callback '!!MyFunc()'
width 20
range is array of REAL - a real array with members Start, End and step (>0).
ndp is REAL - (read only) the number of decimal places. If zero then all
values will be integer.
The NumericInput gadget supports SELECT and MODIFIED events, and users may provide
a callback method to service these events. Note that often no callback is required, and the
numeric input value is merely read and used by other gadgets of the form.
A SELECT event is raised whenever the user clicks ENTER while the numeric input display
field has focus. Typically this happens after the user has typed in a required value, but will
also apply if the user enters the field after modifying the values using the up/down arrows.
The callback can be a simple or an Open callback.
A MODIFIED event is raised for each modification of the displayed value using the up/down
arrows. Modified events are only reported if they are enabled and the user has provided an
Open callback, as this allows differentiation from the SELECT events. The default state is
modified events disabled.
Typically you enter text values into the list using an array of strings and assigning to its
Dtext member.
list .List 'Select some of these' MULTIPLE width 15 height
8
!Elements[1]= 'Element 1'
!Elements[2]= 'Element 2'
!Elements[3]= 'Element 3'
!Elements[4]= 'Element 4'
!Elements[5]= 'Element 5'
!Elements[6]= 'Element 6'
!This.List.Dtext= !Elements
More examples:
list .Components SINGLE width 10 height 15
list .Components 'Colour:' AT . . . MULTIPLE width 10
height 15
list .Elements 'Elements to be deleted' callback
'!this.Delete' MULTIPLE width 10 length 15
As with the option gadget, the list gadget actually contains two parallel lists, of the same
length, one containing display values (Dtext) and the other containing replacement values
(Rtext).
The Dtext values must be supplied, but the replacement values are optional.
If you don’t supply the Rtext values they will default to the Dtext values. The Rtext values
are often used to assign callback strings to each field of the list.
Resetting a list gadget's display-texts automatically deletes the existing display and
replacement-texts and clears any current selections. For example, the contents of gadget
List of Figure 20:3.: The tabbed pages of a complex form could be replaced by:
!Choices[ 1 ] = 'Tea'
!Choices[ 2 ] = 'Coffee'
!Choices[ 3 ] = 'Chocolate'
!This.List.Dtext = !Choices
You can replace the list’s Rtext with a new array of the same length without disturbing the
current Dtexts:
!newRtext[1] = ‘drink6’
!newRtext[2] = ‘drink4’
!newRtext[3] = ‘drink12’
!This.List.Rtext = !newRtext
You can use the new Add methods to add a single new entry to be appended to LIST and
SELECTOR gadgets:
Notes:
1. UNSELECT events are not notified to PML unless an open callback has been specified
(so that SELECT and UNSELECT events can be differentiated).
2. Typically the UNSELECT action allows Appware to manage consequences of
deselection for any dependent gadgets or forms.
3. It is recommend that you do not change the List's selection programmatically in an
UNSELECT event.
The number of columns is deduced from the List's data. If the user specifies a set of (1 or
more) column headings before the list is populated, then this will determine the number of
columns. If no headings are pre-specified then the number of columns is deduced from the
display text of the List's first row. This provides upwards compatibility for existing Appware
using single column lists.
A List gadget's headings can be replaced after the list has been populated. If the new
headings specify the same number of columns then the headings are replaced but the List's
data fields and selection remain unchanged. If the number of columns is different, then the
list is replaced by an empty list with the new headings. Invoking the Clear() method will clear
the list's data fields and rebuild the current headings.
The column headings, and hence the number of columns, for the list are assigned as an
array of STRING:
!This.List.SetHeadings( !headings )
The Dtexts for each column of each row of the list can be provided as a PML array, where
each element is an array of STRING. This can be row oriented or column oriented.
!This.List.SetRows( !rows )
!rows is an array of ‘row arrays’, and its size determines the number of rows in the list.
!This.List.SetColumns( !columns )
!columns is an array of ‘column arrays’, and its size must match the number of columns of
the list. The size of all the column arrays must be the same and determines the no of rows in
the list.
The default width of each column is determined by the longest header or Dtext value
supplied. Rtext values for each row are supplied as for single column lists.
Selection within the list applies to the entire row not individual cells, but rows of the list can
be selected on the basis of a column’s Dtext:
Select( !column is REAL, !dtext is STRING )
This selects the first list row whose column has the given Dtext. If the list is a multi-choice
list then repeated use of this method will add selected rows to the list.
Note: For a multi-column list gadget each row’s Dtext string is held as a ‘horizontal tab’
character separated string of column data, matching the column headings (as
opposed to a simple string for single column list). The standard list members val,
Dtext, Dtext[n] and methods Select( ‘Dtext’, …), Selection( ‘Dtext’ ) work on multi-
column lists with the following limitations:
Typically, the first MODIFIED event is used to allow AppWare to gain control and modify the
properties (e.g. ACTIVE status) of dependent gadgets, which possibly the user should not
have access to until the text field’s VALIDATE and SELECT events have been handled.
The code fragment below defines an RTOGGLE that allows a user specified TV program to
be typed into an associated TEXT gadget.
rToggle .rad6 tagwid 7 |TV:| States '' 'TV'
text .TV width 10 is STRING
!this.rad6.callback = '!this.RGroupSelectionChanged('
-- set open callback on text field and option list
!this.TV.callback = '!this.selectProgram('
!this.Radio.callback = '!this.selectProgram('
- handle first Modified event only
!this.TV.setModified( ‘handleModify’, 1 )
The extended (open) callback selectProgram(, shown below, intelligently handles the
TEXT gadget and OPTION list. The open callback RGroupSelectionChanged sets the
value of the ‘TV’ RTOGGLE from the TEXT gadget.
define method .selectProgram( !gad is GADGET, !event is
STRING )
-- Select new program from option list or text input
field
if( !gad.type( ) eq 'TEXT' ) then
-- Control active state of R-toggle according to
modified state of textfield
!rtog = !this.rad6
if( !event eq 'MODIFIED' ) then
-- deactivate R-toggle
!rtog.active = false
elseif( !event eq 'SELECT' ) then
-- reactivate R-toggle
!rtog.active = true
endif
else
-- select radio program from option list
!rtog = !this.rad5
endif
if( !this.rg1.val eq !rtog.index ) then
-- deselect current selection
!this.rg1.val = 0
!this.choice.clear()
endif
endmethod
define method .RGroupSelectionChanged( !rtog is GADGET,
!event is STRING )
-- Service specified radio-toggle events
if( !event eq 'UNSELECT' ) then
-- Do some application actions
!this.choice.clear()
elseif( !event eq 'SELECT' ) then
!value = !rtog.onValue
-- Do some application actions
if( !value eq 'radio' ) then
!value = !this.Radio.selection('dtext')
elseif( !value eq 'TV' ) then
!value = !this.TV.val
endif
!this.choice.val = !value
endif
endmethod
• Use the mouse Select button to copy and the mouse Adjust button to paste.
• Use shortcut keys, for example: Ctrl C to copy, Ctrl V to paste.
• Use the system Edit popup menu
Pasting into a field acts like typing in, and will set the text field’s Modified appearance.
Note that user defined Popup menus are not supported on Text gadgets because they clash
with the system-supplied Edit menu.
For example, you can create the FORMAT object for a REAL object, so that a distance is
displayed in feet-and-inches with fractions (rather than decimals) with a maximum
denominator of 4:
!!RealFMT = object FORMAT()
!!RealFMT.DIMENSION = 'L'
!!RealFMT.UNITS = 'FINCH'
!!RealFMT.FRACTION = TRUE
!!RealFMT.DENOMINATOR = 4
!!RealFMT.ZEROS = 'FALSE'
See the PDMS Software Customisation Reference Manual for more information on the
FORMAT object.
The TEXT gadget is created with type REAL and assigned a FORMAT object for converting
values to and from text:
text .Dist 'Distance:' width 10 is REAL format
!!RealFMT
When we assign a value in millimetres to the text gadget:
!!Form.Dist.val = 3505.2
The display-text of 11' 6 will be shown in the text gadget.
To switch from displaying feet-and-inches to displaying mm all that is necessary is to
change the setting of the units member of the format object RealFMT from FINCH to MM.
The associated format also determines what can be typed into the field. If the associated
format is FINCH or MM then you can enter distances in either inches or feet and inches.
For example:
MM 3505.2 3505.2
Note that in every case the text field value is actually held in millimetres so that the
command:
q var !!Form.Dist.val
prints
3505.2.
You can use the format object in your PML code when converting a value to a STRING:
!StringValue = !!Form.Dist.val.String(!!RealFMT)
Note: Some older style ‘untyped’ PML1 text fields use the (null) string ‘’ to represent unset
values for numeric fields. You should avoid using these old style fields.
You can force an unset value for any text field (of any type) using:
!Ths.MyTextclear()
You can also assign an unset value to a text field:
! = REAL() - defines an unset variable type REAL
! = STRING() - defines an unset ariable type STRING
!This.MyText = !x
You can check for an unset text gadget of any type using the BOOLEAN method unset():
If( !This.MyText.val.Unset()) then
-- value of text is unset
...
endif
The VALIDATECALL is used to apply any checks you want. If an error is encountered then
the callback raises the error and returns.
Note: The validation callback must not attempt to change the value of the text field. It
should just detect and raise an error.
When an error is raised, an error alert is displayed and the text field is left with keyboard
focus and error highlight. No further form or gadget callbacks are executed and the form is
not dismissed from the screen. The User can then modify the field and re-action it.
The VALIDATECALL is a standard callback, and so it can be a single command, a PML
function, method or macro, or an open PML method or function. For an open callback, for
example:
!textField.validateCall = ‘!this.textvalidate(‘
the corresponding method must be:
define method .1textvalidate( !textin is GADGET, !action
is STRING )
where the action string will be 'VALIDATE'.
-- constructor
!this.t1.validatecall = '!this.textvalidate('
!this.t2.validatecall = '!this.textvalidate('
!this.OKcall = '!this.printValues()'
Endmethod
define method .textvalidate( !textin is GADGET, !action is
STRING )
-- validate the given field contents
-- General form of Validation Callback
-- !action will contain 'VALIDATE'
onerror golabel /Errors
label /Errors
-- Include handle block for any unexpected PML errors
handle ANY
-- Example: can get errors from evaluation of the String
field
-- with an 'unset'value
return error 1 'Invalid input'
endhandle
-- Validation code -------------------------------------
-- Check for unset field first (to avoid possible
unexpected errors)
if( !textin.val.unset() ) then
-- validation callback Failed
return error 2 'Field is unset'
else
!field = !textin.name()
if(!field eq 'T1') then
-- TYPED field must check range
!x = !textin.val
if(!x lt 0.0 or !x gt 100.0) then
return error 1 'value must be in range [0.0 to 100.0]'
endif
elseif(!field eq 'T2') then
-- any string but FRED
!This.text.setfocus()
To enquire how many lines of text a textpane gadget currently contains, use the count
method:
!Nlines = !This.text.count
To set line 21 in a textpane gadget to 'Hello World':
!This.text.setline( 21, 'Hello World' )
To read the current contents of line 21 as a string variable:
!Line = !This.text.line(21)
To set the entire contents of the textpane gadget to an array of strings:
!Lines[1] = 'Hello World'
!Lines[2] = ' '
!Lines[3] = 'Goodbye World'
!This.text.val = !Lines
To read the entire contents of the textpane gadget as an array of strings:
!LinesArray = !This.text.val
To set the current cursor position within a textpane gadget to line 21, character 15 you can
use:
!This.text.setcurpos(21, 15)
or
!ArrayRowColumn[1] = 21
!ArrayRowColumn[2] = 15
!This.text.SetCurPos( !ArrayRowColumn )
To read current cursor position within a textpane gadget:
!ArrayRowColumn = !This.text.CurPos()
Note: Mouse-only copy and paste, and local popup-editing menus are not supported
You do not have to set any attributes and we recommend that you set the view’s attributes in
the form’s default constructor method rather than in the form setup definition.
You can assign popup menus to view gadgets using the gadget’s setpopup() method,
with the name of the popup menu as the argument to the method. For example:
!MyForm.MyView.setpopup( !MyForm.pop1 )
Note: The standard graphical view forms in AVEVA products are normally supplied with
fully customised popup menus, so you should not assign your own ones to these
views
To define the data channel via which the gadget derives its alphanumeric content, use the
channel command:
The bottom-most empty blank line is the command input line, where the user can type in,
and execute the next command by pressing the 'enter' key.
The Alpha view allows the following operations:
1. Double-click of any displayed line will copy that line to the command input line ready for
editing and execution.
2. You can use standard techniques to select text in the view and then use the right-
mouse menu to copy your selection.
3. You can use the right-mouse menu to paste a selection into the command line. Each
line of the pasted selection is executed in turn as if you had typed it in, except the last
line, which becomes the command input line ready for editing and execution.
4. You can use the right-mouse menu to paste a selection as a macro. The set of pasted
lines are captured to a temporary file which is then executed as PML macro. The
selected lines are not echoed into the view's display.
5. Pasting of multiple lines is aborted if an error alert is displayed.
6. You can drag and drop a PML file into the view, and it will then execute as a PML
macro.
7. The right mouse menu also allows you to clear the contents of the view and to change
the size of text in the view's display.
• View Borders
Graphical views have optional sliders or scrollbars which allow modification of the view’s
geometric transformation. They can be turned on or off using the .borders( !Boolean)
method. If the border is switched off, the actual display area expands to fill the available
space.
• View Colours
Graphical views have two colours associated with them, namely the background colour
and the highlight colour.
• Cursor Types
When the mouse cursor enters a graphical view, the View gadget determines what cursor
type should be displayed for the user, or the representation to be assumed during a
graphical interaction.
Some of he types of cursor available are:
POINTER
NOCURSOR
PICK .
PICKPLUS .
CROSSHAIR
Note: The initial cursor type for VOLUME views is a Pointer, and you cannot re-define this.
You can specify the initial setting for the cursor type in a 2D view when you define the view
gadget.
The default values for initial cursor types for PDMS 2D view types are:
PLOT CROSSHAIR
DRAFT CROSSHAIR
COMPARATOR POINTER
You may use the CURSortype attribute command to set an initial cursor type within the
gadget definition. For example:
Cursor POINTER
!this.diagram.background( ‘darkslate’ )
!this.diagram.add( ‘plot1-1’ )
...
endmethod
The area occupied by borders and scroll bars is within the area you specify for the gadget If
the border is switched off, the actual display area expands to fill the available space.
When borders are turned off, the PLOT view becomes fixed and you cannot zoom or pan
within it. This may be useful if you are using PLOT views as if they were paragraphs; for
example, as decoration or for information.
To define the contents of the view specify the PLOTFILE pathname with the .add()
method.
To remove a PLOT from the current PLOT view, use the .clear() method. This will leave
the view empty. If you want to replace the displayed PLOT with another one then just re-use
the .add() method with the new PLOT file.
To define the colour items are displayed in when picked with the left-hand mouse button,
use the .sethighlight() method with one of the standard DRAFT colour numbers or
colournames.
The contents of the view may be any valid 2D graphical element, such as a DRAFT SHEET,
VIEW, LIBRARY, etc. To define the contents of the view, use the put command thus:
Command Effect
If you put new content into an area view that already contains a drawing, the original content
will be replaced by the new, not added to it.
Note: There is currently no .put() method! So to replace the view contents you will need
to use the old PML1 syntax:
edit view !!MyForm.drawing AREA put /site/sheet2
The maximum limits for the drawing, such that only part of the specified content is drawn
(giving, in effect, a window on the overall content area), may be set using the limits
keyword as above or using the .limits() member in the form’s constructor method, as
follows:
!box[1] = 200
!box[2] = 100
!box[3] = 600
!box[4] = 500
!this.drawing.limits = !box
where the limits define two opposite corners of the required area in sheet co-ordinates.
For a full list of VIEW members and methods, see the Software Customisation Reference
Manual.
The view direction is controlled via the 3D view’s .direction member. This is a 3 element
array of REAL, representing a direction vector in model space ( DEAST, DNORTH, DUP ).
So to look east you would specify:
!dir[1] = 1.0
!dir[2] = 0.0
!dir[3] = 0.0
!this.model.direction = !dir
The 3D view gadget labels the view direction information in 'ENU' (East-West, North-South,
Up-Down) format within the view status line and scroll borders, by default. The
LABELSTYLE member allows the user to specify either 'ENU' or 'XYZ' labelling. In the 'XYZ'
scheme the form of the view direction display in the status line is
x<bearing>y<elevation>z, where the bearing angle is in the range [-180, 180] and the
elevation angle is in the range [-90, 90]. The corresponding scroll bars are labelled "-Y -X Y
X -Y" and "-Z 0 Z".
The centre of interest or through point of the 3D view is controlled via the .through
member. This is a 3 element array of REAL, representing a position in model space (EAST,
NORTH, UP).
!thru[1] = 1000.0
!thru[2] = 5600.5
!thru[3] = 500.0
!this.model.through = !thru
The .radius() method specifies the radius of the view in current units - an imaginary
sphere around the through point.
The .range() method specifies how far away from the Through point you want the Eye
point to be.
Modifier Effect
LOOK FROM E10 N50 U85 looks from specified point (defines
eye position)
Modifier Effect
LOOK TOWARDS E10 N50 U85 Looks towards the given direction.
• View Limits
Each volume view needs to know what region of the design model is to be displayed (the 3D
view limits). The DESIGN application has the notion of a current limits box. Unless you
specify otherwise, the limits set for a new view gadget will default to the DESIGN current
limits box (as set by, say, the LIMITS or AUTOLIMITS commands in DESIGN).
In addition, each volume view has an AUTO flag associated with it, which determines
whether or not that view will be updated automatically whenever the DESIGN current limits
box is changed.
To define the view limits and the way in which they are to be updated, use the old syntax:
edit view !!MyForm.Model VOLUME
followed by one of
Modifier Effect
LIMITS LOCAL /EQUI2 /EQUI4 Display limits initially set to enclose the
specified elements and not updated
automatically.
You can set the limits box for a view, subject to the current AUTO flag using the .limits()
method:
-- limits brick set as array of REAL [E1, E2, N1, N2, U1,
U2]
!brick[1] = 1000.0
...
!brick[6] = 1560.4
!this.model.limits( !brick )
In DESIGN you may change the current limits box at any time by using one of the command
formats
• Mousemode
3D views have a mousemode member which can be set to Zoom, Pan, Rotate, or Walk
21 Alert Objects
The ALERT object provides a simple way of giving a warning or getting a response from the
user, avoiding the necessity of creating a separate form. Where an alert needs a simple
acknowledgement by the user, this can be achieved by pressing the spacebar or the Enter
key on the keyboard.
Note: Alerts are always blocking: they prevent interaction with any other form until they
have been actioned.
Below are some examples of how you can define different kinds of alerts:
Code Effect
Code Effect
Note: This function has been added to provide the GUI equivalent of the command
Var !x read
which is only valid in tty mode.
To achieve flexible, user-friendly interfaces, the input alert should be used sparingly
because of its blocking nature.
The FMSYS object (Forms and Menus System object) is a mechanism for providing
methods which are not specific to the standard objects, such as forms, gadgets, menus etc.
The FMSYS methods perform a variety of system-oriented tasks.
Method Purpose
The following code snippet shows an example of use within a form method:
define method .runCurfn( !curfn is string )
-- run specified cursor function
if( !!fmsys.OKCurfnView('g2d', 'normal') ) then
-- valid view exists, so run cursor function
$!curfn@
else
-- no valid view
return error 1 |No suitable displayed 2D views for
cursor function '$!curfn$@'|
endif
endmethod
Notes:
1. OKCurfnView('g2d') is equivalent to OKCurfnView('g2d', 'ANY')
2. OKCurfnView('ANY ', 'NORMAL') will look for both 2D Draft and 3D Design views
23 PML Add-ins
Object Description
Object Description
23.1.2 Callbacks
Each application can now have different types of callback, which are run at different times:
switch calls Runs every time PDMS switches into the application.
Key Description
Name The unique identifier of the add-in, e.g. GEN, DES. This key
is compulsory.
The following keys are only used for applications, i.e. add-ins that have a menu option on
the Applications menu and can be switched to.
Key Description
Synonym Synonym created for the directory (only used if the directory
is specified)
Key Description
Gadgets Integer specifying the gadgets that appear on main form for
this application:
1 - no gadgets
2 - sheet/library gadgets
3 - sheet/library and layer gadgets
4 - sheet/library and note/vnote gadgets
To make it easier for user-defined add-ins to modify existing applications, it is possible for an
add-in to add a startup or switch call to an application. You can do this by adding the lines of
the following form to the add-in file.
startupModify: APPNAME: callback
switchModify: APPNAME: callback
where APPNAME is the name of the application to modify and callback is the callback to be
assigned to it.
If an application with name APPNAME is not defined, the callback is not run.
23.1.5 Initialisation
The add-in definitions are loaded from file during PDMS startup using the function
!!appReadAppDefns(!module is STRING), where !module is the 3-letter identifier for
the module.
This creates the global !!appCntrl object, which holds information about the add-ins.
After reading the definitions of all add-ins, !!appCntrl assigns all callbacks to the
applications.
For backward compatibility, if an add-in is an application, a directory is specified for the
application and the file module\directory\INIT exists, then this file is added as a
23.2 Menus
Each add-in is able to specify menus that can be shown, along with the application in which
they can be shown. Menus are created by running the .modifyMenus() method of the
add-in object when PDMS first starts.
Note: If a menu field with the same field name is already present, the field will not be
added. This is so two add-ins can define the same menu field, and it will appear
independently of whether one or both add-ins are present.
This method does not always add an item to the end of the menu. If a function tries to
add a menu field with the same field name as one that’s already present, the next
menu field created (unless it’s inserted explicitly before or after another field or it’s a
separator) will appear after the existing field. The reason for this is to preserve the
intended order of the fields.
!menu.insertBefore('<TargetFieldName>', 'SEPARATOR' {,
'<FieldName>'})
!menu.insertAfter('<TargetFieldName>', 'SEPARATOR' {,
'<FieldName>'}).
To register the APPMENU object with !!appMenuCntrl, specifying the applications in
which the menu field should be visible, use:
!appMenuCntrl.addMenu('APP', !menu)
It is possible to add menu fields from an APPMENU object to every application. The
application name 'ALL' has been reserved for this purpose.
Command Description
Command Description
23.3 Toolbars
Toolbars are defined by creating a frame of type toolbar on the main form, using the
following syntax.
frame .toolbarName toolbar 'Toolbar description'
[PML commands to define buttons, toggle gadgets, option
gadgets and text gadgets]
exit
It is not possible to add toolbars to a form once it has been created, so all toolbars must be
defined before any application is started. The method .modifyForm() of the add-in object
is run during building of the form so that each add-in can add its own toolbars.
where !toolbarName is the name of the toolbar and !visible is whether it is visible by
default.
The command
!!appTbarCntrl.addToolbar(!toolbarName is STRING)
is a shorthand way of registering the toolbar to be visible in all applications, making it
visible by default.
Any toolbars on the main form that are not registered are automatically detected and hidden
by default. They are registered to be active in all applications.
23.4 Forms
The APPFORMCNTRL object allows control of the behaviour of forms on application
switching and re-entering PDMS. A global instance !!appFormCntrl is created when PDMS
starts.
If a form is to be controlled, it must register itself with the object during the constructor
method.
If !visibleInApps is an empty array, then the form will be visible in all applications.
However, this can be simplified for most forms that do not need all this information stored
about them.
The command:
!!appFormCntrl.registerForm(!form is FORM)
registers the form so that it is re-shown on startup if it is shown when PDMS exits. It
does not depend on any other applications and its visibility is not otherwise controlled.
Note: The form must be able to cope if no data is returned. This will happen the first time
PDMS is run or if the form was not open the previous time the user exited PDMS
The following example shows a form that stores the state of some of its gadgets between
PDMS sessions.
setup form !!testForm
file. Exactly the same syntax can be used here, but if you wish alter any gadgets after they
have been created you must refer to the main form explicitly rather than using the keyword
!this, which will refer to the object.
Toolbars can be registered - see Toolbar Control - any unregistered toolbars will be hidden
by default and will not be controlled.
The same method can be used to add fields to any menu descended from the bar menu of
the main form.
A sample add-in object definition, which must be put in the PMLLIB path, is shown below.
define object APPADDQUERYMENUFIELD
endobject
define method .modifyMenus()
!this.queryMenu()
endmethod
define method .queryMenu()
-- define APPMENU object associated with the Query menu
!menu = object APPMENU('sysQry')
-- add field to query the owner of the current element
!menu.add('CALLBACK', 'Query Owner', 'q owner', 'QueryOwner')
-- register the APPMENU object to be visible in the Pipework
-- application
!!appMenuCntrl.addMenu(!menu, 'PIPE')
endmethod
The corresponding add-in definition file, which is put in the DES\DFLTS\ADDINS directory,
contains:
Name: ADDQUERYMENUFIELD
Object: APPADDQUERYMENUFIELD
This material is chiefly for AVEVA developers and not users, because it requires cooperation
between AppWare and core-software.
The mechanisms described below allow PML defined forms, menufields and gadgets which
are subsequently managed by core code. It should be noted that the use of core code
managed forms, menufields and gadgets requires well planned co-operation between
software and AppWare.
It is permissible to have a mixture of PML and CORE managed gadgets on a PML managed
form.
A CORE managed form may also have some PML managed gadgets and menufields, but
extreme caution should be adopted in this case. Making a form core managed involves
considerable effort in the supporting core code, as it bypasses most of the F&M form
management support.
If OK, CANCEL or APPLY buttons are present they should also be declared as CORE
managed.
Command Effect
FLDFRM ( int LNAM, string FNAME, int Load named form from a definition
FORM ) file. FORM is a handle to the
created form object.
FDSCOR ( int FORM, int IERR) Display the given form. IERR is a
PML error token. It takes the value
0 when there was no error.
• FRAME
• LIST (single, multi and option)
• SELECTOR (single, multi)
Alpha and Graphical view gadgets are not candidates for this mechanism.
These gadgets’ definition graphs now support the ‘CORE’ qualifier:
.-------<---.
/ |
<gadget>--+-- tagtext --| gadget tag
| |
+-- <fgpos> --| gadget position
| |
+-- CORE -----| Core managed gadget
| |
.
.
.
When a form is first displayed its gadgets are created. If a gadget is declared as core
managed then F&M, builds the DRUID widget, sets its active, visible and selected states,
and then calls core code to allow it to take over the gadget callback.
Core code may plug in its own callback function to DRUID so that events on that gadget will
go directly to core code and will no longer go to F&M. Core code will then be responsible for
managing the gadgets state in conjunction with PML AppWare. For gadgets with no
callback, the core code may merely wish to read values from or write to the gadget. You
must exercise great care to avoid clashes between AppWare and software management.
TEXTPANE and PARAGRAPH gadgets do not have a callback.
where:
• FORM and OWNER are returned arguments.
• FORM/GADGET/MENU/MENUFIELD/ELT/OWNER is the F&M handle for an element.
• IERR is a PML error token, where 0 means no error.
B Manipulating VIEWS
This Appendix tells you how to interact with the 2D and 3D View gadgets.
Key Effect
7 8 9
Zoom in x 1.5 on view centre Pan down by half view Zoom in x 2.5 on view
width centre
4 5 6
Pan left by half view width Zoom out to view limits Pan right by half view
width
1 2 3
Zoom out x 1.5 on view centre Pan up by half view Zoom out x 2.5 on view
width centre
Field Meaning
Field Meaning
Fast This is the current value of the gearing modifiers for view
adjustment.
Pressing Ctrl displays Fast
Pressing Shift displays Slow
Pressing both displays Medm.
Otherwise the field is blank.
These modifiers apply to both mouse adjustments and
keypad actions. In general, Fast is about 10 times faster,
and Slow is about 10 times slower. However, for rotations
and zooms different values are used.
This field displays Rest when the Alt key is pressed, and
Save when Alt and Ctrl are pressed together. This relates
to saving and restoring view settings, described below.
• Popup Menu
Pressing MB3 displays the popup view menu when the cursor is over the view canvas.
This will either be the default menu supplied by the Forms and Menus software, or an
application-specific version if provided, either by AVEVA or by your own customisation.
Zoom F2
Pan F3
Rotate F5
Walk F6
The Zoom, Pan, Rotate and Walk options select a mouse mode.
The Options pullright toggles the states of Eye/Model, Shaded/Wireline, Borders On/Off,
and Perspective/Parallel.
These actions may also be achieved by pressing the relevant Function Button while the
view canvas has keyboard focus.
The Restore and Save pullrights allow up to four view settings to be saved and restored. To
save the current view settings, choose S1, S2, S3 or S4 from the Save menu. To restore a
previously saved setting, choose R1, R2, R3, or R4 from the Restore menu. These
functions are also available using Alt, Ctrl, and the keypad numerals 1 to 4 while the canvas
has keyboard focus.
• Mouse Modes
Mode Navigation
Zoom (F2). Press Adjust down over the view canvas, and then,
keeping the button down, move the mouse upwards
to zoom in, and downwards to zoom out.
This changes the perspective angle, or the view
scale in parallel views. It does not change the eye
point or the view direction. Use Ctrl or Shift to speed
up or slow down.
Pan (F3). Press Adjust over the canvas, and then, keeping the
button down, move in any direction to shift the line of
sight in that direction. The current view direction is
not changed.
Note that the picture moves the opposite way to the
mouse movement; it is the observer that is being
moved.
It may be useful to think of moving the mouse
towards the part of the picture you wish to see. Use
Ctrl or Shift to speed up or slow down.
Mode Navigation
Rotate (F5). Press Adjust and keep it down, then move left/right
to change the bearing of the view direction, or up/
down to change the elevation.
The initial movement of the mouse determines which
is changed, and only that component of the view
direction is modified.
To modify the other component, the mouse button
must be released and then pressed down again,
while moving in the other direction.
Walk (F6). Press Adjust button and keep it down, then move up
to walk forwards, or down to walk backwards, along
the line of sight. Use Ctrl or Shift to speed up or
slow down.
Eye (F7). View rotations keep the from- point fixed, and in
Model mode rotations keep the through-point fixed.
Use Ctrl or Shift to speed up or slow down.
In all modes, clicking MB2 will shift the point under the cursor to the centre of the view. In
perspective, the from-point will be maintained, and the view direction and through-point will
be modified. In parallel, the view direction will be maintained, and the from- and through-
points moved. The only exception to this rule is in Pan Mode, when the view direction is
never modified.
When the keyboard focus is in the view, the following functions are available.
7 8 9
Zoom in x 1.5 on view centre Rotate in 10-degree Zoom in x 2.5 on view centre
steps
4 5 6
Rotate in 10-degree steps Zoom out to view limits Rotate in 10-degree steps
1 2 3
Zoom out x 1.5 on view centre Rotate in 10-degree Zoom out x 2.5 on view
steps centre
Key Effect
Key Effect
F10 (if available) Toggle between ENU & XYZ display modes
The following changes have been made to allow consistency with ReviewLE and to improve
usability on 'lap top' keyboards which frequently do not have the Numeric Keypad.
Notes:
• There is no syntax to set the mouse mode, or to enquire it.
• The eye mode can be selected by setting Walkmode On, and Model by setting
Walkmode Off.
• If your keyboard focus is set by moving the cursor into a window without having to click
a mouse button, it is possible to get the state of the Ctrl and Shift keys confused. If
you move the mouse into the view, press Ctrl, press Adjust, move the mouse out of
the view, release Adjust, then release Ctrl, you will still be in Fast mode, as shown in
the status line. To remedy this, move the cursor back into the view, and then press and
release Ctrl.
Index
G K
Gadget box . . . . . . . . . . . . . . . . . . . . . . 17:2 Keyboard focus . . . . . . . . . . . . . . . . . . 19:9
Gadget References . . . . . . . . . . . . . . . . . 9:1 Killing forms . . . . . . . . . . . . . . . . 13:4, 15:12
Gadget:database selector . . . . . . . . . . 20:35
Gadgets:alignment . . . . . . . . . . . 17:2, 17:4 L
Gadgets:auto-placement . . . . . . . . . . . . 17:3
Gadgets:button . . . . . . . . . . . . . . . . . . 20:20 LABEL command . . . . . . . . . . . . . . . . . . 5:6
Gadgets:de-activating . . . . . . . . . . . . . . 19:8 Late evalation of variables . . . . . . . . . . . 8:2
Gadgets:greying-outSee deactivating . . 19:8 LIMITS command . . . . . . . . . . . . . . . . 20:55
Gadgets:list . . . . . . . . . . . . . . . . . . . . . 20:29 LIMITS command:AREA view . . . . . . 20:52
Gadgets:making invisible . . . . . . . . . . . 19:9 Line feeds . . . . . . . . . . . . . . . . . . . . . . . . 8:5
Gadgets:making visible . . . . . . . . . . . . . 19:9 Linking files . . . . . . . . . . . . . . . . . . . . . . 3:3
Gadgets:paragraph . . . . . . . . . . . . . . . 20:19 Linking new form files . . . . . . . . . . . . . . 13:5
Gadgets:positioning . . . . . . . . . . 17:1, 17:2 List gadget . . . . . . . . . . . . . . . . . . . . . 20:29
Gadgets:positioning, absolute . . . . . . . . 17:1 Loading forms . . . . . . . . . . . . . . . . . . . 13:4
Gadgets:positioning, relative . . . . . . . . . 17:2 Logical operators . . . . . . . . . . . . . . . . . . 4:1
Gadgets:refreshing . . . . . . . . . . . . . . . 19:10 LOOKcommand . . . . . . . . . . . . . . . . . 20:54
Gadgets:text input . . . . . . . . . . . . . . . . 20:35 Loops . . . . . . . . . . . . . . . . . . . . . . . . . . . 5:4
Gadgets:textpane . . . . . . . . . . . . . . . . 20:43
Gadgets:toggle . . . . . . . . . . . . . . . . . . 20:22 M
GOLABEL command . . . . . . . . . . . . . . . 5:6
Greying-outSee deactivating . . . . . . . . . 19:8 Macros . . . . . . . . . . . . . . . . . . . . . . . 3:1, 7:1
Grid (for form layout) . . . . . . . . . . . . . . . 17:1 Macros:argument defaults . . . . . . . . . . . 7:3
Macros:argument separators . . . . . . . . . 7:2
Macros:arguments . . . . . . . . . . . . . . . . . 7:1
H Macros:arguments, omitting . . . . . . . . . . 7:3
HALIGN command . . . . . . . . . . . . . . . . 17:4 Macros:arguments, separating . . . . . . . . 7:3
HDISTANCE command . . . . . . . . . . . . 17:3 Macros:naming . . . . . . . . . . . . . . . . . . . . 7:1
Help button . . . . . . . . . . . . . . . . . . . . . 20:22 Macros:running . . . . . . . . . . . . . . . . . . . 7:1
Help options . . . . . . . . . . . . . . . . . . . . . 16:4 Macros:suspending . . . . . . . . . . . . . . . 12:2
Hiding forms . . . . . . . . . . . . . . . 13:4, 15:12 Members (of objects) . . . . . . . . . . . . . . . 2:1
Highlight colour . . . . . . . . . . . . . . . . . . 20:49 Menu bar:defining . . . . . . . . . . . . . . . . 16:2
Menu Bars:inserting menus . . . . . . . . . 16:7
Menus . . . . . . . . . . . . . . . . . . . . . . . . . 16:1
I
Menus:bar . . . . . . . . . . . . . . . . . . . . . . 16:2
Icon title for form . . . . . . . . . . . . . . . . . . 15:5 Menus:de-activating options . . . . . . . . 16:9
IF construct . . . . . . . . . . . . . . . . . . . . . . . 5:1 Menus:defining . . . . . . . . . . . . . . . . . . . 16:3