How to change size of QGraphicsPixmap using animation? - c++

I have class named Pixmap deriving from QGraphicsPixmapItem and StartScreen class deriving from QGraphicsScene. I want to use animations (QPropertyAnimation class) to resize displayed image in certain time range. Other actions like setting position or rotation aren't problem but I couldn't find any property like size (e.g. setSize() method). How can I do that in the other way? Thanks for advance.
StartScreen::StartScreen(int windowWidth, int windowHeight)
{
setSceneRect(0, 0, windowWidth, windowHeight);
setBackgroundBrush(QBrush(QImage(":/images/background.png")));
Pixmap * logo = new Pixmap(":/images/logo.png");
addItem(logo);
logo->setPos((windowWidth - logo->pixmap().width()) / 2, (windowWidth - logo->pixmap().width()) / 2 - 75);
//QPropertyAnimation * animation = new QPropertyAnimation(logo, "");
}

QPropertyAnimation applies to Qt Properties, but only objects that inherit from QObject have Qt properties, so if you want to use animations you can use QGraphicsObject and create your own item, or create a class that inherits the item you want and QObject.
class Pixmap: public QObject, public QGraphicsPixmapItem{
Q_OBJECT
Q_PROPERTY(qreal scale READ scale WRITE setScale)
Q_PROPERTY(qreal rotation READ rotation WRITE setRotation)
Q_PROPERTY(QPointF pos READ pos WRITE setPos)
public:
using QGraphicsPixmapItem::QGraphicsPixmapItem;
};
In the previous example, take advantage of the fact that QGraphicsItem, and therefore its derived classes, have the methods pos(), setPos(), scale(), setScale(), rotation() and setRotation(), so only use them in Q_PROPERTY.
In the next part I show an example:
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
QGraphicsView w;
QGraphicsScene scene(0, 0, 640, 480);
w.setScene(&scene);
w.show();
Pixmap* logo = new Pixmap(QPixmap(":/image.jpg"));
scene.addItem(logo);
QSequentialAnimationGroup group;
QPropertyAnimation animation_scale(logo, "scale");
animation_scale.setDuration(1000);
animation_scale.setStartValue(2.0);
animation_scale.setEndValue(0.1);
QPropertyAnimation animation_pos(logo, "pos");
animation_pos.setDuration(1000);
animation_pos.setStartValue(QPointF(0, 0));
animation_pos.setEndValue(QPointF(100, 100));
/**
* it must indicate the center of rotation,
* in this case it will be the center of the item
*/
logo->setTransformOriginPoint(logo->boundingRect().center());
QPropertyAnimation animation_rotate(logo, "rotation");
animation_rotate.setDuration(1000);
animation_rotate.setStartValue(0);
animation_rotate.setEndValue(360);
group.addAnimation(&animation_scale);
group.addAnimation(&animation_pos);
group.addAnimation(&animation_rotate);
group.start();
return a.exec();
}
#include "main.moc"
In the following link there is an example
Or you can use QVariantAnimation instead of QPropertyAnimation:
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
QGraphicsView w;
QGraphicsScene scene(0, 0, 640, 480);
w.setScene(&scene);
w.show();
QGraphicsPixmapItem* logo = new QGraphicsPixmapItem(QPixmap(":/image.jpg"));
scene.addItem(logo);
QSequentialAnimationGroup group;
QVariantAnimation animation_scale;
animation_scale.setDuration(1000);
animation_scale.setStartValue(2.0);
animation_scale.setEndValue(0.5);
QObject::connect(&animation_scale, &QVariantAnimation::valueChanged, [logo](const QVariant &value){
logo->setScale(value.toReal());
});
animation_scale.start();
QVariantAnimation animation_pos;
animation_pos.setDuration(1000);
animation_pos.setStartValue(QPointF(0, 0));
animation_pos.setEndValue(QPointF(100, 100));
QObject::connect(&animation_pos, &QVariantAnimation::valueChanged, [logo](const QVariant &value){
logo->setPos(value.toPointF());
});
/**
* it must indicate the center of rotation,
* in this case it will be the center of the item
*/
logo->setTransformOriginPoint(logo->boundingRect().center());
QVariantAnimation animation_rotate;
animation_rotate.setDuration(1000);
animation_rotate.setStartValue(0);
animation_rotate.setEndValue(360);
QObject::connect(&animation_rotate, &QVariantAnimation::valueChanged, [logo](const QVariant &value){
logo->setRotation(value.toReal());
});
group.addAnimation(&animation_scale);
group.addAnimation(&animation_pos);
group.addAnimation(&animation_rotate);
group.start();
return a.exec();
}
In the following link there is an example

