How to use mouseMoveEvent on paintEvent on Qt 5? - c++

I'm new on Qt and c++, so I'm having some difficulties. I'm trying to create a widget that can get the mouseMoveEvent position and draw an ellipse on my pixmap on mouse position. Below you can see the code:
#include "myimage.h"
#include <QPainter>
#include <QPen>
#include <QColor>
#include <QMouseEvent>
#include <QDebug>
Myimage::Myimage(QWidget *parent) : QWidget(parent)
{
setMouseTracking(true); // E.g. set in your constructor of your widget.
}
// Implement in your widget
void Myimage::mouseMoveEvent(QMouseEvent *event)
{
qDebug() << event->pos();
}
void Myimage::paintEvent(QPaintEvent * event)
{
event->accept();
QPixmap pixmap2("/home/gabriel/Qt_interfaces/OpenCVTests/Webcam_PyQt5/Images/Court_top_View.jpg");
QRect rectangle(0, 0, width()-1, height()-1);
QPainter painter(this);
painter.drawRect(rectangle);
painter.drawPixmap(5, 5, width()-10, height()-10, pixmap2);
painter.drawEllipse(pos(), 10 ,10 );
}
The mouse position is being printed on console, but no ellipse on image.
Could you help me?
Regards,
Gabriel.

According to the doc:
pos : QPoint
This property holds the position of the widget within its parent
widget.
If the widget is a window, the position is that of the widget on the
desktop, including its frame.
...
Access functions:
QPoint pos() const void
move(int x, int y)
void move(const QPoint &)
As we see this data we do not want it, a possible solution is to create a variable that stores the value of the position obtaining through QMouseEvent and update the painting through the function update(), in addition the first time the Widget there should be no ellipse so we check that the position has been assigned through the function isNull() of QPoint, as I show below:
*.h
private:
QPoint mPoint;
*.cpp
Myimage::Myimage(QWidget *parent)
: QWidget(parent)
{
setMouseTracking(true);
}
void Myimage::mouseMoveEvent(QMouseEvent *event)
{
mPoint = event->pos();
update();
}
void Myimage::paintEvent(QPaintEvent *)
{
QPixmap pixmap2("/home/gabriel/Qt_interfaces/OpenCVTests/Webcam_PyQt5/Images/Court_top_View.jpg");
QRect rectangle(0, 0, width()-1, height()-1);
QPainter painter(this);
painter.drawRect(rectangle);
painter.drawPixmap(5, 5, width()-10, height()-10, pixmap2);
if(!mPoint.isNull()){
painter.drawEllipse(mPoint, 10 ,10 );
}
}

Related

Custom widget is flickering and clipping while moving

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 :

Moving a figure QT [duplicate]

I'm new on Qt and c++, so I'm having some difficulties. I'm trying to create a widget that can get the mouseMoveEvent position and draw an ellipse on my pixmap on mouse position. Below you can see the code:
#include "myimage.h"
#include <QPainter>
#include <QPen>
#include <QColor>
#include <QMouseEvent>
#include <QDebug>
Myimage::Myimage(QWidget *parent) : QWidget(parent)
{
setMouseTracking(true); // E.g. set in your constructor of your widget.
}
// Implement in your widget
void Myimage::mouseMoveEvent(QMouseEvent *event)
{
qDebug() << event->pos();
}
void Myimage::paintEvent(QPaintEvent * event)
{
event->accept();
QPixmap pixmap2("/home/gabriel/Qt_interfaces/OpenCVTests/Webcam_PyQt5/Images/Court_top_View.jpg");
QRect rectangle(0, 0, width()-1, height()-1);
QPainter painter(this);
painter.drawRect(rectangle);
painter.drawPixmap(5, 5, width()-10, height()-10, pixmap2);
painter.drawEllipse(pos(), 10 ,10 );
}
The mouse position is being printed on console, but no ellipse on image.
Could you help me?
Regards,
Gabriel.
According to the doc:
pos : QPoint
This property holds the position of the widget within its parent
widget.
If the widget is a window, the position is that of the widget on the
desktop, including its frame.
...
Access functions:
QPoint pos() const void
move(int x, int y)
void move(const QPoint &)
As we see this data we do not want it, a possible solution is to create a variable that stores the value of the position obtaining through QMouseEvent and update the painting through the function update(), in addition the first time the Widget there should be no ellipse so we check that the position has been assigned through the function isNull() of QPoint, as I show below:
*.h
private:
QPoint mPoint;
*.cpp
Myimage::Myimage(QWidget *parent)
: QWidget(parent)
{
setMouseTracking(true);
}
void Myimage::mouseMoveEvent(QMouseEvent *event)
{
mPoint = event->pos();
update();
}
void Myimage::paintEvent(QPaintEvent *)
{
QPixmap pixmap2("/home/gabriel/Qt_interfaces/OpenCVTests/Webcam_PyQt5/Images/Court_top_View.jpg");
QRect rectangle(0, 0, width()-1, height()-1);
QPainter painter(this);
painter.drawRect(rectangle);
painter.drawPixmap(5, 5, width()-10, height()-10, pixmap2);
if(!mPoint.isNull()){
painter.drawEllipse(mPoint, 10 ,10 );
}
}

