PDA

View Full Version : QGraphicsItem leaves junk on screen



Gopala Krishna
14th December 2006, 16:59
Hello,
I am developing an electric cad software with qt4.2 and obviously using QGraphicsView framework ;)
The problem I am facing is the Wire item - subclass of QGraphicsItem leaves junk on screen when its length or path of wire change rapidly. (Junk ,in the sense, it leaves behind trails of lines on screen). The screenshot attached tells it better.
Actually this happens when component is moved madly fast. :p But since my system is pretty high end and sincethere is every possibility of lots of items to be on view, I don't wan't to take a chance.

The length/path of wire changes when any component connected to wire moves which makes it necessary for the wire to regrow.
Currently I am achieving this by calling Wire::rebuild in component's itemChange()
I guess QGraphicsItem:: prepareGeometryChange() is the culprit behind this. Or may be I am doing something wrong in boundingRect() or shape().
How can I avoid this ?


void Wire::rebuild(const QPointF& s, const QPointF& e)
{
//QList<QLineF*> m_lines is member variable
if(!m_lines.isEmpty())
{
qDeleteAll(m_lines);
m_lines.clear();
}


QPointF st = mapFromScene(s);
QPointF en = mapFromScene(e);

if(st.x() == en.x() || st.y() == en.y())
{
m_lines.append(new QLineF(st,en));
prepareGeometryChange();
return;
}

QPointF inter = QPointF(st.x(),en.y());
m_lines.append(new QLineF(st,inter));
m_lines.append(new QLineF(inter,en));
prepareGeometryChange();
}

QRectF Wire::rectForLine(const QLineF& line) const
{
qreal x = qMin(line.p1().x() , line.p2().x());
qreal y = qMin(line.p1().y() , line.p2().y());
qreal w = qAbs(line.p1().x() - line.p2().x());
if(w < 1.0)
w = 1.0;
qreal h = qAbs(line.p1().y() - line.p2().y());
if(h < 1.0)
h = 1.0;
return QRectF(x,y,w,h);
}

QRectF Wire::boundingRect() const
{
QRectF rect(0.0,0.0,0.0,0.0);
foreach(QLineF* line, m_lines)
rect |= rectForLine(*line);
return rect.adjusted(-1.0,-1.0,1.0,1.0);
}

QPainterPath Wire::shape() const
{
QPainterPath path;
if(m_lines.isEmpty())
return path;
foreach(QLineF *line, m_lines)
path.addRect(rectForLine(*line));
return path;
}

Bitto
21st December 2006, 07:48
You should call prepareGeometryChange() before changing the item's geometry; not after. Try moving the call to the very top of your function, and see what happens.

If that doesn't remove the problem, could you please try to isolate it, or post a complete example?

Gopala Krishna
21st December 2006, 11:42
You should call prepareGeometryChange() before changing the item's geometry; not after. Try moving the call to the very top of your function, and see what happens.

If that doesn't remove the problem, could you please try to isolate it, or post a complete example?

Thanks a lot for the tip !!! :)
Its working pretty fine after i moved prepareGeometryChange() to top of the function as you said. Thanks again!

durbrak
23rd December 2006, 15:06
Hey,
I was wondering regarding your attached screenshot - what's that at the left side of your application where it says vertically "Components"? Is this some kind of Sidebar or something? Did you do it on your own or does Qt has something like that? Or do you even have it from somewhere else?
Just curious :)

Gopala Krishna
23rd December 2006, 15:17
Hey,
I was wondering regarding your attached screenshot - what's that at the left side of your application where it says vertically "Components"? Is this some kind of Sidebar or something? Did you do it on your own or does Qt has something like that? Or do you even have it from somewhere else?
Just curious :)

Yes, it is a sidebar(more appropriately toolview). I am using krawek's ideality library (http://kde-apps.org/content/show.php?content=44015) - an external library which really makes it easy to use. :)

Gopala Krishna
30th December 2006, 17:58
Hi guys,
Though the wires in my app were moving appropriately the performance was bit low when I tried on my old low end comp. The problem was , one of the lines in wire wasn't being drawn at all when the wires geometry changes rapidly espescially, when lines of wire were perpendicular and lengthy as shown below , where "dash-line" represents undrawn line.


|
|
|
|
|_______________________

The reason I guess is overhead in scene's index management system caused due to repeated "prepareGeometryChange()" calls and also to the fact that the boundingRect coveres unwanted areas resulting in more updates.

