PDA

View Full Version : QGraphicsScene is SLOW with a lot of items !



pl01
4th August 2011, 10:30
Hi,

I have a QGraphicScene with 200 items and it is very slow when scrolling or moving the items !!!!

I setup my QGraphicsView this way :



QNodesView::QNodesView(QWidget* parent)
:QGraphicsView(parent), _zoomLevel(0), _zoomValue(1.0f)
{
setRenderHint(QPainter::Antialiasing, true);

setBackgroundBrush(QImage(":/pureStudio/Images/pgEditorBG.png"));
setRenderHints(QPainter::Antialiasing | QPainter::SmoothPixmapTransform);

// Multi-selection
setDragMode(QGraphicsView::RubberBandDrag);
// NoDrag, ScrollHandDrag, RubberBandDrag

// Optimization
setOptimizationFlags(QGraphicsView::DontClipPainte r);
setOptimizationFlags(QGraphicsView::DontSavePainte rState);
setOptimizationFlags(QGraphicsView::DontAdjustForA ntialiasing);
setCacheMode(QGraphicsView::CacheBackground);

// Drag & drop
setAcceptDrops(true);
}

wysota
4th August 2011, 10:44
What kind of items are they? If you do a lot of zooming, background caching will not help you much. Try enabling item caching.

pl01
4th August 2011, 11:19
There are 2 kinds of items :
- QGraphicsNode : a rectangular node, with a text
- QGraphicsLink : it is a curve between the items, and an arrow

It looks like theses :
https://picasaweb.google.com/lh/photo/HcTaawhM0bBdtdhsmY9O4A?feat=embedwebsite
http://www.geeks3d.com/public/jegx/200911/nodebox2_03.jpg

I have try with :


setCacheMode(QGraphicsItem::DeviceCoordinateCache) ;


But nothing helps !!! What is strange is that I'm able to display a 3D models with millions of faces, but not a 2D graphic with 700 items !

SixDegrees
4th August 2011, 12:46
What is strange is that I'm able to display a 3D models with millions of faces, but not a 2D graphic with 700 items !

Why is that strange? QGraphicsFramework uses the CPU for its operations, while 3D displays use OpenGL or some other hardware-assisted mechanism. The graphics framework gives you a lot of canned functionality, but it comes at the cost of performance. If you need high performance, implement your own display routines using OpenGL; Qt provides support for such displays, but you'll have to roll a lot of your own supporting code.

wysota
4th August 2011, 13:49
There are 2 kinds of items :
- QGraphicsNode : a rectangular node, with a text
- QGraphicsLink : it is a curve between the items, and an arrow

It looks like theses :
https://picasaweb.google.com/lh/photo/HcTaawhM0bBdtdhsmY9O4A?feat=embedwebsite
http://www.geeks3d.com/public/jegx/200911/nodebox2_03.jpg

I was rather asking whether they are pixmaps or regular items. If they are custom items, please post the implementation of boundingRect(), shape() and a piece of code showing how you create items.


I have try with :


setCacheMode(QGraphicsItem::DeviceCoordinateCache) ;


But nothing helps !!!
So there is completely no difference? What about ItemCoordinateCache?


What is strange is that I'm able to display a 3D models with millions of faces, but not a 2D graphic with 700 items !
Find the bottleneck and we'll deal with it. Or post a minimal compilable example reproducing the problem and we'll help you with finding the bottleneck.

pl01
5th August 2011, 09:23
Thanks,

I have try with setCacheMode(QGraphicsItem::ItemCoordinateCache); but sometimes it 'lock' my application !!! I have to restart it !!! In fact, sometimes it refresh but I have to wait for 40 seconds !!! It is the slowest mode !

Also, I put the code for the 2 items :



QGraphicsLink::QGraphicsLink(QGraphicsNode* output, int outLine, QGraphicsNode* input, int inLine)
{
setZValue(1.0f);

setFlags(ItemIsSelectable | ItemIsFocusable);

setCacheMode(QGraphicsItem::DeviceCoordinateCache) ;
//setCacheMode(QGraphicsItem::ItemCoordinateCache);
}

