How to Speed Up Grid Loading and Processing |
Grid controls are a very useful way of dealing with tabular data. However, loading and interacting with a grid can sometimes be slow, especially when working in a WindX environment.
The information below looks at ways to speed up grid interactions, specifically different ways to load data into a grid, retrieve grid contents, and how to get/set various grid properties.
A grid can be loaded in several ways:
Each of these methods is explained in more detail below.
Loading the Entire Grid
Loading the entire grid in one command is by far the fastest way to load a grid. This method will speed up the grid load in both the WindX and non-WindX environments.
To do this, you would build a string by concatenating the data for each row with the proper separators. By default, the column separator is the SEP value, but this can be changed by selecting the Column Separator option in the Grid Format Definition in NOMADS or by setting the grid's 'SEP$ property. The default row separator is $0A$, but this can be changed with the 'SEPLOAD$ property. Then, load the entire grid using column 0 and row 0.
Example:
myData$=""
SELECT a$,b$,c$ FROM myfile$
myData$+=a$+SEP+b$+SEP+c$+SEP+$0A$
NEXT RECORD
GRID LOAD mygrid.ctl,0,0,myData$
OR
myData$=""
OPEN (HFN,IOL=*)myfile$
channel=LFO
SELECT * from channel
myData$+=REC(IOL(channel))+$0A$ ! The REC function includes the column separators
NEXT RECORD
CLOSE(channel)
GRID LOAD mygrid.ctl,0,0,myData$
The second fastest way to load a grid is to load it row by row. Each row must include column separators for the columns, but a row separator is not needed.
Example:
SELECT a$,b$,c$ FROM myfile$
GRID LOAD mygrid.ctl,0,0, a$+SEP+b$+SEP+c$+SEP
NEXT RECORD
This method is slower, as the grid control must be refreshed as each row is added. To speed this up, you can hide the grid while it is being loaded, or you can turn the screen refresh Off by printing the '-U' mnemonic before the grid is loaded and then turn it back On by printing the '+U' mnemonic when done. See '+U' & '-U' mnemonics. This will speed up the grid load in both the WindX and non-WindX environments.
Example:
row=0
OPEN(HFN,IOL=*)myfile$
channel=LFO
PRINT '-U',
SELECT * FROM channel
row++
GRID LOAD mygrid.ctl,0,row,REC(IOL(channel))
NEXT RECORD
PRINT '+U',
CLOSE(channel)
When using the '-U' and '+U' mnemonics, the grid control will be displayed, as well as any properties that are set prior to printing '-U'. If you set column headings and formats prior to turning Off screen refresh, these will be displayed. However, the loading of the grid rows with data will not be shown until screen refresh is turned back On with '+U'.
If the grid has been formatted with column names, you can also load the grid row by row by using grid row properties 'LOADIOLIST$ and 'ROWDATA$. See Load/Access by Row Grid Properties.
Example:
rowIOList$=myGrid.ctl'LoadIOList$ ! Gets the IOList for grid row
Then, the row can be loaded using the GRID LOAD directive:
GRID LOAD mygrid.ctl,0,row,REC(rowIOList$)
Or, it can be loaded using the 'ROWDATA$ property:
mygrid.ctl'row=row
mygrid'ctl'RowData$=rec(rowIOList$)
Using properties to load a grid is slower in a WindX environment, as every property SET or GET requires a packet to be sent on the WindX connection.
See Setting/Getting Properties below for information on how to speed up the use of properties.
Loading Cell by Cell
Loading a grid cell by cell is the slowest way to load a grid. This method is generally used when rows and/or columns do not have a consistent format or when you want to update a single cell. This can be done by using the GRID LOAD directive or by specifying properties to set the column, row and value.
Example:
GRID LOAD mygrid.ctl,col,row,value$
OR
mygrid.ctl'colno=col
mygrid.ctl'row=row
mygrid.ctl'value$=myVal$
The example using properties would be slower in a WindX environment, as three packets would have to be sent to set the three properties.
See Setting/Getting Properties below for information on how to speed up the use of properties.
Just as there are several ways to load data into the grid, you can also retrieve data by the cell, by the row, or by the entire contents, depending on how you are processing the data.
Processing the Entire Grid
The fastest way to process a grid is to retrieve the entire contents of the grid and parse the contents programmatically. This is useful if you are processing the grid as a whole, in which case you do not process each individual change but wait until all the changes are made and then submitted.
You can retrieve the entire contents of the grid by using the GRID FIND directive with row and column set to 0 (zero):
myGrid.ctl'SEPLOAD$=$00$
GRID FIND myGrid.ctl,0,0,contents$
This will return the contents of the entire grid with each row separated by the value in the 'SEPLOAD$ property. The columns in each row are separated by the value in the 'SEP$ property (default is SEP).
An easy way to process the contents row by row is to use the FOR..NEXT directive to parse the resulting string into substrings, which hold the contents of each row. Using this syntax, the last character of the contents$ string is the row separator, and the FOR..NEXT loop will parse out each row based on that character.
Example:
FOR rowContents$ FROM contents$
! rowContents$ has the contents of the entire row which can then be parsed into columns
READ DATA FROM rowContents$ TO col1$,col2$,col3$
! Now process the data as required
NEXT
You could also retrieve the IOList for the row using the 'LOADIOLIST$ property and use that to parse the fields:
mySep$=myGrid.ctl'SEP$
myIOLIST$=myGrid.ctl'LoadIOList$
GRID FIND myGrid.ctl,0,0,contents$
FOR rowContents$ FROM contents$
READ DATA FROM rowContents$,SEP=mySep$ TO IOL=myIOLIST$
! Process the data as required
NEXT
Processing Row by Row
To process data in a grid row by row, you can use the GRID FIND directive or retrieve data using the 'ROWDATA$ property.
Example:
GRID FIND myGrid.ctl,0,row,rowContents$
OR
myGrid.ctl'row=row
rowContents$=myGrid.ctl'rowData$
Both of the above examples would populate rowContents$ with the data in the row, with columns separated by the value in the 'SEP$ property (default is SEP), which can then be parsed and processed as needed, perhaps by using the 'LOADIOLIST$ property. For information on using this property, see Loading Row by Row.
Example:
READ DATA from rowContents$ TO IOL=myGrid.ctl'LoadIOList$
When using properties, consider using the methods described below to increase speed. See Setting/Getting Properties.
Processing a Single Cell
In the case where you are processing the data in a single cell whenever it changes, there are two considerations:
If you are using the NOMADS environment:
Whenever a grid cell changes, the corresponding grid control variable is populated with the new value. In addition, the xxxxx.ROW and xxxxx.COLUMN variables (where xxxxx is the grid control name) are populated with the current row and column being processed by the grid.
Example:
myGrid$ would hold the value, and myGrid.ROW and myGrid.COLUMN would hold the row and column of the changed cell. These values can be used without having to access the grid properties.
If you are processing individual cells by using properties:
You would have to get/set several properties to retrieve the value:
myGrid.ctl'row=myGrid.ctl'currentrow
myGrid.ctl'colno=myGrid.ctl'currentColno
value$=myGrid.ctl'value$
This would be slower in a WindX environment but could be speeded up by using the methods described below. See Setting/Getting Properties.
To Set/Get properties of a grid can be slow in a WindX environment if properties are accessed on an individual basis, one at a time. For example, setting a value in a grid cell requires setting the column and row numbers, as well as the value. Setting these separately would require three packets to be set on a WindX connection.
However, there are two ways to combine access to properties to reduce the number of packets. This includes utilizing pseudo multi-properties or using multi-property access by employing the '_PropList$, '_PropValues$ and '_PropSep$ properties.
Pseudo Multi-Properties
Pseudo Multi-Properties can be used to read or update a number of properties at the same time. A pseudo multi-property is basically a property whose name is made up of multiple "string" properties with each property name terminated by a . (period).
Example:
mygrid.ctl'row.colno.celltype$.value.$
A string comprised of the row and column numbers, a celltype and value as strings separated by a trailing separator character would be used to set the four properties:
mygrid.ctl'row.colno.celltype.value.$=str(row)+"/"+str(col)+"/ellipsis/"+val$+"/"
In this case, the last character of the assigned string, "/", is used to separate the values into the associated properties. The separator can be any character depending on usage.
You can also use pseudo multi-properties to read multiple property values. In this case, the property values would be returned in a string comprised of the values separated by the column separator, i.e. the 'SEP$ property.
Example:
S$=mygrid.ctl'sep$
X$=mygrid.ctl'row.colno.celltype$.value.$
READ DATA FROM X$, SEP=S$ TO R,C,Type$,Val$
In the above examples, rather than sending five packets over a WindX connection, only two packets would be required: one to get the separator (not needed if the default is used) and one for the other four properties.
There is a limit on the number of properties that can be included in pseudo multi-properties. The limit is dependent on the length of the string used to define the properties, which is the same as that of a variable name, i.e. 127 characters.
Multi-Property Access using _PropList$, _PropValues$ and _PropSep$ Properties
Multi-Property Access allows you to set/get values for more than one property for a control. A list of the properties to be updated or read would be set in the '_PropList$ property. The string of values for the properties would be in the '_PropValues$ property, separated by the default SEP character or the character defined in the '_PropSep$ property.
'_PropList$ contains a comma-separated list of the property names to read/write.
'_PropValues$ contains a string of values for each of the properties in 'PropList$, separated by the field separator in 'PropSep$.
Example to Set Properties:
myGrid.ctl'_PropList$="Row,Colno,Celltype$,Value$"
myGrid.ctl'_PropValues$=str(row)+SEP+str(col)+SEP+"ellipsis"+SEP+"data"
Example to Read Properties:
myGrid.ctl'_PropList$="CurrentColumn,CurrentRow,Value$"
x$=myGrid.ctl'_PropValues$
READ DATA FROM x$ to Col,Row,Val$
In the above examples, two packets would be sent on a WindX connection, one for setting '_PropList$ and one for '_PropValues$. A third packet would be sent as well if '_PropSep$ were set. '_PropList$ and '_PropSep$ can be changed as needed but do not need to be reset if they do not change.
The limit on the number of properties that can be processed in one statement is dependent on the maximum PxPlus statement length (32K).