Related

How to use the QGraphicsItem::setPos() function

I can't figure out how the setPos() function of the QGraphicsItem class works.
My Rect class has no parent, so its origin is relative to the scene.
I try to put the rectangle back at (0, 0) after it is moved with the mouse but it is placed in a different place depending on where I had moved it.
I suppose that means that the origin of the scene moves but what causes this change?
class Rect : public QGraphicsItem {
public:
Rect(): QGraphicsItem()
{
setFlag(ItemIsMovable);
}
void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) override
{
painter->drawRect(0, 0, 20, 20);
}
void mouseReleaseEvent(QGraphicsSceneMouseEvent *event) override
{
setPos(0, 0);
update();
QGraphicsItem::mouseReleaseEvent(event);
}
QRectF boundingRect() const
{
return QRectF(0, 0, 20, 20);
}
private:
};
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
QGraphicsScene scene;
QGraphicsView view(&scene);
Rect obj;
scene.addItem(&obj);
view.show();
return a.exec();
}
When you create a QGraphicsView you initially accept the default settings. A standard setting is, for example, that it is horizontally centered.
Another factor is that the default area size is probably up to the maximum size.
what you can do set a custom size for the scene. You do that with graphicsView->setSceneRect(0,0,300,300); (for example)
scene = new QGraphicsScene(this);
ui->graphicsView->setScene(scene);
ui->graphicsView->setRenderHint(QPainter::Antialiasing);
ui->graphicsView->setSceneRect(0,0, 300,300);
rectItem = new QGraphicsRectItem(0,0, 100, 100);
rectItem->setPen(QPen(Qt::darkMagenta, 2));
rectItem->setBrush(QGradient(QGradient::SaintPetersburg));
rectItem->setPos(190,10);
scene->addItem(rectItem);
So in summary: if you want to work with fixed values. maybe it is better to know the total size. (that was not clear from your code, that's why I gave this example)

Why QGraphicsView is not shown

I have the following codes in my Qt project with the following main:
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
Widget w;
w.show();
return a.exec();
}
The class Widget is a QWidget object with the following constructor:
Widget::Widget(QWidget *parent)
: QWidget(parent)
{
m_Scene = new QGraphicsScene(this);
QGraphicsLinearLayout* layout = new
QGraphicsLinearLayout(Qt::Orientation::Vertical);
for(int i = 0; i < 10; i++)
{
std::string name = "m_" + std::to_string(i);
GraphicsTextItem* item = new GraphicsTextItem(nullptr, QString(name.c_str()));
layout->addItem(item);
}
QGraphicsWidget* list = new QGraphicsWidget;
list->setPos(0,0);
list->setLayout(layout);
m_Scene->addItem(list);
QGraphicsView* view = new QGraphicsView(this);
view->setScene(m_Scene);
// Why one of these lines must be uncommented?
//m_Scene->setSceneRect(0, 0, 1920, 768);
//QVBoxLayout *ttopLayout = new QVBoxLayout;
//ttopLayout->addWidget(view);
//setLayout(ttopLayout);
}
GraphicsTextItem is just a QGraphicsWidget for displaying text:
class GraphicsTextItem : public QGraphicsWidget
{
public:
QString m_Name;
QColor m_Color;
public:
GraphicsTextItem(QGraphicsItem * parent = nullptr, const QString& name = QString());
void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) override;
{
Q_UNUSED(option)
Q_UNUSED(widget)
QFont font("Times", 10);
painter->setFont(font);
painter->setPen(m_Color);
painter->drawText(0, 0, m_Name);
}
};
My question is that why my scene is not shown. I must either define a SceneRect or define a layout on my widget?
I made an even shorter MCVE for demonstration:
#include <QtWidgets>
int main(int argc, char **argv)
{
qDebug() << "Qt Version:" << QT_VERSION_STR;
QApplication app(argc, argv);
QWidget qWinMain;
qWinMain.resize(320, 240);
QFrame qFrm(&qWinMain);
qFrm.setFrameStyle(QFrame::Box | QFrame::Raised);
qFrm.setLineWidth(0);
qFrm.setMidLineWidth(1);
qWinMain.show();
return app.exec();
}
compiled and started in cygwin64. This is how it looks:
There is a main window (with window manager decoration).
There is a child QFrame.
The child QFrame is "pressed" into the upper left corner.
How comes?
What QWidget does ensure: Child widgets are rendered (in front) when QWidget is rendered.
What QWidget is not (directly) responsible for: Layouting child widgets.
For this, a layout manager has to be plugged in:
#include <QtWidgets>
int main(int argc, char **argv)
{
qDebug() << "Qt Version:" << QT_VERSION_STR;
QApplication app(argc, argv);
QWidget qWinMain;
qWinMain.resize(320, 240);
QVBoxLayout qVBox(&qWinMain);
QFrame qFrm(&qWinMain);
qFrm.setFrameStyle(QFrame::Box | QFrame::Raised);
qFrm.setLineWidth(0);
qFrm.setMidLineWidth(1);
qVBox.addWidget(&qFrm);
qWinMain.show();
return app.exec();
}
compiled and started again in cygwin64. This is how it looks:
Now, the QFrame qFrm is filling the QWidget qWinMain nicely. Resize events received in qWinMain will be forwarded to the layout manager qVBox which will re-layout the children of qWinMain (i.e. qFrm) again.
I strongly believe OP's GraphicsView is just not visible because it has no minimal size requirement. (It's just to small to be visible.)
Hence, adding a layout manager ensures that the GraphicsView fills the parent widget client area. Resizing the contents of GraphicsView (by m_Scene->setSceneRect(0, 0, 1920, 768);) is yet another option to fix this, albeit the worse one.
Finally, the link to Qt Doc.: Layout Management.
Layout Management
The Qt layout system provides a simple and powerful way of automatically arranging child widgets within a widget to ensure that they make good use of the available space.
Introduction
Qt includes a set of layout management classes that are used to describe how widgets are laid out in an application's user interface. These layouts automatically position and resize widgets when the amount of space available for them changes, ensuring that they are consistently arranged and that the user interface as a whole remains usable.
All QWidget subclasses can use layouts to manage their children. The QWidget::setLayout() function applies a layout to a widget. When a layout is set on a widget in this way, it takes charge of the following tasks:
Positioning of child widgets
Sensible default sizes for windows
Sensible minimum sizes for windows
Resize handling
Automatic updates when contents change:
Font size, text or other contents of child widgets
Hiding or showing a child widget
Removal of child widgets

Hide area of QGraphicsItem that is out of boundary

I have a QGraphicsPixmap item in a QGraphicsScene. The item has flags set to ItemIsMovable, and ItemIsSelectable. How do I ensure that when the item is moved out of a certain boundary - it can be a QGraphicsScene or just a fixed frame size at fixed coordinates - the part becomes hidden?
Eg.
The left part of the basketball becomes hidden.
You have to use setClipPath().
In the following code I have created a class that inherits from QGraphicsPixmapItem (the same could do with other classes that inherit from QGraphicsItem) and I created the method setBoundaryPath() that receives a QPainterPath that indicates the visible area, for example in the code use:
QPainterPath path;
path.addRect(QRectF(100, 100, 400, 200));
That QPainterPath is a rectangle whose topleft is the point (100, 100) of the QGraphicsScene with size of 400 in width and 200 in height.
#include <QApplication>
#include <QGraphicsRectItem>
#include <QGraphicsView>
class GraphicsPixmapItem: public QGraphicsPixmapItem{
public:
GraphicsPixmapItem(const QPixmap & pixmap, QGraphicsItem *parent = 0):
QGraphicsPixmapItem(pixmap, parent)
{
setFlag(QGraphicsItem::ItemIsMovable, true);
setFlag(QGraphicsItem::ItemIsSelectable, true);
}
void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget){
if(!m_boundaryPath.isEmpty()){
QPainterPath path = mapFromScene(m_boundaryPath);
if(!path.isEmpty())
painter->setClipPath(path);
}
QGraphicsPixmapItem::paint(painter, option, widget);
}
QPainterPath boundaryPath() const{
return m_boundaryPath;
}
void setBoundaryPath(const QPainterPath &boundaryPath){
m_boundaryPath = boundaryPath;
update();
}
private:
QPainterPath m_boundaryPath;
};
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
QGraphicsView view;
QGraphicsScene scene(0, 0, 600, 400);
view.setScene(&scene);
view.setBackgroundBrush(QBrush(Qt::gray));
GraphicsPixmapItem *p_item = new GraphicsPixmapItem(QPixmap(":/ball.png"));
p_item->setPos(100, 100);
// Define the area that will be visible
QPainterPath path;
path.addRect(QRectF(100, 100, 400, 200));
p_item->setBoundaryPath(path);
scene.addItem(p_item);
// the item is added to visualize the intersection
QGraphicsPathItem *path_item = scene.addPath(path, QPen(Qt::black), QBrush(Qt::white));
path_item->setZValue(-1);
view.show();
return a.exec();
}
You can find the example code in this link.

