7. Internals of xskill

The xskill program is written in Python using the Tkinter GUI (graphical user interface) widget set. Refer to the Python help page for general information about Python and Tkinter.

The xskill application is written in Python using Cleanroom intended functions. For more about Cleanroom, read Dr. Allan M. Stavely's book Toward Zero-Defect Programming, or see John Shipman's cleanroom pages.

The applications has these principal steps:

  1. The command line arguments are processed by instantiating an Args object. Command line options include:

    • -p peoplefile: For testing purposes, you can use this option to specify an alternate people file.

    • -m matrixfile: For testing purposes, you can use this option to specify an alternatematrix file.

    • If a positional argument is given, it must be the login name of an employee in the people file, and the user must be in the tccadmin net-group.

  2. We attempt to check out the knowledge matrix file from RCS, with a lock, using the rcslib.py module. If this fails, the program terminates.

  3. The people file and the knowledge matrix database are read by instantiating the People object and the Skills object, respectively.

    If there are errors in either of these files, the program terminates.

  4. The global variable forPerson is set to a Person object that represents the person whose skills are being updated.

  5. The SkillsApp object is instantiated; this object contains all the Tkinter code. Then its .mainloop() method is invoked to actually run the X application.

  6. If the X application raises SystemExit, the matrix file is checked in to RCS without changes, which will release the lock.

  7. If the X application terminates normally, the matrix file is rewritten in place and then checked back in to RCS.

7.1. The Args object

The Args object is located in source file xskillobjs.py. This object represents the command line arguments passed to the program, and its constructor checks these arguments for validity.

Exports from this object include:

7.2. The SkillsApp object

This object holds the actual Tkinter part of the application. Its exports include:

Widgets in the root window of this application include:

The work of extracting the skills and formatting them as a series of pages is done by a SkillsPagination object, which contains a list of Frame widgets representing the pages. See the SkillsPagination object.

Important note: All widget placements within it should use the .grid geometry manager, not the Packer or Placer. Failure to observe this requirement will cause your application to hang with no messages.

7.3. The SkillsPagination object

This object represents all the headings and skills from a Skills object, only they are broken into page-sized chunks represented by a list of SkillsPage objects.

Exports include:

7.4. The SkillsPage object

This object represents one page's worth of skill radiobuttons and their associated headings. Exports:

7.5. The NodeFactory object

The purpose of this object is to encapsulate all information about the size and appearance of widgets to be placed in a SkillsPage object.

This object uses the Singleton pattern (see Gamma et al., Design Patterns); that is, the first caller will get a new instance, but all successive callers will get the same instance. The NodeFactory() function is a wrapper for the _NodeFactory object that implements Singleton.

Exports include:

7.6. The PageTurner object

This object is a Tkinter widget designed to present a series of frames as if they were pages of a book, along with `Next page' and `Prev page' buttons to allow the user to page forward and backward through the list. This object is basically a container class for Frame widgets, referred to as child frames. It was written for the xskill application, but may be useful in its own right.

To use this widget:

  1. Instantiate it in some parent window. Let's call this instance PT.

  2. If you would like to place additional widgets between the Next page/Prev page widgets and the page body, you can create them mastered to PT.headFrame and place them using .grid().

  3. Create each child frame f as a Frame widget mastered to PT.bodyFrame, then add it using the PT.addPage(f) method. Do not grid these widgets; they will be gridded later.

  4. Use the PT.setPageNo(n) method to display the nth page (counting from 0). If you don't call this method, the first page will be displayed as soon as it is added.

Exports include:

There is one important trick embodied in this object. The child frames are mastered to self.bodyFrame, but they are not gridded. The .grid() method is used to make a page appear, and the .grid_forget method is used to erase it.

7.7. The xml_people.py module

This module represents the ``people file'' defining all current TCC employees. It contains three objects:

7.8. The xml_skills.py module

This module contains a number of objects to represent the ``skills matrix;'' see Matrix file format for the format of this file.

Since the file is in XML format, the module uses Python's xml.dom.minidom module to access the XML elements.

Contents include:

7.9. The rcslib.py module

Here is the README file for this module, which comes from an older Python distribution, from a directory called Demo/pdist.

RCS module for Python - rcslib.py
---------------------------------

Module rcslib.py comes from the Python source distribution,
directory "Demo/pdist".  It allows Python programs to perform
many common RCS function.

Warning: RCS functions are invoked through pipes using
os.popen(), so their output will go to stdout.

Usage
-----

+--
| import rcslib
|   ...
| rcs = rcslib.RCS()
+--

Constructor.  Only one instance is needed.  The return object
represents the RCS files in the current directory (or in its
./RCS/ subdirectory).

+--
| rcs.log ( name_rev, otherflags='' )
+--

Returns the entire log file as a single string.  Arguments:

*   name_rev:  Identifies the file to be checked out.  This
    argument may be either:

    -   a string, treated as the file name of the working file
        in the current directory; or

    -   a tuple (name,rev) where name is the name of the working
        file and rev is the revision number as a string (e.g.,
        "1.14").

*   otherflags: If given, this string is passed intact as
    additional arguments to the "rlog" command.


+--
| rcs.head ( name_rev )
+--

Returns the head revision number for name_rev as a string.  The
name_rev argument is the same as in the .log() method.


+--
| rcs.info ( name_rev )
+--

Returns a dictionary of information from "rlog -h".  Here is
an example of the return value:

    {'access list': '', 'total revisions': '3',
     'RCS file': 'RCS/testfile,v', 'Working file': 'testfile',
     'branch': '', 'symbolic names': '', 'head': '1.3',
     'locks': 'strict', 'keyword substitution': 'kv'}

+--
| rcs.lock ( name_rev )
+--

Sets a lock on name_rev using "rcs -l".


+--
| rcs.unlock ( name_rev )
+--

Clears a lock on name_rev using "rcs -u".


+--
| rcs.checkout ( name_rev, withlock=0, otherflags='' )
+--

Checks out a file.  Arguments:

*   withlock:  If 1, the file is checked out with a lock.

*   otherflags: If a string is provided, that string is added as
    command line arguments to the "co" command.


+--
| rcs.checkin ( name_rev, message=None, otherflags='' )
+--

Checks in a file.  Arguments:

*   message: The text of the check-in message.  If this is the
    initial check-in, it becomes the file description.

*   otherflags: If a string is provided, that string is added as
    command line arguments to the "ci" command.


+--
| rcs.listfiles ( pat=None )
+--

Returns a list of the working file names for files under RCS
control.


+--
| rcs.isvalid ( name )
+--

Predicate: Does the file with the given name have a version file?


+--
| rcs.rcsname ( name )
+--

Returns the relative pathname of the version file for the working
file with the given name.


+--
| rcs.realname ( namev )
+--

If namev is either a working file name or a version file name,
return the working file name.

+--
| rcs.islocked ( name_rev )
+--

Predicate: is name_rev locked?  Works only if name_rev has a
version file.


+--
| rcs.checkfile ( name_rev )
+--

Raises an exception of there is no version file for name_rev.

7.10. Library modules used

The xskill application uses a module from the author's standard Python library:

For more information about this module, see the documentation in file /u/john/tcc/python/lib/scan.tex.