Impossible to get the scene position of an Item in a QGraphicsGridLayout - c++

i've got a problem for my school projet... I just can't get the scene position of any object in a QGraphicsGridLayout, it always give me (0,0). Maybe i subclass badly QGraphicsWidget ?
This is a example of how i use it :
main:
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
QGraphicsScene *scene = new QGraphicsScene();
myCustomWidget *a = new myCustomWidget("a");
myCustomWidget *b = new myCustomWidget("b");
myCustomWidget *c = new myCustomWidget("c");
QGraphicsGridLayout *layout = new QGraphicsGridLayout();
layout->addItem(a,0,0,1,2, Qt::AlignCenter);
layout->addItem(b,1,0,1,1);
layout->addItem(c,1,1,1,1);
QGraphicsWidget *w = new QGraphicsWidget();
w->setLayout(layout);
scene->addItem(w);
QWidget* Window = new QWidget();
Window->setMinimumSize(640,380);
QVBoxLayout* layoutWindow = new QVBoxLayout();
Window->setLayout(layoutWindow);
QGraphicsView *view = new QGraphicsView(Window);
view->setScene(scene);
view->show();
layoutWindow->addWidget(view);
Window->show();
////////////////////////////////
// Always return QPointF(0,0) //
////////////////////////////////
qDebug() << c->scenePos();
return app.exec();
}
myCustomWidget:
class myCustomWidget : public QGraphicsWidget
{
private:
QString caption;
public:
myCustomWidget(QString caption, QGraphicsWidget* parent = 0) : QGraphicsWidget(parent)
{
this->caption = caption;
}
QSizeF sizeHint(Qt::SizeHint which, const QSizeF &constraint) const
{
Q_UNUSED(which);
Q_UNUSED(constraint);
return QSizeF(100,100);
}
QRectF boundingRect() const
{
return QRectF(QPointF(0,0), QSizeF(100,100));
}
void paint(QPainter *painter, const QStyleOptionGraphicsItem *, QWidget * = 0) Q_DECL_OVERRIDE
{
QFont font;
font.setPixelSize(0.75 * qMin(boundingRect().width(), boundingRect().height()));
painter->fillRect(boundingRect(), Qt::black);
painter->setFont(font);
painter->setPen(QPen(Qt::white));
painter->drawText(boundingRect(), Qt::AlignCenter, caption);
}
};

Related

QFrame is not adjusting its size as per the size adjustment of parent widget

