Main Help → Writing Extensions → Python Scripting
The two tasks required before writing Python scripts are as follows. First, you need to know how to use the Python programming language. This documentation will not explain that process so you should use other references. Second, GEDitCOM II allows scripts to access all your genealogy data, but some access is done through the GEDCOM format. Thus, for some tasks, you may need to be familiar with GEDCOM data structures. GEDCOM documentation can be found in the GEDitCOM II help or on line.
To help in writing Python scripts for GEDitCOM II, a "Script Editing Workspace" is available in the GEDitCOM Editor. You can edit any number of scripts in that workspace and test them at any time by using the "Save and Run" command or by clicking the "Run" arrow in the tool bar. To edit Python scripts within an extension, open that extension for editing. To edit any script in the Extensions menu in that work space, select the script or extension while holding down the option key.
Finally, a powerful feature of the Python language is its ability to use Modules, which are libraries of methods and classes for use in scripts. There are many Python third-party modules available for downloading. To make use of modules easy, GEDitCOM II will look for them in a "Modules" folder, which can be in either the system or user application support files. GEDitCOM II is installed with a GEDitCOMII.py module that is highly recommended for use when scripting by Python. This help topic explains Python scripting without the module. You can refer to the separate module documentation for how many of the common scripting tasks can be made easier by using module methods and classes instead.
The Python scripting topics are:
When learning a new programming tool, the first step is often a trivial program to see something happen. Here is a trivial Python script for GEDitCOM II:
from Foundation import *
from ScriptingBridge import *
# fetch application object
gedit = SBApplication.applicationWithBundleIdentifier_("com.geditcom.GEDitCOMII")
# look for the selected record
if gedit.documents().count()==0 :
print("No documents are open.")
quit()
gdoc = gedit.documents()[0]
selrecs = gdoc.selectedRecords()
if selrecs.count()==0 :
print("No record is selected.")
quit()
print("The selected record's name is "+selrecs[0].name())
When writing Python scripts, it is highly recommended that you use the GEDitCOMII.py module. Here is the above script using the module. You import from the module rather then from Foundation and ScriptingBridge (although you can import from them if needed) and replace some awkwardly named functions with clearer names. This simple script is not much easier with the module, but the module makes many tasks in real scripts much easier.
from GEDitCOMII import *
# Check document and fetch application object
gedit = CheckVersionAndDocument("Python Hello",1.6,2)
if not(gedit) : quit()
# look for selected record
gdoc = FrontDocument()
selrecs = gdoc.selectedRecords()
if selrecs.count()==0 :
print("No record is selected.")
quit()
print("The selected record's name is "+selrecs[0].name())
To try these script do the follow:
.py (note: if you want it to appear in the GEDitCOM II scripts menu, you should save it in "Scripts" folder of user application support folder, or any folder in that folder.These scripts, and all Python scripts, start by loading key modules and then fetching a reference to the GEDitCOM II application object. In Python, it loads the Foundation and ScriptingBridge libraries, but when using the module, it will usually only need the GEDitCOMII library. The next line in each script loads a reference to the GEDitCOM II application object (called gedit). The module methods recieves that reference from the CheckVersionAndDocument() method. This reference is needed to access any data in any document. Each document is an object of the application.
The scripts next check if any documents are open and exit if not (the module-based script check is done in the module method and therefore is not needed in the maincode). If a document is open, they fetch the object for the front document (gedit.documents()[0] or FrontDocument()) and then look for the currently selected record. If a record is is found, its name is printed.
All Python scripts for GEDitCOM II will start with the same first lines through to the fetching of the application object and an open document. After those lines, the rest of script will depend on the function of the script. All examples on this page assume the first lines have been used and that gedit has been set to the application object and gdoc to a document object.
The process of becoming an expert GEDitCOM II script writer will include some of the following steps:
- Run Apple's tool for writing AppleScripts - the AppleScript Editor application (available in all MacOS X installations, but in different locations and by different names in various systems - it is always somewhere within the "Applications" folder).
- To see what is available for scripting of GEDitCOM II, choose the AppleScript Editor's "Open Dictionary..." menu command and select GEDitCOM II from the list. This dictionary is written for AppleScripting, but all the commands and objects described in the dictionary can also be used in Python scripts, but there are some important differences:
- All objects are available by the same name in Python scripts.
- Most objects have properties. If the properties are a single word, you read them with the same word, but followed by parentheses (e.g.,
gdoc.individuals()[0].name())- If the property has more than one word, remove the spaces and capitalize the second and later words (e.g.,
gdoc.individuals()[0].birthDate())- To set a property (if it is not a read-only property), put
setin front of the property, capitalize the first letter of the property, append an underscore, and finally put the new setting in parentheses (e.g.,gdoc.individuals()[0].setName_("John /Smith"))- To use any scripting command, refer to the help topic on commands. Each command will list the syntax needed to use that command in a Python script.
- Read the information below on working with genealogy documents in scripts.
- Read the information below on working with GEDCOM genealogy records in scripts.
- Read the information below on working with GEDCOM structures within records.
- Read the information below on working with albums.
- Read about the custom scripting commands incorporated into GEDitCOM II.
- Read about streaming results to the scripting panel.
- To create custom reports, read the section on writing custom reports.
- When using the
GEDitCOMII.py module(as recommended for Python scripts) read the documentation for the module's methods and classes.- Refer to the Scripting tutorials for more examples, sample scripts, references, and utility subroutines you can use in your scripts.
You can create and access genealogy documents opened in GEDitCOM II as follows:
newdoc = gedit.classForScriptingClass_("document").alloc().init()
gedit.documents().addObject_(newdoc)
The first command creates a new document object. The alloc() allocates memory and the init() creates it. The second line adds the document to the application.
Most GEDitCOM II scripts will work on the data in the current front document and thus most scripts will fetch the object for the front document using gdoc=gedit.documents()[0]. When working with a newly created document, you can reference the newdoc object (as defined above).
GEDitCOM II documents have several properties and most are adequately defined in GEDitCOM II's dictionary. The following, however, require additional documentation:
{level #, individual ref} where level # is the generation number (the root for the tree starts with level 0) and individual ref is a reference to the ancestor's individual record.{level #, individual ref, link #} where level # and individual ref are as defined above and link # is a cross reference number associated with that individual (starting at 1). All subsequent times, the individual will appear as {level #, list # for first time} where list # for first time refers to the item number in listed records for the first appearance of the individual.{level #, individual ref, fam ref} where level # is the generation number (the root for the tree starts with level 0), individual ref is a reference to the spouse's individual record, and fam ref is a reference to the family record with that spouse.{level #, "", fam ref} where level # and fam ref are as for known spouses.You can read data from any GEDCOM record by accessing the record by number, by name, or by id as follows (in Python):
print(gdoc.gedcomRecords()[6].id())
theName = gdoc.gedcomRecords().objectWithID_("@I34@").name()
print(theName.encode('utf-8'))
print(gdoc.gedcomRecords().objectWithName_("Burkhart, Caroline").recordType())
The record number is the order of the records in the file. The id of a record is its GEDCOM Record ID. The name of a record is the name as it appears in the index window for a column displayed using the view expression. Note that the second line did not directly print the name. The problem is that Python's print command attempts to convert strings to ASCII and will fail if the name contains an accented letter. The solution is to first create a string and then print using the Python string object's encode method.
Because of the way data is stored in GEDitCOM II and the way scripts access the data, it is preferable to avoid accessing records using the gedcomRecords of documents. It is preferable to access data directly by record type:
gdoc.individuals()[5].name()
gdoc.families().objectWithID_("@F27@").husband()
gdoc.sources()[7].referencedBy()
See the GEDitCOM II dictionary for the names of each type of GEDCOM Record.
You create records by creating the object and then adding it to a document:
p = {"name":"John Smith","sex":"M" }
gindi=gedit.classForScriptingClass_("individual").alloc().initWithProperties_(p)
gdoc.individuals().addObject_(gindi)
gindi.setBirthDate_("4 JUL 1776")
You should normally initialize new records with a dictionary type variable (with key/value pairs to set properties relevant to the record type being created). The first line lists some properties; the second line prepares the object, but the object is not actually created and added to a document until the third line. After adding to the document (and not before), you can access the new record using the object returned in the second line.
You can specify any property listed is the GEDitCOM II dictionary as long as the property is not listed as read only ("r/o"). Unfortunately, not all properties will get set (the ones that work depends on the type of property; i.e., some properties cannot be set until after the record is already in the document). To set properties that do not work in initWithProperties_(p), simply set them in separate commands after the record is added to the document (see the setBirthDate_() example above). Warning: many properties included in the properties variable are not set immediately. If you try to access such properties in the new record, they may not be available. To solve this problem, set those properties in separate commands after the record is added to the document.
You can set the record id in properties, but it must be a unique record ID for the file, otherwise it will cause an error. If the ID is not unique, the script will abort with an error message. Note: if you want to set the id, it must be set in properties when you create the record; it cannot be changed after the record is created.
You can create custom record types by setting the recordType property to a custom GEDCOM record type and using the class gedcomRecord. According to the GEDCOM standard, custom record types must begin with an underscore character (
). If the custom type does not begin with an underscore, it is an error and the script will abort with an error message. A sample script for a custom record is:_
p = {"recordType":"_MYCUSTOM" }
grec=gedit.classForScriptingClass_("gedcomRecord").alloc().initWithProperties_(p)
gdoc.gedcomRecords().addObject_(grec)
The moveTo_() command can move any record to an album (see below) and it will add that record to the album. You can also move many types of records to other records and it will mimic the drag and drop features of GEDitCOM II. In other words, moving a record is the same as dropping a record on the record browser of another record. Some examples are:
gdoc.individuals().objectWithID_("@I7@").moveTo_(gdoc.families().objectWithID_("@F27@"))
gdoc.sources()[2].moveTo_(gdoc.individuals().objectWithID_("@I41@"))
gdoc.multimedia().objectWithName_("John Smith Portrait").moveTo_(gdoc.individuals().objectWithName_("Smith, John, Jr."))
See the editing help in GEDitCOM II for each type of record to know which moves (analogous to drops) are supported. Some common moves are
You can delete a record by number, by name, or by id using the delete() command:
gdoc.individuals().objectWithName_("Smith, John, Jr.").delete()
gdoc.families()[3].delete()
gdoc.multimedia().objectWithID_("@M21@").delete()
You can locate records by enumerating through each record and testing its properties. For faster scripts, however, it is better to locate all records with certain properties by using the filteredArrayUsingPredicate_() command. For example, to find all individuals born in the 1920's, you could use:
sdns = gdoc.sdnRangeFullDate_("FROM 1920 TO 1929")
predFormat = "(birthSDN > "+sdns[0].stringValue()+") AND (birthSDNMax < "+sdns[1].stringValue()+")"
pred = NSPredicate.predicateWithFormat_(predFormat)
fetched = gdoc.individuals().filteredArrayUsingPredicate_(pred)
This single call will load all individuals with required birth dates into the new fetched array. You can locate records of any type by using any combination of conditionals based on the available properties for that type of record and numeric or string comparisons on those properties. The conditional is created using an NSPredicate object which needs a format string with the conditional. The syntax for the conditional string is fairly standard. For example, the above format string would be
(birthSDN > 2422325) AND (birthSDNMax < 2425976)
where the numbers are the SDN values for the date range provided to the sdnRangeFullDate_() command. You may need to consult Apple programming documentation for all the options. You should try to find records by this method whenever possible, because it is much faster then looping over records looking for matches.
There are three ways to read or change the data in GEDCOM records:
The different types of GEDCOM records have properties that can be "read" and "set." Any property listed in the GEDitCOM II dictionary can be read; those not specified as read only ("r/o") can also be changed. For example:
gindi=gdoc.individuals().objectWithID_("@I7@")
# read birth date
bd = gindi.birthDate()
# change the name
gindi.setName_("John /Smith/, Sr.")
Although only selected properties can be set using the above method, all other data can be edited using methods in the sections below. Also recall that when referring to the AppleScripting dictionary while writing Python or Rubt scripts, properties with more than one word need to remove the spaces and capitalize (if not already) the first letter of second and subsequent words (for example, the property in the dictionary "birth date" becomes "birthDate"). To set any property use "set" + property name (first letter now capitalized) + "_(new_value)" (for example, to set "name" property use "setName_(new_name)").
Most properties for each type of record are adequately defined in GEDitCOM II's dictionary. The following, however, require additional documentation:
contextInfo before using it. Although you could loop over all records and setContextInfo_(""), that method is inefficient. To set the property for all records to the same value, it is better to use the setValue_forKey_() command. For example, to use contextInfo of individuals, the script should start by clearing them all with
gdoc.individuals().setValue_forKey_("","contextInfo")
The companion valueForKey_() command can be used to load the same property from all records at once into an array. This method is much more efficient then reading the properties individually in a loop:
cis = gdoc.individuals().valueForKey_("contextInfo")
locked = 0x724C636B, privacy = 0x72507276, and unlocked = 0x72556E6C. Alternatively, you can use the GCConstant() utililty subroutine (which is in the module) and refer to them by name. With this subroutine, you refer to constants using GCConstant("locked"), etc., passing the case-sensitive name defined in the dictionary as a string.Warning: for speed, MacOS tools for scripting do not read all properties until they are needed. This issue is sometimes important in scripts. For example, the following Python script looks for a person's father:
fath = indi.father()
if not(fath) :
print("No father")
else
print("Has father")
The father property returns a reference to an individual's father or empty string if the person has no father. For speed, howeverm MacOS will initially set fath to a reference that can be evaluated later to find the father. The Python not(fath) check will therefore always be false because fath is initially something (it is an unevaluated reference). This code can be fixed two ways:
fath = indi.father().get(). This change forces evaluation of the property and fath will then be empty string if no father is found and not(fath) will correctly be true.fath == "". This changed conditional induces evaluation because the actual object is needed before it can be compared to anything.This issue does not arrise often. It only occurs for properties that return GEDitCOM II objects (such as reference to an individual record, etc.). It does not occur for properties that return standard variable types such as strings, numbers, or lists. The few properties to watch for are:
husband() and wife()father() and mother()contextInfo()nextStructure(), prevStructure(), and parentStructure().If you get unexpected results when using these properties, you may need to add a .get() to force evaluation of the property.
GEDCOM records are organized into GEDCOM structures. You can read any structure that is not hard-wired to a property by using the evaluateExpression_() command such as
cd = gdoc.individuals()[5].evaluateExpression_("CENS.DATE")
This command will read the date for the first census event in individual number 5 by using a GEDCOM expression. For more details on this command, see the evaluate command.
You can directly access GEDCOM structures by number or name (where name refers to the GEDCOM tag word for the structure) and then work with the properties of that structure. For example, to access and change the NCHI structure (or number of children, if it exists) of a family record, you can use:
nchi = gdoc.families().objectWithID_("@F128@").structures().objectWithName_("NCHI")
if nchi.exists() is True:
nchi.setContents_("4")
else:
print("No such structure")
Because you cannot use the structure fetched in the first line unless it already exists, it is a good idea to check if is available by using its exists() method. If it does exist, the setContents_() method sets its value; you can set the contents of any structure, where "contents" is the line value for the first line of the structure. You can also change the GEDCOM tag (using the setName_() method), but you cannot change the level number. Also note that contents should always be a string. Thus, in the above example, the contents must be set with "4" as a string. If it was set with setContents(4) with the number 4, it could corrupt the record (so be careful). If there is more than one structure with the same name, accessing it by objectWithName_() method will return the first one. The best way to access multiple structures, or to access structures without using objectWithName_() or number, is to use the findStructuresTag_output_value_() command.
GEDCOM structures themselves are organized into subordinate structures. You can access any substructure by number or objectWithName_() within the parent structure. For example, the divorce date of an family might be at
div = gdoc.families()[9].structures().objectWithName_("DIV")
if div.exists() == True:
dd = div.structures().objectWithName_("DATE")
if dd.exists() == True:
dd.setContents_("31 OCT 1940")
The above methods assume the structure you want to access already exists. If it does not exist, you may want to create it. You can create new structures and insert them at any location or even have them replace an existing structure:
deat = gdoc.individuals().objectWithID_("@I45@").structures().objectWithName_("DEAT")
p = {"name":"DATE","contents":"4 JUL 1776"}
ddate = gedit.classForScriptingClass_("structure").alloc().initWithProperties_(p)
deat.structures().addObject_(ddate)
birt = gedit.classForScriptingClass_("structure").alloc().initWithProperties_({"name":"BIRT"})
gdoc.individuals()[2].structures().insertObject_atIndex_(birt,3)
birt = gedit.classForScriptingClass_("structure").alloc().initWithProperties_({"name":"BIRT"})
gdoc.individuals()[2].structures().replaceObjectAtIndex_withObject_(2,birt)
You should always specify the name, or GEDCOM tag, for the structure in its properties when preparing a new structure. Once the structure is prepared, it can be added at the desired location. The first addObject_() method puts the structure at the end of the referenced record or structure. The second insertObject_atIndex_() method inserts it the specified index (indices are numbered starting with zero for the first one). The last replaceObjectAtIndex_withObject_() method replaces (so be careful) a previous structure with the new structure. Once the new structure is added to an existing record or structure (and not before), you can use the reference to change it or to add subordinate structures.
You can delete structures with the delete() command.
In AppleScripts, you can rearrange structures using the move, copy, or duplicate commands, but the same methods do not seem to work in Python scripts. The work around is to copy an existing structure, add it to a new location, and finally delete the original (for moves). For example, if gindi is reference to an individual where the surname prefix (SPFX) is mistakenly at level 1, it can be moved subordinate to the NAME structure where it belongs with:
src = gindi.structures().objectWithName_("SPFX")
p = {"name":src.name(),"contents":src.contents()}
dest = gedit.classForScriptingClass_("structure").alloc().initWithProperties_(p)
gindi.structures().objectWithName_("NAME").structures().addObject_(dest)
src.delete()
This method can move structures from one location to another including moves to then be subordinate to a different structure or even to be in a different record. When the new structure is inserted, it will automatically make sure the GEDCOM line level number is adjusted, if needed, for the new location.
Warning: when you move, add, or delete structures, you may invalidate previously defined references to structures in the same record. For example, if you have a reference to structure 5, but then delete structure 4, that reference will still reference structure 5, but structure 5 will now be a new structure (the previous structure 6). In brief, whenever adding or deleting structures, be careful of previously defined references that may now point to different data.
You can read and set the entire GEDCOM data for a record. Reading the GEDCOM data is safe and has many uses. Changing the GEDCOM data, however, might corrupt the data in your file and/or break existing linkages between records. It should only be used by those very familiar with the GEDCOM format and usually only for special applications. To change GEDCOM data for the individual with reference gindi:
recText = gindi.gedcom() (... use Python string methods to change recText) gindi.setGedcom_(recText)
Note this method can only set GEDCOM of GEDCOM records and not of GEDCOM structures. The first line of the new GEDCOM data must begin in "0" (to indicate it is a record), but the ID and record type in that first line will be ignored and replaced by values in the original record. In other words, this approach only changes subordoinate data within the record.
A user can restrict editing of record in GEDitCOM II using a GEDCOM RESN tag. Such a restriction can prevent editing of the record in GEDitCOM II, but does not block scripts in extensions from changing the data. A robust extension should either check for restrictions before changing any data, or document in extension help that it might change locked records.
Python scripts can check for restrictions using:
gdoc = gedit.documents()[0]
if gdoc.individuals()[0].restriction()!=0x724C636B :
# it is "unlocked", "privacy", or "confidential"
else :
# it is locked so do not change it
The restriction property returns any AppleScript code which for locked in 'rLck' and above example expresses that code using hexadecimal version. Note that only individual or family records have restriction property and can be locked. Python returns 0 for restriction property of any other record type and therefore they will correctly be treated as unlocked.
A common need in scripts is to work with dates in your file. The challenge in genealogy is that dates are often incomplete. For example you might know you great-grandfather was born in 1872 but not know the month or day. Similarly a date might be a month and a year (MAR 1842) or even a range of dates (BET 1900 and 1911). These "fuzzy" dates are confusing to many programming languages. The built-in date features of most programming languages expect all dates to be an exact date like "12 JUN 1949" and therefore do not know how to handle genealogy dates. Fortunately, GEDitCOM II scripting provides tools for working with "fuzzy" dates.
The basic task for all date calculations is to convert the date to a serial day number (SDN) which assigns an increasing number to all days with day 1 being a date around 4000 BC. When the date is a "fuzzy" date, the single SDN is represented by a range in day numbers spanning the earliest possible day represented by that date to the last possible day. For example the "fuzzy" date 1872 would correspond to the SDN range {2404794, 2405158} where 240474 is the SDN of 1 JAN 1872 and 2405158 is the SDN for 31 DEC 1872.
In date scripts, the first task is fetch SDNs or SDN ranges for dates in your file. Many of the most common dates are available as properties of records. Some examples are:
# individual birth and death dates
gindi = gdoc.individuals()[4]
bdmin = gindi.birthSDN()
bdmax = gindi.birthSDNMax()
ddrange = [gindi.deathSDN(),gindi.deathSDNMax()]
# family marriage date
gfam = gdoc.families()[1]
mrange = [gfam.marriageSDN(),gfam.marriageSDNMax()]
# get an event date
edmin = gindi.structures().objectWithName_("CENS").eventSDN()
The SDN property gets the minimum SDN for the given date; the SDNMax property gets the maximum SDN for the given date.
For dates not connected to a property or for dates just in a text string, you can use the sdnRangeFullDate_() command. You can combine two GEDitCOM II commands to get today's SDN:
sdns = gdoc.sdnRangeFullDate_(gdoc.dateToday())
which on 1 JAN 2010 returns {2455198, 2455198} (note that the SDN range for an exact date has the same minimum and maximum).
Once you have SDN's for dates, calculations are simple and can use standard scripting methods for working with numbers. For example:
gindi = gdoc.individuals()[4] ageAtDeath = (gindi.deathSDN()-gindi.birthSDN())/365.25 # age based on minimum SDNs minAge = (gindi.deathSDN()-gindi.birthSDNMax())/365.25 # minimum possible age maxAge = (gindi.deathSDNMax()-gindi.birthSDN())/365.25 # maximum possible age
To convert back to a date (using the user-preferred date style), you can use the dateTextSdn_() command:
gdoc.dateTextSdn_(2455032) # gives 19 Jul 2009
You can access existing albums in the document and then access records in that album or add new records to the album. A common task in scripts is to make a new album and then fill it with records:
p = {"name":"New Album"}
album=gedit.classForScriptingClass_("album").alloc().initWithProperties_(p)
gdoc.albums().addObject_(album)
# move record to an album
gdoc.individuals()[0].moveTo_(album)
You can delete albums with the delete() method.
Since albums can have more than one type of record, you have to access records by the generic record class or gedcomRecord:
album.gedcomRecords()[3].name() album.gedcomRecords()[4].delete()
Here the delete() command on a gedcomRecord will remove the record from the album, but will not delete it from the file.
GEDitCOM II has numerous built-in reports accessible through the reports menu. But you are not limited to those reports. By writing a custom script, you can define new reports and display them in a record browser the same way built-in reports are displayed. The following outline of a script creates a custom report:
# build report in a list and join to string at the end
rpt = ["<div>\n"]
rpt.append("<h1>My Custom Report</h1>\n")
...
(insert html commands for the report by appending to rpt)
...
rpt.append("</div>")
theReport = ''.join(rpt)
# display report in a report record
p = {"name":"My Custom Report","body":theReport}
newReport = gedit.classForScriptingClass_("report").alloc().initWithProperties_(p)
gdoc.reports().addObject_(newReport)
newReport.showBrowser()
The first section reads data in the file and combines anything you want into a list variable (here called rpt, hint: building the report in a list and then converting to a string at the end is much faster than building the report by concatenating strings using commands like rpt = rpt + "more text"). The formatting of a report for nice style is done using html commands. The report must start end end with <div> and </div> elements, respectively, to tell GEDitCOM II html content is being used. The report will use the same report.css cascading style sheet (stored as report.css in the "Formats" folder of the system application support folder) that built-in reports use. Thus, if you use standard html commands, your custom report will look similar to GEDitCOM II built-in reports.
One nice feature of html content is providing links to other web sites. An additional option in GEDitCOM II is that your report can provide links to open any record in the file. Links to records use this html style:
<a href='//@I128@'>Smith, John</a>
The link is a standard html "<a href=" link, but the reference must start with two slashes "//" (a requirement unexpectedly added by Apple MacOS that used to work without the slashes). The link is any record ID (which can be read with @rec.id where rec is a record's object name. The text inside the link is the clickable text that will appear in the report. Besides links to records or outside web sites, you can add internal links within the report by standard html methods. Such links are helpful in long reports.
You are not limited to loading reports into GEDitCOM II and then displaying in a record browser. You may prefer to send your report to any other scriptable Macintosh software (e.g., Pages or MS Word). When using this method, your script will use scripting options in the target software to store the report and format as desired. If the report is sent to word processing software, you can then directly open and edit more if desired. To write such a report, refer to the dictionary for the target application for options you have for loading the report text and setting styles for the report text. Note that scripts that target outside applications will only work on computers that have that application. In other words, if you write a script that sends a report to MS Word, it can only be used by people that have MS Word.
Some of the extensions provided with GEDitCOM II create custom reports. You can refer to documentation and scripts in thos extensions for more examples of creating reports. There are also posted tutorials about creating custom reports. When using Python and the GEDitCOMII.py module, you can make use of tools in that module to help creating reports.
While a script is running, it can display text to the user. The text will appear in the scripting panel that appears while a script is running. The panel can accomodate any amount of text in its scrolling text field and can be resized as desired. There are two ways to send text to the panel:
print command in Python) will send text to the script panel.When a script is done, if any standard text output was used, the panel will stay open for the user to read (and copy and paste if desired). The panel and the text output will stay visible until closed by the user or until another script is run. Thus standard text output commands (e.g., print) should be used for output you want to remain on the screen when the script is done. If all text output was through notifyProgressFraction_message_() commands, the panel will close when the script is done. Thus writing text using this command should be used for messages that are relevant while a long script is running, but no longer needed once the script is done.
After reading the above information on writing scripts and browsing all the available Scripting commands, the best way to proceed is to see some sample scripts. To see sample scripts, you can browse the on-line Scripting tutorials on geditcom.com. Utility methods (and much more) for Python are provided in the Python module for GEDitCOM II
Python scripts are a powerful and flexible tool that allows you to enhance GEDitCOM II in many ways. You will find, however, that scripting features of GEDitCOM II will be slower then built-in features. Scripts are ideal for changes to a few records (e.g., the currently selected records) or even for a global change you expect to only make a few times (e.g., changing the case on all names once). Although global changes may be slow, scripts will always be much faster and more reliable than doing all the changes by hand.
Scripts are less ideal for frequent global transformations of all the data in a file. The preferred approach for such complicated transformations is to use built-in features of GEDitCOM II. But, if you need complicated changes not already built-in to GEDitCOM II, a script can serve as a bridge until that feature is available. In fact, a script can be the prototype for a feature to be built into a future version of GEDitCOM II.
Finally, the speed of scripts will depend on how they are written. The two most important techniques for maximum efficiency in scripts are: