- VisualStudio2022插件的安装及使用-编程手把手系列文章
- pprof-在现网场景怎么用
- C#实现的下拉多选框,下拉多选树,多级节点
- 【学习笔记】基础数据结构:猫树
表格作为数据展示的界面,会在很多场景下使用。Qt为我们提供了使用简单方便和扩展性强的表格视图,这里做一个简单的入门整理。 个人能力有限,有错误欢迎留言指正,如果你有更好的方法,也欢迎分享讨论.
Qt、表格、过滤、筛选、自定义单元格、排序、委托、代理 。
QTableWidget、QTableView、QStandardItemModel、QStyledItemDelegate、QSortFilterProxyModel 。
// 学生类
class Student
{
public:
Student(const QString &id, const QString &name, int age, int score, int sex);
~Student();
QString mId; // 学号
QString mName; // 名字
int mAge; // 年龄
int mScore; // 分数
int mSex; // 性别
};
// 初始化数据
void MainWindow::initStudent()
{
// QStringList mHeader;
mHeader << "学号" << "姓名" << "年龄" << "分数" << "性别";
// QList<Student *> mStudents;
mStudents << new Student("501", "小明", 20, 85, 0)
<< new Student("402", "小红", 29, 19, 1)
<< new Student("311", "小刚", 25, 79, 1)
<< new Student("813", "小李", 27, 33, 1)
<< new Student("514", "小赵", 23, 21, 0)
<< new Student("425", "小王", 24, 50, 0)
<< new Student("326", "小张", 26, 44, 1)
<< new Student("28", "小淘", 28, 93, 1)
<< new Student("30", "小杨", 21, 77, 1);
}
在这段代码中, 。
定义学生类,学生类主要包括学生的学号、姓名、年龄、分数、性别; 。
定义了表格的表头; 。
同时创建了几个学生,并将学生存储到QList里.
void MainWindow::initTableWidget()
{
ui->tableWidget->setRowCount(mStudents.size());
ui->tableWidget->setColumnCount(5);
ui->tableWidget->setHorizontalHeaderLabels(mHeader);
ui->tableWidget->horizontalHeader()->setSectionResizeMode(QHeaderView::Stretch);
for (int i = 0; i < mStudents.size(); ++i) {
Student *s = mStudents.at(i);
QTableWidgetItem *item0 = new QTableWidgetItem;
item0->setData(Qt::DisplayRole, s->mId);
QTableWidgetItem *item1 = new QTableWidgetItem;
item1->setData(Qt::DisplayRole, s->mName);
QTableWidgetItem *item2 = new QTableWidgetItem;
item2->setData(Qt::DisplayRole, s->mAge);
QTableWidgetItem *item3 = new QTableWidgetItem;
item3->setData(Qt::DisplayRole, s->mScore);
QTableWidgetItem *item4 = new QTableWidgetItem;
item4->setData(Qt::DisplayRole, s->mSex);
ui->tableWidget->setItem(i, 0, item0);
ui->tableWidget->setItem(i, 1, item1);
ui->tableWidget->setItem(i, 2, item2);
ui->tableWidget->setItem(i, 3, item3);
ui->tableWidget->setItem(i, 4, item4);
}
}
在这段代码中,使用QTableWidget显示数据.
首先设置了行数和列数; 。
然后设置QTableWidget的水平表头的列名,同时设置为平铺拉伸模式; 。
再然后遍历数据对表格进行了填充,使用的是QTableWidgetItem,同时使用setData和Qt::DisplayRole可以方便以后对数字列进行排序.
运行效果如下:
void MainWindow::initTableView()
{
// QStandardItemModel *mTableViewModel;
mTableViewModel = new QStandardItemModel(this);
mTableViewModel->setRowCount(mStudents.size());
mTableViewModel->setColumnCount(5);
mTableViewModel->setHorizontalHeaderLabels(mHeader);
for (int i = 0; i < mStudents.size(); ++i) {
Student *s = mStudents.at(i);
QStandardItem *item0 = new QStandardItem;
item0->setData(s->mId, Qt::DisplayRole);
QStandardItem *item1 = new QStandardItem;
item1->setData(s->mName, Qt::DisplayRole);
QStandardItem *item2 = new QStandardItem;
item2->setData(s->mAge, Qt::DisplayRole);
QStandardItem *item3 = new QStandardItem;
item3->setData(s->mScore, Qt::DisplayRole);
QStandardItem *item4 = new QStandardItem;
item4->setData(s->mSex, Qt::DisplayRole);
mTableViewModel->setItem(i, 0, item0);
mTableViewModel->setItem(i, 1, item1);
mTableViewModel->setItem(i, 2, item2);
mTableViewModel->setItem(i, 3, item3);
mTableViewModel->setItem(i, 4, item4);
}
ui->tableView->setModel(mTableViewModel);
ui->tableView->horizontalHeader()->setSectionResizeMode(QHeaderView::Stretch);
}
在这段代码中,使用了QStandardItemModel和QTableView来显示数据。通过view和model分离的模式,可以创建更多的高级功能。后面将使用QTableView来进行高级功能的演示.
首先,创建了model,设置行数、列数和表头; 。
然后,创建单元格,填充model;需要注意的是QTableWidgetItem和QStandardItem的setData函数的两个参数顺序是相反的; 。
最后,给view设置model,设置表头平铺拉伸显示.
运行效果如下:
QStyledItemDelegate可以让表格拥有更高级的显示效果和编辑功能.
// 代理类
class ComboxDelegate : public QStyledItemDelegate
{
Q_OBJECT
public:
ComboxDelegate(QObject *parent = nullptr);
~ComboxDelegate();
protected:
QWidget * createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const override;
void setEditorData(QWidget *editor, const QModelIndex &index) const override;
void setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const override;
void updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem &option, const QModelIndex &index) const override;
};
在这段代码中,继承了QStyledItemDelegate类,来实现一个代理类。需要实现四个函数.
createEditor:在点击表格视图进入编辑状态时,该函数会创建一个QWidget,在此函数中实现QComboBox的生成; 。
setEditorData:该函数会对QComboBox设置值; 。
setModelData:在编辑完成后,该函数应该对model中的数据进行修改; 。
updateEditorGeometry:该函数应该设置编辑区域的大小; 。
ComboxDelegate::ComboxDelegate(QObject *parent)
{
}
ComboxDelegate::~ComboxDelegate()
{
}
QWidget *ComboxDelegate::createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const
{
QComboBox *combox = new QComboBox(parent);
combox->addItem("女");
combox->addItem("男");
return combox;
}
void ComboxDelegate::setEditorData(QWidget *editor, const QModelIndex &index) const
{
int sex = index.data(Qt::EditRole).toInt();
QComboBox *combox = qobject_cast<QComboBox *>(editor);
combox->setCurrentIndex(sex);
}
void ComboxDelegate::setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const
{
QComboBox *combox = qobject_cast<QComboBox *>(editor);
int sex = combox->currentIndex();
model->setData(index, sex, Qt::EditRole);
}
void ComboxDelegate::updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem &option, const QModelIndex &index) const
{
editor->setGeometry(option.rect);
}
在这段代码中,实现四个函数的具体功能.
createEditor:创建了一个QComboBox,并设置了两个选项; 。
setEditorData:从QModelIndex中获取数据,转换QWidget为QComboBox,并给其设置值; 。
setModelData:获取编辑后的值,将编辑后的值设置给model; 。
updateEditorGeometry:设置几何大小; 。
void MainWindow::initSource()
{
mSourceModel = new QStandardItemModel(this);
mSourceModel->setRowCount(mStudents.size());
mSourceModel->setColumnCount(5);
mSourceModel->setHorizontalHeaderLabels(mHeader);
for (int i = 0; i < mStudents.size(); ++i) {
Student *s = mStudents.at(i);
QStandardItem *item0 = new QStandardItem;
item0->setData(s->mId, Qt::DisplayRole);
QStandardItem *item1 = new QStandardItem;
item1->setData(s->mName, Qt::DisplayRole);
QStandardItem *item2 = new QStandardItem;
item2->setData(s->mAge, Qt::DisplayRole);
QStandardItem *item3 = new QStandardItem;
item3->setData(s->mScore, Qt::DisplayRole);
QStandardItem *item4 = new QStandardItem;
item4->setData(s->mSex, Qt::DisplayRole);
mSourceModel->setItem(i, 0, item0);
mSourceModel->setItem(i, 1, item1);
mSourceModel->setItem(i, 2, item2);
mSourceModel->setItem(i, 3, item3);
mSourceModel->setItem(i, 4, item4);
}
ui->tableView_Source->setModel(mSourceModel);
ui->tableView_Source->horizontalHeader()->setSectionResizeMode(QHeaderView::Stretch);
ComboxDelegate *d = new ComboxDelegate(this);
ui->tableView_Source->setItemDelegateForColumn(4, d);
}
在这段代码中, 。
首先,给model填充数据; 。
然后,创建了ComboxDelegate代理对象; 。
最后,给QTableView的性别列设置了代理对象; 。
运行效果如下: 更多的例子可以参考Qt自带的例子,在「示例」中搜索「Color Editor Factory Example」、「Spin Box Delegate Example」、「Star Delegate Example」 。
除了自带的控件,如果想将自定义的控件或者窗口插入到表格的单元格中应该怎么做呢? 与刚才的例子类似,只不过把QComboBox换成自定义的窗口类就可以了.
class RadioWidget : public QWidget
{
Q_OBJECT
public:
explicit RadioWidget(const QModelIndex &index, QWidget *parent = nullptr);
~RadioWidget();
void setSex(int sex);
int getSex() const;
QStandardItem *getItem() const;
void setItem(QStandardItem *item);
signals:
void sexChangedByQModelIndex(int row, int sex);
void sexChangedByQStandardItem(int row, int sex);
private:
QRadioButton *mRadioMale;
QRadioButton *mRadioFemale;
int mSex;
QStandardItem *mItem = nullptr;
QModelIndex mIndex;
void changeState(QAbstractButton *button, bool checked);
};
在这段代码中,定义了一个自定义的窗口:
它包含两个QRadioButton和性别; 。
对应单元格的QStandardItem和QModelIndex; 。
当性别变化时发出的sexChangedByQModelIndex和sexChangedByQStandardItem信号,发送的参数是单元格所在行号和当前性别; 。
需要注意的是,为了定位单元格,使用了QStandardItem和QModelIndex———这个会在后面进行演示,添加这两变量是为了:
1、由于代理的存在,自定义窗口和模型单元格之间隔了一层,需要将定位信息(主要是为了行号)存在自定义窗口中,
2、比较QStandardItem和QModelIndex,结论就是应该使用QStandardItem,
RadioWidget::RadioWidget(const QModelIndex &index, QWidget *parent) :
QWidget(parent)
{
mIndex = index;
QHBoxLayout *layout = new QHBoxLayout();
mRadioMale = new QRadioButton(this);
mRadioMale->setText("男");
mRadioFemale = new QRadioButton(this);
mRadioFemale->setText("女");
layout->addWidget(mRadioMale, Qt::AlignCenter);
layout->addWidget(mRadioFemale, Qt::AlignCenter);
layout->setMargin(0);
this->setLayout(layout);
mSex = 1;
mRadioMale->setChecked(true);
mRadioFemale->setChecked(false);
QButtonGroup *button_group = new QButtonGroup(this);
button_group->addButton(mRadioMale);
button_group->addButton(mRadioFemale);
connect(button_group, QOverload<QAbstractButton *, bool>::of(&QButtonGroup::buttonToggled),
this, &RadioWidget::changeState);
}
RadioWidget::~RadioWidget()
{
}
void RadioWidget::setSex(int sex)
{
mSex = sex;
if (mSex == 1) {
mRadioMale->setChecked(true);
mRadioFemale->setChecked(false);
} else {
mRadioMale->setChecked(false);
mRadioFemale->setChecked(true);
}
}
QStandardItem *RadioWidget::getItem() const
{
return mItem;
}
void RadioWidget::setItem(QStandardItem *item)
{
mItem = item;
}
int RadioWidget::getSex() const
{
return mSex;
}
void RadioWidget::changeState(QAbstractButton *button, bool checked)
{
if (checked == false) {
return ;
}
if (button == mRadioMale) {
mSex = 1;
} else if (button == mRadioFemale) {
mSex = 0;
}
emit sexChangedByQModelIndex(mIndex.row(), mSex);
if (mItem) {
emit sexChangedByQStandardItem(mItem->row(), mSex);
}
}
在这段代码中,具体实现了各个函数的功能:
在构造函数中,创建了两个性别的QRadioButton和按钮组,并创建了窗口的布局; 。
在setSex函数中,设置了对应的按钮状态; 。
在changeState函数中,当性别变化时,发出对应的两个信号; 。
class RadioDelegate : public QStyledItemDelegate
{
Q_OBJECT
public:
RadioDelegate(QObject *parent = nullptr);
~RadioDelegate();
void setSourceModel(QStandardItemModel *model);
protected:
QWidget * createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const override;
void setEditorData(QWidget *editor, const QModelIndex &index) const override;
void updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem &option, const QModelIndex &index) const override;
void setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const override;
private:
QStandardItemModel *mSourceModel = nullptr;
};
RadioDelegate::RadioDelegate(QObject *parent) : QStyledItemDelegate(parent)
{
}
RadioDelegate::~RadioDelegate()
{
}
QWidget *RadioDelegate::createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const
{
RadioWidget *rw = new RadioWidget(index, parent);
QStandardItem *item = mSourceModel->itemFromIndex(index);
rw->setItem(item);
rw->setSex(item->data(Qt::DisplayRole).toInt());
return rw;
}
void RadioDelegate::setEditorData(QWidget *editor, const QModelIndex &index) const
{
int sex = index.model()->data(index).toInt();
RadioWidget *rw = static_cast<RadioWidget *>(editor);
rw->setSex(sex);
}
void RadioDelegate::updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem &option, const QModelIndex &index) const
{
editor->setGeometry(option.rect);
}
void RadioDelegate::setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const
{
RadioWidget *rw = static_cast<RadioWidget *>(editor);
int sex = rw->getSex();
model->setData(index, sex);
}
void RadioDelegate::setSourceModel(QStandardItemModel *model)
{
mSourceModel = model;
}
在这段代码中,实现了自定义窗口对应的代理,和上面的代理类差不多,需要注意的是
1、要将上面代码中的QComboBox替换成自定义窗口RadioWidget; 。
2、在createEditor函数中,设置了RadioWidget的QModelIndex和QStandardItem,以及性别; 。
3、在调用时使用代码:
RadioDelegate *d = new RadioDelegate(this);
d->setSourceModel(mSourceModel);
ui->tableView_Source->setItemDelegateForColumn(4, d);
从上图的效果可以看出,需要双击才可以显示出单元格的自定义窗口,但是需要始终显示的时候应该怎么做呢?
只需要调用void QAbstractItemView::openPersistentEditor(const QModelIndex &index)函数就可以了.
修改后的代码如下:
void MainWindow::initSource()
{
mSourceModel = new QStandardItemModel(this);
mSourceModel->setRowCount(mStudents.size());
mSourceModel->setColumnCount(5);
mSourceModel->setHorizontalHeaderLabels(mHeader);
for (int i = 0; i < mStudents.size(); ++i) {
Student *s = mStudents.at(i);
QStandardItem *item0 = new QStandardItem;
item0->setData(s->mId, Qt::DisplayRole);
QStandardItem *item1 = new QStandardItem;
item1->setData(s->mName, Qt::DisplayRole);
QStandardItem *item2 = new QStandardItem;
item2->setData(s->mAge, Qt::DisplayRole);
QStandardItem *item3 = new QStandardItem;
item3->setData(s->mScore, Qt::DisplayRole);
QStandardItem *item4 = new QStandardItem;
item4->setData(s->mSex, Qt::DisplayRole);
mSourceModel->setItem(i, 0, item0);
mSourceModel->setItem(i, 1, item1);
mSourceModel->setItem(i, 2, item2);
mSourceModel->setItem(i, 3, item3);
mSourceModel->setItem(i, 4, item4);
}
ui->tableView_Source->setModel(mSourceModel);
ui->tableView_Source->horizontalHeader()->setSectionResizeMode(QHeaderView::Stretch);
// ComboxDelegate *d = new ComboxDelegate(this);
// ui->tableView_Source->setItemDelegateForColumn(4, d);
RadioDelegate *d = new RadioDelegate(this);
d->setSourceModel(mSourceModel);
ui->tableView_Source->setItemDelegateForColumn(4, d);
// 让代理自定义窗口始终显示
for (int i = 0, size = mSourceModel->rowCount(); i < size; ++i) {
ui->tableView_Source->openPersistentEditor(mSourceModel->index(i, 4));
}
}
运行效果如下:
QSortFilterProxyModel可以让表格实现筛选过滤和排序的功能.
在这个例子中实现了名字和性别的筛选过滤.
class SortFilterProxyModel : public QSortFilterProxyModel
{
public:
SortFilterProxyModel(QObject *parent = nullptr);
void setSex(int sex);
protected:
bool filterAcceptsRow(int source_row, const QModelIndex &source_parent) const override;
private:
int mSex = -1;
};
在这段代码中,定义了一个自定义的排序筛选代理模型:
新增了性别变量,当设置性别setSex时,需要调用invalidateFilter()重新筛选; 。
在filterAcceptsRow函数中,获取到源数据,返回比较的结果布尔值; 。
void MainWindow::initProxy()
{
mProxyModel = new SortFilterProxyModel(this);
mProxyModel->setSourceModel(mSourceModel);
ui->tableView_Proxy->setModel(mProxyModel);
ui->tableView_Proxy->horizontalHeader()->setSectionResizeMode(QHeaderView::Stretch);
// 过滤筛选
connect(ui->lineEdit, &QLineEdit::textChanged, this, [=](const QString &text) {
mProxyModel->setFilterRegExp(text);
});
ui->comboBox->addItem("全部", -1);
ui->comboBox->addItem("女", 0);
ui->comboBox->addItem("男", 1);
connect(ui->comboBox, QOverload<int>::of(&QComboBox::currentIndexChanged), this, [=](int index) {
int sex = ui->comboBox->itemData(index).toInt();
mProxyModel->setSex(sex);
});
}
在这段代码中, 。
创建了SortFilterProxyModel对象,设置了一些基础属性:设置源模型,显示到view上,设置表头; 。
通过检测lineEdit文本变化,过滤名字; 。
通过检测comboBox选项变化,过滤性别; 。
运行效果如下:
在上面基础上添加以下代码:
// 排序
connect(ui->tableView_Proxy->horizontalHeader(), &QHeaderView::sortIndicatorChanged,
this, [=](int logicalIndex, Qt::SortOrder order)
{
ui->tableView_Proxy->model()->sort(logicalIndex, order);
});
对于排序的实现,可以自定义void QSortFilterProxyModel::sort(int column, Qt::SortOrder order = Qt::AscendingOrder)函数实现,这里不做过多演示.
运行效果如下:
需要对上面的RadioDelegate做一些修改,添加以下内容:
void setProxyModel(QSortFilterProxyModel *sortModel);
QSortFilterProxyModel *mProxyModel = nullptr;
void RadioDelegate::setProxyModel(QSortFilterProxyModel *sortModel)
{
mProxyModel = sortModel;
QAbstractItemModel *source_model = mProxyModel->sourceModel();
mSourceModel = static_cast<QStandardItemModel *>(source_model);
}
QWidget *RadioDelegate::createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const
{
RadioWidget *rw = new RadioWidget(index, parent);
// QStandardItem *item = mSourceModel->itemFromIndex(index);
// rw->setItem(item);
// rw->setSex(item->data(Qt::DisplayRole).toInt());
if (mProxyModel) {
QStandardItem *item = mSourceModel->itemFromIndex(mProxyModel->mapToSource(index));
rw->setItem(item);
rw->setSex(item->data(Qt::DisplayRole).toInt());
} else {
QStandardItem *item = mSourceModel->itemFromIndex(index);
rw->setItem(item);
rw->setSex(item->data(Qt::DisplayRole).toInt());
}
connect(rw, &RadioWidget::sexChangedByQModelIndex, this, [=](int row, int sex) {
if (mSourceModel) {
// qDebug() << "sexChangedByQModelIndex" << row << mSourceModel->item(row, 0)->text();
mSourceModel->item(row, 4)->setData(sex, Qt::DisplayRole);
}
});
connect(rw, &RadioWidget::sexChangedByQStandardItem, this, [=](int row, int sex) {
if (mSourceModel) {
// qDebug() << "sexChangedByQStandardItem" << row << mSourceModel->item(row, 0)->text();
mSourceModel->item(row, 4)->setData(sex, Qt::DisplayRole);
}
});
return rw;
}
在这段代码中, 。
添加了QSortFilterProxyModel成员变量; 。
为了方便(偷懒),在createEditor函数中判断后,再setItem; 。
连接了两个信号; 。
void MainWindow::initProxy()
{
mProxyModel = new SortFilterProxyModel(this);
mProxyModel->setSourceModel(mSourceModel);
ui->tableView_Proxy->setModel(mProxyModel);
ui->tableView_Proxy->horizontalHeader()->setSectionResizeMode(QHeaderView::Stretch);
// 代理
RadioDelegate *rd = new RadioDelegate(ui->tableView_Proxy);
rd->setProxyModel(mProxyModel);
ui->tableView_Proxy->setItemDelegateForColumn(4, rd);
for (int i = 0, size = mProxyModel->rowCount(); i < size; ++i) {
ui->tableView_Proxy->openPersistentEditor(mProxyModel->index(i, 4));
}
// 过滤筛选
connect(ui->lineEdit, &QLineEdit::textChanged, this, [=](const QString &text) {
mProxyModel->setFilterRegExp(text);
for (int i = 0, size = mProxyModel->rowCount(); i < size; ++i) {
ui->tableView_Proxy->openPersistentEditor(mProxyModel->index(i, 4));
}
});
ui->comboBox->addItem("全部", -1);
ui->comboBox->addItem("女", 0);
ui->comboBox->addItem("男", 1);
connect(ui->comboBox, QOverload<int>::of(&QComboBox::currentIndexChanged), this, [=](int index) {
int sex = ui->comboBox->itemData(index).toInt();
mProxyModel->setSex(sex);
for (int i = 0, size = mProxyModel->rowCount(); i < size; ++i) {
ui->tableView_Proxy->openPersistentEditor(mProxyModel->index(i, 4));
}
});
// 排序
connect(ui->tableView_Proxy->horizontalHeader(), &QHeaderView::sortIndicatorChanged,
this, [=](int logicalIndex, Qt::SortOrder order)
{
ui->tableView_Proxy->model()->sort(logicalIndex, order);
for (int i = 0, size = mProxyModel->rowCount(); i < size; ++i) {
ui->tableView_Proxy->openPersistentEditor(mProxyModel->index(i, 4));
}
});
}
在这段代码中,添加了以下内容:
创建代理,给表格的性别列设置代理,让代理始终显示; 。
在过滤筛选的两个槽函数中,重新设置代理始终显示; 。
在排序的槽函数中,重新设置代理始终显示; 。
对于RadioDelegate::createEditor中的连接的两个信号,可以注释掉其中一个运行一下效果:
使用sexChangedByQModelIndex: 可以看到在多次的筛选排序后,出现了问题.
sexChangedByQStandardItem: 可以看到在多次筛选排序后,都没有出现问题.
结论:使用QStandardItem的row()函数.
QTableView 自定义代理(一):基于QComboBox 的下拉框代理类 。
Qt的复杂代理使用总结 。
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include <QtCore>
#include <QtWidgets>
QT_BEGIN_NAMESPACE
namespace Ui { class MainWindow; }
QT_END_NAMESPACE
// 学生类
class Student
{
public:
Student(const QString &id, const QString &name, int age, int score, int sex);
~Student();
QString mId; // 学号
QString mName; // 名字
int mAge; // 年龄
int mScore; // 分数
int mSex; // 性别
};
// 代理类
class ComboxDelegate : public QStyledItemDelegate
{
Q_OBJECT
public:
ComboxDelegate(QObject *parent = nullptr);
~ComboxDelegate();
protected:
QWidget * createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const override;
void setEditorData(QWidget *editor, const QModelIndex &index) const override;
void setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const override;
void updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem &option, const QModelIndex &index) const override;
};
// 自定义窗口
class RadioWidget : public QWidget
{
Q_OBJECT
public:
explicit RadioWidget(const QModelIndex &index, QWidget *parent = nullptr);
~RadioWidget();
void setSex(int sex);
int getSex() const;
QStandardItem *getItem() const;
void setItem(QStandardItem *item);
signals:
void sexChangedByQModelIndex(int row, int sex);
void sexChangedByQStandardItem(int row, int sex);
private:
QRadioButton *mRadioMale;
QRadioButton *mRadioFemale;
int mSex;
QStandardItem *mItem = nullptr;
QModelIndex mIndex;
void changeState(QAbstractButton *button, bool checked);
};
// 自定义窗口对应的代理类
class RadioDelegate : public QStyledItemDelegate
{
Q_OBJECT
public:
RadioDelegate(QObject *parent = nullptr);
~RadioDelegate();
void setSourceModel(QStandardItemModel *model);
void setProxyModel(QSortFilterProxyModel *sortModel);
protected:
QWidget * createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const override;
void setEditorData(QWidget *editor, const QModelIndex &index) const override;
void updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem &option, const QModelIndex &index) const override;
void setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const override;
private:
QStandardItemModel *mSourceModel = nullptr;
QSortFilterProxyModel *mProxyModel = nullptr;
};
class SortFilterProxyModel : public QSortFilterProxyModel
{
public:
SortFilterProxyModel(QObject *parent = nullptr);
void setSex(int sex);
protected:
bool filterAcceptsRow(int source_row, const QModelIndex &source_parent) const override;
private:
int mSex = -1;
};
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
MainWindow(QWidget *parent = nullptr);
~MainWindow();
private:
Ui::MainWindow *ui;
void initStudent();
void initTableWidget();
void initTableView();
void initSource();
void initProxy();
QStringList mHeader;
QList<Student *> mStudents;
QStandardItemModel *mTableViewModel;
QStandardItemModel *mSourceModel;
SortFilterProxyModel *mProxyModel;
};
#endif // MAINWINDOW_H
#include "mainwindow.h"
#include "ui_mainwindow.h"
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::MainWindow)
{
ui->setupUi(this);
initStudent();
initTableWidget();
initTableView();
initSource();
initProxy();
}
MainWindow::~MainWindow()
{
delete ui;
}
// 初始化数据
void MainWindow::initStudent()
{
// QStringList mHeader;
mHeader << "学号" << "姓名" << "年龄" << "分数" << "性别";
// QList<Student *> mStudents;
mStudents << new Student("501", "小明", 20, 85, 0)
<< new Student("402", "小红", 29, 19, 1)
<< new Student("311", "小刚", 25, 79, 1)
<< new Student("813", "小李", 27, 33, 1)
<< new Student("514", "小赵", 23, 21, 0)
<< new Student("425", "小王", 24, 50, 0)
<< new Student("326", "小张", 26, 44, 1)
<< new Student("28", "小淘", 28, 93, 1)
<< new Student("30", "小杨", 21, 77, 1);
}
void MainWindow::initTableWidget()
{
ui->tableWidget->setRowCount(mStudents.size());
ui->tableWidget->setColumnCount(5);
ui->tableWidget->setHorizontalHeaderLabels(mHeader);
ui->tableWidget->horizontalHeader()->setSectionResizeMode(QHeaderView::Stretch);
for (int i = 0; i < mStudents.size(); ++i) {
Student *s = mStudents.at(i);
QTableWidgetItem *item0 = new QTableWidgetItem;
item0->setData(Qt::DisplayRole, s->mId);
QTableWidgetItem *item1 = new QTableWidgetItem;
item1->setData(Qt::DisplayRole, s->mName);
QTableWidgetItem *item2 = new QTableWidgetItem;
item2->setData(Qt::DisplayRole, s->mAge);
QTableWidgetItem *item3 = new QTableWidgetItem;
item3->setData(Qt::DisplayRole, s->mScore);
QTableWidgetItem *item4 = new QTableWidgetItem;
item4->setData(Qt::DisplayRole, s->mSex);
ui->tableWidget->setItem(i, 0, item0);
ui->tableWidget->setItem(i, 1, item1);
ui->tableWidget->setItem(i, 2, item2);
ui->tableWidget->setItem(i, 3, item3);
ui->tableWidget->setItem(i, 4, item4);
}
}
void MainWindow::initTableView()
{
// QStandardItemModel *mTableViewModel;
mTableViewModel = new QStandardItemModel(this);
mTableViewModel->setRowCount(mStudents.size());
mTableViewModel->setColumnCount(5);
mTableViewModel->setHorizontalHeaderLabels(mHeader);
for (int i = 0; i < mStudents.size(); ++i) {
Student *s = mStudents.at(i);
QStandardItem *item0 = new QStandardItem;
item0->setData(s->mId, Qt::DisplayRole);
QStandardItem *item1 = new QStandardItem;
item1->setData(s->mName, Qt::DisplayRole);
QStandardItem *item2 = new QStandardItem;
item2->setData(s->mAge, Qt::DisplayRole);
QStandardItem *item3 = new QStandardItem;
item3->setData(s->mScore, Qt::DisplayRole);
QStandardItem *item4 = new QStandardItem;
item4->setData(s->mSex, Qt::DisplayRole);
mTableViewModel->setItem(i, 0, item0);
mTableViewModel->setItem(i, 1, item1);
mTableViewModel->setItem(i, 2, item2);
mTableViewModel->setItem(i, 3, item3);
mTableViewModel->setItem(i, 4, item4);
}
ui->tableView->setModel(mTableViewModel);
ui->tableView->horizontalHeader()->setSectionResizeMode(QHeaderView::Stretch);
}
void MainWindow::initSource()
{
mSourceModel = new QStandardItemModel(this);
mSourceModel->setRowCount(mStudents.size());
mSourceModel->setColumnCount(5);
mSourceModel->setHorizontalHeaderLabels(mHeader);
for (int i = 0; i < mStudents.size(); ++i) {
Student *s = mStudents.at(i);
QStandardItem *item0 = new QStandardItem;
item0->setData(s->mId, Qt::DisplayRole);
QStandardItem *item1 = new QStandardItem;
item1->setData(s->mName, Qt::DisplayRole);
QStandardItem *item2 = new QStandardItem;
item2->setData(s->mAge, Qt::DisplayRole);
QStandardItem *item3 = new QStandardItem;
item3->setData(s->mScore, Qt::DisplayRole);
QStandardItem *item4 = new QStandardItem;
item4->setData(s->mSex, Qt::DisplayRole);
mSourceModel->setItem(i, 0, item0);
mSourceModel->setItem(i, 1, item1);
mSourceModel->setItem(i, 2, item2);
mSourceModel->setItem(i, 3, item3);
mSourceModel->setItem(i, 4, item4);
}
ui->tableView_Source->setModel(mSourceModel);
ui->tableView_Source->horizontalHeader()->setSectionResizeMode(QHeaderView::Stretch);
// ComboxDelegate *d = new ComboxDelegate(this);
// ui->tableView_Source->setItemDelegateForColumn(4, d);
RadioDelegate *d = new RadioDelegate(this);
d->setSourceModel(mSourceModel);
ui->tableView_Source->setItemDelegateForColumn(4, d);
for (int i = 0, size = mSourceModel->rowCount(); i < size; ++i) {
ui->tableView_Source->openPersistentEditor(mSourceModel->index(i, 4));
}
}
void MainWindow::initProxy()
{
mProxyModel = new SortFilterProxyModel(this);
mProxyModel->setSourceModel(mSourceModel);
ui->tableView_Proxy->setModel(mProxyModel);
ui->tableView_Proxy->horizontalHeader()->setSectionResizeMode(QHeaderView::Stretch);
// 代理
RadioDelegate *rd = new RadioDelegate(ui->tableView_Proxy);
rd->setProxyModel(mProxyModel);
ui->tableView_Proxy->setItemDelegateForColumn(4, rd);
for (int i = 0, size = mProxyModel->rowCount(); i < size; ++i) {
ui->tableView_Proxy->openPersistentEditor(mProxyModel->index(i, 4));
}
// 过滤筛选
connect(ui->lineEdit, &QLineEdit::textChanged, this, [=](const QString &text) {
mProxyModel->setFilterRegExp(text);
for (int i = 0, size = mProxyModel->rowCount(); i < size; ++i) {
ui->tableView_Proxy->openPersistentEditor(mProxyModel->index(i, 4));
}
});
ui->comboBox->addItem("全部", -1);
ui->comboBox->addItem("女", 0);
ui->comboBox->addItem("男", 1);
connect(ui->comboBox, QOverload<int>::of(&QComboBox::currentIndexChanged), this, [=](int index) {
int sex = ui->comboBox->itemData(index).toInt();
mProxyModel->setSex(sex);
for (int i = 0, size = mProxyModel->rowCount(); i < size; ++i) {
ui->tableView_Proxy->openPersistentEditor(mProxyModel->index(i, 4));
}
});
// 排序
connect(ui->tableView_Proxy->horizontalHeader(), &QHeaderView::sortIndicatorChanged,
this, [=](int logicalIndex, Qt::SortOrder order)
{
ui->tableView_Proxy->model()->sort(logicalIndex, order);
for (int i = 0, size = mProxyModel->rowCount(); i < size; ++i) {
ui->tableView_Proxy->openPersistentEditor(mProxyModel->index(i, 4));
}
});
}
Student::Student(const QString &id, const QString &name, int age, int score, int sex)
{
mId = id;
mName = name;
mAge = age;
mScore = score;
mSex = sex;
}
Student::~Student()
{
}
ComboxDelegate::ComboxDelegate(QObject *parent) :
QStyledItemDelegate(parent)
{
}
ComboxDelegate::~ComboxDelegate()
{
}
QWidget *ComboxDelegate::createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const
{
QComboBox *combox = new QComboBox(parent);
combox->addItem("女");
combox->addItem("男");
return combox;
}
void ComboxDelegate::setEditorData(QWidget *editor, const QModelIndex &index) const
{
int sex = index.data(Qt::EditRole).toInt();
QComboBox *combox = qobject_cast<QComboBox *>(editor);
combox->setCurrentIndex(sex);
}
void ComboxDelegate::setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const
{
QComboBox *combox = qobject_cast<QComboBox *>(editor);
int sex = combox->currentIndex();
model->setData(index, sex, Qt::EditRole);
}
void ComboxDelegate::updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem &option, const QModelIndex &index) const
{
editor->setGeometry(option.rect);
}
RadioWidget::RadioWidget(const QModelIndex &index, QWidget *parent) :
QWidget(parent)
{
mIndex = index;
QHBoxLayout *layout = new QHBoxLayout();
mRadioMale = new QRadioButton(this);
mRadioMale->setText("男");
mRadioFemale = new QRadioButton(this);
mRadioFemale->setText("女");
layout->addWidget(mRadioMale, Qt::AlignCenter);
layout->addWidget(mRadioFemale, Qt::AlignCenter);
layout->setMargin(0);
this->setLayout(layout);
mSex = 1;
mRadioMale->setChecked(true);
mRadioFemale->setChecked(false);
QButtonGroup *button_group = new QButtonGroup(this);
button_group->addButton(mRadioMale);
button_group->addButton(mRadioFemale);
connect(button_group, QOverload<QAbstractButton *, bool>::of(&QButtonGroup::buttonToggled),
this, &RadioWidget::changeState);
}
RadioWidget::~RadioWidget()
{
}
void RadioWidget::setSex(int sex)
{
mSex = sex;
if (mSex == 1) {
mRadioMale->setChecked(true);
mRadioFemale->setChecked(false);
} else {
mRadioMale->setChecked(false);
mRadioFemale->setChecked(true);
}
}
QStandardItem *RadioWidget::getItem() const
{
return mItem;
}
void RadioWidget::setItem(QStandardItem *item)
{
mItem = item;
}
int RadioWidget::getSex() const
{
return mSex;
}
void RadioWidget::changeState(QAbstractButton *button, bool checked)
{
if (checked == false) {
return ;
}
if (button == mRadioMale) {
mSex = 1;
} else if (button == mRadioFemale) {
mSex = 0;
}
emit sexChangedByQModelIndex(mIndex.row(), mSex);
if (mItem) {
emit sexChangedByQStandardItem(mItem->row(), mSex);
}
}
RadioDelegate::RadioDelegate(QObject *parent) : QStyledItemDelegate(parent)
{
}
RadioDelegate::~RadioDelegate()
{
}
QWidget *RadioDelegate::createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const
{
RadioWidget *rw = new RadioWidget(index, parent);
// QStandardItem *item = mSourceModel->itemFromIndex(index);
// rw->setItem(item);
// rw->setSex(item->data(Qt::DisplayRole).toInt());
if (mProxyModel) {
QStandardItem *item = mSourceModel->itemFromIndex(mProxyModel->mapToSource(index));
rw->setItem(item);
rw->setSex(item->data(Qt::DisplayRole).toInt());
} else {
QStandardItem *item = mSourceModel->itemFromIndex(index);
rw->setItem(item);
rw->setSex(item->data(Qt::DisplayRole).toInt());
}
// connect(rw, &RadioWidget::sexChangedByQModelIndex, this, [=](int row, int sex) {
// if (mSourceModel) {
//// qDebug() << "sexChangedByQModelIndex" << row << mSourceModel->item(row, 0)->text();
// mSourceModel->item(row, 4)->setData(sex, Qt::DisplayRole);
// }
// });
connect(rw, &RadioWidget::sexChangedByQStandardItem, this, [=](int row, int sex) {
if (mSourceModel) {
// qDebug() << "sexChangedByQStandardItem" << row << mSourceModel->item(row, 0)->text();
mSourceModel->item(row, 4)->setData(sex, Qt::DisplayRole);
}
});
return rw;
}
void RadioDelegate::setEditorData(QWidget *editor, const QModelIndex &index) const
{
int sex = index.model()->data(index).toInt();
RadioWidget *rw = static_cast<RadioWidget *>(editor);
rw->setSex(sex);
}
void RadioDelegate::updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem &option, const QModelIndex &index) const
{
editor->setGeometry(option.rect);
}
void RadioDelegate::setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const
{
RadioWidget *rw = static_cast<RadioWidget *>(editor);
int sex = rw->getSex();
model->setData(index, sex);
}
void RadioDelegate::setProxyModel(QSortFilterProxyModel *sortModel)
{
mProxyModel = sortModel;
QAbstractItemModel *source_model = mProxyModel->sourceModel();
mSourceModel = static_cast<QStandardItemModel *>(source_model);
}
void RadioDelegate::setSourceModel(QStandardItemModel *model)
{
mSourceModel = model;
}
SortFilterProxyModel::SortFilterProxyModel(QObject *parent)
: QSortFilterProxyModel(parent)
{
}
void SortFilterProxyModel::setSex(int sex)
{
mSex = sex;
invalidateFilter();
}
bool SortFilterProxyModel::filterAcceptsRow(int source_row, const QModelIndex &source_parent) const
{
QModelIndex index1 = sourceModel()->index(source_row, 1);
QString name = sourceModel()->data(index1).toString();
QModelIndex index4 = sourceModel()->index(source_row, 4);
int sex = sourceModel()->data(index4).toInt();
bool sex_flag = (mSex == -1) ? true : (sex == mSex);
return sex_flag && name.contains(filterRegExp());
}
最后此篇关于Qt表格入门的文章就讲到这里了,如果你想了解更多关于Qt表格入门的内容请搜索CFSDN的文章或继续浏览相关文章,希望大家以后支持我的博客! 。
好的,这听起来很简单,但我已经花了几个小时在谷歌上搜索,我只是找不到解决方案,这并不复杂。 我想创建一个包含图像和文本的表格。我希望表格的每一行都具有相同的高度。我希望文本始终从顶部开始。 IE。 \
在我的网站表单上 - 我的出生日期、月份和年份菜单显示在两行上,我希望它们都显示在同一行上。 当我测试代码时,它显示在一行中,所以我相信一定存在宽度问题。 您可以在右侧表格 (incomeprotec
我们需要跟踪和审核生产,本质上我们有很多订单,但我们似乎在途中丢失了一些产品(废品等)。 为了阻止这种情况,我们现在已在 Google 表格上下了订单,并列出了应有的数量,然后员工会写下收到的数量。
我正在转换我的应用程序,以便它适用于 iOS 7。在应用程序的一部分,我有两个搜索栏,每个搜索栏都有一个与之关联的 UISearchDisplayController。当我搜索 UISearchDis
正如标题所说,非固定表格布局是否与类似的 HTML 表格具有相同的性能问题? 最佳答案 非固定表格的问题在于,要确定一列的宽度,必须加载该列的所有单元格。这仅在...... …您有一个包含几千字节或几
我在使用 Javascript 遍历表格并从一行的第一个单元格获取文本时遇到问题。我想获取此单元格的文本,以便我可以将它与其他内容进行比较,如果文本匹配则删除该行。但是,当我尝试获取文本时,实际出现的
我经常发现自己想要制作一个表格表格——一堆行,每一行都是一个单独的表格,有自己的字段和提交按钮。例如,这是一个宠物店应用程序示例——假设这是一个结帐屏幕,您可以选择更新所选宠物的数量和属性,并在结帐前
看过许多UBB代码,包括JS,ASP,JSP的,一直没发现表格的UBB,虽然可以直接用HTML模式实现相同表格功能,但对于某些开放的站点来说开放HTML模式终究是不合适的,故一直想实现表格的UBB。
表格由 table 标签来定义。每个表格均有若干行(由 tr 标签定义),每行被分割为若干单元格(由 td 标签定义)。字母 td 指表格数据(table data),即数据单元格的内容。数据单元格
我有一个 HTML 与 border-radius和使用 position: sticky 的粘性标题看起来像这样: https://codepen.io/muhammadrehansaeed/pen
对于 iPhone 应用程序,我需要以网格格式显示只读表格数据。该数据可能有许多行和列。 我可以使用 UITableView,但问题是数据很可能会非常宽并且需要滚动。 有没有办法将 UITableVi
我知道这里有类似的问题,但我找不到适合我的答案。 我想要的是显示表单“默认”是选择了某些选项(在这种情况下,除了“Ban Appeal”或“Ban Appeal(西类牙语)”之外的所有内容,我希望仅在
天啊! 我想在Flutter中创建以下非常简单的表。基本上是两列文字,左列右对齐,右列左对齐。如果右列具有多个名称,则每一行都将顶部对齐。 左列应自动调整为最大项目的大小(因为每个标题都有翻译字符串)
我们开始构建 SSAS 表格模型,并想知道大多数人是否拥有一个或多个模型。如果有多个,您是否复制每个所需的表,或者是否有办法在模型之间共享表?我想我知道答案,但我希望那些有更多经验的人能够证实我们的发
tl;博士 如何将任意数量的单词分成两列,总是在最后一列中只有最后一个单词,在第一列中包含所有其他单词? =IFS( LEN(C2)-LEN(SUBSTITUTE(C2," ",""))=1, SP
你们知道一个图表或dable,它可以提供一个简短而简洁但仍然完整且相对最新的现有协议(protocol)及其细节的 View ? (即:ZeroMQ、Rendez-Vous、EMS、...所有这些!:
我才刚刚开始开发MFC应用程序,我希望对整个“控件”概念更加熟悉。我在Visual Studio中使用对话框编辑器,到目前为止,我无法找到添加简单表/网格的功能。这对我来说似乎很基础,但是我什至找不到
我需要对一个非常大的表或矩阵执行计算和操作,大约有 7500 行和 30000 列。 矩阵数据将如下所示: 文件编号|字1 |字 2 |字 3 |... |字 30000 |文档类 0032 1 0
我正在使用设计非常糟糕的数据库,我需要在编写查询之前重新调整表格。 以下是我的常见问题: 时间戳已分为两列(一列用于日期,另一列用于时间)。 一些字符串列也被拆分成多个列。 大多数字符串都有固定长度和
我正在尝试显示 $row["name"] 通过 HTML Table 的形式,如下所示: echo " ".$row["name"]." "; 我也从这里获取行变量: $que
我是一名优秀的程序员,十分优秀!