PDA

View Full Version : mouse wheel event and QListWidget: wrong behaviour?



jho
2nd January 2015, 20:23
Hi,
i have three QListWidgets: FileList, DateList and Filesize.

When I scroll inside my FileList, I want that the DateList and Filesize scrolls to the same position like the FileList.

I have a function for the wheelevent. Inside I have this code:



QScrollBar *vb = ui->listWidget->verticalScrollBar();
int v = vb->value();

QScrollBar *vbDate = ui->listWidgetDate->verticalScrollBar();
vbDate->setValue(v);

QScrollBar *vbSize = ui->listWidgetSize->verticalScrollBar();
vbSize->setValue(v);


The strange thing is that the mouse delta only changes when i reached the end of the list. Or the top of the list. And then the position of the other two lists changes.

To say it in other words: Outside of the listwidget every mouse wheel turn is recognized but inside the list widget I have to scroll down to the end or to the top.

anda_skoa
3rd January 2015, 13:54
Any reason you implement the wheel event handling?
Do you need a different behavior than the standard implementation?

Cheers,
_

jho
4th January 2015, 23:01
Hi,
I dont know if I get you right. I use the common wheelmouse function:

void MainWindow::wheelEvent(QWheelEvent *event)

In this I run the code above.

I found out one strange thing:

A listWidget without a vertical scrollbar so every item is visible: mouse delta changes after every single wheel turn.

A listWidget with a vertival scrollbar so every item is not visible: mouse delta changes when i reach the end of the list!

I simply want that the mouse delta is correctly reported like in the first case. :)

Added after 1 16 minutes:

I found a solution: I used a event filter. With the event filter the mouse wheel is recognized correctly inside a list widget.

inside the MainWindow construtor:

qApp->installEventFilter(this);

Somewhere in MainWindow:



bool MainWindow::eventFilter(QObject *obj, QEvent *event)
{
if (event->type() == QEvent::Wheel)
{
QWheelEvent *wEvent = (QWheelEvent *)event;

QScrollBar *vbDate = ui->listWidgetDate->verticalScrollBar();
QScrollBar *vbSize = ui->listWidgetSize->verticalScrollBar();

if(wEvent->delta()>0)
{
vbSize->setValue(vbSize->value()-1);
vbDate->setValue(vbDate->value()-1);
}
else
{
vbSize->setValue(vbSize->value()+1);
vbDate->setValue(vbDate->value()+1);
}
}
return false;
}


header:


bool eventFilter(QObject *obj, QEvent *event);

d_stranz
5th January 2015, 03:46
qApp->installEventFilter(this);

This means that anytime the user rolls the mouse wheel anywhere in your app (like the menu, toolbar, status bar, or any other widget), your lists are going to scroll. Are you sure that is the behavior you want? If you need an event filter at all, shouldn't you install it on something other than your QApplication instance?

jho
5th January 2015, 10:52
Of course not the best idea. I can use something like this to be sure that the mouse wheel event is only activated if i am over a certain widget:



QWidget *widget = qApp->widgetAt(QCursor::pos());
QString widgetName = widget->objectName();

if(widgetName == "listWidget")
{...

anda_skoa
5th January 2015, 10:55
Are you sure that is the behavior you want?

And I repeat my questions

Any reason you implement the wheel event handling?
Do you need a different behavior than the standard implementation?

Cheers,
_

jho
5th January 2015, 11:10
Hi,
what is the standard implementation?

I want to synchronize the scrollbar movement of 3 different listWidgets. So the bar is always at the same place. To do that I need to track the scrollbar position of one list and give the changes to the other listWidgets.

With the eventfilter wheelmouse it is working. With the "standard" mouse wheel event (void MainWindow::wheelEvent(QWheelEvent *event)) which is given by qt, it is not working. The event is emitted only when I reach the end of the list.

d_stranz
5th January 2015, 18:17
void MainWindow::wheelEvent(QWheelEvent *event)

Of course not. The wheel events you want are the ones being sent to the list views, not the main window. So install your event filter on the 3 list widgets, handle it in the main window, and update the two list widgets that are not the one who sent the event.

jho
5th January 2015, 19:04
ok, thanks a lot for the help. :)

