PDA

View Full Version : contextMenuEvent has issues since 4.1.3



momesana
27th May 2006, 23:11
Ok, so I have written this application for gentoo Linux that lets users edit the runlevels. In order to do so they have to rightclick custom widgets upon which a QMenu will pop up ... The widget with the active context menu has a red border. I put in the red border to highlight the widget and create a visual distinction between the active widget and the other ones.

Now, here is the problem: in previous versions, when I rightclicked a widget and then immediately made another rightclick on a second widget, the highlighted borders around the first widget would dissappear along with the Popupmenu and only the second one was highlighted (having the red border). But since I've upgraded qt to 4.1.2/4.1.3 the border of the previous widget will remain highlighted and the contextMenuEvent() will not completely finish, until I do a leftclick etc.

If I insert some print statements, I see that the contextMenuEvent function will remain unfinished and when the leftclick etc. occurs, all pending contextMenuEvents finish. Since this was not the case with previous versions (I have compiled the app with qt-4.1.0 and there it doesn't have this issue) , I assume that is a bug with qt. Can anybody confirm that or tell me where I am doing something wrong?

I can post a link to the tarball if you wish.


Thanx in advance
momesana

wysota
27th May 2006, 23:53
Please post a minimal compilable example which reproduces the problem.

momesana
29th May 2006, 15:36
Hi,
as you requested, below is the minimal code reproducing the issue. Just save it in a directory of its own as "main.h" and execute qmake -project && qmake && make . Try this with Qt version <= Qt-4.1.1 and >=Qt-4.1.2 to see the difference in behaviour.


#include <QApplication>
#include <QWidget>
#include <QLayout>
#include <QScrollArea>
#include <QPainter>
#include <QMenu>
#include <QContextMenuEvent>
#include <QAction>
#include <iostream>

// --------------- WIDGET ---------------- //

class Widget : public QWidget
{
Q_OBJECT
public:
Widget();
protected:
void contextMenuEvent(QContextMenuEvent *event);
void paintEvent(QPaintEvent * event);
private:
bool contextM;
};


Widget::Widget() : QWidget()
{
contextM = false;
}

void Widget::paintEvent(QPaintEvent *event)
{
QPainter p(this);
p.setRenderHint(QPainter::Antialiasing);
contextM ? p.setPen(QColor(255,0,0)) : p.setPen(QColor(120,120,120)) ;
p.drawRoundRect(QRectF(1,1,width()-2,height()-2), 2, 40);
}

void Widget::contextMenuEvent (QContextMenuEvent *event)
{
contextM = true;
update();

QMenu contextMenu;
QAction *act = new QAction( "Dummy Action" ,this);
contextMenu.addAction(act);
contextMenu.exec(event->globalPos());

std::cout << ": contextMenu.exec() just finished" << std::endl;

contextM = false;
update();
}

// -------------- WIDGETS ---------------- //

class Widgets : public QScrollArea
{
public:
Widgets();
private:
QVBoxLayout *layout;
};

Widgets::Widgets() : QScrollArea()
{
resize(600,100);
QWidget *w = new QWidget(this);
setWidget(w);
setWidgetResizable(true);

layout = new QVBoxLayout(w);
layout->addWidget(new Widget);
layout->addWidget(new Widget);
}

int main(int argc, char* argv[])
{
QApplication app(argc, argv);
Widgets mw;
mw.show();
return app.exec();
}

thanx in advance
momesana

momesana
30th May 2006, 01:24
Ok, I tested the code with following selfcompiled versions of Qt:
4.0.1
4.1.0
4.1.1
4.1.2
4.1.3

All versions prior to 4.1.3 had the previous behaviour, which is not perfect either. With those version doing the second rightclick will close the first context menu (exit the contextMenuEvent function) but will not enter the second contextMenuEvent on the current clicked widget. This way, you end up clicking twice in order to get the second context menu. This is a little annoying. The approach with 4.1.3 is more convinient from this perspective since doing the second rightlick will close the first contextMenu and open the second one so there is only one click required. On the other hand, there is this issue with the first contextMenuEvent not completely finishing ...

I'd really appreciate it if someone tells me what I'm doing wrong. Right now, my workaround is pretty ugly. The contextMenuEvent emits a signal that is connected to a removeHighlight() slot in the parent widget. The parentwidget then searches through all child widgets and gives asks them to remove their highlighted borders if they are not the active widget. It works but is quite ugly...

jacek
30th May 2006, 02:36
bool Widget::eventFilter( QObject *obj, QEvent *event )
{
QMenu *menu = qobject_cast< QMenu* >( obj );
if( menu != 0 && event->type() == QEvent::Hide ) {
menu->deleteLater();
contextM = false;
update();
}
return QWidget::eventFilter( obj, event );
}

void Widget::contextMenuEvent ( QContextMenuEvent *event )
{
...
QMenu *contextMenu = new QMenu();
contextMenu->installEventFilter( this );
...
contextMenu->popup( event->globalPos() );
}Instead of using an event filter, you can subclass QMenu and make it emit a signal when it's hidden.

momesana
31st May 2006, 10:23
bool Widget::eventFilter( QObject *obj, QEvent *event );
...


Thank you very much. It worked pretty well. Anyway, isn't that a bug in Qt? Shouldn't it do that automatically without an eventhandler?

thanx
momesana

jacek
31st May 2006, 16:05
Shouldn't it do that automatically without an eventhandler?
What should it do automatically? Set your contextM variable to false?

You want to perform an action when the popup menu is hidden and normally Qt doesn't send any signal when this happens. That's why you have to use an event filter or subclass QMenu.

momesana
31st May 2006, 17:48
What should it do automatically? Set your contextM variable to false?

That's not what I mean. I have programmed the contextMenuEvent in a way that it sets contextM to false and calls update when the QMenu::exec()/QMenu:: popup() returns. When I rightclick on another widget, and the QMenu of the previous highlighted Widget dissapears and the new one pops up, I really expect that the first QMenu::exec() returns so the contextM is set to true and update() is called. What bothers me, is that the QMenu::exec() does not really return in this case. Its a mere hiding of the QMenu hindering the contextM variable to be set to false in the frist place. until I do a left click etc, after which the popup()/exec() really exits.

jacek
31st May 2006, 18:21
I really expect that the first QMenu::exec() returns so the contextM is set to true and update() is called. What bothers me, is that the QMenu::exec() does not really return in this case.
You're right, it would be nice if QMenu::exec() would return immediately after the menu is hidden. Maybe you should send your example program to the Trolls?

momesana
31st May 2006, 18:27
You're right, it would be nice if QMenu::exec() would return immediately after the menu is hidden. Maybe you should send your example program to the Trolls?
I wanted to do that a few days ago, but then I was not sure, whether I am doing something wrong myself. But I will file a bug report now. :)

Thank you