QRubberBand doesn't work - qt

I am using QRubberBand to draw a selection box on my QWidget that displays a QImage.
I am using the code exactly as it is shown in the documentation, but it doesn't work. I get no errors, but it does act strange. Instead of displaying a selection box when I hold my left button down and drag it across my QWidget which is painted with the QImage it just makes one of my buttons on my interface disappear and reappear based on my left button click. I should also add that the button that disappears isn't apart of the QWidget, or even apart of the parent QObject that creates the QWidget.
void Widget::mousePressEvent(QMouseEvent *event)
{
origin = event->pos();
if (!rubberBand)
rubberBand = new QRubberBand(QRubberBand::Rectangle, this);
rubberBand->setGeometry(QRect(origin, QSize()));
rubberBand->show();
}
void Widget::mouseMoveEvent(QMouseEvent *event)
{
rubberBand->setGeometry(QRect(origin, event->pos()).normalized());
}
void Widget::mouseReleaseEvent(QMouseEvent *event)
{
rubberBand->hide();
// determine selection, for example using QRect::intersects()
// and QRect::contains().
}
Any help is appreciated.
Thanks

It is totally my fault. I failed to set my pointer as NULL in my header.
Instead of this :
QRubberBand *rubberBand {rubberBand = NULL};
I just did :
QRubberBand *rubberBand;

Related

Identifying which label was clicked in qt

I have 10 Qlabels with an image on each. When i click on a label, its image should be cleared. I am able to identify which label was clicked theorotically, using the pixel clicked and size of each image. But how to use that information?
Eg. each label has dimension 100*100, the first label starting from 0,0. if pixel clicked is 250,50, i know that the third label was clicked, but how to use this to clear the label.
Im stuck.
There are a few ways how to implement it.
First. I would recommend to use a new class that inherits QLabel and overloads mouseReleaseEvent() handler where you just call clear() method. In this case the label will detect the mouse clicks itself and will clear its content internally.
class SelfClearingLabel : public QLabel
{
public:
using QLabel::QLabel;
protected:
void mouseReleaseEvent(QMouseEvent * event)
{
if (event->button()==Qt::LeftButton)
// process only clicks on the left button
{
clear();
}
QLabel::mouseReleaseEvent(event);
}
};
Second. You can catch mouseReleaseEvent() in your top widget and iterate over all of your child QLabel widgets and check which one is currently under mouse and clear the one. If you have other labels on this widget that shouldn't be cleared on mouse clicks then you can add some property to the QLabels that are under your interest.
void SomeTopFrame::createImageLabels(int count)
{
for (int i=0;i<count;i++)
{
QLabel* label=new QLabel(this);
label->setProperty("clear_on_click",true);
// assume that labels are added to layout *m_labelsLayout;
m_labelsLayout->addWidget(label);
}
}
void SomeTopFrame::mouseReleaseEvent(QMouseEvent * event)
{
if (event->button()==Qt::LeftButton)
// process only clicks on the left button
{
QList<QLabel*> labels=findChildren<QLabel*>();
foreach (QLabel* label, labels)
{
if (label->property("clear_on_click")&&label->underMouse())
{
label->clear();
break;
}
}
}
QFrame::mouseReleaseEvent(event);
}
It is a sample code just to show the principle. In production you can add a check that mouseReleaseEvent() is on the same widget as the mousePressEvent() to avoid triggering on drag and drop events.
Create the custom class that inherit QLabel :
ClickableLabel.h
class ClickableLabel : public QLabel
{
Q_OBJECT
public:
explicit ClickableLabel( const QString& text="", QWidget* parent=0 );
~ClickableLabel();
signals:
void clicked();
protected:
void mousePressEvent(QMouseEvent* event);
};
ClickableLabel.cpp
ClickableLabel::ClickableLabel(const QString& text, QWidget* parent)
: QLabel(parent)
{
setText(text);
}
ClickableLabel::~ClickableLabel()
{
}
void ClickableLabel::mousePressEvent(QMouseEvent* event)
{
emit clicked();
}
Just connect all labels clicked signal to following slot :
MyClass::labelClicked()
{
ClickableLabel *label = (ClickableLabel*)QObject::sender;
if(label)
label->clear();
}