jho
5th January 2015, 21:42
still not working. :(

the listwidget does not recognize a turn of the mouse wheel correctly. Oh thats so annoying...

d_stranz
5th January 2015, 22:17
the listwidget does not recognize a turn of the mouse wheel correctly. Oh thats so annoying...

What is it doing? What do you expect it to do?

On Windows, the Control Panel settings for the mouse wheel allow you to control the number of lines the wheel scrolls with each notch. If you are running Windows, have you checked this setting?

anda_skoa
6th January 2015, 09:38
what is the standard implementation?

Mouse wheel scrolls the view, vertically.



I want to synchronize the scrollbar movement of 3 different listWidgets. So the bar is always at the same place. To do that I need to track the scrollbar position of one list and give the changes to the other listWidgets.

Then why are you not doing that instead?
Do these views have different sizes?

Cheers,
_

jho
6th January 2015, 09:42
Changing the windows mouse settings does not change anything. And I think it is not good to change something here because every user has there own settings.

I attach two pictures to show you my problem. Maybe I can upload a simple app and its code to show my problem.

I scroll within the left listWidget and the scrollbar of the other listWidgets stays at the top because the the listWidget does not send out a wheel event.
10871

When I reach the end of the list the wheel events are being sent and the scrollbars of the other listwidgets are changing.
10872

Added after 4 minutes:


Mouse wheel scrolls the view, vertically.


Then why are you not doing that instead?
Do these views have different sizes?

Cheers,
_

The sizes are always the same. Yes, I only need to check if the scrollbar position changes. The wheel event is not working as I expect. See also my pictures. I was looking for a event filter that just check if something is changing but I could not find an appropriate filter at first glance.

http://qt-project.org/doc/qt-4.8/qevent.html

jho
6th January 2015, 16:34
I solved it now by using a timer event:

http://stackoverflow.com/questions/11651852/how-to-use-qtimer/20778020#20778020

I set the timer to 1 so it is updating all the time. In the update function I set the position of the scrollbars. Seem to work know. I know this is maybe not the right way to do but it is working.

d_stranz
6th January 2015, 16:42
I know this is maybe not the right way to do but it is working.

Absolutely the completely wrong way to do it. Even if this were a good solution, why on earth would you need to update a GUI once every millisecond?

jho
6th January 2015, 16:59
Yes, a very bad way to do. But at the moment my only solution. :( I choose 1ms so it looks smooth. I can surely choose sth like 50ms. But still the same solution. :(

Do you know a QEvent which recognizes when something changes in a widget? In my case the scrollbar position of the listWidget. If there is one then I can also use the eventfilter and install it only for the listWidgets. I tried some but with no success.

anda_skoa
6th January 2015, 22:22
Yes, a very bad way to do. But at the moment my only solution.

You could have synchronized the scrollbars. But apparently you prefer hacks.

Cheers,
_

d_stranz
7th January 2015, 03:25
In my case the scrollbar position of the listWidget.

None of us can understand why you don't do the obvious thing, as anda_skoa has suggested: just synchronize the scroll bars for your three widgets. Something as simple as this:



// Untested, use at your own risk

// MainWindow.h

// ...
private slots:
// Define a slot to handle synchronization
void synchronizeScrollbars( int value );
// ...


// MainWindow.cpp
void MainWindow::synchronizeScrollbars( int value )
{
QScrollBar * listScroll1 = list1->verticalScrollBar();
QScrollBar * listScroll2 = list2->verticalScrollBar();
QScrollBar * listScroll3 = list3->verticalScrollBar();

// Checking the current value is necessary to prevent recursion - calling setValue() results in a new valueChanged() signal
// By changing the value only if it is different from the current value, the recursion is blocked after the first change.

if ( listScroll1->value() != value )
listScroll1->setValue( value );

if ( listScroll2->value() != value )
listScroll2->setValue( value );

if ( listScroll3->value() != value )
listScroll3->setValue( value );
}

MainWindow::MainWindow( QWidget * parent )
{
// ... setupUI() , etc.

QScrollBar * listScroll1 = list1->verticalScrollBar();
QScrollBar * listScroll2 = list2->verticalScrollBar();
QScrollBar * listScroll3 = list3->verticalScrollBar();

connect( listScroll1, SIGNAL( valueChanged( int ) ), this, SLOT( synchronizeScrollbars( int ) ) );
connect( listScroll2, SIGNAL( valueChanged( int ) ), this, SLOT( synchronizeScrollbars( int ) ) );
connect( listScroll3, SIGNAL( valueChanged( int ) ), this, SLOT( synchronizeScrollbars( int ) ) );
}


No event filters, no mouse wheel handling, no nothing. Let the list widgets do their thing, and they'll tell you where the scroll bars are. The only thing you have to guarantee is that the list widgets contain the same number of rows. You could even work around this, but I don't see where it would make sense to synchronize lists with different row counts.

jho
7th January 2015, 09:10
Thanks for your help. I totally forgot that I can solve it with signals/slot. I will give it a try. I hope "valueChanged" also recognizes the position of the scrollbar.

anda_skoa
7th January 2015, 10:34
You should even be able to directly connect each scrollbar's valueChanged() signal to the other two scrollbar's setValue slot.

Cheers,
_

d_stranz
7th January 2015, 15:01
You should even be able to directly connect each scrollbar's valueChanged() signal to the other two scrollbar's setValue slot.

No, I think I tried this once. If you have a circular loop of connections from one list to the next and then back to the first, I am pretty sure this sets up a recursion. setValue() causes valueChanged() to be emitted.

I do not know if the sanity check in my slot is performed internally by the scroll bar, but if it is, then the recursion might not occur and you can directly connect the scrollbars' signals and slots. I will leave that as an exercise for the OP to investigate.

anda_skoa
7th January 2015, 18:07
setValue() causes valueChanged() to be emitted.

setValue() should only trigger valueChanged() if the value changes.
If the two scrollbars have the same range, a change of one value can be exactly mapped to the other.
The signal back would then not change anything, thus not lead to valueChanged() being emitted yet again.

Works for QSlider, which is another implementation of QAbstractSlider.
(very common to connect a slider to a spinbox and vice versa for both type and slide input)

Cheers,
_

jho
7th January 2015, 18:08
Thanks guys, It is working now without a hack. ;) And so easy to implement. :)

d_stranz
7th January 2015, 19:33
setValue() should only trigger valueChanged() if the value changes.
If the two scrollbars have the same range, a change of one value can be exactly mapped to the other.
The signal back would then not change anything, thus not lead to valueChanged() being emitted yet again.


OK, then, if that's the case I can update my code to this:



MainWindow::MainWindow( QWidget * parent )
{
// ... setupUI() , etc.

QScrollBar * listScroll1 = list1->verticalScrollBar();
QScrollBar * listScroll2 = list2->verticalScrollBar();
QScrollBar * listScroll3 = list3->verticalScrollBar();

connect( listScroll1, SIGNAL( valueChanged( int ) ), listScroll2, SLOT( setValue( int ) ) );
connect( listScroll1, SIGNAL( valueChanged( int ) ), listScroll3, SLOT( setValue( int ) ) );

connect( listScroll2, SIGNAL( valueChanged( int ) ), listScroll1, SLOT( setValue( int ) ) );
connect( listScroll2, SIGNAL( valueChanged( int ) ), listScroll3, SLOT( setValue( int ) ) );

connect( listScroll3, SIGNAL( valueChanged( int ) ), listScroll1, SLOT( setValue( int ) ) );
connect( listScroll3, SIGNAL( valueChanged( int ) ), listScroll2, SLOT( setValue( int ) ) );
}