nscl_logo_small.gif (2463 bytes)

SpecTcl's interface to Xamine Client Buttons.

HH00706_.wmf (6530 bytes)

SpecTcl Home  General Information User Guide Programmer's Guide Obtaining and Installing

This page provides information about Xamine client buttons. In particular:

Top Programmer's guide

What Xamine Client buttons are

This section describes what Xamine Client buttons are, why you might want to use them and how they work at a high level. The intent is to give you the needed background to decide if you should create Xamine client buttons and, if so what it means to do so.

Before discussing Xamine Client buttons it is important to understand what is meant by Xamine. SpecTcl itself has no display component. SpecTcl merely creates histograms and makes them available to a separate program, called Xamine, that creates histogram displays.

Xamine and SpecTcl communicate in several ways:

While this division of labor allows Xamine to be a re-usable display program, it does have disadvantages. In particular, SpecTcl has no way of knowing exactly what Xamine is displaying and no direct way to prompt the user to click points into a spectrum and then get those points.

Xamine Client buttons allow you to circumvent some of these restrictions. Xamine Client buttons are a box of GUI buttons that are defined and displayed by Xamine's client (SpecTcl). The buttons are defined using the same message passing interface used to pass gates from SpecTcl to Xamine. When a button has been clicked in a valid way (we will discuss valid button clicks when we describe the Xamine Client Button API), Xamine uses the messages passing interface it normally uses to communicate gates to SpecTcl to instead communicate information about the button click.

To understand how to use Xamine client buttons from SpecTcl you must therefore understand how to use the Xamine Client Button API to create the client button box and to install the appropriate set of buttons in the box. You must also understand how to interface with SpecTcl's Xamine event processing subsystem to gain control when your buttons are clicked, so that you take the actions the buttons should initiate.

Top Programmer's guide

The Xamine Client Button API

This section describes the Xamine Client Button API. This is the set of functions the Xamine interface library provides for creating a client button box, stocking it with buttons and manipulating those buttons. Since SpecTcl has an event processing subsystem for handling messages from Xamine (including button click messages), we will not describe the Xamine interfaces for waiting for, checking for, or reading these events. The next section of this page will describe how to hook into SpecTcl's event processing framework so that you can process your button clicks.

The functions described in this section require that your code

#include <Xamine.h>

Xamine client buttons are a rectangular array of buttons in a top level button box. Create this button box by invoking:

int Xamine_DefineButtonBox(int ncol,int nrow);

Where:

ncol Is the number of columns of buttons supported by the box.
nrow Is the number of rows of buttons supported by the button box.
The function returns 0 if successful or some negative integer on failure. The failure reasons are defined in clientops.h in the SpecTcl include directory.

The button box defines a button coordinate system. The coordintes are invariably given as a row and a column. Rows number from the top and from zero. Columns number from the left and from zero.

Once you have created the button box you can define new buttons and modify existing buttons. This is done via the two functions:

int Xamine_DefineButton(int nrow, int ncol, void *button);
int Xamine_ModifyButton(int nrow, int ncol, void *button);
Where:

nrow and ncol Are the button box coordinates to be affected by the operation. For Xamine_DefineButton, a new button will be created at these coordinates. For Xamine_ModifyButton, the button already existing at those coordinates will be modified.
button Points to a button discription structure. We will describe this structure and its contents in a bit. The button created/modified is created/modified to match this description.

The button parameter in the functions described above is actually a pointer to a ButtonDescription. ButtonDescription is a struct hat describes a button to the button box. Buttons can also have a prompter. A prompter is a dialog that pops up to gather more information when the button is clicked. For example, a prompter may request the user to click some points off the spectrum, or confirm the choice. The prompter desired is also described in the ButtonDescription data type.

The ButtonDescription data type is defined in clientops.h which is included by Xamine.h The definition looks like:

typedef struct {                /* Describe a button. */

             int         button_code; /* Code to return on activation. */
             ButtonTitle label;       /* Text written in button        */
             ButtonType  type;        /* Type of button.               */
             Logical     state;       /* Initial toggle state          */
             Logical     sensitive;   /* Initial sensitivity state     */

                            /* Fields needed for prompters: */

             ButtonPromptType prompter; /* Additional data prompter type */
             ButtonSensitivity whenavailable; /* When button works      */
             ButtonDialogPrompt promptstr; /* Prompt string for dialog  */
             ButtonDialogSpectrumType
                               spectype; /* Type of spectra prompting for */
             Cardinal   minpts, maxpts; /* Number of desired points.   */
             } ButtonDescription;

