PDA

View Full Version : moving one graphical item over other item...



salmanmanekia
1st July 2008, 07:26
Hi,
I want to make the green circle move through the big grey circle as if it is a progress bar...please see the link ..
i am not at all sure how to accomplish this,for my part i think it would be somehow through QPainterPath,its what i think....any help

wysota
1st July 2008, 07:29
With what exactly do you have a problem? This seems a simple task...

salmanmanekia
1st July 2008, 07:41
basically i have implemented the picture in the previous attachment such that the green circle is expressed by QPainterPath..while the grey circle has been drawn in paint() function of QGraphics Item...
i'll post the code here , i am not sure if doing things this way ,i would be able to move the green circle on grey circle...here is the code


ProgressWidget::ProgressWidget()
{
r1.setRect(85,-13.5,30,30);
path.addEllipse(r1);
}

void ProgressWidget::paint(QPainter *painter, const QStyleOptionGraphicsItem *option,
QWidget *widget)
{
painter->save();
painter->setPen(QPen(QBrush(Qt::gray,Qt::SolidPattern),30,Q t::SolidLine,Qt::FlatCap,Qt::MiterJoin));
painter->drawEllipse(0, 0, 200, 200);
painter->restore();
painter->fillPath( path, QBrush( Qt::green, Qt::SolidPattern ) );
}
QRectF ProgressWidget::boundingRect() const
{
qreal penWidth = 150;
return QRectF(-10 - penWidth / 2, -10 - penWidth / 2,20 + penWidth / 2, 20 + penWidth / 2);
}


class ProgressWidget:public QGraphicsItem
{
public:
QRectF r1;

QPainterPath path;


void paint(QPainter *painter, const QStyleOptionGraphicsItem *option,
QWidget *widget);
QRectF boundingRect() const;

ProgressWidget();
~ProgressWidget();
};

caduel
1st July 2008, 07:57
well, how about adding a slot advance() to that widget, connect it to a QTimer;

advance() increments an internal counter (the angle of your circle) and calls update();

base the drawing (the position) of the circle on that angle.

HTH

salmanmanekia
1st July 2008, 08:12
your idea seems to be acceptable..but the problem here is the moving of the green circle in grey circle ...the question is how can i move it...for now i just want to move it...:)

wysota
1st July 2008, 09:01
QGraphicsItem::setPos() is used to move items around. But basically I don't know why you are doing this inside graphics view, unless of course this is a requirement. I would implement a simple custom widget and draw the circles in the paint event based on the current progress.

salmanmanekia
1st July 2008, 09:11
Ok,so what i understand is that,that this function of drawing two circles and moving can be easily done if i do it by QWidget instead of QGraphicsItem...
do i understand correctly...
but in my case i have a QGraphicsSCene in which i want to include the item ,thatswhy i consider it as an item..
also is there any chance of implementing the movement using the code in my first post...i have used a QPainterPath() there..i mean to ask that am i going in the wrong direction by using it...or i can also implement it that way...Thanks

wysota
1st July 2008, 10:08
Ok,so what i understand is that,that this function of drawing two circles and moving can be easily done if i do it by QWidget instead of QGraphicsItem...
You can do it using graphics view as well, but in this case it only complicates things without giving any benefit.

If you need graphics view, then implement your progress bar as a single item - this way it's the same as you would implement it as a widget.

salmanmanekia
1st July 2008, 10:14
If you need graphics view, then implement your progress bar as a single item - this way it's the same as you would implement it as a widget
actually i am implementing it as a single item as you can see in my code in first post ...or do u mean to say that i should consider both the green circle and grey circle as seperate items instead of using something like QPainterPath for the green circle ..
also

You can do it using graphics view as well, but in this case it only complicates things without giving any benefit.
can you please explain how it makes things easy if i use QWidget because i thought this hierarchy is good like QGraphicsView->Scene->Item....:)

caduel
1st July 2008, 10:25
no, you're not implementing it as a single item.
You are implementing an item that does half the work by itself but contains another item.

Wysota suggested to not use an item in your item, but do all the work in your item in paint().

Just reimplement paint() -you'd have to do that regardless whether you're implementing a QWidget or a QGraphicsViewItem- and paint both your big circle and the small one in it.
The small ones position depends on some progress measure.

