Intercepting Tab key press to manage focus switching manually - qt

I want to intercept Tab key press in my main window to prevent Qt from switching focus. Here's what I've tried so far:
bool CMainWindow::event(QEvent * e)
{
if (e && e->type() == QEvent::KeyPress)
{
QKeyEvent * keyEvent = dynamic_cast<QKeyEvent*>(e);
if (keyEvent && keyEvent->key() == Qt::Key_Tab)
return true;
}
return QMainWindow::event(e);
}
This doesn't work, event isn't called when I press Tab. How to achieve what I want?

The most elegant way I found to avoid focus change is to reimplement in your class derived from QWidget the method bool focusNextPrevChild(bool next) and simply return FALSE. In case you want to allow it, return TRUE.
Like other keys you get now also the key Qt::Key_Tab in keyPressEvent(QKeyEvent* event)

Reimplementing virtual bool QApplication::notify(QObject * receiver, QEvent * e) and pasting the code from my question there works.

You can achieve by using setFocusPolicy( Qt::NoFocus) property of QWidget. You can set Focus policy on widget which doesn't require tab focus. I think the reason why event handler is not calling, because Tab is managed by Qt framework internally. Please see QWidget::setTabOrder API, which is static.

You'll need to install an event filter on your main window in order to receive the events. You can use installEventFilter method for this.
Another option is to override the keyPressEvent method to handle the key presses.

Related

Generating a QMouseEvent manually within the keyPressEvent in Qt

I have overridden the contentsMousePressEvent in my listview like this.
void AppListView::contentsMousePressEvent(QMouseEvent *e)
{
AppGenericListView::contentsMousePressEvent(e);
if (e->button() == Qt::RightButton)
emit rightClicked();
emit multiSelection();
}
Here is my keyPressEvent.
void AppListView::keyPressEvent(QKeyEvent * e)
{
AppGenericListView::keyPressEvent(e);
if ((e->key() == Qt::Key_Up) || (e->key() == Qt::Key_Down))
{
QListViewItem * item = currentItem();
if (item)
{
const QRect rect = itemRect(item);
QMouseEvent mEvt(QEvent::MouseButtonPress, rect.center(), Qt::LeftButton, Qt::LeftButton);
contentsMousePressEvent(&mEvt);
}
}
}
For now, this code working fine. Please note that i'm not creating a dynamic QMouseEvent object. What i want to know is will this cause a crash in future ? Does contentMousePressEvent need a dyanamic object ? Qt doc doesn't say much about it. Any ideas ....
It won't crash, because you are not using event loop. But i think you should, for two reasons:
You are simulating mouse press event but you are not serving it to the object as a such. For example, you don't serve your fake mouse event to mousePressEvent(). So for "natural" mouse press events and your simulated one, the application will behave differently. And you may forget why is that and you may get inexplicable crashes when your code evolves.
The original system keypress event handling will be blocked by handling a mouse press event. You can't know who (what thread) will connect() to your signals emitted from overriden mouse event handler. Don't be surprised if you get a deadlock.
Such a half baked shortcuts are good only as temporary solutions. In the long run, they will shoot at your back. If you really want a shortcut, stop pretending it's a mouse event and implement a special separate method which will be called also from the "real" mouse event. If you want a real mouse event, handled properly, create a dynamic QMouseEvent and enqueue it at the event loop.
QMouseEvent* evt = new QMouseEvent(QEvent::MouseButtonPress,
rect.center(),this->mapToGlobal(rect.center()),
Qt::LeftButton, Qt::LeftButton);
QCoreApplication::postEvent(this,evt);
The event handlers don't take ownership of the events they receive. So your current code is fine.
It would be deleted automatically (and cause a crash), if you were passing it to QCoreApplication::postEvent to be sent asynchronously.

silently transfer keyPressEvent to one child , and make it focus?

