gpt4 book ai didi

c++ - 为什么按下 "Tab"键只会发出 QEvent::ShortcutOverride 事件?

转载 作者:可可西里 更新时间:2023-11-01 18:20:44 37 4
gpt4 key购买 nike

背景

我用 QLineEdit 和几个 QPushButtons 制作了一个自定义小部件,以将其与自定义项目委托(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);
}
};

我将像这样用 QListViewQStandardItemModel 绑定(bind)它们:

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 事件,但按下任何其他事件key 发出 QEvent::ShortcutOverrideQEvent::KeyPress 事件?

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

最佳答案

好吧,我终于对此做了一些研究。

说明

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

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

......
}

然而,委托(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);
}

这应该足够了。

棘手

正如上面所说的,委托(delegate)不能捕获编辑器子级的事件。因此,为了使编辑器的行为像“本地”一样,我必须将事件从 child 复制到编辑器。

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

对于QKeyEvent也可以使用qApp->notify(this, event)代替QApplication::postEvent,因为我们过滤了this反正事件。但是对于 QFocusEvent 是不可能的,因为 notify 将重定向事件并且它不会到达 child 。

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

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

......

关于c++ - 为什么按下 "Tab"键只会发出 QEvent::ShortcutOverride 事件?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/12145522/

37 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com