QRectF QGraphicsLink::boundingRect() const
{
qreal extra = pen().width() * 0.5f + 60.f;

return QRectF(
line().p1(),
QSizeF(line().dx(), line().dy())
).normalized().adjusted(-extra, -extra, extra, extra);
}

void QGraphicsLink::paint(QPainter * painter, const QStyleOptionGraphicsItem * option, QWidget * widget = 0)
{
painter->setRenderHint(QPainter::Antialiasing, true);

//---- Choose the color
int penWidth = 2;
int arrowDelta = 11;
QColor drawColor = Qt::black;
if (OutputNode)
{
QConnector connector = OutputNode->GetOutputConnector(OutputConnectorIdx);
if (connector.Type == PG_VNGEOMETRY)
drawColor = PG_VNGEOMETRY_COLOR;
else if (connector.Type == PG_VNCAMERA)
drawColor = PG_VNCAMERA_COLOR;
else if (connector.Type == PG_VNLIGHT)
drawColor = PG_VNLIGHT_COLOR;
else if (connector.Type == PG_VNMATERIAL)
drawColor = PG_VNMATERIAL_COLOR;
else if (connector.Type == PG_VNFILM)
drawColor = PG_VNFILM_COLOR;
else if (connector.Type == PG_VNINTEGRATOR)
drawColor = PG_VNINTEGRATOR_COLOR;

else if (connector.Type == PG_MNSPECTRUM)
drawColor = PG_MNSPECTRUM_COLOR;
else if (connector.Type == PG_MNSPECTRALFUNCTION)
drawColor = PG_MNSPECTRALFUNCTION_COLOR;
else if (connector.Type == PG_MNFLOAT)
drawColor = PG_MNFLOAT_COLOR;
else if (connector.Type == PG_MNFRESNEL)
drawColor = PG_MNFRESNEL_COLOR;
else if (connector.Type == PG_MNMICROFACETDISTRIBUTION)
drawColor = PG_MNMICROFACETDISTRIBUTION_COLOR;
}
else
{
drawColor = QColor(220, 220, 220);
arrowDelta = 5;
}

if (isSelected())
{
drawColor = QColor(220, 220, 220);
penWidth = 4;
}

//---- Curve
QPainterPath path;

if (line().y1() < line().y2())
{
path.moveTo(line().x1(), line().y1());
float deltaX = 0.25f * (line().x2() - line().x1());
float deltaY = 0.25f * (line().y2() - line().y1());

path.cubicTo(
line().x1(), line().y1() + 3 * 14,
line().x1() + 4 * deltaX, line().y1() + 1 * 14,
line().x2(), line().y2() - 14);
}
else
{
path.moveTo(line().x1(), line().y1());
float deltaX = 0.25f * (line().x2() - line().x1());
float deltaY = 0.25f * (line().y2() - line().y1());

path.cubicTo(
line().x1() + 40, line().y1() + 40,
line().x2() - 40 , line().y2() - 40 - 14,
line().x2(), line().y2() - 14);
}

painter->setPen(QPen(drawColor, penWidth, Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin));
painter->drawPath(path);

//---- Arrow
//Draw an arrow-head shaped path at the end of the line's path
QPainterPath arrowHead;
arrowHead.setFillRule(Qt::WindingFill);

//-- Bottom arrow
QPointF pos;
arrowHead.moveTo(QPointF(-4, -5));
arrowHead.lineTo(QPointF(0, 5));
arrowHead.lineTo(QPointF(4, -5));
arrowHead.lineTo(QPointF(-4, -5));

QMatrix mat;
mat.translate(line().p2().x(), line().p2().y() - arrowDelta);
arrowHead = mat.map(arrowHead);

// Create a graphics path item based on this path
painter->setBrush(drawColor);
painter->drawPath(arrowHead);
}

