Ignore keyboard presses on Qt inside OSG - qt

I’m newbie with Qt, and experiencing one problem I cannot deal with for, like, a month. The situation is like this:
I’ve OpenSceneGraph project (which is OpenGL) and trying to make Qt interface inside the 3d scene. I think its not necessary how I deal with that, but if someone wants to know more here is thread with more info on OSG forum (though I didnt get solution there). The problem is, when any key on keyboard is clicked, Qt controls jump around the screen and dont react on any (mouse or keyboard) events anymore. The entire program continues to work, though.
To summarize, my question is like: is there a way to make Qt widgets ignore all keypresses?
I’ve searched a lot, but couldnt find any working solution.
Thanks in advance!

Read a bit about events in Qt. There is a section about event filtering (but please don't jump straight to it :P).
SHORT ANSWER :
void Qwidget::setEnabled ( bool );
The drawback is that it disable also mouse events, change the widget style and that's a bummer.
LONG ANSWER : FILTER EVENTS
One possibility is to filter all events on the Qt application. I suppose the function which launch your Qt code looks like this (if different post here):
int main(int argc, char* argv[]){
QApplication app(argc, argv);
QWidget toplevelwidget1;
toplevelwidget1.show()
//stufff
return app.exec();
}
//doesnt have to exactly like this.
you can to set an event filter on app variable. It is the more elegant solution but it is too complicated because it filters native events and will require some work...
What you can do instead is filter only your top level widgets or windows (the one without parents). You define an event filter (which is a QObject) like :
class KeyboardFilter: public QObject
{
Q_OBJECT
...
protected:
bool eventFilter(QObject *obj, QEvent *event);
};
bool KeyboardFilter::eventFilter(QObject *obj, QEvent *event)
{
//for all events from keyboard, do nothing
if (event->type() == QEvent::KeyPress ||
event->type() == QEvent::KeyRelease ||
event->type() == QEvent::ShortcutOverride ||
) {
return true;
} else {
// for other, do as usual (standard event processing)
return QObject::eventFilter(obj, event);
}
}
Then you set the filter on the desired widgets using:
myDesiredWidgetorObject->installEventFilter(new KeyboardFilter(parent));
And that's it!

Related

Close Widget Window if mouse clicked outside of it

This is sort of a chicken and egg problem. I'd like my widget window to be closed when the mouse clicks outside. As I understand it, there will be no mouse events for my widget for a click occurring outside of it. There is a SetFocus slot, but where is its counterpart or focus loss? "focusOutEvent" doesn't get called for my class.
My widget window is a child window of a widget always shown on my main window and it's a "Qt::ToolTip", so I assume some problems could arise from that fact. Any way around that?
My Goal: I have a custom toolbar widget where buttons on it may have “drop down” widgets. These drop down widgets have no standard windows frame. I don’t want them to “steal” caption focus from the main window and I want them to disappear as soon as the user clicks ANYWHERE on the screen outside of their region. I have having serious difficulties finding a strategy that’s not compromise on Qt to get this done.
Am I missing something? (bet I am).
I used:
setWindowFlags(Qt::FramelessWindowHint | Qt::Popup);
This seems to work well on OSX and Windows. My window appears correctly, does not steal the focus from my main window's caption, and the focus loss event is called correctly as soon as I click outside of it.
If your widget could have focus, and 'steal' the caption focus of some of your other widgets, it would have been easier. Something like this could work:
class ToolBarWidget : public QWidget
{
Q_OBJECT
public:
explicit ToolBarWidget(QWidget * parent = 0)
{
setFocusPolicy(Qt::ClickFocus);
}
protected:
void focusOutEvent(QFocusEvent * event)
{
close();
}
}
And when you create any of your widgets you'd do:
ToolBarWidget * pWidget = new ToolBarWidget(this);
pWidget->show();
pWidget->setFocus();
Done! Well, I guess not quiet. first, you don't want the ToolBarWidget to get any focus in the first place. And second, you want for the user to be able to click anywhere and the ToolBarWidget to be hidden.
So, you may keep track of every ToolBarWidget that you create. For example, in a 'QList ttWidgets' member variable. Then, whenever you create a new ToolBarWidget, you'd do this:
ToolBarWidget * pWidget = new ToolBarWidget(this);
pWidget->installEventFilter(this);
pWidget->show();
and in your main widget class, implement the eventFilter() function. Something like:
bool MainWidget::eventFilter(QObject *obj, QEvent *event)
{
if (event->type() == QEvent::FocusOut ||
event->type() == QEvent::KeyPress ||
event->type() == QEvent::MouseButtonPress)
{
while (!ttWidgets.isEmpty()) {
ToolBarWidget * p = ttWidgets->takeFirst();
p->close();
p->deleteLater();
}
}
return MainWidget::eventFilter(obj, event);
}
And that will work. Because this way, even though your ToolTabWidgets aren't getting focus, some other widget in your main widget has focus. And once that changes (whether the user clicked out of your window, or on another control inside it, or in this case, a key or mouse button is pressed, the control will reach that eventFilter() function and close all your tab widgets.
BTW, in order to capture the MouseButtonPress, KeyPress etc. from the other widgets, you would either need to installEventFilter on them too, or just reimplement the QWidget::event(QEvent * event) function in your main widget, and look for those events there.
you can do this by using QDesktopWidget.h like this
void MainWindow::on_actionAbout_triggered()
{
AboutDialog aboutDialog;
//Set location of player in center of display
aboutDialog.move(QApplication::desktop()->screen()->rect().center() -aboutDialog.rect().center());
// Adding popup flags so that dialog closes when it losses focus
aboutDialog.setWindowFlags(Qt::Popup);
//finally opening dialog
aboutDialog.exec();
}
This is what worked for me in order to not steel the focus from the main application:
.h
bool eventFilter(QObject *obj, QEvent *event) override;
.cpp
bool Notification::eventFilter(QObject *obj, QEvent *event)
{
if(event->type() == QEvent::MouseButtonPress)
deleteLater();
return QObject::eventFilter(obj, event);
}
...
// somewhere else (i.e. constructor, main window,...)
qApp->installEventFilter(this);
OP's own answer is great for Qt versions below 4.8, but as they mention in their answer, it does not work for versions above that. The Qt::Popup widget will not disappear when the mouse is clicked outside of the widget, and it will sink all of the input that would normally close it.
Upon further investigation, this is only an issue for non-dialog widgets. A QDialog using Qt::Popup will properly close when the user clicks outside of it, but any other QWidget, like a QFrame, will not. So in order to work around this behavior change in Qt 4.8, all that is necessary is to wrap the widget in a QDialog.

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.

calling Qt's QGraphicsView::setViewport with a custom QGLWidget

I've derived from QGLWidget before, like so:
class MyGLWidget : public QGLWidget
{
public:
// stuff...
virtual void initializeGL() { /* my custom OpenGL initialization routine */ }
// more stuff...
};
However, I find that if I try to initialize a QGraphicsView with my custom QGLWidget as the viewport, initializeGL doesn't get called (setting a breakpoint within the Qt library, neither does QGLWidget::initializeGL() when created plain).
// initializeGL, resizeGL, paintGL not called
ui.graphicsView->setViewport(new MyGLWidget(QGLFormat(QGL::DoubleBuffer)));
// initializeGL, resizeGL, paintGL *still* not called
ui.graphicsView->setViewport(new QGLWidget(QGLFormat(QGL::DoubleBuffer)));
Where is the correct location to place the code that currently resides in MyGLWidget::initializeGL()?
The setupViewport slot of a custom QGraphicsView could be used to call updateGL() on the QGLWidget, which will cause initializeGL() to be called.
class MyGraphicsView : public QGraphicsView
{
//... The usual stuff
protected slots:
virtual void setupViewport(QWidget *viewport)
{
QGLWidget *glWidget = qobject_cast<QGLWidget*>(viewport);
if (glWidget)
glWidget->updateGL();
}
};
So what I've found is QGraphicsView installs a custom eventFilter on your QGLWidget viewport so it never sees the initialize/resize/repaint events. This probably was done to make it work properly with drawBackground() etc.
My current best resolution is to catch the desired event either in QGraphicsView::resizeEvent()/etc, or install a custom eventFilter on your QGLWidget derived class to catch the resize/paint/etc events before QGraphicsView's custom eventFilter swallows them.
The pain, the pain, ... integrating widgets derived from QGlWidgets into QGraphicsView is no fun, of the parts of Qt that I know this is definitely one of the messier areas. I ended up using a part of kgllib (out of kde) called widgetproxy that is a very decent wrapper around a QGlWidget. I modified it to fit my needs but works reasonably well for most general cases where you want to use an exisiting class derived from QGlWidget inside a QGraphicsView and draw other things on top of it.
initializeGL() won't get called until the first call to either paintGL() or resizeGL() and not when the widget is constructed. This may happen as late as when the widget is first made visible.
I'm going to go ahead and answer my own question. This isn't optimal, but this is how I've gotten around the problem.
Instead of
ui.graphicsView->setViewport(new MyGLWidget(QGLFormat(QGL::DoubleBuffer)));
I've got this instead:
ui.graphicsView->setViewport(new QGLWidget(new CustomContext(QGLFormat(QGL::SampleBuffers))));
CustomContext is a class that derives from QGLContext. I've overridden the create member, like so:
virtual bool create(const QGLContext *shareContext = 0)
{
if(QGLContext::create(shareContext))
{
makeCurrent();
/* do my initialization here */
doneCurrent();
return true;
}
return false;
}
I don't think this is the optimal way to do this, but it's better than the alternative of not having a specific initialization step at all. I'd still be happy to have someone leave a better answer!

Get a notification/event/signal when a Qt widget gets focus

In Qt, when a widget receives focus, how can get a notification about it, so I can execute some custom code? Is there a signal or an event for that?
You can add en event filter.
This is an example of an application written with QtCreator. This form has a QComboBox named combobox.
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),ui(new Ui::MainWindow)
{
ui->setupUi(this);
ui->comboBox->installEventFilter(this);
.
.
.
}
bool MainWindow::eventFilter(QObject *object, QEvent *event)
{
if (event->type() == QEvent::FocusOut)
{
if (object == ui->comboBox)
{
qWarning(object->objectName().toLatin1().data());
}
}
return false;
}
There is a "focusChanged" signal sent when the focus changes, introduced in Qt 4.1.
It has two arguments, he widget losing focus and the one gaining focus:
void QApplication::focusChanged(QWidget * old, QWidget * now)
Qt Designer isn't designed for this level of WYSIWYG programming.
Do it in C++:
class LineEdit : public QLineEdit
{
virtual void focusInEvent( QFocusEvent* )
{}
};
The simplest way is to connect a slot to the QApplication::focusChanged signal.
I'd have to play with it, but just looking at the QT Documentation, there is a "focusInEvent". This is an event handler.
Here's how you find information about.... Open up "QT Assistant". Go to the Index. Put in a "QLineEdit". There is a really useful link called "List of all members, including inherited members" on all the Widget pages. This list is great, because it even has the inherited stuff.
I did a quick search for "Focus" and found all the stuff related to focus for this Widget.
You have hit on of the weird splits in QT, if you look at the documentation focusInEvent is not a slot it is a protected function, you can override it if you are implementing a subclass of your widget. If you you just want to catch the event coming into your widget you can use QObject::installEventFilter it let's you catch any kind of events.
For some odd reason the developers of Trolltech decided to propagate UI events via two avenues, signals/slots and QEvent
Just in case anybody looking for two QMainWindow focus change .
You can use
if(e->type() == QEvent::WindowActivate)
{
//qDebug() << "Focus IN " << obj << e ;
}
QWidget::setFocus() is slot, not signal. You can check if QLineEdit is in focus with focus property. QLineEdit emits signals when text is changed or edited, see documentation.

Resources