我制作了一个子集QTableWidget
,并希望使某些单元格具有QPushButtons
作为单元格小部件,因为我已经使用QPropertyAnimations
制作了一个样式非常丰富的按钮,而实际上不是想要将小部件嵌入单元格中。好吧,我已经使用了setCellWidget(QWidget* widget)
的一部分QTableWidget
函数,它几乎是完美的。由于我在表格的单元格项目上进行了一些子类化的QStyledItemDelegate
类绘制,因此我绘制了一些似乎与单元格小部件的尺寸有些冲突的边界线。
以前,有人问过如何将QCheckBox
放在QTableWidget's
单元格的中心,以便它不会在左侧偏移。这个问题的答案本质上是这样的:
QWidget
QWidget
QH/VLayout
并将顶层QWidget's
的布局设置为此QTableWidget
上,使用setCellWidget
函数并将其设置为顶层QWidget
和voila的行和列,您将在单元格中居中QWidget
您可以根据需要进行操纵和对齐太好了,在视觉上是可行的...但是,我注意到了这种讨厌的副作用。箭头键导航似乎中断了,我不能再按Enter键或Space键来“单击/按下”我在特定单元格的管理QPushButton
中嵌入的QWidget
。似乎在尝试使用键盘上的箭头键移出单元格时,更改焦点会使QTableWidget
内部混乱。
我在网上阅读到,有人说要在设置焦点后禁用表上的setTabKeyNavigation(bool)
来解决一些类似的导航问题……这对我来说没有任何作用。我制作了一个最小的可编译示例,以显示案例的行为
TableWidget.h:
#ifndef TABLEWIDGET_H
#define TABLEWIDGET_H
#include "button.h"
#include "widget.h"
#include <QTableWidget>
#include <QDebug>
class TableWidget : public QTableWidget{
Q_OBJECT
public:
TableWidget(QWidget* parent = nullptr);
Widget *createWidget();
Button *createButton();
void setTableCell(QWidget *selecteditem);
};
#endif // TABLEWIDGET_H
TableWidget.cpp:
#include "tablewidget.h"
TableWidget::TableWidget(QWidget* parent) : QTableWidget(parent){
setFixedSize(750, 500);
setColumnCount(5);
for(int i = 0; i < 5; ++i){
insertRow(rowCount());
for(int j = 0; j < columnCount(); ++j){
QTableWidgetItem* item = new QTableWidgetItem;
item->setFlags(item->flags() ^ Qt::ItemIsEditable);
setItem(j, i, item);
}
}
setCellWidget(0, 0, createButton());
setCellWidget(2, 0, createWidget());
}
Button* TableWidget::createButton(){
Button* button = new Button;
connect(button, &Button::focusReceived, this, [this, button](){ setTableCell(button); }, Qt::DirectConnection);
return button;
}
Widget* TableWidget::createWidget(){
Button* button = new Button;
connect(button, &Button::focusReceived, this, [this, button](){ setTableCell(button); }, Qt::DirectConnection);
return new Widget(button);
}
//Helper to make keyboard focus more intuitive for cell widgets versus regular items
void TableWidget::setTableCell(QWidget* selecteditem){
//Find the sender in the table
for(int row = 0; row < rowCount(); ++row){
for(int col = 0; col < columnCount(); ++col){
if(cellWidget(row, col) == selecteditem){
qDebug() << "TableWidget::setTableCell";
setCurrentCell(row, col);
setCurrentItem(this->item(row, col));
setCurrentIndex(this->indexFromItem(this->item(row, col)));
return;
}
}
}
}
Button.h:
#ifndef BUTTON_H
#define BUTTON_H
#include <QPushButton>
#include <QFocusEvent>
#include <QDebug>
class Button : public QPushButton{
Q_OBJECT
public:
Button(QWidget *parent = nullptr);
void focusIn(QFocusEvent *event);
signals:
void focusReceived();
public slots:
bool event(QEvent* e);
};
#endif // BUTTON_H
Button.cpp:
#include "button.h"
Button::Button(QWidget* parent) : QPushButton(parent){
setStyleSheet(QString("background-color: solid rgba(255, 0, 0, 75);"));
}
bool Button::event(QEvent* event){
switch(event->type()){
case QEvent::FocusIn:
focusIn(static_cast<QFocusEvent*>(event));
return true;
break;
default:
break;
}
return QWidget::event(event);
}
void Button::focusIn(QFocusEvent* event){
qDebug() << "Button::focusIn";
emit focusReceived();
QPushButton::focusInEvent(event);
}
Widget.h:
#ifndef WIDGET_H
#define WIDGET_H
#include <QWidget>
#include <QFocusEvent>
#include <QHBoxLayout>
#include <QPointer>
#include "button.h"
class Widget : public QWidget{
Q_OBJECT
public:
Widget(Button* button, QWidget *parent = nullptr);
public slots:
bool event(QEvent* event);
void focusIn(QFocusEvent *event);
signals:
void focusReceived();
protected:
QPointer<QHBoxLayout> m_hLayout;
QPointer<Button> m_button;
};
#endif // WIDGET_H
Widget.cpp:
#include "widget.h"
Widget::Widget(Button* button, QWidget* parent) : QWidget(parent){
m_button = button;
setStyleSheet(QString("background-color: solid rgba(0, 0, 0, 0);"));
m_hLayout = new QHBoxLayout;
setLayout(m_hLayout);
m_hLayout->addWidget(m_button);
}
bool Widget::event(QEvent* event){
switch(event->type()){
case QEvent::FocusIn:
focusIn(static_cast<QFocusEvent*>(event));
return true;
break;
default:
break;
}
return QWidget::event(event);
}
void Widget::focusIn(QFocusEvent* event){
qDebug() << "Widget::focusIn";
emit focusReceived();
QWidget::focusInEvent(event);
}
因此,在导航TableWidget
时,您希望能够看到突出显示的单元格随光标移动,并且“临时”为窗口小部件提供软焦点,以进行键盘事件。这仅发生在Button
对象上。当选定的单元格包含Button
对象时,该对象将在focusIn
函数中正确打印该对象。但是,您也希望Widget
也会发生相同的行为,因为它的添加方式和代码完全相同,只是在Button
中嵌入了QHBoxLayout
。导航到Button
或Widget
后,TableWidget
的键盘导航似乎会中断,并且按键不会从Widget
转到其子{{1 }},即使我将Button
上的setFocusProxy
设置为其Widget
字段,该字段与可以正确获取m_button
的类型完全相同的Button
。我不确定这是否是错误或是否已纠正某些行为。
答案 0 :(得分:0)
嗯,事实证明,此行为是由QAbstractButton::keyPressEvent(QKeyEvent *e)
函数引起的。因此,当将按钮添加到TableWidget
的单元格窗口小部件时,我相信该表将显式成为其父窗口小部件。这对于内存管理是有意义的,没事的。
因此,QAbstractButtons
有一个Qt::Key_Down
和Qt::Key_Up
键的case语句,它们实际上检查其父窗口小部件是否为QAbstractItemView*
类型。如果这样做,他们将调用QAbstractButtonPrivate::move(int key)
函数。在查询所有按钮的列表之后,这将确定按钮的buttonGroup
的一部分(我不确定我们是否可以控制任何东西),哪个按钮应该在下一个。显然,这是在QAbstractItemView
继承的QTableWidget
中,按照列的主要顺序对其进行排序,这样,如果在下一行或下一列中有一个按钮,则将在下一行中选择该列如果Qt::Key_Down
被捕获,则为行。这解释了我正在接受的行为。为什么他们将其作为基本行为打败了我,因为我很奇怪我无法更改此行为,因为它是QAbstractButtonPrivate
类的一部分,我们大家显然都无法控制。幸运的是它是virtual
,我们可以覆盖此行为。
因此解决方案是实现Button::keyPressEvent(QPressEvent *e)
并在Qt::Key_Down
和Qt::Key_Up
的每种情况下进行覆盖,以发出将被Table
捕获并返回的信号。在任何一种情况下都不要break
并调用基类实现。发出emit
标识符时,就像QAbstractButtonPrivate::move(int key)
函数对表所做的一样,并将您添加到Table
的插槽中的选定单元格/当前项目设置为下一个项目/小部件向右/向左或向上/向下移动,就像您期望的QTableWidgetItems
的正常按键行为一样。这似乎可行,我终于可以通过正确的选择行为来集中关注Widget中嵌入的按钮。