Determine source of QKeyEvent - c++

I have a Qt application with multiple widgets showing buttons at the same time. In certain circumstances, I want key presses to be sent to one of the widgets, even if that widget doesn't have focus. To do this, I have overridden keyPressEvent() in the master widget (that owns all subwidgets in this application) and re-send the key event to the subwidget if it doesn't have focus using code similar to this:
if (!someWidget->hasFocus())
{
QApplication::sendEvent(someWidget, keyEvent);
}
This works great as long as someWidget handes said event. If it ignores it, then it enters a nasty infinite recursive loop since events flow up to parents.
Is there a way to know where an event came from so I can prevent this infinite loop? I'm thinking of something like this:
if (!someWidget->hasFocus() && (keyEvent->source != someWidget))
{
QApplication::sendEvent(someWidget, keyEvent);
}
Or is there a different way I can prevent this from happening?

When you use signals and slots mechanism you can call sender() which can give you information, but here you can do next: use eventFilter which can give you information about every QObject which sends events to mainWindow, so you can catch event and sender
bool MainWindow::eventFilter(QObject *obj, QEvent *event)
{
if(event->type() == QEvent::KeyPress)//your keyPressEvent but with eventFilter
if(!someWidget->hasFocus() && obj != someWidget)//your focus and source checkings, obj is object which send some event,
// but eventFilter catch it and you can do something with this info
{
//do something, post event
}
return QObject::eventFilter(obj, event);
}
Don't forget
protected:
bool eventFilter(QObject *obj, QEvent *event);
Maybe you need use QKeyEvent, so cast QEvent if you sure that event->type() == QEvent::KeyPress. For example:
QKeyEvent *key = static_cast<QKeyEvent*>(event);
if(key->key() == Qt::Key_0)
{
//do something
}

Related

check when a button is clicked event qt

I am a beginner in Qt and I would like to know, how can I create an event which checks when a button in the windows gets clicked? The window has a single push button.
I could not find a simple and correct example for my question, that's why I am asking here. I did google first.
Thank you
class MyClass : public QWidget
{
public:
MyClass ()
{
QPushButton *pButton = new QPushButton(this);
QObject::connect(pButton, &QPushButton::clicked, this, &MyClass::onButtonClicked);
}
public slots:
void onButtonClicked () {qDebug () << "Button clicked";};
};
onButtonClicked will be called if the button pressed.
Please see signals & slots and QPushButton signals for more information.
Qt employs the signals and slots paradigm. You don't check if a button is clicked. When a button is clicked, it emits a signal. You can connect your own functions to that signal. So whenever that button is clicked, the signal is emitted, and triggers all the functions it is connected to.
Be sure to check out signals and slots, which is a core concept of Qt, along with properties and meta data.
Note that as a QWidget derived, a button comes with the following methods, which are triggered by mouse events and you can override them if you wish, although in the case of something as a button that is hardly necessary:
virtual void mouseDoubleClickEvent(QMouseEvent * event)
virtual void mouseMoveEvent(QMouseEvent * event)
virtual void mousePressEvent(QMouseEvent * event)
virtual void mouseReleaseEvent(QMouseEvent * event)

Qt : catched mouseMoseEvent dont interact with QWebView html page element

I catch the mouseMoveEvent of my QWebView for restarting a timer of a screensaver. The problem is that now the mouseMoveEvent isnt distribued to the HTML elements so its impossible for example to move a sliding bar on the page.
I use connect to bind mouseMoveEvent to the restart slot :
QObject::connect(_view, SIGNAL(mouseMoveEvent(QMouseEvent*)), _mediaPlayer, SLOT(stop()));
WebView class :
class WebView : public QWebView
{
Q_OBJECT
public:
WebView(QString menu) : _menuDesc(menu) {};
WebView(){};
void setMenuDesc(QString menu) {_menuDesc = menu;};
QString getMenuDesc() {return _menuDesc;};
void setCurrPage(QString page) {_currPage = page;};
QString getCurrPage() {return _currPage;};
void setCurrCategory(QString page) {_currPage = page;};
QString getCurrCategory() {return _currPage;};
void mouseMoveEvent(QMouseEvent *)
{
emit mouseMoved();
};
signals :
void mouseMoved();
private:
QString _menuDesc = 0;
QString _currPage;
QString _currCategory = 0;
};
Is there a solution to still catch the signal and pass it to the HTML page?
mouseMoveEvent is not a signal but an event handler. You can reimplement this event handler and let it emit a signal you can connect to if you need that.
Like this:
MyWebView::mouseMoveEvent(QMouseEvent * e) {
emit mouseMoved(); // this would be signal you could connect to.
}
Seems you misunderstand event handler and signal usages.
mouseMoveEvent is a member method of QWidget, is not a signal so you cannot connect to it. You can override it in you subclass and emit your own signal.
And if a QWidget's mouse tracking is switched off, mouse move events only occur if a mouse button is pressed while the mouse is being moved. Maybe you need to call setMouseTracking too.