QPainterPath QGraphicsLink::shape() const
{
//---- Curve
QPainterPath path;

if (line().y1() < line().y2())
{
path.moveTo(line().x1(), line().y1());
float deltaX = 0.25f * (line().x2() - line().x1());
float deltaY = 0.25f * (line().y2() - line().y1());

path.cubicTo(
line().x1(), line().y1() + 3 * 14,
line().x1() + 4 * deltaX, line().y1() + 1 * 14,
line().x2(), line().y2() - 14);
}
else
{
path.moveTo(line().x1(), line().y1());
float deltaX = 0.25f * (line().x2() - line().x1());
float deltaY = 0.25f * (line().y2() - line().y1());

path.cubicTo(
line().x1() + 40, line().y1() + 40,
line().x2() - 40 , line().y2() - 40 - 14,
line().x2(), line().y2() - 14);
}

//---- Arrow
path.setFillRule(Qt::WindingFill);
QPointF pos;
path.moveTo(QPointF(-4, -5));
path.lineTo(QPointF(0, 5));
path.lineTo(QPointF(4, -5));
path.lineTo(QPointF(-4, -5));

QMatrix mat;
mat.translate(line().p2().x(), line().p2().y() - 11);
path = mat.map(path);

return ShapeFromPath(path, QPen(QColor(), 6));
}

QPainterPath QGraphicsLink::ShapeFromPath(const QPainterPath &path, const QPen &pen)
{
// We unfortunately need this hack as QPainterPathStroker will set a width of 1.0
// if we pass a value of 0.0 to QPainterPathStroker::setWidth()
const qreal penWidthZero = qreal(0.00000001);

if (path == QPainterPath())
return path;
QPainterPathStroker ps;
ps.setCapStyle(pen.capStyle());
if (pen.widthF() <= 0.f)
ps.setWidth(penWidthZero);
else
ps.setWidth(pen.widthF());
ps.setJoinStyle(pen.joinStyle());
ps.setMiterLimit(pen.miterLimit());
QPainterPath p = ps.createStroke(path);
p.addPath(path);
return p;
}






QGraphicsNode::QGraphicsNode(QNodesScene* qscene)
{
static int count = 0;
setObjectName( QString("object%1").arg(count++) );
setRect(-QGraphicsNodeWidth, -QGraphicsNodeHeight, QGraphicsNodeWidth, QGraphicsNodeHeight);

qscene->addItem(this);
qscene->AddQGraphicsNode(this);
setFlags(ItemIsMovable | ItemIsSelectable | ItemIsFocusable | ItemSendsScenePositionChanges);

setCacheMode(QGraphicsItem::DeviceCoordinateCache) ;
//setCacheMode(QGraphicsItem::ItemCoordinateCache);

setZValue(2.0f);
}

void QGraphicsNode::initializeBackground(QLinearGradien t& gradient)
{
gradient.setColorAt(0.0f, QColor(60,60,60,255));
gradient.setColorAt(0.3f, QColor(30,30,30,255));
gradient.setColorAt(0.31f, QColor(20,20,20,255));
gradient.setColorAt(1.0f, QColor(20,20,20,255));
}

