[2ad1ec] | 1 | /*
|
---|
| 2 | * Project: MoleCuilder
|
---|
| 3 | * Description: creates and alters molecular systems
|
---|
| 4 | * Copyright (C) 2010 University of Bonn. All rights reserved.
|
---|
| 5 | * Please see the LICENSE file or "Copyright notice" in builder.cpp for details.
|
---|
| 6 | */
|
---|
| 7 |
|
---|
| 8 | /**
|
---|
| 9 | * \file qt-gui.dox
|
---|
| 10 | *
|
---|
| 11 | * Created on: Jan 5, 2012
|
---|
| 12 | * Author: heber
|
---|
| 13 | */
|
---|
| 14 |
|
---|
| 15 | /**
|
---|
| 16 | * \page qt-gui Qt GUI
|
---|
| 17 | *
|
---|
| 18 | * The Qt GUI is the most advanced interface and thus the most complex.
|
---|
| 19 | *
|
---|
| 20 | * In the following we want to explain some of the details that are involved.
|
---|
| 21 | *
|
---|
[52c5d4] | 22 | * \section qt-gui-general General Concepts
|
---|
| 23 | *
|
---|
| 24 | * Let us first discuss about the general concepts.
|
---|
| 25 | *
|
---|
| 26 | * MoleCuilder is about atoms, bonds and the molecules made up by them. But
|
---|
| 27 | * there is more: There are fragments, potentials, shapes, and so on.
|
---|
| 28 | *
|
---|
| 29 | * In the Qt GUI all of these are displayed in certain areas of the screen
|
---|
| 30 | * and also in a certain manner:
|
---|
| 31 | * -# the 3D view represents a three-dimensional representation of all atoms,
|
---|
| 32 | * and their bonds or possibly the molecules they form alone. Also the
|
---|
| 33 | * bounding box is shown and all selected shapes. Atoms or molecules can
|
---|
| 34 | * be selected by clicking. The view can be manipulated through rotation
|
---|
| 35 | * and translation.
|
---|
| 36 | * -# an element list shows all available elements of the period table.
|
---|
| 37 | * -# a molecule list shows all present molecules sorted by their formula.
|
---|
| 38 | * -# a fragment list shows all fragments with their energies and contributions
|
---|
| 39 | * -# a potential list shows all currently instantiated potentials and
|
---|
| 40 | * gives a 2D plot.
|
---|
| 41 | * -# a shape list displays all currently available shapes, allows to select
|
---|
| 42 | * them and buttons allow to combine them via boolean operation.
|
---|
| 43 | * -# an info box informs about the current atom/molecule the mouse pointer
|
---|
| 44 | * is hovering over.
|
---|
| 45 | *
|
---|
| 46 | * So, there are many objects that need to be filled with information and
|
---|
| 47 | * they need to access the World and other singletons in order to obtain
|
---|
| 48 | * this information.
|
---|
| 49 | *
|
---|
| 50 | * One major obstacle, or rather THE major obstacle, is that Qt is threaded,
|
---|
| 51 | * i.e. the Actions are processed in one thread and the Gui does its event
|
---|
| 52 | * processing in another one. Qt's Signal/Slot system is handled via this
|
---|
| 53 | * event system, i.e. a signal launched by one thread may be handled by
|
---|
| 54 | * the slot function in another thread. The Observer/Observable system
|
---|
| 55 | * of the CodePatterns which we used internally/outside Qt's scope does
|
---|
| 56 | * not do this.
|
---|
| 57 | *
|
---|
| 58 | * Also, signals may get delayed. This can happen either deliberately, e.g.
|
---|
| 59 | * there is a QTimer that only updates an object in regular intervals, or
|
---|
| 60 | * because of asynchronous threads. Elsewhen, the slot callback for a
|
---|
| 61 | * certain signal is called directly. For all of these cases we have to
|
---|
| 62 | * accommodate. This is especially problematic with the instantiation and
|
---|
| 63 | * destruction of objects.
|
---|
| 64 | *
|
---|
| 65 | * A clarifying example: Imagine an atom is constructed, the AtomObserver
|
---|
| 66 | * notifies about it, but the information is not processed immediately.
|
---|
| 67 | * Shortly after, the atom is destroyed again before its representation is
|
---|
| 68 | * instantiated in the GUI. Afterwards the GUI attempts to instantiate it
|
---|
| 69 | * but can not longer access the atom for its position and element.
|
---|
| 70 | *
|
---|
| 71 | * The only possible way out is to duplicate information. This is the usual
|
---|
| 72 | * way how to deal with environments with multiple threads. I.e. all the
|
---|
| 73 | * information that the GUI representants of information inside the World
|
---|
| 74 | * needs to be doubled such that when the original information is destroyed
|
---|
| 75 | * the representant can still be accessed as long as needed.
|
---|
| 76 | *
|
---|
| 77 | * \subsection qt-gui-general-observedvalue Observed Value
|
---|
| 78 | *
|
---|
| 79 | * These representants are called \a ObservedValue in CodePatterns and they
|
---|
| 80 | * are used everywhere in the Qt Gui.
|
---|
| 81 | *
|
---|
| 82 | * They contain an internal information, e.g. a boolean, a Vector or even
|
---|
| 83 | * a complex structure such as a Tesselation. They require an updater
|
---|
| 84 | * function to obtain the derived information from the original source. And
|
---|
| 85 | * they signOn to the source in order to be notified either generally on
|
---|
| 86 | * updates or for specific channels only.
|
---|
| 87 | *
|
---|
| 88 | * The ObservedValue will automatically and immediately update its internal
|
---|
| 89 | * representation of the derived information by calling the updater function
|
---|
| 90 | * as soon as it has been informed about the update. Hence, the internal
|
---|
| 91 | * information is always up-to-date and lives beyond the scope of the
|
---|
| 92 | * source of the information until its own destruction. As updates are
|
---|
| 93 | * processed immediately, this pattern only makes sense for "small" pieces
|
---|
| 94 | * of information, i.e. when the updater function is very light-weight and
|
---|
| 95 | * does not do much in terms of using computing resources.
|
---|
| 96 | *
|
---|
| 97 | * Note that there is another concept that is opposite to the observed value,
|
---|
| 98 | * namely the Cacheable. This pattern will update itself only when requested,
|
---|
| 99 | * referred to as "lazy evaluation". Hence, this pattern is used for "large"
|
---|
| 100 | * pieces of information that require more computing resources within the
|
---|
| 101 | * updater. Also, the Cacheable's information can only be obtained as long
|
---|
| 102 | * as the source of information is still alive.
|
---|
| 103 | *
|
---|
| 104 | * Both concepts can be used in threaded environments as mutexed are used to
|
---|
| 105 | * protect read and write accesses.
|
---|
| 106 | *
|
---|
| 107 | * \subsection qt-gui-general-signalslot Observer/Observable and Signal/Slot
|
---|
| 108 | *
|
---|
| 109 | * In the following we refer to Observer/Observable as "O/O" and to Signal/Slot
|
---|
| 110 | * as "S/S".
|
---|
| 111 | *
|
---|
| 112 | * One thing we need to do is to translate between update() or
|
---|
| 113 | * recieveNotification() calls from an Observable and subsequent signal/slot
|
---|
| 114 | * calls. The general idea is to use these ObservedValues as translation
|
---|
| 115 | * points for small pieces of information and Cacheables for larger pieces.
|
---|
| 116 | *
|
---|
| 117 | * However, we need more of these translation points:
|
---|
| 118 | * -# GLWorldView checks for
|
---|
| 119 | * -# World's MoleculeInserted
|
---|
| 120 | * -# World's SelectionChanged
|
---|
| 121 | * -# WorldTime's TimeChanged
|
---|
| 122 | * -# each molecule's AtomInserted and AtomRemoved
|
---|
| 123 | * -# AtomObservable's AtomChanged.
|
---|
| 124 | * -# ShapeRegistry's ShapedAdded, ShapeRemoved, and SelectionChanged
|
---|
| 125 | * -# GLMoleculeObject_molecule checks for
|
---|
| 126 | * -# molecule's AtomInserted, AtomRemoved, AtomMoved, IndexChanged
|
---|
| 127 | * -# World's SelectionChanged
|
---|
| 128 | *
|
---|
[2ad1ec] | 129 | * \section qt-gui-qt3d Qt3D and the way to get atoms and bonds displayed
|
---|
| 130 | *
|
---|
[52c5d4] | 131 | * By far the most difficult component of the Qt GUI is the 3D view. So,
|
---|
| 132 | * let us explain it in detail.
|
---|
| 133 | *
|
---|
| 134 | * The general widget making up the view is called \a GLWorldView. It contains
|
---|
| 135 | * the GLWorldScene (i.e. all atoms, bonds, molecules, and shapes). Also
|
---|
| 136 | * the "dreibein" and the domain. It processes key presses and mouse events
|
---|
| 137 | * to manipulate the view. And it also serves as the translator O/O to S/S
|
---|
| 138 | * system.
|
---|
| 139 | *
|
---|
| 140 | * The GLWorldScene contains the actual nodes of the molecular system, i.e.
|
---|
| 141 | * the atoms, bonds, molecules, and shapes. All of these are derived from
|
---|
| 142 | * GLMoleculeObject and have their parent to the instance of the GLWorldScene
|
---|
| 143 | * which goes through its list of children and to call draw() on them.
|
---|
| 144 | *
|
---|
| 145 | * The bottom-most structure is GLMoleculeObject_atom displaying a sphere
|
---|
| 146 | * of an element-specific color at the atom's position. The atom relies
|
---|
| 147 | * on its representants to be contain all required information but it
|
---|
| 148 | * is also signOn() to the atom itself whose O/O are translated to S/S
|
---|
| 149 | * for processing whenever desired.
|
---|
| 150 | *
|
---|
| 151 | * Next comes the GLMoleculeObject_bond which displays a cylinder between
|
---|
| 152 | * two atoms. Actual, a true bond consists of two of these objects. If the
|
---|
| 153 | * bond is between heterogeneous atoms each half will be displayed in the
|
---|
| 154 | * color of the closer atom. These bond objects are not associated with
|
---|
| 155 | * the atoms directly as the are linked to two atoms at the same time. They
|
---|
| 156 | * rely on ObservedValues for position and element of either atom and for
|
---|
| 157 | * the degree of the bond itself.
|
---|
| 158 | *
|
---|
| 159 | * Parallel to these are GLMoleculeObject_shape which display the surface
|
---|
| 160 | * of a selected shape. A shape in general does not change after instantation,
|
---|
| 161 | * hence the shape lives with the information it gets on instantiation till
|
---|
| 162 | * it dies.
|
---|
| 163 | *
|
---|
| 164 | * Finally, the GLMoleculeObject_molecule owns both atoms and bonds. This
|
---|
| 165 | * allows for switching the view between the classical ball-and-stick model
|
---|
| 166 | * and the tesselated surface of the molecule. The latter uses a lot less
|
---|
| 167 | * triangles and thus is faster. Also, it is especially suited for large
|
---|
| 168 | * molecules. The molecule also needs ObservedValues for its bounding box
|
---|
| 169 | * (used to show when it's selected), the index, the selection status,
|
---|
| 170 | * and the list of atom ids. As Cacheable we use the tesselation structure.
|
---|
| 171 | *
|
---|
| 172 | * \section qt-gui-cases Sample cases
|
---|
| 173 | *
|
---|
| 174 | * Let us discuss some cases and how the different instances interact.
|
---|
| 175 | *
|
---|
| 176 | * \section qt-gui-cases-start Start
|
---|
| 177 | *
|
---|
| 178 | * When molecuilder is started, several singletons such as the World and
|
---|
| 179 | * others are instantiated. No atoms are yet present, no bonds, no molecules.
|
---|
| 180 | * Hence, nothing to display yet.
|
---|
| 181 | *
|
---|
| 182 | * Before launching any Action the ActionQueue is forced to wait till the
|
---|
| 183 | * GUI is finished instantiating. This is to ensure that GLWorldView and
|
---|
| 184 | * others are in place to receive signals from the O/O system.
|
---|
| 185 | *
|
---|
| 186 | * When a molecule is loaded, the instantiation of a GLMoleculeObject_molecule
|
---|
| 187 | * does not happen immediately. Hence, GLWorldView listens to the World's
|
---|
| 188 | * MoleculeInserted. On receiving it, it also signOn()s to the molecule
|
---|
| 189 | * to get its subjectKilled(). It translates then these and also all
|
---|
| 190 | * AtomInserted and AtomRemoved to the S/S system as moleculeInserted,
|
---|
| 191 | * moleculeRemoved and atomInserted/atomRemoved respectively, which are
|
---|
| 192 | * processed by the GLWorldScene.
|
---|
| 193 | *
|
---|
| 194 | * The GLWorldScene records any atomInserted/atomRemoved until the molecule
|
---|
| 195 | * has been instantiated. On instantiation all recorded events are played.
|
---|
| 196 | * This is to ensure that there is no overlap in instantiation and signOn()
|
---|
| 197 | * to the molecule. If we would simply get all atoms which are present
|
---|
| 198 | * on processing the molecule's instantiation we might stumble over a signal
|
---|
| 199 | * of a molecule of a just added atom. This occurs frequently as both
|
---|
| 200 | * are very much correlated.
|
---|
| 201 | *
|
---|
| 202 | * GLWorldView keep track of all ObservedMolecules. And GLWorldScene keeps
|
---|
| 203 | * track of all shapes and molecules in the scene. Each
|
---|
| 204 | * GLMoleculeObject_molecule in turn keeps track of all atoms and bonds in
|
---|
| 205 | * its part of the scene.
|
---|
[2ad1ec] | 206 | *
|
---|
[eee1b7] | 207 | * \section QtElementList
|
---|
| 208 | *
|
---|
[52c5d4] | 209 | * Lists for each element how often it occurs in the world. Selecting an entry
|
---|
[eee1b7] | 210 | * calls SelectionAtomByElementAction to select all atoms of that particular
|
---|
| 211 | * element.
|
---|
| 212 | *
|
---|
[52c5d4] | 213 | * Initially, it fills itself by looking at all elements in the World's
|
---|
| 214 | * periodentafel. It also listens to AtomObserver's ElementChanged to know
|
---|
| 215 | * when to update a certain element in its list. By using an internal list
|
---|
| 216 | * for each atom's element, it can update each element's occurrence.
|
---|
[eee1b7] | 217 | *
|
---|
| 218 | * \section QtMoleculeList
|
---|
| 219 | *
|
---|
| 220 | * Lists all the molecules currently in the world grouped by their formula.
|
---|
| 221 | * Selecting an entry calls the SelectionMoleculeByIdAction.
|
---|
| 222 | *
|
---|
[52c5d4] | 223 | * The QtMoleculeList is also a rather complex beast. It is a tree of
|
---|
| 224 | * rows and each row consists of a number of elements. There are two
|
---|
| 225 | * levels, the group level where the common formula for all molecules
|
---|
| 226 | * is given, and the molecule level where are molecules of this specific
|
---|
| 227 | * formula are summarized.
|
---|
| 228 | *
|
---|
| 229 | * The group items are QStandardItems. Sadly, they are not derived from
|
---|
| 230 | * QObject and hence do not use the S/S system. The group items are
|
---|
| 231 | * directly controlled by the QtMoleculeList.
|
---|
| 232 | *
|
---|
| 233 | * However, the molecule items are different. They are derived from
|
---|
| 234 | * QtMoleculeList and use an ObservedValue internally to contain an always
|
---|
| 235 | * valid copy of the required information. They inform the QtMoleculeList on
|
---|
| 236 | * updates via a callback (as QStandardItem, from which they are also derived,
|
---|
| 237 | * does not use the S/S system). The callback takes care of then also updating
|
---|
| 238 | * the group items and possibly moving the molecule items around, e.g. if
|
---|
| 239 | * their formula has changed they suddenly belong to another group.
|
---|
| 240 | *
|
---|
| 241 | * All items are instantiated by the QtMoleculeItemFactory.
|
---|
| 242 | *
|
---|
| 243 | * QtMoleculeList uses an internal QTimer to only update itself at regular
|
---|
| 244 | * intervals. Hence, updates are processed rather lazily. We keep lists
|
---|
| 245 | * of changes, separated for group and molecule items. And these are processed
|
---|
| 246 | * one after the other at the intervals dictated by the QTimer in
|
---|
| 247 | * updateItemStates().
|
---|
[eee1b7] | 248 | *
|
---|
| 249 | * \section QtShapeController
|
---|
| 250 | *
|
---|
| 251 | * This is the interface for the ShapeRegistry. It lists all the shapes in the
|
---|
| 252 | * registry and lets the user select them. It also features buttons to call
|
---|
| 253 | * actions creating and manipulating the selected shapes.
|
---|
| 254 | *
|
---|
| 255 | * As an Observer it handles the following messages from ShapeRegistry:
|
---|
| 256 | * - ShapeRegistry::ShapeInserted
|
---|
| 257 | * - ShapeRegistry::ShapeRemoved
|
---|
| 258 | * - ShapeRegistry::SelectionChanged
|
---|
| 259 | *
|
---|
| 260 | * \section QtInfoBox
|
---|
| 261 | *
|
---|
[8c9049] | 262 | * Shows information about the atom and molecule the cursor is currently hovering
|
---|
| 263 | * over inside the GLWorldView.
|
---|
[eee1b7] | 264 | *
|
---|
[8c9049] | 265 | * GLWorldView emits hoverChanged signals (via QT's signal slot mechanism) which
|
---|
| 266 | * the QtInfoBox receives. QtInfoBox then creates its info pages for the atom
|
---|
| 267 | * being transmitted as the signal's parameter.
|
---|
[eee1b7] | 268 | *
|
---|
[8c9049] | 269 | * The info pages are Observers for the atom/molecule. When recieving subjectKilled
|
---|
| 270 | * they automatically clear the info box.
|
---|
| 271 | *
|
---|
[52c5d4] | 272 | * \date 2015-07-15
|
---|
[2ad1ec] | 273 | */
|
---|