1 /***************************************************************************
2 qgsmodelcomponentgraphicitem.h
3 ----------------------------------
4 Date : March 2020
5 Copyright : (C) 2020 Nyall Dawson
6 Email : nyall dot dawson at gmail dot com
7 ***************************************************************************
8 * *
9 * This program is free software; you can redistribute it and/or modify *
10 * it under the terms of the GNU General Public License as published by *
11 * the Free Software Foundation; either version 2 of the License, or *
12 * (at your option) any later version. *
13 * *
14 ***************************************************************************/
15
16 #ifndef QGSMODELCOMPONENTGRAPHICITEM_H
17 #define QGSMODELCOMPONENTGRAPHICITEM_H
18
19 #include "qgis.h"
20 #include "qgis_gui.h"
21 #include <QGraphicsObject>
22 #include <QFont>
23 #include <QPicture>
24 #include <QPointer>
25
26 class QgsProcessingModelComponent;
27 class QgsProcessingModelParameter;
28 class QgsProcessingModelChildAlgorithm;
29 class QgsProcessingModelOutput;
30 class QgsProcessingModelComment;
31 class QgsProcessingModelAlgorithm;
32 class QgsModelDesignerFlatButtonGraphicItem;
33 class QgsModelDesignerFoldButtonGraphicItem;
34 class QgsModelGraphicsView;
35 class QgsModelViewMouseEvent;
36 class QgsProcessingModelGroupBox;
37
38 ///@cond NOT_STABLE
39
40 /**
41 * \ingroup gui
42 * \brief Base class for graphic items representing model components in the model designer.
43 * \warning Not stable API
44 * \since QGIS 3.14
45 */
46 class GUI_EXPORT QgsModelComponentGraphicItem : public QGraphicsObject
47 {
48 Q_OBJECT
49
50 public:
51
52 //! Available item states
53 enum State
54 {
55 Normal, //!< Normal state
56 Selected, //!< Item is selected
57 Hover, //!< Cursor is hovering over an deselected item
58 };
59
60 //! Available flags
61 enum Flag
62 {
63 // For future API flexibility only and to avoid sip issues, remove when real entries are added to flags.
64 Unused = 1 << 0, //!< Temporary unused entry
65 };
66 Q_DECLARE_FLAGS( Flags, Flag )
67
68 /**
69 * Constructor for QgsModelComponentGraphicItem for the specified \a component, with the specified \a parent item.
70 *
71 * The \a model argument specifies the associated processing model. Ownership of \a model is not transferred, and
72 * it must exist for the lifetime of this object.
73 *
74 * Ownership of \a component is transferred to the item.
75 */
76 QgsModelComponentGraphicItem( QgsProcessingModelComponent *component SIP_TRANSFER,
77 QgsProcessingModelAlgorithm *model,
78 QGraphicsItem *parent SIP_TRANSFERTHIS );
79
80 ~QgsModelComponentGraphicItem() override;
81
82 /**
83 * Returns item flags.
84 */
85 virtual Flags flags() const;
86
87 /**
88 * Returns the model component associated with this item.
89 */
90 QgsProcessingModelComponent *component();
91
92 /**
93 * Returns the model component associated with this item.
94 */
95 const QgsProcessingModelComponent *component() const SIP_SKIP;
96
97 /**
98 * Returns the model associated with this item.
99 */
100 QgsProcessingModelAlgorithm *model();
101
102 /**
103 * Returns the associated view.
104 */
105 QgsModelGraphicsView *view();
106
107 /**
108 * Returns the font used to render text in the item.
109 * \see setFont()
110 */
111 QFont font() const;
112
113 /**
114 * Sets the \a font used to render text in the item.
115 * \see font()
116 */
117 void setFont( const QFont &font );
118
119 /**
120 * Moves the component by the specified \a dx and \a dy.
121 *
122 * \warning Call this method, not QGraphicsItem::moveBy!
123 */
124 void moveComponentBy( qreal dx, qreal dy );
125
126 /**
127 * Shows a preview of moving the item from its stored position by \a dx, \a dy.
128 */
129 void previewItemMove( qreal dx, qreal dy );
130
131 /**
132 * Sets a new scene \a rect for the item.
133 */
134 void setItemRect( QRectF rect );
135
136 #ifndef SIP_RUN
137
138 /**
139 * Shows a preview of setting a new \a rect for the item.
140 */
141 QRectF previewItemRectChange( QRectF rect );
142
143 /**
144 * Sets a new scene \a rect for the item.
145 */
146 void finalizePreviewedItemRectChange( QRectF rect );
147
148 /**
149 * Handles a model hover enter \a event.
150 */
151 virtual void modelHoverEnterEvent( QgsModelViewMouseEvent *event );
152
153 /**
154 * Handles a model hover move \a event.
155 */
156 virtual void modelHoverMoveEvent( QgsModelViewMouseEvent *event );
157
158 /**
159 * Handles a model hover leave \a event.
160 */
161 virtual void modelHoverLeaveEvent( QgsModelViewMouseEvent *event );
162
163 /**
164 * Handles a model double-click \a event.
165 */
166 virtual void modelDoubleClickEvent( QgsModelViewMouseEvent *event );
167 #endif
168 void mouseDoubleClickEvent( QGraphicsSceneMouseEvent *event ) override;
169 void hoverEnterEvent( QGraphicsSceneHoverEvent *event ) override;
170 void hoverMoveEvent( QGraphicsSceneHoverEvent *event ) override;
171 void hoverLeaveEvent( QGraphicsSceneHoverEvent *event ) override;
172 QVariant itemChange( GraphicsItemChange change, const QVariant &value ) override;
173 QRectF boundingRect() const override;
174 bool contains( const QPointF &point ) const override;
175 void paint( QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget = nullptr ) override;
176
177 /**
178 * Returns the rectangle representing the body of the item.
179 */
180 QRectF itemRect( bool storedRect = false ) const;
181
182 /**
183 * Returns the item's label text.
184 *
185 * \see setLabel()
186 */
187 QString label() const;
188
189 /**
190 * Returns the item's \a label text.
191 *
192 * \see label()
193 */
194 void setLabel( const QString &label );
195
196 /**
197 * Returns the item's current state.
198 */
199 State state() const;
200
201 /**
202 * Returns the number of link points associated with the component on the specified \a edge.
203 */
204 virtual int linkPointCount( Qt::Edge edge ) const;
205
206 /**
207 * Returns the text to use for the link point with the specified \a index on the specified \a edge.
208 */
209 virtual QString linkPointText( Qt::Edge edge, int index ) const;
210
211 /**
212 * Returns the location of the link point with the specified \a index on the specified \a edge.
213 */
214 QPointF linkPoint( Qt::Edge edge, int index, bool incoming ) const;
215
216 /**
217 * Returns the best link point to use for a link originating at a specified \a other item.
218 *
219 * \param other item at other end of link
220 * \param edge item edge for calculated best link point
221 * \returns calculated link point in item coordinates.
222 */
223 QPointF calculateAutomaticLinkPoint( QgsModelComponentGraphicItem *other, Qt::Edge &edge SIP_OUT ) const;
224
225 /**
226 * Returns the best link point to use for a link originating at a specified \a other point.
227 *
228 * \param other point for other end of link (in scene coordinates)
229 * \param edge item edge for calculated best link point
230 * \returns calculated link point in item coordinates.
231 */
232 QPointF calculateAutomaticLinkPoint( const QPointF &point, Qt::Edge &edge SIP_OUT ) const;
233
234 /**
235 * Called when the comment attached to the item should be edited.
236 *
237 * The default implementation does nothing.
238 */
editComment()239 virtual void editComment() {}
240
241 /**
242 * Returns TRUE if the component can be deleted.
243 */
canDeleteComponent()244 virtual bool canDeleteComponent() { return false; }
245
246 /**
247 * Called when the component should be deleted.
248 *
249 * The default implementation does nothing.
250 */
deleteComponent()251 virtual void deleteComponent() {}
252
253 signals:
254
255 // TODO - rework this, should be triggered externally when the model actually changes!
256
257 /**
258 * Emitted by the item to request a repaint of the parent model scene.
259 */
260 void requestModelRepaint();
261
262 /**
263 * Emitted when the definition of the associated component is about to be changed
264 * by the item.
265 *
266 * The \a text argument gives the translated text describing the change about to occur, and the
267 * optional \a id can be used to group the associated undo commands.
268 */
269 void aboutToChange( const QString &text, int id = 0 );
270
271 /**
272 * Emitted when the definition of the associated component is changed
273 * by the item.
274 */
275 void changed();
276
277 /**
278 * Emitted when item requests that all connected arrows are repainted.
279 */
280 void repaintArrows();
281
282 /**
283 * Emitted when item requires that all connected arrow paths are recalculated.
284 */
285 void updateArrowPaths();
286
287 /**
288 * Emitted when the item's size or position changes.
289 */
290 void sizePositionChanged();
291
292 protected slots:
293
294 /**
295 * Called when the component should be edited.
296 *
297 * The default implementation does nothing.
298 */
editComponent()299 virtual void editComponent() {}
300
301 protected:
302
303 /**
304 * Truncates a \a text string so that it fits nicely within the item's width,
305 * accounting for margins and interactive buttons.
306 */
307 QString truncatedTextForItem( const QString &text ) const;
308
309 /**
310 * Returns the fill color for the item for the specified \a state.
311 */
312 virtual QColor fillColor( State state ) const = 0;
313
314 /**
315 * Returns the stroke color for the item for the specified \a state.
316 */
317 virtual QColor strokeColor( State state ) const = 0;
318
319 /**
320 * Returns the label text color for the item for the specified \a state.
321 */
322 virtual QColor textColor( State state ) const = 0;
323
324 /**
325 * Returns the stroke style to use while rendering the outline of the item.
326 */
327 virtual Qt::PenStyle strokeStyle( State state ) const;
328
329 /**
330 * Returns the title alignment
331 */
332 virtual Qt::Alignment titleAlignment() const;
333
334 /**
335 * Returns a QPicture version of the item's icon, if available.
336 */
337 virtual QPicture iconPicture() const;
338
339 /**
340 * Returns a QPixmap version of the item's icon, if available.
341 */
342 virtual QPixmap iconPixmap() const;
343
344 /**
345 * Updates the position and size stored in the model for the associated comment
346 */
347 virtual void updateStoredComponentPosition( const QPointF &pos, const QSizeF &size ) = 0;
348
349 /**
350 * Updates the item's button positions, based on the current item rect.
351 */
352 void updateButtonPositions();
353
354 private:
355
356 QSizeF itemSize() const;
357
358 void updateToolTip( const QPointF &pos );
359
360 void fold( Qt::Edge edge, bool folded );
361
362 std::unique_ptr< QgsProcessingModelComponent > mComponent;
363 QgsProcessingModelAlgorithm *mModel = nullptr;
364
365 bool mInitialized = false;
366 QgsModelDesignerFoldButtonGraphicItem *mExpandTopButton = nullptr;
367 QgsModelDesignerFoldButtonGraphicItem *mExpandBottomButton = nullptr;
368
369 QString mLabel;
370
371 QgsModelDesignerFlatButtonGraphicItem *mEditButton = nullptr;
372 QgsModelDesignerFlatButtonGraphicItem *mDeleteButton = nullptr;
373
374 static constexpr double MIN_COMPONENT_WIDTH = 70;
375 static constexpr double MIN_COMPONENT_HEIGHT = 30;
376
377 static constexpr double DEFAULT_BUTTON_WIDTH = 16;
378 static constexpr double DEFAULT_BUTTON_HEIGHT = 16;
379 static constexpr double BUTTON_MARGIN = 2;
380 static constexpr double TEXT_MARGIN = 4;
381 static constexpr double RECT_PEN_SIZE = 2;
382 QSizeF mButtonSize { DEFAULT_BUTTON_WIDTH, DEFAULT_BUTTON_HEIGHT };
383
384 QFont mFont;
385
386 bool mIsHovering = false;
387 bool mIsMoving = false;
388 QSizeF mTempSize;
389
390 };
Q_DECLARE_OPERATORS_FOR_FLAGS(QgsModelComponentGraphicItem::Flags)391 Q_DECLARE_OPERATORS_FOR_FLAGS( QgsModelComponentGraphicItem::Flags )
392
393 /**
394 * \ingroup gui
395 * \brief A graphic item representing a model parameter (input) in the model designer.
396 * \warning Not stable API
397 * \since QGIS 3.14
398 */
399 class GUI_EXPORT QgsModelParameterGraphicItem : public QgsModelComponentGraphicItem
400 {
401 Q_OBJECT
402
403 public:
404
405 /**
406 * Constructor for QgsModelParameterGraphicItem for the specified \a parameter, with the specified \a parent item.
407 *
408 * The \a model argument specifies the associated processing model. Ownership of \a model is not transferred, and
409 * it must exist for the lifetime of this object.
410 *
411 * Ownership of \a parameter is transferred to the item.
412 */
413 QgsModelParameterGraphicItem( QgsProcessingModelParameter *parameter SIP_TRANSFER,
414 QgsProcessingModelAlgorithm *model,
415 QGraphicsItem *parent SIP_TRANSFERTHIS );
416
417 void contextMenuEvent( QGraphicsSceneContextMenuEvent *event ) override;
418 bool canDeleteComponent() override;
419
420 protected:
421
422 QColor fillColor( State state ) const override;
423 QColor strokeColor( State state ) const override;
424 QColor textColor( State state ) const override;
425 QPicture iconPicture() const override;
426 void updateStoredComponentPosition( const QPointF &pos, const QSizeF &size ) override;
427
428 protected slots:
429
430 void deleteComponent() override;
431
432 private:
433 QPicture mPicture;
434
435 };
436
437 /**
438 * \ingroup gui
439 * \brief A graphic item representing a child algorithm in the model designer.
440 * \warning Not stable API
441 * \since QGIS 3.14
442 */
443 class GUI_EXPORT QgsModelChildAlgorithmGraphicItem : public QgsModelComponentGraphicItem
444 {
445 Q_OBJECT
446
447 public:
448
449 /**
450 * Constructor for QgsModelChildAlgorithmGraphicItem for the specified \a child, with the specified \a parent item.
451 *
452 * The \a model argument specifies the associated processing model. Ownership of \a model is not transferred, and
453 * it must exist for the lifetime of this object.
454 *
455 * Ownership of \a child is transferred to the item.
456 */
457 QgsModelChildAlgorithmGraphicItem( QgsProcessingModelChildAlgorithm *child SIP_TRANSFER,
458 QgsProcessingModelAlgorithm *model,
459 QGraphicsItem *parent SIP_TRANSFERTHIS );
460 void contextMenuEvent( QGraphicsSceneContextMenuEvent *event ) override;
461 bool canDeleteComponent() override;
462
463 /**
464 * Sets the results obtained for this child algorithm for the last model execution through the dialog.
465 */
466 void setResults( const QVariantMap &results );
467
468 /**
469 * Sets the inputs used for this child algorithm for the last model execution through the dialog.
470 */
471 void setInputs( const QVariantMap &inputs );
472
473 protected:
474
475 QColor fillColor( State state ) const override;
476 QColor strokeColor( State state ) const override;
477 QColor textColor( State state ) const override;
478 QPixmap iconPixmap() const override;
479 QPicture iconPicture() const override;
480
481 int linkPointCount( Qt::Edge edge ) const override;
482 QString linkPointText( Qt::Edge edge, int index ) const override;
483 void updateStoredComponentPosition( const QPointF &pos, const QSizeF &size ) override;
484
485 protected slots:
486
487 void deleteComponent() override;
488
489 private slots:
490 void deactivateAlgorithm();
491 void activateAlgorithm();
492
493 private:
494 QPicture mPicture;
495 QPixmap mPixmap;
496 QVariantMap mResults;
497 QVariantMap mInputs;
498 bool mIsValid = true;
499 };
500
501
502 /**
503 * \ingroup gui
504 * \brief A graphic item representing a model output in the model designer.
505 * \warning Not stable API
506 * \since QGIS 3.14
507 */
508 class GUI_EXPORT QgsModelOutputGraphicItem : public QgsModelComponentGraphicItem
509 {
510 Q_OBJECT
511
512 public:
513
514 /**
515 * Constructor for QgsModelOutputGraphicItem for the specified \a output, with the specified \a parent item.
516 *
517 * The \a model argument specifies the associated processing model. Ownership of \a model is not transferred, and
518 * it must exist for the lifetime of this object.
519 *
520 * Ownership of \a output is transferred to the item.
521 */
522 QgsModelOutputGraphicItem( QgsProcessingModelOutput *output SIP_TRANSFER,
523 QgsProcessingModelAlgorithm *model,
524 QGraphicsItem *parent SIP_TRANSFERTHIS );
525
526 bool canDeleteComponent() override;
527
528 protected:
529
530 QColor fillColor( State state ) const override;
531 QColor strokeColor( State state ) const override;
532 QColor textColor( State state ) const override;
533 QPicture iconPicture() const override;
534 void updateStoredComponentPosition( const QPointF &pos, const QSizeF &size ) override;
535
536 protected slots:
537
538 void deleteComponent() override;
539
540 private:
541
542 QPicture mPicture;
543 };
544
545
546
547 /**
548 * \ingroup gui
549 * \brief A graphic item representing a model comment in the model designer.
550 * \warning Not stable API
551 * \since QGIS 3.14
552 */
553 class GUI_EXPORT QgsModelCommentGraphicItem : public QgsModelComponentGraphicItem
554 {
555 Q_OBJECT
556
557 public:
558
559 /**
560 * Constructor for QgsModelCommentGraphicItem for the specified \a comment, with the specified \a parent item.
561 *
562 * The \a model argument specifies the associated processing model. Ownership of \a model is not transferred, and
563 * it must exist for the lifetime of this object.
564 *
565 * Ownership of \a output is transferred to the item.
566 */
567 QgsModelCommentGraphicItem( QgsProcessingModelComment *comment SIP_TRANSFER,
568 QgsModelComponentGraphicItem *parentItem,
569 QgsProcessingModelAlgorithm *model,
570 QGraphicsItem *parent SIP_TRANSFERTHIS );
571 ~QgsModelCommentGraphicItem() override;
572 void contextMenuEvent( QGraphicsSceneContextMenuEvent *event ) override;
573 bool canDeleteComponent() override;
574
575 /**
576 * Returns the parent model component item.
577 */
578 QgsModelComponentGraphicItem *parentComponentItem() const;
579
580 protected:
581
582 QColor fillColor( State state ) const override;
583 QColor strokeColor( State state ) const override;
584 QColor textColor( State state ) const override;
585 Qt::PenStyle strokeStyle( State state ) const override;
586 void updateStoredComponentPosition( const QPointF &pos, const QSizeF &size ) override;
587
588 protected slots:
589
590 void deleteComponent() override;
591 void editComponent() override;
592 private:
593
594 QgsProcessingModelComment *modelComponent();
595
596 std::unique_ptr< QgsProcessingModelComponent > mParentComponent;
597 QPointer< QgsModelComponentGraphicItem > mParentItem;
598
599
600 };
601
602
603 /**
604 * \ingroup gui
605 * \brief A graphic item representing a group box in the model designer.
606 * \warning Not stable API
607 * \since QGIS 3.14
608 */
609 class GUI_EXPORT QgsModelGroupBoxGraphicItem : public QgsModelComponentGraphicItem
610 {
611 Q_OBJECT
612
613 public:
614
615 /**
616 * Constructor for QgsModelGroupBoxGraphicItem for the specified group \a box, with the specified \a parent item.
617 *
618 * The \a model argument specifies the associated processing model. Ownership of \a model is not transferred, and
619 * it must exist for the lifetime of this object.
620 *
621 * Ownership of \a output is transferred to the item.
622 */
623 QgsModelGroupBoxGraphicItem( QgsProcessingModelGroupBox *box SIP_TRANSFER,
624 QgsProcessingModelAlgorithm *model,
625 QGraphicsItem *parent SIP_TRANSFERTHIS );
626 ~QgsModelGroupBoxGraphicItem() override;
627 void contextMenuEvent( QGraphicsSceneContextMenuEvent *event ) override;
628 bool canDeleteComponent() override;
629 protected:
630
631 QColor fillColor( State state ) const override;
632 QColor strokeColor( State state ) const override;
633 QColor textColor( State state ) const override;
634 Qt::PenStyle strokeStyle( State state ) const override;
635 Qt::Alignment titleAlignment() const override;
636 void updateStoredComponentPosition( const QPointF &pos, const QSizeF &size ) override;
637
638 protected slots:
639
640 void deleteComponent() override;
641 void editComponent() override;
642 private:
643
644
645 };
646
647 ///@endcond
648
649 #endif // QGSMODELCOMPONENTGRAPHICITEM_H
650