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

 

 

  

posted @ 2024-06-27 01:15  蜡笔小新Pointer  阅读(45)  评论(0)    收藏  举报