source: src/documentation/constructs/qt-gui.dox@ 3aa204

Action_Thermostats Add_AtomRandomPerturbation Add_RotateAroundBondAction Add_SelectAtomByNameAction Adding_Graph_to_ChangeBondActions Adding_MD_integration_tests Adding_StructOpt_integration_tests AutomationFragmentation_failures Candidate_v1.6.1 ChangeBugEmailaddress ChangingTestPorts ChemicalSpaceEvaluator Docu_Python_wait EmpiricalPotential_contain_HomologyGraph_documentation Enhance_userguide Enhanced_StructuralOptimization Enhanced_StructuralOptimization_continued Example_ManyWaysToTranslateAtom Exclude_Hydrogens_annealWithBondGraph Fix_ChronosMutex Fix_StatusMsg Fix_StepWorldTime_single_argument Fix_Verbose_Codepatterns ForceAnnealing_goodresults ForceAnnealing_oldresults ForceAnnealing_tocheck ForceAnnealing_with_BondGraph ForceAnnealing_with_BondGraph_continued ForceAnnealing_with_BondGraph_continued_betteresults ForceAnnealing_with_BondGraph_contraction-expansion GeometryObjects Gui_displays_atomic_force_velocity IndependentFragmentGrids_IntegrationTest JobMarket_RobustOnKillsSegFaults JobMarket_StableWorkerPool PythonUI_with_named_parameters QtGui_reactivate_TimeChanged_changes Recreated_GuiChecks RotateToPrincipalAxisSystem_UndoRedo StoppableMakroAction TremoloParser_IncreasedPrecision TremoloParser_MultipleTimesteps Ubuntu_1604_changes
Last change on this file since 3aa204 was 0824dd, checked in by Frederik Heber <heber@…>, 9 years ago

Enhanced documentation for Qt constructs.

  • Property mode set to 100644