When user types in a QWidget based window, I wanted a QLineEdit to process
all input keys,
so I tried the following two solution in keyPressEvent() of that QWidget:
A.
void Window::keyPressEvent (QKeyEvent *e)
{
switch (e->key())
{
// handle other short cuts
default:
QApplication::sendEvent (lineEdit , e);
break;
}
}
Well, this sometimes crashes the whole interface, especially when I resize window.
B.
void Window::keyPressEvent (QKeyEvent *e)
{
switch (e->key())
{
// handle other short cuts
default:
if ( ! lineEdit.hasFocus () )
{
lineEdit.setFocus ();
lineEdit.setText (e->key());
// i wanted to push the first key input to that QLineEdit , but how ?
// or i'll miss it
}
break;
}
}
Also I'm thinking about giving lineEdit focus all the time, but I can't do that as other events needed to be handled by the main UI.
Update
It won't crash when I filter key inputs, but why ?
default:
if ( e->key() == Qt::Key_Backspace || e->key() == Qt::Key_Delete ||
(e->key() >= Qt::Key_A && e->key() <= Qt::Key_Z )
)
QApplication::sendEvent(filter , e);
break;
}
I believe you are running into a crash because you are using sendEvent to send an event object that you don't have control over.
I don't think the Qt event system expects you to grab its events and throw them in other directions, and it's likely that the event object is getting destroyed before the line edit expects. In the case where you're filtering out input keys, it's probably not crashing because the line edit doesn't care about those kinds of key strokes and isn't using the event object as much as it would otherwise.
If you really want to use the sendEvent() functionality, then I would suggest you create your own QKeyEvent on the stack and pass it to the sendEvent() function (as demonstrated here), or you can just do something like this:
lineEdit.setText( lineEdit.text() + event->text() );
When a widget does not handle an event, it forwards it to its parent. So using sendEvent() to forward to a child is dangerous, as it can make a recursion.
The easiest way of doing it would be to use QKeyEvent::text instead of QKeyEvent::key and you should be OK. You might also try to create a copy of QKeyEvent and pass it to your QLineEdit. Thos are rather hacks than solutions though. If you need shortcuts in main window while QLineEdit has focus (assuming it is in this window) you can use QShortcut with Qt::WidgetWithChildrenShortcut context - this way you can keep your LineEdit active at all times.

Qt force QCheckBox to emit signal on setChecked

If I called QCheckBox::setChecked( x ) the toggled signal is only emitted if x is not the same as the current checkbox state. I understand the logic behind this, to avoid signaling if nothing has changed. However, in some situations where I have a more complicated widgets setup, I need the signal to always be emitted. This ensures anybody who has connected to the checkbox will receive the first state.
Is there a way to have QCheckBox::setChecked(bool) emit a signal regardless of whether the state has changed?
My simple workaround now is to just force the checkbox into multiple states by doing setChecked(!x) and setChecked(x). I was hoping for a more correct way of doing this.
Looking into the QAbstractButton implementation, I found the following lines of code:
if (!d->checkable || d->checked == checked) {
if (!d->blockRefresh)
checkStateSet();
return;
}
where checkStateSet is a virtual function. QCheckBox overrides this and emits a stateChanged() signal only if the state changed.
I haven't tested this, but I think d->blockRefresh is set to false if you call QCheckBox::setChecked( ... ) directly.
If this is the case, it means you could subclass QCheckBox and override the checkStateSet() method to something like this:
void MyCheckBox::checkStateSet()
{
QCheckBox::checkStateSet();
if( m_oldState == checkState() )
{
// emit the signal here, as QCheckBox::checkStateSet() didn't do it.
emit stateChanged( m_oldState );
}
else
{
// don't emit the signal as it has been emitted already,
// but save the old state
m_oldState = checkState();
}
}
where the header file contains
private:
Qt::CheckState m_oldState;
which must be initialised to Qt::Unchecked in the constructor.
Here is another solution which may or may not be possible for your case:
If you can be 100% sure that your signals and slots are connected before the checkbox has a chance to change its state, every connected class can initialize itself safely assuming the checkbox is not checked. This is because checkboxes are always unchecked upon construction.
This way you might not have to call setChecked() after connecting the signals.
However, This approach does not work if there is a chance a signal gets connected after the checkbox has already changed. I'm not 100% fond of this approach myself but it might be an option for you nevertheless.
One way would be to subclass QCheckBox and implement the emitting of signals in that where you need it, for example :
class MyCheckBox : public QCheckBox
{
Q_OBJECT
public:
MyCheckBox(QWidget *parent = 0) : QCheckBox(parent) {};
virtual void setChecked(bool checked) {
QCheckBox::setChecked(checked); emit checkWasSet(checked);
};
signals:
void checkWasSet(bool value);
};
Now use this class instead of the regular QCheckBox class, and you can connect to the checkWasSet() signal for whenever the check state is set.
You could emit the signal with the current state yourself:
checkbox.stateChanged.emit(checkbox.checkState())

QGraphicsView and eventFilter

