QT mouse event handling problem - c++

Greetings all,
As seen in the picture
I have an extended QWidget object (which draws the cell images and some countour data) inside a QScrollBar.
User can zoom in/out the Image (QWidget size is changed according to the zoomed size of the QImage ) using mouse wheel.
I process the events (mouseMoveEvent(),wheelEvent()..etc) by implementing the listener methods in QWidget.
My problem is ,I can only perform zooming (and other events) when the mouse pointer is over the QWidget.
If the mouse point is over the QScrollBar (the gray area in the image) ,those events are consumed by the QScroolBar.
Any tips,
[Edit] Sorry I was refering to QScrollArea , not QScrollBar.
thanks,
umanga

I'm uncertain if you want the scroll wheel to only ever be used for zooming the image or if you want the scroll wheel to control zooming when the image is smaller than the scroll area viewport and then use the scroll wheel to do scrolling when the image is larger than the scroll area viewport. In either case, you should be able to customize how the wheel is handled with the following:
Since I've not actually tried this one, I'm not sure if it will work. The hope is that if you install an event filter and set ignore on the event, the event will still be propagated back to your image widget. This will allow you to leave your current mouse handling in the image widget intact.
bool YourImageWidget::eventFilter(QObject *obj, QEvent *event)
{
if((obj == scrollAreaPointer) && (event->type() == QEvent::Wheel))
{
if(!scrollAreaShouldHandleWheel)
{
event->ignore();
}
}
return false; // always pass the event back to the scroll area
}
The scrollAreaShouldHandleWheel flag is a boolean you would set from your image widget based on whether or not you want the scroll area to handle the wheel events.
Somewhere in your code you would install your image widget as an event filter for the scrollarea.
scrollArea->installEventFilter(imageWidget);
If this doesn't work, you can always use this filter to make sure your widget gets the event and handle it directly and then return true so that the scroll area won't be able to receive the event.

I recommend you use QGraphicsScene and QGraphicsView. The graphics framework already provides a lot of useful features (including viewport transformation). And the QGraphicsView is a scroll area.

have you done grabMouse() for Qwidget i.e for the one which you display image?

Related

Disable QWidget's mask during painting or letting mouse events pass without mask

