Adding a GUI window

There are several steps needed to add a new built-in GUI window to VisIt.

  1. Create a state object
  2. Generate the window code for the state object
  3. Connect the new window in the VisIt GUI

State object

The procedure for adding a new state object is covered on the State objects page. To summarize, one uses the XMLEdit tool to create a description of the state that the state object will contain. Once saved, the XML representation of the state object can be used to autogenerate C++, Java, Python code that will be hooked into VisIt in various places to ensure that VisIt can transmit the state object between components.

Generate window

The XML state object description produced by XMLEdit can be used to autogenerate Qt window source code that can be compiled into VisIt's GUI. The autogenerated source code will observe the state object that was added to the ViewerState and update the widgets in the window to the values stored in the state object when the state object changes. When the user interacts with the new window, the window will translate the user actions and input into state object method calls that set the values within the state object.

To create the new window, use the xml2window code generation tool.

xml2window -clobber MyNewStateObject.xml

The above will generate 2 new files: QvisMyNewStateObjectWindow.C and QvisMyNewStateObjectWindow.h, which are the C++ sources for the new window. The new window will inherit from the QvisPostableWindowObserver class from the VisIt GUI's class hierarchy. This base class is appropriate for windows which will observe a single state object and it will have the usual Apply, Reset, Make Default buttons at the bottom of the window. If you do not want those buttons in your window or if you need to observe multiple state objects, you may want to change your class declaration so it instead inherits from QvisPostableWindowSimpleObserver. That class does not observe state objects by default so you'll have to Attach/Detach the state objects yourself. That class also enables you to pass various flags to its constructor which determines which buttons will be automatically added to the window.

The xml2window program will generate the code according to heuristics where enum fields become radio buttons, strings will use line edit controls, bool fields will use check boxes, and so on. The layout of the generated window is relatively simple with labels on the left and the controls for a field on the right. The label text comes from the label field attribute in the XML file -- so supply labels and make them descriptive.

The generated code may not be particularly attractive so many VisIt windows started life as autogenerated code from xml2window and then were hand-altered to provide better window layout and group related items into group boxes. This is relatively straightforward to do if you modify the code in the generated window's CreateWindowContents method.

Connect the new window

Connecting the new window in the VisIt GUI takes a small amount of effort when you know the steps. The code is not set up to allow general window plugins so it is necessary to hand-edit some of the main files to add the new window. In all of these examples, we'll use the window name of XXX, though you'd replace that name with the proper name of your new window.

Adding a menu option

The menus in the VisIt Main window are all created in a class called QvisMainWindow. In the VisIt GUI, there is a single instance of QvisMainWindow and it communicates the intent to open other windows by emitting a Qt signal. By convention, the name of the signal is activateXXXWindow() where XXX is the name of the window to be opened. The code in QvisMainWindow that creates the top-level menu inserts a new action into the menu and connects the action with the window's method to activate the window.

Add a new signal in QvisMainWindow.h in the signals section of the class declaration:

    void activateXXXWindow(); // Replace XXX with the right name

In the source file where menu creation is performed (in the QvisMainWindow constructor), add a new menu action and connect it to the new signal. There are different menus where you can add your new window: File, Controls, Options, Windows, Help. Decide which top level menu is most appropriate for the functionality that you are adding. Most things belong in Controls.

// Adding an XXX option to the controls menu that will cause the
// window to emit a signal to activate the new window. Replace XXX
// with the right name!
ctrls->addAction(tr("XXX . . ."),
                 this, SIGNAL(activateXXXWindow()));

Create and connect the new window

The main class in VisIt's GUI is QvisGUIApplication. This class sets up everything, including the various windows. When windows need to communicate amongst themselves, they usually do so through QvisGUIApplication. In this case, the QvisMainWindow has a new signal to activate a new window. We want to connect that signal to QvisGUIApplication so that when we select the new menu option, the new window gets created and opens up.

Changes to QvisGUIApplication.h

  1. Add a showXXXWindow() slot function declaration. This slot will be called in response to the QvisMainWindow's signal to open the new window.

Changes to QvisGUIApplication.C

  1. Include your window's header file so the new class will be defined
  2. Define a WINDOW_XXX macro that will serve as the ID for your window. Be sure to use the next ascending value (there are other WINDOW_ macros)
  3. Append a name for the new window to the windowNames list in the QvisGUIApplication constructor. This name is used for the window caption.
  4. Connect the main window's activateXXXWindow() signal to the new showXXXWindow() slot.
  5. Create an instance of your window in the WindowFactory() method
  6. Define the body of the showXXXWindow() slot function

The first step looks like this:

#include <QvisXXXWindow.h>

When adding the WINDOW_ macro for your new window, look in the QvisGUIApplication.C file around line 230. You'll see a list of macro definitions for the other windows that QvisGUIApplication knows about. Be sure to use the next largest value (e.g. if the last one was 34, you'd use 35).

#define WINDOW_XXX  35

In the QvisGUIApplication constructor, there is code to initialize a string list called windowNames. You'll want to add an entry to the end of the list for the name of your new window. Note that the tr() function is used for internationalization.

windowNames += tr("XXX");

In the QvisGUIApplication::SetupWindows() method there are a lot of calls to the connect method to hook up the main window's signals to slots in QvisGUIApplication. We'll add a new one to make sure that when the main window wants to open the new window, the QvisGUIApplication will be responsible for opening the new window.

connect(mainWin, SIGNAL(activateXXXWindow()),
        this, SLOT(showXXXWindow()));

QvisGUIApplication has a method called WindowFactory() that lets it create an instance of a window based on the ID of a window. We already added a new ID for the window in the form of the WINDOW_XXX macro. We want to extend WindowFactory so when WINDOW_XXX is passed in, we create an instance of the new window type. This is done by adding a new case into the switch statement. Since the autogenerated window was set up to observe a state object, and we want it to observe the new state object that we added to ViewerState, we call GetViewerState()->GetXXXAttributes() to get a pointer to the new state object (again, substitute the right name for XXX). Next, we pass windowNames[i] as the name and short name for the window. Finally, we pass a pointer to the Main window's notepad area so the new window can post into that notepad area. Note that we set the new window pointer into the win pointer, which will be returned from this factory method. This method is called on demand when we need to create the new window.

case WINDOW_XXX:
  {
     win = new QvisXXXWindow(GetViewerState()->GetXXXAttributes(),
                             windowNames[i], windowNames[i], 
                             mainWin->GetNotepad());
     // Do any other window initialization or signal/slot connections here.
  }
  break;

Finally, we need to implement the showXXXWindow() slot function that will be called when the Main window wants to open the new window. The bodies for the various window show methods are at the bottom of QvisGUIApplication.C.

void QvisGUIApplication::showXXXWindow() { GetInitializedWindowPointer(WINDOW_XXX)->show(); }