QTableView mouseRelease event not called when drag ends, bug? - qt

I would simply like to catch the mouseRelease event when a user drags an item from a QTableView and releases the left button.
I want to highlight possible dropping zones in my app, like changing the background of a widget.
I start by detecting the drag-start by re-implementing:
void LibraryTableView::startDrag( Qt::DropActions supportedActions )
{
m_dragReleased = false;
emit dragStart();
QTableView::startDrag(supportedActions);
}
and emitting my own signal.
Now that the dropzone has changed, I need to catch the release event to redraw the dropzone as before whether the drag and drop succeded or not !
I tried different models, reimplementing the 4 mouse events and eventFilter, but the Mouse Release Event is never catched.
Here is my code:
void LibraryTableView::mouseDoubleClickEvent( QMouseEvent* event )
{
QTableView::mouseDoubleClickEvent(event);
}
void LibraryTableView::mouseMoveEvent( QMouseEvent* event )
{
qDebug() << "move";
QTableView::mouseMoveEvent(event);
}
void LibraryTableView::mousePressEvent( QMouseEvent* event )
{
qDebug() << "press";
QTableView::mousePressEvent(event);
}
void LibraryTableView::mouseReleaseEvent( QMouseEvent* event )
{
qDebug() << "real"; // Never called when drag ends ...
QTableView::mouseReleaseEvent(event);
}
So, it is a bug ?
If you know a trick, it would help me a lot.
Thanks !
Edit: I cannot reimplement dropEvent for every widget in my application, if the user drags and drop an element in another application, I still want to catch the release event ...

As said above it's been 3 years but thank to the last answer I found an even easier solution for this problem.
void LibraryTableView::startDrag( Qt::DropActions supportedActions )
{
m_dragReleased = false;
emit dragStart();
QTableView::startDrag(supportedActions);
//CODE HERE WILL BE EXECUTED ONLY WHEN THE MOUSE BUTTON HAS
//BEEN RELEASED SO YOU CAN DO THE FOLLOWING
emit dragStop();
}

Three years since this question was asked and this Qt problem still exists in Qt 5.4. Lately I had the same problem: Drops outside the application and no MouseReleaseEvent. The solution is simple and not really a trick:
The mousePressEvent, which starts the drag&drop looks like this (simplified):
void DragNDropListView::mousePressEvent(QMouseEvent *event){
....
QDrag *drag = new QDrag(this);
QMimeData *mimeData = new QMimeData;
mimeData->setData("application/x-xxxxxx", QByteArray());
drag->setMimeData(mimeData);
drag->exec();
// The d&d ended here. Inside the widget where the drag
// started, a mouseReleaseEvent would have been fired.
// Outside this widget not.
// drag->mimeData() is here still available.
}
}
}
The trick is simple: drag->exec() starts its own event loop, which is exited when the mouse button is released. The mouse position after drag->exec(); can be determined with QCursor.

For QTableView you have to reimplement three function for dnd support:
void dragEnterEvent ( QDragEnterEvent * event ) - in this function the mouse enters the widget
void QAbstractItemView::dragMoveEvent ( QDragMoveEvent * event ) - in this function you can update your drop zone highlighting
void QAbstractItemView::dropEvent ( QDropEvent * event ) - in this function you decide whether to accept the event

I encountered a similar issue and was not happy to find out that the MouseReleaseEvent was not getting fired at the end of a drag.
I just used the DragLeaveEvent and toggled my variables off, as I would have done in the MouseReleaseEvent. If user dragged off the app, and then back on, those previously toggled off variables would get re-enabled in the DragMoveEvent (assuming it is accepted).
That was my trick, at least. Hope it helps.

Related

Qt mousePressEvent left button

I am using Qt but have run into a problem.
I want to register when the mouse is pressed and released on a QTableWidget. There are signals only for pressed, not released. Therefore I use mousePressEvent and mouseReleaseEvent instead.
MyWidget.h
protected:
void mousePressEvent(QMouseEvent * event);
void mouseReleaseEvent(QMouseEvent * event);
MyWidget.cpp
void MyWidget::mousePressEvent(QMouseEvent *event)
{
qDebug() << "Pressed";
}
void MyWidget::mouseReleaseEvent(QMouseEvent *event)
{
qDebug() << "Released";
}
It prints "Released" when the left, right or middle mouse button is released over the table. However, it prints "Pressed" only when the right or middle button is pressed.
What could be wrong? How can I register when the left button is pressed?
The cell widget probably consumes the mousePressEvent on the left mouse button, so you (the parent widget) are not receiving it.
In the documentation of QMouseEvent, it is said that:
A mouse event contains a special accept flag that indicates whether the receiver wants the event. You should call ignore() if the mouse event is not handled by your widget. A mouse event is propagated up the parent widget chain until a widget accepts it with accept(), or an event filter consumes it.
Because of that, we can suppose that probably the events you mentioned are consumed by some other actors.
It would help knowing who are those actors.
I found the solution (with help from the other answers):
There was a QFrame on top of the widget that consumed the event. I didn't think of that since it worked for the other buttons. It was solved by:
ui->frame->setAttribute(Qt::WA_TransparentForMouseEvents, true);