I have a widget that is used to cover other widgets. Its goal is to block mouse events on all widgets of window but a counted ones (the uncovered widgets). The covering widget paints a semi-translucent image everywhere but over the uncovered widgets.
I succeeded in doing it by using QWidget::setMask and a composition of QRegions made from widget's rects (like ALL.rect - uncovered_widget_1.rect - unconvered_widget_2.rect - ...).
// mask_path is a member of type QPainterPath
// cover_widget is a member of type QWidget*
if (cover_widget->mask().isEmpty()) { // first time
cover_widget->setMask(cover_widget->parentWidget()->rect());
mask_path = {};
}
QPainterPath path;
path.addRect(uncovered_widget_rect);
mask_path.addPath(path);
cover_widget->setMask(cover_widget->mask().subtracted(path.toFillPolygon().toPolygon()));
Now, the specifications have changed a bit and now the holes shall have rounded corners. At a first attempt I changed the addRect with addRoundedRect but masks are binary and the corners do not always render rounded without anti-aliasing:
I can get the visual result I want by disabling the mask and using a clipping path in the paint event handler, but then the mouse events are not going through the widget.
QPainterPath path;
path.addRect(geometry());
painter.setClipPath(path.subtracted(mask_path));
I've unsuccessfully tried to ignore mouse events in the covering widget. AFAIK once an event is ignored it goes up to the parent but doesn't goes down back again to a different child (correct me if I'm wrong please).
I've seen the RenderFlag::IgnoreMask but the only place to use it is through a custom call to QWidget::render, not during standard paint flow.
Is there any way I can get such effect: smooth clipping polygons + letting mouse events to go through only in certain areas?

How do I design a swipe/slide function in Qt to select a widget?

I would like to create a GUI interface as per the attached pic
[
My main issue is the central slider widget..as you can see I would like to create a function choosing widget that the user can slide left and right then click on the desired cook function..
Unfortunately it has to be done with Qt C++ widget not QML.
There are many ways to accomplish that.
One would be arranging the small widgets next to each other manually, using setGeometry(). Overlay the complete visible area of the parent with a transparent widget. Reimplement the mouseEvents in that overlay and use the move, press click events to decide how to move the small widgets (by repeatedly calling setGeometry on them with other coordinates) or whether one has been clicked.
Should be very lightweighted and straight forward to implement and allows complete control. Would be also easy to change the sizes by calling setGeometry) with a different size to model a fluid zoom effect. E.g. to have the center widget bigger than the peripheral.
You may also have a look at QScroller which should help you with the scrolling control.
How about Qt gestures ? Haven't used it but looks like your use-case.
reference:
http://doc.qt.io/qt-4.8/gestures-overview.html
http://doc.qt.io/qt-4.8/qswipegesture.html
You could capture the gesture on your widget and do actions like:
bool ImageWidget::gestureEvent(QGestureEvent *event)
{
if (QGesture *swipe = event->gesture(Qt::SwipeGesture))
swipeTriggered(static_cast<QSwipeGesture *>(swipe));
return true;
}
void ImageWidget::swipeTriggered(QSwipeGesture *gesture)
{
if (gesture->state() == Qt::GestureFinished) {
if (gesture->horizontalDirection() == QSwipeGesture::Left) {
// highlight the right widget , you could even bring it to center
} else if (gesture->horizontalDirection() == QSwipeGesture::Right) {
// highlight the left widget , you could even bring it to center
}
}
}

Change QWidget Parent During Mouse Event

I'm trying to create a detachable type style widget, like in the way Chrome tabs are detachable (class is called Tab). I have everything working, except for a bug where sometimes (maybe 50% of the time), the Tab object never gets the mouse release event, and stops getting mouse move events.
Essentially, the detaching system works by allowing drags in the mouse press/move/release functions, just like normal. The mouseMoveEvent checks the total distance moved from the start, and if over a certain amount, will start the "detaching" process. The detaching process involves setting the parent widget to 0 (top level widget, undecorated window), so the Tab object is pretty much floating above everything, under the mouse, and continues to be dragged along with it until released.
I ran through all the QEvent items being delivered, and I found that when this issue occurs, the QEvent::MouseMove items (and all mouse events after this) are being sent to the TabBar (the Tab object's original parent). This occurs directly after calling setParent(0) on the Tab.
Basic mouse handling overview:
void Tab::mousePressEvent(*) {
[set up some boolean, start positions, etc]
}
void Tab::mouseMoveEvent(*) {
[track the updated position]
if (positionChange > STATIC_AMOUNT)
detachTab();
}
void Tab::mouseReleaseEvent(*) {
[return the Tab to its original position, and set the parent back to the TabBar]
}
void Tab::detachTab() {
QPoint mappedPos = mapToGlobal(0, 0);
setParent(0); //The loss of MouseMove events occurs when this returns.
move(mappedPos);
show();
raise();
}
Here are the events that the Tab object receives (first row is QEvent type, second is the name)
[Tab::detachTab() started]
[setParent(0) started]
QEvent::Hide
QEvent::Leave
qApp QEvent::MouseMove [ TabBar ] <-- now the TabBar is soaking up the mouse events
QEvent::HideToParent
QEvent::ParentAboutToChange
QEvent::ParentChange
[setParent(0) returned]
....
Summed up: my draggable QWidget loses QEvent::MouseMove and QEvent::MouseButtonRelease events after having its parent set to 0.
Any advice would be really appreciated!
A bit tricky workaround. I didn't test it, it's just an idea.
When your mouse hovers draggable part of a widget you may create topmost widget (let's call it Shade) with Qt::FramelessWindowHint (and possible with Qt::WA_TranslucentBackground). You may manipulate with Shade apperance via reimplementing paintEvent. For example - draw content of original widget, or draw some transparent preview, etc.
Then you may resize a Shade during dragging, to show user that widget will be detached. You will not loose mouse capture.
When user release mouse - you remember position of Shade, destroy it and detach+move original widget.
Feel free to ask, if you want more details.
Here is similar question.
So you suppose to use QDocWidget and enforce stacking of this widgets using tabifyDockWidget.

Drag a rectangle over image in Qt

I guess there are lots of ways to achieve this. I have an application in which a video stream is shown over a custom QWidget that I have subclasses from QLabel, and painting frames using QPainter. Given that, is it possible to let the user to drag a rectangle over the image and retrieve the coordinates? The requirement is that the rectangle must be visible during the dragging.
Thanks in advance,
Have a look at QRubberBand. It allows you to place such a rect on top of e.g. a QLabel. The documentation also contains an example how to move and resize the rubberband using the mouse.
the QGraphicsView has the void setRubberBandSelectionMode ( Qt::ItemSelectionMode mode ) but i dont know if the QLabel has some similar feature ...
maybe you have to draw your own rectangle while the user drags the rectangle and catch it on mouserelease
soo long zai
In you widget you could track mouse pressed and released events and track where on the widget the corners of the selection rect are. For drawing the rectangle, I'd take a look at QStyle::drawFocusRect. I think the intent of that is to draw a rect you'd be able to see regardless of what's behind it.
Or perhaps try this:
QStylePainter painter(this);
QStyleOptionFocusRect option;
option.initFrom(this);
option.backgroundColor = palette().color(QPalette::Background);
painter.drawPrimitive(QStyle::PE_FrameFocusRect, option);

A scrollbar event when scrolling?

I need an event for detecting if an user has moved the scrollbar position to another one.
In other words, if the user does scroll up/down, would it be possible to catch a signal so I can know the scroll has been changed its position?
I think it's not important, but the scrollbar I refer to is inside a QGraphicsView.
Regards.
Edit:
QGraphicsView is for displaying items in the screen, and if those items are too big it shows the scrollbars I refer to. What I need is to know when the user changes the position of those scrollbars.
Sliders have a sliderMoved(int value) signal, where value is the new position of slider.
If you need to get notified when the scroll bar position is changed, you need to subclass the QGraphicsView and reimplement the QWidget::mouseMoveEvent(QMouseEvent*). For this you also need to enable mouse tracking. Here is Qt 4.7 QGraphicsView reference.