How can I set the display range of a QGraphicItemGroup?

I have a QGraphicsItemGroup aggregating several child items, and I want to show only part of the group.(not the numbers of child items, area). Just like the image here.
I want to show the display area.
To do that, I have tried the override the QGraphicsItemGroup::boundingRect(). However, nothing have happened. And i find this in QT docs, maybe this is the reason why doesn't work.
The boundingRect() function of QGraphicsItemGroup returns the bounding rectangle of all items in the item group.
Also, I know I can change the size of QGraphicsView to make it work. However I put the View as CentralWidget, as I also need to display other object in the View, I can not change the size of the View.
How can I set the display range of a QGraphicItemGroup?
To perform this task we can overwrite shape() by returning a QPainterPath that defines the visible region, so that it spreads to its children we enable the flag ItemClipsChildrenToShape:
class GraphicsItemGroup: public QGraphicsItemGroup{
public:
GraphicsItemGroup(QGraphicsItem * parent = 0):QGraphicsItemGroup(parent){
setFlag(QGraphicsItem::ItemClipsChildrenToShape, true);
}
QPainterPath shape() const
{
if(mShape.isEmpty())
return QGraphicsItemGroup::shape();
return mShape;
}
void setShape(const QPainterPath &shape){
mShape = shape;
update();
}
private:
QPainterPath mShape;
};
Example:
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
QWidget w;
w.setLayout(new QVBoxLayout);
QGraphicsView view;
QPushButton button("click me");
w.layout()->addWidget(&view);
w.layout()->addWidget(&button);
view.setScene(new QGraphicsScene);
GraphicsItemGroup group;
view.scene()->addItem(&group);
auto ellipse = new QGraphicsEllipseItem(QRectF(0, 0, 100, 100));
ellipse->setBrush(Qt::red);
auto rect = new QGraphicsRectItem(QRect(150, 150, 100, 100));
rect->setBrush(Qt::blue);
group.addToGroup(ellipse);
group.addToGroup(rect);
QObject::connect(&button, &QPushButton::clicked, [&group](){
QPainterPath shape;
if(group.shape().boundingRect() == group.boundingRect()){
shape.addRect(0, 50, 250, 150);
}
group.setShape(shape);
});
w.show();
return a.exec();
}
Output:
The complete example can be found in the following link.

QtGraphicsView size in QTGraphicsScene

I'm having trouble getting my QtGraphicsView to take up 100% of the window I'm working in. Is there a way to set the view size to take up 100% of the graphics window that is created?
QApplication app(argc, argv);
QGraphicsScene * scene = new QGraphicsScene();
int counter = 1000;
scene->setSceneRect(0, 0, counter, counter);//scene size
QGraphicsEllipseItem * item = new QGraphicsEllipseItem(0, scene);
item->setRect(0,0, 1000.0, 1000.0);
QGraphicsView view(scene);
view.setRect(100, 100);
view.setRenderHints(QPainter::Antialiasing);
view.show();
QGraphicsView should have a method(slot) called showFullScreen since it inherits QWidget
, which should do what you want.