How to process qt mouse events in parent QWidget first, and then pass it to children?

I have implemented a sort of gestures listener in the parent widget. If the mouse event is a tap ( press and release with out any move events) then the children of that widget should handle the event, if not, and the events describe a swipe, then the parent widget should handle the events. Is there any way to redirect events to the parents first, then rebroadcast them so that the appropriate child could handle it if the need arises.
I think http://qt-project.org/doc/qt-4.8/qobject.html#installEventFilter is what you are looking for. So what you do is that you set any class inheriting from QObject and set it as the event filter. So to quote from the DOCs:
An event filter is an object that receives all events that are sent to this object.
The filter can either stop the event or forward it to this object.
The example in the link is for a QKeyEvent but can obviously be adapted to use various mouse functions as well. Below is a dummy example that would 'eat' the first click on a QPushButton while if you double click, the event would go through as normal (note that in this particular example first you would call the 'eat' sequence and upon the second click in a short time the button would take over).
bool MyWidget::eventFilter(QObject *obj, QEvent *event)
{
if( obj != pushButton ) {
return QObject::eventFilter(obj, event);
}
if (event->type() == QEvent::MouseButtonPress ) {
QMouseEvent *mouseEvent = static_cast<QMouseEvent *>(event);
qDebug("Intercepted mouse click with button %d", mouseEvent->button());
return true;
} else {
// standard event processing
return QObject::eventFilter(obj, event);
}
}
Does this help?

in Qt, what QEvent means loses window focus, regain window focus? (Set transparency)

I need to set transparency when my application loses focus.
I also need to reset the transparency when it regains focus (from a mouse click or alt-tab or whatever)
I know how to set the transparency, so that is not the issue:
setWindowOpacity(0.75);
The issue is WHEN?
I agree with Kévin Renella that there are sometimes issues with QWidget::focusInEvent and QWidget::focusOutEvent. Instead, a better approach would be to implement QWidget::changeEvent():
void MyQWidget::changeEvent(QEvent *event)
{
QWidget::changeEvent(event);
if (event->type() == QEvent::ActivationChange)
{
if(this->isActiveWindow())
{
// widget is now active
}
else
{
// widget is now inactive
}
}
}
You can also achieve the same thing by installing an event-filter. See The Event System on Qt Documentation for more information.
When a QFocusEvent event occurs. Just re-implement
void QWidget::focusInEvent ( QFocusEvent * event );
void QWidget::focusOutEvent ( QFocusEvent * event );
from QWidget. Make sure to always call the super-class method before or after doing your work. i.e., (before case)
void Mywidget::focusInEvent (QFocusEvent * event ){
QWidget::focusInEvent(event);
// your code
}
But, there are sometimes issues with QWidget::focusInEvent and QWidget::focusOutEvent. See this answer for a more reliable approach.
There are sometimes issues with QWidget::focusInEvent and QWidget::focusOutEvent events of QWidget
There is an alternative using QWidget::windowActivationChange(bool state). True, your widget is active, false otherwise.

When I send or post a QMouseEvent, at the position of a QPushbutton, its clicked() signal is not emitted

