Skip to content

Look Plug‐in

std::gregwar edited this page Jul 2, 2017 · 4 revisions

Look plug-ins define how nodes are drawn to the screen, and how the user can further interact with them. They are the last stage of ELVE visualization pipeline and their implementation is very tied to Qt's ways of drawing and interacting.

Default look plug-ins are BasicLook and BlockLook.

Development

Looks implementation follow the factory pattern. Meaning the following methods are producers and must be overridden:

/**
     * @brief node producing method
     * @param n a node to create look for
     * @return a subclass of NodeLook
     *
     * Re-implement this method.
     *
     * In this method you're given a node and must output a subclass of NodeLook
     * representing the node in a QGraphicsScene.
     */
Elve::NodeLook* node(const Elve::Node& n) override;

/**
     * @brief edge producing method
     * @param ancestor nodelook from which the edge comes
     * @param outi ancestor out index
     * @param children nodelook to which the edge goes
     * @param children in index
     * @return a subclass of EdgeLook
     *
     * Re-implement this method
     *
     * In this method you're given two NodeLook that you previously returned
     * from the node(const Node& n) method and must return an subclass of EdgeLook
     * that will represent the edge between those nodes.
     */
Elve::EdgeLook* edge(const Elve::NodeLook& ancestor,Elve::Index outi, const Elve::NodeLook& children, Elve::Index ini) override;

The duty of any look plugin is to instantiate subclasses of Elve::NodeLook and Elve::EdgeLook when asked for. Often the plugin subproject (.pro) will be filled with the corresponding subclasses.

Elve::NodeLook

Node looks subclasses indirectly inherit from QGraphicsItemGroup (and Movable, making the connection from the Layout Plug‐in). Hence it is a good idea to look at Qt's QGraphicsItemGroup documentation to keep in mind what are the possibilities.

The only methods that are mandatory to override are:

/**
     * @brief called when the selection color of the node changes
     * @param col
     */
    virtual void onColorChange(const QColor& col) = 0;

    /**
     * @brief called when the node is unselected and must be reverted to normal
     * colors
     */
    virtual void onColorReset() = 0;

Which take part in the Selection Masks System, it allows the selection to show on screen.

Then, for the node not to be invisible, QGraphicsItems must be added while constructing the NodeLook. There is no better place than the constructor to do this. Since the underlying node is available at this point. Samples are available in BasicLook and BlockLook provided implementations. The code to setup drawing is often messy, but one could use QML to model the node look if at ease with integration of QML in QGraphicsScenes, which will not be described here.

Of course, since access is granted to all Qt features for QGraphicsItems at this point, imagination is the only limit of what could be achieved in terms of interactivity, just pay attention to always call parent implementation while overriding eventsHandlers for examples, unless overwriting of the current node grab/click/select behaviour is wanted. At the time of writing concerned handlers are:

void mousePressEvent(QGraphicsSceneMouseEvent *event) override;
void mouseReleaseEvent(QGraphicsSceneMouseEvent *event) override;
void mouseMoveEvent(QGraphicsSceneMouseEvent *event) override;
void onStateChange(const QVector2D& pos, const QVector2D& speed) override;

Other optionnal methods to override are:

     /**
     * @brief position of the input at index
     * @param index index of the input
     * @return a point in the scene
     *
     * Re-implement this method to tell your EdgeLook instances where are their ends
     */
    virtual QPointF inputPos(int index) const ;

    /**
     * @brief position of the output at index
     * @param index index of the output
     * @return a point in the scene
     *
     * Re-implement this method to tell your EdgeLook instances where are their ends
     */
    virtual QPointF outputPos(int index) const ;

If connection points of various edges are to be differentiated (this imply the methods are used in the corresponding EdgeLook implementation). The default implementation returns the origin of the QGraphicsItemGroup.

In any case, it is recommended to take a look at the Elve::NodeLook documentation.

Elve::EdgeLooks

When writing an EdgeLook subclass the following method are to be overriden:

/**
     * @brief add the shape of this edge
     * @param path path to be updated
     *
     * The edges in elve are batched together to speed up drawing, this is thus
     * the only way to add geometry from the edge, update the provided path by
     * adding the current edge to it. Don't clear the given path.
     */
    virtual void addToPath(QPainterPath& path) = 0;

    /**
     * @brief state the pen that must draw this edge
     * @return the pen
     *
     * Since edges are batched in elve, and qt can draw only with one pen at
     * once. Edges are sorted by Pen values to group drawing of edges sharing
     * common pens.
     */
    virtual QPen pen() const = 0;

The former is the only way the Edge will be drawn, since edges are batched when drawing for efficiency reasons. A QPainterPath is provided and method must add the edge drawing to this path.

The latter allows to decide the way (the QPen) the previously filled path will be drawn.

This pattern allow the engine to group the edges by Pens, drawing them by groups sharing same drawing settings.

Examples can be found in BasicEdgeLook and BlockEdgeLook

Usage

To use looks plug-ins their shared libraries (.so,.dll,.dynlib...) must be placed in:

<elve_root>/plugins/looks

they are then available trough the Look menu and trough the CLI by issuing commands of this form:

<look_cli_name>_look [options]

Using CLI or GUI is fully equivalent, as the GUI button will construct the commands and issue them.

Clone this wiki locally