The DataCAD Developer Network (DDN) is an online resource for information and support for DCAL® (DataCAD Applications Language) developers as well as anyone interested in creating fonts, toolbars, hatch patterns, or linetypes for use in DataCAD.
#72332 by Jsosnowski
Mon Sep 18, 2017 3:02 pm
PART SIX - A DCAL FOR DELPHI PRIMER - OTHER MACRO CONSIDERATIONS

Once you have created your main function and added a Menu function to it, you are ready to begin coding your macro features. There are still plenty of challenges in developing a functional macro. This Post will discuss a variety of subjects including special situations, tips, strategies and whatever else may be useful for achieving your macro goals, a grab bag if you will. Each reply post covers a different topic.

Topics
1. Accessing Datacad Variables
2. Planning Delphi Units
3. Preserving variables with '.INI' files
Last edited by Jsosnowski on Mon Sep 18, 2017 3:55 pm, edited 2 times in total.
#72333 by Jsosnowski
Mon Sep 18, 2017 3:03 pm
Accessing Datacad Variables

Edit: 12/3/17 & 12/6/17 Changed name of function to PGSavevar in code snippet

All available Datacad variables are listed in the 'uvariables.pas' file. Most of them are described in the DCAL manual although some may have been added since the manual was published. In DCAl macros these variables are directly accessible such as txtang := 0;. This code line will not work in in D4D macros. Variable listings in 'uvariables.pas' declare these variables as fields in a record, savevar. Actually, variables are contained in two records, the second named svtype. The reason for two may be beyond this author's pay grade.

In order to access the variables use a function included in 'uinterfaces.pas' called PtrGSavevar for savevar variable fields and PtrGSv for svtype variable fields. The code statement to read or write the 'txtent variable then would be:

Code: Select all...
var
  tempAngle : afloat;
begin
  //read variable
  tempAngle := PGSavevar.txtang;
 
  //write variable
  PtrGSv.txtang := 5.0;
 
end;
Last edited by Jsosnowski on Wed Dec 06, 2017 1:41 pm, edited 2 times in total.
#72335 by Jsosnowski
Mon Sep 18, 2017 3:39 pm
Planning Delphi Units
Rev1 11/1/17 Added paragraph on data initialization procedure

For smaller macros all code can be placed in the initial library file unit created when you start a new '.dll' project (Delphi Menu: Project/New Project/DLL Library). Like in DCAL, for larger projects with a large body of operational options, it makes more sense to separate the code into multiple unit files (Delphi Menu: File/New/Unit-Delphi). Units offer a few capabilities that are not found in the library file. Units, for instance have multiple sections including:

1. interface - used to declare constants, types, variables, functions and procedures that are accessible to other units in the macro.
2. implementation - used to declare constants, types, variables not accessible by other units along with the code for all functions and procedures declared in the interface section or only available within this unit.
3. Initialization - used to declare any functions and procedures that should run when the unit is first opened at the start of the macro. Unit Initialization sections are opened in the order they are declared in 'uses' section declared in the 'library' project file.
4. Finalization - used to declare functions and procedures that should be run before the macro is terminated. Unit Finalization sections are opened in the reverse order they are declared in the 'uses' section declared in the 'library' project file.

Note that the library '.dll' file does not have these sections and every declaration made in the library will be accessible to other units.

Another important consideration for planning your units is that each unit must have a 'uses' section declaring any other units containing content in its 'interface' section that is used in this one. However, when compiling the code, Delphi will object any circular references. If 'Unit A' uses 'Unit B', the reverse cannot also occur. For this reason it may be a good strategy to create a single unit as a container to include all constants, variables, and types used in a single unit that does not have 'uses' links to other macro unit files.

Initialize your data
If a unit that contain global variables it is a good idea to create a procedure init_<name> to set initial values. Keeping this operation as separate procedure allow you to use it in a variety of situations. Certainly it should be used when the macro is started, but will also be useful if you are using data files and need to reset your data to an initial condition before loading the new data file. Variables such as string lists or other dynamic arrays will certainly need to be reset to a starting size in order to add the new data.

Once you begin coding macros it is difficult to stop. There is always another good idea that could simplify your work. By collecting related code in individual units it is possible to reuse them in other macros. One good example of this is the wrtutl.pas unit provided in Datacad's sample macro directories. This unit handles a variety of text display methods for use in displaying messages in Datacad. Another good utility unit might collect various methods for drawing various entity types in Datacad. Each new macro you write may add code to draw a different entity type. One unit holds them all and is easy to find the next time you need one of these previous functions.
Last edited by Jsosnowski on Wed Nov 01, 2017 1:01 pm, edited 1 time in total.
#72337 by Jsosnowski
Mon Sep 18, 2017 3:51 pm
.INI Files - Preserving Unit Variables Between Macro Uses