HTH

wysota
1st July 2008, 11:01
Oh, one more thing - get rid of the painter path, you don't need it. Simply draw two or three ellipses.

salmanmanekia
1st July 2008, 12:05
no, you're not implementing it as a single item.
can you please put more light on it,because i still think that there is only one item..
also correct me if i am wrong that you are suggesting me to just simply draw two ellipses as
QGraphicsEllipseItem and then find some mechanism to move one ellipse over another in the paint() function...

The small ones position depends on some progress measure.
also any guide on 'SOME PROGRESS MEASURE '..
Thanks alot

caduel
1st July 2008, 12:17
I got the impression that the green circle was an item. Seems it is not. Sorry for the confusion.

I (we) merely suggested that you just draw 2 ellipses in paint().
Something like that (untried)


void ProgressWidget::paint(QPainter *painter, const QStyleOptionGraphicsItem *option,
QWidget *widget)
{
painter->save();
painter->setPen(QPen(QBrush(Qt::gray,Qt::SolidPattern),
30,Qt::SolidLine,Qt::FlatCap,Qt::MiterJoin));
painter->drawEllipse(0, 0, 200, 200);

// draw your progress marker (the circle) over it
// rotate/translate painter so you don't have to do trigonometry
painter->translate(100,100); // move origin to middle
painter->rotate(...); // depending on your progress
painter->translate(0,80); // move origin into your ellipse
painter->setPen(QPen(QBrush(Qt::green,Qt::SolidPattern),
3,Qt::SolidLine,Qt::FlatCap,Qt::MiterJoin));

painter->drawEllipse(0, 0, 20,20);
painter->restore();
}


'SOME PROGRESS MEASURE':

a counter that increases with your 'progress'
I would suggest to use an int, ranged from 0-359; interpreted as the angle (see: rotate);
with every advance, you increase (mod 360) this angle
that way repeatedly advancing will make your little circle move round the ellipse

HTH

salmanmanekia
1st July 2008, 12:28
Thanks ..it seems you have left me with deciding how to rotate...Thanks AGAIN :)

caduel
1st July 2008, 12:38
not how to rotate, just to what angle.
QPainter::rotate etc do all the hard work for you. You just have to provide an angle.

See the AnalogClock example for the usage of rotate.

salmanmanekia
1st July 2008, 12:43
To be honest with you, i am realy surprised that few lines could do this thing..i thought i had to write some lengthy code...any ways the good part is that i did understand now how things works....
i had some question popping regarding QPainter and related to that ,so there they are
1,Initially as you know i was trying to use QGraphicsPathItem...can you tell me why PathItem is not suitable in my case and where should it be used ?
2,Also can you tell what difference does it make with save() and restore() method..
and last but not the least
..Thanks

caduel
1st July 2008, 13:50
1,Initially as you know i was trying to use QGraphicsPathItem...can you tell me why PathItem is not suitable in my case and where should it be used ?
Don't know. Probably it is not efficient or, for your simple need, overy complicated.

well
2,Also can you tell what difference does it make with save() and restore() method..
I have to guess a bit here...

basically you are passed a QPainter. Calling rotate() -amongst other things- you modifiy it.
So, either the code calling you, has to restore the painter to a know good state, or you have to. Otherwise the painting of other parts of the widget might use your (inappropriate) modifications to the QPainter.

If it is your responsibility, it is only done when necessary.

salmanmanekia
1st July 2008, 14:19
Thanks ..but i think my problem are not over yet...:)
i am trying to move green circle this way but it shows green circle on view everywhere else then the grey circle ....





painter->setPen(QPen(QBrush(Qt::gray,Qt::SolidPattern),30,Q t::SolidLine,Qt::FlatCap,Qt::MiterJoin));
painter->drawEllipse(0, 0, 200, 200);
painter->setPen(QPen(QBrush(Qt::green,Qt::SolidPattern),15, Qt::SolidLine,Qt::FlatCap,Qt::MiterJoin));

for(int i = 30; i< 120; i=i+30)
{
painter->translate(dx,dy);
painter->rotate(i);
painter->translate(0,91);
painter->drawEllipse(0,0,15,15);
}

