Difference between revisions of "Slicer3:UIEngineering"

From Slicer Wiki
Jump to: navigation, search
m (33 revisions)
 
(11 intermediate revisions by one other user not shown)
Line 5: Line 5:
 
=== GUI architecture and interface to application layer ===
 
=== GUI architecture and interface to application layer ===
  
Our goal is to create a thin GUI layer for Slicer3 whose architecture allows the flexible expression of interfaces that are appropriate for Slicer’s different use scenarios: an end-user application graphical user interface; script-driven batch processing and unit testing; and command-line style interaction for launching pipeline/grid processing, debugging during development, ''etc''.
+
Our goal was to create a thin and stateless GUI layer for Slicer3 whose architecture allows the flexible expression of interfaces that are appropriate for Slicer’s different use scenarios: an end-user application graphical user interface; script-driven batch processing and unit testing; and command-line style interaction for launching pipeline/grid processing, debugging during development, ''etc''.
 
 
Slicer2's GUI code is quite dense, mixed in with application control logic and data model. There is no way to separate the gui from the rest of the Slicer2 application which limits the ability to perform automated tests of modules and makes development and maintenance of software more challenging. To meet our goal, the Slicer3 architecture requires a thoughtful translation of existing Slicer2 modules' functionality, and won't be as simple as dropping in a new set of widgets. The aim is to provide a framework that makes this task as easy as possible for Slicer developers while separating the Slicer3 GUI code from the application layer.
 
  
 
The schematic below shows the Slicer3 GUI layer design and its interface with the application layer (which includes the logic layer and the data model as shown). The design isolates the application layers from GUI code, allowing them to be invoked by the Slicer3 desktop application and GUI (Slicer3), or a custom application with or without a graphical user interface, or which may use a completely different GUI toolkit.
 
The schematic below shows the Slicer3 GUI layer design and its interface with the application layer (which includes the logic layer and the data model as shown). The design isolates the application layers from GUI code, allowing them to be invoked by the Slicer3 desktop application and GUI (Slicer3), or a custom application with or without a graphical user interface, or which may use a completely different GUI toolkit.
Line 17: Line 15:
 
The GUI's visual style is being designed to reflect Slicer3 brand. Developers are encouraged to adhere to style guidelines to maintain a consistent look and feel across core and custom modules and to make Slicer3 easier to learn. The style is defined and described by the Tk Options Database configured by a Slicer3 class (vtkSlicerTheme.h/cxx). These options can be overridden in an module's GUI, if a developer finds this necessary, by redefining new Tk options for widgets defined there.
 
The GUI's visual style is being designed to reflect Slicer3 brand. Developers are encouraged to adhere to style guidelines to maintain a consistent look and feel across core and custom modules and to make Slicer3 easier to learn. The style is defined and described by the Tk Options Database configured by a Slicer3 class (vtkSlicerTheme.h/cxx). These options can be overridden in an module's GUI, if a developer finds this necessary, by redefining new Tk options for widgets defined there.
  
=== How to add a Slicer module (MyModule) GUI class ===
+
=== Architecture of a Module GUI ===
 
+
A schematic showing classes from which a developer's module GUI are derived is shown below. For more detailed information about adding a module GUI to Slicer, see: [[Slicer3:Interface_Design#First_steps:_How_to_build_a_Slicer3_Module_GUI | How to build a Module GUI.]]
First, note that the GUI base classes may be refactored as Slicer develops and the procedure for adding your module into Slicer will also be simplified; it's recommended that you check documentation periodically for information that may be useful.
 
 
 
The figure below shows an overview of the three classes a new module (called MyModule for example) will probably need to define:
 
 
 
*vtkSlicerMyModuleGUI,
 
*vtkSlicerMyModuleLogic,
 
*vtkMRMLMyModuleNode
 
 
 
and some of the methods those classes should include to utilize the Slicer3 infrastructure. These classes should reside in the slicer3/Modules/MyModule/ directory.
 
 
 
[[Image:Slicer3MyModule.png|[[Image:Slicer3MyModule.png| Base classes for GUI, Logic and MRML ]]]]
 
 
 
'''Deriving your GUI class:''' To create a Module GUI that expresses its interface in Slicer's shared UIpanel, derive your class (vtkSlicerMyModuleGUI) from vtkSlicerModuleGUI. For a Module GUI that expresses its interface in a different panel of the Main Slicer Window, or in a toplevel widget, derive vtkSlicerMyModuleGUI from vtkSlicerComponentGUI instead. Your GUI class will inherit application logic and MRML pointers, an API for setting and observing them, and a framework for defining MRML, Logic and GUI callbacks from its parent class. Specific mediator methods, other logic and MRML node pointers can be added to the module's class definition. Importantly, keep logic and MRML classes independent of the GUI to facilitate testing and command-line execution possible *without* instantiating the Slicer3 GUI.
 
 
 