I created a widget class. This class will be maintaining child_1 of type QFrame(which is of type QWidget) and also a child_2 of type QGridLayout . child_2 will be adding child_1 to itself. The child_1 will be having a child of type QGridLayout and ultimately QGridLayout will be having some push buttons.
*I am unable to adjust the size of QFrame child(child_1)as per the adjustment of parent widget.
Also, I am trying to set the size of QFrame child that of parent. Even this isn't happening.
I have tried using methods setFrameRect(), setGeometry() and resize().
I have implemented evenhandler resizeEvent(QResizeEvent event). But while application is running, I am trying to resize the widget and I see that log isn't printing.
main.cpp
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
Widget w;
w.show();
return a.exec();
}
widget.h
namespace Ui {
class Widget;
}
class Widget : public QWidget
{
Q_OBJECT
public:
explicit Widget(QWidget *parent = 0);
~Widget();
private:
Ui::Widget *ui;
QGridLayout* p_mPanelLayout; // Instance of QGridLayout.
FrameDemo* framePtr;
};
widget.cpp
Widget::Widget(QWidget *parent) :
QWidget(parent),
ui(new Ui::Widget)
{
ui->setupUi(this);
p_mPanelLayout = new QGridLayout(this);
framePtr = new FrameDemo(this);
framePtr->createPushButtonUtility();
p_mPanelLayout->addWidget(framePtr, 0, 0, Qt::AlignCenter);
}
Widget::~Widget()
{
delete ui;
}
framedemo.h
#define LOG(message){\
qDebug()<< __LINE__<<__FUNCTION__<<message<<"\n";\
}
class FrameDemo : public QFrame
{
Q_OBJECT
public:
FrameDemo(QWidget *parent = 0);
void createPushButtonUtility();
private:
QGridLayout* m_pLayout;
protected:
void resizeEvent(QResizeEvent * event);
};
framedemo.cpp
FrameDemo::FrameDemo(QWidget *parent):QFrame(parent)
{
m_pLayout = new QGridLayout(this);
// m_pLayout->setRowStretch(0, parent->width());
QRect defaultRect = rect();
QSize currentSize = size();
LOG(currentSize.height())
LOG(currentSize.width())
int xP1 = 0;
int yP1 = 0;
int xP2 = 0;
int yP2 = 0;
defaultRect.getCoords(&xP1, &yP1, &xP2, &yP2);
LOG(xP1)
LOG(yP1)
LOG(xP2)
LOG(yP2)
LOG(parent->width())
LOG(defaultRect.height())
// QRect newRect = QRect(xP1,yP1,parent->width(),defaultRect.height());
// QRect newRect = QRect(0, 0, 0, defaultRect.height());
// setFrameRect(newRect);
resize(QSize(400, currentSize.height()));
setFrameShape(QFrame::Box);
// setGeometry(0, 0, parent->width(), defaultRect.height());
}
void FrameDemo::createPushButtonUtility()
{
QPushButton *readButton = new QPushButton("Read", this);
m_pLayout->addWidget(readButton, 0, 0, Qt::AlignCenter);
QPushButton *writeButton = new QPushButton("Write", this);
m_pLayout->addWidget(writeButton, 0, 1, Qt::AlignCenter);
QPushButton *updateButton = new QPushButton("Update", this);
m_pLayout->addWidget(updateButton, 0, 2, Qt::AlignCenter);
}
void FrameDemo::resizeEvent(QResizeEvent *event)
{
LOG("resize")
}
I managed to fix the problem.
I missed to implement the event handler resizeEvent(QResizeEvent *event) in parent widget.
I implemented the same and called the resizeEvent of FrameDemo. Inside this event handler, I called setGeometry().

QGraphicsItem::pos returns 0 when in layout

I have the following Qt code fragment in my widget.cpp:
Widget::Widget(QWidget *parent)
: QWidget(parent)
{
m_Scene = new QGraphicsScene(this);
m_Scene->setSceneRect(0, 0, 1024, 768);
GraphicsTextItem* m_1 = new GraphicsTextItem(nullptr, QString("l_1"));
GraphicsTextItem* m_2 = new GraphicsTextItem(nullptr, QString("l_2"));
QGraphicsLinearLayout* layout = new QGraphicsLinearLayout;
layout->addItem(m_1);
layout->addItem(m_2);
QGraphicsWidget* list = new QGraphicsWidget;
list->setLayout(layout);
m_Scene->addItem(list);
qDebug() << m_2->x() << " " << m_2->y(); // Prints 0,0 Why?
QGraphicsView* view = new QGraphicsView(this);
view->setScene(m_Scene);
}
GraphicsTextItem is a derived class of QGraphicsWidget :
class GraphicsTextItem : public QGraphicsWidget
{
private:
QString m_Name;
public:
GraphicsTextItem(QGraphicsItem * parent = nullptr, const QString& name = QString());
void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) override
{
QFont font("Times", 12);
painter->setFont(font);
painter->drawText(0, 0, m_Name);
}
};
I also give my short main located in main.cpp:
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
Widget w;
w.show();
return a.exec();
}
My question is that why the qDebug line prinnts me 0,0 as certainly the position of the widget is non-zero. If I don't put the widget in a layout and I call setPos() qDebug prints the correct value set previously.
The initial position of all QGraphicsItem is (0, 0) and that includes m_2, and m_2 will change its position when the QGraphicsLinearLayout is applied that will be an instant after the synchronous task is finished and the eventloop starts working, this can be observed using QTimer::singleShot(0, ...):
Widget::Widget(QWidget *parent)
: QWidget(parent)
{
m_Scene = new QGraphicsScene(this);
QGraphicsView* view = new QGraphicsView(this);
view->setScene(m_Scene);
m_Scene->setSceneRect(0, 0, 1024, 768);
GraphicsTextItem *m_1 = new GraphicsTextItem(nullptr, QString("l_1"));
GraphicsTextItem *m_2 = new GraphicsTextItem(nullptr, QString("l_2"));
QGraphicsLinearLayout* layout = new QGraphicsLinearLayout;
layout->addItem(m_1);
layout->addItem(m_2);
QGraphicsWidget* list = new QGraphicsWidget;
list->setLayout(layout);
m_Scene->addItem(list);
qDebug() << "synchronous" << m_2->pos() << m_2->mapToScene(QPointF{});
QTimer::singleShot(0, m_2, [m_2](){
qDebug() << "asynchronous"<< m_2->pos() << m_2->mapToScene(QPointF{});
});
}
Output:
synchronous QPointF(0,0) QPointF(0,0)
asynchronous QPointF(62,6) QPointF(62,6)

