QTableview 隐藏单元格内控件无效的原因
背景:
在QTableview的单元格中创建多个QComboBox,当某条件成立时,隐藏特定单元格中的QComboBox,使得该单元格为空。
DEMO:
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QStandardItemModel>
#include <QMainWindow>
#include <QComboBox>
QT_BEGIN_NAMESPACE
namespace Ui { class MainWindow; }
QT_END_NAMESPACE
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
MainWindow(QWidget *parent = nullptr);
~MainWindow();
private:
void init();
void changeData();
private:
QStandardItemModel *mpDataModel; ///< 表格数据模型
QList<QComboBox*> mComboList;
private:
Ui::MainWindow *ui;
};
#endif // MAINWINDOW_H
#include "mainwindow.h"
#include "ui_mainwindow.h"
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::MainWindow)
{
ui->setupUi(this);
init();
changeData();
}
MainWindow::~MainWindow()
{
delete ui;
}
void MainWindow::init()
{
mpDataModel = new QStandardItemModel;
mpDataModel->setRowCount(10 );
mpDataModel->setColumnCount(1);
ui->tableView->setModel(mpDataModel);
QStringList strList;
strList<<"1"<<"2";
for(int i=0; i<10; i++)
{
QComboBox *combo = new QComboBox(this);
combo->addItems(strList);
mComboList.append(combo);
QModelIndex index = mpDataModel->index(i, 0);
ui->tableView->setIndexWidget(index, combo);
}
}
void MainWindow::changeData()
{
//第一种方式
// int row1 = 1;
// QWidget *widget = ui->tableView->indexWidget(mpDataModel->index(row1,0));
// QComboBox *combo = dynamic_cast<QComboBox*>(widget);
// if(combo != nullptr)
// {
// combo->setVisible(false);
// }
//第二种方式
// widget->hide();
//第三种方式
// mComboList.at(row)->setVisible(false);
//第四种方式
int row2 = 1;
ui->tableView->setIndexWidget(mpDataModel->index(row2,0),nullptr);
}
demo中创建了一个简单的主窗口,包含一个表格视图 (QTableView)。表格视图有 10 行和 1 列,每个单元格都包含一个下拉列表 (QComboBox)。
init() 函数设置了表格视图,创建一个包含两个选项 (“1” 和 “2”) 的下拉列表 (QComboBox),并将其添加到表格视图的每个单元格中。最后,将每个 QComboBox 对象添加到 mComboList 向量中。
changeData()试图隐藏表格视图中的某个特定单元格内的 QComboBox。
运行结果:除了第四种方式,其余方式隐藏无效。
分析原因:
<帮助手册>
<源代码:qabstractitemview.cpp>
探讨setIndexWidget函数的作用:
如果在放入新控件时,当前单元格存在旧控件,那么d->persistent.remove(oldWidget);会将旧控件从persistent 集合中移除,防止内存泄漏,同时确保旧控件不再与模型索引关联。除此之外,会将其从编辑器列表中移除,并取消对它的事件过滤,deleteLater将其安排在稍后删除。
排查隐藏失败的原因:
在demo中:
1、该下拉列表框已经被正确添加到了视图中,目标隐藏位置确实存在QComboBox控件:
语句:
qDebug() << ui->tableView->indexWidget(mpDataModel->index(row1,0));
输出:
2、控件的visible属性没有在其他代码部分重新设置为true
3、控件没有被其他控件遮挡
4、控件所在的父控件没有设置setVisible(false)
5、强制视图重新绘制后也无法隐藏
语句:
ui->tableView->viewport()->update();
6、控件的可见性属性确实被设置为false
语句:
bool isVisible = combo->testAttribute(Qt::WA_WState_Visible);
qDebug() << "Is visible: " << isVisible;
输出:
合理推测隐藏失败原因:
在tableview被创建时,单元格存在默认的控件:在放入下拉框前打印
语句:
for(int row=0; row<10;row++)
{
qDebug() << ui->tableView->indexWidget(mpDataModel->index(row,0));
}
输出:
我们使用setIndexWidget时,将新控件代替了旧控件,此时旧控件已经从视图中移除。如果我们删除或隐藏新控件,该单元格中没有可以显示的控件,也许这样导致了删除或隐藏失败,保留原视图显示。
欢迎一起讨论!