also i want the previous green circle to be removed as the new green circle draws itself to the next position ...and it should look like a progress ..as if green circle revolves around grey in a couple of second...i hope you understand..

wysota
1st July 2008, 17:35
You have to redraw the whole widget when the progress value changes and not draw all of the values at once :)

salmanmanekia
1st July 2008, 18:27
You have to redraw the whole widget when the progress value changes and not draw all of the values at once
aah..i am confused i have to draw the WHOLE widget and also not draw ALL values...whats this supposed to mean:rolleyes:

wysota
1st July 2008, 19:03
You have to animate the ellipse over the other ellipse by drawing one frame during every redraw cycle and not all frames during one redraw cycle. When you update the progress, call update() and your widget will redraw itself with the new value.

salmanmanekia
2nd July 2008, 07:56
You have to animate the ellipse over the other ellipse by drawing one frame during every redraw cycle and not all frames during one redraw cycle
sorry but may be it would sound absurd,but do you mean that the number of times i have to draw the green circle is the number of times i have to implement the paint function...it somehow doesnt make sense to me..but this is what i am able to understand..

caduel
2nd July 2008, 08:20
no.

You need one paint() function.
This function is called when the CURRENT state of your widget/item is to be drawn.
I.e. you implement this function to paint your circle at the one position that corresponds to your current angle/progress value.



painter->setPen(QPen(QBrush(Qt::gray,Qt::SolidPattern),30,Q t::SolidLine,Qt::FlatCap,Qt::MiterJoin));
painter->drawEllipse(0, 0, 200, 200);
painter->setPen(QPen(QBrush(Qt::green,Qt::SolidPattern),15, Qt::SolidLine,Qt::FlatCap,Qt::MiterJoin));
painter->translate(dx,dy);
// draw one circle at the position that is stored in your widget
// i.e. your widget knows its state (in numbers): just visualize it
painter->rotate(yourCurrentAngleAsStoredInSomeWidgetMemberV ariable);
painter->translate(0,91);
painter->drawEllipse(0,0,15,15);

When progress advances, you increase your "yourCurrentAngleAsStoredInSomeWidgetMemberVariable" variable and call update().
When Qt sometime later causes a repaint, paint() gets called and you just access the current value of that variable.
This results in an animation.

I.e. you do not animate inside paint().
paint() paints snapshots, no movement or animation whatsoever.
Animation results from the repeated calls (of update() -> paint()) as a sequence of such snapshots.

HTH

salmanmanekia
2nd July 2008, 08:41
When progress advances, you increase your "yourCurrentAngleAsStoredInSomeWidgetMemberVariabl e" variable and call update().
does this has to be done by signal/slots in my paint() function ?


Animation results from the repeated calls (of update() -> paint()) as a sequence of such snapshots.
where should these call be make and are you sure about update()->paint()..because it doesnt work

caduel
2nd July 2008, 10:07
no, as i wrote in an earlier posting:

add a slot to your widget/item, call it advance() -or whatever, let's assume you called it advance()-


YourItem::advance()
{
angle +=10;
angle %=360;
update();
}

