Qt QPainter error when rotating an ellipse using horizontalSlider - c++

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.

Related

Creating separate timer for speed and ball creating

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.

How to make a canvas QWidget that can be zoomed and drawn on?

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);
}

How to draw rectangle on custom video widget t in QT?

I want to select an area on a custom video widget and draw rectangle on selected area.
So far I can select an area with QRubberband but I am having trouble with drawing the rectangle after releasing left click.
Whenever I click-drag then release to draw rectangle it gives this error:
QBackingStore::endPaint() called with active painter on backingstore paint device
The program has unexpectedly finished.
Here is my code:
myvideoobject.h
#ifndef MYVIDEOOBJECT_H
#define MYVIDEOOBJECT_H
#include <QObject>
#include <QVideoWidget>
#include <QRubberBand>
#include <QPainter>
#include <QPen>
#include <QPaintEvent>
#include <QRect>
#include <QMouseEvent>
#include <QDebug>
class MyVideoObject : public QVideoWidget
{
Q_OBJECT
public:
explicit MyVideoObject(QWidget *parent = 0);
void mouseMoveEvent(QMouseEvent *ev);
void mousePressEvent(QMouseEvent *ev);
void mouseReleaseEvent(QMouseEvent *ev);
void paintEvent(QPaintEvent *ev);
private:
QRubberBand* rubberBand;
QPainter* painter;
//QRect *rectangle;
QPoint origin;
QPoint endPoint;
};
#endif // MYVIDEOOBJECT_H
myvideoobject.cpp
#include "myvideoobject.h"
MyVideoObject::MyVideoObject(QWidget* parent) :
QVideoWidget(parent)
{
rubberBand = new QRubberBand(QRubberBand::Rectangle, this);
rubberBand->setGeometry(0,0,50,50);//ileride silebilrisin
}
void MyVideoObject::mouseMoveEvent(QMouseEvent *ev)
{
rubberBand->setGeometry(QRect(origin,ev->pos()).normalized());
}
void MyVideoObject::mousePressEvent(QMouseEvent *ev)
{
origin = ev->pos();
if(!rubberBand)
rubberBand = new QRubberBand(QRubberBand::Rectangle, this);
rubberBand->setGeometry(QRect(origin,QSize()));
rubberBand->show();
}
void MyVideoObject::mouseReleaseEvent(QMouseEvent *ev)
{
rubberBand->hide();
endPoint = ev->pos();
painter->begin(this);
painter->drawRect(QRect(origin,endPoint));
}
void MyVideoObject::paintEvent(QPaintEvent *ev)
{
QRect rect = ev->rect();
painter = new QPainter(this);
painter->setRenderHint(QPainter::Antialiasing);
painter->setPen(Qt::black);
painter->drawText(rect,Qt::AlignCenter,"Data");
painter->drawRect(rect);
//painter->setPen(Qt::red);
}
I didn't add mainwindow.cpp and mainwindow.h cuz there isn't much code in those other than selecting video with openfiledialog.
When you create a pointer: QPainter *painter, this can point to any memory since it has garbage. so when you do painter->begin(this) you are accessing uninitialized memory, that's why you get that error. On the other hand in a QWidget such as QVideoWidget should only be painted in the method paintEvent, the strategy is to have variables that save the state of what you want to paint, for example the QRect, and call update to paint it.
myvideoobject.h
#ifndef MYVIDEOOBJECT_H
#define MYVIDEOOBJECT_H
#include <QVideoWidget>
class QRubberBand;
class MyVideoObject : public QVideoWidget
{
public:
MyVideoObject(QWidget *parent = nullptr);
protected:
void mouseMoveEvent(QMouseEvent *ev);
void mousePressEvent(QMouseEvent *ev);
void mouseReleaseEvent(QMouseEvent *ev);
void paintEvent(QPaintEvent *ev);
private:
QRubberBand *rubberBand;
QPoint origin;
QRect rect;
};
#endif // MYVIDEOOBJECT_H
myvideoobject.cpp
#include "myvideoobject.h"
#include <QMouseEvent>
#include <QPainter>
#include <QRubberBand>
MyVideoObject::MyVideoObject(QWidget *parent):
QVideoWidget(parent),
rubberBand(nullptr){}
void MyVideoObject::mousePressEvent(QMouseEvent *ev)
{
origin = ev->pos();
if(!rubberBand)
rubberBand = new QRubberBand(QRubberBand::Rectangle, this);
rubberBand->setGeometry(QRect(origin,QSize()));
rubberBand->show();
QVideoWidget::mousePressEvent(ev);
}
void MyVideoObject::mouseMoveEvent(QMouseEvent *ev)
{
rubberBand->setGeometry(QRect(origin,ev->pos()).normalized());
QVideoWidget::mouseMoveEvent(ev);
}
void MyVideoObject::mouseReleaseEvent(QMouseEvent *ev)
{
rect = rubberBand->geometry();
update();
QVideoWidget::mouseReleaseEvent(ev);
}
void MyVideoObject::paintEvent(QPaintEvent *ev)
{
QVideoWidget::paintEvent(ev);
QPainter painter(this);
painter.save();
painter.setBrush(Qt::red);
if(!rect.isNull())
painter.drawRect(rect);
painter.restore();
}

