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();
}
Related
I'll give you a minimal reproduceable example that was a part of the more complex widget.
Here we just have a custom widget(called MovableItem) with a simple paintEvent. Widget is created, placed onto the central widget and moved to mouse position on MainWindow::mousePressEvent-s.
When moving, it seems like the widget is getting clipped at the side it's moving towards.
mainwindow.h
#include <QMainWindow>
#include <QMouseEvent>
#include <QPropertyAnimation>
#include "movableitem.h"
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
MainWindow(QWidget *parent = nullptr);
~MainWindow();
void mousePressEvent(QMouseEvent *event) override;
QWidget* mItem;
};
mainwindow.cpp
#include "mainwindow.h"
MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent)
{
resize(640, 480);
QWidget* central_widget = new QWidget(this);
mItem = new MovableItem(central_widget);
mItem->move(20, 20);
setCentralWidget(central_widget);
}
void MainWindow::mousePressEvent(QMouseEvent *event) {
QPoint pos = event->pos();
QPropertyAnimation* anim = new QPropertyAnimation(mItem, "geometry");
anim->setDuration(750);
anim->setStartValue(QRect(mItem->x(), mItem->y(), mItem->width(), mItem->height()));
anim->setEndValue(QRect(pos.x(), pos.y(), mItem->width(), mItem->height()));
anim->start();
}
MainWindow::~MainWindow() {}
movableitem.h
#include <QWidget>
#include <QPainter>
#include <QPainterPath>
class MovableItem : public QWidget
{
Q_OBJECT
public:
MovableItem(QWidget *parent = nullptr);
QSize sizeHint() const override;
void paintEvent(QPaintEvent *event) override;
};
movableitem.cpp
#include "movableitem.h"
MovableItem::MovableItem(QWidget *parent) : QWidget(parent)
{
setParent(parent);
}
QSize MovableItem::sizeHint() const {
return QSize(150, 40);
}
void MovableItem::paintEvent(QPaintEvent *event) {
QRect r = rect();
QPainter painter(this);
painter.setRenderHint(QPainter::Antialiasing);
QPainterPath path;
path.addRoundedRect(r, 5, 5);
QBrush brush(QColor(217, 217, 217));
painter.fillPath(path, brush);
painter.drawPath(path);
}
Example
As you can see, movement is not fluid, but choppy. I have no idea what is happening. Am I doing something completely wrong ? Do I need to implement some additional functions, is double buffering needed, is this because of Qt's automatic clipping ? Should I rewrite it in QGraphicsView ?
Add mItem->repaint(); and mItem->update(); in mousePressEvent function
void MainWindow::mousePressEvent(QMouseEvent *event)
{
QPoint pos = event->pos();
QPropertyAnimation* anim = new QPropertyAnimation(mItem, "geometry");
anim->setDuration(750);
anim->setStartValue(QRect(mItem->x(), mItem->y(), mItem->width(), mItem->height()));
anim->setEndValue(QRect(pos.x(), pos.y(), mItem->width(), mItem->height()));
anim->start();
mItem->repaint();
mItem->update();
}
my out put is here :
I did sub-classing to include mouse click function. Here, a rectangle can be chosen by mousePressEvent, mouseMoveEvent and mouseReleaseEvent. When I am trying to chose another rectangle, my previous rectangle is not being removed. It is still displaying with my previous drawn rectangle, which I don't want to display. I want to chose and display only one rectangle. I meant when I press again to chose another rectange, the previous one should be removed.
I included here my subclass named mouse_crop
mouse_crop.h is as follows
#ifndef MOUSE_CROP_H
#define MOUSE_CROP_H
#include <QMainWindow>
#include <QObject>
#include <QWidget>
#include <QMouseEvent>
#include <QLabel>
#include <QRubberBand>
class mouse_crop : public QLabel
{
Q_OBJECT
public:
mouse_crop(QWidget *parent=0);
QRubberBand *rubberBand;
QPoint origin, ending;
protected:
void mousePressEvent(QMouseEvent *ev);
void mouseMoveEvent(QMouseEvent *ev);
void mouseReleaseEvent(QMouseEvent *ev);
signals:
void sendMousePosition(QPoint&);
void sendMouseEnding(QPoint&);
};
#endif // MOUSE_CROP_H`
And mouse_crop.cpp is as follows
#include "mouse_crop.h"
mouse_crop::mouse_crop(QWidget *parent):QLabel (parent)
{
}
void mouse_crop::mousePressEvent(QMouseEvent *ev)
{
origin = ev->pos();
rubberBand = new QRubberBand(QRubberBand::Rectangle, this);
if(ev->button()== Qt::LeftButton || ev->button()== Qt::RightButton)
{
rubberBand->show();
emit sendMousePosition(origin);
}
}
void mouse_crop::mouseMoveEvent(QMouseEvent *ev)
{
rubberBand->setGeometry(QRect(origin, ev->pos()).normalized());
}
void mouse_crop::mouseReleaseEvent(QMouseEvent *ev)
{
ending = ev->globalPos();
if(ev->button()== Qt::LeftButton || ev->button()== Qt::RightButton)
{
emit sendMouseEnding(ending);
}
}
Can any one tell me how to solve this? Thanks in advance.
The problem is caused because every time you press the mouse you are creating a new QRubberBand, what you must do is create only a QRubberBand, hide it and show it when necessary.
mouse_crop::mouse_crop(QWidget *parent)
: QLabel(parent)
{
rubberBand = new QRubberBand(QRubberBand::Rectangle, this);
rubberBand->hide();
}
void mouse_crop::mousePressEvent(QMouseEvent *ev)
{
origin = ev->pos();
rubberBand->setGeometry(QRect(origin, origin));
if(ev->button()== Qt::LeftButton || ev->button()== Qt::RightButton)
{
rubberBand->show();
emit sendMousePosition(origin);
}
}
void mouse_crop::mouseMoveEvent(QMouseEvent *ev)
{
rubberBand->setGeometry(QRect(origin, ev->pos()).normalized());
}
void mouse_crop::mouseReleaseEvent(QMouseEvent *ev)
{
ending = ev->globalPos();
if(ev->button()== Qt::LeftButton || ev->button()== Qt::RightButton)
{
emit sendMouseEnding(ending);
}
}
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);
I have a window based on a semitransparent image:
import QtQuick 1.1
import QtWebKit 1.1
Image {
source: "qrc:/assets/bg.png"
}
And something like this in main window
#include "mainwindow.h"
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent)
{
setAttribute(Qt::WA_TranslucentBackground);
setStyleSheet("background:transparent;");
/* turn off window decorations */
setWindowFlags(Qt::FramelessWindowHint);
ui = new QDeclarativeView;
ui->setSource(QUrl("qrc:/assets/ui.qml"));\
setCentralWidget(ui);
}
MainWindow::~MainWindow()
{
delete ui;
}
and
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include <QtDeclarative/QDeclarativeView>
namespace Ui {
class MainWindow;
}
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = 0);
~MainWindow();
private:
QDeclarativeView *ui;
};
#endif // MAINWINDOW_H
I wonder how to make my window draggable across the screen (user presses on an image and drugs window around..)?
Reimplement mousePressEvent() and mouseReleaseEvent() to know when the user is holding the mouse down, then reimplement mouseMoveEvent() and if the user is holding the mouse down, move the widget.
// **Untested code**
protected:
virtual void mousePressEvent(QMouseEvent *event) { _mouseIsDown = true; }
virtual void mouseReleaseEvent(QMouseEvent *event) { _mouseIsDown = false; }
virtual void mouseMoveEvent(QMouseEvent *event) { if(_mouseIsDown) { move(event->pos() + globalPos()); } }
#include <QMouseEvent>
#include <Qpoint>
class MainWindow : public QMainWindow{
...
void mousePressEvent(QMouseEvent *event);
void mouseMoveEvent(QMouseEvent *event);
QPoint LastPoint;
QPoint LastTopLeft;
void mousePressEvent(QMouseEvent *event)
{
if (event->button() == Qt::LeftButton) {
QPoint Point=event->globalPos();
LastTopLeft=this->frameGeometry().topLeft();
LastPoint=Point;
}
}
void mouseMoveEvent(QMouseEvent *event)
{
if ((event->buttons() & Qt::LeftButton)) {
const QPoint Point=event->globalPos();
QPoint offset=Point-LastPoint;
this->move(LastTopLeft+offset);
}
}
...
}
It worked for me after I removed the first two declarations.
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.