-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy pathFAQ
347 lines (286 loc) · 20.5 KB
/
FAQ
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
13:17 <peer> Hi
13:19 <peer> What is still unclear for me, is how you model the idea of connections from your QAIM.
this is quite complex:
a connection is a child of a node, and a node is a child of the root so we have 3 different types. all share a abstract base class.
a connection is basically a pointer to another node. and the other node does also own a reference (which is not stored as a connection), so
the reference is invisible to the model.
- but when deleting either node we can see that there is either a reference or a connection. in both cases we have to do something about it.
the GraphicsView visualizes that connection and the model helps to find the connections destination QModelIndex with dst(..); this way
we can construct a Connection object knowing both parents.
if you have further question, please ask. note also that the QGraphicsItem(s) do break MVC as they tend to query data not using the model but:
- a node updates a connection if it is moved
- a connection notifies a node that it is gone, when it is deleted (might not be true for the automate project but for the springrts rng mapper)
bootstrapping and removing of such objects tends to be quite complex and error phrone as one has to keep in mind that objects in the gui might
not exist yet when launching a new gui. example: a node is generated, then the connection: but dst(..) finds a QPersistentModelIndex which does
not yet have a QGraphicsItem associated. (this is not fixed currently - in both projects)
if i recall correctly then:
- automate project tries to insert all nodes, then all connections. this helps to keep object dependancies in the gui consistent
- springrts.com rng mapper does not have such handling yet
13:19 <peer> Also, I think your approach lacks a bit of flexibility.
13:19 <peer> Let me explain that later point:
13:21 <peer> For one: as far as I see, there is no way to use different types of GrahicsItems to represent different types of nodes.
13:22 <peer> You can obviously not use the same approach as QAIV uses (delegates), as there is only one delegate that does the drawing for a lot of items. Since your
items are interactive objects on your graphics view, that is not going to work.
13:23 <peer> But: why not use the same approach as there is for the *editors* for item views: use a factory approach. That lets you specify what type of GraphicsItem
should be used to represent what type of node in your model.
THIS IS NOT POSSIBLE
this is due to the nature of the 'QGraphicsScene' which requires (the scene) that all items, that is QGraphicsItem(s), are existent.
the QGraphicsView (when doing the rendering) treats paints these QGraphicsItems using the paint() function.
in contrast: a QTreeView can use delegates to create an editor of arbitrary choise because it is only used for drawing and DOES NOT have such
a complex item dependancy as a QGraphicsScene/QGraphicsView where the items have a actual position in x,y and z coordinates.
yes can actually do that without much trouble:
in the gui part, just create a base class and then according to a type information create the object of your choice. in my cases i didn't need to yet.
but if you want i can give you further hints, where to modify which code. in general this is:
ItemView.cpp::rowsInserted(..)
if (data(index, myTypeRole) == freakyModule)
return create(freakyModule);
if (data(index, myTypeRole) == normalModule)
return create(normalModule)
this can obviously extended by a factory
13:24 <peer> Furthermore, I find it a bit limiting that the layout is defined by properties the items in the model.
13:25 <peer> Sure, that is one way to do it, but why not make that more flexible? Why not use a separate layout manager class that provides this layout in some,
undefined, way? The default one may just use coordinates stored in your items, but it would not be very flexible to assume items always have them...
the layout can be adapted by a central instance. you can do so using two different approaches:
1. if a new node is created then place it below the cursor (this is the default when inserting with the mouse)
but if a new item is inserted using the TreeView one has to find a good position for that item, which can be done by the backend
2. there could be a button which will layout the items using a library as graphviz. the Model::layoutChange signal is the candidate there
as it does not require a view to remove all items but instead only changes their position. (in contrast to modelReset())
please keep in mind that:
- qt views actually 'want' view independant coordinates of objects, this helps to have individual view(s) of the items
- my concept was different: i wanted a base layout for all items.
-> one opens a new view the same position are used.
-> BUT i wanted to be able to modify the item individually per view later on
-> my code IS not consistent as load/save was never implemented and therefore i got two states of object positions:
- the position in the backend stored/retrieved via the property/setProperty functions from QObject
- the altered positions in the QGraphicsView, these were never written back to the model once touched
create a blog entry and discuss the O(?) problems as:
- check the performance impact of the GraphicsItemRelay concept. painting seems to be a little slower now... ;P
-> profile void GraphicsItemRelay::updatePosition(QPointF pos) {
- have a look at FAQ, move this discussion there?!
- how many items can be inserted?
- how fast is model 2 screen index and vice versa
- overhead by removing/inserting of new items
- messanger objects for frontend to frontend module function calls
- view to view dependancies for faster drawing, discuss resutling issues
=========================================================================================
QGraphcsScene/QGraphcsView vs 'using QGraphicsScene/View as a QAbstractItemView
=========================================================================================
12:30 <peer2> I've read posts where people tried having a QGraphcsView inside a QAbstractItemView but they run into problems
12:30 <qknight> concept 1 of 2
12:31 <qknight> a QGraphicsScene is a model and a QGraphicsScene can have several views, aka QGraphicsView. the QGraphicsScene model handles insertion and stuff as well as
updating of all changed items in the QGraphicsView via it's own concepts
12:31 <qknight> concept 2 of 2
12:31 <qknight> Data <--> QAbstractItemModel <--> QGraphicsView is the other attempt doing the same thing but NOT using a QGraphicsScene or QGraphicsView
12:31 <qknight> this has other benefits and pitfalls
12:32 <qknight> so to summarize:
12:33 <qknight> a QGraphicsScene/QGraphcisView works different from a QAbstractItemView in this way: a QGraphicsScene/QGraphcisView always needs all items to be in the
scene and a QAbstractItemView works more or less like a delegate where items are queried on demand for rendering
12:33 <qknight> well, that's it
12:33 <qknight> i will add this to the discussion on my next posting
===========================================================================
why QGraphicsItem(s) do need direct access to each other sometimes
===========================================================================
the x,y position of a module is stored in the module property (backend) and is important for the QGraphicsScene/QGraphicsView basically.
when talking about Qt MVC the Model translates backend items into frontend items. one backend item may have several frontend items.
however, when moving a module using a drag'n'drop operation in the QGraphicsView the connections are redrawn immediately. this helps
to create a good looking arrangement of items as the drawing happens as fast as possible - realtime. it is important to note that the model
is not used while moving the item and therefore the graphical representation (frontend) and the acctual module (backend) are out of
sync. this changes when the drag'n'drop operation is finished using the 'QGraphicsItem::ItemPositionHasChanged'-signal which will
write the new x,y coordinate to the backend immediately.
altough it seems wise to do so, at least regarding the performance increase, it can result in pitfalls as it partially breaks the
MVC pattern, which basically says: a graphical representation (a frontend item as a module or connection) may not read data from
another source than the model. since updating the connection depends on the position change information of a module (both frontend items)
we break that rule by using another frontend item (the module) to be accessed by a connection and vice versa.
the problem now is: how to solve the frontend dependancies without adding too much code and making the process of further code changes
more error phrone.
there are a few different ways:
- slow: always use scene->items().contains(myItem) before acutally using that 'myItem'. this check would only check if that particular
frontend item exists, before using it.
FAIL: this is slow as hell
- all frontend items could implement an 'reference counter' using smart pointers. the object exists until the last reference is removed
FAIL: this does not work as the dependancy builds a loop between two objects
- extend all QGraphicsItems by inheriting also QObject, then use signals+slots
FAIL: - so every QGraphicsItem (namely Port/Connection/Module) needs to inherit from QObject, setProperty currently used will
have to be renamed as well
- i don't want to add QObject to every frontend module/connection/port
- despite that: this would be a nice solution as it would be very flexible
- frontend items do register or unregister their references manually:
this can be very complex and error phrone [NOT IMPLEMENTED ANYMORE]
- use a messanger/relay class which is used to forward messages between frontend classes
[CURRENTLY IMPLEMENTED]
problem viewed from the (frontend) module side:
onModuleMove: foreach(module-port(s)-connection(s), Connection* c) c->updatePosition();
I if a connection is removed, it has to inform the port to not request update(); anymore
II if a module (therefore the ports are removed) each port has to inform the connection not to use it anymore (in case of I)
-> therefore:
- graphical representations must be designed in a way that they can work even when not all dependacies are meat:
-> inserting a module (read PORT): [IMPLEMENTED]
creating a module, then creating a connection but the second module isn't there yet, if the second module is created the connection
starts the drawing. see class 'Connection' for issues
-> deleting a module: [IMPLEMENTED]
same when two modules exist and one connection. now the reset() clears all objects in arbitrary order: one module goes down, the
connection simply suspends accessing the module which is now not accessable anymore
====================================================================================================
MESSANGER/RELAY DESIGN (LATE POPULATION of frontend items)
====================================================================================================
- use a messanger/relay class which is used to forward messages between frontend classes:
- this does not require to use QObject
- objects (Connection/Port) can register and unregister at any time
- the last unregister will remove the messanger
- this class has two clients at max (bidirectional communication)
see classGraphicsItemRelay (for implementation)
a object creation cycle (when a GraphicsScene is populated using rowsInserted(..) by the class Model:
module1 (reference only) problem1
module2 - con1 - module 3 problem2
module3 (reference only)
module4 - con2 - module1
new module1:
PROBLEM1:
has only a reference so no 'connection' was added to connections and no crash will happen when moving the module which
will call:
foreach ( QGraphicsItem *g, childItems()) {
g->updateConnections();
PROBLEM2:
module2 does have a child connection but as there is no frontend representation for module3 yet drawing con1 will crash the program.
the current implementation will not create the con1 in the first place, which means that there will be NO frontend 'Connection' object!
SOLUTION:
- PROBLEM1
Q: can a class 'Port' know that it has references?
A: not yet
if a Port knows it has references assigned, it can complete the port <-> relay <-> connection process
class 'Relay' would work like that:
based on an identifier every object Port or Connection registers a 'Relay' r. r is used to pass messages between frontend items.
the identifier is only used to find r.
what must be changed to implement this system?
- Port must know about references and create one Relay 'r' per object
- Port must register one 'r' per outgoing connection
- PROBLEM2
con1 is created, it can't find a
module1 -- port1 -- relay -- connection -- relay -- port1 -- module2
one can do 'late population' as creating:
- module2, module1, connection or connection, module2, module1 does not have any problem with that
also deleting: one can delete a frontend item in an arbitrary order:
remove connection, module2, module1 or module1, module2, connection
Q: who accesses 'module properties'?
A: a list of uses:
- write property (the same as: initial creation of a property)
- Module.cpp::updatePosition(..) when an item is moved from x,y to x',y'
- Model.cpp::insertModule(..)
- the modules export their properties as: Perlin would create 'frequency' and 'octave count'
- QTreeView (this is an editor)
- read property
- ItemView.cpp to find the x,y position to place the QGraphicsItem
- QTreeView (this is an editor)
---- workflow analysis: how to generate a map ----
this is a guideline which helps me to define the problem i try to solve:
user case:
1. user joins a new game (in the qtlobby)
2. server offers game settings and the meta-map
3. user downloads the meta-map information
4. user generates map
5. user signals ready state to the server
6. server starts the game for all users
mapgenerator:
1. mapgenerator input is a xml file
2. the output generated based on different noise modules is a 1-30+mb map image
---- libnoise use case example ----
how to create the frontend-library for the libnoise library used by the mapper?
1. user starts by adding modules
2. user connects modules and defines an output
here i need a usability analysis! what workflow comes in handy? ideas:
- the user can see the output of a module right after connection in a preview
- the user can doubleclick the preview to obtain a complete image
- 3d heightmap visualizer (copied from qtlobby)
- the preview should contain a way to assign color
- gradient (done already)
- gradient assigning textures (not done yet)
- possibility to select several different gradient types of either case
- parameter changes on modules should output new images immediately
- this might require using the cache module in a transparent manner
- this might require implementing changes instead of pure flush/rebuild
of the renderer chain
module::RidgedMulti myModule;
myModule.SetOctaveCount (job.octave);
myModule.SetFrequency (job.frequency);
---- how to write the frontend for the backend? ----
define: frontend: the library which builds the scene:
a set of genmodules with combiners and one output module
-> this must be done using the ModuleFactory of course since it has to
be done in a dynamic way based on the data he produced with his graphic
module clicking
backend: the libnoise library (unmodified)
---- how to obtain a map -> x*y dimension heightmap ----
problem: how to select the bounding box for the map in the libnoiseview?
1. user defines a map dimension
2. user adds noisegen modules
3. user adds and connects a renderer (output module)
4. user looks at the result (output) and then decides which x or y coordinate
has to be changed in order to get his desired map-design done.
example: a user wants to have a hill in the middle of the map on a isle
he would have to generate a suitable isle with one module. on
the next step he would user a max(isle_gen,hill_gen) combiner
module to 'add' the hill to the map. he has to find a suitable
hill heightmap in the second noisegen
---- general class design and data storage ----
current plan:
QTableView or similar to modify module properties
\
\
QGraphicsView <---> QGraphicsScene <---> QAbstractItemModel <---> (XML?) data store (the map is stored here)
| / \
| / \
QGraphicsItem libNoiseAbstraction ModuleFactory
| (the frontend) (create/list modules)
| /
custom editor /
libNoise (the backend)
(bison for syntax checking)
---- unsolved issues ----
Q) how to manage the libNoiseAbstraction layer?
A) first we need to have a workflow. currently the plan is:
- keep as close as possible to the libnoise library and later
- introduce convenient modules which do things easier
- (even later) introduce pixel processing
Q) how does a graphical module as a renderer (QGraphisItem) display an image?
A) if the libNoiseAbstraction detects a change it will update it's image so that the
attached view is updated automatically.
Q) how to view a noisegen as the libnoise-viewer does currently?
A) somehow through the model, we need to request images for x,y pairs through the model to the
libNoiseAbstraction layer to the libnoise core library
the model is the central core in this paradigm. this helps to keep everything in sync
although it might slow down the views. a threading model is needed to set priorities so that
the view has always a pleasant responsiveness (when rendering images in the background).
Q) how to represent data?
A) either as the qt example or as a xml document. xml document would be cool since this could
be used to save the map without having to convert it first on save/load.
note: using xml directly would make this data storage quite complex since we need to maintain
a graph with certain conditions we have to check, example:
root --- node1
|- node2 <--\ node3 is connected to node2 (directional graph)
|- node3 ---/
we need this properties to check if a module is in ready state or not.
def: ready state: top level elements are always capable of being used as input
other modules relay on certain configurations before being used, so these
have to check first if all conditions are acceptable as:
- minimal input connections
- modifier connections
- doubleconnections
- one output, two different modules relay on this output ...
- and so on
Q) what kind of connections and modules do we have
A) connections:
- we have libnoise connections (abstract module connections)
- we have pixel data processing (output of a renderer module)
and later throughput of filters of imagemagick
- we have spring data specific modules and connections
modules
* libnoise modules
* pixel filter modules
* spring modules (to create maps)
-- solved problems --
Q) how do we track backward connections? how was it done in the 'automate' project?
A) this project is now using references. a port does have also a QList of connections which reference this port
Q) modules do have ports, how to know which port is where in regars to model <-> view?
A) input, modput, output are the 3 possible port types all represented by a Port object which
is a child of a Module
model->insertConnection(QPersistentModelIndex a, QPersistentModelIndex b); where a and b are representing a Port, see Document.cpp