为什么按下“Tab”键只发出QEvent :: ShortcutOverride事件? [英] Why pressing of "Tab" key emits only QEvent::ShortcutOverride event?

查看:1213
本文介绍了为什么按下“Tab”键只发出QEvent :: ShortcutOverride事件?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

嗨家伙。我已经做了一个自定义窗口小部件与 QLineEdit 和几个 QPushButtons 使用它与自定义项委托:

Hi guys. I've made a custom widget with QLineEdit and several QPushButtons to use it with custom item delegate:

class LineEditor : public QWidget
{
public:
    explicit LineEditor(QWidget *parent = 0) : QWidget(parent) {
        setLayout(new QHBoxLayout);

        layout()->setContentsMargins(0, 0, 0, 0);
        layout()->setSpacing(0);

        QLineEdit *edit = new QLineEdit(this);
        layout()->addWidget(edit);
        layout()->addWidget(new QPushButton(this));
        layout()->addWidget(new QPushButton(this));

        setFocusProxy(edit);
    }
};

class PropertyDelegate : public QItemDelegate
{
public:
    QWidget *createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const {
        return new LineEditor(parent);
    }

    bool eventFilter(QObject *object, QEvent *event) {
        if (event->type() == QEvent::KeyPress) {
            qDebug() << "KeyPress";
        }
        if (event->type() == QEvent::ShortcutOverride) {
            qDebug() << "ShortcutOverride";
        }

        return QItemDelegate::eventFilter(object, event);
    }
};

我将使用 QListView QStandardItemModel 像这样:

QStandardItemModel *model = new QStandardItemModel;
model->appendRow(new QStandardItem("1"));
model->appendRow(new QStandardItem("2"));
model->appendRow(new QStandardItem("3"));

QListView w;
w.setItemDelegate(new PropertyDelegate);
w.setModel(model);
w.show();



问题



c $ c> PropertyDelegate :: eventFilter 当 Tab 键被按下时只有 QEvent :: ShortcutOverride 事件,但按任何其他键会发出 QEvent :: ShortcutOverride QEvent :: KeyPress

Question

Why in the PropertyDelegate::eventFilter when Tab key is pressed there is only QEvent::ShortcutOverride event, but pressing of any other key emits both QEvent::ShortcutOverride and QEvent::KeyPress events?

UPD:我要通过按 Tab code> Backtab 。

UPD: I want to implement the moving between lines by pressing Tab and Backtab like with standard widgets.

推荐答案

一些研究。

当视图调用 createEditor 的委托,它还将委托事件过滤器安装到编辑器。

When a view calls createEditor function of delegate it also installs the delegate event filter to editor.