While the comments in the definition above give you a good starting point to understand the meaning of each of these fields, each field is described in detail below as well.

Field Data Type Description
button_code integer This is a unique code that will identify the button in the event received from Xamine when the button has been used. The field is intended to allow user button event handlers to know which button a button event has come from.
label C string This null terminated string will be used to label the button. For pushbuttons, the string will appear on the face of the button, for checkbuttons, the label will appear near the checkbutton.
type ButtonType This is an enumerated type the describes the type of button to display. Legal values for this field are:
  • Push - A push button will be created.
  • Toggle - A toggle (or check box) button will be created
state Logical This field is only used for toggle button descriptions. If TRUE the toggle button is initially shown with the indicator lit (on). If FALSE the toggle button indicator is inially unlit (off).
sensitive Logical If TRUE the button is initially usable. That is a user can click get the button to send SpecTcl a button event by clicking it and, if necessary, responding appropriately to the prompter associated with the button. If FALSE the button is disabled and will not produce button events. The only way to switch the state of this is to use Xamine_ModifyButton on a previously existing button.
prompter ButtonPromptType Allows you to associate a prompter with the button. When the button is sensitive, if it is clicked and has a prompter, the associated dialog is displayed and the user must respond appropriately to it to cause a button event to be sent to SpecTcl. Prompters allow data be associated with the button event. Valid values for the prompter field are:
  • NoPrompt - No prompter is associated with the button
  • Confirmation - A confirmation (yes cancel) dialog is displayed. If the user confirms, a button event is sent, otherwise no button event is sent.
  • Text - The user is prompted for a text string which is passed to SpecTcl with the button event.
  • Points - The user is prompted for points from the current selected Spectrum. The point coordinates in spectrum coordinates are returned to SpecTcl with the button event.
  • Spectrum - The user is prompted for a spectrum via a spectrum chooser dialog. The Xamine id of the spectrum chosen is returned to the caller. The CHistogrammer::DisplayBinding() function can be used to obtain the Spectrum object associated with that ID note, however that the SpectTcl display binding id is one less than the Xamine Id.
  • Filename - A file chooser dialog will be displayed. The file selected or typed in will be passed to SpecTcl as part of the Button event.
whenavailable ButtonSensitivity Allows Xamine to determine the button sensitivity (whether or not it can be clicked) depending on the current selected pane. This can be one of the following values:
  • Anytime - The button will always be sensitive
  • InSpectrum - The button will only be sensitive if the active pane contains a spectrum
  • In1dSpectrum - The button will only be sensitive if the active pane contains a 1d Spectrum. Note that Xamine does not know anything about the finer distinctions of 1-d spectrum types provided by SpecTcl, thus this allows sensitivity for 1d, 1d gamma, stripchart and all other spectra displayed with a 1-d rendition.
  • In2dSpectrum - The button will only be sensitive if the active pane contains a 2d spectrum. Once again, Xamine knows nothing about the various types of 2-d spectra SpecTcl supports.
If the sensitive field of the description is false, this field is ignored. If the sensitive field of the description is true, it is modified by the value of wheavailable
promptstr ButtonDialogPrompt Provides a C null terminated string that will be placed in any prompter dialog that can be used to help the user know what is being asked for and why.
spectype This is used for spectrum prompters to potentially restrict the set of legal spectrum choices. It can be one of the following values:
  • Any - Any type of spectrum can be selected.
  • Oned - Only 1-d spectra can be selected.
  • Twod - Only 2-d spectra can be selected.
minpts,
maxpts
Cardinal This pair of fields is only used by a points prompter and specifies the minimum and maximum number of points that can be accepted. Currently, at most 50 points can be accepted by a points prompter.

The following are a set of additional Xamine client button API functions.

int Xamine_EnableButton(int r, int c);
int Xamine_DisableButton(int r, int c);
int Xamine_DeleteButton(int r, int c);
int Xamine_DeleteButtonBox();
int Xamine_InquireButton(int r, int c,
			 void  *ack);

The functions Xamine_EnableButton and Xamine_DisableButton are shortcuts for modifying the button by setting or clearing the sensitive field of the descriptor. r and c are the row and column coordinates of the button to modify.