I try to design a gui for an augmented reality application using the kinect. The idea is, to use the hands detected by the kinect skeleton tracking to control an application via gestures.
This question is not about gestures, as this part of my appilcation works fine.
In my application i want to simulate a mouse click whenever a click gesture is performed. To do this, i am sending two events one, mousebuttonpressed, and one mousebuttonreleased, as a normal click is also a series of press and release.
The whole thing works fine on a QWebView. In the browser window, i can "click" on links.
But for some reason i cannot "click" on a QPushButton. The clicked() signal is not emitted.
I have a short example to illustrate my problem:
First the main function:
int main(int argc, char **argv){
QApplication app( argc, argv );
QWebViewButtons mainapp( 0, Qt::Window );
app.setActiveWindow( &mainapp );
mainapp.show();
mainapp.setApplication( &app ); //this is a setter to also get access to the main application in the widget
return app.exec();
}
This is my own widget:
QWebViewButtons::QWebViewButtons(QWidget* parent, Qt::WindowFlags flags ): QWidget(parent, flags ){
this->m_ui.setupUi( this );
QObject::connect( this->m_ui.pushButton, SIGNAL(clicked(bool)), this, SLOT( buttonClicked(bool) ) );
}
void QWebViewButtons::mousePressEvent( QMouseEvent* event ){
printf("mouse click, %d, %d\n", event->pos().x(), event->pos().y() );
}
void QWebViewButtons::buttonClicked( bool clicked ){
printf("slot button clicked\n");
}
void QWebViewButtons::keyPressEvent( QKeyEvent* event ){
printf("Key pressed\n");
QPoint pos( this->width()/2, this->height()/2 );
QPoint global = this->mapToGlobal(pos);
QWidget *w = this->m_app->widgetAt(global);
//printf("widget under point of click: %s", w);
QApplication::sendEvent( w, new QMouseEvent( QEvent::MouseButtonPress, pos, Qt::MouseButton::LeftButton, Qt::MouseButton::LeftButton, Qt::KeyboardModifier::NoModifier ) );
QApplication::sendEvent( w, new QMouseEvent( QEvent::MouseButtonRelease, pos, Qt::MouseButton::LeftButton, Qt::MouseButton::LeftButton, Qt::KeyboardModifier::NoModifier ) );
}
I followed the suggestion here:
Qt Artificial mouse clicks doesnt work properly
and send to send my mouse events directly to the QPushButton. But that didn't help.
I also tried to send the mouse events to "this", or the main app.
Now I am running out of ideas.
What I want to have is, that the buttonClicked() slot is called if i press any key. But i only is called if i click the button with my mouse.
How can i accomplish this? Or is my basic idea completely false?
Thanks for your ideas.
So, do I get that right? When doing your "click-gesture" you come into keyPressEvent? If so, you can check whether the "click" has been done above the button and explicitly call the button's clicked() signal. But that's not what you want?
And what exactly is QWebViewButtons? The area the user does his gestures in?
Have you debugged into the keyPressEvent to see if your sendEvent has the correct widget (w)? I can't see why the widget should not recieve the event...
And remember that when new'ing a QMouseEvent and sending it via sendEvent is is never deleted. When using sendEvent you should create your event on the stack.
Maybe you should have a look at this thread, where a QTestEventList is recommended: Mimicking/faking a Mouse Click and Mouse Wheel using Qt. But I can imagine that you don't want test functions do the trick ;)
Ok, the trick was to really send the click to the EXACT widget at the desired click position. In my application i had some trouble because of semi transparent widgets lying over each other.
So the widgetAt(global) command wouldnt help me.
But you can use childAt(global) instead. Of course you need to know from which widget you want to find the child.
That did the trick for me. :-)

Getting MouseMoveEvents in Qt

In my program, I'd like to have mouseMoveEvent(QMouseEvent* event) called whenever the mouse moves (even when it's over another window).
Right now, in my mainwindow.cpp file, I have:
void MainWindow::mouseMoveEvent(QMouseEvent* event) {
qDebug() << QString::number(event->pos().x());
qDebug() << QString::number(event->pos().y());
}
But this seems to only be called when I click and drag the mouse while over the window of the program itself. I've tried calling
setMouseTracking(true);
in MainWindow's constructor, but this doesn't seem to do anything differently (mouseMoveEvent still is only called when I hold a mouse button down, regardless of where it is). What's the easiest way to track the mouse position globally?
You can use an event filter on the application.
Define and implement bool MainWindow::eventFilter(QObject*, QEvent*). For example
bool MainWindow::eventFilter(QObject *obj, QEvent *event)
{
if (event->type() == QEvent::MouseMove)
{
QMouseEvent *mouseEvent = static_cast<QMouseEvent*>(event);
statusBar()->showMessage(QString("Mouse move (%1,%2)").arg(mouseEvent->pos().x()).arg(mouseEvent->pos().y()));
}
return false;
}
Install the event filter when the MainWindows is constructed (or somewhere else). For example
MainWindow::MainWindow(...)
{
...
qApp->installEventFilter(this);
...
}
I had the same problem, further exacerbated by the fact that I was trying to call this->update() to repaint the window on a mouse move and nothing would happen.
You can avoid having to create the event filter by calling setMouseTracking(true) as #Kyberias noted. However, this must be done on the viewport, not your main window itself. (Same goes for update).
So in your constructor you can add a line this->viewport()->setMouseTracking(true) and then override mouseMoveEvent rather than creating this filter and installing it.

Resources