edge adjust() calculates endpoints correctly (with brute force :P), no arrow if the line is too short, no line if the nodes collide

master
Denes Matetelki 14 years ago
parent 6018bc6436
commit e4510d57af

@ -1,4 +1,6 @@
#include <QPainter> #include <QPainter>
#include <QDebug>
#include "edge.h" #include "edge.h"
#include "node.h" #include "node.h"
@ -17,7 +19,7 @@ Edge::Edge(Node *sourceNode, Node *destNode)
source->addEdge(this); source->addEdge(this);
dest->addEdge(this); dest->addEdge(this);
adjust(); adjust();
setZValue(1); // setZValue(1);
} }
Node *Edge::sourceNode() const Node *Edge::sourceNode() const
@ -30,28 +32,57 @@ Node *Edge::destNode() const
return dest; 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() void Edge::adjust()
{ {
if (!source || !dest) if (!source || !dest)
return; return;
prepareGeometryChange();
QLineF line(mapFromItem(source, 0, 0) + source->boundingRect().center(), QLineF line(mapFromItem(source, 0, 0) + source->boundingRect().center(),
mapFromItem(dest, 0, 0) + dest->boundingRect().center()); mapFromItem(dest, 0, 0) + dest->boundingRect().center());
qreal length = line.length(); qreal length = line.length();
prepareGeometryChange();
if (length > qreal(20.)) { if (length > qreal(20.)) {
// QPointF edgeOffset((line.dx() * 10) / length, (line.dy() * 10) / length);
// QPointF sourceOffset( ); QPointF sourceOffset(firstNotContainedPoint(line,
// if ( source->contains( source->boundingRect().center() + source->boundingRect().height() / 2 / tan(line.angle())), source->pos(),
// source->boundingRect().width() / 2 ) source->boundingRect()
// { ));
// sourcePoint = line.p1() + QPointF( , tan( line.angle() ) * source->boundingRect().height() / 2 ); QPointF destOffset(firstNotContainedPoint(line,
// } dest->pos(),
dest->boundingRect()
sourcePoint = line.p1(); ,true));
destPoint = line.p2();
sourcePoint = sourceOffset;
destPoint = destOffset;
} else { } else {
sourcePoint = destPoint = line.p1(); sourcePoint = destPoint = line.p1();
} }
@ -77,13 +108,21 @@ void Edge::paint(QPainter *painter, const QStyleOptionGraphicsItem *, QWidget *)
return; return;
QLineF line(sourcePoint, destPoint); 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; return;
// Draw the line itself // Draw the line itself
painter->setPen(QPen(Qt::black, 1, Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin)); painter->setPen(QPen(Qt::black, 1, Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin));
painter->drawLine(line); painter->drawLine(line);
if (line.length() < qreal(10.))
return;
// Draw the arrows // Draw the arrows
double angle = ::acos(line.dx() / line.length()); double angle = ::acos(line.dx() / line.length());
if (line.dy() >= 0) if (line.dy() >= 0)

