Qt QRubberBand selected in QTableView - qt

I'm trying to select item in QTableView, but when I press and mouse move fast. Some item not select.
I'm using QRubberBand and overide MouseMove, MousePress,MouseRealse event:
Mouse Press Event :
void tableviewTest::mousePressEvent(QMouseEvent *event)
{
QTableView::mousePressEvent(event);
mStartPoint = QPoint(event->pos().x(), event->pos().y());
mRubberBand->setGeometry (mStartPoint.x (), mStartPoint.y (), 0, 0);
mRubberBand->show ();
}
Mouse Move Events:
void tableviewTest::mouseMoveEvent(QMouseEvent *event)
{
QPoint movePoint = QPoint(event->pos ().x(), event->pos ().y ());
mRubberBand->setGeometry(QRect(mStartPoint,movePoint).normalized());
QRect rect = mRubberBand->geometry ();
if (!rect.intersects (QRect(x,y,0,0))) {
selectRow (indexAt (event->pos ()).row ());
}
this->viewport ()->update ();
mRubberBand->update ();
QTableView::mouseMoveEvent(event);
}
Mouse Release Event:
void tableviewTest::mouseReleaseEvent(QMouseEvent *inEvent)
{
mRubberBand->hide();
QTableView::mouseReleaseEvent(inEvent);
}
My question: How to select item when press and mouse move fast ? and If I wrong coding, please help me fix it.
Thanks all.

Related

Qt: How to display a text consistently on the cursor, not depending on cursor position?

I want a text to be displayed persistently on the curser event when the cursor is moving, not depending on the cursor position. I used Qtooltip for this purpose. This is the code to show the text:
Widget::Widget(QWidget *parent)
: QWidget(parent)
{
// ...
}
bool Widget::event (QEvent *ev)
{
if (event->type() == QEvent::ToolTip) {
QHelpEvent *helpEvent = static_cast<QHelpEvent *>(ev);
QToolTip::showText(helpEvent->globalPos(), "Something got it");
return false;
}
return QWidget::event(ev);
}
But when I run this code the text is not displayed consistently and it shows up only sometimes, disappears while moving the cursor, and the whole window flickers.
You can probably achieve what you want by intercepting mouse move events rather than the tool tip notifications...
class tooltip_event_filter: public QLabel {
using super = QLabel;
public:
tooltip_event_filter ()
{
setWindowFlags(windowFlags()
| Qt::BypassWindowManagerHint
| Qt::FramelessWindowHint
);
}
protected:
virtual bool eventFilter (QObject *obj, QEvent *event) override
{
if (event->type() == QEvent::MouseMove) {
/*
* Note the QPoint(1, 0) offset here. If we don't do that then the
* subsequent call to qApp->widgetAt(QCursor::pos()) will return a
* pointer to this widget itself.
*/
move(QCursor::pos() + QPoint(1, 0));
if (const auto *w = qApp->widgetAt(QCursor::pos())) {
setText(QString("widget#%1").arg((qulonglong)w));
show();
} else {
hide();
}
}
return super::eventFilter(obj, event);
}
};
Then install an instance of tooltip_event_filter on the application instance...
tooltip_event_filter tooltip_event_filter;
qApp->installEventFilter(&tooltip_event_filter);
The example shown simply displays the address of the widget under the mouse pointer as it's moved.

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.

QGraphicsView rubber band selection rectangle not visible

I have a QGraphicsView widget with lots of items on scene. I am panning the view on ctr+left mouse click and zooming it to rectangle of rubber band created with left mouse button drag. I am not able to see rubber band selection rectangle (dotted lines) whereas rubberband selection functionality works fine. Can anybody help me understand this?.I use these flags in my view:
setTransformationAnchor(QGraphicsView::AnchorUnderMouse);
setViewportUpdateMode(QGraphicsView::SmartViewportUpdate);
setRenderHints(QPainter::HighQualityAntialiasing | QPainter::SmoothPixmapTransform);
setOptimizationFlag(QGraphicsView::DontSavePainterState,true);
setCacheMode(QGraphicsView::CacheBackground);
setOptimizationFlag(QGraphicsView::DontAdjustForAntialiasing);
setViewport(new QGLWidget);
below are my event handlers.
Mouse press event handler:
void MyView::mousePressEvent(QMouseEvent* event)
{
if(event->button()==Qt::LeftButton)
{
if(event->modifiers()==Qt::ControlModifier)
{
setDragMode(QGraphicsView::NoDrag);
m_rubberBandActive = false;
mousepressed=true;
m_lastDragPos = event->pos();
return;
}
else
{
setDragMode(QGraphicsView::RubberBandDrag);
m_rubberBandOrigin = event->pos();
m_rubberBandActive = true;
}
}
event->accept();
}
else
{
QWidget::mousePressEvent(event);
}
}
Mouse move event:
void MyView::mouseMoveEvent(QMouseEvent* event)
{
if(mousepressed)
{
QPointF delta = mapToScene(event->pos()) - mapToScene(m_lastDragPos);
this->panView(delta);
m_lastDragPos = event->pos();
return;
}
event->accept();
}
Mouse release event Handler:
void MyView::mouseReleaseEvent(QMouseEvent *event)
{
if (m_rubberBandActive)
{
QPoint rubberBandEnd = event->pos();
QRectF zoomRectInScene = QRectF(mapToScene(m_rubberBandOrigin),mapToScene(rubberBandEnd));
fitInView(zoomRectInScene, Qt::KeepAspectRatio);
m_rubberBandActive = false;
}
mousepressed=false;
event->accept();
}
Pan view:
void MyView::panView(QPointF delta)
{
QPoint viewCenter(viewport()->width() / 2 + delta.x(), viewport()->height() / 2 + delta.y());
QPointF newCenter = mapToScene(viewCenter);
centerOn(newCenter);
}

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