Esempio n. 1
0
void GraphConnection::deserialize(Poco::JSON::Object::Ptr obj)
{
    auto outputId = QString::fromStdString(obj->getValue<std::string>("outputId"));
    auto inputId = QString::fromStdString(obj->getValue<std::string>("inputId"));
    auto outputKey = QString::fromStdString(obj->getValue<std::string>("outputKey"));
    auto inputKey = QString::fromStdString(obj->getValue<std::string>("inputKey"));

    auto draw = dynamic_cast<GraphDraw *>(this->parent());
    assert(draw != nullptr);

    //resolve IO objects by id
    QPointer<GraphObject> inputObj = nullptr;
    QPointer<GraphObject> outputObj = nullptr;
    for (const auto obj : draw->getGraphObjects(false))
    {
        if (obj->getId() == inputId) inputObj = obj;
        if (obj->getId() == outputId) outputObj = obj;
    }

    if (inputObj.isNull()) throw Pothos::Exception("GraphConnection::deserialize()", "cant resolve object with ID: '"+inputId.toStdString()+"'");
    if (outputObj.isNull()) throw Pothos::Exception("GraphConnection::deserialize()", "cant resolve object with ID: '"+outputId.toStdString()+"'");

    this->setupEndpoint(GraphConnectionEndpoint(inputObj, GraphConnectableKey(inputKey, true)));
    this->setupEndpoint(GraphConnectionEndpoint(outputObj, GraphConnectableKey(outputKey, false)));

    assert(this->getInputEndpoint().isValid());
    assert(this->getOutputEndpoint().isValid());

    GraphObject::deserialize(obj);
}
GraphConnectionEndpoint GraphDraw::mousedEndpoint(const QPoint &pos)
{
    auto objs = this->items(pos);
    if (_connectLineItem) objs.removeOne(_connectLineItem.get());
    if (objs.empty()) return GraphConnectionEndpoint();
    auto topObj = dynamic_cast<GraphObject *>(objs.front());
    if (topObj == nullptr) return GraphConnectionEndpoint();
    const auto point = topObj->mapFromParent(this->mapToScene(pos));
    return GraphConnectionEndpoint(topObj, topObj->isPointingToConnectable(point));
}
Esempio n. 3
0
void GraphDraw::doClickSelection(const QPointF &point)
{
    const bool ctrlDown = QApplication::keyboardModifiers() & Qt::ControlModifier;
    const auto objs = this->items(this->mapFromScene(point));

    //nothing selected, clear the last selected endpoint
    if (objs.empty()) _lastClickSelectEp = GraphConnectionEndpoint();

    //connection creation logic
    if (not ctrlDown and not objs.empty())
    {
        auto topObj = dynamic_cast<GraphObject *>(objs.front());
        if (topObj == nullptr) return;
        GraphConnectionEndpoint thisEp(topObj, topObj->isPointingToConnectable(topObj->mapFromParent(point)));

        //valid keys, attempt to make a connection
        QPointer<GraphConnection> conn;
        if (thisEp.isValid() and _lastClickSelectEp.isValid() and not (thisEp == _lastClickSelectEp) and //end points valid
            (_lastClickSelectEp.getConnectableAttrs().direction == GRAPH_CONN_OUTPUT or _lastClickSelectEp.getConnectableAttrs().direction == GRAPH_CONN_SIGNAL) and //last endpoint is output
            (thisEp.getConnectableAttrs().direction == GRAPH_CONN_INPUT or thisEp.getConnectableAttrs().direction == GRAPH_CONN_SLOT)) //this click endpoint is input
        {
            try
            {
                conn = this->getGraphEditor()->makeConnection(thisEp, _lastClickSelectEp);
                this->getGraphEditor()->handleStateChange(GraphState("connect-arrow", tr("Connect %1[%2] to %3[%4]").arg(
                    conn->getOutputEndpoint().getObj()->getId(),
                    conn->getOutputEndpoint().getKey().id,
                    conn->getInputEndpoint().getObj()->getId(),
                    conn->getInputEndpoint().getKey().id
                )));
            }
            catch (const Pothos::Exception &ex)
            {
                poco_warning(Poco::Logger::get("PothosGui.GraphDraw.connect"), Poco::format("Cannot connect port %s[%s] to port %s[%s]: %s",
                    _lastClickSelectEp.getObj()->getId().toStdString(),
                    _lastClickSelectEp.getKey().id.toStdString(),
                    thisEp.getObj()->getId().toStdString(),
                    thisEp.getKey().id.toStdString(),
                    ex.message()));
            }
        }

        //cleanup after new connection
        if (not conn.isNull())
        {
            _lastClickSelectEp = GraphConnectionEndpoint();
            this->deselectAllObjs();
        }
        //otherwise save the click select
        else
        {
            _lastClickSelectEp = thisEp;
        }
    }
}
void GraphDraw::mouseReleaseEvent(QMouseEvent *event)
{
    QGraphicsView::mouseReleaseEvent(event);
    if (QApplication::keyboardModifiers() & Qt::ControlModifier) return;

    //releasing the connection line to create
    if (_connectLineItem)
    {
        const auto thisEp = this->mousedEndpoint(event->pos());
        this->tryToMakeConnection(thisEp);
        auto actions = MainActions::global();
        if (not actions->clickConnectModeAction->isChecked())
            _lastClickSelectEp = GraphConnectionEndpoint();
    }

    //emit the move event up to the graph editor
    if (_selectionState == SELECTION_STATE_MOVE)
    {
        bool moved = false;
        for (const auto &pair : _preMovePositions)
        {
            if (pair.first->pos() != pair.second) moved = true;
        }
        if (moved) this->getGraphEditor()->handleStateChange(
            GraphState("transform-move", tr("Move %1").arg(this->getSelectionDescription(~GRAPH_CONNECTION))));
    }

    this->clearSelectionState();
    this->render();
}
void GraphDraw::mousePressEvent(QMouseEvent *event)
{
    QGraphicsView::mousePressEvent(event);
    if (QApplication::keyboardModifiers() & Qt::ControlModifier) return;

    //record the conditions of this press event, nothing is changed
    if (event->button() == Qt::LeftButton)
    {
        _selectionState = SELECTION_STATE_PRESS;

        const auto objs = this->getObjectsAtPos(event->pos());
        if (objs.empty())
        {
            //perform deselect when background is clicked
            _lastClickSelectEp = GraphConnectionEndpoint();
            this->deselectAllObjs();
        }
        else
        {
            //make the clicked object topmost
            objs.front()->setZValue(this->getMaxZValue()+1);
        }

        //handle click selection for connections
        const auto thisEp = this->mousedEndpoint(event->pos());

        //enter the connect drag mode and object immobilization
        //slots are exempt because they are the block's body
        if (thisEp.isValid())
        {
            auto topObj = thisEp.getObj();
            if (thisEp.getKey().direction != GRAPH_CONN_SLOT)
            {
                _connectLineItem.reset(new QGraphicsLineItem(topObj));
                _connectLineItem->setPen(QPen(QColor(GraphObjectDefaultPenColor), ConnectModeLineWidth));
                _connectModeImmobilizer.reset(new GraphObjectImmobilizer(topObj));
            }

            //if separate clicks to connect when try to make connection
            auto actions = MainActions::global();
            if (actions->clickConnectModeAction->isChecked())
            {
                if (not this->tryToMakeConnection(thisEp))
                {
                    //stash this endpoint when connection is not made
                    _lastClickSelectEp = thisEp;
                }
            }

            //stash the connection endpoint for a drag+release connection
            else _lastClickSelectEp = thisEp;
        }
    }

    this->render();
}
bool GraphDraw::tryToMakeConnection(const GraphConnectionEndpoint &thisEp)
{
    QPointer<GraphConnection> conn;
    const auto &lastEp = _lastClickSelectEp;

    //valid keys, attempt to make a connection when the endpoints differ in direction
    if (thisEp.isValid() and lastEp.isValid() and //both are valid
        lastEp.getKey().isInput() != thisEp.getKey().isInput()) //directions differ
    {
        try
        {
            conn = this->getGraphEditor()->makeConnection(thisEp, _lastClickSelectEp);
            this->getGraphEditor()->handleStateChange(GraphState("connect-arrow", tr("Connect %1[%2] to %3[%4]").arg(
                conn->getOutputEndpoint().getObj()->getId(),
                conn->getOutputEndpoint().getKey().id,
                conn->getInputEndpoint().getObj()->getId(),
                conn->getInputEndpoint().getKey().id
            )));
        }
        catch (const Pothos::Exception &ex)
        {
            static auto &logger = Poco::Logger::get("PothosFlow.GraphDraw");
            logger.warning("Cannot connect port %s[%s] to port %s[%s]: %s",
                _lastClickSelectEp.getObj()->getId().toStdString(),
                _lastClickSelectEp.getKey().id.toStdString(),
                thisEp.getObj()->getId().toStdString(),
                thisEp.getKey().id.toStdString(),
                ex.message());
        }

        //cleanup regardless of failure
        _lastClickSelectEp = GraphConnectionEndpoint();
        this->deselectAllObjs();
    }

    //if this is a signal or slot connection
    //open the properties panel for configuration
    if (conn and conn->isSignalOrSlot())
    {
        emit this->modifyProperties(conn);
    }

    return bool(conn);
}
Esempio n. 7
0
void GraphEditor::handleMoveGraphObjects(const int index)
{
    if (not this->isVisible()) return;
    if (index >= this->count()) return;
    auto draw = this->getCurrentGraphDraw();
    auto desc = tr("Move %1 to %2").arg(draw->getSelectionDescription(~GRAPH_CONNECTION), this->tabText(index));

    //move all selected objects
    for (auto obj : draw->getObjectsSelected())
    {
        obj->setSelected(false);
        this->getGraphDraw(index)->scene()->addItem(obj);
    }

    //reparent all connections based on endpoints:
    std::vector<GraphConnection *> boundaryConnections;
    for (auto obj : this->getGraphObjects(GRAPH_CONNECTION))
    {
        auto conn = dynamic_cast<GraphConnection *>(obj);
        assert(conn != nullptr);

        //Connection has the same endpoints, so make sure that the parent is corrected to the endpoint
        if (conn->getOutputEndpoint().getObj()->scene() == conn->getInputEndpoint().getObj()->scene())
        {
            if (conn->getOutputEndpoint().getObj()->scene() != conn->scene())
            {
                conn->getInputEndpoint().getObj()->scene()->addItem(conn);
            }
        }

        //otherwise stash it for more processing
        else
        {
            boundaryConnections.push_back(conn);
        }
    }

    //create breakers for output endpoints that have to cross
    for (auto conn : boundaryConnections)
    {
        const auto &epOut = conn->getOutputEndpoint();
        const auto &epIn = conn->getInputEndpoint();

        auto sigSlotPairs = conn->getSigSlotPairs();
        if (sigSlotPairs.empty()) sigSlotPairs.resize(1); //add empty
        for (const auto &sigSlotPair : sigSlotPairs)
        {
            auto breaker = findInputBreaker(this, epOut);
            if (breaker != nullptr) continue;

            breaker = new GraphBreaker(epOut.getObj()->draw());
            breaker->setInput(true);
            const auto name = QString("%1[%2]").arg(epOut.getObj()->getId(), epOut.getKey().id);
            breaker->setId(this->newId(name));
            breaker->setNodeName(breaker->getId()); //the first of its name
            breaker->setRotation(epIn.getObj()->rotation());
            breaker->setPos(epIn.getObj()->pos());

            auto outConn = this->makeConnection(epOut, GraphConnectionEndpoint(breaker, breaker->getConnectableKeys().at(0)));
            if (not sigSlotPair.first.isEmpty()) outConn->addSigSlotPair(std::make_pair(sigSlotPair.first, breaker->getConnectableKeys().at(0).id));
            if (outConn->scene() != breaker->scene()) breaker->scene()->addItem(outConn); //use desired parent
        }
    }

    //create breakers for input endpoints that have to cross
    for (auto conn : boundaryConnections)
    {
        const auto &epOut = conn->getOutputEndpoint();
        const auto &epIn = conn->getInputEndpoint();

        auto sigSlotPairs = conn->getSigSlotPairs();
        if (sigSlotPairs.empty()) sigSlotPairs.resize(1); //add empty
        for (const auto &sigSlotPair : sigSlotPairs)
        {
            //find the existing breaker or make a new one
            const auto name = findInputBreaker(this, epOut)->getNodeName();
            GraphBreaker *breaker = nullptr;
            for (auto obj : this->getGraphObjects(GRAPH_BREAKER))
            {
                if (obj->draw() != epIn.getObj()->draw()) continue;
                auto outBreaker = dynamic_cast<GraphBreaker *>(obj);
                assert(outBreaker != nullptr);
                if (outBreaker->isInput()) continue;
                if (outBreaker->getNodeName() != name) continue;
                breaker = outBreaker;
                break;
            }

            //make a new output breaker
            if (breaker == nullptr)
            {
                breaker = new GraphBreaker(epIn.getObj()->draw());
                breaker->setInput(false);
                breaker->setId(this->newId(name));
                breaker->setNodeName(name);
                breaker->setRotation(epOut.getObj()->rotation());
                breaker->setPos(epOut.getObj()->pos());
            }

            //connect to this breaker
            auto inConn = this->makeConnection(epIn, GraphConnectionEndpoint(breaker, breaker->getConnectableKeys().at(0)));
            if (not sigSlotPair.second.isEmpty()) inConn->addSigSlotPair(std::make_pair(breaker->getConnectableKeys().at(0).id, sigSlotPair.second));
            if (inConn->scene() != breaker->scene()) breaker->scene()->addItem(inConn); //use desired parent
        }

        delete conn;
    }

    handleStateChange(GraphState("transform-move", desc));
}