I am attempting to implement a rubberband. Here is my code.
void TOTMain::mousePressEvent(QMouseEvent * event)
{
QPoint origin = event->pos();
_selectionSquare = new QRubberBand(QRubberBand::Rectangle, this);
_selectionSquare->setGeometry(QRect(origin, QSize()));
_selectionSquare->raise();
_selectionSquare->show();
}
void TOTMain::mouseMoveEvent(QMouseEvent *event)
{
QPoint origin = event->pos();
_selectionSquare->setGeometry(QRect(origin, event->pos()).normalized());
}
void TOTMain::mouseReleaseEvent(QMouseEvent *event)
{
_selectionSquare->hide();
// determine selection, for example using QRect::intersects()
// and QRect::contains().
}
The issue is that the band is not being drawn. I confirm it is being constructed and persists through the dragging, but no rendering.
Any ideas appreciated.
Take a look at your mouseMoveEvent implementation...
void TOTMain::mouseMoveEvent(QMouseEvent *event)
{
QPoint origin = event->pos();
_selectionSquare->setGeometry(QRect(origin, event->pos()).normalized());
}
You're setting the geometry of the rubber band to QRect(origin, event->pos()), but you've previously set origin = event->pos() so the top-left and bottom-right corners of the rectangle are the same.
Make origin a member variable variable of your class (untested)...
class TOTMain: public QWidget {
public:
protected:
virtual void mousePressEvent (QMouseEvent *event) override
{
_origin = event->pos();
_selectionSquare = new QRubberBand(QRubberBand::Rectangle, this);
_selectionSquare->setGeometry(QRect(_origin, QSize()));
_selectionSquare->raise();
_selectionSquare->setStyleSheet("{ background-color : red; }");
_selectionSquare->show();
}
virtual void mouseMoveEvent (QMouseEvent *event) override
{
_selectionSquare->setGeometry(QRect(_origin, event->pos()).normalized());
}
virtual void mouseReleaseEvent (QMouseEvent *event) override
{
_selectionSquare->hide();
// determine selection, for example using QRect::intersects()
// and QRect::contains().
}
private:
QPoint _origin;
QRubberBand *_selectionSquare = nullptr;
};
Related
I have label inside custom frame.
I try to movement of MainWindow (all aplication) on mouse event:
void settingslogolabel::mouseMoveEvent(QMouseEvent *ev)
{
if ((ev->buttons() & Qt::LeftButton) && firstCIsNotNull){
window()->move( mapToGlobal(ev->pos() - m_dragPosition - this->geometry().topLeft()));
}
}
BUT! this->geometry() returns local geometry.
So, how can I get global geometry of child?
What I try to make:
When you press mouse and move - all application should move as your cursor move, until you up mouse button. I want to make this interactive for label.
Full code:
HPP:
#ifndef SETTINGSLOGOLABEL_H
#define SETTINGSLOGOLABEL_H
#include <QLabel>
#include <QWidget>
#include <QMouseEvent>
class settingslogolabel : public QLabel
{
Q_OBJECT
public:
explicit settingslogolabel(QWidget *parent = 0);
void mouseMoveEvent(QMouseEvent *ev);
void mousePressEvent(QMouseEvent *ev);
void mouseReleaseEvent(QMouseEvent *ev);
private:
QPoint m_dragPosition;
bool firstCIsNotNull = true;
private:
};
#endif // SETTINGSLOGOLABEL_H
CPP:
#include "settingslogolabel.hpp"
settingslogolabel::settingslogolabel(QWidget *parent) :
QLabel(parent)
{
}
void settingslogolabel::mouseMoveEvent(QMouseEvent *ev)
{
if ((ev->buttons() & Qt::LeftButton) && firstCIsNotNull){
window()->move( mapToGlobal(ev->pos() - m_dragPosition - this->geometry().topLeft()));
}
}
void settingslogolabel::mousePressEvent(QMouseEvent *ev)
{
if (ev->button() == Qt::LeftButton) {
m_dragPosition = ev->pos();
firstCIsNotNull = true;
}
}
void settingslogolabel::mouseReleaseEvent(QMouseEvent *ev)
{
if (ev->button() == Qt::LeftButton) {
firstCIsNotNull = false;
}
}
Not sure I fully understand the problem but, the global coordinates of the top left corner of a widget can be found -- from within that widget's member functions -- using...
mapToGlobal(QPoint(0, 0));
Similarly, the global geometry would be...
rect().translated(mapToGlobal(QPoint(0, 0)));
Edit:
If the aim is to allow dragging of the top level window then your mouseMoveEvent implementation should be something like (untested)...
void settingslogolabel::mouseMoveEvent (QMouseEvent *ev)
{
if ((ev->buttons() & Qt::LeftButton) && firstCIsNotNull) {
auto delta = ev->pos() - m_dragPosition;
window()->move(window()->pos() + delta);
}
}
I'm loading image into QImage from data in memory, I've managed successfully paint it with QPainter onto QWidget. Then I'm trying to change some pixels of this QImage to highlight rectangle area using rubber band. My problem is that I'm getting only part of the rectangle, and sometimes nothing at all (see picture below). Code .h:
class PaintWidget : public QWidget
{
Q_OBJECT
public:
using QWidget::QWidget;
void setSourceSample(Sample src) ;
void drawRectangle(OCRRectangle rect);
protected:
void paintEvent(QPaintEvent *event) override;
void mousePressEvent(QMouseEvent *ev) override;
void mouseMoveEvent(QMouseEvent *ev) override;
void mouseReleaseEvent(QMouseEvent *ev) override;
private:
QRectF target=QRectF(0, 0, 1000, 1000);
QImage p_source;
QPoint origin, end;
QRubberBand * rubberBand;
QPair<float, float> rsz_ratio;
void calcROI();
QPoint translateCoordinates(cv::Point point);
};
Code .cpp:
void PaintWidget::paintEvent(QPaintEvent *event) {
QPainter p(this);
p.drawImage(target, p_source);
}
void PaintWidget::mousePressEvent(QMouseEvent *ev) {
origin=ev->pos();
rubberBand=new QRubberBand(QRubberBand::Rectangle, this);
rubberBand->setGeometry(QRect(origin, QSize()));
rubberBand->show();
}
void PaintWidget::mouseMoveEvent(QMouseEvent *ev) {
rubberBand->setGeometry(QRect(origin, ev->pos()).normalized());
}
void PaintWidget::mouseReleaseEvent(QMouseEvent *ev) {
end=ev->pos();
rubberBand->hide();
delete rubberBand;
calcROI();
}
void PaintWidget::calcROI() {
int p1x=min(end.x(),origin.x())*rsz_ratio.first;
int p2x=max(end.x(),origin.x())*rsz_ratio.first;
int p1y=min(end.y(), origin.y())*rsz_ratio.second;
int p2y=max(end.y(), origin.y())*rsz_ratio.second;
drawRectangle(OCRRectangle(cv::Point(p1x, p1y),cv::Point(p2x, p2y)));
}
void PaintWidget::setSourceSample(Sample src) {
QImage inter=src.toQImage();
p_source=inter.convertToFormat(QImage::Format::Format_RGB32);
rsz_ratio.first=(float)src.size().width/(float)(target.width());
rsz_ratio.second=(float)src.size().height/(float)target.height();
}
QPoint PaintWidget::translateCoordinates(cv::Point point) {
QPoint qtpoint;
qtpoint.setX(point.x);
qtpoint.setY(point.y);
return qtpoint;
}
void PaintWidget::drawRectangle(OCRRectangle rect) {
QPoint p1(translateCoordinates(rect.p1)), p2(translateCoordinates(rect.p2));
for (int i=p1.x(); i<=p2.x(); i++) {
p_source.setPixelColor(i,p1.y(),QColor(255,0,0));
p_source.setPixelColor(i, p2.y(), QColor(255,0,0));
}
for (int i=p1.y(); i<=p2.y(); i++) {
p_source.setPixelColor(p1.x(),i,QColor(255,0,0));
p_source.setPixelColor(p2.x(),i,QColor(255,0,0));
}
}
UPDATE: it seems that the problem lies within rescaling of original image. When I'm drawing filled red rectangle it goes absolutely fine, my guess is that rescaling 1px border rectangle is just erasing borders. But setting thicker borders (up to 5 pixels) doesn't provide me with solution.
Making thicker borders solved this problem.
I'm trying to implement segments that can be draggable by mouse between two points (shown below where green and red lines must be draggable between black dash lines).
sample image
I can drag the lines by mouse.
As a next step I want to set a limit to this draggable area, which I'm having issues to implement with. I really appreciate if someone can help me to do this. I now explain the situation in a bit detail:
.h is:
public:
MainWindow(QWidget *parent = nullptr);
~MainWindow();
void paintEvent(QPaintEvent *e);
public slots:
void changeP1value(int);
private:
Ui::MainWindow *ui;
bool dragging = false;
QPoint p1 = QPoint(400,100);
QPoint p2 = QPoint(450,100);
QPoint p3 = QPoint(450,100);
QPoint p4 = QPoint(500,100);
QPoint *CurPoint1=nullptr;
QPoint *CurPoint2=nullptr;
protected:
virtual void mousePressEvent(QMouseEvent *event) override;
virtual void mouseReleaseEvent(QMouseEvent *event) override;
virtual void mouseMoveEvent(QMouseEvent *event) override;
};
#endif // MAINWINDOW_H
and in .cpp file:
// small helper to give us the distance
int distance(QPoint x1, QPoint x2)
{
return abs(x2.y() - x1.y());
}
// the Paint event to draw lines
void MainWindow::paintEvent(QPaintEvent *e)
{
QPainter painter1(this);
QPen pointpen(Qt::black);
pointpen.setWidth(5);
QPen linepen1(Qt::red);
linepen1.setWidth(2);
QPen linepen2(Qt::green);
linepen2.setWidth(2);
painter1.setPen(linepen1);
painter1.drawLine(p1,p2);
painter1.setPen(linepen2);
painter1.drawLine(p3,p4);
}
// when user clicks
void MainWindow::mousePressEvent(QMouseEvent *event)
{
QPoint mp = event->pos(); // where is mouse
// test if we hit the line. give user 20 pixels slack as its hard to hit one pixel
if (distance ( mp, p1) < 20 && ( mp.x() > p1.x() && mp.x() < p2.x() ) ) {
dragging = true; // flag we are dragging
CurPoint1 = &p1;
CurPoint2 = &p2;
}
else if (distance ( mp, p3) < 20 && ( mp.x() > p3.x() && mp.x() < p4.x() ) ) {
dragging = true;
CurPoint1 = &p3;
CurPoint2 = &p4;
}
}
void MainWindow::mouseReleaseEvent(QMouseEvent *event)
{
dragging = false; // if user release mouse we are not draggign anymore
}
// then when mouse move
void MainWindow::mouseMoveEvent(QMouseEvent *event)
{
// If we are dragging, call your normal slider changed function to update your points.
if (dragging)
{
if(CurPoint1->y()>50 && CurPoint1->y()<150)
{
changeP1value(event->y());
}
else
update();
}
}
void MainWindow::changeP1value(int value)
{
CurPoint1->setY(value);
CurPoint2->setY(value);
update();
}
In the mouseMoveEvent, I've condition to check if I'm dragging a line and if the hight is between 50 and 150:
if (dragging)
{
if(CurPoint1->y()>50 && CurPoint1->y()<150) //----> it get stucked and cannot be dragged down again
{
changeP1value(event->y());
}
else
update();
}
In this case I can drag , the line stops at the set limits but I cant drag down again. Can you spot an issue?
Thanks in advance
You need to check the new y value, not the current value:
void MainWindow::mouseMoveEvent(QMouseEvent *event)
{
if (dragging)
{
if(event->y() > 50 && event->y() < 150)
{
changeP1value(event->y());
}
else
update();
}
}
There's a class inherited QQuickPaintedItem and included QwtPlot (in order to use it in QML):
class QmlQwtPlot : public QQuickPaintedItem
{
Q_OBJECT
public:
QmlQwtPlot(QQuickItem* parent = 0);
~QmlQwtPlot();
void paint(QPainter *painter) override;
void mousePressEvent(QMouseEvent *event) override;
void mouseMoveEvent(QMouseEvent* event) override;
void mouseReleaseEvent(QMouseEvent* event) override;
void wheelEvent(QWheelEvent *event) override;
private:
QwtPlot* qwtPlot;
QwtPlotPanner* panner;
bool isDragging;
QPoint previousPosition;
QwtPlotPicker* picker;
};
For example, panning a plot was implemented by using mouse events handling and the QwtPlotPanner's signal :
void QmlQwtPlot::mouseMoveEvent(QMouseEvent* event) {
if (isDragging) { // if the mouse button has been pressed
QPoint currentPosition = event->pos();
QPoint diff = currentPosition - previousPosition;
emit panner->panned(diff.x(),diff.y());
previousPosition = event->pos();
update();
}
}
QwtPlotPicker is a Qwt library class providing selections on a plot canvas. QwtPlotPicker works well (a cursor with x,y coordinates of point and cross-line moving with cursor are appeared) with QWidget, but it doesn't work with QQuickPaintedItem because of different principles of work.
/* QWidget's child constructor (the same is in QQuickPaintedItem's one) */
PlottingWidget::PlottingWidget(QWidget *parent)
: QWidget(parent)
{
// ...
d_picker = new QwtPlotPicker(
QwtPlot::xBottom, QwtPlot::yLeft,
QwtPlotPicker::CrossRubberBand,
QwtPicker::AlwaysOn,
plot->canvas()
);
d_picker->setRubberBandPen( QColor( Qt::red ) );
d_picker->setTrackerPen( QColor( Qt::black ) );
d_picker->setStateMachine( new QwtPickerTrackerMachine() );
// ...
}
How to force QwtPlotPicker work with QQuickPaintedItem?
I'm currently working with Qts Chart plotting tool. I have now a plot where I can zoom in and out by using the chartview class provieded by this example (with small adjustments).
I would like to see the ability to not only zoom, but also move my view a pressed middle mouse button (which is used a lot in other applications and therefore is very intuitive).
How can I do this in Qt? How can I check if a middle mouse button is pressed and released and change my view at the plot if the mouse moves during a pressed middle mouse button...
I'm sure someone has coded this before and would really appreciate a small example/help.
you need to derive a class from QChartView and overload the mouse events:
class ChartView: public QChartView
{
Q_OBJECT
public:
ChartView(Chart* chart, QWidget *parent = 0);
protected:
virtual void mousePressEvent(QMouseEvent *event) override;
virtual void mouseMoveEvent(QMouseEvent *event) override;
private:
QPointF m_lastMousePos;
};
ChartView::ChartView(Chart* chart, QWidget *parent)
: QChartView(chart, parent)
{
setDragMode(QGraphicsView::NoDrag);
this->setMouseTracking(true);
}
void ChartView::mousePressEvent(QMouseEvent *event)
{
if (event->button() == Qt::MiddleButton)
{
QApplication::setOverrideCursor(QCursor(Qt::SizeAllCursor));
m_lastMousePos = event->pos();
event->accept();
}
QChartView::mousePressEvent(event);
}
void ChartView::mouseMoveEvent(QMouseEvent *event)
{
// pan the chart with a middle mouse drag
if (event->buttons() & Qt::MiddleButton)
{
QRectF bounds = QRectF(0,0,0,0);
for(auto series : this->chart()->series())
bounds.united(series->bounds())
auto dPos = this->chart()->mapToValue(event->pos()) - this->chart()->mapToValue(m_lastMousePos);
if (this->rubberBand() == QChartView::RectangleRubberBand)
this->chart()->zoom(bounds.translated(-dPos.x(), -dPos.y()));
else if (this->rubberBand() == QChartView::HorizontalRubberBand)
this->chart()->zoom(bounds.translated(-dPos.x(), 0));
else if (this->rubberBand() == QChartView::VerticalRubberBand)
this->chart()->zoom(bounds.translated(0, -dPos.y()));
m_lastMousePos = event->pos();
event->accept();
}
QChartView::mouseMoveEvent(event);
}
I'd like to offer a simpler version of Nicolas's mouseMoveEvent():
void ChartView::mouseMoveEvent(QMouseEvent *event)
{
// pan the chart with a middle mouse drag
if (event->buttons() & Qt::MiddleButton)
{
auto dPos = event->pos() - lastMousePos_;
chart()->scroll(-dPos.x(), dPos.y());
lastMousePos_ = event->pos();
event->accept();
QApplication::restoreOverrideCursor();
}
QChartView::mouseMoveEvent(event);
}
Also, be sure to include QApplication::restoreOverrideCursor() so the cursor returns to usual after the move is done.