itemDoubleClicked signal is not emitted when mouseDoubleClickEvent is implemented

I'm implementing a class that inherits the QTreeWidget,
I'm trying to do something only when the user left-clicks on an item.
Since itemDoubleClicked only gives you the item and not the mouse event,
and mouseDoubleClickEvent only gives you the mouse event with no item,
so I thought I would add a member in the class and record whether left or right button was pressed in mouseDoubleClickEvent,
then check that info when entering the slot connected to signal itemDoubleClicked.
That is, if the signal is emitted after the event handler. I was planning on experimenting if this was true, but then I ran into this issue.
Ok, back to the class, it looks something like this:
class myTreeWidget : public QTreeWidget{
Q_OBJECT
private:
Qt::MouseButton m_button;
public:
myTreeWidget(QWidget* parent):QTreeWidget(parent){
m_button = Qt::NoButton;
connect(this, SIGNAL(itemDoubleClicked(QTreeWidgetItem*, int)),
this, SLOT(slot_doubleClick(QTreeWidgetItem*,int)));
}
void mouseDoubleClickEvent(QMouseEvent* event){
m_button = event->button();
}
public slots:
void slot_doubleClick(QTreeWidgetItem* item, int column);
signals:
void itemDoubleClicked(QTreeWidgetItem* item, int column);
}
Yep, something like this.
Then I used gdb to check which was called first,
mouseDoubleClickEvent or slot_doubleClick,
and it turns out that slot_doubleClick was not called at all.
I commented out mouseDoubleClickEvent and tried again,
and slot_doubleClick was called.
So um... what I'm asking here is...
is this a limitation in Qt?
Can I only choose one between signals&slots and event handlers?
Or am I just doing it wrong?
Moreover, if this is a limitation,
can you recommend another solution to what I'm trying to do?
(only respond to left double-clicks)
Sorry for the long post and thanks!
If you override some event handler and want also default behavior, you should call base handler implementation. For example try this:
void mouseDoubleClickEvent(QMouseEvent* event){
m_button = event->button();
QTreeWidget::mouseDoubleClickEvent(event);
}

Qt4 qwidget after shown event

I need to handle an event that is fired after widget is fully visible like after show event. But I cannot find any event like that in Qt4. I already tried the solutions suggested below. But none of them worked.
My aim: I am working on an embedded system and I am using Qt for UI. What I am trying to do is to show hardware accelerated camera on UI after CustomWidget is shown. If I use showEvent, camera is shown before CustomWidget is fully drawn.
It seems that showEvent is fired before widget is fully drawn. It behaves like before show event.
Failure-1
bool CustomWidget::event(QEvent *event)
{
bool returnValue = QWidget::event(event);
if (event->type() == QEvent::Polish)
{
this->camera->show();
}
return returnValue;
}
Polish event is called once. When I hide and show the widget again and again, it never fired.
Failure-2
void CustomWidget::showEvent(QShowEvent *event)
{
QWidget::showEvent(event);
QTimer::singleShot(0, this, SLOT(dialogExec));
}
void CustomWidget::dialogExec()
{
this->camera->show();
}
This did not work either.
Failure-3
void CustomWidget::paintEvent( QPaintEvent *event )
{
QWidget::paintEvent( event );
if( !this->camera->isVisible() )
{
this->camera->show();
}
}
void CustomWidget::hideEvent( QHideEvent *event )
{
this->camera->hide();
}
QWidget has protected members called closeEvent( QCloseEvent* event ) and showEvent( QShowEvent* event ). Maybe you can use these methods to manage the camera.

