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
I have two separate threads.
First thread for GUI, and second for application data.
Initially, I wanted to use QUndoStack and QUndoView.
But there was a problem - this view works directly with the stack:
https://code.woboq.org/qt5/qtbase/src/widgets/util/qundoview.cpp.html#_ZN10QUndoModel20setStackCurrentIndexERK11QModelIndex
In this case I got race condition.
To solve this problem I wrote custom myUndoView using QListView and QAbstractListModel.
Now all my slots using queued connections and I store a lightweight copy of the "real" undo stack in the custom view model.
This is same size and same order of the "real" undo stack elements.
A lightweight element contains only type of the undo command and text.
Now I have another problem. I'm not blame for this ))
I have a QLineEdit that emits signal on value changed when I click Enter key or lost focus.
This value in turn is sent to object (app model) with "real" undo stack. It works.
But this does not work when I interact with undo view too.
Repeat, I'm not blame for this. QUndoView has the same behavior.
Step by step:
QLineEdit in focus.
Changing value, still in focus.
Click the mouse in the undo view.
Oops.. currentIndexChanged() signal from undo view can be sent first,
or signal from QLineEdit can be sent first.
It always differs ..
If signal from QLineEdit was sent first - it works correctly.
The history of changes not lost.
I want to make enter/blur and other changes (not in history view) always invoked first. Probably I can use QTimer::singleShot() for delay of emit undo view signals . But not curentIndexChanged() because this signal emit with user interactions and when undo stack updated programmatically. We can not determine who make changes - user or application.
What I tried?
Intercept mouse clicks:
myUndoView::mousePressEvent(QMouseEvent *event)
{
event->ignore();
qDebug() << "catched!";
}
But sometimes it loses the clicks.
At the bottom of the list item (under the letters) is an area that pass a click to the item.
This may be a Qt bug, found in my environment: Debian, Mate, GTK+ Qt-style.
I think, I can place another transparent widget over list, and get coordinates of the click and use it:
http://doc.qt.io/qt-5/qabstractitemview.html#indexAt
to get the selected index.
Or I make all wrong?
Maybe there is an easier way?
How to make it right?
I would try blocking the list model signals while the line edit is focused.
Let's have an event filter like this:
class EventFilter : public QObject
{
Q_OBJECT
public:
EventFilter(QObject * model) : _model(model){}
bool eventFilter(QObject *watched, QEvent *event);
private:
QObject * _model;
};
which keeps a private reference to the list model as a pointer to QObject, passed in constructor argument.
The filter implementation:
bool EventFilter::eventFilter(QObject *watched, QEvent *event)
{
if(event->type() == QEvent::FocusIn)
{
_model->blockSignals(true);
}
return false;
}
Keep a reference to an instance of the filter in the window class (Form, in my example), along with the list model instance reference:
private:
EventFilter * filter;
QAbstractListModel * model;
The filter has to be instantiated and installed in line edit, in Form constructor (don't forget to delete it in the destructor):
filter = new EventFilter(model); //the model is passed to the filter in construction
ui->lineEdit->installEventFilter(filter);
At this point, model events will be blocked when the line edit gets focus. To unlock them, use the line edit editingFinished slot:
void Form::on_lineEdit_editingFinished()
{
model->blockSignals(false);
}
This is my Context Menu after right click on QPlainTextEdit. I want to add function to load data from file in Context Menu. Can I? How?
Method 1: QPlainTextEdit::contextMenuEvent
You should override the QPlainTextEdit::contextMenuEvent as mentioned in the Qt documentation:
void MyQPlainTextEdit::contextMenuEvent(QContextMenuEvent *event)
{
QMenu *menu = createStandardContextMenu();
menu->addAction(tr("My Menu Item"));
//...
menu->exec(event->globalPos());
delete menu;
}
You can connect the QAction::triggered signal to your method (slot) to load the data or you can use one of the QMenu::addAction overloads, which allows you to specify a slot directly.
If you do not want to subclass QPlainTextEdit (to override contextMenuEvent), you can use event filtering in Qt.
Note that contextMenuEvent() is only called when contextMenuPolicy is not set (or set to its default value Qt::DefaultContextMenu)
Method 2: QWidget::customContextMenuRequested
As an alternative, you can use Qt's signal and slot mechanism to create the context menu when requested by the user.
The contextMenuPolicy property should be set to Qt::CustomContextMenu, in which case the QWidget::customContextMenuRequested signal is invoked whenever a context menu is requested by the user. This signal should be connected to your own slot, which should create the context menu as shown in the code above (Method 1).
Using MyQPlainTextEdit in Qt Designer
To use your MyQPlainTextEdit in a .ui file, you should implement it as a promoted QPlainTextEdit and use it in your .ui file instead of a regular QPlainTextEdit. See the Qt documentation for more information.
To be able to use your class in the Qt Designer, you should not forget to implement a constructor accepting a parent QWidget as is done in the AnalogClock example. Note that implementing such a constructor is always a good idea, because Qt typically manages ownership through a child-parent relationship.
Building on #m7913d answer.
The downside to the techniques is you must derive from the QPlainTextEdit class for a very minor extension. My preferred method, especially when using designer based widgets, is to add an eventFilter, and filter out the mouse event that is a mousebutton press
MyWidget::MyWidget(...)
{
...
ui->plainTextEdit->installEventFiler( this )
}
MyWidget::eventFilter( QObject * obj, QEvent * event )
{
if ( ( obj == ui->plainTextEdit )
&& ( event->type() = QEvent::MouseButtonPress )
&& ( dynamic_cast< QMouseEvent * >( event )->buttons() & Qt::MouseButton::RightButton )
{
// create menu
auto menu = ui->plainTextEdit->createStandardContextMenu();
// modify menu
menu->exec( mouseEvent->globalPos() );
delete menu;
return true;
}
return false;
}
I currently works on QT for my project. I implemented a MainWindow class which inherited from QMainWindow.
In MainWindow, I handled mouse wheel event like this:
void MainWindow::wheelEvent ( QWheelEvent * event )
{
if (event->modifiers().testFlag(Qt::ControlModifier)) {
if (event->delta() > 0) {
zoomInAct();
}
else if(event->delta()<0){
zoomOutAct();
}
}
}
The problem is: when I press CONTROL KEY and Wheel the mouse, the scroll bar alway scroll to top or bottom before reach my wheelEvent function. Would you please help to allow zoom-in/out when press control and wheel the mouse? (Not scroll the scroll bar)
Sorry for my bad english.
Looking at your current implementation here you have not specified event->accept() if you have added your own functionality and you don't want to propagate the event further to the parent. Try adding event->accept() in the if conditions where you have added your logic.
And try adding debug statement to test whether the event is reaching here or someone else is handling the event. By someone else I mean some other child widget. Some description of the UI and what widget is to be zoomed in would be helpful to further investigate the problem.
Make sure you read about the event system in Qt. Docs available here
Acctualy, there is a child widget that handle the wheel event first (default event handle is scroll the scrollbar).
Solution: override wheelevent in child widget to send it to parent widget (MainWindow) when the control key is pressed.
class WorkArea: public QWidget {
...
virtual void wheelEvent(QWheelEvent *event)
{
if(event->modifiers().testFlag(Qt::ControlModifier))
MainWindow::wheelEvent(event);
else
QWidget::wheelEvent(event);
}
...
}
I am using
void QGraphicsItem::installSceneEventFilter(QGraphicsItem * filterItem);
to set an event filter on a QgraphicsItem (see itemChanged() in QGraphicsItem for a many different items)
Now, for some of these items, I'd like to restrict the movement, i.e. change the x and y position of the item so that the user would be restricted in some area in the object move.
I first tried to modify the event with:
(static cast <QGraphicsSceneMouseEvent*>(event))->setPos(QPoint(150, watched->y()));
The whole handler beiing:
bool generic_graphic_item::sceneEventFilter(QGraphicsItem* watched, QEvent* event)
{
if(event->type() == QEvent::QEvent::GraphicsSceneMouseMove)
{
(static_cast<QGraphicsSceneMouseEvent*>(event))->setPos(QPointF(150, watched->y()));
//emit my_item_changed(watched); // signal that the item was moved
emit(item_pos_changed(watched, watched->x(), watched->y()));
}
return false; // pass the event to the original target item
}
But it did not work. I was not really sure either about the specific event class hiding behind a QEvent::GraphicsSceneMouseEvent.
I then tried to call watched->setX() and watched->setY() within the event handler, but that was not very popular... which I can understand...
Is it possible to restrict the movement within the scene event handler?
I have read that QGraphicsItem::itemChange() can be used to do that, but then I am back into the problem described in 'itemChanged() in QGraphicsItem for a many different items', i.e. how can I have this common to many items without subclassing each of them...
many thanks,
The code you post in this question is responding to the event of the moving of the mouse. For what you're describing that you want to do, I suggest you check for the event of a widget being moved with QEvent::GraphicsSceneMove: -
if(event->type() == QEvent::GraphicsSceneMove)
{
// set the position of the item.
}
I have a QMainWindow with an event filter installed.
After I open and close a QDialog the keyboard arrow keys don't respond since QMainWindow receives only ShortcutOverride events instead of KeyPress events.
When I changed the QMainWindow's event filter to handle ShortcutOverride events I got a strange behavior since in my program each key press event is preceded by two shortcut override events (why??).
This doesn't work - events are handled more than once:
bool eventFilter(QObject *, QEvent *event) {
if(type == QEvent::KeyPress || type == QEvent::ShortcutOverride) {
QKeyEvent *keyEvent = static_cast<QKeyEvent *>(event);
switch(keyEvent->key()) {
case Qt::Key_Up:
case Qt::Key_Down:
case Qt::Key_Left:
case Qt::Key_Right:
// Do something here
break;
default:
break;
}
return true;
}
return false;
}
Why is this happening? Where do those ShortcutOverride events come from?
Edit:
As it turns out, QMainwindow loses focus when the QDialog is opened and never gains it back when the QDialog is closed.
I used setFocus() in the eventFilter of QMainwindow and it seems to do the trick.
Lost focus is exactly the reason for not getting any keyboard events after a dialog opens up. The solution you suggested is probably the best fix for this issue.
As for ShortcutOverride events, they come for every keypress because any key or combination of keys can be registered as a shortcut. This event gives you an option to cancel shortcut handling, if needed. It has to be processed at the earliest possible moment when the engine doesn't know yet if the pressed key is going to be used as a shortcut or not. Thus the event is generated for all key presses "just in case". You may read more here: https://wiki.qt.io/ShortcutOverride
It is happens when a key press in child is made. It is used to for overriding shortcut key handling (QKeyEvent).
refer http://qt-project.org/doc/qt-4.8/qevent.html#Type-enum