Xamine_DeleteButton deletes a button at the button box row and column coordinates r and c.

Xamine_DeleteButtonBox destorys the button box and all buttons it contains. The client buttons will no longer be visible on the display.

Finally, Xamine_InquireButton requests information about the button at button box coordinates r and c. The ack parameters must be a pointer to storage that is at least sizeof(struct msg_InquireButtonAck). The data returned in this buffer will have the form of that structure which is:

struct msg_InquireButtonAck {	/* Button information... */
  ButtonCoordinate  size;	/* Size of the button grid... */
  Logical           valid;	/* Indicates if button queried was valid. */
  ButtonDescription button;	/* Info about the requested button */
};

Where:

Field Type Meaning
size ButtonCoordinate Is a struct with the fields column and row which, in this case provide the number of columns and rows in the button box.
valid Boolean Is a boolean that,when TRUE indicates that there is in fact a button a the coordinates r and c.
button ButtonDescription Describes the button. This field only has meaningful data if valid is true. For information about the ButtonDescription type, see the discussion in the documentation of Xamine_DefineButton function e.g.
Top Programmer's guide

Processing Xamine Button Events in SpecTcl

This section describes how to process Xamine events. This discussion will include

Background on SpecTcl's Xamine Event processing

SpecTcl works with the Tcl event loop to ensure that the user interface remains lively at all times, that event analysis gets scheduled, and that external inputs get mapped to Tcl events. In this case, Xamine's gate and messages are external inputs that get mapped to a Tcl Event that, in turn is dispatched to a CXamineEventHandler object that is held in the base class of the class you define in MySpecTclApp.cpp.

The CXamineEventHandler class is described in the header XamineEventHandler.h. The key points I want to point out are:

When a button event is detected, control eventually passes to the OnButton function of the CXamineEventHandler object. During the processing of a button event, the button handlers in the m_buttonHandlers are invoked in the order in which they were registered. Each button handler is given a chance to process the button event. If a button handler returns true OnButton calls no more button handlers.

This scheme is analagous to the event processing pipeline but, instead is intended to allow you to set up button handlers that cooperate to handle all possible button events.

The SpecTcl API for hooking into button events

We have already seen that the CXamineEventHandler object maintains a list of button handlers which are called in response to button events. A button handler is an object of a class that is derived from CXamineEventHandler::CButtonHandler. CXamineEventHandler::CButtonHandler is an abstract base class whose definition is nested inside the definition of CXamineEventHandler as follows:

  class CButtonHandler {
  public:
    virtual Bool_t operator()(CButtonEvent& event) = 0;
  };

The event parameter is a reference to the massaged button event that is being processed. It's header ButtonEvent.h shows that it exports the following key member functions:

class CButtonEvent      
{
  
public:

  Int_t getId() const;              // Get button id.
  Int_t getCurrentSpectrum() const; // Get Xamine id of selected spectrum.
  Bool_t getToggleState() const;    // Get state of toggle if toggle button.
  Int_t getPromptedSpectrum() const; // Get Xamine id of spectrum selected by user
  STD(string) getm_sPromptedString() const; // Get String user typed in prompter.
  const PointArray& getPoints() const; // Get points picked by user

  PointIterator begin ();           // Iterator to first user picked point.
  PointIterator end ();             // Iterator for end of user picked points.
  UInt_t size () const;             // # of user picked points.
  const CPoint& operator[] (UInt_t nI)  const; // Index into user picked points.
 
};

Button handlers are registered by invoking:

void CXamineEventHandler::addButtonHandler(CButtonHandler& handler);
This function is not a static function and therefore requires that you are able to obtain a pointer or reference to the CXamineEventHandler object.

Recall that in MySpecTclApp.cpp you define a class that is derived from CTclGrammerApp adding code to member functions as needed to set up your analysis. The displayer is setup in the SelectDisplayer member function. Normally this looks like:

void 
CMySpecTclApp::SelectDisplayer(UInt_t nDisplaySize, CHistogrammer& rHistogrammer)  
{ 
  CTclGrammerApp::SelectDisplayer(nDisplaySize, rHistogrammer);
}
Where the call to the base class SelectDisplayer sets up both Xamine and the Xamine event handler. The event handler object is saved as private member data in the base class and can be retrieved by calling the base class function getXamineEvents