Prevent a QMenu from closing when one of its QAction is triggered

I'm using a QMenu as context menu. This menu is filled with QActions. One of these QActions is checkable, and I'd like to be able to check/uncheck it without closing the context menu (and having to re-open it again to choose the option that I want).
I've tried disconnecting the signals emitted by the checkable QAction with no luck.
Any ideas? Thanks.
Use a QWidgetAction and QCheckBox for a "checkable action" which doesn't cause the menu to close.
QCheckBox *checkBox = new QCheckBox(menu);
QWidgetAction *checkableAction = new QWidgetAction(menu);
checkableAction->setDefaultWidget(checkBox);
menu->addAction(checkableAction);
In some styles, this won't appear exactly the same as a checkable action. For example, for the Plastique style, the check box needs to be indented a bit.
There doesn't seem to be any elegant way to prevent the menu from closing. However, the menu will only close if the action can actually trigger, i.e. it is enabled. So, the most elegant solution I found is to trick the menu by shortly disabling the action at the moment when it would be triggered.
Subclass QMenu
Reimplement relevant event handlers (like mouseReleaseEvent())
In the event handler, disable the action, then call base class' implementation, then enable the action again, and trigger it manually
This is an example of reimplemented mouseReleaseEvent():
void mouseReleaseEvent(QMouseEvent *e)
{
QAction *action = activeAction();
if (action && action->isEnabled()) {
action->setEnabled(false);
QMenu::mouseReleaseEvent(e);
action->setEnabled(true);
action->trigger();
}
else
QMenu::mouseReleaseEvent(e);
}
To make the solution perfect, similar should be done in all event handlers that may trigger the action, like keyPressEvent(), etc...
The trouble is that it is not always easy to know whether your reimplementation should actually trigger the action, or even which action should be triggered. The most difficult is probably action triggering by mnemonics: you would need to reimplement the complex algorithm in QMenu::keyPressEvent() yourself.
This is my solution:
// this menu don't hide, if action in actions_with_showed_menu is chosen.
class showed_menu : public QMenu
{
Q_OBJECT
public:
showed_menu (QWidget *parent = 0) : QMenu (parent) { is_ignore_hide = false; }
showed_menu (const QString &title, QWidget *parent = 0) : QMenu (title, parent) { is_ignore_hide = false; }
void add_action_with_showed_menu (const QAction *action) { actions_with_showed_menu.insert (action); }
virtual void setVisible (bool visible)
{
if (is_ignore_hide)
{
is_ignore_hide = false;
return;
}
QMenu::setVisible (visible);
}
virtual void mouseReleaseEvent (QMouseEvent *e)
{
const QAction *action = actionAt (e->pos ());
if (action)
if (actions_with_showed_menu.contains (action))
is_ignore_hide = true;
QMenu::mouseReleaseEvent (e);
}
private:
// clicking on this actions don't close menu
QSet <const QAction *> actions_with_showed_menu;
bool is_ignore_hide;
};
showed_menu *menu = new showed_menu ();
QAction *action = menu->addAction (new QAction (menu));
menu->add_action_with_showed_menu (action);
Here are couple ideas I've had... Not sure at all they will work tho ;)
1) Try to catch the Event by using the QMenu's method aboutToHide(); Maybe you can "Cancel" the hide process ?
2) Maybe you could consider using an EventFilter ?
Try to have a look at : http://qt.nokia.com/doc/4.6/qobject.html#installEventFilter
3) Otherwise you could reimplement QMenu to add your own behavior, but it seems a lot of work to me...
Hope this helps a bit !
(I started with Andy's answer, so thank you Andy!)
1) aboutToHide() works, by re-popping the menu at a cached position, BUT it can also enter an infinite loop. Testing if the mouse is clicked outside the menu to ignore re-opening should do the trick.
2) I tried an event filter but it blocks the actual click to the menu item.
3) Use both.
Here is a dirty pattern to prove that it works. This keeps the menu open when the user holds down CTRL when clicking:
# in __init__ ...
self.options_button.installEventFilter(self)
self.options_menu.installEventFilter(self)
self.options_menu.aboutToHide.connect(self.onAboutToHideOptionsMenu)
self.__options_menu_pos_cache = None
self.__options_menu_open = False
def onAboutToHideOptionsMenu(self):
if self.__options_menu_open: # Option + avoid an infinite loop
self.__options_menu_open = False # Turn it off to "reset"
self.options_menu.popup(self.__options_menu_pos_cache)
def eventFilter(self, obj, event):
if event.type() == QtCore.QEvent.MouseButtonRelease:
if obj is self.options_menu:
if event.modifiers() == QtCore.Qt.ControlModifier:
self.__options_menu_open = True
return False
self.__options_menu_pos_cache = event.globalPos()
self.options_menu.popup(event.globalPos())
return True
return False
I say it is dirty because the widget here is acting as an event filter for both the button that opens the menu as well as the menu itself. Using explicit event filter classes would be easy enough to add and it would make things a little easier to follow.
The bools could probably be replaced with a check to see if the mouse is over the menu, and if not, don't pop it open. However, the CTRL key still has to be factored in for my use case, so it probably isn't far off a nice solution as it is.
When the user holds down CTRL and clicks on the menu, it flips a switch so the menu opens itself back up when it tried to close. The position is cached so it opens at the same position. There is a quick flicker, but it feels OK since the user knows they are holding a key down to make this work.
At the end of the day (literally) I already had the whole menu doing the right thing. I just wanted to add this functionality and I definitely didn't want to change to using a widget just for this. For this reason, I am keeping even this dirty patch for now.
Subclass QMenu and override setVisible. You can utilize activeAction() to know if an action was selected and the visible arg to see if the QMenu is trying to close, then you can override and call QMenu::setVisible(...) with the value you want.
class ComponentMenu : public QMenu
{
public:
using QMenu::QMenu;
void setVisible(bool visible) override
{
// Don't hide the menu when holding Shift down
if (!visible && activeAction())
if (QApplication::queryKeyboardModifiers().testFlag(Qt::ShiftModifier))
return;
QMenu::setVisible(visible);
}
};
Connect the QMenu.show to the action trigger. I know this is code for Qt5 (and in Python), but the principle should be back compatible.
from PyQt5 import QtWidgets
class CheckableMenu(QtWidgets.QMenuBar):
def __init__(self,parent=None):
super().__init__(parent)
self.menuObj=QtWidgets.QMenu("View")
self.addMenu(self.menuObj)
for i in ['Both','Even','Odd']: #my checkable items
t=QtWidgets.QAction(i,self.menuObj,checkable=True)
t.triggered.connect(self.menuObj.show)
self.menuObj.addAction(t)
Starting from baysmith solution, the checkbox didn´t work as I was expecting, because I was connecting to the action triggered(), rather than connecting to the checkbox toggled(bool). I´m using the code to open a menu with several checkboxes when I press a button :
QMenu menu;
QCheckBox *checkBox = new QCheckBox("Show Grass", &menu);
checkBox->setChecked(m_showGrass);
QWidgetAction *action = new QWidgetAction(&menu);
action->setDefaultWidget(checkBox);
menu.addAction(action);
//connect(action, SIGNAL(triggered()), this, SLOT(ToggleShowHardscape_Grass()));
connect(checkBox, SIGNAL(toggled(bool)), this, SLOT(ToggleShowHardscape_Grass()));
menu.exec(QCursor::pos() + QPoint(-300, 20));
For my use case, this works like a charm
I have been struggling with this for half a day.
There was many accepted answers on the net suggesting overriding setVisible function of the QMenu which did not work for me at all.
I found a solution based on this post (The last reply by the OP)
my C++ implementation for this matter is as follows:
bool MainWindow::eventFilter(QObject *watched, QEvent *event)
{
if (event->type() == QEvent::MouseButtonRelease)
{
auto action = static_cast<QMenu*>(watched)->activeAction();
if (action && action->isCheckable())
{
action->trigger();
return true;
}
}
return QObject::eventFilter(watched, event);
}