So, I came up with an ugly/good hack by replacing wires with "QRubberBand" of unit width (to represent line) while changing geometry and "QGraphicsLineItem" while static. I hide the QGraphicsLineItem when wire's geometry starts changing and show QRubberBand and vice-versa otherwise . This is working as I expected - fast and clean.
(I've attached screenshot with QRubberBand "proxying" lines.)

Do you think this hack is a better alternative or Is there any better way ? Is there any potential problem ?

wysota
30th December 2006, 19:41
Maybe you should turn off BSP indexing while moving objects around? Or if you have composite lines, make them separate objects, so that their bounding rect is much smaller.

rohitjun
21st March 2007, 15:52
Hi Gopal

I read your problem that you faced in the past with the wiring two components. I am doing some thing like the same in my application. For the wiring two components, Can you suggest me how to proceed. Do i have to call the wiring class refrence in the Mouse press event of the component classs or ????????????. Please suggest me some thing or send me example some code.

Thanks alot.
Rohit

Gopala Krishna
21st March 2007, 18:37
Hello,

Hi Gopal

I read your problem that you faced in the past with the wiring two components. I am doing some thing like the same in my application. For the wiring two components, Can you suggest me how to proceed. Do i have to call the wiring class refrence in the Mouse press event of the component classs or ????????????. Please suggest me some thing or send me example some code.

Thanks alot.
Rohit

It depends on how you want to wire the components. It can be done either in the component class or the scene. What I do in my app is, when a component connected to other component is moved, I create an instance of wire in component's mouse move event. When the component is moved further without releasing mouse, I keep updating wire by calling rebuild() in mouse move event itself.
But I couldn't understand you completely. Sorry for that.

Bitto
22nd March 2007, 08:26
In 4.2, there's certainly a hit when reindexing items rapidly. This hit is gone in the upcoming 4.3. Any rendering problems you have had with vertical or horizontal lines should have been fixed in 4.2.3, and is definitely gone in 4.3.

QRubberBand probably works fine for your case (unless you rotate your view, that is). But with 4.3, I don't think you need it.

rohitjun
22nd March 2007, 11:30
Thanks Gopal and Bitto...

I understood from your code Gopal how to rebuild wiring when the component is moved. But i think i am in the step before of the component move step. Means i am just trying to connect two components through wiring. As to do wiring i have to pass the starting component and ending component in the wiring instance. What i have to impement in the mouse events of the component class to acheive this. I am sorry as i have very few experience of GUI designing.

Thanks
Rohit

rohitjun
22nd March 2007, 13:23
Hi

May be i was not able to make you understand my question last time.

The two components are not connected in the begining. So when the user want to connect two componets, he has to click the first component and then move the mouse till second component without releasing the mouse and then release the mouse on the second component. Then the wiring will be done. And after that the same what you did...Rebuild the wiring if the component is moved.

Please suggest
Thanks
Rohit

Gopala Krishna
22nd March 2007, 14:34
In 4.2, there's certainly a hit when reindexing items rapidly. This hit is gone in the upcoming 4.3. Any rendering problems you have had with vertical or horizontal lines should have been fixed in 4.2.3, and is definitely gone in 4.3.

QRubberBand probably works fine for your case (unless you rotate your view, that is). But with 4.3, I don't think you need it.

Oh Good news!
Hey , can I expect good speed while moving about 20*3 items ( each of 20 items move 3 more items ) in Qt 4.3. This is what is challenging to me presently.

Gopala Krishna
22nd March 2007, 14:40
Hi

May be i was not able to make you understand my question last time.

The two components are not connected in the begining. So when the user want to connect two componets, he has to click the first component and then move the mouse till second component without releasing the mouse and then release the mouse on the second component. Then the wiring will be done. And after that the same what you did...Rebuild the wiring if the component is moved.

Please suggest
Thanks
Rohit

Ok I get it now.
When a component is moved and placed(after mouse release) on other component, I simply add the new componet in the component's list of the corresponding node and store the connection status. I dont create wire here.

Next when the user moves any one of these components , I create a new node and update the connections. Then I create a wire between these two nodes.
If the wire exist beforehand, I just keep updating the wire's geometry ( in mycase I call rebuild() )
And yes I do all this in scene's subclass since I need to keep track of everything for undo/redo.

Gopala Krishna
22nd March 2007, 14:47
Thanks Gopal and Bitto...

I understood from your code Gopal how to rebuild wiring when the component is moved. But i think i am in the step before of the component move step. Means i am just trying to connect two components through wiring. As to do wiring i have to pass the starting component and ending component in the wiring instance. What i have to impement in the mouse events of the component class to acheive this. I am sorry as i have very few experience of GUI designing.

Thanks
Rohit

What you can do is the moment the component is placed on another , update the connection status of both the components. Probably you can create 0 length wire. You need to store locations of connection points in wire object.
When the user moves any of this component , update both the end points in wire (if you don't know how to please ask) and just rebuild the wire(). Probably you can implement in mouse events of Component

rohitjun
22nd March 2007, 16:00
Thanks for the help Gopal.

Your reply is very helpful for my understanding.

But one more thing, The components are not moved.

I created a shape of And_gate having input and output connectors. I have to connect the output of one And_gate to the input of the another And_gate. The And_gate is movable but not the connector (Connector is attached with the And_gate so it will move when And_gate is moved). And i have to connect the connecotrs and store the And_Gate information with their connections.

Rohit

rohitjun
22nd March 2007, 16:25
I am explaining my problem again below...Gopal

Suppose there are two Gates in the GUI having one input and one output connector. I should be able to connect the Gates with the wire (Means output connector of one Gate with the input connector of the other Gate). The wiring will done this way...The user will take the mouse on the output connector of the one gate and then with out releasing the mouse, the user will go to the input connector of the other Gate and then release the mouse.The wiring will be visible in the GUI.

This is similar procedure as LabVIEW uses(Just for your info).
Rohit

Gopala Krishna
11th December 2007, 08:51
In 4.2, there's certainly a hit when reindexing items rapidly. This hit is gone in the upcoming 4.3. Any rendering problems you have had with vertical or horizontal lines should have been fixed in 4.2.3, and is definitely gone in 4.3.

QRubberBand probably works fine for your case (unless you rotate your view, that is). But with 4.3, I don't think you need it.

I had forgotten to report my experiment.
Even with qt-4.3 there is "not so great" performance. :( But ofcourse the problem is worst only when the wire's bounding rect is huge (say rect of size 800x600) and the wire is being moved constantly.

As a result, i still stick on to using rubberband as the view isn't rotated throughout.

aamer4yu
12th December 2007, 06:23
Hi, havent read ur long messages.,,,,
but a thing came to my mind.. have u tried using shape() with boundingRect() ?? shape() defines the actual area/shap of ur item, so it help in speed,,, i guess
am not so sure of it,,, but u can give it a try :)