For Example:

CMySpecTclApp::SelectDisplayer(UInt_t nDisplaySize, CHistogrammer& rHistogrammer)  
{ 
  CTclGrammerApp::SelectDisplayer(nDisplaySize, rHistogrammer);


  CXamineEventHandler* pEventHandler = getXamineEvents();

   // do pEventHandler->addButtonHandler calls for each handler object you want
   // to register.
   …
}

How to tie it all together

This section ties this all together by presenting code fragments that implement a button that accepts a pair of points on a 1-d spectrum and then performs a gaussian fit to the data between these points using the SpecTcl fit command.

We will start by creating a class CFitButton defined as follows:

class CFitButton : public CXamineEventHandler::CButtonHandler
{
private:
  int   m_FitButtonId;		// Ids assigned to buttons.

public:
  CFitButton(CXamineEventHandler* pHandler);
  virtual ~CFitButton();
private:
  virtual Bool_t operator()(CButtonEvent& event);

  STD(string) spectrumName(CButtonEvent& event);
  void        invokeScript(STD(string) script);

};

In this example, we are just going to show the implementations of the constructor and the function call operator (operator()). The other two member functions:

In the constructor, we must add the button to a button box that we assume already exists, and add ourself as an event handler using the pointer to the event handler that was passed in:

CFitButton::CFitButton(CXamineEventHandler* pEventHandler) 
{
  // Generic Gauss fit button.


  m_FitButtonId          = 1;

  ButtonDescription myButton;
  myButton.button_code   = m_FitButtonId;
  strcpy(myButton.label, "Gauss Fit");
  myButton.type          = Push;
  myButton.sensitive     = T;
  myButton.prompter      = Points;
  myButton.whenavailable = In1dSpectrum;
  strcpy(myButton.promptstr, "Fit limits"); 
  myButton.spectype      = Oned;            // I think this is ignored for this prompter
  myButton.minpts        = 2;
  myButton.maxpts        = 2;               // need exactly 2 pts.

  Xamine_DefineButton(0,0, &myButton);

  pEventHandler->addButtonHandler(*this);
  

}
As you can see the bulk of this code fills in the ButtonDescription struct myButton. Once filled in, the button is made at position 0,0 in the button box via the call to Xamine_DefineButton, and finally the object itself is registered as a button handler with the CXamineEventHandler object.

When a button event is triggered, we execute the following code:

Bool_t 
CFitButton::operator()(CButtonEvent& event)
{

  int buttonId = event.getId();


  if (buttonId == m_FitButtonId) {

    PointArray     pts           = event.getPoints();
    string spectrum              = spectrumName(event);

    string fitName;
    fitName       = event.getm_sPromptedString();

    // Now we can create the fit:

    char x1String[100];
    char x2String[100];

    string command = "fit ";
    command += fitName;
    command += ' ';
    command += spectrum;
    command += " ";
    
    sprintf(x1String, "%d", pts[0].X());
    sprintf(x2String, "%d", pts[1].X());

    command += x1String;
    command += ' ';
    command += x2String;
    command += " gaussian";

    invokeScript(command); 
  }
  else {
    return kfFALSE;
  }
  return kfTRUE;
}

The main point of this code is to get the information from the button event and build an appropriate SpecTcl fit command which is then executed to create the fit.

The only thing that remains is to create the button box, and create an CFitButton object in MySpecTclApp.cpp once the event handler has been set up. To do this we make SelectDisplayer look like this:

void 
CMySpecTclApp::SelectDisplayer(UInt_t nDisplaySize, CHistogrammer& rHistogrammer)  
{ 
  CTclGrammerApp::SelectDisplayer(nDisplaySize, rHistogrammer);

  CXamineEventHandler* pEventHandler = getXamineEvents();
  Xamine_DefineButtonBox(2,2);
  new CFitButton(pEventHandler);

}

There are a few subjects we have not touched on in this example. There's no error detection/processing in order to make this code as brief as possible. Even more seriously, if Xamine exists the button box and its buttons will not be re-established. If you are making production button boxes, it is important to know how to catch Xamine exits and re-create your button boxes. See Writing Xamine Restart Handlers for a description of how to do this.

Top Programmer's guide
fox@nscl.msu.edu
Last modified: Thu Apr 5 17:28:33 EDT 2007