在QTableWidget表格中显示一个QCheckBox:
第一种方法,QTableWidgetItem这个对象有CheckState属性,既能显示QCheckBox,又能读取状态。
QTableWidget *tableWidget = new QTableWidget;
QTableWidgetItem *checkBox = new QTableWidgetItem();
checkBox->setCheckState(Qt::Checked);
tableWidget ->setItem(2, 2, checkBox);
以上代码就在tablewidget中显示一个QCheckBox。但是如何判断复选框是否被选中呢,方法是利用QTableWidget::cellChanged()函数,检查单元格内容的变化,然后连接此信号,在槽函数中检测checkBox的状态。
connect(tableWidget, SIGNAL(cellChanged(int,int)), this, SLOT(changeTest(int, int)));
void changeTest(int row, int col)
{
if(tableWidget ->item(row, col)->checkState() == Qt::Checked) //选中
…
else
…
}
不过这样的QCheckBox是左对齐的。如果想要让居中对齐就还是需要使用第二种方法。而且可以设置我们想要的checkbox的样式
第二种方法:void QTableWidget::setCellWidget(int row, int column, QWidget *widget),事实证明使用这个函数正常显示CheckBox没有问题
关于状态判断,可以借助 void setObjectName(const QString &name),通过 objectName,去设置和获取行列号
pCheckBox->setObjectName(QString("%1_%2_%3_Itme").arg(table->objectName()).arg(row).arg(column));
拆解objectName,就可以获取到行和列。
QString objectName = check->objectName();
QStringList nameList = objectName.split("_");//拆解
if(nameList.count() == 4)
{
QString tableName = nameList.at(0);//表格名称
int row = nameList.at(1).toInt();//行
int column = nameList.at(2).toInt();//列
bool checked = check->isChecked();//是否被选中
//知道了表格、行、列,就可以执行我们所需要的操作了。。。
qDebug() << QString("%1表第%2行第%3列是否被选中:%4")
.arg(tableName).arg(row).arg(column).arg(checked?"是":"否");
}
有个坑需要注意:
当行列发生变化了,QCheckBox 不在原来的行和列了,需要同步更新 setObjectName,否则通过ObjectName 获取到的行和列是不正确的,严重的可能引起越界导致的崩溃问题
详细代码如下:
#include <QCheckBox>
#include <QTableWidget>
#include <QHBoxLayout>
#include <QDebug>
#include <QStyleFactory>
//获取CheckBox
QCheckBox* getCheckBox(QTableWidget*table,int row ,int column)
{
QWidget* pWidget = 0;
pWidget = table->cellWidget(row,column); //找到单元格
QCheckBox *pCheckBox = 0;
bool bNew = true;
if(pWidget != 0) //
{
bNew = false;
}
if(bNew)
{
pWidget = new QWidget(table); //创建一个widget
QHBoxLayout *hLayout = new QHBoxLayout(pWidget); //创建布局
pCheckBox = new QCheckBox(pWidget);
//根据objectName ,去拆解所属的tableWidget 、行、列
pCheckBox->setObjectName(QString("%1_%2_%3_Itme").arg(table->objectName()).arg(row).arg(column));
pCheckBox->setChecked(false);
pCheckBox->setFont(table->font());
pCheckBox->setFocusPolicy(Qt::NoFocus);
pCheckBox->setStyle(QStyleFactory::create("fusion"));
pCheckBox->setStyleSheet(QString(".QCheckBox {margin:3px;border:0px;}QCheckBox::indicator {width: %1px; height: %1px; }").arg(20));
hLayout->addWidget(pCheckBox); //添加
hLayout->setMargin(0); //设置边缘距离
hLayout->setAlignment(pCheckBox, Qt::AlignCenter); //居中
hLayout->setSpacing(0);
pWidget->setLayout(hLayout); //设置widget的布局
table->setCellWidget(row,column,pWidget);
}
QList<QCheckBox *> allCheckBoxs = pWidget->findChildren<QCheckBox *>();
if(allCheckBoxs.size() > 0)
pCheckBox = allCheckBoxs.first();
return pCheckBox;
}
//处理CheckBox
QCheckBox* setCheckBox(QTableWidget*table,int row ,int column,bool checkd)
{
QCheckBox *check = getCheckBox(table,row,column);
if(check != 0) //
{
check->setChecked(checkd);
}
QCheckBox::connect(check,&QCheckBox::stateChanged,[=]{
QString objectName = check->objectName();
QStringList nameList = objectName.split("_");//拆解
if(nameList.count() == 4)
{
QString tableName = nameList.at(0);//表格名称
int row = nameList.at(1).toInt();//行
int column = nameList.at(2).toInt();//列
bool checked = check->isChecked();//是否被选中
//知道了表格、行、列,就可以执行我们所需要的操作了。。。
qDebug() << QString("%1表第%2行第%3列是否被选中:%4")
.arg(tableName).arg(row).arg(column).arg(checked?"是":"否");
}
});
return check;
}
具体使用,写了例子,
ui->tableWidget->horizontalHeader()->setSectionResizeMode(QHeaderView::Stretch);
ui->tableWidget->setRowCount(4);
ui->tableWidget->setColumnCount(4);
for(int i =0; i <2; i++)
for(int j =0; j <4; j++)
setCheckBox(ui->tableWidget,i,j,false);
for(int i =2; i <4; i++)
{
for(int j =0; j <4; j++)
{
QTableWidgetItem*item = new QTableWidgetItem();
item->setTextAlignment(Qt::AlignCenter);
item->setFont( ui->tableWidget->font());
item->setCheckState(Qt::Checked);
ui->tableWidget->setItem(i, j, item);
}
}
执行效果如下图:前两行是QCheckBox,后两行是 QTableWidgetItem这个对象有CheckState属性
第二种方法的CheckBox不仅可以居中,可以设置框的大小。通过样式进行设置。用fusion风格
pCheckBox->setStyle(QStyleFactory::create("fusion"));
QCheckBox::indicator {width: 20px; height:20px; }
getCheckBox函数返回的QCheckBox* 指针,有了指针就可以绑定信号和槽,当状态修改了,触发信号时,槽就会执行了
补充一下:有评论说不需要使用setObjectName,除了评论的方式,下面再介绍另外两种不使用setObjectName的方式,获取到改变的对应数据
方式1:通过坐标转为QModelIndex,从而获取到对应的行和列,知道行列就能确定是哪一条数据变了
QCheckBox *check = getCheckBox(table,row,column);
connect(check, &QCheckBox::stateChanged, [=] {
int x = check->parentWidget()->frameGeometry().x();
int y = check->parentWidget()->frameGeometry().y();
QModelIndex index = table->indexAt(QPoint(x, y));
int row_current = index.row();
int row_column = index.column();
// 后面就根据行列找到数据
});
方式2:QObject中有动态属性,可以设置用户自己的数据
- 即使
check
原来没有"userData"
属性,setProperty()
创建新属性。 QObject::setProperty()
会动态创建新属性,然后存放用户数据,比如说ID
唯一的ID设置为check的自定义用户数据,然后触发信号时,直接根据这个唯一的ID去确定是哪一条数据发生了改变,然后就跳过了行列的步骤了。
QCheckBox *check = getCheckBox(table,row,column);
check->setProperty("userData", QVariant::fromValue(id));
connect(check, &QCheckBox::stateChanged, [=] {
int id = check->property("userData").value<int>();
//根据 id 找,就可以做我们想要的操作处理了,比如把修改后的值保存到数据库等等
//....
});