QPropertyAnimation of a QGraphicsTextItem with a frame make the text shaky

I am animating a QGraphicsTextItem that I have added a frame around. During the animation the text seems to shake slightly inside the frame, which is very annoying.
An example code:
class MovingFrameText : public QGraphicsTextItem
{
Q_OBJECT;
public:
MovingFrameText( ) : QGraphicsTextItem(0)
{
setPlainText ( "human ");
QFont f = font();
f.setPixelSize(40);
setFont(f);
setFlags(QGraphicsItem::ItemIsMovable);
}
QRectF boundingRect() const
{
return QGraphicsTextItem::boundingRect().adjusted(-2,-2,+2,+2);
}
void paint(QPainter *painter,const QStyleOptionGraphicsItem *option,QWidget *widget)
{
QGraphicsTextItem::paint(painter,option,widget);
painter->setPen(Qt::black);
painter->drawRect(boundingRect());
}
};
int main(int argc, char *argv[])
{
QApplication app(argc,argv);
MovingFrameText t;
t.setPos(640,680);
QGraphicsScene scene;
scene.addItem(&t);
QGraphicsView view(&scene);
view.resize(640, 680);
view.show();
auto moveAnimation = new QPropertyAnimation( &t, "pos" );
moveAnimation->setDuration( 10000 );
moveAnimation->setStartValue( QPointF(640, 680) );
moveAnimation->setEndValue( QPointF(0, 0) );
moveAnimation->setEasingCurve( QEasingCurve::Linear );
moveAnimation->start(QAbstractAnimation::DeleteWhenStopped);
return app.exec();
}
Is there any way to smooth the animation?
Solution
You can substantially improve the animation by:
Using QVariantAnimation instead of QPropertyAnimation and calling QGraphicsItem::update on each iteration
Buffering the painting using an additional QPainter with a QPixmap as a canvas for all painting operations and then painting the canvas using the painter passed to the paint method
Note: The QGraphicsTextItem will still shake a bit, but at least it will behave as one object instead of several independent ones.
Example
Here is an example I have prepared for you of how your code could be changed in order to implement the proposed solution:
class MovingFrameText : public QGraphicsTextItem
{
public:
MovingFrameText(const QString &text, QGraphicsItem *parent = nullptr)
: QGraphicsTextItem(parent)
{
QFont f(font());
f.setPixelSize(40);
setFont(f);
setPlainText(text);
}
void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
{
painter->setClipping(true);
painter->setClipRect(option->rect);
painter->setRenderHint(QPainter::SmoothPixmapTransform);
QPixmap canvas(option->rect.size());
QPainter canvasPainter;
canvas.fill(Qt::transparent);
canvasPainter.begin(&canvas);
canvasPainter.setFont(font());
canvasPainter.drawRect(option->rect.adjusted(0, 0, -1, -1));
canvasPainter.drawText(option->rect, toPlainText());
painter->drawPixmap(0, 0, canvas);
}
};
int main(int argc, char *argv[])
{
QApplication app(argc,argv);
QGraphicsView view;
auto *t = new MovingFrameText("human");
view.setScene(new QGraphicsScene(&view));
view.setAlignment(Qt::AlignLeft | Qt::AlignTop);
view.setSceneRect(0, 0, 640, 680);
view.scene()->addItem(t);
view.show();
auto *moveAnimation = new QVariantAnimation();
moveAnimation->setDuration(10000);
moveAnimation->setStartValue(QPointF(640, 680));
moveAnimation->setEndValue(QPointF(0, 0));
moveAnimation->start(QAbstractAnimation::DeleteWhenStopped);
QObject::connect(moveAnimation, &QVariantAnimation::valueChanged, [t](const QVariant &value){
t->setPos(value.toPointF());
t->update();
});
return app.exec();
}
#scopchanov answer is a very good one. I added here a solution I tested as well, and seems to work. I will need further time to inspect which one I think is better, but I will leave it here for others to try, if you end with a similar issue.
class MovingFrameText : public QGraphicsTextItem
{
Q_PROPERTY( QPointF pos READ pos WRITE setPosition )
public:
void setPosition(QPointF pos)
{
setPos(floor(pos.x()+0.5),floor(pos.y()+.5) );
}
};

