8.4 代理(Delegate)
8.4 代理(Delegate)
在平时我们使用一些表格软件的时候,我们经常会往表格中插入一些内容,但大多数情况下,我们会对表格输入的内容进行限制,比如我们电子表格中经常会将一个表格做成下拉框,又或者是在某些专门填写日期的表格中当用户点击表格时,会出现一个日期的选择的小部件。实际上这些功能我们通过Qt也能实现。并且需要用到Qt中的代理(Delegate)来实现这些功能。
8.4.1 案例,通过Qt中的Delegate来设计表格的控件。
(1)新建一个Qt的GUI项目,将项目命名为DelegateEx,不要勾选创建ui文件的复选框。完成项目的创建。
(2)我们需要从文件中读取数据,因此需要将文件中的数据读入到内存中以便于后期使用。在mainwindow.h中添加以下成员
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include <QMenu>
#include <QMenuBar>
#include <QAction>
#include <QFile>
#include <QFileDialog>
#include <QMessageBox>
#include <QDebug>
#include <QStandardItemModel>
#include <QTableView>
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
MainWindow(QWidget *parent = nullptr);
~MainWindow();
private slots:
void openFileSlots();
private:
void createMenu();
private:
QStandardItemModel*m_model;
QTableView*m_tableView;
private:
QAction*m_openFileAction;
QMenu*m_fileMenu;
private:
QStringList m_data;
};
#endif // MAINWINDOW_H
(3)在mainwindow.cpp中添加这些成员的实现
#include "mainwindow.h"
MainWindow::MainWindow(QWidget *parent):QMainWindow(parent)
{
createMenu();
m_tableView = new QTableView;
}
MainWindow::~MainWindow()
{
delete m_tableView;
m_tableView = nullptr;
if(m_model)
{
delete m_model;
m_model = nullptr;
}
}
void MainWindow::openFileSlots()
{
QString fileName = QFileDialog::getOpenFileName(this,QString("打开数据文件"));
if(fileName.isEmpty())
{
QMessageBox::warning(this,QString("提示"),QString("未选择任何数据文件!"));
}
else
{
QFile file(fileName);
if(!file.open(QIODevice::ReadOnly|QIODevice::Text))
{
QMessageBox::warning(this,QString("提示"),QString("打开文件'%1',失败!").arg(fileName));
}
else
{
while(!file.atEnd())
{
QString lineData = file.readLine();
if(lineData.endsWith('\n'))
{
lineData = lineData.remove('\n');
}
m_data.push_back(lineData);
}
file.close();
qDebug() << m_data;
//加载数据到模型
if(m_data.size() > 0) //如果有数据
{
int rowCount = m_data.size();//先获取数据的条数
int columnCount = m_data.at(0).split(":").size();//获取列数
qDebug() << rowCount << ":" << columnCount;
m_model = new QStandardItemModel(rowCount,columnCount);
//加载表头
for(int i = 0;i<columnCount;i++)
{
m_model->setHeaderData(i,Qt::Horizontal,m_data[0].split(":").at(i));
}
//加载数据
for(int i = 1;i<rowCount;++i)
{
QList<QStandardItem*>items;
for(int j = 0;j<columnCount;++j)
{
items.push_back(new QStandardItem(m_data[i].split(":").at(j)));
}
m_model->insertRow(i-1,items);
}
m_tableView->setModel(m_model);
setCentralWidget(m_tableView);
}
else
{
QMessageBox::information(this,QString("提示"),QString("数据文件为空!"));
}
}
}
}
void MainWindow::createMenu()
{
m_fileMenu = new QMenu(QString("文件"));
m_openFileAction = new QAction(QString("打开"));
m_openFileAction->setShortcut(QString("Ctrl+O"));
m_openFileAction->setToolTip(QString("打开数据文件"));
m_fileMenu->addAction(m_openFileAction);
menuBar()->addMenu(m_fileMenu);
connect(this->m_openFileAction,&QAction::triggered,this,&MainWindow::openFileSlots);
}
(4)新建一个C++类,名称为DateDelegate,我们的DateDelegate类需要继承自QItemDelegate,并且重写里面的createEditor,setData,setEditorData以及updateEditorGeometry方法
datedelegate.h中要有如下方法的声明
#ifndef DATEDELEGATE_H
#define DATEDELEGATE_H
/*
* 如果要实现可以在表格中点击指定列的表格之后出现一个日期组件,那么就需要编写一个类来继承自QItemDelegate类
*/
#include <QObject>
#include <QItemDelegate>
#include <QDateTimeEdit>
#include <QModelIndex>
class DateDelegate:public QItemDelegate
{
Q_OBJECT
public:
DateDelegate(QObject* parent = nullptr);
~DateDelegate();
public:
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;
};
#endif // DATEDELEGATE_H
在datedelegate.cpp中进行实现
#include "datedelegate.h"
DateDelegate::DateDelegate(QObject* parent)
{
}
DateDelegate::~DateDelegate()
{
}
QWidget *DateDelegate::createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const
{
QDateTimeEdit*edit = new QDateTimeEdit(parent);//新建一个日期的组件作为输入的控件
edit->setDisplayFormat("yyyy-MM-dd");//设置显示格式
edit->setCalendarPopup(true);//设置这个日期组件可被弹出
edit->installEventFilter(const_cast<DateDelegate*>(this));//安装事件过滤器,使得DateDelegate能够捕获QDateTimeEdit产生的事件
return edit;
}
void DateDelegate::setEditorData(QWidget *editor, const QModelIndex &index) const
{
/*由于我点击了某个表格之后,就要弹出一个日期组件,但是从逻辑上来说,就必须要把表格的日期先设置到日期组件上*/
QString dateStr = index.model()->data(index).toString();//获取指定表格项的日期字符串
QDate date = QDate::fromString(dateStr);//之后将这个日期字符串构造一个Date类型的变量
QDateTimeEdit*edit = static_cast<QDateTimeEdit*>(editor);//转换对象
edit->setDate(date);//将日期设置到编辑器上
}
void DateDelegate::setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const
{
/*同样的,当我选择了日期组件上的日期之后,我就应该讲这个日期设置到模型中的所选的选项上*/
QDateTimeEdit*dateEdit = static_cast<QDateTimeEdit*>(editor);
QDate date = dateEdit->date();
index.model()->data(index).toString();
model->setData(index,date.toString("yyyy-MM-dd"));
}
void DateDelegate::updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem &option, const QModelIndex &index) const
{
editor->setGeometry(option.rect);
}
(5)同样的,我们新建一个C++类名叫ComboDelegate,来为职业那一栏添加这种下拉框,便于用户选择职业。同样的,我们需要写这个类并继承自QItemDeleage类,然后重写createEditor,setModelData,setEditorData,updateEditorGeometry方法,按照前面的流程可以很容易编写以下代码:
combodelegate.h中声明出需要重写的createEditor,setEditorData,setModelData和updateEditorGeometry方法
#ifndef COMBODELEGATE_H
#define COMBODELEGATE_H
#include <QObject>
#include <QModelIndex>
#include <QItemDelegate>
#include <QComboBox>
class ComboDelegate:public QItemDelegate
{
public:
ComboDelegate(QObject* parent = nullptr);
~ComboDelegate()override;
public:
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;
};
#endif // COMBODELEGATE_H
在combodelegate.cpp中进行实现
#include "combodelegate.h"
ComboDelegate::ComboDelegate(QObject* parent)
{
}
ComboDelegate::~ComboDelegate()
{
}
QWidget *ComboDelegate::createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const
{
QComboBox*jobComboBox = new QComboBox(parent);
jobComboBox->addItem(QString("工人"));
jobComboBox->addItem(QString("医生"));
jobComboBox->addItem(QString("军人"));
jobComboBox->addItem(QString("律师"));
jobComboBox->installEventFilter(const_cast<ComboDelegate*>(this));
return jobComboBox;
}
void ComboDelegate::setEditorData(QWidget *editor, const QModelIndex &index) const
{
QComboBox*jobEditor = static_cast<QComboBox*>(editor);
QString chooseJob = index.model()->data(index).toString();
jobEditor->setCurrentIndex(jobEditor->findText(chooseJob));
}
void ComboDelegate::setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const
{
QComboBox*jobEditor = static_cast<QComboBox*>(editor);
QString job = jobEditor->currentText();
model->setData(index,job);
}
void ComboDelegate::updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem &option, const QModelIndex &index) const
{
editor->setGeometry(option.rect);
}
(5)同样的,我们再写一个关于薪水的下拉框,分别在salarycombodelegate.h和salarycombodelegate.cpp中编写这些代码:
#ifndef SALARYCOMBODELEGATE_H
#define SALARYCOMBODELEGATE_H
#include <QObject>
#include <QModelIndex>
#include <QItemDelegate>
#include <QComboBox>
class SalaryComboDelegate:public QItemDelegate
{
Q_OBJECT
public:
SalaryComboDelegate(QObject* parent = nullptr);
~SalaryComboDelegate()override;
public:
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;
};
#endif // SALARYCOMBODELEGATE_H
#include "salarycombodelegate.h"
SalaryComboDelegate::SalaryComboDelegate(QObject* parent)
{
}
SalaryComboDelegate::~SalaryComboDelegate()
{
}
QWidget *SalaryComboDelegate::createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const
{
QComboBox*salaryComboBox = new QComboBox(parent);
for(unsigned int money = 1000;money < 10000;money += 500)
{
salaryComboBox->addItem(QObject::tr("%1").arg(money));
}
salaryComboBox->installEventFilter(const_cast<SalaryComboDelegate*>(this));
return salaryComboBox;
}
void SalaryComboDelegate::setEditorData(QWidget *editor, const QModelIndex &index) const
{
QString salary = index.model()->data(index).toString();
QComboBox*salaryComboBox = static_cast<QComboBox*>(editor);
salaryComboBox->setCurrentIndex(salaryComboBox->findText(salary));
}
void SalaryComboDelegate::setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const
{
QComboBox*salaryComboBox = static_cast<QComboBox*>(editor);
QString salary = salaryComboBox->currentText();
model->setData(index,salary);
}
void SalaryComboDelegate::updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem &option, const QModelIndex &index) const
{
editor->setGeometry(option.rect);
}
(6)最后,我们需要再mainwindow.h中添加上面那些小部件代理的指针
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include <QMenu>
#include <QMenuBar>
#include <QAction>
#include <QFile>
#include <QFileDialog>
#include <QMessageBox>
#include <QDebug>
#include <QStandardItemModel>
#include <QTableView>
#include "datedelegate.h"
#include "combodelegate.h"
#include "salarycombodelegate.h"
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
MainWindow(QWidget *parent = nullptr);
~MainWindow();
private slots:
void openFileSlots();
private:
void createMenu();
private:
QStandardItemModel*m_model;
QTableView*m_tableView;
DateDelegate*m_dateDelegate;//日期的代理小部件
ComboDelegate*m_comboDelegate;//职业的代理小部件
SalaryComboDelegate*m_salaryComboDelegate;//薪水的代理小部件
private:
QAction*m_openFileAction;
QMenu*m_fileMenu;
private:
QStringList m_data;
};
#endif // MAINWINDOW_H
(7)在mainwindow.cpp中进行实现,我们需要使用QTableView的setItemDelegateForColumn来将小部件设置到表格上
#include "mainwindow.h"
MainWindow::MainWindow(QWidget *parent):QMainWindow(parent)
{
createMenu();
m_tableView = new QTableView;
m_dateDelegate = new DateDelegate;
m_comboDelegate = new ComboDelegate;
m_salaryComboDelegate = new SalaryComboDelegate;
}
MainWindow::~MainWindow()
{
delete m_dateDelegate;
m_dateDelegate = nullptr;
delete m_comboDelegate;
m_comboDelegate = nullptr;
delete m_salaryComboDelegate;
m_salaryComboDelegate = nullptr;
}
void MainWindow::openFileSlots()
{
QString fileName = QFileDialog::getOpenFileName(this,QString("打开数据文件"));
if(fileName.isEmpty())
{
QMessageBox::warning(this,QString("提示"),QString("未选择任何数据文件!"));
}
else
{
QFile file(fileName);
if(!file.open(QIODevice::ReadOnly|QIODevice::Text))
{
QMessageBox::warning(this,QString("提示"),QString("打开文件'%1',失败!").arg(fileName));
}
else
{
while(!file.atEnd())
{
QString lineData = file.readLine();
if(lineData.endsWith('\n'))
{
lineData = lineData.remove('\n');
}
m_data.push_back(lineData);
}
file.close();
qDebug() << m_data;
//加载数据到模型
if(m_data.size() > 0) //如果有数据
{
int rowCount = m_data.size();//先获取数据的条数
int columnCount = m_data.at(0).split(":").size();//获取列数
qDebug() << rowCount << ":" << columnCount;
m_model = new QStandardItemModel(rowCount,columnCount);
//加载表头
for(int i = 0;i<columnCount;i++)
{
m_model->setHeaderData(i,Qt::Horizontal,m_data[0].split(":").at(i));
}
//加载数据
for(int i = 1;i<rowCount;++i)
{
QList<QStandardItem*>items;
for(int j = 0;j<columnCount;++j)
{
items.push_back(new QStandardItem(m_data[i].split(":").at(j)));
}
m_model->insertRow(i-1,items);
}
m_tableView->setModel(m_model);
m_tableView->setItemDelegateForColumn(1,m_dateDelegate);
m_tableView->setItemDelegateForColumn(2,m_comboDelegate);
m_tableView->setItemDelegateForColumn(3,m_salaryComboDelegate);
setCentralWidget(m_tableView);
}
else
{
QMessageBox::information(this,QString("提示"),QString("数据文件为空!"));
}
}
}
}
void MainWindow::createMenu()
{
m_fileMenu = new QMenu(QString("文件"));
m_openFileAction = new QAction(QString("打开"));
m_openFileAction->setShortcut(QString("Ctrl+O"));
m_openFileAction->setToolTip(QString("打开数据文件"));
m_fileMenu->addAction(m_openFileAction);
menuBar()->addMenu(m_fileMenu);
connect(this->m_openFileAction,&QAction::triggered,this,&MainWindow::openFileSlots);
}
(8)运行程序



本节代码:https://files.cnblogs.com/files/blogs/792763/DelegateEx.zip?t=1719422113&download=true

浙公网安备 33010602011771号