How to move the whole window when mouse is on the window's custom widget in Qt?

Let's say I have a custom widget and add it to the main window in qt.
As you can see, the red area is the custom widget. What I want to do is when the mouse is pressed in the red area and moved, the whole window will move as well.
I know how to simply implement mousePressEvent and mouseMoveEvent; but when dealing with a window with the custom widget, I do not know how to move the whole window when mouse is pressed on the custom widget.
Also I want to mention that I only want the window movable when mouse is pressed and moved in the red area, and when mouse is pressed and moved in the rest part of the main window area, nothing will happen.
This is what my CustomWidget class looks like:
CustomWidget::CustomWidget(QWidget *parent) : QWidget(parent)
{
setFixedSize(50, 50);
setStyleSheet("QWidget { background: red; }");
}
void CustomWidget::paintEvent(QPaintEvent *)
{
QStyleOption opt;
opt.init(this);
QPainter painter(this);
style()->drawPrimitive(QStyle::PE_Widget, &opt, &painter, this);
}
void CustomWidget::mousePressEvent(QMouseEvent *event)
{
xCoord = event->x();
yCoord = event->y();
}
void CustomWidget::mouseMoveEvent(QMouseEvent *event)
{
move(event->globalX() - xCoord, event->globalY() - yCoord);
}
In case you wonder why I want to do this, in my app, I hid the title bar and drew a custom title bar by myself. But the window is not movable, so I want to make the whole window movable when mouse is pressed and moved on the title bar.
Hope I explained myself clearly.
To move the window from any widget it is necessary to be able to access the window, and for this we use the method window() that returns the top level, it is not necessary to separate the coordinates x() and y(), the following code implements the solution:
customwidget.h
#ifndef CUSTOMWIDGET_H
#define CUSTOMWIDGET_H
#include <QWidget>
class CustomWidget : public QWidget
{
Q_OBJECT
public:
explicit CustomWidget(QWidget *parent = nullptr);
protected:
void paintEvent(QPaintEvent *);
void mousePressEvent(QMouseEvent *event);
void mouseMoveEvent(QMouseEvent *event);
private:
QPoint startPos;
};
#endif // CUSTOMWIDGET_H
customwidget.cpp
#include "customwidget.h"
#include <QMouseEvent>
#include <QPainter>
#include <QStyleOption>
CustomWidget::CustomWidget(QWidget *parent) : QWidget(parent)
{
setFixedSize(50, 50);
setStyleSheet("QWidget { background: red; }");
}
void CustomWidget::paintEvent(QPaintEvent *)
{
QStyleOption opt;
opt.init(this);
QPainter painter(this);
style()->drawPrimitive(QStyle::PE_Widget, &opt, &painter, this);
}
void CustomWidget::mousePressEvent(QMouseEvent *event)
{
startPos = event->pos();
QWidget::mousePressEvent(event);
}
void CustomWidget::mouseMoveEvent(QMouseEvent *event)
{
QPoint delta = event->pos() - startPos;
QWidget * w = window();
if(w)
w->move(w->pos() + delta);
QWidget::mouseMoveEvent(event);
}
If you are working on Windows, can use it:
#include "mywidget.h"
#include <windows.h>
#include <QWindow>
MyWidget::MyWidget(QWidget *parent)
: QWidget(parent)
{
}
MyWidget::~MyWidget()
{
}
void MyWidget::mousePressEvent(QMouseEvent* event)
{
if (event->buttons().testFlag(Qt::LeftButton))
{
HWND hWnd = ::GetAncestor((HWND)(window()->windowHandle()->winId()), GA_ROOT);
POINT pt;
::GetCursorPos(&pt);
::ReleaseCapture();
::SendMessage(hWnd, WM_NCLBUTTONDOWN, HTCAPTION, POINTTOPOINTS(pt));
}
}
void QHexWindow::mousePressEvent(QMouseEvent *event)
{
QLabel *child = static_cast<QLabel*>(childAt(event->pos()));
if (child!=mTitleBar) //mTitlebar is the QLabel on which we want to implement window drag
{
return;
}
isMousePressed = true;
mStartPos = event->pos();
}
void QHexWindow::mouseMoveEvent(QMouseEvent *event)
{
if(isMousePressed)
{
QPoint deltaPos = event->pos() - mStartPos;
this->move(this->pos()+deltaPos);
}
}
void QHexWindow::mouseReleaseEvent(QMouseEvent *event)
{
QLabel *child = static_cast<QLabel*>(childAt(event->pos()));
if (child!=mTitleBar)
{
return;
}
isMousePressed = false;
}
I have implemented the above in one of my github project https://github.com/VinuRajaKumar/AVR-HEX-Viewer where QLabel is used as TitleBar for the window.

How to draw a rectangle on a graph with XY axes Qt

