I am working on Falling ball application,
About the application: the balls keep on falling at regular intervals, and there is a basket that catches the balls. If caught then we earn a point if not then lose one life.
For this application, I am using qt and CPP.
I have created one class Sprite with specifications regarding the initial position of the ball(x and y) and speed (dx and dy).
I am using one timer for the speed of the ball, but I need to another timer for ball creation at regular intervals. Could anyone help me with how to implement the ball creation at regular intervals?
sprite.h
--------
#ifndef SPRITE_H
#define SPRITE_H
#include <QTimer>
#include <QPainter>
#include <QWidget>
/**
* Header class for Sprite
*/
class Sprite : public QTimer
{
public:
Sprite(QWidget *parent);
//draw() to draw a sprite
void draw(QPainter &painter);
protected:
//This timerEvent will be called after certain time prescribed.
virtual void timerEvent(QTimerEvent *e) override;
int x;//position of sprite in x-direction.
int y;//position of sprite in y-direction.
int dx;//difference in x-direction position.
int dy;//difference in y-direction position.
int x1;//position of the basket in x-direction
int y1;//position of the basket in y-direction.
QWidget *parent;//parent class for all widgets.
};
#endif // SPRITE_H
sprite.cpp
-----------
#include "sprite.h"
#include <QDebug>
Sprite::Sprite(QWidget *parent):parent(parent)
{
QRect rct = parent->rect();
x = rand() % rct.width();//randomly initialize the x-position for the sprite.
y=rct.height()*0.05;//start position for the sprite is about 5% after the top of the menu bar.
dx = rand() % 10;//the speed is randomly set in x-direction.
// dy = rand() % 10;//the speed is randomly set in y-direction.
dy = 4;
x1=rct.width()/2;
y1 = rct.height()-80;
start(10);
}
void Sprite::draw(QPainter &painter)
{
qDebug() <<"Sprite::draw() called";
painter.drawEllipse(x, y, 15, 15);//ball
painter.drawRect( x1, y1, 80, 30);//basket
}
void Sprite::timerEvent(QTimerEvent *)
{
qDebug("timerEvent called");
QRect rct = parent->rect();
if ( x > rct.width() || x < 0)
dx *= -1;
if ( y > rct.height() || y < 0){}
x += dx;
y += dy;
parent->update();
}
mainwindow.cpp
-----------------
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QDebug>
#include <QKeyEvent>
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::MainWindow)
{
ui->setupUi(this);
qDebug() <<"mainWindow constructor";
QTimer timer;
QObject::connect(&timer, &QTimer::timeout, this,&MainWindow::onTimer);
timer.start(1000);
}
MainWindow::~MainWindow()
{
qDebug() <<"mainWindow destructor";
delete ui;
}
void MainWindow::onTimer()
{
std::cout << "Tick!-----------------------------------------------------------------------------" << std::endl;
}
void MainWindow::paintEvent(QPaintEvent *)
{
qDebug() <<"painEvent() called";
QPainter painter(this);
painter.fillRect(rect(), QBrush(QColor(Qt::white)));
painter.setPen(Qt::black);
painter.setBrush(QBrush(QColor(Qt::darkBlue)));
emit draw(painter);
}
MainWindow.h
-----------
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include "sprite.h"
#include <balls.h>
QT_BEGIN_NAMESPACE
namespace Ui { class MainWindow; }
QT_END_NAMESPACE
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
MainWindow(QWidget *parent = nullptr);
~MainWindow();
signals:
void draw(QPainter &painter);
void move(int nDirection);
protected:
//This event handler captures all the paint events request to repaint all or part of the widget.
void paintEvent(QPaintEvent* event)override;
public slots:
void onTimer();
private slots:
void on_actionStart_triggered();
void on_actionStop_triggered();
private:
Ui::MainWindow *ui;
Sprite *sprite;
};
#endif // MAINWINDOW_H
How can I create balls at regular intervals, any sudo code will help.
Or any better way to implement this is also welcome.
Calling QObject::startTimer twice, will run two timers simultaneously:
int timerId1 = startTimer(2000);
int timerId2 = startTimer(10);
void MyObject::timerEvent(QTimerEvent *event)
{
if (event->timerId() == timerId1)
; // 2000ms timer is fired
else if (event->timerId() == timerId2)
; // 10ms timer is fired
}
Also the Qt documentation itself contains a clear example of this use case.
An alternative is to use two QTimer objects, and connect a slot to the timeout signal of each QTimer object.
Related
I'm trying to create a canvas to draw on with the mouse similar to most digital painting applications that I can also zoom in on (zoom in on the drawn image)
So far I've created a class that uses QWidget and added it to the ui then use the mouse events and QPaintEvent to draw on this widget which works. However the problem I'm not sure how do I zoom on this as well? I tried placing the QWidget inside of a scrollable area but it stops it from registering click events. I also tried extending from QGraphicsViewer instead of QWidget but this stops me from being able to paint as well.
//Class definition
PaintArea::PaintArea(QWidget *parent) : QWidget(parent)
{
this->setMouseTracking(true);
}
I'm mostly looking for a recommendation of how to scrolling and drawing with a mouse on the same widget (Possibly with scroll bar but just wheel scrolling for sure)
Thanks
If you follow the QWidget way, you may want to look carefully to the scribble example that is included with Qt docs. In this example, the drawing is made off-screen on a QImage object, which is then painted by the widget. The problem is to zoom the image.
I prefer your second way: QGraphicsView has a scale() function among many other excellent features. You may do something similar to the scribble example: draw off-screen on a QPixmap image which is set (every time you change the image) into a QGraphicsPixmapItem which belongs to the QGraphicsScene. I've implemented this crude example, borrowing some elements from the scribble example. Use the mouse wheel to zoom the image (it screws the scrolling a bit, sorry).
test.pro
QT += core gui widgets
CONFIG += c++11
DEFINES += QT_DEPRECATED_WARNINGS
SOURCES += \
drawablescene.cpp \
main.cpp \
mainwindow.cpp
HEADERS += \
drawablescene.h \
mainwindow.h
main.cpp
#include "mainwindow.h"
#include <QApplication>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
MainWindow w;
w.show();
return a.exec();
}
mainwindow.h
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include "drawablescene.h"
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = nullptr);
void wheelEvent(QWheelEvent *event) override;
private:
QGraphicsView *m_view;
DrawableScene *m_scene;
};
#endif // MAINWINDOW_H
mainwindow.cpp
#include <QGraphicsView>
#include <QWheelEvent>
#include "mainwindow.h"
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent),
m_view(new QGraphicsView(this)),
m_scene(new DrawableScene(this))
{
setCentralWidget(m_view);
m_scene->setSceneRect(0,0,640,480);
m_view->setScene(m_scene);
}
void MainWindow::wheelEvent(QWheelEvent *event)
{
qreal delta = 1 + (event->delta() > 0 ? 0.1 : -0.1);
m_view->scale(delta, delta);
event->accept();
}
drawablescene.h
#ifndef DRAWABLESCENE_H
#define DRAWABLESCENE_H
#include <QObject>
#include <QGraphicsScene>
#include <QGraphicsPixmapItem>
class DrawableScene : public QGraphicsScene
{
public:
explicit DrawableScene(QObject *parent = nullptr);
private:
void mouseMoveEvent(QGraphicsSceneMouseEvent *mouseEvent) override;
void mousePressEvent(QGraphicsSceneMouseEvent *mouseEvent) override;
void mouseReleaseEvent(QGraphicsSceneMouseEvent *mouseEvent) override;
void drawLineTo(const QPointF &endPoint);
bool m_modified;
bool m_scribbling;
int m_penWidth;
QColor m_penColor;
QPointF m_lastPoint;
QPixmap *m_image;
QGraphicsPixmapItem *m_item;
};
#endif // DRAWABLESCENE_H
drawablescene.cpp
#include <QGraphicsSceneMouseEvent>
#include <QPainter>
#include "drawablescene.h"
DrawableScene::DrawableScene(QObject *parent)
: QGraphicsScene(parent),
m_modified(false),
m_scribbling(false),
m_penWidth(3),
m_penColor(Qt::blue)
{
m_image = new QPixmap(640, 480);
m_image->fill(Qt::white);
m_item = addPixmap(*m_image);
}
void DrawableScene::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
{
if ((event->buttons() & Qt::LeftButton) && m_scribbling) {
drawLineTo(event->scenePos());
event->accept();
}
else QGraphicsScene::mouseMoveEvent(event);
}
void DrawableScene::mousePressEvent(QGraphicsSceneMouseEvent *event)
{
if (event->button() == Qt::LeftButton) {
m_lastPoint = event->scenePos();
m_scribbling = true;
event->accept();
}
else QGraphicsScene::mousePressEvent(event);
}
void DrawableScene::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
{
if (event->button() == Qt::LeftButton && m_scribbling) {
drawLineTo(event->scenePos());
m_scribbling = false;
event->accept();
}
else QGraphicsScene::mouseReleaseEvent(event);
}
void DrawableScene::drawLineTo(const QPointF &endPoint)
{
QPainter painter(m_image);
painter.setPen(QPen(m_penColor, m_penWidth, Qt::SolidLine, Qt::RoundCap,Qt::RoundJoin));
painter.drawLine(m_lastPoint, endPoint);
m_modified = true;
m_lastPoint = endPoint;
m_item->setPixmap(*m_image);
}
I'm trying to create a simple frame in Qt with a tick and some text. I made two new label implementations because I wanted the labels to dynamically fill all the available space but when I resize the window the sizes are off, as shown by the qDebug output, which represents the size of the image label:
Resized: 244 , 244 <-- Window first created
Resized: 305 , 305 <-- Window maximized
Resized: 135 , 135 <-- Window restored to original size
As you can see, when the window is restored to its original size the image is not. The last size should be 244, 244.
The code which describes the behaviour of the two widgets is the following:
"widgets.h":
/*
* This file includes many custom widgets.
*/
#ifndef APOCRYPHA_WIDGETS
#define APOCRYPHA_WIDGETS
#include <QWidget>
#include <QLabel>
#include <QTimer>
#include <QPixmap>
#include <QResizeEvent>
#include <QPaintEvent>
class AutoTextLabel : public QLabel {
Q_OBJECT
public:
explicit AutoTextLabel(QWidget* parent);
AutoTextLabel(QWidget* parent, QString text);
protected:
void resizeEvent(QResizeEvent* event) override;
private:
QTimer* resizeTimer;
private slots:
void onResizeEnd();
};
class AutoImageLabel : public QLabel {
Q_OBJECT
public:
explicit AutoImageLabel(QWidget* parent);
AutoImageLabel(QWidget* parent, const QPixmap& pixmap);
void setFillOrientation(int orientation);
QSize sizeHint() const override;
public slots:
void setPixmap(const QPixmap &newPix);
void resizeEvent(QResizeEvent* event) override;
protected:
// void paintEvent(QPaintEvent* event) override;
private:
int fillOrientation;
int widthForHeight(int h) const;
int heightForWidth(int w) const override;
QPixmap scaledPixmap() const;
QPixmap labelPixmap;
};
#endif //APOCRYPHA_WIDGETS
"widgets.cpp":
/*
* This file includes many custom widgets.
*/
#include "widgets.h"
#include <QPainter>
#include <QDebug>
AutoTextLabel::AutoTextLabel(QWidget *parent, QString text) : QLabel(text, parent){
// Enable antialiasing
QFont aaFont(font());
aaFont.setStyleStrategy(QFont::PreferAntialias);
setFont(aaFont);
// This timer is used to fire a slot when a window is resized
resizeTimer = new QTimer();
resizeTimer->setSingleShot(true);
connect(resizeTimer, SIGNAL(timeout()), SLOT(onResizeEnd()));
}
AutoTextLabel::AutoTextLabel(QWidget *parent) : AutoTextLabel(parent, "") {}
void AutoTextLabel::resizeEvent(QResizeEvent *event) {
QWidget::resizeEvent(event);
// Only fire when 25ms have passed since the last resize.
resizeTimer->start(25);
}
void AutoTextLabel::onResizeEnd() {
QFont updatedFont(font());
// Resize Text
if (!text().isEmpty()){
int fontSize = 1;
updatedFont.setPixelSize(fontSize);
QRect boundingRectangle;
// Update bounding rectangle
if (wordWrap())
boundingRectangle = QFontMetrics(updatedFont).boundingRect(contentsRect(), Qt::TextWordWrap, text());
else
boundingRectangle = QFontMetrics(updatedFont).boundingRect(text());
while (boundingRectangle.height() <= contentsRect().height()) {
fontSize++;
updatedFont.setPixelSize(fontSize);
// Update bounding rectangle
if (wordWrap())
boundingRectangle = QFontMetrics(updatedFont).boundingRect(contentsRect(), Qt::TextWordWrap, text());
else
boundingRectangle = QFontMetrics(updatedFont).boundingRect(text());
}
updatedFont.setPixelSize(fontSize - 1);
setFont(updatedFont);
}
}
/* Auto Image Label */
AutoImageLabel::AutoImageLabel(QWidget *parent, const QPixmap &pixmap) : QLabel(parent) {
setMinimumSize(1, 1);
setScaledContents(false);
setPixmap(pixmap);
}
AutoImageLabel::AutoImageLabel(QWidget *parent) : QLabel(parent) {
setScaledContents(false);
}
void AutoImageLabel::resizeEvent(QResizeEvent *event) {
QWidget::resizeEvent(event);
if(!labelPixmap.isNull())
QLabel::setPixmap(scaledPixmap());
qDebug() << "Resized: " << scaledPixmap().width() << ", " << scaledPixmap().height();
}
int AutoImageLabel::widthForHeight(int h) const {
return labelPixmap.isNull() ? width() : (labelPixmap.width() * h) / labelPixmap.height();
}
int AutoImageLabel::heightForWidth(int w) const {
return labelPixmap.isNull() ? height() : (labelPixmap.height() * w) / labelPixmap.width();
}
void AutoImageLabel::setFillOrientation(int orientation) {
this->fillOrientation = orientation;
}
QSize AutoImageLabel::sizeHint() const {
if (fillOrientation == Qt::Horizontal)
return QSize(width(), heightForWidth(width()));
else
return QSize(widthForHeight(height()), height());
}
QPixmap AutoImageLabel::scaledPixmap() const {
return labelPixmap.scaled(sizeHint(), Qt::KeepAspectRatio, Qt::SmoothTransformation);
}
void AutoImageLabel::setPixmap(const QPixmap &newPix) {
labelPixmap = newPix;
QLabel::setPixmap(scaledPixmap());
}
"other_frames.h":
//
// Created by Riccardo on 18/09/2017.
//
#ifndef APOCRYPHA_OTHER_FRAMES_H
#define APOCRYPHA_OTHER_FRAMES_H
#include <QFrame>
#include <QLabel>
#include <QGridLayout>
#include <QWidget>
#include <QResizeEvent>
#include <QPixmap>
#include <QTimer>
#include "widgets.h"
class ConfirmationFrame : public QFrame {
Q_OBJECT
public:
explicit ConfirmationFrame(QWidget* parent);
ConfirmationFrame(QWidget* parent, const QString& text);
private:
QGridLayout* layout;
AutoImageLabel* imageLabel;
AutoTextLabel* textLabel;
};
#endif //APOCRYPHA_OTHER_FRAMES_H
"other_frames.cpp":
//
// Created by Riccardo on 18/09/2017.
//
#include "other_frames.h"
#include <QDebug>
ConfirmationFrame::ConfirmationFrame(QWidget* parent, const QString &text) : QFrame(parent) {
textLabel = new AutoTextLabel(this, text);
QPixmap pix(":/images/check-tick.png");
imageLabel = new AutoImageLabel(this, pix);
textLabel->setAlignment(Qt::AlignCenter);
imageLabel->setAlignment(Qt::AlignCenter);
textLabel->setWordWrap(true);
// Green Background
setStyleSheet("background-color: rgba(106, 242, 94, 1);");
layout = new QGridLayout();
layout->setSpacing(0);
layout->setContentsMargins(32, 32, 32, 32);
layout->setRowStretch(0, 1);
layout->setRowStretch(1, 1);
layout->addWidget(imageLabel, 0, 1);
layout->addWidget(textLabel, 1, 1);
setLayout(layout);
}
ConfirmationFrame::ConfirmationFrame(QWidget *parent) : ConfirmationFrame(parent, "") {
}
"window_main.h":
#ifndef WINDOW_MAIN_H
#define WINDOW_MAIN_H
#include <QMainWindow>
#include <QMenuBar>
#include <QMenu>
#include <QGridLayout>
#include <QFrame>
#include <QScreen>
class MainWindow : public QMainWindow {
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = nullptr);
QFrame *mainFrame;
void center(QScreen* screen);
void autoSetSize(QScreen* screen);
private:
void createMenu();
// Components
QGridLayout *mainLayout;
QMenuBar *menuBar;
QMenu *fileMenu;
};
#endif // WINDOW_MAIN
"window_main.cpp":
#include "window_main.h"
MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) {
mainFrame = new QFrame();
mainLayout = new QGridLayout();
mainLayout->setSpacing(0);
mainLayout->setContentsMargins(0, 0, 0, 0);
createMenu();
mainFrame->setStyleSheet("background-color: red;");
mainFrame->setLayout(mainLayout);
setCentralWidget(mainFrame);
}
void MainWindow::createMenu(){
menuBar = new QMenuBar;
fileMenu = new QMenu(tr("&File"), this);
menuBar->addMenu(fileMenu);
setMenuBar(menuBar);
}
void MainWindow::center(QScreen *screen) {
QSize size = screen->availableSize();
int x = size.width() / 2 - width() / 2;
int y = size.height() / 2 - height() / 2;
move(x, y);
}
void MainWindow::autoSetSize(QScreen *screen) {
QSize screenSize = screen->availableSize();
// TODO Math.round
setMinimumSize(QSize((int)(screenSize.width() / 1.25), (int)(screenSize.height() / 1.25)));
}
"main.cpp":
#include <QApplication>
#include <iostream>
#include <QFile>
#include "quiz/choice.h"
#include "quiz/question.h"
#include "quiz/quizmaker.h"
#include <QSettings>
#include <QStandardPaths>
#include <QDebug>
#include <src/user_interface/other_frames.h>
#include "user_interface/window_main.h"
#include <QScreen>
#include <QFontDatabase>
int main(int argc, char *argv[]) {
QApplication a(argc, argv);
// Set Application Parameters
QCoreApplication::setOrganizationName("Riccardo Fagiolo");
QCoreApplication::setOrganizationDomain("kopharex.me");
QCoreApplication::setApplicationName("Apocrypha");
// Set application font
const int id = QFontDatabase::addApplicationFont(":/fonts/montserrat/Montserrat-Regular.otf");
QString family = QFontDatabase::applicationFontFamilies(id).at(0);
QFont font(family);
font.setStyleStrategy(QFont::PreferAntialias);
a.setFont(font);
// App Settings
QSettings settings;
settings.setValue("data_dir", QStandardPaths::writableLocation(QStandardPaths::AppDataLocation));
// Create UI
auto* window = new MainWindow();
ConfirmationFrame* cframe = new ConfirmationFrame(window, "But I must explain to you how all this mistaken idea of denouncing pleasure and praising pain was born and I will give you a complete account of the system, and expound the actual teachings of the great explorer of the truth, the master-builder of human happiness. No one rejects, dislikes, or avoids pleasure itself, because it is pleasure, but because those who do not know how to pursue pleasure rationally encounter consequences that are extremely painful. Nor again is there anyone who loves or pursues or desires to obtain pain of itself, because it is pain, but because occasionally circumstances occur in which toil and pain can procure him some great pleasure. To take a trivial example, which of us ever undertakes laborious physical exercise, except to obtain some advantage from it? But who has any right to find fault with a man who chooses to enjoy a pleasure that has no annoying consequences, or one who avoids a pain that produces no resultant pleasure?");
window->mainFrame->layout()->addWidget(cframe);
window->autoSetSize(a.primaryScreen());
//cframe->updateTextLabel();
window->show();
window->center(a.primaryScreen());
// [...] - Nothing related to user interface.
return a.exec();
}
Here is a screenshot of the current MainWindow and ConfirmationFrame to give you an idea of what i'm trying to accomplish:
Window Screenshot
All comments regarding the code are welcome.
Thanks for any help,
Riccardo
Hello I tried to fix the resizing issue with an hack.
Before starting the timer to resize the text, just reduce its font to a 1 pixel font:
void AutoTextLabel::resizeEvent(QResizeEvent *event) {
QWidget::resizeEvent(event);
// set a very small font, then start the timer
QFont updatedFont(font());
updatedFont.setPixelSize(1);
setFont(updatedFont);
// Only fire when 25ms have passed since the last resize.
resizeTimer->start(25);
}
Can the effect be acceptable in your opinion?
I'm trying to create a simple animation of a ball that is wandering on a screen and simply bounces off on walls. Since I'm fairly new to C++, I've got some problems with this task. I'm also using the Qt-library.
The ball just stays on the same spot and moves back and forth a little which is odd, because I create a SceneRect with the same size as my graphicsView.
mainwindow.cpp:
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QGraphicsScene>
#include <QtWidgets>
float movement_x = 2.5;
float movement_y = 2.0;
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
}
void MainWindow::showEvent(QShowEvent *event){
scene = new QGraphicsScene(this);
scene -> setSceneRect(ui->graphicsView->geometry().x(), ui->graphicsView->geometry().y(),
ui->graphicsView->geometry().width(), ui->graphicsView->geometry().height());
ui->graphicsView->setScene(scene);
QPen redpen(Qt::red);
QBrush brush(Qt::green);
//QBrush brush2(Qt::black);
QPen mypen(Qt::blue);
mypen.setWidth(7);
ellipse = scene->addEllipse(100.0,100.0,20.0,20.0,mypen,brush);
timer1 = startTimer(50);
timer2 = startTimer(600);
}
void MainWindow::timerEvent(QTimerEvent *event){
// check for radius
if(ellipse->pos().x() >= scene->sceneRect().right() || ellipse->pos().x() <= scene->sceneRect().left()){
movement_x = -movement_x;
}
if(ellipse->pos().y() >= scene->sceneRect().bottom() || ellipse->pos().y() <= scene->sceneRect().top()){
movement_y = -movement_y;
}
ellipse->moveBy(movement_x, movement_y);
}
MainWindow::~MainWindow()
{
delete ui;
}
mainwindow.h:
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QGraphicsScene>
#include <QMainWindow>
namespace Ui {
class MainWindow;
}
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = 0);
~MainWindow();
protected:
void showEvent(QShowEvent* event);
void timerEvent(QTimerEvent* event);
private:
Ui::MainWindow *ui;
QGraphicsScene *scene;
QGraphicsEllipseItem *ellipse;
};
#endif // MAINWINDOW_H
main.cpp:
#include "mainwindow.h"
#include <QApplication>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
MainWindow w;
w.resize(800,600);
w.show();
return a.exec();
}
The initial value of pos is (0,0). Because of this your update oscillate between two values. After addEllipse you have to set an initial position within the scene rect. For example
ellipse->setPos(100,100);
You should map the ellipse position from the scene to find the boundaries :
QPoint posOfEclps = ellipse->mapToScene(ellipse->pos());
if(posOfEclps.x() >= scene->sceneRect().right() || posOfEclps.x() <= scene->sceneRect().left())
{
movement_x = -movement_x;
}
if(posOfEclps.y() >= scene->sceneRect().bottom() || posOfEclps.y() <= scene->sceneRect().top())
{
movement_y = -movement_y;
}
ellipse->moveBy(movement_x, movement_y);
I want to make a simple QWidget which is a simple rectangle fade away. The main problem is that the paint event paint at the same place every time in fact making the effect opposite, it make the colour stronger. Is there any way of achieving this functionality? Could you maybe provide some simple example?
My code:
`
#ifndef WIDGET_H
#define WIDGET_H
#include <QWidget>
#include <QTimer>
namespace Ui {
class Widget;
}
class Widget : public QWidget
{
Q_OBJECT
public:
explicit Widget(QWidget *parent = 0);
void paintEvent(QPaintEvent*);
~Widget();
private:
Ui::Widget *ui;
int alfa;
int i;
QTimer time;
};
#endif // WIDGET_H
`
and the cpp:
#include "widget.h"
#include "ui_widget.h"
#include <QColor>
#include <QPainter>
#include <QBrush>
#include <QTimer>
#include <QDebug>
Widget::Widget(QWidget *parent) :
QWidget(parent,Qt::Popup | Qt::FramelessWindowHint),
ui(new Ui::Widget),
time()
{
ui->setupUi(this);
setAttribute(Qt::WA_NoSystemBackground, true);
setAttribute(Qt::WA_TranslucentBackground, true);
setAttribute(Qt::WA_PaintOnScreen);
time.setInterval(500);
time.start();
connect(&time,SIGNAL(timeout()),this,SLOT(update()));
alfa=100;
i=0;
}
void Widget::paintEvent(QPaintEvent *)
{
QPainter painter(this);
int rectBase = height();
QColor c(255,0,255);
alfa=alfa-(i*10);
c.setAlpha(alfa);
qDebug()<<c.alpha();
i++;
painter.setBrush(QBrush(c));
painter.drawRect(0, 0, width(),height());
}
Widget::~Widget()
{
delete ui;
}
You shouldn't rely on QWidget::paintEvent() to change your alpha level, since it can be called less or more than you want (multiple update() calls may result in only one paintEvent() and paintEvent() may be called when you don't expect it).
So a more reliable way to get to the result, is have a separate slot where you decrease the alpha level and then call update(). Your class definition might look like this:
class Widget : public QWidget
{
Q_OBJECT
public:
Widget( QWidget * inParent );
private:
void paintEvent(QPaintEvent *);
private slots:
void animate();
private:
QTimer * mTimer;
int mAlpha;
};
And the declaration:
Widget::Widget( QWidget * inParent )
:
QWidget( inParent ),
mTimer( new QTimer( this ) ),
mAlpha( 255 )
{
setAttribute(Qt::WA_NoSystemBackground, true);
setAttribute(Qt::WA_TranslucentBackground, true);
setAttribute(Qt::WA_PaintOnScreen);
mTimer->setInterval( 40 );
mTimer->setSingleShot( false );
connect( mTimer, SIGNAL(timeout()), this, SLOT(animate()) );
mTimer->start();
}
void
Widget::paintEvent(QPaintEvent *)
{
QPainter painter(this);
painter.setBrush( QColor(255,0,255,mAlpha ) );
painter.drawRect(rect());
}
void
Widget::animate()
{
if ( mAlpha > 0 )
{
mAlpha -= 3;
}
else
{
mTimer->stop();
}
update();
}
Notice that I did decrease the interval of the timer. You only called an update() every half a second. That typically does not result in a smooth animation.
I get a warning with this code on Kubuntu Linux, under Qt5:
QWidget::paintEngine: should no longer be called
The originating line is in qWarning("QWidget::paintEngine: Should no longer be called");, in src/widgets/kernel/qwidget_qpa.cpp and discussed a bit in this ticket:
https://qt.gitorious.org/qt/qtbase-harmattan/commit/3037525
You can get the warning to stop by removing the setAttribute(Qt::WA_PaintOnScreen);, so I did that. After taking that line out, it works for me--although your subtraction model is strange. You are modifying the alpha as well as changing the subtraction value on each iteration; you probably didn't intend both. So either change it to:
QColor c (255, 0, 255);
alfa = alfa - 10;
if (alfa >= 0) {
c.setAlpha(alfa);
} else {
time.stop();
}
...or:
QColor c(255,0,255);
if (alfa - i * 10 >= 0) {
c.setAlpha(alfa - i * 10);
i++;
} else {
time.stop();
}
Etc. (See also #PrisonMonkeys note on your timer not necessarily being the only source of update() calls.) Regarding getting these warnings to be more vocal so you don't miss them, you might look at The Essential Noisy Debug Hook For Qt, which I should update.
If with the change, an alpha blended window doesn't work on your platform at all, you should mention explicitly what your circumstance is...as it is working for me.
I have to create a simple box which rotates an ellipse and some text depending upon value from horizontalSlider/spinBox. The widget has to be resizable, And size of the ellipse has to change depending upon that.
For now only the ellipse is being painted. The text painting will be added if this works. The problem is that if the window after resize exceeds the original window size, the painting is weird.
window.h:
#ifndef WINDOW_H
#define WINDOW_H
#include <QtGui>
#include "ui_form.h"
class Window : public QWidget, private Ui::Form
{
Q_OBJECT
public:
Window(QWidget *parent = 0);
public slots:
void rotateEllip(int angle);
void rotateText(int angle);
protected:
void paintEvent(QPaintEvent *event);
};
#endif // WINDOW_H
window.cpp:
#include "window.h"
qreal textAngle = 0.0;
qreal ellipAngle = 0.0;
Window::Window(QWidget *parent) : QWidget(parent)
{
setupUi(this);
connect(spinBox_ellipse,SIGNAL(valueChanged(int)),this,SLOT(rotateEllip(int)));
connect(horizontalSlider_ellipse,SIGNAL(valueChanged(int)),this,SLOT(rotateEllip(int)));
connect(spinBox_text,SIGNAL(valueChanged(int)),this,SLOT(rotateText(int)));
connect(horizontalSlider_text,SIGNAL(valueChanged(int)),this,SLOT(rotateText(int)));
}
void Window::rotateEllip(int angle)
{
ellipAngle = (qreal) angle;
Window::Window(this);
}
void Window::rotateText(int angle)
{
textAngle = (qreal) angle;
Window::Window(this);
}
void Window::paintEvent(QPaintEvent *event)
{
QPen pen(Qt::black,2,Qt::SolidLine);
QPoint center(0,0);
QPainter painter(this);
painter.setRenderHint(QPainter::Antialiasing);
/* Drawing ellipse*/
painter.eraseRect(10,10,frame_ellipse->width(),frame_ellipse->height());
painter.translate(frame_ellipse->width()/2+10,frame_ellipse->height()/2+10);
painter.rotate(ellipAngle);
if (frame_ellipse->width() > frame_ellipse->height()) painter.drawEllipse(center,(frame_ellipse->height()/4)-5,(frame_ellipse->height()/2)-10);
else if (frame_ellipse->width() <= frame_ellipse->height() ) painter.drawEllipse(center,(frame_ellipse->width()/2)-10,(frame_ellipse->width()/4)-5);
painter.rotate(-ellipAngle);
painter.translate(-frame_ellipse->width()/2+10,-frame_ellipse->height()/2+10);
}
main.cpp is normal window.show() calling.
My guess is the call to constructor creates a temporary widget object and messes up the drawing.