Core: Gui: DAGView: skeleton implemented
Need to enable through parameter BaseApp/Preferences/DAGView
This commit is contained in:
parent
6349a3d478
commit
3e65723472
|
@ -33,6 +33,7 @@ include_directories(
|
|||
${CMAKE_CURRENT_BINARY_DIR}/propertyeditor
|
||||
${CMAKE_CURRENT_BINARY_DIR}/TaskView
|
||||
${CMAKE_CURRENT_BINARY_DIR}/Quarter
|
||||
${CMAKE_CURRENT_BINARY_DIR}/DAGView
|
||||
${Boost_INCLUDE_DIRS}
|
||||
${COIN3D_INCLUDE_DIRS}
|
||||
${PYTHON_INCLUDE_DIRS}
|
||||
|
@ -261,6 +262,8 @@ set(Gui_MOC_HDRS
|
|||
TaskView/TaskWatcher.h
|
||||
TaskView/TaskEditControl.h
|
||||
TaskView/TaskView.h
|
||||
DAGView/DAGView.h
|
||||
DAGView/DAGModel.h
|
||||
)
|
||||
#qt4_wrap_cpp(Gui_MOC_SRCS ${Gui_MOC_HDRS})
|
||||
fc_wrap_cpp(Gui_MOC_SRCS ${Gui_MOC_HDRS})
|
||||
|
@ -556,6 +559,8 @@ SET(Dock_Windows_CPP_SRCS
|
|||
Tree.cpp
|
||||
TreeView.cpp
|
||||
ProjectView.cpp
|
||||
DAGView/DAGView.cpp
|
||||
DAGView/DAGModel.cpp
|
||||
)
|
||||
SET(Dock_Windows_HPP_SRCS
|
||||
CombiView.h
|
||||
|
@ -568,6 +573,8 @@ SET(Dock_Windows_HPP_SRCS
|
|||
Tree.h
|
||||
TreeView.h
|
||||
ProjectView.h
|
||||
DAGView/DAGView.h
|
||||
DAGView/DAGModel.h
|
||||
)
|
||||
SET(Dock_Windows_SRCS
|
||||
${Dock_Windows_CPP_SRCS}
|
||||
|
|
702
src/Gui/DAGView/DAGModel.cpp
Normal file
702
src/Gui/DAGView/DAGModel.cpp
Normal file
|
@ -0,0 +1,702 @@
|
|||
/***************************************************************************
|
||||
* Copyright (c) 2015 Thomas Anderson <blobfish[at]gmx.com> *
|
||||
* *
|
||||
* This file is part of the FreeCAD CAx development system. *
|
||||
* *
|
||||
* This library is free software; you can redistribute it and/or *
|
||||
* modify it under the terms of the GNU Library General Public *
|
||||
* License as published by the Free Software Foundation; either *
|
||||
* version 2 of the License, or (at your option) any later version. *
|
||||
* *
|
||||
* This library is distributed in the hope that it will be useful, *
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
|
||||
* GNU Library General Public License for more details. *
|
||||
* *
|
||||
* You should have received a copy of the GNU Library General Public *
|
||||
* License along with this library; see the file COPYING.LIB. If not, *
|
||||
* write to the Free Software Foundation, Inc., 59 Temple Place, *
|
||||
* Suite 330, Boston, MA 02111-1307, USA *
|
||||
* *
|
||||
***************************************************************************/
|
||||
|
||||
#include "PreCompiled.h"
|
||||
#ifndef _PreComp_
|
||||
#include <boost/signals.hpp>
|
||||
#include <boost/bind.hpp>
|
||||
#include <boost/graph/topological_sort.hpp>
|
||||
|
||||
#include <QAbstractEventDispatcher>
|
||||
#include <QApplication>
|
||||
#include <QString>
|
||||
#include <QGraphicsTextItem>
|
||||
#include <QGraphicsEllipseItem>
|
||||
#include <QGraphicsPixmapItem>
|
||||
#include <QGraphicsRectItem>
|
||||
#include <QGraphicsSceneHoverEvent>
|
||||
#include <QPen>
|
||||
#include <QBrush>
|
||||
#include <QColor>
|
||||
#include <QPainter>
|
||||
#endif
|
||||
|
||||
#include <deque>
|
||||
#include <unordered_set>
|
||||
|
||||
#include <Base/TimeInfo.h>
|
||||
#include <Gui/Application.h>
|
||||
#include <Gui/Document.h>
|
||||
#include <Gui/ViewProviderDocumentObject.h>
|
||||
#include <Gui/Selection.h>
|
||||
|
||||
#include "DAGModel.h"
|
||||
|
||||
using namespace Gui;
|
||||
using namespace DAG;
|
||||
|
||||
ViewEntryRectItem::ViewEntryRectItem(QGraphicsItem* parent) : QGraphicsRectItem(parent)
|
||||
{
|
||||
selected = false;
|
||||
preSelected = false;
|
||||
}
|
||||
|
||||
//I dont think I should have to call invalidate
|
||||
//and definitely not on the whole scene!
|
||||
//if we have performance problems, this will definitely
|
||||
//be something to re-visit. I am not wasting anymore time on
|
||||
//this right now.
|
||||
// this->scene()->invalidate();
|
||||
// this->scene()->invalidate(this->sceneTransform().inverted().mapRect(this->boundingRect()));
|
||||
// update(boundingRect());
|
||||
//note: I haven't tried this again since I turned BSP off.
|
||||
|
||||
void ViewEntryRectItem::paint(QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget* widget)
|
||||
{
|
||||
//TODO figure out how to mimic painting of itemviews. QStyle, QStyledItemDelegate.
|
||||
|
||||
QBrush brush = backgroundBrush;
|
||||
if (selected)
|
||||
brush = selectionBrush;
|
||||
if (preSelected)
|
||||
brush = preSelectionBrush;
|
||||
if (selected && preSelected)
|
||||
brush = bothBrush;
|
||||
|
||||
//heights are negative.
|
||||
float radius = std::min(this->rect().width(), std::fabs(this->rect().height())) * 0.1;
|
||||
painter->setBrush(brush);
|
||||
painter->setPen(this->pen()); //should be Qt::NoPen.
|
||||
painter->drawRoundedRect(this->rect(), radius, radius);
|
||||
|
||||
// QGraphicsRectItem::paint(painter, option, widget);
|
||||
}
|
||||
|
||||
VertexProperty::VertexProperty() :
|
||||
rectangle(new ViewEntryRectItem()),
|
||||
point(new QGraphicsEllipseItem()),
|
||||
icon(new QGraphicsPixmapItem()),
|
||||
text(new QGraphicsTextItem()),
|
||||
row(0),
|
||||
column(0),
|
||||
colorIndex(0),
|
||||
lastVisibleState(VisibilityState::None)
|
||||
{
|
||||
//All flags are disabled by default.
|
||||
this->rectangle->setFlags(QGraphicsItem::ItemIsSelectable);
|
||||
|
||||
//set z values.
|
||||
this->rectangle->setZValue(-1000.0);
|
||||
this->point->setZValue(1000.0);
|
||||
this->icon->setZValue(0.0);
|
||||
this->text->setZValue(0.0);
|
||||
}
|
||||
|
||||
const GraphLinkRecord& Model::findRecord(Vertex vertexIn)
|
||||
{
|
||||
typedef GraphLinkContainer::index<GraphLinkRecord::ByVertex>::type List;
|
||||
const List &list = graphLink->get<GraphLinkRecord::ByVertex>();
|
||||
List::const_iterator it = list.find(vertexIn);
|
||||
assert(it != list.end());
|
||||
return *it;
|
||||
}
|
||||
|
||||
const GraphLinkRecord& Model::findRecord(const App::DocumentObject* dObjectIn)
|
||||
{
|
||||
typedef GraphLinkContainer::index<GraphLinkRecord::ByDObject>::type List;
|
||||
const List &list = graphLink->get<GraphLinkRecord::ByDObject>();
|
||||
List::const_iterator it = list.find(dObjectIn);
|
||||
assert(it != list.end());
|
||||
return *it;
|
||||
}
|
||||
|
||||
const GraphLinkRecord& Model::findRecord(const ViewProviderDocumentObject* VPDObjectIn)
|
||||
{
|
||||
typedef GraphLinkContainer::index<GraphLinkRecord::ByVPDObject>::type List;
|
||||
const List &list = graphLink->get<GraphLinkRecord::ByVPDObject>();
|
||||
List::const_iterator it = list.find(VPDObjectIn);
|
||||
assert(it != list.end());
|
||||
return *it;
|
||||
}
|
||||
|
||||
const GraphLinkRecord& Model::findRecord(const ViewEntryRectItem* rectIn)
|
||||
{
|
||||
typedef GraphLinkContainer::index<GraphLinkRecord::ByRectItem>::type List;
|
||||
const List &list = graphLink->get<GraphLinkRecord::ByRectItem>();
|
||||
List::const_iterator it = list.find(rectIn);
|
||||
assert(it != list.end());
|
||||
return *it;
|
||||
}
|
||||
|
||||
const GraphLinkRecord& Model::findRecord(const std::string &stringIn)
|
||||
{
|
||||
typedef GraphLinkContainer::index<GraphLinkRecord::ByUniqueName>::type List;
|
||||
const List &list = graphLink->get<GraphLinkRecord::ByUniqueName>();
|
||||
List::const_iterator it = list.find(stringIn);
|
||||
assert(it != list.end());
|
||||
return *it;
|
||||
}
|
||||
|
||||
void Model::eraseRecord(const ViewProviderDocumentObject* VPDObjectIn)
|
||||
{
|
||||
typedef GraphLinkContainer::index<GraphLinkRecord::ByVPDObject>::type List;
|
||||
const List &list = graphLink->get<GraphLinkRecord::ByVPDObject>();
|
||||
List::iterator it = list.find(VPDObjectIn);
|
||||
assert(it != list.end());
|
||||
graphLink->get<GraphLinkRecord::ByVPDObject>().erase(it);
|
||||
}
|
||||
|
||||
Model::Model(QObject *parentIn, const Gui::Document &documentIn) : QGraphicsScene(parentIn)
|
||||
{
|
||||
//turned off BSP as it was giving inconsistent discovery of items
|
||||
//underneath cursor.
|
||||
this->setItemIndexMethod(QGraphicsScene::NoIndex);
|
||||
|
||||
theGraph = std::shared_ptr<Graph>(new Graph());
|
||||
graphLink = std::shared_ptr<GraphLinkContainer>(new GraphLinkContainer());
|
||||
setupViewConstants();
|
||||
|
||||
graphDirty = false;
|
||||
currentPrehighlight = nullptr;
|
||||
|
||||
connectNewObject = documentIn.signalNewObject.connect(boost::bind(&Model::slotNewObject, this, _1));
|
||||
connectDelObject = documentIn.signalDeletedObject.connect(boost::bind(&Model::slotDeleteObject, this, _1));
|
||||
connectChgObject = documentIn.signalChangedObject.connect(boost::bind(&Model::slotChangeObject, this, _1, _2));
|
||||
}
|
||||
|
||||
Model::~Model()
|
||||
{
|
||||
if (connectNewObject.connected())
|
||||
connectNewObject.disconnect();
|
||||
if (connectDelObject.connected())
|
||||
connectDelObject.disconnect();
|
||||
if (connectChgObject.connected())
|
||||
connectChgObject.disconnect();
|
||||
|
||||
removeAllItems();
|
||||
}
|
||||
|
||||
void Model::setupViewConstants()
|
||||
{
|
||||
QFontMetrics fontMetric(qApp->font());
|
||||
fontHeight = fontMetric.height();
|
||||
verticalSpacing = 1.0;
|
||||
rowHeight = (fontHeight + 2.0 * verticalSpacing) * -1.0; //pixel space top and bottom.
|
||||
iconSize = fontHeight;
|
||||
pointSize = fontHeight / 2.0;
|
||||
pointSpacing = pointSize;
|
||||
pointToIcon = iconSize;
|
||||
rowPadding = fontHeight;
|
||||
backgroundBrushes = {qApp->palette().base(), qApp->palette().alternateBase()};
|
||||
forgroundBrushes =
|
||||
{
|
||||
QBrush(Qt::red),
|
||||
QBrush(Qt::darkRed),
|
||||
QBrush(Qt::green),
|
||||
QBrush(Qt::darkGreen),
|
||||
QBrush(Qt::blue),
|
||||
QBrush(Qt::darkBlue),
|
||||
QBrush(Qt::cyan),
|
||||
QBrush(Qt::darkCyan),
|
||||
QBrush(Qt::magenta),
|
||||
QBrush(Qt::darkMagenta),
|
||||
// QBrush(Qt::yellow), can't read
|
||||
QBrush(Qt::darkYellow),
|
||||
QBrush(Qt::gray),
|
||||
QBrush(Qt::darkGray),
|
||||
QBrush(Qt::lightGray)
|
||||
}; //reserve some of the these for highlight stuff.
|
||||
}
|
||||
|
||||
void Model::slotNewObject(const ViewProviderDocumentObject &VPDObjectIn)
|
||||
{
|
||||
Vertex virginVertex = boost::add_vertex(*theGraph);
|
||||
this->addItem((*theGraph)[virginVertex].rectangle.get());
|
||||
this->addItem((*theGraph)[virginVertex].point.get());
|
||||
this->addItem((*theGraph)[virginVertex].icon.get());
|
||||
this->addItem((*theGraph)[virginVertex].text.get());
|
||||
|
||||
GraphLinkRecord virginRecord;
|
||||
virginRecord.DObject = VPDObjectIn.getObject();
|
||||
virginRecord.VPDObject = &VPDObjectIn;
|
||||
virginRecord.rectItem = (*theGraph)[virginVertex].rectangle.get();
|
||||
virginRecord.uniqueName = std::string(virginRecord.DObject->getNameInDocument());
|
||||
virginRecord.vertex = virginVertex;
|
||||
graphLink->insert(virginRecord);
|
||||
|
||||
//construct pixmaps.
|
||||
QIcon baseIcon = VPDObjectIn.getIcon();
|
||||
(*theGraph)[virginVertex].pixmapEnabled = baseIcon.pixmap(iconSize, iconSize, QIcon::Normal, QIcon::On);
|
||||
(*theGraph)[virginVertex].pixmapDisabled = baseIcon.pixmap(iconSize, iconSize, QIcon::Disabled, QIcon::Off);
|
||||
|
||||
//setup rectangle.
|
||||
auto *rectangle = (*theGraph)[virginVertex].rectangle.get();
|
||||
rectangle->setPen(Qt::NoPen);
|
||||
QColor preSelectionColor = qApp->palette().highlight().color();
|
||||
preSelectionColor.setAlphaF(0.25);
|
||||
rectangle->setPreselectionBrush(QBrush(preSelectionColor));
|
||||
rectangle->setSelectionBrush(qApp->palette().highlight());
|
||||
QColor bothSelectionColor = qApp->palette().highlight().color();
|
||||
bothSelectionColor.setAlphaF(0.75);
|
||||
rectangle->setBothBrush(QBrush(bothSelectionColor));
|
||||
|
||||
//setup point.
|
||||
auto *point = (*theGraph)[virginVertex].point.get();
|
||||
point->setPen(Qt::NoPen);
|
||||
|
||||
graphDirty = true;
|
||||
}
|
||||
|
||||
void Model::slotDeleteObject(const ViewProviderDocumentObject &VPDObjectIn)
|
||||
{
|
||||
Vertex vertex = findRecord(&VPDObjectIn).vertex;
|
||||
|
||||
//remove items from scene.
|
||||
this->removeItem((*theGraph)[vertex].rectangle.get());
|
||||
this->removeItem((*theGraph)[vertex].point.get());
|
||||
this->removeItem((*theGraph)[vertex].icon.get());
|
||||
this->removeItem((*theGraph)[vertex].text.get());
|
||||
|
||||
//remove connector items
|
||||
auto outRange = boost::out_edges(vertex, *theGraph);
|
||||
for (auto outEdgeIt = outRange.first; outEdgeIt != outRange.second; ++outEdgeIt)
|
||||
this->removeItem((*theGraph)[*outEdgeIt].connector.get());
|
||||
auto inRange = boost::in_edges(vertex, *theGraph);
|
||||
for (auto inEdgeIt = inRange.first; inEdgeIt != inRange.second; ++inEdgeIt)
|
||||
this->removeItem((*theGraph)[*inEdgeIt].connector.get());
|
||||
|
||||
//remove the actual vertex.
|
||||
boost::clear_vertex(vertex, *theGraph);
|
||||
boost::remove_vertex(vertex, *theGraph);
|
||||
|
||||
eraseRecord(&VPDObjectIn);
|
||||
graphDirty = true;
|
||||
}
|
||||
|
||||
void Model::slotChangeObject(const ViewProviderDocumentObject &VPDObjectIn, const App::Property& propertyIn)
|
||||
{
|
||||
std::string name("Empty Name");
|
||||
if (propertyIn.getName()) //getName can return 0.
|
||||
name = propertyIn.getName();
|
||||
assert(!name.empty());
|
||||
|
||||
// std::cout << std::endl << "inside changed object." << std::endl <<
|
||||
// "Property name is: " << name << std::endl <<
|
||||
// "Property type is: " << propertyIn.getTypeId().getName() << std::endl << std::endl;
|
||||
|
||||
//renaming of objects.
|
||||
if (std::string("Label") == name)
|
||||
{
|
||||
const GraphLinkRecord &record = findRecord(&VPDObjectIn);
|
||||
auto *text = (*theGraph)[record.vertex].text.get();
|
||||
text->setPlainText(QString::fromUtf8(record.DObject->Label.getValue()));
|
||||
}
|
||||
|
||||
//link changes. these require a recalculation of connectors.
|
||||
const static std::unordered_set<std::string> linkTypes =
|
||||
{
|
||||
"App::PropertyLink",
|
||||
"App::PropertyLinkList",
|
||||
"App::PropertyLinkSub",
|
||||
"App::PropertyLinkSubList"
|
||||
};
|
||||
|
||||
if (linkTypes.find(propertyIn.getTypeId().getName()) != linkTypes.end())
|
||||
{
|
||||
const GraphLinkRecord &record = findRecord(&VPDObjectIn);
|
||||
boost::clear_vertex(record.vertex, *theGraph);
|
||||
graphDirty = true;
|
||||
}
|
||||
}
|
||||
|
||||
void Model::selectionChanged(const SelectionChanges& msg)
|
||||
{
|
||||
//note that treeview uses set selection which sends a message with just a document name
|
||||
//and no object name. Have to explore further.
|
||||
|
||||
//lamda for clearing selections.
|
||||
auto clearSelection = [this]()
|
||||
{
|
||||
BGL_FORALL_VERTICES_T(currentVertex, *theGraph, Graph)
|
||||
{
|
||||
ViewEntryRectItem *rect = (*theGraph)[currentVertex].rectangle.get();
|
||||
assert(rect);
|
||||
rect->selectionOff();
|
||||
}
|
||||
};
|
||||
|
||||
//lamda for getting rectangle.
|
||||
auto getRectangle = [this](const char *in)
|
||||
{
|
||||
assert(in);
|
||||
std::string name(in);
|
||||
assert(!name.empty());
|
||||
const GraphLinkRecord &record = findRecord(name);
|
||||
ViewEntryRectItem *rect = (*theGraph)[record.vertex].rectangle.get();
|
||||
assert(rect);
|
||||
return rect;
|
||||
};
|
||||
|
||||
if (msg.Type == SelectionChanges::AddSelection)
|
||||
{
|
||||
if (msg.pObjectName)
|
||||
getRectangle(msg.pObjectName)->selectionOn();
|
||||
}
|
||||
else if(msg.Type == SelectionChanges::RmvSelection)
|
||||
{
|
||||
if (msg.pObjectName)
|
||||
getRectangle(msg.pObjectName)->selectionOff();
|
||||
}
|
||||
else if(msg.Type == SelectionChanges::SetSelection)
|
||||
{
|
||||
clearSelection();
|
||||
|
||||
auto selections = Gui::Selection().getSelection(msg.pDocName);
|
||||
for (const auto &selection : selections)
|
||||
{
|
||||
assert(selection.FeatName);
|
||||
getRectangle(selection.FeatName)->selectionOn();
|
||||
}
|
||||
}
|
||||
else if(msg.Type == SelectionChanges::ClrSelection)
|
||||
{
|
||||
clearSelection();
|
||||
}
|
||||
|
||||
this->invalidate();
|
||||
}
|
||||
|
||||
void Model::awake()
|
||||
{
|
||||
if (graphDirty)
|
||||
{
|
||||
updateSlot();
|
||||
this->invalidate();
|
||||
}
|
||||
updateVisible();
|
||||
}
|
||||
|
||||
void Model::updateSlot()
|
||||
{
|
||||
Base::TimeInfo startTime;
|
||||
|
||||
//here we will cycle through the graph updating edges.
|
||||
//empty outList means it is a root.
|
||||
//empty inList means it is a leaf.
|
||||
|
||||
BGL_FORALL_VERTICES_T(currentVertex, *theGraph, Graph)
|
||||
{
|
||||
bool foundFirst = false; //temp hack.
|
||||
#if 0
|
||||
//based on claim children. don't think this will be enough.
|
||||
const auto *VPDObject = findRecord(currentVertex).VPDObject;
|
||||
auto children = VPDObject->claimChildren();
|
||||
for (const auto *currentChild : children)
|
||||
{
|
||||
Vertex otherVertex = findRecord(currentChild).vertex;
|
||||
bool result;
|
||||
Edge edge;
|
||||
boost::tie(edge, result) = boost::add_edge(currentVertex, otherVertex, *theGraph);
|
||||
if (result)
|
||||
{
|
||||
if (!foundFirst)
|
||||
{
|
||||
(*theGraph)[edge].relation = EdgeProperty::BranchTag::Continue;
|
||||
foundFirst = true;
|
||||
}
|
||||
else
|
||||
(*theGraph)[edge].relation = EdgeProperty::BranchTag::Terminate;
|
||||
|
||||
(*theGraph)[edge].connector = std::shared_ptr<QGraphicsPathItem>(new QGraphicsPathItem());
|
||||
(*theGraph)[edge].connector->setZValue(0.0);
|
||||
this->addItem((*theGraph)[edge].connector.get());
|
||||
}
|
||||
}
|
||||
#else
|
||||
//based on outlist. this won't be enough either.
|
||||
const App::DocumentObject *currentDObject = findRecord(currentVertex).DObject;
|
||||
std::vector<App::DocumentObject *> otherDObjects = currentDObject->getOutList();
|
||||
for (auto ¤tOtherDObject : otherDObjects)
|
||||
{
|
||||
Vertex otherVertex = findRecord(currentOtherDObject).vertex;
|
||||
bool result;
|
||||
Edge edge;
|
||||
boost::tie(edge, result) = boost::add_edge(currentVertex, otherVertex, *theGraph);
|
||||
if (result)
|
||||
{
|
||||
if (!foundFirst)
|
||||
{
|
||||
(*theGraph)[edge].relation = EdgeProperty::BranchTag::Continue;
|
||||
foundFirst = true;
|
||||
}
|
||||
else
|
||||
(*theGraph)[edge].relation = EdgeProperty::BranchTag::Terminate;
|
||||
|
||||
(*theGraph)[edge].connector = std::shared_ptr<QGraphicsPathItem>(new QGraphicsPathItem());
|
||||
(*theGraph)[edge].connector->setZValue(0.0);
|
||||
this->addItem((*theGraph)[edge].connector.get());
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
indexVerticesEdges();
|
||||
Path sorted;
|
||||
boost::topological_sort(*theGraph, std::back_inserter(sorted));
|
||||
int currentRow = 0;
|
||||
int currentColumn = -1; //we know the first one will be a root so we can assume it will get kicked up to 0.
|
||||
int maxColumn = currentColumn; //used for determining offset of icons and text.
|
||||
float maxTextLength = 0;
|
||||
for (const auto ¤tVertex : sorted)
|
||||
{
|
||||
if (boost::out_degree(currentVertex, *theGraph) == 0)
|
||||
currentColumn++;
|
||||
else
|
||||
{
|
||||
bool foundTarget = false;
|
||||
OutEdgeIterator it, itEnd;
|
||||
boost::tie(it, itEnd) = boost::out_edges(currentVertex, *theGraph);
|
||||
for (;it != itEnd; ++it)
|
||||
{
|
||||
if ((*theGraph)[*it].relation == EdgeProperty::BranchTag::Continue)
|
||||
{
|
||||
Vertex target = boost::target(*it, *theGraph);
|
||||
currentColumn = (*theGraph)[target].column;
|
||||
foundTarget = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!foundTarget)
|
||||
currentColumn++;
|
||||
}
|
||||
|
||||
maxColumn = std::max(currentColumn, maxColumn);
|
||||
QBrush currentBrush(forgroundBrushes.at(currentColumn % forgroundBrushes.size()));
|
||||
|
||||
auto *rectangle = (*theGraph)[currentVertex].rectangle.get();
|
||||
rectangle->setRect(-rowPadding, 0.0, rowPadding, rowHeight); //calculate actual length later.
|
||||
rectangle->setTransform(QTransform::fromTranslate(0, rowHeight * currentRow));
|
||||
rectangle->setBackgroundBrush(backgroundBrushes[currentRow % backgroundBrushes.size()]);
|
||||
|
||||
auto *point = (*theGraph)[currentVertex].point.get();
|
||||
point->setRect(0.0, 0.0, -pointSize, -pointSize);
|
||||
point->setTransform(QTransform::fromTranslate(pointSpacing * currentColumn,
|
||||
rowHeight * currentRow + rowHeight / 2.0 + pointSize / 2.0));
|
||||
point->setBrush(currentBrush);
|
||||
|
||||
auto *pixmap = (*theGraph)[currentVertex].icon.get();
|
||||
pixmap->setTransform(QTransform::fromTranslate(0.0, rowHeight * currentRow + rowHeight)); //calculate x location later.
|
||||
|
||||
auto *text = (*theGraph)[currentVertex].text.get();
|
||||
text->setPlainText(QString::fromUtf8(findRecord(currentVertex).DObject->Label.getValue()));
|
||||
maxTextLength = std::max(maxTextLength, static_cast<float>(text->boundingRect().width()));
|
||||
text->setTransform(QTransform::fromTranslate
|
||||
(0.0, rowHeight * currentRow + rowHeight - verticalSpacing * 2.0)); //calculate x location later.
|
||||
(*theGraph)[currentVertex].lastVisibleState = VisibilityState::None; //force visual update for color.
|
||||
|
||||
//store column and row int the graph. use for connectors later.
|
||||
(*theGraph)[currentVertex].row = currentRow;
|
||||
(*theGraph)[currentVertex].column = currentColumn;
|
||||
(*theGraph)[currentVertex].colorIndex = currentColumn % forgroundBrushes.size();
|
||||
|
||||
//our list is topo sorted so all dependents should be located, so we can build the connectors.
|
||||
//will have some more logic for connector path, simple for now.
|
||||
float currentX = pointSpacing * currentColumn - pointSize / 2.0;
|
||||
float currentY = rowHeight * currentRow + rowHeight / 2.0;
|
||||
OutEdgeIterator it, itEnd;
|
||||
boost::tie(it, itEnd) = boost::out_edges(currentVertex, *theGraph);
|
||||
for (; it != itEnd; ++it)
|
||||
{
|
||||
Vertex target = boost::target(*it, *theGraph);
|
||||
float dependentX = pointSpacing * (*theGraph)[target].column - pointSize / 2.0; //on center.
|
||||
float dependentY = rowHeight * (*theGraph)[target].row + rowHeight / 2.0;
|
||||
|
||||
QGraphicsPathItem *pathItem = (*theGraph)[*it].connector.get();
|
||||
pathItem->setBrush(Qt::NoBrush);
|
||||
QPainterPath path;
|
||||
path.moveTo(currentX, currentY);
|
||||
if (currentColumn != (*theGraph)[target].column)
|
||||
path.lineTo(dependentX, currentY);
|
||||
path.lineTo(dependentX, dependentY); //y is always different.
|
||||
pathItem->setPath(path);
|
||||
}
|
||||
|
||||
currentRow++;
|
||||
}
|
||||
|
||||
float columnSpacing = (maxColumn * pointSpacing);
|
||||
for (const auto ¤tVertex : sorted)
|
||||
{
|
||||
auto *pixmap = (*theGraph)[currentVertex].icon.get();
|
||||
QTransform iconTransform = QTransform::fromTranslate(columnSpacing + pointToIcon, 0.0);
|
||||
pixmap->setTransform(pixmap->transform() * iconTransform);
|
||||
|
||||
auto *text = (*theGraph)[currentVertex].text.get();
|
||||
QTransform textTransform = QTransform::fromTranslate(columnSpacing + pointToIcon + iconSize, 0.0);
|
||||
text->setTransform(text->transform() * textTransform);
|
||||
|
||||
auto *rectangle = (*theGraph)[currentVertex].rectangle.get();
|
||||
QRectF rect = rectangle->rect();
|
||||
rect.setWidth(columnSpacing + pointToIcon + iconSize + maxTextLength + 2.0 * rowPadding);
|
||||
rectangle->setRect(rect);
|
||||
}
|
||||
|
||||
//Modeling_Challenge_Casting_ta4 with 59 features: "Initialize DAG View time: 0.007"
|
||||
// std::cout << "Initialize DAG View time: " << Base::TimeInfo::diffTimeF(startTime, Base::TimeInfo()) << std::endl;
|
||||
|
||||
// outputGraphviz<Graph>(*theGraph, "./graphviz.dot");
|
||||
graphDirty = false;
|
||||
}
|
||||
|
||||
void Model::indexVerticesEdges()
|
||||
{
|
||||
std::size_t index = 0;
|
||||
|
||||
//index vertices.
|
||||
VertexIterator it, itEnd;
|
||||
for(boost::tie(it, itEnd) = boost::vertices(*theGraph); it != itEnd; ++it)
|
||||
{
|
||||
boost::put(boost::vertex_index, *theGraph, *it, index);
|
||||
index++;
|
||||
}
|
||||
|
||||
//index edges. didn't need this when I put it in.
|
||||
EdgeIterator eit, eitEnd;
|
||||
index = 0;
|
||||
for(boost::tie(eit, eitEnd) = boost::edges(*theGraph); eit != eitEnd; ++eit)
|
||||
{
|
||||
boost::put(boost::edge_index, *theGraph, *eit, index);
|
||||
index++;
|
||||
}
|
||||
}
|
||||
|
||||
void Model::removeAllItems()
|
||||
{
|
||||
if (theGraph)
|
||||
{
|
||||
BGL_FORALL_VERTICES_T(currentVertex, *theGraph, Graph)
|
||||
{
|
||||
this->removeItem((*theGraph)[currentVertex].rectangle.get());
|
||||
this->removeItem((*theGraph)[currentVertex].point.get());
|
||||
this->removeItem((*theGraph)[currentVertex].icon.get());
|
||||
this->removeItem((*theGraph)[currentVertex].text.get());
|
||||
}
|
||||
|
||||
BGL_FORALL_EDGES_T(currentEdge, *theGraph, Graph)
|
||||
this->removeItem((*theGraph)[currentEdge].connector.get());
|
||||
}
|
||||
}
|
||||
|
||||
void Model::updateVisible()
|
||||
{
|
||||
//not sure I want to use the same pixmap merge for failing feature icons.
|
||||
//thinking maybe red background or another column of icons for state?
|
||||
|
||||
BGL_FORALL_VERTICES_T(currentVertex, *theGraph, Graph)
|
||||
{
|
||||
const GraphLinkRecord &record = findRecord(currentVertex);
|
||||
auto *text = (*theGraph)[currentVertex].text.get();
|
||||
auto *pixmap = (*theGraph)[currentVertex].icon.get();
|
||||
QIcon baseIcon = record.VPDObject->getIcon();
|
||||
VisibilityState currentState = (record.VPDObject->isShow()) ? (VisibilityState::On) : (VisibilityState::Off);
|
||||
if
|
||||
(
|
||||
(currentState != (*theGraph)[currentVertex].lastVisibleState) ||
|
||||
((*theGraph)[currentVertex].lastVisibleState == VisibilityState::None)
|
||||
)
|
||||
{
|
||||
if (record.VPDObject->isShow())
|
||||
{
|
||||
text->setDefaultTextColor(forgroundBrushes.at((*theGraph)[currentVertex].colorIndex).color());
|
||||
pixmap->setPixmap((*theGraph)[currentVertex].pixmapEnabled);
|
||||
}
|
||||
else
|
||||
{
|
||||
text->setDefaultTextColor(qApp->palette().color(QPalette::Disabled, QPalette::Text));
|
||||
pixmap->setPixmap((*theGraph)[currentVertex].pixmapDisabled);
|
||||
}
|
||||
(*theGraph)[currentVertex].lastVisibleState = currentState;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ViewEntryRectItem* Model::getRectFromPosition(const QPointF& position)
|
||||
{
|
||||
ViewEntryRectItem *rect = nullptr;
|
||||
auto theItems = this->items(position, Qt::IntersectsItemBoundingRect, Qt::DescendingOrder);
|
||||
for (auto *currentItem : theItems)
|
||||
{
|
||||
rect = dynamic_cast<ViewEntryRectItem *>(currentItem);
|
||||
if (rect) break;
|
||||
}
|
||||
|
||||
return rect;
|
||||
}
|
||||
|
||||
void Model::mouseMoveEvent(QGraphicsSceneMouseEvent* event)
|
||||
{
|
||||
auto clearPrehighlight = [this]()
|
||||
{
|
||||
if (currentPrehighlight)
|
||||
{
|
||||
currentPrehighlight->preHighlightOff();
|
||||
currentPrehighlight = nullptr;
|
||||
}
|
||||
};
|
||||
|
||||
ViewEntryRectItem *rect = getRectFromPosition(event->scenePos());
|
||||
if (!rect)
|
||||
{
|
||||
clearPrehighlight();
|
||||
return;
|
||||
}
|
||||
|
||||
if (rect == currentPrehighlight)
|
||||
return;
|
||||
|
||||
clearPrehighlight();
|
||||
rect->preHighlightOn();
|
||||
currentPrehighlight = rect;
|
||||
invalidate();
|
||||
|
||||
QGraphicsScene::mouseMoveEvent(event);
|
||||
}
|
||||
|
||||
void Model::mousePressEvent(QGraphicsSceneMouseEvent* event)
|
||||
{
|
||||
ViewEntryRectItem *rect = getRectFromPosition(event->scenePos());
|
||||
if (rect)
|
||||
{
|
||||
const App::DocumentObject *dObject = findRecord(rect).DObject;
|
||||
Gui::Selection().addSelection(dObject->getDocument()->getName(), dObject->getNameInDocument());
|
||||
}
|
||||
|
||||
//need an else here to clear the selections.
|
||||
//don't have current selection stored yet.
|
||||
|
||||
QGraphicsScene::mousePressEvent(event);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
#include <moc_DAGModel.cpp>
|
354
src/Gui/DAGView/DAGModel.h
Normal file
354
src/Gui/DAGView/DAGModel.h
Normal file
|
@ -0,0 +1,354 @@
|
|||
/***************************************************************************
|
||||
* Copyright (c) 2015 Thomas Anderson <blobfish[at]gmx.com> *
|
||||
* *
|
||||
* This file is part of the FreeCAD CAx development system. *
|
||||
* *
|
||||
* This library is free software; you can redistribute it and/or *
|
||||
* modify it under the terms of the GNU Library General Public *
|
||||
* License as published by the Free Software Foundation; either *
|
||||
* version 2 of the License, or (at your option) any later version. *
|
||||
* *
|
||||
* This library is distributed in the hope that it will be useful, *
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
|
||||
* GNU Library General Public License for more details. *
|
||||
* *
|
||||
* You should have received a copy of the GNU Library General Public *
|
||||
* License along with this library; see the file COPYING.LIB. If not, *
|
||||
* write to the Free Software Foundation, Inc., 59 Temple Place, *
|
||||
* Suite 330, Boston, MA 02111-1307, USA *
|
||||
* *
|
||||
***************************************************************************/
|
||||
|
||||
#ifndef DAGMODEL_H
|
||||
#define DAGMODEL_H
|
||||
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
#include <boost/signal.hpp>
|
||||
#include <boost/multi_index_container.hpp>
|
||||
#include <boost/multi_index/member.hpp>
|
||||
#include <boost/multi_index/ordered_index.hpp>
|
||||
#include <boost/graph/adjacency_list.hpp>
|
||||
#include <boost/graph/iteration_macros.hpp>
|
||||
#include <boost/graph/reverse_graph.hpp>
|
||||
#include <boost/graph/graphviz.hpp>
|
||||
|
||||
#include <QGraphicsScene>
|
||||
#include <QGraphicsRectItem>
|
||||
#include <QBrush>
|
||||
|
||||
class QGraphicsSceneHoverEvent;
|
||||
|
||||
namespace App{class DocumentObject;}
|
||||
|
||||
namespace Gui
|
||||
{
|
||||
class Document;
|
||||
class ViewProviderDocumentObject;
|
||||
class SelectionChanges;
|
||||
|
||||
namespace DAG
|
||||
{
|
||||
/*all right I give up! the parenting combined with the zvalues is fubar!
|
||||
* you can't control any kind of layering between children of separate parents
|
||||
*/
|
||||
class ViewEntryRectItem : public QGraphicsRectItem
|
||||
{
|
||||
public:
|
||||
ViewEntryRectItem(QGraphicsItem* parent = 0);
|
||||
void setBackgroundBrush(const QBrush &brushIn){backgroundBrush = brushIn;}
|
||||
void setPreselectionBrush(const QBrush &brushIn){preSelectionBrush = brushIn;}
|
||||
void setSelectionBrush(const QBrush &brushIn){selectionBrush = brushIn;}
|
||||
void setBothBrush(const QBrush &brushIn){bothBrush = brushIn;}
|
||||
void preHighlightOn(){preSelected = true;}
|
||||
void preHighlightOff(){preSelected = false;}
|
||||
void selectionOn(){selected = true;}
|
||||
void selectionOff(){selected = false;}
|
||||
bool isSelected(){return selected;}
|
||||
bool isPreSelected(){return preSelected;}
|
||||
protected:
|
||||
virtual void paint(QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget* widget = 0);
|
||||
private:
|
||||
QBrush backgroundBrush; //!< brush used for background. not used yet.
|
||||
QBrush selectionBrush; //!< brush used when selected.
|
||||
QBrush preSelectionBrush; //!< brush used when pre selected.
|
||||
QBrush bothBrush; //!< brush for when both selected and preSelected.
|
||||
//start with booleans, may expand to state.
|
||||
bool selected;
|
||||
bool preSelected;
|
||||
};
|
||||
|
||||
enum class VisibilityState
|
||||
{
|
||||
None = 0, //<! not determined.
|
||||
On, //<! shown
|
||||
Off, //<! hidden
|
||||
};
|
||||
|
||||
/*! @brief Graph vertex information
|
||||
*
|
||||
* My data stored for each vertex;
|
||||
*/
|
||||
struct VertexProperty
|
||||
{
|
||||
VertexProperty();
|
||||
std::shared_ptr<ViewEntryRectItem> rectangle; //!< background
|
||||
std::shared_ptr<QGraphicsEllipseItem> point; //!< point
|
||||
std::shared_ptr<QGraphicsPixmapItem> icon; //!< icon
|
||||
std::shared_ptr<QGraphicsTextItem> text; //!< text
|
||||
int row; //!< row for this entry.
|
||||
int column; //!< column number containing the point.
|
||||
int colorIndex; //!< index in forground brushes
|
||||
VisibilityState lastVisibleState; //!< visibility test.
|
||||
QPixmap pixmapEnabled;
|
||||
QPixmap pixmapDisabled;
|
||||
};
|
||||
/*! @brief boost data for each vertex.
|
||||
*
|
||||
* needed to create an internal index for vertex. needed for listS.
|
||||
* color is needed by some algorithms */
|
||||
typedef boost::property
|
||||
<
|
||||
boost::vertex_index_t, std::size_t,
|
||||
boost::property <boost::vertex_color_t, boost::default_color_type, VertexProperty>
|
||||
> vertex_prop;
|
||||
|
||||
/*! @brief Graph edge information
|
||||
*
|
||||
* My data stored for each edge;
|
||||
*/
|
||||
struct EdgeProperty
|
||||
{
|
||||
//! Feature relation meta data.
|
||||
enum class BranchTag
|
||||
{
|
||||
None = 0, //!< not defined.
|
||||
Create, //!< create a new branch.
|
||||
Continue, //!< continue a branch.
|
||||
Terminate //!< terminate a branch.
|
||||
};
|
||||
BranchTag relation;
|
||||
std::shared_ptr <QGraphicsPathItem> connector; //!< line representing link between nodes.
|
||||
};
|
||||
/*! @brief needed to create an internal index for graph edges. needed for setS.*/
|
||||
typedef boost::property<boost::edge_index_t, std::size_t, EdgeProperty> edge_prop;
|
||||
|
||||
typedef boost::adjacency_list<boost::setS, boost::listS, boost::bidirectionalS, vertex_prop, edge_prop> Graph;
|
||||
typedef boost::graph_traits<Graph>::vertex_descriptor Vertex;
|
||||
typedef boost::graph_traits<Graph>::edge_descriptor Edge;
|
||||
typedef boost::graph_traits<Graph>::vertex_iterator VertexIterator;
|
||||
typedef boost::graph_traits<Graph>::edge_iterator EdgeIterator;
|
||||
typedef boost::graph_traits<Graph>::in_edge_iterator InEdgeIterator;
|
||||
typedef boost::graph_traits<Graph>::out_edge_iterator OutEdgeIterator;
|
||||
typedef boost::graph_traits<Graph>::adjacency_iterator VertexAdjacencyIterator;
|
||||
typedef boost::reverse_graph<Graph, Graph&> GraphReversed;
|
||||
typedef std::vector<Vertex> Path; //!< a path or any array of vertices
|
||||
|
||||
template <class GraphEW>
|
||||
class Edge_writer {
|
||||
public:
|
||||
Edge_writer(const GraphEW &graphEWIn) : graphEW(graphEWIn) {}
|
||||
template <class EdgeW>
|
||||
void operator()(std::ostream& out, const EdgeW& edgeW) const
|
||||
{
|
||||
out << "[label=\"";
|
||||
out << "edge";
|
||||
out << "\"]";
|
||||
}
|
||||
private:
|
||||
const GraphEW &graphEW;
|
||||
};
|
||||
|
||||
template <class GraphVW>
|
||||
class Vertex_writer {
|
||||
public:
|
||||
Vertex_writer(const GraphVW &graphVWIn) : graphVW(graphVWIn) {}
|
||||
template <class VertexW>
|
||||
void operator()(std::ostream& out, const VertexW& vertexW) const
|
||||
{
|
||||
out << "[label=\"";
|
||||
out << graphVW[vertexW].text->toPlainText().toAscii().data();
|
||||
out << "\"]";
|
||||
}
|
||||
private:
|
||||
const GraphVW &graphVW;
|
||||
};
|
||||
|
||||
template <class GraphIn>
|
||||
void outputGraphviz(const GraphIn &graphIn, const std::string &filePath)
|
||||
{
|
||||
std::ofstream file(filePath.c_str());
|
||||
boost::write_graphviz(file, graphIn, Vertex_writer<GraphIn>(graphIn),
|
||||
Edge_writer<GraphIn>(graphIn));
|
||||
}
|
||||
|
||||
//! get all the leaves of the templated graph. Not used right now.
|
||||
template <class GraphIn>
|
||||
class RakeLeaves
|
||||
{
|
||||
typedef boost::graph_traits<Graph>::vertex_descriptor GraphInVertex;
|
||||
typedef std::vector<GraphInVertex> GraphInVertices;
|
||||
public:
|
||||
RakeLeaves(const GraphIn &graphIn) : graph(graphIn) {}
|
||||
GraphInVertices operator()() const
|
||||
{
|
||||
GraphInVertices out;
|
||||
BGL_FORALL_VERTICES_T(currentVertex, graph, GraphIn)
|
||||
{
|
||||
if (boost::out_degree(currentVertex, graph) == 0)
|
||||
out.push_back(currentVertex);
|
||||
}
|
||||
return out;
|
||||
}
|
||||
private:
|
||||
const GraphIn &graph;
|
||||
};
|
||||
|
||||
//! get all the roots of the templated graph. Not used right now.
|
||||
template <class GraphIn>
|
||||
class DigRoots
|
||||
{
|
||||
typedef boost::graph_traits<Graph>::vertex_descriptor GraphInVertex;
|
||||
typedef std::vector<GraphInVertex> GraphInVertices;
|
||||
public:
|
||||
DigRoots(const GraphIn &graphIn) : graph(graphIn) {}
|
||||
GraphInVertices operator()() const
|
||||
{
|
||||
GraphInVertices out;
|
||||
BGL_FORALL_VERTICES_T(currentVertex, graph, GraphIn)
|
||||
{
|
||||
if (boost::in_degree(currentVertex, graph) == 0)
|
||||
out.push_back(currentVertex);
|
||||
}
|
||||
return out;
|
||||
}
|
||||
private:
|
||||
const GraphIn &graph;
|
||||
};
|
||||
|
||||
/*! Multi_index record. */
|
||||
struct GraphLinkRecord
|
||||
{
|
||||
const App::DocumentObject *DObject; //!< document object
|
||||
const Gui::ViewProviderDocumentObject *VPDObject; //!< view provider
|
||||
const ViewEntryRectItem *rectItem; //!< qgraphics item.
|
||||
std::string uniqueName; //!< name for document object.
|
||||
Vertex vertex; //!< vertex in graph.
|
||||
|
||||
//@{
|
||||
//! used as tags.
|
||||
struct ByDObject{};
|
||||
struct ByVPDObject{};
|
||||
struct ByRectItem{};
|
||||
struct ByUniqueName{};
|
||||
struct ByVertex{};
|
||||
//@}
|
||||
};
|
||||
|
||||
namespace BMI = boost::multi_index;
|
||||
typedef boost::multi_index_container
|
||||
<
|
||||
GraphLinkRecord,
|
||||
BMI::indexed_by
|
||||
<
|
||||
BMI::ordered_unique
|
||||
<
|
||||
BMI::tag<GraphLinkRecord::ByDObject>,
|
||||
BMI::member<GraphLinkRecord, const App::DocumentObject*, &GraphLinkRecord::DObject>
|
||||
>,
|
||||
BMI::ordered_unique
|
||||
<
|
||||
BMI::tag<GraphLinkRecord::ByVPDObject>,
|
||||
BMI::member<GraphLinkRecord, const Gui::ViewProviderDocumentObject*, &GraphLinkRecord::VPDObject>
|
||||
>,
|
||||
BMI::ordered_unique
|
||||
<
|
||||
BMI::tag<GraphLinkRecord::ByRectItem>,
|
||||
BMI::member<GraphLinkRecord, const ViewEntryRectItem*, &GraphLinkRecord::rectItem>
|
||||
>,
|
||||
BMI::ordered_unique
|
||||
<
|
||||
BMI::tag<GraphLinkRecord::ByUniqueName>,
|
||||
BMI::member<GraphLinkRecord, std::string, &GraphLinkRecord::uniqueName>
|
||||
>,
|
||||
BMI::ordered_unique
|
||||
<
|
||||
BMI::tag<GraphLinkRecord::ByVertex>,
|
||||
BMI::member<GraphLinkRecord, Vertex, &GraphLinkRecord::vertex>
|
||||
>
|
||||
>
|
||||
> GraphLinkContainer;
|
||||
|
||||
class Model : public QGraphicsScene
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
Model(QObject *parentIn, const Gui::Document &documentIn);
|
||||
virtual ~Model() override;
|
||||
void awake(); //!< hooked up to event dispatcher for update when idle.
|
||||
void selectionChanged(const SelectionChanges& msg);
|
||||
|
||||
protected:
|
||||
virtual void mouseMoveEvent(QGraphicsSceneMouseEvent* event) override;
|
||||
virtual void mousePressEvent(QGraphicsSceneMouseEvent* event) override;
|
||||
|
||||
private Q_SLOTS:
|
||||
void updateSlot();
|
||||
|
||||
private:
|
||||
Model(){}
|
||||
//documentObject slots.
|
||||
typedef boost::BOOST_SIGNALS_NAMESPACE::connection Connection;
|
||||
Connection connectNewObject;
|
||||
Connection connectDelObject;
|
||||
Connection connectChgObject;
|
||||
Connection connectRenObject;
|
||||
Connection connectActObject;
|
||||
Connection connectEdtObject;
|
||||
Connection connectResObject;
|
||||
Connection connectHltObject;
|
||||
Connection connectExpObject;
|
||||
void slotNewObject(const Gui::ViewProviderDocumentObject &VPDObjectIn);
|
||||
void slotDeleteObject(const Gui::ViewProviderDocumentObject &VPDObjectIn);
|
||||
void slotChangeObject(const Gui::ViewProviderDocumentObject &VPDObjectIn, const App::Property& propertyIn);
|
||||
|
||||
std::shared_ptr<GraphLinkContainer> graphLink;
|
||||
std::shared_ptr<Graph> theGraph;
|
||||
bool graphDirty;
|
||||
|
||||
const GraphLinkRecord& findRecord(Vertex vertexIn);
|
||||
const GraphLinkRecord& findRecord(const App::DocumentObject* dObjectIn);
|
||||
const GraphLinkRecord& findRecord(const Gui::ViewProviderDocumentObject* VPDObjectIn);
|
||||
const GraphLinkRecord& findRecord(const ViewEntryRectItem* rectIn);
|
||||
const GraphLinkRecord& findRecord(const std::string &stringIn);
|
||||
void eraseRecord(const Gui::ViewProviderDocumentObject* VPDObjectIn);
|
||||
|
||||
void indexVerticesEdges();
|
||||
void removeAllItems();
|
||||
void updateVisible();
|
||||
|
||||
ViewEntryRectItem* getRectFromPosition(const QPointF &position); //!< can be nullptr
|
||||
|
||||
//! @name View Constants for spacing
|
||||
//@{
|
||||
float fontHeight; //!< height of the current qApp default font.
|
||||
float verticalSpacing; //!< pixels between top and bottom of text to background rectangle.
|
||||
float rowHeight; //!< height of background rectangle.
|
||||
float iconSize; //!< size of icon to match font.
|
||||
float pointSize; //!< size of the connection point.
|
||||
float pointSpacing; //!< spacing between pofloat columns.
|
||||
float pointToIcon; //!< spacing from last column points to icon.
|
||||
float rowPadding; //!< spaces added to rectangle bacground width ends.
|
||||
std::vector<QBrush> backgroundBrushes; //!< brushes to paint background rectangles.
|
||||
std::vector<QBrush> forgroundBrushes; //!< brushes to paint points, connectors, text.
|
||||
void setupViewConstants();
|
||||
//@}
|
||||
|
||||
ViewEntryRectItem *currentPrehighlight;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
#endif // DAGMODEL_H
|
117
src/Gui/DAGView/DAGView.cpp
Normal file
117
src/Gui/DAGView/DAGView.cpp
Normal file
|
@ -0,0 +1,117 @@
|
|||
/***************************************************************************
|
||||
* Copyright (c) 2015 Thomas Anderson <blobfish[at]gmx.com> *
|
||||
* *
|
||||
* This file is part of the FreeCAD CAx development system. *
|
||||
* *
|
||||
* This library is free software; you can redistribute it and/or *
|
||||
* modify it under the terms of the GNU Library General Public *
|
||||
* License as published by the Free Software Foundation; either *
|
||||
* version 2 of the License, or (at your option) any later version. *
|
||||
* *
|
||||
* This library is distributed in the hope that it will be useful, *
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
|
||||
* GNU Library General Public License for more details. *
|
||||
* *
|
||||
* You should have received a copy of the GNU Library General Public *
|
||||
* License along with this library; see the file COPYING.LIB. If not, *
|
||||
* write to the Free Software Foundation, Inc., 59 Temple Place, *
|
||||
* Suite 330, Boston, MA 02111-1307, USA *
|
||||
* *
|
||||
***************************************************************************/
|
||||
|
||||
#include "PreCompiled.h"
|
||||
#ifndef _PreComp_
|
||||
#include <QAbstractEventDispatcher>
|
||||
#include <QVBoxLayout>
|
||||
#endif
|
||||
|
||||
#include <sstream>
|
||||
|
||||
#include <Base/Console.h>
|
||||
|
||||
#include <App/Document.h>
|
||||
#include <Gui/Document.h>
|
||||
#include <Gui/Application.h>
|
||||
|
||||
#include "DAGModel.h"
|
||||
#include "DAGView.h"
|
||||
|
||||
using namespace Gui;
|
||||
using namespace DAG;
|
||||
|
||||
DAG::DockWindow::DockWindow(Gui::Document* gDocumentIn, QWidget* parent): Gui::DockWindow(gDocumentIn, parent)
|
||||
{
|
||||
dagView = new View(this);
|
||||
QVBoxLayout *layout = new QVBoxLayout();
|
||||
layout->addWidget(dagView);
|
||||
this->setLayout(layout);
|
||||
}
|
||||
|
||||
View::View(QWidget* parentIn): QGraphicsView(parentIn)
|
||||
{
|
||||
Application::Instance->signalActiveDocument.connect(boost::bind(&View::slotActiveDocument, this, _1));
|
||||
Application::Instance->signalDeleteDocument.connect(boost::bind(&View::slotDeleteDocument, this, _1));
|
||||
|
||||
//just update the dagview when the gui process is idle.
|
||||
connect(QAbstractEventDispatcher::instance(), SIGNAL(awake()), this, SLOT(awakeSlot()));
|
||||
}
|
||||
|
||||
View::~View()
|
||||
{
|
||||
Application::Instance->signalActiveDocument.disconnect(boost::bind(&View::slotActiveDocument, this, _1));
|
||||
Application::Instance->signalDeleteDocument.disconnect(boost::bind(&View::slotDeleteDocument, this, _1));
|
||||
}
|
||||
|
||||
void View::slotActiveDocument(const Document &documentIn)
|
||||
{
|
||||
ModelMap::const_iterator it = modelMap.find(&documentIn);
|
||||
if (it == modelMap.end())
|
||||
{
|
||||
ModelMap::value_type entry(std::make_pair(&documentIn, std::shared_ptr<Model>(new Model(this, documentIn))));
|
||||
modelMap.insert(entry);
|
||||
this->setScene(entry.second.get());
|
||||
}
|
||||
else
|
||||
{
|
||||
this->setScene(it->second.get());
|
||||
}
|
||||
}
|
||||
|
||||
void View::slotDeleteDocument(const Document &documentIn)
|
||||
{
|
||||
ModelMap::iterator it = modelMap.find(&documentIn);
|
||||
if (it != modelMap.end())
|
||||
modelMap.erase(it);
|
||||
}
|
||||
|
||||
void View::awakeSlot()
|
||||
{
|
||||
Model *model = dynamic_cast<Model *>(this->scene());
|
||||
if (model)
|
||||
model->awake();
|
||||
}
|
||||
|
||||
void View::onSelectionChanged(const SelectionChanges& msg)
|
||||
{
|
||||
//dispatch to appropriate document.
|
||||
ModelMap::iterator it;
|
||||
for (auto it = modelMap.begin(); it != modelMap.end(); ++it)
|
||||
{
|
||||
if (std::string(it->first->getDocument()->getName()) == std::string(msg.pDocName))
|
||||
{
|
||||
it->second->selectionChanged(msg);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
//why am I getting a spontanous event with an empty name?
|
||||
std::ostringstream stream;
|
||||
stream << std::endl << "couldn't find document of name: " << std::string(msg.pDocName) << std::endl << std::endl;
|
||||
Base::Console().Warning(stream.str().c_str());
|
||||
// assert(0); //no document of name.
|
||||
}
|
||||
|
||||
|
||||
|
||||
#include "moc_DAGView.cpp"
|
75
src/Gui/DAGView/DAGView.h
Normal file
75
src/Gui/DAGView/DAGView.h
Normal file
|
@ -0,0 +1,75 @@
|
|||
/***************************************************************************
|
||||
* Copyright (c) 2015 Thomas Anderson <blobfish[at]gmx.com> *
|
||||
* *
|
||||
* This file is part of the FreeCAD CAx development system. *
|
||||
* *
|
||||
* This library is free software; you can redistribute it and/or *
|
||||
* modify it under the terms of the GNU Library General Public *
|
||||
* License as published by the Free Software Foundation; either *
|
||||
* version 2 of the License, or (at your option) any later version. *
|
||||
* *
|
||||
* This library is distributed in the hope that it will be useful, *
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
|
||||
* GNU Library General Public License for more details. *
|
||||
* *
|
||||
* You should have received a copy of the GNU Library General Public *
|
||||
* License along with this library; see the file COPYING.LIB. If not, *
|
||||
* write to the Free Software Foundation, Inc., 59 Temple Place, *
|
||||
* Suite 330, Boston, MA 02111-1307, USA *
|
||||
* *
|
||||
***************************************************************************/
|
||||
|
||||
#ifndef DAGVIEW_H
|
||||
#define DAGVIEW_H
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include <QtGui/QGraphicsView>
|
||||
|
||||
#include <Gui/DockWindow.h>
|
||||
#include <Gui/Document.h>
|
||||
#include <Gui/Selection.h>
|
||||
|
||||
#include "DAGModel.h"
|
||||
|
||||
namespace Gui
|
||||
{
|
||||
namespace DAG
|
||||
{
|
||||
//! @brief view for DAG viewer
|
||||
class View : public QGraphicsView, public SelectionObserver
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
View(QWidget *parentIn = 0);
|
||||
virtual ~View() override;
|
||||
|
||||
public Q_SLOTS:
|
||||
void awakeSlot(); //!< hooked up to event dispatcher for update when idle.
|
||||
|
||||
private:
|
||||
virtual void onSelectionChanged(const SelectionChanges& msg) override;
|
||||
|
||||
void slotActiveDocument(const Gui::Document &documentIn);
|
||||
void slotDeleteDocument(const Gui::Document &documentIn);
|
||||
|
||||
typedef std::map<const Gui::Document*, std::shared_ptr<Model> > ModelMap;
|
||||
ModelMap modelMap;
|
||||
};
|
||||
|
||||
//! @brief dock window for DAG viewer
|
||||
class DockWindow : public Gui::DockWindow
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
DockWindow(Gui::Document* gDocumentIn = 0, QWidget *parent = 0);
|
||||
~DockWindow(){};
|
||||
|
||||
private:
|
||||
View *dagView;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
#endif // DAGVIEW_H
|
|
@ -98,6 +98,7 @@
|
|||
#include "CombiView.h"
|
||||
#include "PythonConsole.h"
|
||||
#include "TaskView/TaskView.h"
|
||||
#include "DAGView/DAGView.h"
|
||||
|
||||
#include "DlgTipOfTheDayImp.h"
|
||||
#include "DlgUndoRedo.h"
|
||||
|
@ -374,6 +375,20 @@ MainWindow::MainWindow(QWidget * parent, Qt::WindowFlags f)
|
|||
pcPython->setObjectName
|
||||
(QString::fromLatin1(QT_TRANSLATE_NOOP("QDockWidget","Python console")));
|
||||
pDockMgr->registerDockWindow("Std_PythonView", pcPython);
|
||||
|
||||
//Dag View.
|
||||
//work through parameter.
|
||||
ParameterGrp::handle group = App::GetApplication().GetUserParameter().
|
||||
GetGroup("BaseApp")->GetGroup("Preferences")->GetGroup("DAGView");
|
||||
bool enabled = group->GetBool("Enabled", false);
|
||||
group->SetBool("Enabled", enabled); //ensure entry exists.
|
||||
if (enabled)
|
||||
{
|
||||
DAG::DockWindow *dagDockWindow = new DAG::DockWindow(nullptr, this);
|
||||
dagDockWindow->setObjectName
|
||||
(QString::fromAscii(QT_TRANSLATE_NOOP("QDockWidget","DAG View")));
|
||||
pDockMgr->registerDockWindow("Std_DAGView", dagDockWindow);
|
||||
}
|
||||
|
||||
#if 0 //defined(Q_OS_WIN32) this portion of code is not able to run with a vanilla Qtlib build on Windows.
|
||||
// The MainWindowTabBar is used to show tabbed dock windows with icons
|
||||
|
|
|
@ -639,6 +639,14 @@ DockWindowItems* StdWorkbench::setupDockWindows() const
|
|||
root->addDockWidget("Std_CombiView", Qt::LeftDockWidgetArea, false, false);
|
||||
root->addDockWidget("Std_ReportView", Qt::BottomDockWidgetArea, true, true);
|
||||
root->addDockWidget("Std_PythonView", Qt::BottomDockWidgetArea, true, true);
|
||||
|
||||
//Dagview through parameter.
|
||||
ParameterGrp::handle group = App::GetApplication().GetUserParameter().
|
||||
GetGroup("BaseApp")->GetGroup("Preferences")->GetGroup("DAGView");
|
||||
bool enabled = group->GetBool("Enabled", false);
|
||||
if (enabled)
|
||||
root->addDockWidget("Std_DAGView", Qt::RightDockWidgetArea, false, false);
|
||||
|
||||
return root;
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue
Block a user