I want to draw a rectangle on a graph with XY axes using Qt. I have found QCustomPlot widget, but it is not what i need (or i did not understand how to apply it to solve my problem).
Please any suggestions how to make it work?
This is an example of what you need:
#include <QWidget>
#include <QPainter>
class MyPlot : public QWidget
{
Q_OBJECT
public:
MyPlot(QWidget *parent = 0)
: QWidget(parent)
{
}
protected:
void paintEvent(QPaintEvent *event)
{
QPainter painter(this);
painter.save();
painter.translate(2, height() -2); // 2 pixels between axes and the windows frame
painter.scale(1,-1);
QPen pen;
pen.setWidth(2);
painter.setPen(pen);
// X Axis
painter.drawLine(0,0, width(),0);
// Y Axis
painter.drawLine(0,0, 0,height());
pen.setWidth(4);
painter.setPen(pen);
// Rect
painter.drawRect(10,10, 60,80);
painter.restore();
}
};
You can do it by adding QCPItemRect to QCPLayer of QCustomPlot. It seems to be the easiest solution.
The simplest override paintEvent in QWidget:
void MyWidget::paintEvent(QPaintEvent * event)
{
Q_UNUSED(event);
QPainter painter(this);
painter.drawLine(0, 10, 100, 10);
painter.drawLine(10, 0, 10, 100);
painter.drawRect(20, 20, 30, 30);
}
You can use just the plain QWidget and reimplement it's paintEvent() function. The painting would be realized by the QPainter.
void CMyWidget::paintEvent(QPaintEvent* event)
{
QPainter p(this);
p.drawLine(...);
p.drawRect(...);
p.drawText(...);
}
Or you can use a QGraphicsView / QGraphicsScene framework: http://doc.qt.io/qt-4.8/graphicsview.html

Erasing painted areas from translucent widgets in Qt

I am faced with the problem of having to erase previously painted areas on a Qt widget.
The basic idea is, the user selects an area of the screen by clicking and dragging the mouse and a rectangle is drawn over the selected area.
The header
class ClearBack : public QWidget
{
Q_OBJECT
public:
explicit ClearBack(const QPoint &startingPos);
bool eventFilter(QObject *obj, QEvent *event);
void paintEvent(QPaintEvent *);
void mouseMoveEvent(QMouseEvent *event);
signals:
void regionSelected(const QRect &);
private:
QRect currentRegion;
};
The Implementation
ClearBack::ClearBack(const QPoint &startingPos)
{
setBackgroundRole(QPalette::Base);
installEventFilter(this);
currentRegion.setTopLeft(startingPos);
currentRegion.setBottomRight(startingPos);
this->setWindowFlags(Qt::Dialog | Qt::FramelessWindowHint);
this->showMaximized();
}
void ClearBack::paintEvent(QPaintEvent * event)
{
Q_UNUSED(event);
QPainter painter(this);
painter.setRenderHint(QPainter::Antialiasing);
painter.setPen(Qt::black);
painter.drawRect(currentRegion);
}
void ClearBack::mouseMoveEvent(QMouseEvent *event)
{
QPoint currentPos(event->globalX(), event->globalY());
currentRegion.setBottomRight(currentPos);
this->repaint();
}
On a widget that has a solid background the effect works quite nicely, producing a single rectangle.
However, when the background is set to setAttribute(Qt::WA_TranslucentBackground); the following occurs.
The rectangles that were drawn previously are not "erased"
Is there a way to erase the previously painted rectangles on a translucent background, and if so, how?
Also for "bonus points" why does this effect occur on a translucent background and not on a solid one?
Widgets with WA_TranslucentBackground attribute do not clear their backgrounds automatically. You have to:
Change the composition mode from the default SourceOver to Source,
Explicitly clear the old rectangle with a transparent brush,
Paint the new rectangle.
Below is a working example, tested under Qt 5. You have to press the mouse to draw the initial rectangle and drag it around; the program exits when you release the mouse.
#include <QApplication>
#include <QWidget>
#include <QPainter>
#include <QMouseEvent>
class ClearBack : public QWidget
{
Q_OBJECT
QRect m_currentRegion, m_lastRegion;
public:
explicit ClearBack(const QPoint &startingPos) :
m_currentRegion(startingPos, startingPos)
{
setWindowFlags(Qt::Dialog | Qt::FramelessWindowHint);
setAttribute(Qt::WA_TranslucentBackground);
showMaximized();
}
Q_SIGNAL void regionSelected(const QRect &);
protected:
void paintEvent(QPaintEvent *) {
QPainter painter(this);
painter.setCompositionMode(QPainter::CompositionMode_Source);
painter.setRenderHint(QPainter::Antialiasing);
painter.setPen(QPen(Qt::transparent, 3));
painter.drawRect(m_lastRegion);
m_lastRegion = m_currentRegion;
painter.setPen(Qt::black);
painter.drawRect(m_currentRegion);
}
void mouseMoveEvent(QMouseEvent *event) {
m_currentRegion.setBottomRight(event->globalPos());
update();
}
void mouseReleaseEvent(QMouseEvent *) {
emit regionSelected(m_currentRegion);
}
};
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
ClearBack back(QPoint(200,200));
a.connect(&back, SIGNAL(regionSelected(QRect)), SLOT(quit()));
return a.exec();
}
#include "main.moc"