File size: 18.0 KB
Line 
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 *
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. All of these cases we have to be
62 * accommodated for. This is especially problematic with the instantiation and
63 * destruction of objects that represent atoms and molecules in the World.
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 proceed in 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. Here, we use
76 * the ObservedValue construct of CodePatterns.
77 *
78 * \subsection qt-gui-general-observedvalue Observed Value
79 *
80 * These representants are called \a ObservedValue in CodePatterns and they
81 * are used everywhere in the Qt Gui.
82 *
83 * They contain an internal information, e.g. a boolean, a Vector or even
84 * a complex structure such as a Tesselation. They require an updater
85 * function to obtain the derived information from the original source. And
86 * they signOn to the source in order to be notified either generally on
87 * updates or for specific channels only.
88 *
89 * The ObservedValue will automatically and immediately update its internal
90 * representation of the derived information by calling the updater function
91 * as soon as it has been informed about the update. Hence, the internal
92 * information is always up-to-date and lives beyond the scope of the
93 * source of the information until its own destruction. As updates are
94 * processed immediately, this pattern only makes sense for "small" pieces
95 * of information, i.e. when the updater function is very light-weight and
96 * does not do much in terms of using computing resources.
97 *
98 * Note that there is another concept that is opposite to the observed value,
99 * namely the Cacheable. This pattern will update itself only when requested,
100 * referred to as "lazy evaluation". Hence, this pattern is used for "large"
101 * pieces of information that require more computing resources within the
102 * updater. Also, the Cacheable's information can only be obtained as long
103 * as the source of information is still alive. However, so far Cacheable's
104 * content is marked invalid when an update signal has been received and
105 * update itself only on request, which is no longer possible when the object
106 * to represent is gone. Hence, after destruction the information is no longer
107 * accessible, not even in the Cacheable, at the current moment.
108 *
109 * Both concepts can be used in threaded environments as mutexes are used to
110 * protect read and write accesses.
111 *
112 * \subsection qt-gui-general-observedinstance The observed instance board
113 *
114 * The setup is then as follows: We have two distinct realms, the World (with
115 * atoms and molecules) on the one side and the QtGUI (with visual
116 * representations of atoms and molecules) on the other side.
117 *
118 * There is an interface between this world such that the destruction of an
119 * atom does not directly invalidate its visual representation. This interface
120 * between the two realms is contained in the class QtObservedInstanceBoard,
121 * which is a singleton and is similar to the World instance in the World realm
122 * for the QtGui realm.
123 *
124 * All properties, e.g. the position of an element, relevant to the QtGUI are
125 * duplicated as ObservedValues. Properties associated to the same instance in
126 * the World, e.g. the same atom, are combined into a QtObservedAtom instance,
127 * and similarly QtObservedMolecule for molecule. All of these observed
128 * instances are placed into ObservedValuesContainer which are contained in
129 * the interface QtObservedInstanceBoard.
130 *
131 * The sequence of events is then as follows (here exemplified with an atom):
132 * -# an atom is created (World::createAtom()), the World notifies about it
133 * via its World::AtomInserted channel.
134 * -# QtObservedInstanceBoard is signOn()ed to this channel and instantiates
135 * a new QtObservedAtom which is placed into its respective
136 * ObservedValuesContainer.
137 * -# on instantiation of QtObservedAtom a vector of specific ObservedValues is
138 * created, one for each property (position, element, bond count, ...).
139 * Each signOn()s to the respective atom's channel. Also the QtObservedAtom
140 * signOn()s to each of these channels as it converts these notifications
141 * into Qt signals (and the updated value can be accessed via getters from
142 * the QtObservedAtom instance). The QtObservedInstanceBoard is notified
143 * of this with the instance being marked as connected.
144 * -# when the atom is destroyed (World::destroyAtom()), being an Observable
145 * it will call subjectKilled() on all its channels. The
146 * ObservedValue_wCallback announces this subjectKilled() via the callback
147 * function which notifies QtObservedAtom. Once all subjectKilled(), for
148 * each observed value and for QtObservedAtom itself, have been received,
149 * the QtObservedInstanceBoard is notified by the instance now being
150 * marked as disconnected and ready for erase.
151 * -# then the QtObservedInstanceBoard removes the instance from its
152 * ObservedValuesContainer.
153 *
154 * Note however that the instance is a shared_ptr and will continue to exist
155 * and therefore its getters will still deliver the last piece of information
156 * before the atom was destroyed until all shared_ptrs are released. Hence,
157 * the QtGui may safely continue using the pointer.
158 *
159 * As new observed instances may come in immediately having the same id and
160 * as it is difficult to keep track who got its observed instance already
161 * and who not, a soft fail is required. I.e. of the QtObservedInstanceBoard
162 * returns an empty shared_ptr this means that the object -- despite any
163 * received (and probably delayed) signal -- has been destroyed and should
164 * not be displayed, updated by, ... whatsoever.
165 *
166 * \subsection qt-gui-general-signalslot Details on the slot connections
167 *
168 * Qt's event system does not guarantee that events are always processed in
169 * the order they are emitted. This is because connections can be defined
170 * as direct or queued (or both with auto). Direct connections will always
171 * be executed as direct function calls, i.e. immediately. Queued connections
172 * however are inserted into Qt's event queue and may even get processed by
173 * a different thread.
174 *
175 * We have to take care of this.
176 *
177 * Basically what we do in QtObservedInstanceBoard and the observed instances
178 * of type QtObservedAtom and QtObservedMolecule is that we translate between
179 * the Observer/Observable (O/O) system of CodePatterns with its callback
180 * functions and the event system of Qt with its Signal/Slot (S/S).
181 *
182 * That is in the recieveNotification() functions many "emit()"s can be found.
183 *
184 * Furthermore, signals are used in a specific way to ensure synchronicity.
185 * This is only a problem with the visual representation as we there find a
186 * a nested problem: First molecules are created, then atoms belonging to a
187 * certain molecule. And on destruction it is the other way round. This
188 * enforces a certain sequence of events and thus of signals.
189 *
190 * \subsubsection qt-gui-general-signalslot-glworldscene Details on GLWorldScene
191 *
192 * The central place for all events is the GLWorldScene instance. There
193 * signals from QtObservedInstanceBoard for insertion and removal of both atoms
194 * and molecules are caught. Insertion of molecules is dealt with directly,
195 * we sign on to the inserted&removed channels for its atoms, then we emit
196 * a queued signal to actually instantiate the GLMoleculeObject_molecule.
197 *
198 * Until its instantiation we store incoming signals in the
199 * GLWorldScene::MoleculeMissedStateMap, protected by a mutex to enforce atomic
200 * access. After it has been instantiated (and all stored signals have been
201 * processed), they are relayed onto the specific instance. However, we do not
202 * do this via emits but by directly using Qt's invokeMethod() which allows
203 * to trigger queued events. This way it is done in a likewise manner whether
204 * it has been a stored or live signal that was received.
205 *
206 * \subsubsection qt-gui-general-signalslot-other Details on other signals
207 *
208 * All other signals that only change the property of a visual representation,
209 * e.g. the element of an atom, is directly processed by, in this case, the
210 * GLMoleculeObject_atom connected to QtObservedAtom.
211. *
212 * \section qt-gui-qt3d Qt3D and the way to get atoms and bonds displayed
213 *
214 * By far the most difficult component of the Qt GUI is the 3D view. So,
215 * let us explain it in detail.
216 *
217 * The general widget making up the view is called \a GLWorldView. It contains
218 * the GLWorldScene (i.e. all atoms, bonds, molecules, and shapes). Also
219 * the "dreibein" and the domain. It processes key presses and mouse events
220 * to manipulate the view. And it also serves as the translator O/O to S/S
221 * system.
222 *
223 * The GLWorldScene contains the actual nodes of the molecular system, i.e.
224 * the atoms, bonds, molecules, and shapes. All of these are derived from
225 * GLMoleculeObject and have their parent to the instance of the GLWorldScene
226 * which goes through its list of children and to call draw() on them.
227 *
228 * The bottom-most structure is GLMoleculeObject_atom displaying a sphere
229 * of an element-specific color at the atom's position. The atom relies
230 * on its representants to be contain all required information but it
231 * is also signOn() to the QtObservedAtom itself whose O/O are translated to
232 * S/S for processing whenever desired.
233 *
234 * Next comes the GLMoleculeObject_bond which displays a cylinder between
235 * two atoms. Actual, a true bond consists of two of these objects. If the
236 * bond is between heterogeneous atoms each half will be displayed in the
237 * color of the closer atom. These bond objects are not associated with
238 * the atoms directly as the are linked to two atoms at the same time. They
239 * rely on ObservedValues for position and element of either atom and for
240 * the degree of the bond itself.
241 *
242 * Parallel to these are GLMoleculeObject_shape which display the surface
243 * of a selected shape. A shape in general does not change after instantiation,
244 * hence the shape lives with the information it gets on instantiation till
245 * it dies.
246 *
247 * Finally, the GLMoleculeObject_molecule owns both atoms and bonds. This
248 * allows for switching the view between the classical ball-and-stick model
249 * and the tesselated surface of the molecule. The latter uses a lot less
250 * triangles and thus is faster. Also, it is especially suited for large
251 * molecules. The molecule also needs ObservedValues for its bounding box
252 * (used to show when it's selected), the index, the selection status,
253 * and the list of atom ids.
254 *
255 * \section qt-gui-cases Sample cases
256 *
257 * Let us discuss some cases and how the different instances interact.
258 *
259 * \section qt-gui-cases-start Start
260 *
261 * When molecuilder is started, several singletons such as the World and
262 * others are instantiated. No atoms are yet present, no bonds, no molecules.
263 * Hence, nothing to display yet.
264 *
265 * Before launching any Action the ActionQueue is forced to wait till the
266 * GUI is finished instantiating. This is to ensure that GLWorldView and
267 * others are in place to receive signals from the O/O system.
268 *
269 * When a molecule is loaded, the instantiation of a GLMoleculeObject_molecule
270 * does not happen immediately. Hence, GLWorldView listens to the World's
271 * MoleculeInserted. On receiving it, it also signOn()s to the molecule
272 * to get its subjectKilled(). It translates then these and also all
273 * AtomInserted and AtomRemoved to the S/S system as moleculeInserted,
274 * moleculeRemoved and atomInserted/atomRemoved respectively, which are
275 * processed by the GLWorldScene.
276 *
277 * The GLWorldScene records any atomInserted/atomRemoved until the molecule
278 * has been instantiated. On instantiation all recorded events are played.
279 * This is to ensure that there is no overlap in instantiation and signOn()
280 * to the molecule. If we would simply get all atoms which are present
281 * on processing the molecule's instantiation we might stumble over a signal
282 * of a molecule of a just added atom. This occurs frequently as both
283 * are very much correlated.
284 *
285 * GLWorldView keep track of all ObservedMolecules. And GLWorldScene keeps
286 * track of all shapes and molecules in the scene. Each
287 * GLMoleculeObject_molecule in turn keeps track of all atoms and bonds in
288 * its part of the scene.
289 *
290 * \section QtElementList
291 *
292 * Lists for each element how often it occurs in the world. Selecting an entry
293 * calls SelectionAtomByElementAction to select all atoms of that particular
294 * element.
295 *
296 * Initially, it fills itself by looking at all elements in the World's
297 * periodentafel. It also listens to AtomObserver's ElementChanged to know
298 * when to update a certain element in its list. By using an internal list
299 * for each atom's element, it can update each element's occurrence.
300 *
301 * \section QtMoleculeList
302 *
303 * Lists all the molecules currently in the world grouped by their formula.
304 * Selecting an entry calls the SelectionMoleculeByIdAction.
305 *
306 * The QtMoleculeList is also a rather complex beast. It is a tree of
307 * rows and each row consists of a number of elements. There are two
308 * levels, the group level where the common formula for all molecules
309 * is given, and the molecule level where are molecules of this specific
310 * formula are summarized.
311 *
312 * The group items are QStandardItems. Sadly, they are not derived from
313 * QObject and hence do not use the S/S system. The group items are
314 * directly controlled by the QtMoleculeList.
315 *
316 * However, the molecule items are different. They are derived from
317 * QtMoleculeList and use an ObservedValue internally to contain an always
318 * valid copy of the required information. They inform the QtMoleculeList on
319 * updates via a callback (as QStandardItem, from which they are also derived,
320 * does not use the S/S system). The callback takes care of then also updating
321 * the group items and possibly moving the molecule items around, e.g. if
322 * their formula has changed they suddenly belong to another group.
323 *
324 * All items are instantiated by the QtMoleculeItemFactory.
325 *
326 * QtMoleculeList uses an internal QTimer to only update itself at regular
327 * intervals. Hence, updates are processed rather lazily. We keep lists
328 * of changes, separated for group and molecule items. And these are processed
329 * one after the other at the intervals dictated by the QTimer in
330 * updateItemStates().
331 *
332 * \section QtShapeController
333 *
334 * This is the interface for the ShapeRegistry. It lists all the shapes in the
335 * registry and lets the user select them. It also features buttons to call
336 * actions creating and manipulating the selected shapes.
337 *
338 * As an Observer it handles the following messages from ShapeRegistry:
339 * - ShapeRegistry::ShapeInserted
340 * - ShapeRegistry::ShapeRemoved
341 * - ShapeRegistry::SelectionChanged
342 *
343 * \section QtInfoBox
344 *
345 * Shows information about the atom and molecule the cursor is currently hovering
346 * over inside the GLWorldView.
347 *
348 * GLWorldView emits hoverChanged signals (via QT's signal slot mechanism) which
349 * the QtInfoBox receives. QtInfoBox then creates its info pages for the atom
350 * being transmitted as the signal's parameter.
351 *
352 * The info pages are Observers for the atom/molecule. When recieving subjectKilled
353 * they automatically clear the info box.
354 *
355 * \date 2016-02-13
356 */
Note: See TracBrowser for help on using the repository browser.