Developer Documentation
Loading...
Searching...
No Matches
graphicsScene.cc
1/*===========================================================================*\
2* *
3* OpenFlipper *
4 * Copyright (c) 2001-2015, RWTH-Aachen University *
5 * Department of Computer Graphics and Multimedia *
6 * All rights reserved. *
7 * www.openflipper.org *
8 * *
9 *---------------------------------------------------------------------------*
10 * This file is part of OpenFlipper. *
11 *---------------------------------------------------------------------------*
12 * *
13 * Redistribution and use in source and binary forms, with or without *
14 * modification, are permitted provided that the following conditions *
15 * are met: *
16 * *
17 * 1. Redistributions of source code must retain the above copyright notice, *
18 * this list of conditions and the following disclaimer. *
19 * *
20 * 2. Redistributions in binary form must reproduce the above copyright *
21 * notice, this list of conditions and the following disclaimer in the *
22 * documentation and/or other materials provided with the distribution. *
23 * *
24 * 3. Neither the name of the copyright holder nor the names of its *
25 * contributors may be used to endorse or promote products derived from *
26 * this software without specific prior written permission. *
27 * *
28 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS *
29 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED *
30 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A *
31 * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER *
32 * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, *
33 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, *
34 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR *
35 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF *
36 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING *
37 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS *
38 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. *
39* *
40\*===========================================================================*/
41
42
43
44
45//== INCLUDES =================================================================
46#include "graphicsScene.hh"
47#include "graphicsView.hh"
48#include "sceneElement.hh"
49#include "sceneTools.hh"
50#include "elementArea.hh"
51
52#include "elementInput.hh"
53#include "elementOutput.hh"
54#include "elementFunction.hh"
55#include "connection.hh"
56#include "connectionPoint.hh"
57#include "wayfind.hh"
58
59#include "baseWidget.hh"
60
61#include <QApplication>
62#include <QPaintEngine>
63#include <QMimeData>
64
65#include "../parser/context.hh"
66#include "../parser/output.hh"
67#include "../parser/function.hh"
68
69#define SELECTION_RED 0x00
70#define SELECTION_GREEN 0x00
71#define SELECTION_BLUE 0x7f
72#define SELECTION_ALPHA 0x7f
73
74//== NAMESPACES ===============================================================
75namespace VSI {
76
77//=============================================================================
78//
79// CLASS VSI::GraphicsScene - IMPLEMENTATION
80//
81//=============================================================================
82
85 QGraphicsScene (),
86 ctx_ (_ctx),
87 function_ (_function),
88 dndElement_ (0),
89 elementArea_ (0),
90 selectionActive_ (false),
91 blockChanges_ (false),
92 activeConnection_ (0),
93 currentZ_ (0)
94{
95 // tools area
96 tools_ = new SceneTools (this);
97 addItem (tools_);
98 tools_->setZValue (10);
99
100 // selecion item
101 selection_ = new QGraphicsRectItem ();
102 addItem (selection_);
103 selection_->setZValue (1);
104 selection_->hide ();
105
106 selection_->setBrush(QBrush(QColor(SELECTION_RED,
107 SELECTION_GREEN,
108 SELECTION_BLUE,
109 SELECTION_ALPHA / 2)));
110
111 selection_->setPen(QColor(SELECTION_RED,
112 SELECTION_GREEN,
113 SELECTION_BLUE,
114 SELECTION_ALPHA));
115
116 elementArea_ = new ElementArea (this);
117 addItem (elementArea_);
118 elementArea_->setZValue (0.0);
119
120 // add start and end
121 if (!function_)
122 {
123 SceneElement *end = new SceneElement (this, ctx_->element ("end"));
124 addElement (end);
125 SceneElement *start = new SceneElement (this, ctx_->element ("start"));
126 addElement (start);
127 }
128 else
129 {
130 SceneElement *end = new SceneElement (this, function_->function ()->end ());
131 addElement (end);
132 SceneElement *start = new SceneElement (this, function_->function ()->start ());
133 addElement (start);
134 }
135
136 // initalize wayfinding
137 wayFind_ = new WayFind (this);
138
139 view_ = new GraphicsView ();
140 view_->setScene (this);
141 view_->setRenderHint (QPainter::Antialiasing, true);
142
145
146 connect (this, SIGNAL(sceneRectChanged( const QRectF& )), this, SLOT(sceneResized(const QRectF&)));
147 dontMoveStart_ = false;
148}
149
150//------------------------------------------------------------------------------
151
154{
155 delete wayFind_;
156
159 view_->deleteLater ();
160}
161
162//------------------------------------------------------------------------------
163
164// draw background
165void GraphicsScene::drawBackground(QPainter *_painter, const QRectF &_rect)
166{
167 QGraphicsScene::drawBackground (_painter, _rect);
168}
169
170//------------------------------------------------------------------------------
171
172// drag enter
173void GraphicsScene::dragEnterEvent (QGraphicsSceneDragDropEvent *_event)
174{
175 // create scene element if mimedata is valid
176 if (validMimeData (_event->mimeData ()) && !dndElement_ && ctx_->element (mimeDataElementName (_event->mimeData ())))
177 {
178 dndElement_ = new SceneElement (this, ctx_->element (mimeDataElementName (_event->mimeData ())));
179 dndElement_->setOpacity (0.5);
180 addElement (dndElement_);
181
182 QPoint off = mimeDataPoint (_event->mimeData ());
183
184 QPointF p = elementArea_->mapFromScene (_event->scenePos ().x (), _event->scenePos ().y ());
185 dndElement_->setPos (p.x () - (dndElement_->size ().width () / 2) - off.x(),
186 p.y () - (dndElement_->size ().height () / 2) - off.y());
187
188 }
189 else
190 _event->ignore ();
191
192}
193
194//------------------------------------------------------------------------------
195
196// drag leave
197void GraphicsScene::dragLeaveEvent (QGraphicsSceneDragDropEvent *_event)
198{
199 // destroy created scene element
200 if (validMimeData (_event->mimeData ()) && dndElement_)
201 {
202 tools_->mouseRelease (_event->scenePos (), dndElement_);
203 removeElement (dndElement_);
204 delete dndElement_;
205 dndElement_ = NULL;
206 }
207 else
208 _event->ignore ();
209}
210
211//------------------------------------------------------------------------------
212
213// drag move
214void GraphicsScene::dragMoveEvent (QGraphicsSceneDragDropEvent *_event)
215{
216 // update position of dragged element
217 if (validMimeData (_event->mimeData ()) && dndElement_)
218 {
219 QPoint off = mimeDataPoint (_event->mimeData ());
220 QPointF p = elementArea_->mapFromScene (_event->scenePos ().x (), _event->scenePos ().y ());
221 dndElement_->setPos (p.x () - (dndElement_->size ().width () / 2) - off.x (),
222 p.y () - (dndElement_->size ().height () / 2) - off.y ());
223
224 tools_->mouseMove (_event->scenePos ());
225 }
226 else
227 _event->ignore ();
228}
229
230//------------------------------------------------------------------------------
231
232// drop
233void GraphicsScene::dropEvent (QGraphicsSceneDragDropEvent *_event)
234{
235 // leave dropped element in scene
236 if (validMimeData (_event->mimeData ()) && dndElement_)
237 {
238 tools_->mouseRelease (_event->scenePos (), dndElement_);
239 dndElement_->setOpacity (1.0);
240 dndElement_ = NULL;
241 contentChange ();
242 }
243 else
244 _event->ignore ();
245}
246
247//------------------------------------------------------------------------------
248
251{
252 currentZ_ += 0.01;
253 return currentZ_;
254}
255
256//------------------------------------------------------------------------------
257
259void GraphicsScene::moveElements (qreal _dx, qreal _dy, bool _selected)
260{
261 elementArea_->moveBy (_dx, _dy);
262 if (_selected)
263 return;
264
265 contentChange ();
266 foreach (QGraphicsItem *e, elementArea_->elements ())
267 if (e->isSelected ())
268 {
269 QPointF p = elementArea_->mapFromScene (0, 0) -
270 elementArea_->mapFromScene (_dx, _dy);
271 e->moveBy (p.x (), p.y ());
272 }
273}
274
275//------------------------------------------------------------------------------
276
279{
280 QRectF rect = sceneRect();
281 scaleElements (_delta, QPointF (rect.x () + (rect.width () / 2.0),
282 rect.y () + (rect.height () / 2.0)));
283}
284
285//------------------------------------------------------------------------------
286
288void GraphicsScene::scaleElements (qreal _delta, QPointF _center)
289{
290 QPointF p(elementArea_->mapFromScene (_center.x (), _center.y ()));
291 elementArea_->setTransform(QTransform().translate(p.x (), p.y ()).scale(_delta, _delta).translate(-p.x (), -p.y ()), true);
292}
293
294//------------------------------------------------------------------------------
295
298{
299 contentChange ();
300 addItem (_element);
301 elementArea_->addElement (_element);
302}
303
304//------------------------------------------------------------------------------
305
308{
309 contentChange ();
310 bool rv = elementArea_->removeElement (_element);
311 if (rv)
312 removeItem (_element);
313
314 return rv;
315}
316
317//------------------------------------------------------------------------------
318
320void GraphicsScene::mouseMove (QPointF _pos)
321{
322 tools_->mouseMove (_pos);
323}
324
325//------------------------------------------------------------------------------
326
328void GraphicsScene::mouseRelease (QPointF _pos, QGraphicsItem *_item)
329{
330 tools_->mouseRelease (_pos, _item);
331}
332
333//------------------------------------------------------------------------------
334
336{
337 if (elementArea_)
338 return elementArea_->mapToScene (elementArea_->childrenBoundingRect ()).boundingRect ();
339 else
340 return QRectF ();
341}
342
343//------------------------------------------------------------------------------
344
346const QList<SceneElement *>& GraphicsScene::elements () const
347{
348 return elementArea_->elements ();
349}
350
351//------------------------------------------------------------------------------
352
353// start selection rectangle if pressing on empty area of scene
354void GraphicsScene::mousePressEvent (QGraphicsSceneMouseEvent *_event)
355{
356 QPainterPath p;
357
358 foreach (QGraphicsItem *e, selectedItems ())
359 p += e->mapToScene (e->shape ());
360
361 QGraphicsScene::mousePressEvent (_event);
362 selectionStart_ = _event->scenePos ();
363
364 if (itemAt (selectionStart_,QTransform()) != elementArea_ && !_event->isAccepted ())
365 {
366 if (_event->modifiers () & (Qt::ControlModifier | Qt::ShiftModifier))
367 setSelectionArea (p);
368
369 selectionActive_ = true;
370 selection_->setRect (QRectF (selectionStart_, QSizeF (0, 0)));
371 selection_->show ();
372 _event->accept ();
373 }
374
375 dontMoveStart_ = true;
376}
377
378//------------------------------------------------------------------------------
379
380// update selection rectangle
381void GraphicsScene::mouseMoveEvent (QGraphicsSceneMouseEvent *_event)
382{
383 if (selectionActive_)
384 {
385 selection_->setRect (QRectF (selectionStart_, _event->scenePos ()).normalized ());
386 _event->accept ();
387 }
388 else if (activeConnection_)
389 {
390 activeConnection_->mouseMoveEvent (_event);
391 }
392 else
393 QGraphicsScene::mouseMoveEvent (_event);
394}
395
396//------------------------------------------------------------------------------
397
398// select all elements in selection rectangle
399void GraphicsScene::mouseReleaseEvent (QGraphicsSceneMouseEvent *_event)
400{
401 if (selectionActive_)
402 {
403 selection_->hide ();
404 QPainterPath p;
405
406 p.addRect (QRectF (selectionStart_, _event->scenePos ()).normalized ());
407
408 if (_event->modifiers () == Qt::ControlModifier)
409 {
410 p += selectionArea ();
411 setSelectionArea (p);
412 }
413 else if (_event->modifiers () == Qt::ShiftModifier)
414 {
415 foreach (QGraphicsItem *e, items (QRectF (selectionStart_, _event->scenePos ()).normalized ()))
416 if (e->isSelected ())
417 e->setSelected (false);
418 }
419 else
420 setSelectionArea (p);
421
422 selectionActive_ = false;
423
424 _event->accept ();
425 }
426 if (activeConnection_)
427 {
428 activeConnection_->mouseReleaseEvent (_event);
429 }
430
431 QGraphicsScene::mouseReleaseEvent (_event);
432}
433
434//------------------------------------------------------------------------------
435
436// returns all scene elements. Also all elements of sub-functions
437QList<SceneElement *> GraphicsScene::getAllElements()
438{
439 QList<SceneElement *> rv;
440
441 foreach (SceneElement *e, elementArea_->elements ())
442 {
443 rv.append (e);
444 foreach (ElementFunction *ef, e->functions ())
445 {
446 rv.append (ef->scene ()->getAllElements ());
447 }
448 }
449
450 return rv;
451}
452
453//------------------------------------------------------------------------------
454
456QString GraphicsScene::generateCode (QString &errors, bool _codeOnly)
457{
458 QString rv = "";
459
460 // only code block for functions
461 if (!_codeOnly)
462 {
463 // collect all elements and functions
464 QSet<Element *> set;
465 QList<ElementFunction *> functions;
466
467 foreach (SceneElement *e, getAllElements ())
468 {
469 set.insert (e->element ());
470 foreach (ElementFunction *ef, e->functions ())
471 functions.append (ef);
472 }
473
474 // add precode block for each element only once
475 foreach (Element *e, set)
476 if (!e->precode ().isEmpty ())
477 {
478 rv += "# - ";
479 rv += e->shortDescription ();
480 rv += " -\n";
481 rv += e->precode ();
482 rv += "\n";
483 }
484
485 // add functiond
486 if (!functions.isEmpty ())
487 {
488 rv += "# ------- FUNCTIONS -----------\n\n";
489 }
490
491 foreach (ElementFunction *ef, functions)
492 {
493 rv += "# - ";
494 rv += ef->element ()->element ()->shortDescription ();
495 rv += " - ";
496 rv += ef->function ()->shortDescription ();
497 rv += " -\n";
498 rv += "def func_" + ef->element ()->variableId () + "_" + ef->function ()->name ();
499
500 QString param;
501 foreach (Output *o, ef->function ()->start ()->outputs ())
502 {
503 param += o->name () + ", ";
504 }
505
506 if (!param.isEmpty ())
507 param.remove (param.length () - 2, 2);
508
509 rv += " (" + param + "):\n";
510
511 QString code = ef->scene ()->generateCode (errors, true);
512
513 // Add identation
514 QString ident(" ");
515 QStringList lines = code.split("\n");
516 for (int l=0;l<lines.length();l++) lines[l] = ident + lines[l];
517 code = lines.join("\n");
518
519 rv += code + "\n\n";
520 }
521
522 if (!functions.isEmpty ())
523 {
524 rv += "# ------- END FUNCTIONS -------\n\n";
525 }
526 }
527
528
529 QList<SceneElement *> elements = elementArea_->elements ();
530
531 SceneElement *start = 0;
532 SceneElement *end = 0;
533
534 // reset code block of all elements and find start element
535 foreach (SceneElement *e, elements)
536 {
538
539 if ((!function_ && e->element ()->name () == "start") ||
540 (function_ && function_->function ()->start () == e->element ()))
541 start = e;
542 if ((!function_ && e->element ()->name () == "end") ||
543 (function_ && function_->function ()->end () == e->element ()))
544 end = e;
545 }
546
547 if (!start)
548 {
549 // should never happen
550 errors += "No start element!";
551 return "";
552 }
553
554 elements.removeAll (start);
555 elements.removeAll (end);
556
557 // Ignore empty scenes
558 if (elements.isEmpty() && end->inputs().isEmpty ())
559 end = NULL;
560
561 rv += "# --- CODE BEGIN ---\n\n";
562
563 updateConnections (start, true);
564
565 SceneElement *found = (SceneElement*)1;
566
567 // iterate over all elements
568 while ((!elements.empty () || end) && found)
569 {
570 found = NULL;
571
572 // pick element where all inputs are set
573 foreach (SceneElement *e, elements)
574 {
575 bool set = true;
576 foreach (ElementInput *i, e->inputs ())
577 if (!i->valid ())
578 set = false;
579
580 if (e->dataIn () && !e->dataIn ()->valid ())
581 set = false;
582
583 if (set)
584 found = e;
585 }
586
587 if (!found)
588 {
589 bool set = true;
590 foreach (ElementInput *i, end->inputs ())
591 if (!i->valid ())
592 set = false;
593
594 if (end->dataIn () && !end->dataIn ()->valid ())
595 set = false;
596
597 // Ignore end elements without inputs
598 if (set)
599 {
600 if (!end->inputs ().isEmpty ())
601 found = end;
602 end = NULL;
603 }
604 }
605
606 if (found)
607 {
608 elements.removeAll (found);
609
610 rv += "# - ";
611 rv += found->element ()->shortDescription ();
612 rv += " -\n";
613 rv += updateConnections (found, false);
614
615 QString dName = "ask_user_output_" + found->variableId();
616 QString dInputs = "";
617
618 // replace all inputs that are not connected with user defined value or prepare for asking during execution
619 foreach (ElementInput *i, found->inputs ()){
620 if (i->isSet ())
621 {
622 found->replaceCodeBlock ("is_set", i->inOut ()->name (), "True");
623 found->replaceCodeBlock ("input", i->inOut ()->name (), i->value ());
624 }
625 else if (!i->connections ().isEmpty ())
626 {
627 found->replaceCodeBlock ("is_set", i->inOut ()->name (), "rue");
628 }
629 else if (i->connections ().isEmpty ())
630 {
631 if (i->state () & Input::Optional && !i->isForceAskSet ())
632 {
633 found->replaceCodeBlock ("input", i->inOut ()->name (), "\"\"");
634 found->replaceCodeBlock ("is_set", i->inOut ()->name (), "False");
635 }
636 else
637 {
638 found->replaceCodeBlock ("is_set", i->inOut ()->name (), "True");
639 found->replaceCodeBlock ("input", i->inOut ()->name (), dName + "[\"" + i->inOut ()->name ()+"\"]");
640 dInputs += i->inOut ()->name () + ",";
641 }
642 }
643
644 // Replace the is_connected block for inputs as well.
645 if (i->connections ().isEmpty ())
646 found->replaceCodeBlock ("is_connected", i->inOut ()->name (), "False");
647 else
648 found->replaceCodeBlock ("is_connected", i->inOut ()->name (), "True");
649 }
650
651 foreach (ElementFunction *ef, found->functions ())
652 found->replaceCodeBlock ("function", ef->function ()->name (), "func_" + found->variableId () + "_" + ef->function ()->name ());
653
654 if (!dInputs.isEmpty ())
655 {
656 // remove last ,
657 dInputs.remove (dInputs.length () - 1, 1);
658 rv += "# Ask user for missing input values\n";
659 rv += "" + dName + " = eval(visualscripting.askForInputs (\"" + found->element ()->name ();
660 rv += "\", \"" + dInputs + "\"));\n";
661 }
662
663 rv += found->code ();
664 rv += "\n";
665 }
666 }
667
668 rv += "# --- CODE END ---\n";
669
670 if (end)
671 elements.append (end);
672
673 if (!elements.isEmpty ())
674 {
675 // find all elements that have real missing inputs
676 foreach (SceneElement *e, elements)
677 {
678 foreach (ElementOutput *o, e->outputs ())
679 {
680 foreach (Connection *c, o->connections ())
681 if (c->input ())
682 {
683 c->input ()->setValid (true);
684 }
685 }
686
687 if (e->dataOut ()) {
688 foreach (Connection *c, e->dataOut ()->connections ())
689 if (c->input ())
690 {
691 c->input ()->setValid (true);
692 break;
693 }
694 }
695 }
696
697 QList<SceneElement *> fixE;
698 foreach (SceneElement *e, elements)
699 {
700 bool set = true;
701 foreach (ElementInput *i, e->inputs ())
702 if (!i->valid ())
703 set = false;
704
705 if (e->dataIn () && !e->dataIn ()->valid ())
706 set = false;
707
708 if (!set)
709 fixE.append (e);
710 }
711
712 errors += "<dl><dt>";
713
714 if (!function_)
715 errors += "<b>Function: </b>Main";
716 else
717 errors += "<b>Function: </b>" + function_->function ()->shortDescription();
718
719 errors += "</dt><dd>";
720
721 foreach (SceneElement *e, fixE)
722 {
723 errors += "<b>Element: </b>" + e->element ()->shortDescription () + " <i>(";
724 foreach (ElementInput *i, e->inputs ())
725 if (!i->valid ())
726 errors += i->inOut ()->shortDescription () + ", ";
727
728 if (e->dataIn () && !e->dataIn ()->valid ())
729 errors += e->dataIn ()->inOut ()->shortDescription ();
730
731 if (errors.endsWith (", "))
732 errors.remove (errors.length () - 2, 2);
733 errors += ")</i><br>";
734 }
735
736 if (errors.endsWith ("<br>"))
737 errors.remove (errors.length () - 4, 4);
738
739 errors += "<dd></dl>";
740
741 return "";
742 }
743
744 return rv;
745}
746
747//------------------------------------------------------------------------------
748
749// Mark all inputs that are connected to the outputs of _from as valid
750QString GraphicsScene::updateConnections (SceneElement *_from, bool _isStart)
751{
752 QString rv;
753 foreach (ElementOutput *o, _from->outputs ())
754 {
755 QString oName;
756
757 if (!_isStart)
758 {
759 oName = "output_" + _from->variableId() + "_" + o->inOut ()->name ();
760 rv += "" + oName + " = None;\n";
761
762 _from->replaceCodeBlock ("output", o->inOut ()->name (), oName);
763
764 if (o->connections ().isEmpty ())
765 _from->replaceCodeBlock ("is_connected", o->inOut ()->name (), "False");
766 else
767 _from->replaceCodeBlock ("is_connected", o->inOut ()->name (), "True");
768 }
769 else
770 oName = o->inOut ()->name ();
771
772 foreach (Connection *c, o->connections ())
773 if (c->input ())
774 {
775 c->input ()->setValid (true);
776 c->input ()->element ()->replaceCodeBlock ("input", c->input ()->inOut ()->name (), oName);
777 }
778 }
779
780 if (_from->dataOut ()) {
781 foreach (Connection *c, _from->dataOut ()->connections ())
782 if (c->input ())
783 {
784 c->input ()->setValid (true);
785 break;
786 }
787 }
788 return rv;
789}
790
791//------------------------------------------------------------------------------
792
793// Checks for supported mimetype
794bool GraphicsScene::validMimeData(const QMimeData *_m)
795{
796 return _m->hasFormat ("application/x-openflipper.vsi");
797}
798
799//------------------------------------------------------------------------------
800
801// gets the element name from the mimetype data
802QString GraphicsScene::mimeDataElementName(const QMimeData *_m)
803{
804 if (!validMimeData (_m))
805 return "";
806 QStringList sl = QString (_m->data ("application/x-openflipper.vsi")).split (";");
807
808 if (sl.length() != 3)
809 return "";
810
811 return sl[2];
812}
813
814//------------------------------------------------------------------------------
815
816// gets the mouse hotspot from the mimetype data
817QPoint GraphicsScene::mimeDataPoint(const QMimeData *_m)
818{
819 if (! validMimeData (_m))
820 return QPoint (0, 0);
821
822 QStringList sl = QString (_m->data ("application/x-openflipper.vsi")).split (";");
823
824 if (sl.length() != 3)
825 return QPoint (0, 0);
826
827 bool ok1, ok2;
828 int x = sl[0].toInt (&ok1);
829 int y = sl[1].toInt (&ok2);
830
831 if (!ok1 || !ok2)
832 return QPoint (0, 0);
833
834 return QPoint (x, y);
835}
836
837//------------------------------------------------------------------------------
838
840void GraphicsScene::saveToXml (QDomDocument &_doc, QDomElement &_root)
841{
842 QTransform t = elementArea_->transform ();
843 QDomElement s = _doc.createElement("transform");
844 _root.appendChild(s);
845 QDomText txt = _doc.createTextNode(
846 QString::number (t.m11 ()) + "|" + QString::number (t.m12 ()) + "|" + QString::number (t.m13 ()) + "|" +
847 QString::number (t.m21 ()) + "|" + QString::number (t.m22 ()) + "|" + QString::number (t.m23 ()) + "|" +
848 QString::number (t.m31 ()) + "|" + QString::number (t.m32 ()) + "|" + QString::number (t.m33 ())
849 );
850 s.appendChild(txt);
851
852 s = _doc.createElement("x");
853 _root.appendChild(s);
854 txt = _doc.createTextNode (QString::number(elementArea_->pos ().x ()));
855 s.appendChild(txt);
856
857 s = _doc.createElement("y");
858 _root.appendChild(s);
859 txt = _doc.createTextNode (QString::number(elementArea_->pos ().y ()));
860 s.appendChild(txt);
861
862 QDomElement el = _doc.createElement("elements");
863 _root.appendChild(el);
864
865 foreach (SceneElement *e, elements ())
866 e->saveToXml (_doc, el);
867}
868
869//------------------------------------------------------------------------------
870
872void VSI::GraphicsScene::loadFromXml (QDomElement& _domElement)
873{
874 clearScene (true);
875
876 QStringList sl = Context::getXmlString (_domElement, "transform").split ("|");
877
878
879
880 if (sl.length () == 9)
881 {
882
883 qreal m[9];
884
885 bool ok;
886 for (unsigned int i = 0; i < 9; i++)
887 {
888 m[i] = sl[i].toDouble (&ok);
889 if (!ok)
890 break;
891 }
892 if (ok)
893 elementArea_->setTransform (QTransform (m[0], m[1], m[2], m[3], m[4], m[5], m[6], m[7], m[8]), false);
894 }
895
896 bool ok1, ok2;
897 double x, y;
898 QString val;
899
900 val = Context::getXmlString (_domElement, "x");
901 x = val.toDouble (&ok1);
902
903 val = Context::getXmlString (_domElement, "y");
904 y = val.toDouble (&ok2);
905
906 if (ok1 && ok2)
907 elementArea_->setPos (x, y);
908
909
910 // To make parsing easier, we collect all connections, while we traverse the element.
911 std::vector<QString> connections;
912
913 // Search through all children of the current element if we have inputs, outputs or functions
914 for(QDomElement n = _domElement.firstChildElement(); !n.isNull(); n = n.nextSiblingElement() )
915 {
916 // Found an input Tag!
917 if (n.tagName() == "elements") {
918
919 // Iterate over all elements inside it (e.g. this are the algorithm boxes.
920 for(QDomElement sceneElement = n.firstChildElement(); !sceneElement.isNull(); sceneElement = sceneElement.nextSiblingElement() )
921 {
922 loadElement (sceneElement,connections);
923 }
924 }
925 }
926
927 for (auto connection : connections) {
928 loadConnection(connection);
929 }
930
931 QTimer::singleShot(0, this, SLOT (updateConnections()));
932}
933
934//------------------------------------------------------------------------------
935
936// load element from xml
937void GraphicsScene::loadElement(QDomElement & _domElement,std::vector<QString>& _connections)
938{
939 QString name = _domElement.attribute("name");
940 if (name.isEmpty () || !ctx_->element (name))
941 return;
942
943
944 SceneElement *e = new SceneElement (this, ctx_->element (name));
945 addElement (e);
946
947 e->loadFromXml (_domElement,_connections);
948}
949
950//------------------------------------------------------------------------------
951
952// load connection from xml
953void GraphicsScene::loadConnection(QString& _connection)
954{
955 bool ok;
956 int inId, outId;
957
958 QStringList connectionInfo = _connection.split(";");
959
960 if (connectionInfo.size() != 6 ) {
961 return;
962 }
963
964 QString inEl = connectionInfo[0];
965 QString inElId = connectionInfo[1];
966 QString inIn = connectionInfo[2];
967
968 QString outEl = connectionInfo[3];
969 QString outElId = connectionInfo[4];
970 QString outOut = connectionInfo[5];
971
972 inId = inElId.toInt (&ok);
973 if (!ok)
974 return;
975
976 outId = outElId.toInt (&ok);
977 if (!ok)
978 return;
979
980 SceneElement *in = NULL, *out = NULL;
981
982 foreach (SceneElement *e, elements ())
983 {
984 if (e->element ()->name () == inEl && e->id () == inId)
985 in = e;
986 else if (e->element ()->name () == outEl && e->id () == outId)
987 out = e;
988 }
989
990 if (!in || !out)
991 return;
992
993 ConnectionPoint *p1 = NULL, *p2 = NULL;
994
995 if (in->dataIn () && in->dataIn ()->inOut ()->name () == inIn &&
996 out->dataOut () && out->dataOut ()->inOut ()->name () == outOut)
997 {
998 p1 = in->dataIn ()->connectionPointItem ();
999 p2 = out->dataOut ()->connectionPointItem ();
1000 }
1001 else
1002 {
1003 foreach (ElementInput *i, in->inputs())
1004 if (i->inOut ()->name () == inIn)
1005 {
1006 p1 = i->connectionPointItem ();
1007 break;
1008 }
1009
1010 foreach (ElementOutput *o, out->outputs())
1011 if (o->inOut ()->name () == outOut)
1012 {
1013 p2 = o->connectionPointItem ();
1014 break;
1015 }
1016 }
1017
1018 if (p1 && p2)
1019 new Connection (p1, p2, this);
1020}
1021
1022//------------------------------------------------------------------------------
1023
1026{
1027 elementArea_->resetTransform ();
1028 elementArea_->setPos (0, 0);
1029 foreach (SceneElement *e, elements())
1030 {
1031 bool rv = elementArea_->removeElement (e, true);
1032 if (rv)
1033 {
1034 removeItem (e);
1035 delete e;
1036 }
1037 }
1038 if (!_start)
1039 {
1040 if (!function_)
1041 {
1042 SceneElement *start = new SceneElement (this, ctx_->element ("start"));
1043 addElement (start);
1044
1045 start->setPos(5, (height () - start->size ().height ()) / 2);
1046
1047 SceneElement *end = new SceneElement (this, ctx_->element ("end"));
1048 addElement (end);
1049
1050 end->setPos(width () - end->size ().width () - 5, (height () - end->size ().height ()) / 2);
1051 }
1052 else
1053 {
1054 SceneElement *start = new SceneElement (this, function_->function ()->start ());
1055 addElement (start);
1056
1057 start->setPos(5, (height () - start->size ().height ()) / 2);
1058
1059 SceneElement *end = new SceneElement (this, function_->function ()->end ());
1060 addElement (end);
1061
1062 end->setPos(width () - end->size ().width () - 5, (height () - end->size ().height ()) / 2);
1063 }
1064 }
1065 dontMoveStart_ = false;
1066}
1067
1068//------------------------------------------------------------------------------
1069
1070// update all connections in the scene
1071void GraphicsScene::updateConnections()
1072{
1073 foreach (SceneElement *e, elements ())
1074 {
1075 foreach (ElementInput *i, e->inputs ())
1076 foreach (Connection *c, i->connections ())
1077 c->updatePositions ();
1078
1079 foreach (ElementOutput *o, e->outputs ())
1080 foreach (Connection *c, o->connections ())
1081 c->updatePositions ();
1082
1083 if (e->dataIn ()) {
1084 foreach (Connection *c, e->dataIn ()->connections ())
1085 c->updatePositions ();
1086 }
1087
1088 if (e->dataOut ()) {
1089 foreach (Connection *c, e->dataOut ()->connections ())
1090 c->updatePositions ();
1091 }
1092 }
1093}
1094
1095//------------------------------------------------------------------------------
1096
1099{
1100 if (blockChanges_)
1101 return;
1102
1103 emit contentChanged ();
1104 dontMoveStart_ = true;
1105}
1106
1107//------------------------------------------------------------------------------
1108
1109void VSI::GraphicsScene::sceneResized(const QRectF & /*_rect*/)
1110{
1111 if (dontMoveStart_)
1112 return;
1113
1114 blockChanges_ = true;
1115
1116 QList<SceneElement *> elements = elementArea_->elements ();
1117
1118 SceneElement *start = 0;
1119 SceneElement *end = 0;
1120
1121 // reset code block of all elements and find start element
1122 foreach (SceneElement *e, elements)
1123 {
1124 e->resetCodeGeneration ();
1125
1126 if ((!function_ && e->element ()->name () == "start") ||
1127 (function_ && function_->function ()->start () == e->element ()))
1128 start = e;
1129 if ((!function_ && e->element ()->name () == "end") ||
1130 (function_ && function_->function ()->end () == e->element ()))
1131 end = e;
1132 }
1133
1134 if (start)
1135 start->setPos(5, (height () - start->size ().height ()) / 2);
1136 if (end)
1137 end->setPos(width () - end->size ().width () - 5, (height () - end->size ().height ()) / 2);
1138
1139 blockChanges_ = false;
1140}
1141
1142//=============================================================================
1143}
1144//=============================================================================
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
static BaseWidget * getBaseWidget()
Returns singleton.
void addScene(GraphicsScene *_scene)
add a new scene
void removeScene(GraphicsScene *_scene)
remove a scene
ElementInput * input()
Input of this connection.
void updatePositions()
called to update position on element movement
static QString getXmlString(QDomElement &_element, const QString &_tag, QString _default="")
Gets the string of a xml query.
Definition context.cc:514
Element * element(const QString &_name)
Returns the element with a given name.
Definition context.cc:155
bool removeElement(SceneElement *_element, bool _force=false)
Remove an element, if its removeable or _force is set.
const QList< SceneElement * > & elements() const
All elements.
QRectF boundingRect() const
Bounding rectangle.
void addElement(SceneElement *_element)
Add a child element.
SceneElement * element()
The scene element of this function element.
GraphicsScene * scene()
Scene.
Function * function()
Function class.
InOut * inOut() const
InOut context object.
QList< Connection * > connections() const
Connections.
void setValid(bool _value)
Sets the valid flag (needed during code generation)
QString value() const
Returns value set by user.
bool valid()
Returns state of valid flag (needed during code generation)
bool isSet()
Return "set" flag.
bool isForceAskSet() const
Return "ForceAsk" flag.
unsigned int state()
VSI::Input state passthrough.
const QString & shortDescription() const
Short description.
Definition element.hh:88
QString name() const
Element name.
Definition element.hh:82
const QVector< Output * > & outputs() const
Outputs.
Definition element.hh:97
const QString & shortDescription() const
Short description.
Definition function.hh:84
Element * end() const
End element of this function (can be NULL)
Definition function.hh:93
Element * start() const
Start element of this function.
Definition function.hh:90
QString name() const
Name.
Definition function.hh:81
void contentChange()
handle content changes
QRectF elementsBoundingRect()
Bounding rectangle of all scene elements.
void loadFromXml(QDomElement &_domElement)
Load from xml.
void clearScene(bool _start=false)
clear the whole scene (_start = keep start element)
void contentChanged()
Informs about content changes.
QString generateCode(QString &errors, bool _codeOnly=false)
Generate textual script code (_codeOnly = only pure code block)
qreal getNewZ()
Returns a new Z value that is above all elements.
void mouseMove(QPointF _pos)
Redirect mouse movement to tools area.
GraphicsScene(VSI::Context *_ctx, ElementFunction *_function=NULL)
Constructor.
void addElement(SceneElement *_element)
Add the element to the scene.
void scaleElements(qreal _delta)
Scale all elements with scaling center in the center of the scene.
const QList< SceneElement * > & elements() const
Scene elements.
void mouseRelease(QPointF _pos, QGraphicsItem *_item)
Redirect mouse release to tools area.
bool removeElement(SceneElement *_element)
Remove the element from the scene.
~GraphicsScene()
Destructor.
void saveToXml(QDomDocument &_doc, QDomElement &_root)
Save to xml.
void moveElements(qreal _dx, qreal _dy, bool _selected=false)
Moves all elements.
const QString & shortDescription() const
Short description.
Definition inout.hh:73
const QString & name() const
Name.
Definition inout.hh:70
QString variableId()
Unique variable name for code generation.
QVector< ElementOutput * > outputs()
Outputs.
void replaceCodeBlock(const QString &_name, const QString &_id, const QString &_value)
Replace block with name _name and id _id with _value.
void saveToXml(QDomDocument &_doc, QDomElement &_root)
Save to xml.
void loadFromXml(QDomElement &_domElement, std::vector< QString > &_connections)
Load one scene Element from xml.
ElementInput * dataIn()
Scene input.
void resetCodeGeneration()
Reset code block for code generation.
Element * element() const
Context VSI::Element.
ElementOutput * dataOut()
Scene output.
QString code()
Code block.
QVector< ElementFunction * > functions()
Functions.
QVector< ElementInput * > inputs()
Inputs.
void mouseRelease(QPointF _pos, QGraphicsItem *_item)
Handles mouse release (will be called by the scene, if the element is dropped)
void mouseMove(QPointF _pos)
Handles mouse movement (will be called by the scene, if the mouse with a draged element is moved)