Algorithm animation(QPainter)
-
wrote on 24 Jan 2016, 14:50 last edited by
I'm trying to make an animation of an algorithm, so far I could only add some rectangles with the scene->addRect(20+(i*30),0-random_numbers_[i],28,random_numbers_[i],Pen,darkMagentaBrush);
I'm a little bit confused, how I can change the color of the rectangle, for example the fifth one and the sixth, to show that this two will swap. Is it better to use the QPainter?? -
Hi,
It would be nice if you provided more info or ocde, but I'll just assumescene
in your code is QGraphicsScene (am I right?).
If so, then theaddRect()
method returns a pointer toQGraphicsRectItem
representing your rectangle.
You should store these pointers in some array, likeQVector<QGraphicsRectItem*>
.
Then you can get to any of the items and use its setBrush method to set any brush you want on it. The QBrush you set can be a solid color, gradient , texture or a pattern, whatever you need. -
Hi,
It would be nice if you provided more info or ocde, but I'll just assumescene
in your code is QGraphicsScene (am I right?).
If so, then theaddRect()
method returns a pointer toQGraphicsRectItem
representing your rectangle.
You should store these pointers in some array, likeQVector<QGraphicsRectItem*>
.
Then you can get to any of the items and use its setBrush method to set any brush you want on it. The QBrush you set can be a solid color, gradient , texture or a pattern, whatever you need.wrote on 24 Jan 2016, 16:10 last edited by@Chris-Kawa, Thank you. BTW can I draw a number inside of a rectangle? I tried to use QPainter but no luck
-
You can create a QGraphicsSimpleTextItem and put it inside of you rectangle.
Beware though that you're creating rectangles with an anchor outside of them so the text item won't align properly.You could use something like this instead:
for(int i = 0; i < whatever; ++i) { QGraphicsRectItem* rectangle = scene->addRect(0, 0, someWidth, someHeight); QGraphicsSimpleTextItem* text = new QGraphicsSimpleTextItem("hello", rectangle); rectangle->moveBy(someOffsetX, someOffsetY); }
-
You can create a QGraphicsSimpleTextItem and put it inside of you rectangle.
Beware though that you're creating rectangles with an anchor outside of them so the text item won't align properly.You could use something like this instead:
for(int i = 0; i < whatever; ++i) { QGraphicsRectItem* rectangle = scene->addRect(0, 0, someWidth, someHeight); QGraphicsSimpleTextItem* text = new QGraphicsSimpleTextItem("hello", rectangle); rectangle->moveBy(someOffsetX, someOffsetY); }
wrote on 24 Jan 2016, 18:06 last edited by@Chris-Kawa And the last question. How I can do smth like this, for example, I have a red rectangle and after 5 sec it becomes blue during run time?
-
I have a red rectangle and after 5 sec it becomes blue during run time?
One way would be with a timer:
QGraphicsRectItem* rectangle = ... //get it from somewhere QTimer::singleShot(5000, [=]{ rectangle->setBrush(Qt::blue); });
-
I have a red rectangle and after 5 sec it becomes blue during run time?
One way would be with a timer:
QGraphicsRectItem* rectangle = ... //get it from somewhere QTimer::singleShot(5000, [=]{ rectangle->setBrush(Qt::blue); });
wrote on 24 Jan 2016, 18:28 last edited by@Chris-Kawa said:
QTimer::singleShot(5000, [=]{ rectangle->setBrush(Qt::blue); });
MOTHER OF GOD! Thank you so much now I can continue with the app :-)
-
I have a red rectangle and after 5 sec it becomes blue during run time?
One way would be with a timer:
QGraphicsRectItem* rectangle = ... //get it from somewhere QTimer::singleShot(5000, [=]{ rectangle->setBrush(Qt::blue); });
wrote on 24 Jan 2016, 19:16 last edited by@Chris-Kawa Ok, got a little bit stuck again how I can update the scene view. For example I found that one rectangle is bigger than the other one how I can animate the swap. I have a list of pointers to the rectangles, after I swapped the pointers in the list, how I can repaint the GraphicsView with the new pointers?
-
one rectangle is bigger than the other one how I can animate the swap
Sorry, I can't make sense of what you're asking. What swap? What does a rectangle size have to do with it?
after I swapped the pointers in the list, how I can repaint the GraphicsView with the new pointers?
Again this makes no sense. Moving a pointer in a list has nothing to do with graphics view. Do you mean you want to move the rectangle in the scene or what? A pointer to a graphics item has nothing to do with its position on the scene.
What are you trying to do exactly? I mean conceptually.
-
one rectangle is bigger than the other one how I can animate the swap
Sorry, I can't make sense of what you're asking. What swap? What does a rectangle size have to do with it?
after I swapped the pointers in the list, how I can repaint the GraphicsView with the new pointers?
Again this makes no sense. Moving a pointer in a list has nothing to do with graphics view. Do you mean you want to move the rectangle in the scene or what? A pointer to a graphics item has nothing to do with its position on the scene.
What are you trying to do exactly? I mean conceptually.
wrote on 24 Jan 2016, 19:31 last edited by@Chris-Kawa I thought I could repaint the new List which contains the pointers. I want to swap two rectangles. The first one is = 3(height) the second one is = 2; I want to swap them, so that on the first position would be rectangle number two with the height of 2, and on the second position would rectangle number one with height = 3. But I want it to swap with some pause, so I could see it, smth like a timer.
-
You don't need to repaint anything manually. You just change the position of the item in the scene and it will refresh the view automatically. To animate the move you can use a QGraphicsItemAnimation.
Here's a simple example:
void moveItemTo(QGraphicsItem* item, const QPointF& endPoint) { QTimeLine* t= new QTimeLine(2000); t->setEasingCurve(QEasingCurve::InOutCubic); QObject::connect(t, &QTimeLine::finished, t, &QTimeLine::deleteLater); QGraphicsItemAnimation* a = new QGraphicsItemAnimation(t); a->setTimeLine(t); a->setItem(item); a->setPosAt(0.0, item->pos()); a->setPosAt(1.0, endPoint); timeLine->start(); }
-
You don't need to repaint anything manually. You just change the position of the item in the scene and it will refresh the view automatically. To animate the move you can use a QGraphicsItemAnimation.
Here's a simple example:
void moveItemTo(QGraphicsItem* item, const QPointF& endPoint) { QTimeLine* t= new QTimeLine(2000); t->setEasingCurve(QEasingCurve::InOutCubic); QObject::connect(t, &QTimeLine::finished, t, &QTimeLine::deleteLater); QGraphicsItemAnimation* a = new QGraphicsItemAnimation(t); a->setTimeLine(t); a->setItem(item); a->setPosAt(0.0, item->pos()); a->setPosAt(1.0, endPoint); timeLine->start(); }
wrote on 24 Jan 2016, 20:11 last edited by mandruk1331@Chris-Kawa I'll give it a try. Thank you a lot. And the last question, for sure, How I can emit a SingleShot every second in a loop, because
for(int i=0;i<random_numbers_.size()-1;i++){
for(int j=0;j<random_numbers_.size()-1;j++){
QTimer::singleShot(1000, [=]{ rectan[i]->setBrush(Qt::green); });
}
During the executing, after a second it makes every rectangle till the 9-th green, it loops and SingleShots every second in a loop. But how I can do this with a pause, liek: The first one is green, after a second the second becomes green, so I could see it?
} -
for(int i = 0; i < random_numbers_.size(); ++i) { QTimer::singleShot(1000 + i * 1000, [=]{ rectan[i]->setBrush(Qt::green); }); }
-
for(int i = 0; i < random_numbers_.size(); ++i) { QTimer::singleShot(1000 + i * 1000, [=]{ rectan[i]->setBrush(Qt::green); }); }
wrote on 24 Jan 2016, 20:17 last edited by@Chris-Kawa I thought about that but is there a way that SingleShot won't be chained by time, because In the future I will need to set the second one green and the first one red, So it'l look like that the rectangle is crawling till the end
-
Ok, I think you're going deeper and deeper into the woods but it's a totally bad direction.
So if I'm getting this right you are making some algorithm that does stuff (sorting?) to some items represented by rectangles and you want to visualize the steps. Is that right?
In that case setting up hardcoded timers and single shots is going against the wind because you're effectively tying to predict the whole thing and set timers for it. That's just gonna give you a headache and you won't know what you did the next day.Step back and look at the problem. You've got some algorithm that operates on an array. Conceptually:
while(!done()) { doAnotherStep(); }
Now you want to visualize the steps, conceptually:
while(!done()) { doAnotherStep(); visualizeTheStep(); }
The problem is that the visualization should take time, so this approach is not gonna work. What you need to do is make it event driven, i.e. conceptually:
stepEnded -> visualize visualizationEnded -> done ? exit : doAnotherStep
For this you can use the signal/slot mechanism of Qt. Make a class that does the algorithm and has a step method. At the end of the method emit a signal. Make another class for visualization and give it a visualize method. At the end of it emit a signal. Then just connect the two.
Rough concept:
class Algo : public QObject { Q_OBJECT public: Algo(Vector<stuff>* data) : dataPtr(data) {} bool done() { return /* check if more steps needed */ } void doStep() { /* do stuff to dataPtr */; emit stepFinished(someStuffToSwap, someOtherStuffToSwap); } signal: void stepFinished(stuff* item1, stuff* item2); private: Vector<stuff>* dataPtr; }; class Vis : public QObject { Q_OBJECT public: Vis(Vector<stuff>* data) { /*create graphics items and whatnot */ } void visualizeStep(stuff* item1, stuff* item2) { /* do the animations here and emit finished() when they're done*/ } signals: void finished(); }; //and then you can use it like this: Vector<stuff> data = ... Algo algo(&data); Vis vis(&data); connect(algo, &Algo::stepFinished, vis, &Vis::visualizeStep); connect(vis, &Vis::finished(), [&]{ if(!algo.done()) algo.doStep(); }); algo.doStep(); /start the first step
1/15