背景
嗨,大家好。 我做了一个自定义窗口小部件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);
}
};
我要与束缚他们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();
题
为什么在PropertyDelegate::eventFilter
当Tab
键被按下,只有QEvent::ShortcutOverride
事件,但按任意键可以发出两个QEvent::ShortcutOverride
和QEvent::KeyPress
事件?
UPD:我想按实现线之间移动Tab
和Backtab
像标准的部件。
好了,终于我做了关于一些研究。
说明
当视图调用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;
}
......
}
......
}
因此在我的案件的焦点设置为下一个QPushButton
后QLineEdit
和事件不会传播到父( 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->notify(this, event)
的QKeyEvent
代替QApplication::postEvent
,因为无论如何,我们筛选此事件。 但它不可能QFocusEvent
因为notify
将重定向事件,也不会达到一个孩子。
需要注意的是标准的( QItemDelegate
或QStyledItemDelegate
)代表会在意的情况时,重点是内部自身改变:
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();
}
......