'''Example:''' An early example to work from is the GradientAnisotropicDiffusionFilter (GAD) Module; define all the widgets you need within the class and create Get Macros for each of them. Define the methods you need from vtkSlicerComponentGUI, including BuildGUI(); in this method, you'll first add a page to the class's UIPanel (for now, only create one page). Different logical sections of your interface like "Parameterize" or "Display options" (which were located in separate notebook tabs in Slicer2) will be located in instances of the vtkKWFrameWithLabel widget, and packed into the UIPanel page. This approach produces a vertical stack of collapsing frames in the UIPanel instead of horizontally arrayed tabs. The "Help&About" frame should be located first (at the top) in the stack of your module GUI's set of collapsing frames; a method to help create this frame in a consistent manner is inherited from the vtkSlicerModuleGUI base class. In your derived module's BuildGUI() method, after adding your module's page to the UIpanel, define a text string for 'help', and one for 'about' if you wish to make acknowledgements, and call the BuildHelpAndAboutFrame(page, helpstr, aboutstr).
 
 
 
'''Methods to define:''' Define the methods you require from vtkSlicerComponentGUI base class, like: AddGUIObservers(), RemoveGUIObservers(), ProcessLogicEvents(), ProcessGUIEevents(), ProcessMRMLEvents(), Enter() and Exit(); and whatever else your module needs. (Eventually, available modules will be automatically detected, but this is not yet implemented; then, the Enter() method will probably be made to call the BuildGUI() method. For now, instantiate your class in Slicer3.cxx and call its BuildGUI() and other methods, following the pattern for other modules established there.)
 
 
 
'''Adding observers:''' In AddGUIObservers, add an observer on each widget whose events you want to process. When an event is observed, the ProcessGUIEvents() method is called via the GUICallbackCommand; define this class to propagate information from the GUI to logic and MRML. Though it is tempting, try not to use ProcessGUIEvents() to update the GUI state directly -- just modify the Logic and MRML state, and allow observers on the logic and MRML to trigger subsequent processing in your ProcessLogicEvents() and ProcessMRMLEvents() methods to bring that state change BACK into the GUI. To prevent infinite looping, check to see if the GUI and Logic/MRML parameters are out of sync, and only update the GUI if necessary.
 
 
 
'''Removing observers:''' In RemoveGUIObservers, make sure you remove every observer you've added to widgets in the GUI before calling Delete() on your widget. Make sure you call SetAndObserveMRML() and SetAndObserveLogic( ) with NULL pointers in your GUI class destructor to remove all observers on MRML and Logic that you have created.
 
 
 
'''Defining your own widgets:''' Currently there are two types of new widgets, those defined as extensions to vtkKW (like vtkKWWindowLevelThresholdEditor.h/cxx) and those defined as Slicer-specific widgets, (like vtkSlicerSliceControlWidget.h/cxx, derived from the vtkSlicerWidget.h/cxx base class). The Slicer widgets have methods for putting observers on their widget components, Logic and MRML, and processing events as well. Thus GUI classes that instance them do not have to manage events for them if the widgets' methods are used instead.
 
 
 