How to resize QGraphicsView properly?

I try to create a simple application with QGraphicsView. The application can load graphical files with different ratios in the view. I would like to implement the following feature: on the application window resize, the content of the view should be also resized and centered. I do not know this part of the Qt well, so I can resize the content, but I cannot center it. How can I fix it?
MainWindow.h snippet:
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
MainWindow(QWidget *parent = 0);
~MainWindow();
void resizeEvent(QResizeEvent *event) override;
public slots:
void onButtonClicked();
private:
bool m_flag = false;
QGraphicsPixmapItem *m_item = nullptr;
QGraphicsView *m_view = nullptr;
QPixmap m_pixmap;
};
MainWindow.cpp:
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
{
auto central = new QWidget(this);
setCentralWidget(central);
// layouts
auto mainLayout = new QVBoxLayout;
mainLayout->setAlignment(Qt::AlignTop);
central->setLayout(mainLayout);
// top layout
auto topLayout = new QHBoxLayout;
topLayout->setAlignment(Qt::AlignLeft);
mainLayout->addLayout(topLayout);
auto btn = new QPushButton(this);
btn->setText("Test");
connect(btn, &QPushButton::clicked, this, &MainWindow::onButtonClicked);
topLayout->addWidget(btn);
m_view = new QGraphicsView;
mainLayout->addWidget(m_view);
auto scene = new QGraphicsScene;
m_view->setScene(scene);
m_view->setMinimumSize(800, 600);
m_view->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
m_view->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
QString name = ":/pic1.jpg";
m_pixmap = QPixmap{ name }.scaled(800, 600, Qt::KeepAspectRatio);
m_item = scene->addPixmap(m_pixmap);
m_view->viewport()->resize(m_pixmap.size());
}
void MainWindow::onButtonClicked()
{
m_flag = !m_flag;
QString name = m_flag ? ":/pic2.png" : ":/pic1.jpg";
m_pixmap = QPixmap{ name }.scaled(m_view->size(), Qt::KeepAspectRatio);
m_item->setPixmap(m_pixmap);
m_view->fitInView(m_item, Qt::KeepAspectRatio);
}
void MainWindow::resizeEvent(QResizeEvent *event)
{
QMainWindow::resizeEvent(event);
m_view->fitInView(m_item, Qt::KeepAspectRatio);
}
Test result for the pic2.png:
When an image is scaled, it is done with respect to the top left, so it will always look up if the height is smaller than the prefix or to the right if the width is smaller.
The solution is to repaint the scaled QPixmap in the middle of the final QPixmap.
const QSize pixmap_size{800, 600};
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
{
auto central = new QWidget(this);
setCentralWidget(central);
// layouts
auto mainLayout = new QVBoxLayout;
mainLayout->setAlignment(Qt::AlignTop);
central->setLayout(mainLayout);
// top layout
auto topLayout = new QHBoxLayout;
topLayout->setAlignment(Qt::AlignLeft);
mainLayout->addLayout(topLayout);
auto btn = new QPushButton(this);
btn->setText("Test");
connect(btn, &QPushButton::clicked, this, &MainWindow::onButtonClicked);
topLayout->addWidget(btn);
m_view = new QGraphicsView;
mainLayout->addWidget(m_view);
auto scene = new QGraphicsScene;
m_view->setScene(scene);
m_view->setMinimumSize(pixmap_size);
m_view->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
m_view->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
m_pixmap = QPixmap(pixmap_size);
m_pixmap.fill(Qt::transparent);
m_item = new QGraphicsPixmapItem;
m_view->scene()->addItem(m_item);
show();
onButtonClicked();
}
QPixmap MainWindow::createPixmap(const QString & filename, const QSize & size) const{
QPixmap tmp = QPixmap{ filename }.scaled(size, Qt::KeepAspectRatio);
QPixmap pixmap(size);
pixmap.fill(Qt::transparent);
QPainter p(&pixmap);
QPoint point(QRect({}, size).center()-tmp.rect().center());
p.drawPixmap(point, tmp);
p.end();
return pixmap;
}
void MainWindow::onButtonClicked()
{
QString name = m_flag ? ":/pic2.png" : ":/pic1.jpg";
m_item->setPixmap(createPixmap(name, pixmap_size));
m_flag = !m_flag;
m_view->fitInView(m_item, Qt::KeepAspectRatio);
}
void MainWindow::resizeEvent(QResizeEvent *event)
{
QMainWindow::resizeEvent(event);
m_view->fitInView(m_item, Qt::KeepAspectRatio);
}