In DCAL macros any variables declared at the unit file level were both globally accessible and preserved between uses of the macro. While Delphi unit variables are also globally accessible in the macro, their values are not preserved between calls to the macro. This means that any settings made by the user during the use of a macro must be reset in the next call. To compensate it is necessary to store the values in a file at the end of each macro use and reload them at the start of the next use. One of the simplest means for doing so is to use an '<name>.ini' file to hold the relevant information. This is how Datacad stores a number of settings that are independent of a single drawing file.

To do so, it is necessary to use one of Delphi's class types tIniFile. This primer does not go into detail requirements of using Delphi classes, however the following code snippet provides an example of the class in use.

Code: Select allunit <name>_u;

interface
uses
  System.SysUtils, System.Classes, {1}
  inifiles,  {1}

  UConstants,  //contants declarations
  Uvariables, //all variables available in DCAl
  URecords,  //all record structures available in DCAL
  UInterfaces, //DCAL for Delphi methods used for interfacing directly with user
  UinterfacesRecords;  //Record structures for various interface functions.
const
  cfilename = '<name>.ini';
 
var
  iniFl: tIniFile; {2}
 
  //persistent variables
  int : integer;
  fl : afloat;
  str : string;
  bool : boolean;

 
implementation

procedure InitVariables;
begin
  int := 1;
  fl := 2.3;
  str := 'My string';
  bool := true;
end;

Procedure <unitName>StartUp;  {3}
//Load settings from 'ini' file
var
  fname, path : str255;
begin
  getpath (path, pathsup);  //places the ini file in the Support directory of Datacad. use pthmcr for macro directory.
  fname := path + filename;  //change the name for custom ini file.
  if fileexists(fName) then  begin  //change the name to match Datacad settings
    try
     iniFl :=  TIniFile.Create(fname);  //create the class object
   //insert your variables    
{     iniFl.readinteger ('Section 1', 'the Integer', int);
      iniFl.readFloat ('Section 1', 'the Float', fl);
     iniFl.readString ('Section 1', 'the Float', str);
     iniFl.readBool ('Section 1', 'the Boolean', bool);
}    
    finally
     IniFl.free;  //close the class object
    end;   
  end
  else
   initVariables;
  end; 
end; //<unitName>StartUp

Procedure <unitName>ShutDown;  {4}
//Save settings to 'ini' file
var
  fname, path : str255;
begin
  getpath (path, pathsup);  //places the ini file in the Support directory of Datacad. use pthmcr for macro directory.
  fname := fname + cfilename;  //change the name for custom ini file. 
  try
   iniFl :=  TIniFile.Create(fname);  //create the class object
   //insert your variables
(   iniFl.writeInteger ('Section 1', 'the Integer', int);
    iniFl.writeFloat ('Section 1', 'the Float', fl);
   iniFl.writeString ('Section 1', 'the String', str);
   iniFl.writeBool ('Section 1', 'the Boolean', bool);
}   
  finally
   IniFl.free;  //close the class object
  end;   
end; //<unitName>ShutDown

initialization
  <unitName>StartUp;  {5}

finalization
  <unitName>Shutdown:  {5}

{1} Add the Delphi 'inifiles' unit for use in this file.
{2} Define a tiniFile object variable for use in the unit.
{3} Define a <unitName>startup initialization procedure to load the settings from an 'ini' file. The procedure begins by getting the path where Datacad stores support files. 'Pathsup' is a constant defined in uconstants.pas. As an alternative directory you might also consider 'pathmcr' which is the directory used to store macros. After getting the path, provide a specific <name>.ini file name. The code then tests to see if the file already exists. If it does, the values are loaded to your variables. If not, the procedure InitVariables is called to load default values.Use the Delphi try/finally statement to create the Tinifile object and write the specific variables to the file. The format for all iniFl.write and iniFl.read procedure arguments is (Section, Identifier, Value) which reads in the '.ini' file as:

[Section]
Identifier=Value


These values can be inspected and edited outside of the macro by the user with any text editor. Also note that the last step in the try/finally statement frees the object releasing its memory allocation, a necessary step for all Delphi class objects.
{4} Similar to <unitName>startup the <unitName>shutdown procedure writes the current variable values from the file and loads them into your macro unit's global variables.
{5} Place calls for the startup and shutdown procedures in the initialization and finalization sections. These procedures will then be called when the macro is loaded and closed. Note that 'initialization' and 'finalization' sections cannot be used in the actual 'dll' library file. They must be placed in a '<name>unit' file to be called by the macro. (see the 'Initialization & Finalization' section in this post.

You must include procedures in each unit of your macro to read and write the global variables it contains. Each unit should assign a different 'section' argument in the 'iniFl.read...' or 'iniFl.write...' procedure calls.

Who is online

Users browsing this forum: No registered users and 12 guests

About DataCAD Forum

The DataCAD Forum is a FREE online community we provide to enhance your experience with DataCAD.

We hope you'll visit often to get answers, share ideas, and interact with other DataCAD users around the world.

DataCAD

Software for Architects Since 1984