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:

  1. Collects user input using a dialog.
  2. Modifies a database, reporting progress as we go.
  3. 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

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.hlp

Part 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 );
}