QMainWindow does not show Qwidgets background

I have a problem regarding a QWidget (custom made) inside a QMainWindow (custom made). My problem is that when I add my widget to my window as its central widget using setCentralWidget() method, it won't show the widgets background. It's important to show the background correctly.
Here is my MyWindow.cpp code:
#include "MyMainWindow.h"
MyMainWindow::MyMainWindow(QWidget * parent, Qt::WindowFlags flag) :
QMainWindow(parent, flag)
{
this->setFixedSize(1120, 630);
menu = new MyMenu(this);
// setting = new MySetting();
// tutorial = new MyTutorial();
// game = new MyGame();
this->setCentralWidget(menu);
this->show();
}
MyMainWindow::~MyMainWindow()
{
}
My MyMenu.cpp code:
#include "MyMenu.h"
MyMenu::MyMenu(QWidget *parent, Qt::WindowFlags f) :
QWidget(parent, f)
{
this->resize(1120, 630);
this->set_background();
this->construct_buttons();
this->construct_menu();
}
MyMenu::~MyMenu()
{
delete start;
delete setting;
delete tutorial;
delete exit;
delete buttons;
delete logo;
delete menu;
}
void MyMenu::construct_menu()
{
menu = new QVBoxLayout(this);
logo = new QLabel(this);
QPixmap *pixmap = new QPixmap("/home/kahrabian/ClionProjects/Shooter-AP93UT/Contents/logo.png");
logo->setPixmap(*pixmap);
logo->setAlignment(Qt::AlignHCenter);
menu->addWidget(logo);
menu->addLayout(buttons);
delete pixmap;
}
void MyMenu::construct_buttons()
{
buttons = new QHBoxLayout();
start = new QPushButton("Start", this);
buttons->addWidget(start);
setting = new QPushButton("Setting", this);
buttons->addWidget(setting);
tutorial = new QPushButton("Tutorial", this);
buttons->addWidget(tutorial);
exit = new QPushButton("Exit", this);
buttons->addWidget(exit);
}
void MyMenu::set_background()
{
QPalette *palette = new QPalette();
palette->setBrush(this->backgroundRole(),QBrush(QImage("/home/kahrabian/ClionProjects/Shooter-AP93UT/Contents/background_menu.jpg")));
this->setPalette(*palette);
delete palette;
}
My main.cpp code:
#include <QApplication>
#include "MyMenu.h"
#include "MyMainWindow.h"
int main(int argc, char **argv)
{
QApplication app (argc, argv);
MyMainWindow *mainwin = new MyMainWindow();
// MyMenu *MyMenu = new MyMenu();
// MyMenu->show();
return app.exec();
}
Can anyone help me with this problem??
Check this answer.
I would recommend you to use Qt style sheets.
You would need to call something like this:
setStyleSheet("image: url(path/to/background/image.png);");
on your widget.
Also, you might need to implement paintEvent() for widget to accept style sheets.
I'm usually doing it like this:
void MyWidget::paintEvent(QPaintEvent *pe)
{
QStyleOption opt;
opt.init(this);
QPainter p(this);
style()->drawPrimitive(QStyle::PE_Widget, &opt, &p, this);
QWidget::paintEvent(pe);
}