Developing the QT GUI
The QT Gui is a simple GUI built to highlight and display Graphs.
Main Window
Main Window builds the Graphical Interface with all its components. It also has the event handlers for most GUI actions. The goal has been to separate the logic actually implementing the effects of the actions into the Main Controller. The Main Window class receives all interface actions and then marshals them to the Main Controller, the Main Controller then handles the internal data structures and takes care of any filter actions, etc. that needs to be implemented on the Graph.
Main Window inherits from QMainWindow and has a menu bar, a central widget containing the search area as well as the GraphView widget, and then three dockable widgets - two Filter Menus for Filters and Conditional Formatting and one Tree View for the tree perspective on the Graph. QT's Signals and Slots mechanism is used to communicate actions between the Filter Menus, the Tree View, the Graph View and the Main Controller.
Main Controller
The Main Controller manages the main Grapher data structure, the current Linkler in place as well as two Graph Filter List containing all filters for pre and post layout operations. Pre layout filters are applied directly on the Grapher data structure controlling which nodes get added to the Grapher and which doesn't, whereas the post layout filters are used when building the GUI data models of how and which nodes to display in the GUI.
The Main Controller also handles such activities such as loading an XML file from disk, creating a Linkler structure, or implementing the LinlerDiff actions in order to generate a difference view.
From the Main Controller the GUI receives QT data models which is what all other GUI components interfaces. The idea is that the data models is an abstraction from the Grapher so that the GUI components do not have to have intricate knowledge of the Grapher data structure. Using QT data models also enables the use of standard QT widgets which know how to deal with data delivered in the form of a QT data model. Using QT's standard data model structure and making the translation between our data structures and QT data structures also allows us to use such operations as QT's SortFilterProxies and other such built in features.
Data Models
There are currently two types of data models used by GraphTool:
- Graph Model
- Filter Model
Each are discrete, standardized representations ultimately inherted from QAbstractItemModel, implementing QT's Model-View framework.
Graph Model
This is a model representing a graph. Since QT's data models are based on a matrix this data model when representing a graph is implemented as a type of adjacency list. On the top level we have all the nodes in the graph. Then they have children representing all nodes which they have an edge to (leading to the tree basically only having two levels). The columns of the model are the various fields of the Graph Model.
The model is implemented by an overloaded class called GraphModel which has GraphModelItems as it's data bearing objects. GraphModelItems is the base class, and then in order to implement specific fields for specific types of Graphs there are child classes called SWUGraphModelItem and ModuleGraphModelItem. Currently SWUGraphModel is the only in use, but in case graphing of Modules is required that support is there fore the future.
The GraphModel's are constructed by methods buildTree
and buildGraph
. Using the buildGraph
you can get a data model representing the Graph in the way described above, as an adjacency list. Using the buildTree
method it is also possible to build a Tree which is rooted in a specific item, meaning that all child items of a specific item are generated. This is used for the Tree View to list the full Tree of dependencies starting from a single node. It will be a type of hierarchical graph or tree. Any time either of the methods find an item which they have already graphed they will add the item but stop iterating down the tree and simply mark the item as a circular
dependency. If we were not to do this we would find an infinite recursion of items.
It is in the buildGraph
method that post filters are executed, that is before the items are added to the model. This allows GraphModelItem properties to be set without touching the original data structure (the Grapher).
To decide which properties are available and published through the GraphModel, the GraphModel enum column_index
needs to be modified. Further the correct access of data need to be implemented in the data()
method call, either on the level of SWUGraphModelItem/ModuleGraphModelItem
, GenericGraphModelItem
or GraphModelItem
, depending on the level of specificity of the data (ie. at which level the data property used is available on - if only available for SWU's then naturally it needs to be implemented in the data() overload in SWUGraphModelItem
Filter Model
A model representing the sets of filters. Currently this is a standard StringList data model from the QT and there is a convenience class called FilterModel which provides static functions to implement various construction methods of this model. Each filter is generated at the top level, and its properties are child nodes to the filter. The MainController generates FilterModels from GraphFilterLists it has loaded. These filter models are provided to the FilterMenu which uses a QT tree view component to display them.
Changes in the GUI Filter Menus are transmitted back to the MainController via signals and slots. These are then translated by MainController back into GraphFilters and GraphActions and the correct GraphFilterList will be updated accordingly. These are then applied to the Graph and the appropriate GraphChanged
signal is emitted.
Graph Drawing
Graph Drawing is done using QT's QGraphicsView framework. We have three Graphical components - GraphView, inherting QGraphicsView, GraphScene, inheriting QGraphicsScene, and NodeWidget and EdgeWidget, both inherting QGraphicsItem.
The view provides the interface to the scene and controls what part of the scene is currently displayed, as well as handling GUI actions such as right click menus, signals to the rest of the UI (marshalled back to MainController by MainWindow). The GraphScene controls the full rendering of the current Graph, including colouring and which UI items are displayed or not. The method GraphScene::populateScene
method is used to build a scene of NodeWidgets and EdgeWidgets on the basis of a GraphModel (described above). The scene goes through the model, picks out properties which are then translated into NodeWidgets and EdgeWidgets. EdgeWidgets and NodeWidgets then have the appropriate methods to actually render the Graph items. By design NodeWidgets and EdgeWidget's have no specific knowledge of what items in the Graph they represent.
Important: the underlying assumption in this, though, is that the names of SWUs are primary keys and are used across the program. If this is not the case then this would need to be redesigned around some other primary ID.
Important Signals & Slots
MainController has three important signals it emits: * graphChanged - issued whenever the currently displayed graph has changed * postFilterModelChanged - issued whenever the post filter model applied to the graph has changed * preFilterModelChaned - issued whenever the pre filter model applied to the graph has changed