Move QGraphicsItem only when pressing inside the shape. Otherwise, drag scene

I have a QGraphicsView with a bigger QGraphicsScene that can be dragged.
In the QGraphicsScene I have a subclassed QGraphicsItem (TestItem) that displays a QGraphicsPixmapItem, which can have random shapes.
(I don't use QGraphicsPixmapItem directly because of extra functionality to be implemented in the future)
I want this item to be movable, but only if the user presses within the shape of the item. If outside the shape, but still inside the boundingRectangle, I want the scene behind it to be dragged. This because the boundingRectangle can be much bigger than the shape and the user doesn't see it, so it would be weird trying to drag the scene near the Pixmap and it not working.
This is my subclassed item:
TestItem::TestItem(QPointF position, QPixmap testImage, double width,
double length, QGraphicsItem * parent):
QGraphicsItem(parent),
m_boundingRect(QRectF(0,0,5, 5)),
m_dragValid(false),
m_path(QPainterPath()),
mp_image(new QGraphicsPixmapItem(this))
{
setBoundingRect(QRectF(0,0,width,length));
setPos(position - boundingRect().center());
setFlag(QGraphicsItem::ItemIsMovable);
mp_image->setPixmap(testImage.scaled(width, length));
m_path = mp_image->shape();
}
QPainterPath TestItem::shape()
{
return m_path;
}
QRectF TestItem::boundingRect() const
{
return m_boundingRect;
}
void TestItem::setBoundingRect(QRectF newRect)
{
prepareGeometryChange();
m_boundingRect = newRect;
}
I've tried overriding the mouse events like this, but all it brings me is no functionality at all when outside the shape but inside the bounding rectangle
void TestItem::mousePressEvent(QGraphicsSceneMouseEvent *event)
{
if(shape().contains(event->pos()))
{
QGraphicsItem::mousePressEvent(event);
m_dragValid = true;
}
}
void TestItem::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
{
if(m_dragValid)
QGraphicsItem::mouseMoveEvent(event);
}
void TestItem::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
{
if(m_dragValid)
QGraphicsItem::mouseReleaseEvent(event);
m_dragValid = false;
}
which of course makes sense, but I wouldn't know how to implement the dragging of the scene, since it's the scene itself that sends the mouse events to the graphics item.
(My QGraphicsView is setup to DragMode QGraphicsView::ScrollHandDrag)
Anyone have ideas?
I figured it out. I only needed to add a event->ignore(); to my mouse events.
void TestItem::mousePressEvent(QGraphicsSceneMouseEvent *event)
{
if(shape().contains(event->pos()))
{
QGraphicsItem::mousePressEvent(event);
m_dragValid = true;
}
else
event->ignore();
}
void TestItem::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
{
if(m_dragValid)
QGraphicsItem::mouseMoveEvent(event);
else
event->ignore();
}
void TestItem::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
{
if(m_dragValid)
QGraphicsItem::mouseReleaseEvent(event);
else
event->ignore();
m_dragValid = false;
}
You just need to enable QGraphicsItem::ItemClipsToShape flag:
The item clips to its own shape. The item cannot draw or receive mouse, tablet, drag and drop or hover events outside its shape. It is disabled by default.

How to resize a frameless widget in Qt

I am researching on how to resize the frameless widget in Qt 5.1. Due to cross-platform consideration, Windows-related codes will not be appropriate.
Now my method is: when a user hovers mouse at the edge of the window(widget),cursor shape changes to hint the window can be resized at that time. After clicking and then drag, a rubberband is being shown to demonstrate the rectangle area that the window would resize to. Once the user releases the mouse, rubberband disappears, and the window then resize to the desired size. Have I made it clear?
Since Qt can't make drawing directly on the screen, as a makeshift, a full screenshot(put in a QLabel) is utilized to simulate the actual screen, and a rubberband can then be constructed in the full-screen QLabel. It seems to the users that they are dragging the window in the real window screen.
My problem is: after the user clicked the main widget,though a full-screen QLabel is shown, the rubberband refuses to appear. Instead, only after you release mouse first and then click-drag again can the rubberband appear.
Although I have sent one "MouseButtonRelease" event to the main widget and a "MouseMove" event to the label, it doesn't seem to work. Any hint or suggestion will be highly appreciated, thanks a lot!
(To simplify the code, any other extra codes have been removed)
mainwidget.h
class MainWidget : public QWidget
{
Q_OBJECT
public:
MainWidget(QWidget *parent = 0);
~MainWidget();
private:
QScreen *screen;
QLabel *fullScreenLabel;
QPixmap fullScreenPixmap;
QRubberBand *rubberBand;
protected:
virtual bool eventFilter(QObject *o, QEvent *e);
void mousePressEvent(QMouseEvent *e);
};
mainwidget.cpp
MainWidget::MainWidget(QWidget *parent)
: QWidget(parent)
{
this->setWindowFlags(Qt::FramelessWindowHint);
screen = QGuiApplication::primaryScreen();
rubberBand=NULL;
fullScreenLabel=new QLabel();
fullScreenLabel->installEventFilter(this);
resize(600,450);
}
MainWidget::~MainWidget()
{
delete fullScreenLabel;
}
bool MainWidget::eventFilter(QObject *o, QEvent *e)
{
if(o!=fullScreenLabel)
return QWidget::eventFilter(o,e);
QMouseEvent *mouseEvent=static_cast<QMouseEvent*>(e);
if(mouseEvent->type()==QEvent::MouseMove)
{
qDebug()<<"label mouse move: "<< mouseEvent->pos();
if(!rubberBand)
{
rubberBand=new QRubberBand(QRubberBand::Rectangle,fullScreenLabel);
rubberBand->setGeometry(QRect(this->pos(),QSize()));
rubberBand->show();
}
rubberBand->setGeometry(QRect(this->pos(),mouseEvent->pos()).normalized());
return true;
}
if((mouseEvent->button()==Qt::LeftButton)
&& (mouseEvent->type()==QEvent::MouseButtonRelease))
{
if(rubberBand)
{
rubberBand->hide();
delete rubberBand;
rubberBand=NULL;
}
return true;
}
return false;
}
void MainWidget::mousePressEvent(QMouseEvent *e)
{
if(screen)
{
fullScreenPixmap=QPixmap();
fullScreenPixmap=screen->grabWindow(0,0,0,-1,-1);
}
fullScreenLabel->setPixmap(fullScreenPixmap);
fullScreenLabel->showFullScreen();
QMouseEvent clickEvent(QEvent::MouseButtonRelease,
e->pos(),
Qt::LeftButton,
Qt::LeftButton,
Qt::NoModifier);
qApp->sendEvent(this,&clickEvent);
QMouseEvent moveEvent(QEvent::MouseMove,
this->pos(),
Qt::NoButton,
Qt::NoButton,
Qt::NoModifier);
qApp->sendEvent(fullScreenLabel,&moveEvent);
}
You may research how it's done in Qt - QSizeGrip. There are same question

how to use mouse move event for QGraphicsScene?

hey want to drag this bezier curve when mouse button is pressed and moved..
I did this:
void MainWindow::mouseMoveEvent(QMouseEvent *e)
{
qDebug()<<"in mouse move - outside if";
if((e->buttons() & Qt::RightButton) && isStart && enableDrag)
{
qDebug()<<"mouse dragging happening";
xc2=e->pos().x();
yc2=e->pos().y();
drawDragBezier(xc2,yc2);
}
}
this starts dragging when i press right button and start moving mouse in whole main window..but I want to start dragging only when I press mouse button and move mouse inside the QGraphicsScene.
how to solve this?
EDIT:
void mySubClass1::mousePressEvent(QGraphicsSceneMouseEvent *event)
{
if(event->button() == Qt::LeftButton)
{
qDebug()<<"in musubclass mouse press event: "<<event->pos().x()<<" "
<<event- >pos().y();
if(shape().contains(event->pos()))
{
currPosX=event->pos().x();
currPosY=event->pos().y();
qDebug()<<"currPosX currPosY: "<<currPosX<<" "<<currPosY;
}
}
}
And the mainwindow class is:
{
myGPath=new mySubClass1();
myScene=new QGraphicsScene;
myScene->addItem(myGPath);
ui->graphicsView->setScene(myScene);
QPointF *startPoint=new QPointF(50,50);
myPaintPath=new QPainterPath(*startPoint);
myPaintPath->quadTo(100,25,200,200);
myGPath->setPath(*myPaintPath);
}
is this the right way?
Personally, to solve this issue I'd take a different approach.
Create a class inherited from QGraphicsItem (or QGraphicsObject if you want signals and slots) to represent the bezier curve. Then implement the mouseMoveEvent of the object in this class.
class MyBezierCurve : public QGraphicsItem
{
protected:
void mousePressEvent(QGraphicsSceneMouseEvent*);
void mouseMoveEvent(QGraphicsSceneMouseEvent*);
void mouseReleaseEvent(QGraphicsSceneMouseEvent*);
};
This way, the object can detect in its mousePressEvent when the mouse is directly over one of its control points and update the control points with mouse move events until the release event occurs.
Handling the mouse events in the QGraphicsView will work, but if you introduce more bezier curves, or other objects you'll find you'll need to check which of them you need to be interacting with. Handling it in the object itself will take care of that for you.
You should subclass QGraphicsView and detect mouseMoveEvent over there.
class MyGraphicsView : public QGraphicsView
{
Q_OBJECT
...
protected:
void mouseMoveEvent(QMouseEvent *event);
...
};

Self-expanding Widget, on mouse over

I want to implement a widget (with some edit boxes and sliders) that would open beneath or next to a button ("Opener") when I hover it. The key is it's a temporary widget - as soon as it loses focus, I want it gone. Also, I want it to pop up right next to the Opener, ideally pointing an arrow to Opener.
So, it's basically a tooltip. But it needs to be a widget with buttons and sliders and stuff like that. Is there a clever way to implement it without making a custom widget and writing handlers for all the mouse and focus events and recomputing its ideal position every time I open it or the Opener moves?
class OpenerButton : public QPushButton
{
public:
OpenerButton(QWidget * parent = 0);
protected:
void enterEvent(QEvent *e);
void leaveEvent(QEvent *e);
};
OpenerButton::OpenerButton(QWidget * parent)
: QPushButton(parent)
{
//Do necessary initializations For ex:set a menu for opener button
}
void OpenerButton::leaveEvent(QEvent * e)
{
//hide the popup_menu
}
void OpenerButton::enterEvent(QEvent * e)
{
//Show the menu
//You can use animation for ex:
Popup_menu=new Popup_Dialog(this);//Popup_Dialog is a dialog containing all your widgets
QPropertyAnimation *animation = new QPropertyAnimation(Popup_menu,"geometry");
animation->setDuration(500);
animation->setDirection(QAbstractAnimation::Forward);
QRect startRect(Rect_Relative_to_Opener_Button);
QRect endRect(Shifted_Rect_Relative_to_Opener_Button);
animation->setStartValue(startRect);
animation->setEndValue(endRect);
animation->start();
}
Enterevent is called when the mouse cursor enters the widget. Similarly leaveevent when mouse cursor leaves the widget.

Resources