GX Program Structure
Introduction
To create a new GX extension you will normally use an existing GX as a model of a standard program pattern. All Geosoft GXs are provided as source code that you will find in in folder .../GX Developer/gx/src.
We will use the COPY GX (.../gx/src/copy/) to walk through a the basic program pattern for a simple GX that has the following:
Collects user input using a dialog.
Modifies a database, reporting progress as we go.
Scriptable.
The COPY GX copies one database channel to a new channel - if the new channel does not exist, it will be created, and the channel data can be decimated during the copy.
Python Scripts
Extensions can be created using the GX language as documented here, or with Python scripts. Although geosoft.gxpy is a higher-level Python module for working with Python, geosoft.gxapi provides all low-level API functions available to a GX Language programmer. Python developers that want to create scriptable extensions can use the same programming pattern adopted here. See Python Extensions for more information on working with Python.
Sections on this page
- 1 Introduction
- 1.1 Python Scripts
- 2 Part 1: Set the header information
- 3 Part 2: Include the GX resources
- 4 Part 3: Include GX Function Header files
- 5 Part 4: Declare variables
- 6 Part 5: Open the main code block
- 7 Part 6: Initial setup
- 8 Part 7: Perform interactive processes
- 9 Part 8: Retrieve and verify parameters
- 10 Part 9: Prepare to process data
- 11 Part 10: Process the data
- 12 Part 11: Clean up.
Part 1: Set the header information
Every GXC begins with a NAME, VERSION and DESCRIPTION. In addition to being useful for source control, these parameters are reported by the VIEWGX program. Note that multi-line text is supported (see the DESCRIPTION statement.). Parameters residing in the Project Parameter Block are explained as they are used and/or modified. As in normal C, “//” indicates the start of a comment, which is ignored during compilation.
//====================================================================
NAME = "Copy one channel to another"
VERSION = "v1.01.00 Copyright Geosoft Inc. 1999"
DESCRIPTION = "
Copies one channel to another. If the new channel does not exist, it
will be created with the same definition as the original channel. The
channel data can be decimated during the copy.
Parameters:
COPY.FROM - Original channel
.TO - Destination channel
.DECIMATE - Decimation factor, default 1
.FIDSTART - New fiducial start, default is current start.
.FIDINCR - New fiducial increment, default is current increment.
"Part 2: Include the GX resources
If your GX interacts with the user with a dialog defined in a resource file you will need to compile the resource file (copy.grc in this example, see below). Compiling a resource file will create a Geosoft Resource “GR” file, and a set of resource identifiers a Geosoft Resource Header “GRH” file. Add these next:
//====================================================================
// RESOURCES
//====================================================================
RESOURCE = "copy.gr"
#include "copy.grh"For reference, the Resource file copy.grc is:
//
// COPY.GRC
//-----------------------------------------------------------------------------
RESOURCE,FORM,COPYForm,"Copy a channel",-1
LEDIT,,,16,"Copy FROM",R,FORCE,,CHAN
LEDIT,,,16,"TO",R,,,CHAN
EDIT,,,16,"Decimation factor",,INT,1
EDIT,,,16,"New fiducial start",,REAL
EDIT,,,16,"New fiducial increment",,REAL
EBUT,&OK,0
EBUT,&Cancel,1,CANCEL
HBUT,&Help,help
RESOURCE,LIST,CHAN
RESOURCE,HELP,help,nogx.hlpPart 3: Include GX Function Header files
The GX API is described as function prototypes in GXH files. The “catch-all” header all.gxh includes all the regular, non-mapping Geosoft function prototypes. Constants defined by the #define pragma are also included. Refer to the .../gx/include folder which contains all function sets organized by name.
//====================================================================
// INCLUDE
//====================================================================
#include <all.gxh> // system Part 4: Declare variables
As a C language, all variables must be declared by type before they are used. This include all object handles, as well as the int, real and string types. Though not required, it is common practice to declare variables in the following order: handles, integers, reals, strings.
//====================================================================
// VARIABLES
//====================================================================
EDB EData; // Database handle
DB Data; // Database handle
DB_SYMB InCh; // Channel Handle
DB_SYMB OutCh; // Channel Handle
DB_SYMB Line; // Line Handle
DGW Diag; // Dialogue handle
LST List; // List handle
int i; // Utility
int iN; // Decimation factor
int iLines; // Number of Lines Processed
int iTotLines; // Total Number of Lines to Process
real rFidStart; // Fid start
real rFidIncr; // Fid increment
real rNewStart; // New fid start
real rNewIncr; // New fid increment
string(50) sInCh; // Channel Names
string(50) sOutCh; // Channel Names
string(32) sTemp; // Temp string
string(60) sLabel; // Label for progress bar Part 5: Open the main code block
This includes the remainder of the source file. The code is enclosed in a pair of curly brackets.
//====================================================================
// CODE
//====================================================================
{Part 6: Initial setup
This usually involves getting the current database or map, and setting up information required later for user dialogs. Some GXs may also recover parameters from a control or initialization file here, and set various default values.
// --- Get database ---
EData = Current_EDB();
Data = Lock_EDB(EData);Part 7: Perform interactive processes
Most GX's support scripting and need to check if they are running interactively or in a script. If running interactively a dialog can be opened to get user input and handle any interactive logic. This generally involves creating, initializing, displaying, then interrogating a dialog object for information modifiable by a user.
To support scripting, variables can be initialized from and loaded back into the Project Parameter Block, using the SetInfoSYS_DGW and GetInfoSYS_DGW functions.
// --- Are we running interactively ? ---
if (iInteractive_SYS())
{
// --- Create the Dialogue ---
Diag = Create_DGW("COPYForm"); // name matches the dialog name in copy.grc
// --- Set up lists ---
List = GetList_DGW(Diag,_COPYFORM_0);
SymbLST_DB(Data,List,DB_SYMB_CHAN);
Sort_LST(List,0,0);
List = GetList_DGW(Diag,_COPYFORM_1);
SymbLST_DB(Data,List,DB_SYMB_CHAN);
Sort_LST(List,0,0);
// --- Initialize to last settings from the Project Parameters ---
SetInfoSYS_DGW(Diag,_COPYFORM_0,DGW_TEXT,"COPY","FROM");
SetInfoSYS_DGW(Diag,_COPYFORM_1,DGW_TEXT,"COPY","TO");
SetInfoSYS_DGW(Diag,_COPYFORM_2,DGW_TEXT,"COPY","DECIMATE");
SetInfoSYS_DGW(Diag,_COPYFORM_3,DGW_TEXT,"COPY","FIDSTART");
SetInfoSYS_DGW(Diag,_COPYFORM_4,DGW_TEXT,"COPY","FIDINCR");
// --- Display the Dialogue ---
i = iRunDialogue_DGW(Diag);
if (i != 0) Cancel_SYS(); // The user cancelled
// --- Put user input back into Project Parameters ---
GetInfoSYS_DGW(Diag,_COPYFORM_0,DGW_TEXT,"COPY","FROM");
GetInfoSYS_DGW(Diag,_COPYFORM_1,DGW_TEXT,"COPY","TO");
GetInfoSYS_DGW(Diag,_COPYFORM_2,DGW_TEXT,"COPY","DECIMATE");
GetInfoSYS_DGW(Diag,_COPYFORM_3,DGW_TEXT,"COPY","FIDSTART");
GetInfoSYS_DGW(Diag,_COPYFORM_4,DGW_TEXT,"COPY","FIDINCR");
// --- Destroy the Dialogue ---
Destroy_DGW(Diag);
}Part 8: Retrieve and verify parameters
Parameters are retrieved from the Project Parameter Block and verified. Verification ensures that the data exists, and that it falls within acceptable limits for the process that must be performed.
// --- Get Parameters from Project Parameter Block ---
GetString_SYS("COPY","FROM",sInCh);
GetString_SYS("COPY","TO",sOutCh);
iN = GetInt_SYS("COPY","DECIMATE");
rNewStart = GetReal_SYS("COPY","FIDSTART");
rNewIncr = GetReal_SYS("COPY","FIDINCR");
// --- Verify parameters ---
if (iN==iDUMMY) iN = 1;
if (iN<= 0)
Abort_SYS("Decimation factor must be > 0.");
if ((rNewIncr!=rDUMMY)&&(rNewIncr<=0.0))
Abort_SYS("Fid increment must be > 0.");Part 9: Prepare to process data
Here we create handles to channels we will be using in the processing loop. We also initialize counter variables for reporting progress.
// --- Does the Input Channel Exist ? ---
if (!iExistChan_DB(Data,sInCh))
Abort_SYS(_("channel does not exist."));
InCh = FindChan_DB(Data,sInCh);
// --- Does the Output Channel Exist ? ---
if (!iExistChan_DB(Data,sOutCh)) {
OutCh = DupSymb_DB(Data,InCh,sOutCh); // Create it
UnLockSymb_DB(Data,OutCh);
} else
OutCh = FindChan_DB(Data,sOutCh);
// --- Lock the channel symbols ---
if (InCh != OutCh) LockSymb_DB(Data,InCh,DB_LOCK_READONLY,DB_WAIT_INFINITY);
LockSymb_DB(Data,OutCh,DB_LOCK_READWRITE,DB_WAIT_INFINITY);
// --- Prepare to do the work ---
iLines = 0;
iTotLines = iCountSelLines_DB(Data);
Progress_SYS(1);Part 10: Process the data
Databases are normally processed line-by-line starting with the first selected line.
// --- Go through all selected Lines ---
ProgName_SYS("",1);
Line = FirstSelLine_DB(Data);
while (iIsLineValid_DB(Data,Line))
{
// --- Update the Progress Bar ---
LockSymb_DB(Data,Line,DB_LOCK_READONLY,DB_WAIT_INFINITY);
GetSymbName_DB(Data,Line,sLine);
UnLockSymb_DB(Data,Line);
Printf_STR(sProg1, sizeof(sProg1), _("Copying %s to %s: Line %s"), sInCh, sOutCh, sLine);
ProgName_SYS(sProg1,0);
ProgUpdateL_SYS(iLines,iTotLines);
// --- Copy/Decimate ---
Decimate_DU(Data,Line,InCh,OutCh,iN);
// --- Correct the Fiducial Start ---
if ((rNewIncr!=rDUMMY)||(rNewStart!=rDUMMY)) {
if (rNewStart==rDUMMY)
rFidStart = rGetFidStart_DB(Data,Line,InCh);
else
rFidStart = rNewStart;
if (rNewIncr==rDUMMY)
rFidIncr = rGetFidIncr_DB(Data,Line,InCh);
else
rFidIncr = rNewIncr;
SetFid_DB(Data,Line,OutCh,rFidStart,rFidIncr);
}
// --- Advance to Next Line ---
Line = NextSelLine_DB(Data, Line );
iLines++;
}Part 11: Clean up.
A "Maker" is a record of the GX that made a channel so that a user can remake a channel later, or see the details of how a channel was made.
We then destroy any created objects which should not exist once the GX terminates. Terminating a GX will automatically destroy all objects, but it is considered good practice to explicitly clean-up resources.
// --- Add maker ---
EasyMakerSymb_DB(Data,OutCh, _("Copy channel"),"COPY;");
// --- Unlock the Channel Symbol ---
if (InCh != OutCh) UnLockSymb_DB(Data,InCh);
UnLockSymb_DB(Data,OutCh);
// --- done ---
Progress_SYS(0);
UnLock_EDB(EData); // --- Release the database ---
// --- display the new channel ---
LoadChan_EDB( EData, sOutCh );
}