ribi::cmap::QtNode * ribi::cmap::QtRateConceptMap::AddNode(const boost::shared_ptr<Node> node) { //const boost::shared_ptr<QtRateStrategy> display_strategy(new QtRateStrategy(node->GetConcept())); //assert(display_strategy); //QtNode * const qtnode = new QtNode(node,display_strategy); QtNode * const qtnode { IsCenterNode(node) ? new QtCenterNode(boost::dynamic_pointer_cast<CenterNode>(node)) : new QtNode(node,GetDisplayStrategy(node->GetConcept())) }; assert(qtnode); assert(IsCenterNode(qtnode->GetNode()) == IsQtCenterNode(qtnode) && "Should be equivalent"); //General: inform an Observer that this item has changed qtnode->m_signal_item_has_updated.connect( boost::bind(&QtConceptMap::OnItemRequestsUpdate,this,boost::lambda::_1)); //General: inform an Observer that a QGraphicsScene needs to be updated qtnode->m_signal_request_scene_update.connect( boost::bind(&QtConceptMap::OnRequestSceneUpdate,this)); //Specific: inform an Observer that the Node requests its Concept being rated qtnode->m_signal_node_requests_rate_concept.connect( boost::bind( &ribi::cmap::QtRateConceptMap::OnNodeRequestsRateConcept, this, boost::lambda::_1)); //Do not forget the placeholder! //Specific: inform an Observer that the Node requests its Examples being rated qtnode->m_signal_node_requests_rate_examples.connect( boost::bind( &ribi::cmap::QtRateConceptMap::OnNodeRequestsRateExamples, this, boost::lambda::_1)); //Do not forget the placeholder! assert(!qtnode->scene()); this->scene()->addItem(qtnode); assert(std::count( GetConceptMap()->GetNodes().begin(), GetConceptMap()->GetNodes().end(), node) == 1 && "Assume Node is already in the concept map"); //this->GetConceptMap()->AddNode(node); assert(qtnode->pos().x() == node->GetX()); assert(qtnode->pos().y() == node->GetY()); //Cannot test this: during construction not all nodes are put in //assert(Collect<QtNode>(this->scene()).size() == this->GetConceptMap()->GetNodes().size()); return qtnode; }
void ribi::cmap::QtConceptMap::DeleteNode(QtNode * const qtnode) { #ifndef NDEBUG const int n_items_before = this->scene()->items().count(); #endif //Delete the edges connected to this node { const std::vector<QtEdge *> qtedges = GetQtEdges(); const std::size_t sz = qtedges.size(); for (std::size_t i=0; i!=sz; ++i) { QtEdge * const qtedge = qtedges[i]; assert(qtedge); if (qtedge->GetFrom() == qtnode || qtedge->GetTo() == qtnode) { DeleteEdge(qtedge); } } } //Remove from non-GUI, which removes the left-overs GetConceptMap()->DeleteNode(qtnode->GetNode()); //Remove node from GUI this->scene()->removeItem(qtnode); #ifndef NDEBUG const int n_items_after = this->scene()->items().count(); assert(n_items_before - n_items_after >= 1 && "At least one item is deleted: one node and x edges"); assert(Collect<QtNode>(this->scene()).size() == this->GetConceptMap()->GetNodes().size() && "GUI and non-GUI concept map must match"); #endif }
const std::vector<const ribi::cmap::QtEdge *> ribi::cmap::QtConceptMap::GetQtEdges() const { const std::vector<const QtEdge *> qtedges = Collect<const QtEdge>(this->scene()); assert(qtedges.size() == GetConceptMap()->GetNodes().size() && "GUI and non-GUI must contain an equal amount of edges"); return qtedges; }
const std::vector<const ribi::cmap::QtNode *> ribi::cmap::QtConceptMap::GetQtNodes() const { const std::vector<const QtNode *> qtnodes = Collect<const QtNode>(this->scene()); if (qtnodes.size() != GetConceptMap()->GetNodes().size()) { TRACE("Warning: GUI and non-GUI contain an unequal amount of nodes"); } //assert(qtnodes.size() == GetConceptMap()->GetNodes().size() // && "GUI and non-GUI must contain an equal amount of nodes"); return qtnodes; }
void ribi::cmap::QtConceptMap::CheckInvariants() const noexcept { #ifndef NDEBUG //If there is one QtEdge selected, its Edge must be able to been found try { const auto qtedge = ExtractTheOneSelectedQtEdge(*GetScene()); //The QtEdge its edge must be in the concept map //Can only compare IDs, as QtEdge::GetEdge() and its equivalent in the concept map may mismatch assert( has_custom_edge_with_my_edge( qtedge->GetEdge(), GetConceptMap(), [](const Edge& lhs, const Edge& rhs) { return lhs.GetId() == rhs.GetId(); } ) ); const auto edge = ExtractTheOneSelectedEdge(this->GetConceptMap(), *GetScene()); assert(qtedge->GetEdge().GetId() == edge.GetId()); } catch (...) {} //No problem //All QtNodes must have a scene { const auto qtnodes = GetQtNodes(scene()); for (const auto qtnode: qtnodes) { assert(qtnode); assert(qtnode->scene()); } } //All QtEdges, their QtNodes and Arrows must have a scene { const auto qtedges = GetQtEdges(scene()); for (const auto qtedge: qtedges) { assert(qtedge); assert(qtedge->scene()); assert(qtedge->GetArrow()); assert(qtedge->GetArrow()->scene()); assert(qtedge->GetQtNode()); assert(qtedge->GetQtNode()->scene()); assert(qtedge->GetFrom()); assert(qtedge->GetFrom()->scene()); assert(qtedge->GetTo()); assert(qtedge->GetTo()->scene()); } } #endif }
void ribi::cmap::QtConceptMap::DeleteEdge(QtEdge * const qtedge) { #ifndef NDEBUG const int n_items_before = this->scene()->items().count(); #endif assert(scene()->items().contains(qtedge)); //Remove non-GUI edges GetConceptMap()->DeleteEdge(qtedge->GetEdge()); //Remove GUI edge this->scene()->removeItem(qtedge); //No left-overs when deleting an edge //DeleteLeftovers(); #ifndef NDEBUG const int n_items_after = this->scene()->items().count(); assert(n_items_after + 1 == n_items_before); //Cannot do the check below: in DeleteNode multiple edges are deleted //assert(Collect<QtNode>(this->scene()).size() == this->GetConceptMap()->GetNodes().size() // && "GUI and non-GUI concept map must match"); #endif }
ribi::cmap::QtConceptMap::QtConceptMap( const boost::shared_ptr<ConceptMap> concept_map, QWidget* parent) : QtKeyboardFriendlyGraphicsView(parent), m_concept_map(concept_map), m_examples_item(new QtExamplesItem) { assert( (concept_map || !concept_map) && "Also empty concept maps must be displayed"); assert(GetConceptMap() == concept_map); assert( (!concept_map || concept_map->IsValid()) && "Expect no or a valid concept map"); //Cannot test this ABC here, its derived classes will test themselves this->setScene(new QGraphicsScene(this)); assert(!m_examples_item->scene()); scene()->addItem(m_examples_item); //Add the examples so it has a parent assert(Collect<QtNode>(scene()).empty()); //Without this line, mouseMoveEvent won't be called this->setMouseTracking(true); { //QLinearGradient linearGradient(-500,-500,500,500); //linearGradient.setColorAt(0.0,QColor(214,214,214)); //linearGradient.setColorAt(1.0,QColor(255,255,255)); assert(this->scene()); //this->scene()->setBackgroundBrush(linearGradient); this->scene()->setBackgroundBrush(QBrush(QColor(255,255,255))); } }
void ribi::cmap::QtConceptMap::SetConceptMap(const ConceptMap& conceptmap) { RemoveConceptMap(); m_conceptmap = conceptmap; assert(GetConceptMap() == conceptmap); assert(this->scene()); //This std::vector keeps the QtNodes in the same order as the nodes in the concept map //You cannot rely on Collect<QtConceptMapNodeConcept*>(scene), as this shuffles the order //std::vector<QtNode*> qtnodes; assert(Collect<QtNode>(scene()).empty()); //Add the nodes to the scene, if there are any const auto vip = vertices(m_conceptmap); for(auto i=vip.first; i!=vip.second; ++i) { assert(boost::num_vertices(m_conceptmap)); const auto pmap = get(boost::vertex_custom_type, m_conceptmap); const Node node = get(pmap, *i); const bool is_focal_node{i == vip.first}; QtNode * const qtnode{new QtNode(node)}; if (is_focal_node) { qtnode->setFlags(0); } assert(qtnode); assert(!qtnode->scene()); scene()->addItem(qtnode); assert(qtnode->scene()); assert(FindQtNode(node.GetId(), scene())); } //Add the Edges const auto eip = edges(m_conceptmap); for(auto i = eip.first; i != eip.second; ++i) { assert(boost::num_edges(m_conceptmap)); const VertexDescriptor vd_from = boost::source(*i, m_conceptmap); const VertexDescriptor vd_to = boost::target(*i, m_conceptmap); assert(vd_from != vd_to); const auto vertex_map = get(boost::vertex_custom_type, m_conceptmap); const Node from = get(vertex_map, vd_from); const Node to = get(vertex_map, vd_to); assert(from.GetId() != to.GetId()); QtNode * const qtfrom = FindQtNode(from.GetId(), scene()); QtNode * const qtto = FindQtNode(to.GetId(), scene()); assert(qtfrom != qtto); const auto edge_map = get(boost::edge_custom_type, m_conceptmap); const Edge edge = get(edge_map, *i); QtEdge * const qtedge{new QtEdge(edge,qtfrom,qtto)}; if (qtfrom->GetNode().IsCenterNode() || qtto->GetNode().IsCenterNode()) { qtedge->GetQtNode()->setVisible(false); } assert(!qtedge->scene()); assert(!qtedge->GetQtNode()->scene()); assert(!qtedge->GetArrow()->scene()); scene()->addItem(qtedge); //scene()->addItem(qtedge->GetQtNode()); //Get these for free //scene()->addItem(qtedge->GetArrow()); //Get these for free assert(qtedge->scene()); assert(qtedge->GetQtNode()->scene()); assert(qtedge->GetArrow()->scene()); } assert(GetConceptMap() == conceptmap); CheckInvariants(); }
void ribi::cmap::QtConceptMap::keyPressEvent(QKeyEvent *event) { CheckInvariants(); UpdateConceptMap(); CheckInvariants(); switch (event->key()) { case Qt::Key_F1: case Qt::Key_F2: { const auto items = scene()->selectedItems(); if (items.size() != 1) { break; } if (QtNode * const qtnode = dynamic_cast<QtNode*>(items.front())) { OnNodeKeyDownPressed(qtnode, event->key()); } } break; #ifndef NDEBUG case Qt::Key_F9: { throw std::runtime_error("Exception forced by user"); } #endif case Qt::Key_Delete: { UpdateConceptMap(); if (GetVerbosity()) { TRACE("Pressing delete"); } try { DoCommand(new CommandDeleteSelected(m_conceptmap, scene(), m_tools)); } catch (std::logic_error& e) { if (GetVerbosity()) { TRACE(e.what()); } } } return; case Qt::Key_Escape: { if (GetVerbosity()) { TRACE("Pressing Escape"); } //Only remove the 'new arrow' if present if (m_arrow->isVisible()) { if (GetVerbosity()) { TRACE("Remove the new arrow"); } m_arrow->hide(); assert(!m_arrow->isVisible()); return; } } break; case Qt::Key_Equal: if (GetVerbosity()) { TRACE("Pressing Qt::Key_Equal"); } this->scale(1.1,1.1); break; case Qt::Key_Minus: if (GetVerbosity()) { TRACE("Pressing Qt::Key_Minus"); } this->scale(0.9,0.9); break; case Qt::Key_E: if (event->modifiers() & Qt::ControlModifier) { if (GetVerbosity()) { TRACE("Pressing CTRL-E"); } try { this->DoCommand(new CommandCreateNewEdgeBetweenTwoSelectedNodes(GetConceptMap(),m_mode,scene(),m_tools)); } catch (std::logic_error& ) {} } return; case Qt::Key_T: if (event->modifiers() & Qt::ControlModifier) { if (GetVerbosity()) { TRACE("Pressing CTRL-T"); } try { const auto cmd = new CommandToggleArrowTail(GetConceptMap(), scene()); this->DoCommand(cmd); } catch (std::logic_error& e) {} } return; case Qt::Key_H: if (event->modifiers() & Qt::ControlModifier) { if (GetVerbosity()) { TRACE("Pressing CTRL-H"); } try { const auto cmd = new CommandToggleArrowHead(GetConceptMap(), scene()); this->DoCommand(cmd); } catch (std::logic_error& e) {} } return; case Qt::Key_N: if (event->modifiers() & Qt::ControlModifier) { if (GetVerbosity()) { TRACE("Pressing CTRL-N"); } try { this->DoCommand(new CommandCreateNewNode(m_conceptmap,m_mode,scene(),m_tools,0.0,0.0)); } catch (std::logic_error& ) {} } return; case Qt::Key_Z: if (event->modifiers() & Qt::ControlModifier) { if (event->modifiers() & Qt::ShiftModifier) { this->m_undo.redo(); } else { this->m_undo.undo(); } } return; case Qt::Key_Question: if (GetVerbosity()) { TRACE("Pressing Qt::Key_Question"); } UpdateConceptMap(); break; } for (auto qtedge: GetSelectedQtEdges(*GetScene())) { qtedge->keyPressEvent(event); qtedge->update(); } QtKeyboardFriendlyGraphicsView::keyPressEvent(event); UpdateConceptMap(); CheckInvariants(); }