Qt表格入门

news/2024/9/21 20:22:39/文章来源:https://www.cnblogs.com/wsry/p/18350462

摘要

    表格作为数据展示的界面,会在很多场景下使用。Qt为我们提供了使用简单方便和扩展性强的表格视图,这里做一个简单的入门整理。
个人能力有限,有错误欢迎留言指正,如果你有更好的方法,也欢迎分享讨论。

关键词

    Qt、表格、过滤、筛选、自定义单元格、排序、委托、代理

主要类

    QTableWidget、QTableView、QStandardItemModel、QStyledItemDelegate、QSortFilterProxyModel

〇、准备数据

  • Qt 5.14.2
  • 数据类
// 学生类
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里。


一、显示数据(QTableWidget)

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可以方便以后对数字列进行排序。

  • 运行效果如下:
    image


二、显示数据(QTableView和QStandardItemModel)

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,设置表头平铺拉伸显示。

  • 运行效果如下:
    image


三、数据代理(委托)(QStyledItemDelegate)

QStyledItemDelegate可以让表格拥有更高级的显示效果和编辑功能。

1、使用QComboBox代理显示性别

// 代理类
class ComboxDelegate : public QStyledItemDelegate
{Q_OBJECTpublic: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的性别列设置了代理对象;

  • 运行效果如下:
    image


    更多的例子可以参考Qt自带的例子,在「示例」中搜索「Color Editor Factory Example」、「Spin Box Delegate Example」、「Star Delegate Example」
    image
    image
    image


2、使用自定义的窗口代理显示性别

除了自带的控件,如果想将自定义的控件或者窗口插入到表格的单元格中应该怎么做呢?
与刚才的例子类似,只不过把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);
  • 4、运行效果如下:
    image

3、如何让代理始终显示呢

从上图的效果可以看出,需要双击才可以显示出单元格的自定义窗口,但是需要始终显示的时候应该怎么做呢?

只需要调用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));}
}

运行效果如下:
image


四、筛选过滤、排序(QSortFilterProxyModel)

QSortFilterProxyModel可以让表格实现筛选过滤和排序的功能。

1、实现筛选过滤

在这个例子中实现了名字和性别的筛选过滤。

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选项变化,过滤性别;

  • 运行效果如下:
    image

2、实现排序

在上面基础上添加以下代码:

    // 排序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)函数实现,这里不做过多演示。

运行效果如下:
image

3、显示代理

需要对上面的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));}});
}

在这段代码中,添加了以下内容:

  • 创建代理,给表格的性别列设置代理,让代理始终显示;

  • 在过滤筛选的两个槽函数中,重新设置代理始终显示;

  • 在排序的槽函数中,重新设置代理始终显示;

4、运行效果

对于RadioDelegate::createEditor中的连接的两个信号,可以注释掉其中一个运行一下效果:

使用sexChangedByQModelIndex:
image
可以看到在多次的筛选排序后,出现了问题。

sexChangedByQStandardItem:
image
可以看到在多次筛选排序后,都没有出现问题。

结论:使用QStandardItem的row()函数。


附录一:参考文献

  • QTableView 自定义代理(一):基于QComboBox 的下拉框代理类

  • Qt的复杂代理使用总结

附录二:完整代码

  • mainwindow.h
点击折叠或展开代码
#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_OBJECTpublic: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_OBJECTpublic: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

  • mainwindow.cpp
点击折叠或展开代码
#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());
}

  • ui_mainwindow.ui

image


本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/801189.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

王悦帆的第一次作业

这个作业属于哪个课程 https://edu.cnblogs.com/campus/zjlg/rjjc这个作业的目标 熟悉如何运用博客,展示自己姓名-学号 王悦帆 2022329301024一、自我介绍 (一)基本情况 大家好,我叫王悦帆,来自河南长垣,是自动化一班的成员,兴趣爱好是踢足球,看足球比赛。曾经去过现场…

day5[LangGPT结构化提示词编写实践]

任务要求:利用LangGPT优化提示词,使LLM输出正确结果。

Rebound-hackthebox