void QGraphicsNode::paint(QPainter* p, const QStyleOptionGraphicsItem* opt, QWidget* w)
{
Q_UNUSED(w)

if (HasSubTitle())
setRect(-QGraphicsNodeWidth, -QGraphicsNodeHeight, QGraphicsNodeWidth, QGraphicsNodeHeight + 20);

//---- Draw the background
QLinearGradient grad(rect().topLeft(), rect().bottomLeft());
if(isSelected())
{
grad.setColorAt(0.0f, QColor(150,60,60,255));
grad.setColorAt(0.3f, QColor(120,30,30,255));
grad.setColorAt(0.31f, QColor(110,20,20,255));
grad.setColorAt(1.0f, QColor(110,20,20,255));
}
else
initializeBackground(grad);

p->fillRect(rect(), grad);
p->drawRect(rect());

//---- Draw the input connectors
for(int i = 0; i < GetInputConnectorCount(); i++)
{
int connectorType = GetInputConnector(i).Type;
QRectF ir = GetInputConnectorRect(i);

if (connectorType == PG_VNGEOMETRY)
p->fillRect(ir, PG_VNGEOMETRY_COLOR);
else if (connectorType == PG_VNCAMERA)
p->fillRect(ir, PG_VNCAMERA_COLOR);
else if (connectorType == PG_VNLIGHT)
p->fillRect(ir, PG_VNLIGHT_COLOR);
else if (connectorType == PG_VNMATERIAL)
p->fillRect(ir, PG_VNMATERIAL_COLOR);
else if (connectorType == PG_VNFILM)
p->fillRect(ir, PG_VNFILM_COLOR);
else if (connectorType == PG_VNINTEGRATOR)
p->fillRect(ir, PG_VNINTEGRATOR_COLOR);

p->drawRect(ir);
}

//---- Draw the output connectors
for(int i = 0; i < GetOutputConnectorCount(); i++)
{
int connectorType = GetOutputConnector(i).Type;
QRectF or = GetOutputConnectorRect(i);

if (connectorType == PG_VNGEOMETRY)
p->fillRect(or, PG_VNGEOMETRY_COLOR);
else if (connectorType == PG_VNCAMERA)
p->fillRect(or, PG_VNCAMERA_COLOR);
else if (connectorType == PG_VNLIGHT)
p->fillRect(or, PG_VNLIGHT_COLOR);
else if (connectorType == PG_VNMATERIAL)
p->fillRect(or, PG_VNMATERIAL_COLOR);
else if (connectorType == PG_VNFILM)
p->fillRect(or, PG_VNFILM_COLOR);
else if (connectorType == PG_VNINTEGRATOR)
p->fillRect(or, PG_VNINTEGRATOR_COLOR);

else if (connectorType == PG_MNSPECTRUM)
p->fillRect(or, PG_MNSPECTRUM_COLOR);
else if (connectorType == PG_MNSPECTRALFUNCTION)
p->fillRect(or, PG_MNSPECTRALFUNCTION_COLOR);
else if (connectorType == PG_MNFLOAT)
p->fillRect(or, PG_MNFLOAT_COLOR);
else if (connectorType == PG_MNFRESNEL)
p->fillRect(or, PG_MNFRESNEL_COLOR);
else if (connectorType == PG_MNMICROFACETDISTRIBUTION)
p->fillRect(or, PG_MNMICROFACETDISTRIBUTION_COLOR);

p->drawRect(or);
}

//---- Name
QString nodeName = GetName();

QRectF nameRect(rect().left(), rect().top(), rect().width(), 25);
QFont font(p->font());
font.setWeight(QFont::DemiBold);
p->setFont(font);
p->setPen(Qt::white);
p->drawText(nameRect, Qt::AlignCenter, nodeName);

//---- Sub title
if (!HasSubTitle())
return;

QRectF subTitleRect(rect().left(), rect().top() + 20, rect().width(), 25);
font.setWeight(QFont::Light);
p->setFont(font);
p->drawText(subTitleRect, Qt::AlignCenter, GetSubTitle());
}

meazza
5th August 2011, 09:44
For optimizing your QGraphicsView and QGraphicsScene take a look at these two links. They helped me alot.

http://agurines.blogspot.com/2011/02/qgraphicsview-performance.html#comment-form

http://qt.nokia.com/learning/online/talks/developerdays2010/tech-talks/qt-graphics-view-in-depth

MarekR22
5th August 2011, 10:25
Yuor code is not DRY (http://en.wikipedia.org/wiki/Don%27t_repeat_yourself)!
There is QGraphicsPathItem (set no brush update path when positions of connected node changes). This class should be written in best possible way. You can subclass it to save time and performance.

IMHO your items can be build easily with ready items and this should give better results.

pl01
5th August 2011, 11:08
Thanks MarekR22,

But what (and how ??) "set no brush update path when positions of connected node changes" ?

Even, I have completely put in comment the "paint" method from the "link" graphic-item but it is still slow !!! :-( :-(

wysota
5th August 2011, 15:06
Your boundingRect() and shape() implementations are suboptimal. There is no point in recalculating those values each time the method is called. If something makes your application freeze then apparently something in your code is invalid. Implementation of paint() is also suboptimal although this is a secondary issue if you are using item cache.

pl01
5th August 2011, 16:23
Ok,

but how can I do ? is there a way to know that I have to recompute theses values ? Which method/event should I use ?

Thx

wysota
5th August 2011, 17:06
is there a way to know that I have to recompute theses values ?
You are in control of your item. If you do something that changes either the bounding rect or the shape (usually both) and you call prepareGeometryChange() then you have to recalculate those values.