Exemple #1
0
void GraphEditor::handleBlockXcrement(const int adj)
{
    if (not this->isVisible()) return;
    auto draw = this->getCurrentGraphDraw();
    GraphObjectList changedObjects;
    for (auto obj : draw->getObjectsSelected(GRAPH_BLOCK))
    {
        auto block = dynamic_cast<GraphBlock *>(obj);
        assert(block != nullptr);
        for (const auto &propKey : block->getProperties())
        {
            auto paramDesc = block->getParamDesc(propKey);
            if (paramDesc->has("widgetType") and paramDesc->getValue<std::string>("widgetType") == "SpinBox")
            {
                const auto newValue = block->getPropertyValue(propKey).toInt() + adj;
                block->setPropertyValue(propKey, QString("%1").arg(newValue));
                changedObjects.push_back(block);
                break;
            }
        }
    }

    if (changedObjects.empty()) return;
    const auto desc = (changedObjects.size() == 1)? changedObjects.front()->getId() : tr("selected");
    if (adj > 0) handleStateChange(GraphState("list-add", tr("Increment %1").arg(desc)));
    if (adj < 0) handleStateChange(GraphState("list-remove", tr("Decrement %1").arg(desc)));
}
Exemple #2
0
void GraphEditor::load(void)
{
    auto fileName = this->getCurrentFilePath().toStdString();

    if (fileName.empty())
    {
        _stateManager->resetToDefault();
        handleStateChange(GraphState("document-new", tr("Create new topology")));
        _stateManager->saveCurrent();
        this->render();
        return;
    }

    try
    {
        poco_information_f1(Poco::Logger::get("PothosGui.GraphEditor.load"), "Loading %s from file", fileName);
        postStatusMessage(tr("Loading %1").arg(QString::fromStdString(fileName)));
        std::ifstream inFile(fileName.c_str());
        this->loadState(inFile);
    }
    catch (const std::exception &ex)
    {
        poco_error_f2(Poco::Logger::get("PothosGui.GraphEditor.load"), "Error loading %s: %s", fileName, std::string(ex.what()));
    }

    _stateManager->resetToDefault();
    handleStateChange(GraphState("document-new", tr("Load topology from file")));
    _stateManager->saveCurrent();
    this->updateGraphEditorMenus();
    this->render();
}
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 BlockPropertiesPanel::handleCommitButton(void)
{
    _updateTimer->stop();

    //were there changes?
    std::vector<QString> propertiesModified;
    for (const auto &prop : _block->getProperties())
    {
        if (_block->getPropertyValue(prop.getKey()) != _propIdToOriginal[prop.getKey()]) propertiesModified.push_back(prop.getName());
    }

    //was the ID changed?
    if (_idOriginal != _block->getId()) propertiesModified.push_back(tr("ID"));

    //was the affinity zone changed?
    if (_affinityZoneOriginal != _block->getAffinityZone()) propertiesModified.push_back(tr("Affinity Zone"));

    if (propertiesModified.empty()) return this->handleCancelButton();

    //emit a new graph state event
    auto desc = (propertiesModified.size() == 1)? propertiesModified.front() : tr("properties");
    emit this->stateChanged(GraphState("document-properties", tr("Edit %1 %2").arg(_block->getId()).arg(desc)));

    //done with this panel
    this->deleteLater();
}
void ConnectionPropertiesPanel::handleCommit(void)
{
    const auto createdPairs = mySetDifference(_conn->getSigSlotPairs(), _originalKeyPairs);
    const auto removedPairs = mySetDifference(_originalKeyPairs, _conn->getSigSlotPairs());

    //description of the change
    QString desc;
    if (createdPairs.empty() and removedPairs.empty()) return this->handleCancel();
    else if (createdPairs.empty() and removedPairs.size() == 1)
    {
        desc = tr("Removed %1->%2")
            .arg(_conn->getKeyName(removedPairs.at(0).first, _conn->getOutputEndpoint()))
            .arg(_conn->getKeyName(removedPairs.at(0).second, _conn->getInputEndpoint()));
    }
    else if (createdPairs.size() == 1 and removedPairs.empty())
    {
        desc = tr("Created %1->%2")
            .arg(_conn->getKeyName(createdPairs.at(0).first, _conn->getOutputEndpoint()))
            .arg(_conn->getKeyName(createdPairs.at(0).second, _conn->getInputEndpoint()));
    }
    else
    {
        desc = tr("Changed signals->slots");
    }

    //emit a new graph state event
    emit this->stateChanged(GraphState("document-properties", desc));
}
Exemple #6
0
void GraphDraw::mouseReleaseEvent(QMouseEvent *event)
{
    QGraphicsView::mouseReleaseEvent(event);

    //mouse released from a pressed state - alter selections at point
    if (_selectionState == SELECTION_STATE_PRESS)
    {
        this->doClickSelection(this->mapToScene(event->pos()));
    }

    //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();
}
Exemple #7
0
void GraphEditor::handleSetEnabled(const bool enb)
{
    if (not this->isVisible()) return;
    auto draw = this->getCurrentGraphDraw();

    auto objs = draw->getObjectsSelected();
    if (objs.isEmpty()) return;

    for (auto obj : objs)
    {
        obj->setEnabled(enb);
    }

    if (enb) handleStateChange(GraphState("document-import", tr("Enable %1").arg(draw->getSelectionDescription())));
    else handleStateChange(GraphState("document-export", tr("Disable %1").arg(draw->getSelectionDescription())));
}
Exemple #8
0
void GraphEditor::handleAddBlock(const Poco::JSON::Object::Ptr &blockDesc, const QPointF &where)
{
    if (not blockDesc) return;
    auto draw = this->getCurrentGraphDraw();
    auto block = new GraphBlock(draw);
    block->setBlockDesc(blockDesc);

    QString hint;
    const auto title = block->getTitle();
    for (int i = 0; i < title.length(); i++)
    {
        if (i == 0 and title.at(i).isNumber()) hint.append(QChar('_'));
        if (title.at(i).isLetterOrNumber() or title.at(i).toLatin1() == '_')
        {
            hint.append(title.at(i));
        }
    }
    block->setId(this->newId(hint));

    //set highest z-index on new block
    block->setZValue(draw->getMaxZValue()+1);

    block->setPos(where);
    block->setRotation(0);
    handleStateChange(GraphState("list-add", tr("Create block %1").arg(title)));
}
void BlockPropertiesPanel::handleCancelButton(void)
{
    _updateTimer->stop();

    //empty state causes reset to the last known point
    emit this->stateChanged(GraphState());

    this->deleteLater();
}
Exemple #10
0
void GraphEditor::handleDeleteGraphPage(void)
{
    if (not this->isVisible()) return;
    const auto oldName = this->tabText(this->currentIndex());
    this->removeTab(this->currentIndex());
    if (this->count() == 0) this->makeDefaultPage();
    this->updateGraphEditorMenus();

    handleStateChange(GraphState("edit-delete", tr("Delete graph page ") + oldName));
}
Exemple #11
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;
        }
    }
}
Exemple #12
0
void GraphEditor::handleRotateRight(void)
{
    if (not this->isVisible()) return;
    auto draw = this->getCurrentGraphDraw();
    //TODO rotate group of objects around central point
    for (auto obj : draw->getObjectsSelected(~GRAPH_CONNECTION))
    {
        obj->rotateRight();
    }
    handleStateChange(GraphState("object-rotate-right", tr("Rotate %1 right").arg(draw->getSelectionDescription(~GRAPH_CONNECTION))));
}
Exemple #13
0
void GraphEditor::handleCreateGraphPage(void)
{
    if (not this->isVisible()) return;
    const QString newName = QInputDialog::getText(this, tr("Create page"),
        tr("New page name"), QLineEdit::Normal, tr("untitled"));
    if (newName.isEmpty()) return;
    this->addTab(new GraphDraw(this), newName);
    this->updateGraphEditorMenus();

    handleStateChange(GraphState("document-new", tr("Create graph page ") + newName));
}
Exemple #14
0
void GraphEditor::handleRenameGraphPage(void)
{
    if (not this->isVisible()) return;
    const auto oldName = this->tabText(this->currentIndex());
    const QString newName = QInputDialog::getText(this, tr("Rename page"),
        tr("New page name"), QLineEdit::Normal, oldName);
    if (newName.isEmpty()) return;
    this->setTabText(this->currentIndex(), newName);
    this->updateGraphEditorMenus();

    handleStateChange(GraphState("edit-rename", tr("Rename graph page ") + oldName + " -> " + newName));
}
Exemple #15
0
void GraphEditor::handleAffinityZoneClicked(const QString &zone)
{
    if (not this->isVisible()) return;
    auto draw = this->getCurrentGraphDraw();

    for (auto obj : draw->getObjectsSelected(GRAPH_BLOCK))
    {
        auto block = dynamic_cast<GraphBlock *>(obj);
        assert(block != nullptr);
        block->setAffinityZone(zone);
    }
    handleStateChange(GraphState("document-export", tr("Set %1 affinity zone").arg(draw->getSelectionDescription(GRAPH_BLOCK))));
}
Exemple #16
0
void GraphEditor::handleDelete(void)
{
    if (not this->isVisible()) return;
    auto draw = this->getCurrentGraphDraw();
    auto desc = tr("Delete %1").arg(draw->getSelectionDescription());

    //delete all selected graph objects
    for (auto obj : draw->getObjectsSelected())
    {
        delete obj;
    }

    this->deleteFlagged();

    handleStateChange(GraphState("edit-delete", desc));
}
Exemple #17
0
void GraphEditor::handleInsertGraphWidget(QObject *obj)
{
    auto block = dynamic_cast<GraphBlock *>(obj);
    assert(block != nullptr);
    assert(block->isGraphWidget());

    auto draw = this->getCurrentGraphDraw();
    auto display = new GraphWidget(draw);
    display->setGraphBlock(block);
    display->setId(this->newId("Widget"+block->getId()));
    display->setZValue(draw->getMaxZValue()+1);
    display->setPos(draw->getLastContextMenuPos());
    display->setRotation(0);

    handleStateChange(GraphState("insert-image", tr("Insert widget %1").arg(block->getId())));
}
Exemple #18
0
void GraphEditor::handleCreateBreaker(const bool isInput)
{
    if (not this->isVisible()) return;

    const auto dirName = isInput?tr("input"):tr("output");
    const auto newName = QInputDialog::getText(this, tr("Create %1 breaker").arg(dirName),
        tr("New breaker node name"), QLineEdit::Normal, tr("untitled"));
    if (newName.isEmpty()) return;

    auto draw = this->getCurrentGraphDraw();
    auto breaker = new GraphBreaker(draw);
    breaker->setInput(isInput);
    breaker->setNodeName(newName);
    breaker->setId(this->newId(newName));
    breaker->setPos(draw->getLastContextMenuPos());

    handleStateChange(GraphState("document-new", tr("Create %1 breaker %2").arg(dirName, newName)));
}
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);
}
Exemple #20
0
void GraphEditor::handlePaste(void)
{
    if (not this->isVisible()) return;
    auto draw = this->getCurrentGraphDraw();

    auto mimeData = QApplication::clipboard()->mimeData();
    const bool canPaste = mimeData->hasFormat("text/json/pothos_object_array") and
                      not mimeData->data("text/json/pothos_object_array").isEmpty();
    if (not canPaste) return;

    //extract object array
    const auto data = mimeData->data("text/json/pothos_object_array");
    const std::string dataStr(data.constData(), data.size());
    std::istringstream iss(dataStr);
    Poco::JSON::Parser p; p.parse(iss);
    auto graphObjects = p.getHandler()->asVar().extract<Poco::JSON::Array::Ptr>();
    assert(graphObjects);

    //rewrite ids
    std::map<std::string, std::string> oldIdToNew;
    for (size_t objIndex = 0; objIndex < graphObjects->size(); objIndex++)
    {
        const auto jGraphObj = graphObjects->getObject(objIndex);
        auto oldId = jGraphObj->getValue<std::string>("id");
        oldIdToNew[oldId] = this->newId(QString::fromStdString(oldId)).toStdString();
    }
    for (size_t objIndex = 0; objIndex < graphObjects->size();)
    {
        for (auto &pair : *graphObjects->getObject(objIndex))
        {
            if (QString::fromStdString(pair.first).endsWith("id", Qt::CaseInsensitive))
            {
                //if not in oldIdToNew, remove from list
                if (oldIdToNew.count(pair.second) == 0)
                {
                    graphObjects->remove(objIndex);
                    goto nextObj;
                }
                pair.second = oldIdToNew[pair.second];
            }
        }
        objIndex++;
        nextObj: continue;
    }

    //unselect all objects
    draw->deselectAllObjs();

    //create objects
    GraphObjectList objsToMove;
    objsToMove.append(handlePasteType(draw, graphObjects, "Block"));
    objsToMove.append(handlePasteType(draw, graphObjects, "Breaker"));
    handlePasteType(draw, graphObjects, "Connection"); //dont append, connection position doesnt matter
    objsToMove.append(handlePasteType(draw, graphObjects, "Widget"));

    //deal with initial positions of pasted objects
    QPointF cornerest(1e6, 1e6);
    for (auto obj : objsToMove)
    {
        cornerest.setX(std::min(cornerest.x(), obj->pos().x()));
        cornerest.setY(std::min(cornerest.y(), obj->pos().y()));
    }

    //determine an acceptable position to center the paste
    auto view = dynamic_cast<QGraphicsView *>(this->currentWidget());
    auto pastePos = view->mapToScene(view->mapFromGlobal(QCursor::pos()));
    if (not view->sceneRect().contains(pastePos))
    {
        pastePos = view->mapToScene(this->size().width()/2, this->size().height()/2);
    }

    //move objects into position
    for (auto obj : objsToMove) obj->setPos(obj->pos()-cornerest+pastePos);

    handleStateChange(GraphState("edit-paste", tr("Paste %1").arg(draw->getSelectionDescription())));
}
Exemple #21
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));
}