MGitWriteSparkline is an example of a file writer for the iTools system. It creates a PNG file representing a sparkline for vector data. Sparklines are intense, word-like graphics like Boulder temperature highs 55. Readers and writers are some of the simplest components to add to an iTool and a good place to start learning how to create custom iTools.

MGitWriteSparkline dialog

In order to run this demo, you will need the the MGitWriteSparkline class (doc), the calling program (doc), and the original sparklines routine (doc and see this article about the sparklines routine).

The calling program

The calling program, MG_SPARKWRITER_DEMO, is fairly simple.

iplot, randomu(seed, 50)
id = itGetCurrent(tool=oplot)
oplot->registerFileWriter, 'Sparkline writer', 'MGitWriteSparkline', $
                           description='PNG representation of a sparkline'

It creates an iPlot with some random data, gets the object reference for the iPlot, and registers the file writer with it.

Methods of MGitWriteSparkline

I’ll list all the methods of MGitWriteSparkline with simple descriptions. Most of these methods will be straightforward to write for those with experience with how properties work in the iTools or object graphics systems.

function mgitwritesparkline::init, _ref_extra=e
pro mgitwritesparkline::cleanup

The init method is responsible for registering and initializing properties of the class. This will be discussed in the properties section below. The cleanup method just calls the parent IDLitWriter::cleanup method since we don’t allocate any resources that need to be freed.

pro mgitwritesparkline::getProperty, width=width, height=height, $
                                     color=color, $
                                     transparent_background=transparent_background, $
                                     background=background, $
                                     use_range_band=use_range_band, $
                                     band_color=band_color, $
                                     endpoint_color=endpoint_color, $

pro mgitwritesparkline::setProperty, width=width, height=height, $
                                     color=color, $
                                     transparent_background=transparent_background, $
                                     background=background, $
                                     use_range_band=use_range_band, $
                                     band_color=band_color, $
                                     endpoint_color=endpoint_color, $

In the init, setProperty, and getProperty methods make sure to pass along keywords not expicitly declared to their corresponding IDLitWriter methods. We’ll discuss how to deal with the properties and their attributes in more detail in a section below.

function mgitwritesparkline::setData, oPlotData

This method is special for IDLitWriter subclasses and it is where the real work is done. We’ll discuss it in more detail in a section below.

Properties of MGitWriteSparkline

A property is created in the init method with registerProperty. The name of the property is the positional parameter argument while the NAME gives a human readable version. An optional second position parameter gives the data type of the property. If not present then one of the type keywords must be used. Here, the COLOR keyword indicates the type. Also, the SENSITIVE keyword indicates the user interface should not allow this property to be modified. (The aspects of the property here corresponding to these keywords are called attributes.)

self->registerProperty, 'background', name='Background', $
                        description='Background color of the plot', $
                        /color, sensitive=0

Later, in the getProperty method, a straightforward test using ARG_PRESENT indicates whether the stored value should be passed back. Here we have stored each property in an instance variable of the same name in the object.

if (arg_present(background)) then background = self.background

The setProperty method is more complicated because the value of the TRANSPARENT_BACKGROUND property indicates whether the BACKGROUND property is needed (and hence if it can be set in the user interface). The relevant part for our property BACKGROUND is:

if (n_elements(transparent_background) gt 0) then begin
  self.transparent_background = transparent_background
  self->setPropertyAttribute, 'background', $
if (n_elements(background) gt 0) then self.background = background

We use the setPropertyAttribute method to change attributes like SENSITIVE or HIDE.

MGitWriteSparkline::setData method

Finally, the setData method is responsible for getting an iTools data object and creating the output file. The filename of this file is found by using the getFilename method:

filename = self->getFilename()
if (filename eq '') then return, 0

The return value for this function is a status code: 0 for failure, 1 for success.

if (~obj_valid(oPlotData)) then begin
  self->errorMessage, ['Invalid plot data object'], title='Error', $
  return, 0

The oPlotData parameter is an iTools data object containing the data to be written (i.e. selected by the user in the writing dialog). Here, only type IDLVECTOR data can be used to create a sparkline.

odata = oPlotData->getByType('IDLVECTOR', count=nplots)
if (nplots eq 0) then begin
  self->errorMessage, ['Invalid data provided to file writer'], $
                      title='Error', /severity

The odata return value of the getByType method could contain an array of data objects. Here, we’ll only consider the first one, pulling the data out of it with the getData method.

odata = odata[0]
result = odata->getData(y)
if (result eq 0) then begin
  self->errorMessage, ['Error retrieving plot data'], $
                      title='Error', severity=2

The y variable now holds the data to be written by the mg_sparkline routine.

mg_sparkline, filename, y, xsize=self.width, ysize=self.height, $
              color=self.color, $
              background=keyword_set(self.transparent_background) $
                ? undefined $
                : self.background, $
              band_color=keyword_set(self.use_range_band) $
                ? self.band_color $
                : undefined, $

The undefined variable is actually undefined, i.e. n_elements(undefined) eq 0. It is needed here to indicate that we are not passing a value to a particular keyword.

return, 1B

Finally, return 1B to indicate success.