Moving a figure QT [duplicate] - 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

QGraphicsScene item is drawn at twice(x2) position

(warning crossposted on: https://forum.qt.io/topic/105158/qgraphicsscene-item-is-drawn-at-twice-x2-position)
In the following code I am creating a custom widget which has a subclass of QGraphicsScene embeded in it. My aim is to click and add a point to the scene. A point is my subclass of QGraphicsItem (GIPoint). I want to move that point around and later connect it to other points and make a spline path.
The problem I am facing is that the point is not drawn at where I click but at a place which is formed by doubling the mouse-event's scenePos() coordinates. So if I click at (100,100) the point is drawn at (200,200). I suspect that I have misunderstood the coordinate system despite reading the documentation.
The question How to add item in a QGraphicsScene? seems relevant but the proposed solution to transform the mouse-event's coordinates via mapToScene(event->pos()); actually doubles up the position (before it will print that it draws on same position but it would then be x2. Now it also prints it as x2).
So I am asking additionally to point me to some simple-to-digest advice on how the widgets placement works. btw. is QRectF GIPoint::boundingRect() const {
return QRectF(pos().x(), pos().y(), 5, 5);
correct regarding the (x,y) coordinates of the rectangle?
My example code follows:
/* use the following pro:
QT += widgets core gui
CONFIG += debug console
SOURCES = example.cpp
TARGET = example
*/
#include <QGraphicsItem>
#include <QPainter>
#include <QWidget>
#include <QRectF>
#include <QPointF>
#include <QGraphicsScene>
#include <QStyleOptionGraphicsItem>
#include <QGraphicsSceneMouseEvent>
#include <QKeyEvent>
#include <QGraphicsView>
#include <QApplication>
#include <QOpenGLWidget>
#include <QMainWindow>
#include <QGridLayout>
#include <QDebug>
class GIPoint : public QGraphicsItem{
public:
GIPoint(QGraphicsItem * parent, const QPointF &position);
protected:
QVariant itemChange(QGraphicsItem::GraphicsItemChange change, const QVariant &value) override;
QRectF boundingRect() const override;
void paint(
QPainter *painter,
const QStyleOptionGraphicsItem *option,
QWidget *widget
);
};
class GraphicsSceneWidget : public QGraphicsScene {
public:
explicit GraphicsSceneWidget(QObject *parent);
~GraphicsSceneWidget();
virtual void mousePressEvent(QGraphicsSceneMouseEvent *event);
};
class VectorGraphicsWidget : public QWidget {
public:
VectorGraphicsWidget(QWidget *parent);
~VectorGraphicsWidget();
private:
GraphicsSceneWidget *myGraphicsSceneWidget;
};
// implementation
GIPoint::GIPoint(
QGraphicsItem *parent,
const QPointF &position
) : QGraphicsItem(parent) {
setFlag(QGraphicsItem::ItemIsMovable, true);
setFlag(QGraphicsItem::ItemIsSelectable, true);
setPos(position);
qWarning() << "GIPoint::GIPoint() : init at " << position;
}
QVariant GIPoint::itemChange(
GraphicsItemChange change,
const QVariant &value
){
if (change == QGraphicsItem::ItemPositionChange) {
qWarning("position changed");
}
return value;
}
QRectF GIPoint::boundingRect() const {
return QRectF(pos().x(), pos().y(), 5, 5);
// return QRectF(0,0, 5, 5);
}
void GIPoint::paint(
QPainter *painter,
const QStyleOptionGraphicsItem *option,
QWidget *widget
){
(void )option;
(void )widget;
QPointF xx = scenePos();
QRectF rect = QRectF(xx.x(), xx.y(), 10, 10);
qWarning() << "painting: scenePos " << scenePos() << ", rect " << rect;
QBrush brush = QBrush(Qt::black, Qt::SolidPattern);
//painter->fillRect(rect, brush);
painter->drawRect(rect);
}
GraphicsSceneWidget::GraphicsSceneWidget(QObject *parent)
: QGraphicsScene(parent)
{}
GraphicsSceneWidget::~GraphicsSceneWidget(){}
void GraphicsSceneWidget::mousePressEvent(
QGraphicsSceneMouseEvent *event
){
GIPoint *gip = new GIPoint(Q_NULLPTR, event->scenePos());
addItem(gip);
QGraphicsScene::mousePressEvent(event);
}
VectorGraphicsWidget::VectorGraphicsWidget(QWidget *parent) :
QWidget(parent)
{
myGraphicsSceneWidget = new GraphicsSceneWidget(this);
QGraphicsView *view = new QGraphicsView(myGraphicsSceneWidget);
myGraphicsSceneWidget->setSceneRect(QRectF(0, 0, 500, 500));
QGridLayout *centralLayout = new QGridLayout;
centralLayout->addWidget(view);
setLayout(centralLayout);
myGraphicsSceneWidget->addRect(
QRectF(0, 0, 100, 100),
QPen(Qt::black),
QBrush(Qt::green)
);
view->show();
}
VectorGraphicsWidget::~VectorGraphicsWidget() {
delete myGraphicsSceneWidget;
}
int main(int argc, char **argv){
QApplication app(argc, argv);
app.setApplicationName("test");
app.setOrganizationName("myorg");
app.setOrganizationDomain("myorg.com");
QMainWindow *w = new QMainWindow();
w->resize(500, 500);
w->setCentralWidget(new VectorGraphicsWidget(Q_NULLPTR));
w->show();
return app.exec();
}
The problem is that the boundingRect() and the paint() methods is respect the coordinate system of the item, not the scene. So the solution is not to use scenePos() in both methods but 0, 0:
QRectF GIPoint::boundingRect() const {
return QRectF(0, 0, 10, 10);
}
void GIPoint::paint(
QPainter *painter,
const QStyleOptionGraphicsItem *option,
QWidget *widget
){
(void )option;
(void )widget;
QRectF rect = QRectF(0, 0, 10, 10);
qWarning() << "painting: scenePos " << scenePos() << ", rect " << rect;
QBrush brush = QBrush(Qt::black, Qt::SolidPattern);
painter->fillRect(rect, brush);
painter->drawRect(rect);
}
Although I would recommend using QGraphicsRectItem as it implements what you have done.

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 use mouseMoveEvent on paintEvent on Qt 5?

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 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"