QWidget *QAbstractItemViewPrivate::editor(const QModelIndex &index,
                                          const QStyleOptionViewItem &options)
{
    Q_Q(QAbstractItemView);
    QWidget *w = editorForIndex(index).widget.data();
    if (!w) {
        QAbstractItemDelegate *delegate = delegateForIndex(index);
        if (!delegate)
            return 0;
        w = delegate->createEditor(viewport, options, index);
        if (w) {
            w->installEventFilter(delegate);

    ......
}

委托只能捕获编辑器小部件的事件,但不能捕获其子代的事件。当按下 Tab 键时,调用 QWidget :: event 函数,它使用它来将焦点更改为另一个窗口小部件:

However the delegate can catch only events of editor widget, but not events of its children. When the Tab key is pressed the QWidget::event function is called, it uses it to change focus to another widget:

bool QWidget::event(QEvent *event)
{
    ......

    switch (event->type()) {
        ......
        case QEvent::KeyPress: {
        QKeyEvent *k = (QKeyEvent *)event;
        bool res = false;
        if (!(k->modifiers() & (Qt::ControlModifier | Qt::AltModifier))) {  //### Add MetaModifier?
            if (k->key() == Qt::Key_Backtab
                || (k->key() == Qt::Key_Tab && (k->modifiers() & Qt::ShiftModifier)))
                res = focusNextPrevChild(false);
            else if (k->key() == Qt::Key_Tab)
                res = focusNextPrevChild(true);
            if (res)
                break;
        }

        ......
    }

    ......
}

因此,在我的情况下,焦点设置为 QPushButton $ c> QLineEdit 且事件不会传播到父级( LineEditor )。

Accordingly in my case the focus is set to next QPushButton after QLineEdit and event isn't propagated to the parent (LineEditor).

正确的解决问题的方法是 QSpinBox 。因为它也有 QLineEdit 。在widget的构造函数中,它为行编辑设置焦点代理:

The right way to solve the problem is like QSpinBox does it. Because it is also has QLineEdit. In constructor of widget it sets focus proxy for line edit:

edit->setFocusProxy(this);

所有的焦点事件都会到达主窗口。还必须设置 focusPolicy 属性,因为它默认为 NoFocus

So all of focus events will reach the main widget. Also the focusPolicy property must be set because it's NoFocus by default:

setFocusPolicy(Qt::WheelFocus);

现在我们需要做的是将必要的事件传播到 QLineEdit 从主要部件,像这样:

All we need to do in this moment it is to propagate necessary events to QLineEdit from main widget like this:

bool LineEditor::event(QEvent *e)
{
    switch(e->type())
    {
    case QEvent::ShortcutOverride:
        if(m_lineEdit->event(e))
            return true;
        break;

    case QEvent::InputMethod:
        return m_lineEdit->event(e);

    default:
        break;
    }

    return QWidget::event(e);
}

void LineEditor::keyPressEvent(QKeyEvent *e)
{
    m_lineEdit->event(e);
}

void LineEditor::mousePressEvent(QMouseEvent *e)
{
    if(e->button() != Qt::LeftButton)
        return;

    e->ignore();
}

void LineEditor::mouseReleaseEvent(QMouseEvent *e)
{
    e->accept();
}

void LineEditor::focusInEvent(QFocusEvent *e)
{
    m_lineEdit->event(e);
    QWidget::focusInEvent(e);
}

void LineEditor::focusOutEvent(QFocusEvent *e)
{
    m_lineEdit->event(e);
    QWidget::focusOutEvent(e);
}

这应该就够了。

如上所述,代理无法捕获编辑孩子的事件。所以为了使编辑器的行为像native,我必须从孩子到编辑器重复事件。

As it's said above the delegate can't catch events of editor's children. So to make editor's behavior like "native" I have to duplicate events from children to editor.

LineEditor 在构造函数中安装事件过滤器到 QLineEdit

LineEditor installs event filter to QLineEdit in constructor:

edit->installEventFilter(this);

过滤器的实现方式如下:

Implementation of filter looks like this:

bool LineEditor::eventFilter(QObject *object, QEvent *event)
{
    if(event->type() == QEvent::KeyPress)
    {
        QKeyEvent* keyEvent = static_cast<QKeyEvent *>(event);
        if(keyEvent->key() == Qt::Key_Tab || keyEvent->key() == Qt::Key_Backtab)
        {
            QApplication::postEvent(this, new QKeyEvent(keyEvent->type(), keyEvent->key(), keyEvent->modifiers()));
            // Filter this event because the editor will be closed anyway
            return true;
        }
    }
    else if(event->type() == QEvent::FocusOut)
    {
        QFocusEvent* focusEvent = static_cast<QFocusEvent *>(event);
        QApplication::postEvent(this, new QFocusEvent(focusEvent->type(), focusEvent->reason()));

        // Don't filter because focus can be changed internally in editor
        return false;
    }

    return QWidget::eventFilter(object, event);
}

也可以使用 qApp-> notify (这个事件)代替而不是 QApplication :: postEvent 事件反正。但是 QFocusEvent 是不可能的,因为 notify 会重定向该事件,它不会到达一个孩子。

It's also possible to use qApp->notify(this, event) for QKeyEvent instead of QApplication::postEvent because we filter this events anyway. But it's not possible for QFocusEvent because notify will redirect the event and it will not reach a child.

请注意,标准( QItemDelegate QStyledItemDelegate )委托会关心焦点在内部自行改变的情况:

Note that the standard (QItemDelegate or QStyledItemDelegate) delegate will care about situation when focus is changed internally by itself:

if (event->type() == QEvent::FocusOut || (event->type() == QEvent::Hide && editor->isWindow())) {
        //the Hide event will take care of he editors that are in fact complete dialogs
        if (!editor->isActiveWindow() || (QApplication::focusWidget() != editor)) {
            QWidget *w = QApplication::focusWidget();
            while (w) { // don't worry about focus changes internally in the editor
                if (w == editor)
                    return false;
                w = w->parentWidget();
            }

    ......

这篇关于为什么按下“Tab”键只发出QEvent :: ShortcutOverride事件?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