This has been bugging me for more than two days now, so i thought i should ask. I am using Qt 4.5.3 (compiled with VC2008) on Win7.
I have MyGraphicsView (inherits QGraphicsView) and MyFilter (inherits QObject) classes.
When i install the MyFilter object as an event filter to MyGraphicsView, Mouse events are delivered to MyFilter after they are delivered to MyGraphicsView whereas Key events are delivered to MyFilter before they are delivered to MyGraphicsView.
In the second case, i install the MyFilter object as an event filter to MyGraphicsView->viewport() (which is a standart QGLWidget), Mouse events are delivered to MyFilter before they are delivered to MyGraphicsView, whereas Key events are delivered to only MyGraphicsView.
The events are supposed to be delivered to event filters before they are delivered to the actual object, so why is this happening? What should i do to ensure this order?
Thanks in advance.
Best Regards.
QGraphicsView is a subclass of QAbstractScrollArea which is the cause of these behaviors.
In the first case, the QAbstractScrollArea adds itself as a event filter to the MyGraphicsView when setViewport() is called. The QAbstractScrollArea's event filter captures the mouse event, first sends it through viewportEvent(), and then to the QWidget event handling which propagates to the MyGraphicsView mouse event handlers. Only after this is the QAbstractScrollArea's event filter finished and MyFilter gets to run.
In the second case, key events are delivered only to the MyGraphicsView because in setViewport() the QAbstractScrollArea sets itself as the focus proxy. If the focus proxy is reset with the following code, the key events will be delivered.
w.viewport()->setFocusProxy(0);
An alternative is to install the event filter on both the graphics view and its viewport, but modify the filter to only process key events from one object and mouse events from the other.
Change MyFilter.h
QObject *keyObj;
QObject *mouseObj;
public:
MyFilter(QObject *keyObj, QObject *mouseObj, QObject *parent = NULL);
Change MyFilter.cpp
MyFilter::MyFilter(QObject *keyObj, QObject *mouseObj, QObject *parent /*= NULL*/ ) : QObject(parent), keyObj(keyObj), mouseObj(mouseObj)
and
if (obj == keyObj && e->type() == QEvent::KeyPress)
{
qDebug()<<"Key Event recieved by MyFilter";
}
else if (obj == mouseObj && e->type() == QEvent::MouseButtonPress)
{
qDebug()<<"Mouse Event recieved by MyFilter";
}
Change main.cpp
MyFilter *filter = new MyFilter(&w, w.viewport(), &w);
// Use this line to install to the viewport
w.viewport()->installEventFilter(filter);
//Use this line to install to MyGraphicsView
w.installEventFilter(filter);
How about to try not using filter but reimplement necessary QEvent handlers at MyGraphicsView like here:
void MyGraphicsView::mousePressEvent(QMouseEvent* pe)
{
if (pe->buttons() & Qt::LeftButton)
{
this->setCursor(Qt::CrossCursor);
zoomOrigin = pe->pos();
rubberBand = new QRubberBand(QRubberBand::Rectangle, this);
rubberBand->setGeometry(QRect(zoomOrigin, QSize(0,0)));
rubberBand->show();
}
if (pe->buttons() & Qt::MidButton)
{
panOrigin = pe->pos();
this->setCursor(Qt::ClosedHandCursor);
}
}

JTextField keyevent handling issue

Doesn't .getKeyCode( ) return the key's int value? Because I have set my JTextField to listen to a keylistener, and in the keytyped method, I check what key has been pressed. Here's a snippet of my code:
JTextField jtf = new JTextField( );
jtf.addKeyListener( this );
.
.
.
public void keyTyped( KeyEvent e )
{
if( e.getKeyCode( ) == KeyEvent.VK_ENTER ) System.out.println( "pressed enter" );
}
but everytime I type enter in the JTextField, nothing happens, ie nothing prints.
maybe you should check first if you event handler is actually get called when you pressed anything... or if your event handler is able to receive KeyEvents objects, i believe the problem lies there... for it to work, the component must have focus... Java Tutorial
I think you are comparing the wrong values. e.getKeyCode() returns the key, then i guess KeyEvent.VK_ENTER is something really different.
I just figured out a better way. you should put all your key methods inside an inner class just under your main class, Put your GUI construction code in a constructor method again out side of your main method. now. have you're inner method implement the KeyListener interface, and instantiate the three abstract methods associated with said interface, you're only using the keyTyped(KeyEvent e) method. inside that method, instantiate an int variable called keyHit and assign it to e.getKeyChar(); then put an if statement that states if(keyHit == '\n') the \n will corresopnd to the ENTER key,
I tried to post a template of the code Iused to get it to work, but It was a little long to go through and indent so if you're really need it i'll send it to you via some e-mail address or something
VK_ENTER corresponds to the int value of the enter key, seeing as how it is a constant. You should be using input and action maps from the swing class to listen for the key values when the JTextField is in focus as opposed to applying a key listener to the text field and listening for one key. Because an event is created (although may or may not be handled) every time a key is pressed, you should prevent the method from running when keys that aren't the enter key are pressed. Therefore preventing runtime errors and unnecessary method calls.
You have to implement KeyListener Inteface and override the keyTyped method in your class definition to use
jtf.addKeyListener(this).
Replace the above line with
jtf.addKeyListener(new KeyAdapter(){
#Override
public void keyTyped(KeyEvent e) {
if( e.getKeyCode( ) == KeyEvent.VK_ENTER )
System.out.println( "pressed enter" );
}
});

Resources