【问题标题】:Why pressing of "Tab" key emits only QEvent::ShortcutOverride event?为什么按下“Tab”键只会发出 QEvent::ShortcutOverride 事件?
【发布时间】:2012-08-22 03:36:10
【问题描述】:

背景

我用QLineEdit 和几个QPushButtons 制作了一个自定义小部件,以便将它与自定义项目委托一起使用:

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);
    }
};

我将像这样将它们与QListViewQStandardItemModel 绑定:

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();

问题

为什么在PropertyDelegate::eventFilter 中按下Tab 键时只有QEvent::ShortcutOverride 事件,但按下任何其他键会同时发出QEvent::ShortcutOverrideQEvent::KeyPress 事件?

UPD:我想通过按TabBacktab 来实现行间移动,就像使用标准小部件一样。

【问题讨论】:

    标签: c++ qt keypress qitemdelegate qevent


    【解决方案1】:

    好吧,最后我对此进行了一些研究。

    说明

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

    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 函数被调用,它使用它来改变焦点到另一个小部件:

    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;
            }
    
            ......
        }
    
        ......
    }
    

    因此,在我的情况下,焦点设置为 QLineEdit 之后的下一个 QPushButton,并且事件不会传播到父级 (LineEditor)。

    解决

    解决问题的正确方法就像QSpinBox 一样。因为它也有QLineEdit。在小部件的构造函数中,它为行编辑设置焦点代理:

    edit->setFocusProxy(this);
    

    所以所有焦点事件都会到达主窗口小部件。还必须设置 focusPolicy 属性,因为默认情况下它是 NoFocus

    setFocusPolicy(Qt::WheelFocus);
    

    此时我们需要做的就是将必要的事件从主小部件传播到QLineEdit,如下所示:

    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);
    }
    

    这应该足够了。

    棘手

    正如上面所说,委托无法捕获编辑子节点的事件。因此,要使编辑器的行为像“本地”一样,我必须将事件从子级复制到编辑器。

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

    edit->installEventFilter(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-&gt;notify(this, event) 用于QKeyEvent 而不是QApplication::postEvent,因为无论如何我们都会过滤此事件。但QFocusEvent 不可能,因为notify 将重定向事件,它不会到达孩子。

    请注意,标准(QItemDelegateQStyledItemDelegate)委托将关心焦点在自身内部更改时的情况:

    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();
                }
    
        ......
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多