diff --git a/edge.cpp b/edge.cpp index 5c2e418..edde12a 100644 --- a/edge.cpp +++ b/edge.cpp @@ -1,4 +1,6 @@ #include +#include + #include "edge.h" #include "node.h" @@ -17,7 +19,7 @@ Edge::Edge(Node *sourceNode, Node *destNode) source->addEdge(this); dest->addEdge(this); adjust(); - setZValue(1); +// setZValue(1); } Node *Edge::sourceNode() const @@ -30,28 +32,57 @@ Node *Edge::destNode() const return dest; } +/// @note This is brute force. Isn't there a simple fv for this? +QPointF firstNotContainedPoint(const QLineF &line, + const QPointF &pos, + const QRectF &rectangle, + bool reverse = false) +{ + QRectF rect(rectangle.topLeft()+pos, rectangle.bottomRight()+pos); + if (reverse) + { + for (qreal t = 1; t!=0; t-=0.01) + { + if (!rect.contains(line.pointAt(t))) return line.pointAt(t); + } + } + else + { + for (qreal t = 0; t!=1; t+=0.01) + { + if (!rect.contains(line.pointAt(t))) return line.pointAt(t); + } + } + return QPoint(0,0); +} + void Edge::adjust() { if (!source || !dest) return; + + prepareGeometryChange(); + QLineF line(mapFromItem(source, 0, 0) + source->boundingRect().center(), mapFromItem(dest, 0, 0) + dest->boundingRect().center()); qreal length = line.length(); - prepareGeometryChange(); + if (length > qreal(20.)) { -// QPointF edgeOffset((line.dx() * 10) / length, (line.dy() * 10) / length); -// QPointF sourceOffset( ); -// if ( source->contains( source->boundingRect().center() + source->boundingRect().height() / 2 / tan(line.angle())), -// source->boundingRect().width() / 2 ) -// { -// sourcePoint = line.p1() + QPointF( , tan( line.angle() ) * source->boundingRect().height() / 2 ); -// } - - sourcePoint = line.p1(); - destPoint = line.p2(); + + QPointF sourceOffset(firstNotContainedPoint(line, + source->pos(), + source->boundingRect() + )); + QPointF destOffset(firstNotContainedPoint(line, + dest->pos(), + dest->boundingRect() + ,true)); + + sourcePoint = sourceOffset; + destPoint = destOffset; } else { sourcePoint = destPoint = line.p1(); } @@ -77,13 +108,21 @@ void Edge::paint(QPainter *painter, const QStyleOptionGraphicsItem *, QWidget *) return; QLineF line(sourcePoint, destPoint); - if (qFuzzyCompare(line.length(), qreal(0.))) + + /// @bug this test does not filter out when the nodes intersect +// if (qFuzzyCompare(line.length(), qreal(0.))) +// return; + + if (sourceNode()->collidesWithItem(destNode())) return; // Draw the line itself painter->setPen(QPen(Qt::black, 1, Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin)); painter->drawLine(line); + if (line.length() < qreal(10.)) + return; + // Draw the arrows double angle = ::acos(line.dx() / line.length()); if (line.dy() >= 0) diff --git a/graphwidget.cpp b/graphwidget.cpp index 793a21c..82ea094 100644 --- a/graphwidget.cpp +++ b/graphwidget.cpp @@ -12,51 +12,52 @@ GraphWidget::GraphWidget(QWidget *parent) scene = new QGraphicsScene(this); scene->setItemIndexMethod(QGraphicsScene::NoIndex); - scene->setSceneRect(-200, -200, 400, 400); + scene->setSceneRect(-400, -400, 800, 800); setScene(scene); setCacheMode(CacheBackground); setViewportUpdateMode(BoundingRectViewportUpdate); setRenderHint(QPainter::Antialiasing); setTransformationAnchor(AnchorUnderMouse); - scale(qreal(0.8), qreal(0.8)); setMinimumSize(400, 400); - -// node1 = new Node(this); -// scene->addItem(node1); -// node1->setPos(0, 0); Node *node1 = new Node(); - node1->setHtml(QString("salalal")); + node1->setHtml(QString("me")); scene->addItem(node1); - node1->setPos(-100, -100); + node1->setPos(-10, -10); Node *node2 = new Node(); - node2->setHtml(QString("denes is\na really nice person")); + node2->setHtml(QString("work")); scene->addItem(node2); - node2->setPos(100, 100); - - -// QGraphicsTextItem *item = new QGraphicsTextItem(); -// item->setPlainText(QString("salalal")); -// scene->addItem(item); -// item->setPos(20, 20); -// item->setFlag(QGraphicsItem::ItemIsMovable); -// item->setFlag(QGraphicsItem::ItemSendsGeometryChanges); -// item->setCacheMode(QGraphicsItem::DeviceCoordinateCache); -// item->setZValue(-1); - -// QGraphicsTextItem *item2 = new QGraphicsTextItem(); -// item2->setHtml(QString("denes is\na really nice person")); -// scene->addItem(item2); -// item2->setPos(10, 10); -// item2->setFlag(QGraphicsItem::ItemIsMovable); -// item2->setFlag(QGraphicsItem::ItemSendsGeometryChanges); -// item2->setCacheMode(QGraphicsItem::DeviceCoordinateCache); -// item2->setZValue(-1); - - scene->addItem(new Edge(node1, node2)); - activeNode = node1; - activeNode->setFocus(); + node2->setPos(60, -10); + + Node *node3 = new Node(); + node3->setHtml(QString("read")); + scene->addItem(node3); + node3->setPos(-70, -10); + + Node *node4 = new Node(); + node4->setHtml(QString("pragmatic programmer")); + scene->addItem(node4); + node4->setPos(-120, -80); + + Node *node5 = new Node(); + node5->setHtml(QString("joy")); + scene->addItem(node5); + node5->setPos(-10, 50); + + Node *node6 = new Node(); + node6->setHtml(QString("rape goats")); + scene->addItem(node6); + node6->setPos(-10, 100); + + scene->addItem(new Edge(node1, node2)); + scene->addItem(new Edge(node1, node3)); + scene->addItem(new Edge(node3, node4)); + scene->addItem(new Edge(node1, node5)); + scene->addItem(new Edge(node5, node6)); + + activeNode = node1; + activeNode->setFocus(); } QGraphicsScene *GraphWidget::getScene() @@ -102,6 +103,44 @@ void GraphWidget::wheelEvent(QWheelEvent *event) scaleView(pow((double)2, -event->delta() / 240.0)); } +void GraphWidget::drawBackground(QPainter *painter, const QRectF &rect) + { + Q_UNUSED(rect); + + // Shadow + QRectF sceneRect = this->sceneRect(); +// QRectF rightShadow(sceneRect.right(), sceneRect.top() + 5, 5, sceneRect.height()); +// QRectF bottomShadow(sceneRect.left() + 5, sceneRect.bottom(), sceneRect.width(), 5); +// if (rightShadow.intersects(rect) || rightShadow.contains(rect)) +// painter->fillRect(rightShadow, Qt::darkGray); +// if (bottomShadow.intersects(rect) || bottomShadow.contains(rect)) +// painter->fillRect(bottomShadow, Qt::darkGray); + + // Fill + QLinearGradient gradient(sceneRect.topLeft(), sceneRect.bottomRight()); + gradient.setColorAt(0, Qt::white); + gradient.setColorAt(1, Qt::lightGray); + painter->fillRect(rect.intersect(sceneRect), gradient); + painter->setBrush(Qt::NoBrush); + painter->drawRect(sceneRect); + +// // Text +// QRectF textRect(sceneRect.left() + 4, sceneRect.top() + 4, +// sceneRect.width() - 4, sceneRect.height() - 4); +// QString message(tr("Click and drag the nodes around, and zoom with the mouse " +// "wheel or the '+' and '-' keys")); + +// QFont font = painter->font(); +// font.setBold(true); +// font.setPointSize(14); +// painter->setFont(font); +// painter->setPen(Qt::lightGray); +// painter->drawText(textRect.translated(2, 2), message); +// painter->setPen(Qt::black); +// painter->drawText(textRect, message); + } + + void GraphWidget::scaleView(qreal scaleFactor) { qreal factor = transform().scale(scaleFactor, scaleFactor).mapRect(QRectF(0, 0, 1, 1)).width(); diff --git a/graphwidget.h b/graphwidget.h index 0f578da..3ba26a9 100644 --- a/graphwidget.h +++ b/graphwidget.h @@ -21,6 +21,7 @@ protected: void keyPressEvent(QKeyEvent *event); void wheelEvent(QWheelEvent *event); void scaleView(qreal scaleFactor); + void drawBackground(QPainter *painter, const QRectF &rect); private: Node *activeNode; diff --git a/mainwindow.cpp b/mainwindow.cpp index e486a95..0d76adb 100644 --- a/mainwindow.cpp +++ b/mainwindow.cpp @@ -3,9 +3,32 @@ #include "aboutdialog.h" #include -//#include #include +//#include + +/// QPixmap: It is not safe to use pixmaps outside the GUI thread +/// tr is messy +/* +extern void exportScaneToPng(QGraphicsScene *scene, + const QString &fileName, + Ui::MainWindow *ui) +{ + // start export in a diff thread + QImage img(scene->sceneRect().width(), + scene->sceneRect().height(), + QImage::Format_ARGB32_Premultiplied); + QPainter painter(&img); + painter.setRenderHint(QPainter::Antialiasing); + scene->render(&painter); + painter.end(); + + img.save(fileName); + ui->statusBar->showMessage(tr("MindMap exported as ") + fileName, + 5000); // millisec +} +*/ + MainWindow::MainWindow(bool isSystemtray, QWidget *parent) : QMainWindow(parent), @@ -58,18 +81,26 @@ void MainWindow::exportScene() { QStringList fileNames(dialog.selectedFiles()); - // start export in a diff thread + /// @note Shall I start the export in diff thread? +// QtConcurrent::run(exportScaneToPng, +// graphicsView->getScene(), +// fileNames.first(), +// ui); + QImage img(graphicsView->getScene()->sceneRect().width(), graphicsView->getScene()->sceneRect().height(), QImage::Format_ARGB32_Premultiplied); QPainter painter(&img); painter.setRenderHint(QPainter::Antialiasing); + + /// @bug scene background is not rendered graphicsView->getScene()->render(&painter); painter.end(); img.save(fileNames.first()); ui->statusBar->showMessage(tr("MindMap exported as ") + fileNames.first(), - 5000); + 5000); // millisec + } } diff --git a/node.cpp b/node.cpp index 1b90498..b684d2b 100644 --- a/node.cpp +++ b/node.cpp @@ -11,10 +11,13 @@ Node::Node(GraphWidget *parent) : graph(parent), active(false) setFlag(ItemIsMovable); setFlag(ItemSendsGeometryChanges); setCacheMode(DeviceCoordinateCache); - setZValue(2); +// setZValue(1); + + // shall I use system colors? + setDefaultTextColor(QColor(0,0,0)); // shall I set it after some spec key? - setTextInteractionFlags(Qt::TextEditorInteraction); +// setTextInteractionFlags(Qt::TextEditorInteraction); } void Node::addEdge(Edge *edge) @@ -32,8 +35,8 @@ QVariant Node::itemChange(GraphicsItemChange change, const QVariant &value) switch (change) { case ItemPositionHasChanged: + foreach (Edge *edge, edgeList) edge->adjust(); - // graph->itemMoved(); break; default: break; @@ -56,18 +59,32 @@ void Node::mouseReleaseEvent(QGraphicsSceneMouseEvent *event) { qDebug() << __PRETTY_FUNCTION__; -// active = false; -// setScale(1.0); update(); QGraphicsTextItem::mouseReleaseEvent(event); } -//void Node::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) -//{ -// qDebug() << __PRETTY_FUNCTION__; -// QGraphicsTextItem::paint(painter, option, widget); -// qDebug() << "I " << (hasFocus() ? "have " : "don't have ") << "focus."; -// setScale(hasFocus() ? 1.2 : 1.0); -//// setScale(active ? 1.2 : 1.0); -//} +void Node::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *w) +{ + qDebug() << __PRETTY_FUNCTION__; + + QGraphicsTextItem::paint(painter, option, w); + + QPen pen(Qt::blue,1); +// pen.setJoinStyle(Qt::RoundJoin); +// pen.setStyle(Qt::MiterJoin); +// pen.setCapStyle(Qt::RoundCap); +// pen.setMiterLimit(3); + painter->setPen(pen); + +// painter->setPen(QPen(Qt::blue +// , 1, Qt::SolidLine,Qt::SquareCap, Qt::RoundJoin +// )); + m_rect = QRect( boundingRect().topLeft().toPoint() +// - QPoint(4,4) + , + boundingRect().bottomRight().toPoint() + - QPoint(1,1) + ); + painter->drawRect(m_rect); +} diff --git a/node.h b/node.h index 41393d6..d65f8a4 100644 --- a/node.h +++ b/node.h @@ -8,6 +8,7 @@ class GraphWidget; +/// @bug no signal when size change class Node : public QGraphicsTextItem { // Q_OBJECT @@ -22,12 +23,14 @@ protected: QVariant itemChange(GraphicsItemChange change, const QVariant &value); void mousePressEvent(QGraphicsSceneMouseEvent *event); void mouseReleaseEvent(QGraphicsSceneMouseEvent *event); -// void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget); + void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget); + private: QList edgeList; GraphWidget *graph; bool active; + QRectF m_rect; };