Displacement in QGraphicsScene

I have such program: in my Widget I have qpushbuttons, qslider and qgraphicsview. I place qgraphicsscene in qgraphicsview. The problem is that when I click in QGraphicsscene and want to draw a Point, that was clicked, there is a displacement. When I click in center of QGraphicsScene there is not any problems.
if (mouseEvent->button() == Qt::LeftButton)
{
QPointF pos = mouseEvent->scenePos();
addEllipse(pos.x(), pos.y(), 5, 5, QPen(QColor(Qt::green)), QBrush(QColor(Qt::red)));
}
There is a screen too.
So, the question is how to avoid this moving of scene.
In the screen I have clicked in right bottom, but point was drawn in the center
Try this. In my computer it works without displacement
.*h
#ifndef GRAPHICSSCENE_H
#define GRAPHICSSCENE_H
#include <QGraphicsScene>
#include <QMouseEvent>
class GraphicsScene : public QGraphicsScene
{
Q_OBJECT
public:
explicit GraphicsScene(QObject *parent = 0);
signals:
protected:
void mousePressEvent(QGraphicsSceneMouseEvent *mouseEvent);
public slots:
};
#endif // GRAPHICSSCENE_H
*.cpp
#include "graphicsscene.h"
#include <QDebug>
#include <QGraphicsSceneMouseEvent>
GraphicsScene::GraphicsScene(QObject *parent) :
QGraphicsScene(parent)
{
}
void GraphicsScene::mousePressEvent(QGraphicsSceneMouseEvent *mouseEvent)
{
qDebug() << "in";
if (mouseEvent->button() == Qt::LeftButton)
{
QPointF pos = mouseEvent->scenePos();
addEllipse(pos.x(), pos.y(), 5, 5, QPen(QColor(Qt::green)), QBrush(QColor(Qt::red)));
}
}
Using
GraphicsScene *scene = new GraphicsScene(this);
ui->graphicsView->setSceneRect(50,50,50,50);//for example
ui->graphicsView->setScene(scene);
ui->graphicsView->show();
view->setSceneRect(0, 0, 640, 480);
has helped me

QWidget does not respond after a mouse press event.

I am trying to get a mouse press event to work with my widget I created but every time I click the widget, the window stops responding and I have to kill the program. Does anyone know how to fix this and also how to get the color to change?
Here is the .h and the .cpp files.
.cpp file:
#include "iconwidget.h"
#include <QPaintEvent>
#include <QPainter>
#include <QPainterPath>
iconWidget::iconWidget(QWidget *parent) :
QWidget(parent)
{
this->resize(ICON_WIDGET_WIDTH,ICON_WIDGET_HEIGHT);
pressed = false;
}
void iconWidget::paintEvent(QPaintEvent *event)
{
QRect areatopaint = event->rect();
QPainter painter(this);
QBrush brush(Qt::black);
QPointF center = this->rect().center();
QPainterPath icon;
icon.addEllipse(center,20,20);
painter.drawPath(icon);
painter.fillPath(icon, brush);
if (pressed) {
brush.setColor(Qt::red);
}
}
void iconWidget::mousePressEvent(QMouseEvent *event)
{
pressed = true;
update();
iconWidget::mousePressEvent(event);
}
.h file:
#define ICONWIDGET_H
#include <QWidget>
#define ICON_WIDGET_WIDTH 45
#define ICON_WIDGET_HEIGHT 45
class iconWidget : public QWidget
{
Q_OBJECT
public:
explicit iconWidget(QWidget *parent = 0);
void paintEvent(QPaintEvent *event);
bool pressed;
protected:
void mousePressEvent(QMouseEvent *event);
};
#endif // ICONWIDGET_H
You call mousePressEvent() in an endless recursion. You should change the line:
iconWidget::mousePressEvent(event);
in your mousePressEvent function to:
QWidget::mousePressEvent(event);