(and don't forget the Q_OBJECT macro in your class declaration!)

somewhere else:

QTimer timer;
yourItem->connect(&timer, SINGAL(timeout()), SLOT(advance()));
timer.start(25);

(Of course your timer must not be/go out of scope, otherwise the animation stops.
Perhaps it is best to declare it as QTimer* in your widget and allocate it with new.)

salmanmanekia
2nd July 2008, 10:08
i have tried to implement the thing this way ,but it wont work..
now i have two questions when i debug the code compiler says that
'warning: Object::connect: No such slot QObject::advance()'

and most importantly am i doing right thing concept wise..



ProgressWidget::ProgressWidget()
{
r1.setRect(85,-13.5,30,30);
dx = 100;
dy = 100;
timerProg = new QTimer();
connect(timerProg,SIGNAL(timeout()),this,SLOT(adva nce()));
timerProg->start(1000);
}

void ProgressWidget::paint(QPainter *painter, const QStyleOptionGraphicsItem *option,
QWidget *widget)
{
//painter->save();
painter->setPen(QPen(QBrush(Qt::gray,Qt::SolidPattern),30,Q t::SolidLine,Qt::FlatCap,Qt::MiterJoin));
painter->drawEllipse(0, 0, 200, 200);
painter->setPen(QPen(QBrush(Qt::green,Qt::SolidPattern),15, Qt::SolidLine,Qt::FlatCap,Qt::MiterJoin));
painter->translate(dx,dy);
painter->rotate(rotation);
painter->translate(0,91);
painter->drawEllipse(0,0,15,15);
}
QRectF ProgressWidget::boundingRect() const
{
qreal penWidth = 150;
return QRectF(-10 - penWidth / 2, -10 - penWidth / 2,20 + penWidth / 2, 20 + penWidth / 2);
}
void ProgressWidget::advance()
{
rotation = rotation + 30;
this->update();
}
ProgressWidget::~ProgressWidget()
{
}




#include <QObject>
#include <QtGui/QWidget>
#include <QTimer>

#include <QGraphicsItem>
#include <QPainter>
#include <QPainterPath>

class ProgressWidget:public QGraphicsItem,public QObject
{
public:
QRectF r1;

qreal dx ;
qreal dy ;

QTimer *timerProg;

int rotation;

void paint(QPainter *painter, const QStyleOptionGraphicsItem *option,
QWidget *widget);
QRectF boundingRect() const;

ProgressWidget();
~ProgressWidget();

public slots:
void advance();
};

#endif

salmanmanekia
2nd July 2008, 10:36
also surprisingly if i add Q_OBJECT macro it throws a compile time error:eek:..and says 'connect undeclared'

salmanmanekia
2nd July 2008, 11:05
sorry for the last post..it was a mistake from my side ..anyways ..the green bar moves around the grey circle but if i only minimized and then maximized my output screen...it issnt moving on the screen if i dont minimize it...to simplify it doesnt move in normal conditions :)

caduel
2nd July 2008, 11:28
The code looks ok (perhaps the 1000ms you pass in start is a bit large).

If you provide (attachment) a self-contained compilable example, we can try to help you with the minimize/maximize issue.

salmanmanekia
2nd July 2008, 12:06
i have tried it with lesser value ..but still the same result...in the docs i found this statement..

This function does not cause an immediate paint; instead it schedules a paint request that is processed by QGraphicsView after control reaches the event loop
..my class hiererachy is something like i am calling from a main function a class named training
such as
class training: public QGraphicsView..
from this class i call an instance of
class scene such as
class scene: public QGraphicsScene
and after that in hierarchy comes the class item
class item: public QGraphicsItem..
and all the work i am doing for now is done in item class..so again coming to the docs i think this line has soemthing to do with the situation i am facing...

caduel
2nd July 2008, 13:00
Put debug statements (qDebug() << __PRETTY_FUNCTION__;) inside the slot and the paint() function... that way you can see if these get called as often as expected.

salmanmanekia
2nd July 2008, 14:07
i am using eclipse integration with qt ..and it shows that the paint() and advance slots are called consecetively...
i think this problem is due to something else ,may be as i posted in my previous post...

salmanmanekia
2nd July 2008, 14:48
If you provide (attachment) a self-contained compilable example,
what do you mean ?,the code or .exe or something else....i didnt get it

caduel
2nd July 2008, 15:30
Well, compilable kind of implies source code, right :o ?

I am Linux based, so your exe would not help me. And I hardly could debug/modify it.
If you can post a (minimal) compilable example (as a .zip, or .tar.gz file) I could try it out and help.

salmanmanekia
3rd July 2008, 06:53
Hi..
I have attached all the source ,header and .pro file..i hope you would be able to figure out something...just one small correction,please remove TrainingUI.qrc from the .pro file

caduel
3rd July 2008, 07:29
The error is: your bounding rect is not correct.
Your code does try to repaint, but the incorrect bounding rect prevents the relevant area to be redrawn.

Return something like QRectF(0,0,300,300) (which errs, too, but on the 'safe' side). And you see will something moving.

Please read up in the docs on QGraphicsItem::boudingRect and perhaps QGraphicsItem::shape.

HTH

salmanmanekia
3rd July 2008, 07:59
Thanks it worked...u deserve a great Thanks ..for all the way helping me into this...and answering sometime il/logical questions ;)