端口扫描smb探测 crackmapexec smb 10.10.11.231 -u anonymous -p "" --sharesRID 枚举 使用 CME 工具对指定主机的 SMB 服务进行扫描,并尝试使用 RID 枚举技术获取主机上的用户和组信息。RID 枚举(Relative Identifier enumeration)是一种用于获取 Windows 主机上…

CSP-J 2024 入门组初赛第一轮初赛试题及答案解析

CSP-J 2024 入门组初赛第一轮初赛试题及答案解析 一、 单项选择题(共15题,每题2分,共计30分:每题有且仅有一个正确选项) 1 32 位 int 类型的存储范围是( ) A -2147483647 ~ +2147483647 B -2147483647 ~ +2147483648 C -2147483648 ~ +2147483647 D -2147483648 ~ +…

数字产品护照 (DPP) 解决方案:利用 Blazor 和区块链实现产品全生命周期追踪

数字产品护照 (DPP) 解决方案:利用 Blazor 和区块链实现产品全生命周期追踪 随着全球对可持续发展和产品透明度的关注日益增加,企业需要一种可靠的方法来跟踪和管理产品生命周期中的关键数据。我们的数字产品护照(Digital Product Passport,DPP)系统正是为此而生,提供了一…

四种常用的IO模型

不管是做C端还是做B端,都要接触网络。文件操作,rpc,网上冲浪等,都与网络相关。网络又离不开IO。用的最多的IO操作就是读取和写入了。在Linux系统中,用read系统调用来发起读取操作,用write系统调用来发起写入操作。虽然在开发中,很少接触到底层的原理。但是学习后可以让我…

java学习9.21

今天回炉mybatis的用法,由于之前只是跟着教程走能成功配置数据库,但是一旦出现细小的区别就会产生自己改不了的bug,因此熟悉mybaits和其他技术的内容。知道问题出在哪里,以及怎么改。 mybatis配置 1.导入操作 (1)官网下载jar包并导入 (2)maven直接导入依赖 2.导入完之后创建…

龙祖维的第一次作业

这个作业属于哪个课程 https://edu.cnblogs.com/campus/zjlg/rjjc这个作业的目标 熟悉博客、向老师介绍自己姓名-学号 龙祖维 2022329301084一、自我介绍 基本信息 我叫龙祖维,来自湖南郴州,是22电气工程及其自动化2班的一名学生 兴趣爱好 我喜欢运动,篮球、足球、乒乓及羽毛…

企业如何快速拥有专属的Agent平台?

LinkAI3.0基于企业场景全新打造的Agen3.0平台,既聚焦业务,带来了新的业务型功能,也基于管理需求,增加了通用的管理型功能:全新「客户管理」支持AI生成客户标签及画像;工作流自定义变量拓展个性化的使用场景;支持精细化管控成员角色权限的「企业空间」;「企业专属智能体…

docker阶段04 Docker仓库管理(Harbor), Docker 的资源限制

Docker 仓库管理 上传docker hub官方镜像仓库#1.给本地镜像打标签 上传本地镜像前必须先给上传的镜像用docker tag 命令打标签 标签格式: docker.io/用户帐号/镜像名:TAG #范例: [root@ubuntu1804 ~]#docker tag alpine:3.11 docker.io/wangxiaochun/alpine:3.11-v1#2.登录 do…

【EasyBlog】基于React+AntD+NextJS+NestJS+MySQL打造的开源博客系统

Easy-Blog 是一套集成文章发表、页面创建、知识库管理、博客后台管理等功能于一体的博客系统。Github项目地址:https://github.com/fecommunity/easy-blog, 欢迎Star。Github项目地址:https://github.com/fecommunity/easy-blog, 欢迎Star。Easy-Blog Easy-Blog 是一套集成…

为啥chrome查看到网页,只有5000多行,应该有1万多行才对

大家好,我是皮皮。 一、前言 前几天在Python白银交流群【磐奚鸟】问了一个Python网络爬虫处理的问题,这里拿出来给大家分享下。二、实现过程 这里【惜君】给了一个指导,可能网站有限制数据量。这里【瑜亮老师】发现了问题所在,如下图所示:数据方面确实存在,顺利地解决了粉…