@ -12,49 +12,50 @@ GraphWidget::GraphWidget(QWidget *parent)
scene = new QGraphicsScene(this); scene = new QGraphicsScene(this);
scene->setItemIndexMethod(QGraphicsScene::NoIndex); scene->setItemIndexMethod(QGraphicsScene::NoIndex);
scene->setSceneRect(-200, -200, 400, 400); scene->setSceneRect(-400, -400, 800, 800);
setScene(scene); setScene(scene);
setCacheMode(CacheBackground); setCacheMode(CacheBackground);
setViewportUpdateMode(BoundingRectViewportUpdate); setViewportUpdateMode(BoundingRectViewportUpdate);
setRenderHint(QPainter::Antialiasing); setRenderHint(QPainter::Antialiasing);
setTransformationAnchor(AnchorUnderMouse); setTransformationAnchor(AnchorUnderMouse);
scale(qreal(0.8), qreal(0.8));
setMinimumSize(400, 400); setMinimumSize(400, 400);
// node1 = new Node(this);
// scene->addItem(node1);
// node1->setPos(0, 0);
Node *node1 = new Node(); Node *node1 = new Node();
node1->setHtml(QString("salalal")); node1->setHtml(QString("me"));
scene->addItem(node1); scene->addItem(node1);
node1->setPos(-100, -100); node1->setPos(-10, -10);
Node *node2 = new Node(); Node *node2 = new Node();
node2->setHtml(QString("<b>denes</b> is\na really nice person")); node2->setHtml(QString("work"));
scene->addItem(node2); scene->addItem(node2);
node2->setPos(100, 100); node2->setPos(60, -10);
Node *node3 = new Node();
// QGraphicsTextItem *item = new QGraphicsTextItem(); node3->setHtml(QString("read"));
// item->setPlainText(QString("salalal")); scene->addItem(node3);
// scene->addItem(item); node3->setPos(-70, -10);
// item->setPos(20, 20);
// item->setFlag(QGraphicsItem::ItemIsMovable); Node *node4 = new Node();
// item->setFlag(QGraphicsItem::ItemSendsGeometryChanges); node4->setHtml(QString("pragmatic programmer"));
// item->setCacheMode(QGraphicsItem::DeviceCoordinateCache); scene->addItem(node4);
// item->setZValue(-1); node4->setPos(-120, -80);
// QGraphicsTextItem *item2 = new QGraphicsTextItem(); Node *node5 = new Node();
// item2->setHtml(QString("<b>denes</b> is\na really nice person")); node5->setHtml(QString("joy"));
// scene->addItem(item2); scene->addItem(node5);
// item2->setPos(10, 10); node5->setPos(-10, 50);
// item2->setFlag(QGraphicsItem::ItemIsMovable);
// item2->setFlag(QGraphicsItem::ItemSendsGeometryChanges); Node *node6 = new Node();
// item2->setCacheMode(QGraphicsItem::DeviceCoordinateCache); node6->setHtml(QString("rape goats"));
// item2->setZValue(-1); scene->addItem(node6);
node6->setPos(-10, 100);
scene->addItem(new Edge(node1, node2)); 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 = node1;
activeNode->setFocus(); activeNode->setFocus();
} }
@ -102,6 +103,44 @@ void GraphWidget::wheelEvent(QWheelEvent *event)
scaleView(pow((double)2, -event->delta() / 240.0)); 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) void GraphWidget::scaleView(qreal scaleFactor)
{ {
qreal factor = transform().scale(scaleFactor, scaleFactor).mapRect(QRectF(0, 0, 1, 1)).width(); qreal factor = transform().scale(scaleFactor, scaleFactor).mapRect(QRectF(0, 0, 1, 1)).width();

@ -21,6 +21,7 @@ protected:
void keyPressEvent(QKeyEvent *event); void keyPressEvent(QKeyEvent *event);
void wheelEvent(QWheelEvent *event); void wheelEvent(QWheelEvent *event);
void scaleView(qreal scaleFactor); void scaleView(qreal scaleFactor);
void drawBackground(QPainter *painter, const QRectF &rect);
private: private:
Node *activeNode; Node *activeNode;

@ -3,9 +3,32 @@
#include "aboutdialog.h" #include "aboutdialog.h"
#include <QDebug> #include <QDebug>
//#include <QLayout>
#include <QFileDialog> #include <QFileDialog>
//#include <QtConcurrentRun>
/// 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) : MainWindow::MainWindow(bool isSystemtray, QWidget *parent) :
QMainWindow(parent), QMainWindow(parent),
@ -58,18 +81,26 @@ void MainWindow::exportScene()
{ {
QStringList fileNames(dialog.selectedFiles()); 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(), QImage img(graphicsView->getScene()->sceneRect().width(),
graphicsView->getScene()->sceneRect().height(), graphicsView->getScene()->sceneRect().height(),
QImage::Format_ARGB32_Premultiplied); QImage::Format_ARGB32_Premultiplied);
QPainter painter(&img); QPainter painter(&img);
painter.setRenderHint(QPainter::Antialiasing); painter.setRenderHint(QPainter::Antialiasing);
/// @bug scene background is not rendered
graphicsView->getScene()->render(&painter); graphicsView->getScene()->render(&painter);
painter.end(); painter.end();
img.save(fileNames.first()); img.save(fileNames.first());
ui->statusBar->showMessage(tr("MindMap exported as ") + fileNames.first(), ui->statusBar->showMessage(tr("MindMap exported as ") + fileNames.first(),
5000); 5000); // millisec
} }
} }

@ -11,10 +11,13 @@ Node::Node(GraphWidget *parent) : graph(parent), active(false)
setFlag(ItemIsMovable); setFlag(ItemIsMovable);
setFlag(ItemSendsGeometryChanges); setFlag(ItemSendsGeometryChanges);
setCacheMode(DeviceCoordinateCache); setCacheMode(DeviceCoordinateCache);
setZValue(2); // setZValue(1);
// shall I use system colors?
setDefaultTextColor(QColor(0,0,0));
// shall I set it after some spec key? // shall I set it after some spec key?
setTextInteractionFlags(Qt::TextEditorInteraction); // setTextInteractionFlags(Qt::TextEditorInteraction);
} }
void Node::addEdge(Edge *edge) void Node::addEdge(Edge *edge)
@ -32,8 +35,8 @@ QVariant Node::itemChange(GraphicsItemChange change, const QVariant &value)
switch (change) { switch (change) {
case ItemPositionHasChanged: case ItemPositionHasChanged:
foreach (Edge *edge, edgeList) edge->adjust(); foreach (Edge *edge, edgeList) edge->adjust();
// graph->itemMoved();
break; break;
default: default:
break; break;
@ -56,18 +59,32 @@ void Node::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
{ {
qDebug() << __PRETTY_FUNCTION__; qDebug() << __PRETTY_FUNCTION__;
// active = false;
// setScale(1.0);
update(); update();
QGraphicsTextItem::mouseReleaseEvent(event); 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."; void Node::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *w)
// setScale(hasFocus() ? 1.2 : 1.0); {
//// setScale(active ? 1.2 : 1.0); 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);
}

@ -8,6 +8,7 @@
class GraphWidget; class GraphWidget;
/// @bug no signal when size change
class Node : public QGraphicsTextItem class Node : public QGraphicsTextItem
{ {
// Q_OBJECT // Q_OBJECT
@ -22,12 +23,14 @@ protected:
QVariant itemChange(GraphicsItemChange change, const QVariant &value); QVariant itemChange(GraphicsItemChange change, const QVariant &value);
void mousePressEvent(QGraphicsSceneMouseEvent *event); void mousePressEvent(QGraphicsSceneMouseEvent *event);
void mouseReleaseEvent(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: private:
QList<Edge *> edgeList; QList<Edge *> edgeList;
GraphWidget *graph; GraphWidget *graph;
bool active; bool active;
QRectF m_rect;
}; };

Loading…
Cancel
Save