为什么按下“Tab”键只发出QEvent :: ShortcutOverride事件? [英] Why pressing of "Tab" key emits only QEvent::ShortcutOverride event?
问题描述
嗨家伙。我已经做了一个自定义窗口小部件与 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屋!