'''Undo:''' Make sure you process those events that mark junctures at which MRML state should be saved for Undo/Redo (using MRML's SaveStateForUndo() method. For instance, when an entry widget's value has changed, before changing a parameter in the appropriate MRML node, make a call to the MRMLScene's SaveStateForUndo() method with that node as a parameter. Save MRML state at reasonable junctures: for instance, for scale widgets, save MRML state when the scale starts changing, rather than continuously as the scale changes. For an example of they way SaveStateForUndo() is called, see slicer3/Base/GUI/vtkSlicerSliceControllerWidget.cxx.
 
 
 
'''Keyboard accelerators (hotkeys):''' Before defining hot-keys for your module, please check the table of global hotkeys, and other module-specific assignments [[Slicer3:EventBindings| (Slicer3 Event Bindings)]]. If a key is already globally assigned, it may not be re-assigned in your module. If other modules are using a key for similar functionality, try to use the same key in your module too, for consistency.
 
 
 
'''GUI Style:''' Try not to add style elements (like foreground and background color, font, relief, etc.) to the interface you create; let the options database (as set up by the vtkSlicerTheme class) specify the style for you so that all modules appear consistent within the Slicer3 application.
 
 
 
'''Files you will have to touch:''' At the current time, to incorporate your module into Slicer, you need to take a number of steps:
 
* create your own CMakeLists.txt file and your own vtkMyModuleWin32Header.h file following the pattern set by other Modules.
 
* add your module SUBDIR to the CMakeLists.txt file in the Slicer3/Modules directory above.
 
* to add your module to Slicer3, create a new instance of vtkSlicerMyModuleLogic and vtkSlicerMyModuleGUI in Slicer3/Applications/GUI/Slicer3.cxx, and follow the pattern used by other modules, like the GradientAnisotropicDiffusionFilter module.
 
* include relevant .h files in Slicer3/Applications/GUI/Slicer3.cxx, and
 
* specify your module's source and binary Include directories in Slicer3/Application/GUI/CMakeLists.txt
 
* and include your module in the CMakeLists.txt target link libraries
 
 
 
=== Current status and TO-DO ===
 
  
The existing base classes form a basic framework for the UI. For the Alpha deadline, we provided a main application window with the general layout described in our current design. For the Beta deadline, we are focusing on:
+
[[Image:Slicer3MyModule.png | Module base classes and their methods ]]
  
* clean up vtkDebug Leaks
 
* copyright existing icons and logo
 
* working on improving the Theme to enhance module GUI appearance and readability,
 
* implementing functionality for view control widgets,
 
* implementing lightbox viewer
 
  
 +
=== Independent stateless GUI layer ===
  
 +
An important design features is the restriction of application state to the MRML and Logic classes; the GUI layer is completely independent of these two application layers. This stateless GUI layer offers many benefits. For one, any MRML scene may be deleted or loaded and the GUI will automatically track that scene; for another, Slicer's full functionality is available even when Slicer operates without the GUI layer, or with another GUI entirely. For more information about how this design is accomplished, see: [[Slicer3:StatelessGUI | Keeping the GUI layer independent and stateless ]].
  
 
''' Return to TOC '''
 
''' Return to TOC '''
  
 
[[Slicer3:Interface_Design|Return to Slicer3 Interface Design and Usability ]]
 
[[Slicer3:Interface_Design|Return to Slicer3 Interface Design and Usability ]]

Latest revision as of 17:53, 15 May 2008

Home < Slicer3:UIEngineering

Return to Slicer3 Interface Design and Usability

Slicer3 GUI layer architecture and engineering

GUI architecture and interface to application layer

Our goal was to create a thin and stateless GUI layer for Slicer3 whose architecture allows the flexible expression of interfaces that are appropriate for Slicer’s different use scenarios: an end-user application graphical user interface; script-driven batch processing and unit testing; and command-line style interaction for launching pipeline/grid processing, debugging during development, etc.

The schematic below shows the Slicer3 GUI layer design and its interface with the application layer (which includes the logic layer and the data model as shown). The design isolates the application layers from GUI code, allowing them to be invoked by the Slicer3 desktop application and GUI (Slicer3), or a custom application with or without a graphical user interface, or which may use a completely different GUI toolkit.

User input to the GUI layer modifies parameters in the MRML scene and Logic classes, and updates and executes pipelines. So that the GUI can accurately reflect application state, it maintains observers on the MRML scene, MRML nodes, and Logic objects; when the application state encapsulated in the parameters of those objects changes, events are generated, then observed and processed by the GUI to update its widgets' transient state (like entry widgets or scale values, and rendered image output).

The GUI's visual style is being designed to reflect Slicer3 brand. Developers are encouraged to adhere to style guidelines to maintain a consistent look and feel across core and custom modules and to make Slicer3 easier to learn. The style is defined and described by the Tk Options Database configured by a Slicer3 class (vtkSlicerTheme.h/cxx). These options can be overridden in an module's GUI, if a developer finds this necessary, by redefining new Tk options for widgets defined there.

Architecture of a Module GUI

A schematic showing classes from which a developer's module GUI are derived is shown below. For more detailed information about adding a module GUI to Slicer, see: How to build a Module GUI.

Module base classes and their methods


Independent stateless GUI layer

An important design features is the restriction of application state to the MRML and Logic classes; the GUI layer is completely independent of these two application layers. This stateless GUI layer offers many benefits. For one, any MRML scene may be deleted or loaded and the GUI will automatically track that scene; for another, Slicer's full functionality is available even when Slicer operates without the GUI layer, or with another GUI entirely. For more information about how this design is accomplished, see: Keeping the GUI layer independent and stateless .

Return to TOC

Return to Slicer3 Interface Design and Usability