8.3 视图(View)

8.3 视图(View)

  实现自定义的视图,既可以通过写一个类来继承自QAbstractItemView类,对其中的纯虚函数进行实现,也可以通过使用现成的一些已经有的类来进行实现。这里我们通过一个案例来说明一下通过自定义的View并借助TableModel来写一个数据显示的柱状统计图的例子。

  8.3.1 先实现读取文本文件中的信息,并将其显示到表格中的这样一种功能

  (1)新建Qt的GUI应用程序,并将项目名称命名为ViewEx,不要勾选创建mainwindow.ui文件。

  (2)在QMainWindow对应的头文件和cpp文件中添加如下内容

    在mainwindow.h中添加这些成员

#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>
#include <QMenu>
#include <QAction>
#include <QMenuBar>
#include <QFileDialog>
#include <QStandardItemModel>
#include <QDebug>
#include <QFile>
#include <QStringList>
#include <QMap>
#include <QList>
#include <QTableView>
class MainWindow : public QMainWindow
{
    Q_OBJECT
public:
    MainWindow(QWidget *parent = nullptr);
    ~MainWindow();
private slots:
    void openSlots();
private:
    void setUpView();
    void loadModel();
    void loadFile(QString fileName);
    void createMenu();
private:
    QStandardItemModel*m_model;
    QMap<int,QStringList>m_data;
    QTableView*m_tableView;
private:
    QMenu*m_memu;
    QAction*m_openFile;
};
#endif // MAINWINDOW_H

  在mainwindow.cpp中添加这些实现

#include "mainwindow.h"

MainWindow::MainWindow(QWidget *parent):QMainWindow(parent)
{
    createMenu();
    connect(this->m_openFile,&QAction::triggered,this,&MainWindow::openSlots);
}

MainWindow::~MainWindow()
{
}

void MainWindow::openSlots()
{
    QString filename = QFileDialog::getOpenFileName(this);
    loadFile(filename);
    loadModel();
    setUpView();
}

void MainWindow::setUpView()
{
    m_tableView = new QTableView;
    if(m_model)
    {
        m_tableView->setModel(m_model);
        QItemSelectionModel*selectModel = new QItemSelectionModel(m_model);
        m_tableView->setSelectionModel(selectModel);
connect(selectModel,SIGNAL(selectionChanged(QItemSelection,QItemSelection)),m_tableView,SLOT(selectionChanged(QItemSelection,QItemSelection)));
    }
    setCentralWidget(m_tableView);
}

void MainWindow::loadModel()
{
    int columnCount = m_data[0].size();
    int rowCount = m_data.size();
    if(columnCount >0 && rowCount > 0)
    {
        qDebug() << columnCount << ":" << rowCount;
        m_model = new QStandardItemModel(rowCount,columnCount,this);
        m_model->setHeaderData(0,Qt::Horizontal,tr("部门"));//设置表头的数据
        m_model->setHeaderData(1,Qt::Horizontal,tr("男"));
        m_model->setHeaderData(2,Qt::Horizontal,tr("女"));
        m_model->setHeaderData(3,Qt::Horizontal,tr("退休"));

        for(int i = 0;i < m_data.size();i++)
        {
            QList<QStandardItem*>rows;//这个列表的Item项,里面可以存储多个Item指针,这意味着可以同时储存多个数据项,然后直接通过insertRow就插入多个项了
            rows.push_back(new QStandardItem(m_data[i].at(0)));
            rows.push_back(new QStandardItem(m_data[i].at(1)));
            rows.push_back(new QStandardItem(m_data[i].at(2)));
            rows.push_back(new QStandardItem(m_data[i].at(3)));
            //另外StandardItem同样支持在里面设置图标
            m_model->insertRow(i,rows);
        }
    }
}

void MainWindow::loadFile(QString fileName)
{
    QFile file(fileName);
    if(!file.open(QIODevice::ReadOnly|QIODevice::Text))
    {
        qDebug() << "Open Data File " << fileName << " Faild!";
        file.close();
    }
    else
    {
        int index = 0;
        while(!file.atEnd())
        {
            QString lineData = file.readLine();
            if(lineData.endsWith('\n'))
            {
                lineData = lineData.remove('\n');
            }
            m_data.operator[](index++) = lineData.split(":");
        }
        file.close();
        qDebug() << m_data;
    }
}

void MainWindow::createMenu()
{
    m_memu = new QMenu(QString("文件"),this);
    m_openFile = new QAction(QString("打开"));
    m_openFile->setShortcut(QString("Ctrl+O"));
    m_openFile->setToolTip(QString("打开数据文件"));
    m_memu->addAction(m_openFile);
    menuBar()->addMenu(m_memu);
}

  运行,并打开data文件

  至此,就已经完成了将文件中的数据加载到表格中的这种操作,接下来,我们需要说明的是柱状图的绘制过程。由于柱状图并不存在于Qt的视图类中,也没有这样的视图类可以处理这种业务需求。所以我们需要自定义一个View来达到目的。我们要写一个HistogramView类来继承自QAbstractItemView,来对表格的数据进行显示。另外需要说明的是,由于QAbstractItemView里面有很多的纯虚函数,但我们只需要去实现其中的一小部分,而其他的虚函数也务必要声明出来并在对应的cpp文件中对其进行"空实现",以保证可以通过编译。

  (1)新建一个C++的类,类名就为HistogramView,在HistogramView.h中添加如下代码

#ifndef HISTOGRAMVIEW_H
#define HISTOGRAMVIEW_H

#include <QObject>
#include <QMouseEvent>
#include <QPainter>
#include <QAbstractItemView>
#include <QModelIndex>
#include <QItemSelectionModel>
#include <QRegion>
class HistogramView:public QAbstractItemView
{
    Q_OBJECT
public:
    explicit HistogramView(QWidget*parent = nullptr);
    ~HistogramView();
public:
    //通过查看Assiatant文档中的QAbstractItemView的相关文档,我们发现,有以下这些纯虚函数需要我们实现
    virtual QModelIndex indexAt(const QPoint &point) const;//该函数是当鼠标在视图中单击或位置发生改变时,它会返回鼠标所在点的QModelIndex值
    virtual void scrollTo(const QModelIndex &index, QAbstractItemView::ScrollHint hint = EnsureVisible);
    virtual QRect visualRect(const QModelIndex &index) const;
public:
    void setSelectionModel(QItemSelectionModel *selectionModel) override;//设置选择的模式
    void paintEvent(QPaintEvent*event)override;//该函数用来完成具体的柱状图的绘制工作
    void mousePressEvent(QMouseEvent *event) override;//柱状图可以被鼠标单击选择,选中后,以不同的方式显示
public:
    QRegion itemRegion(QModelIndex index);
protected slots:
    void selectionChanged(const QItemSelection &selected, const QItemSelection &deselected) override;//当选择的数据项选择发生改变时(当从一个选项变到另一个选项时),会调用到此槽函数
    void dataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight, const QVector<int> &roles = QVector<int>()) override;//当模型中的数据发生改变时,会调用此槽
protected:
    QModelIndex moveCursor(CursorAction cursorAction, Qt::KeyboardModifiers modifiers) override;
    int horizontalOffset() const override;
    int verticalOffset() const override;
    bool isIndexHidden(const QModelIndex &index) const override;
    void setSelection(const QRect &rect, QItemSelectionModel::SelectionFlags command) override;//将位于QRect的范围内的数据项按照SelectionFlags的方式进行更新
    QRegion visualRegionForSelection(const QItemSelection &selection) const override;
private:
    QItemSelectionModel*m_selections;//用于保存视图选择项有关的内容
    QList<QRegion>m_MRegionList;//用于保存与视图选择项相关的内容
    QList<QRegion>m_FRegionList;
    QList<QRegion>m_SRegionList;
};
/*
 * Model
 * Abstract:QAbstractItemModel,QAbstractTableModel,QAbstractListModel
 * Normal:QStandradItemModel,QProxyModel,QDirModel,QFileSystemModel,QSqlQueryModel
*/
/* Viewer
 * Abstract:QAbstractItemView
 * Normal:QTableView,QThreeView,QListView,QColumnView,QHeaderView,QUndoView
*/
/*
 * Delegate
 * Abstract:QAbstractItemDelegate
*/
#endif // HISTOGRAMVIEW_H

    (2)在HistogramView.cpp对这些成员进行实现

#include "histogramview.h"

HistogramView::HistogramView(QWidget*parent)
{

}

HistogramView::~HistogramView()
{
    //Delete Some Dynamic Object There
}

QModelIndex HistogramView::indexAt(const QPoint &point) const
{
    QPoint newPoint(point.x(),point.y());
    QRegion region;
    //男列
    foreach (region,m_MRegionList)
    {
        if(region.contains(newPoint))
        {
            int row = m_MRegionList.indexOf(region);
            QModelIndex index = model()->index(row,1,rootIndex());
            return index;
        }
    }
    //女列
    foreach(region,m_FRegionList)
    {
        if(region.contains(newPoint))
        {
            int row = m_FRegionList.indexOf(region);
            QModelIndex index = model()->index(row,2,rootIndex());
            return index;
        }
    }
    //合计列
    foreach(region,m_SRegionList)
    {
        if(region.contains(newPoint))
        {
            int row = m_SRegionList.indexOf(region);
            QModelIndex index = model()->index(row,3,rootIndex());
            return index;
        }
    }
    return QModelIndex();
}

void HistogramView::scrollTo(const QModelIndex &index, QAbstractItemView::ScrollHint hint)
{

}

QRect HistogramView::visualRect(const QModelIndex &index) const
{

}

void HistogramView::setSelectionModel(QItemSelectionModel *selectionModel)
{
    m_selections = selectionModel;
}

void HistogramView::paintEvent(QPaintEvent *event)
{
    QPainter painter;//以viewport()作为绘图设备新建一个QPainter对象
    painter.begin(viewport());//开始在视口上画画
    painter.setPen(Qt::black);//设置画笔
    int x0 = 40;
    int y0 = 250;
    painter.drawLine(x0,y0,40,30);//从
    painter.drawLine(38,32,40,30);
    painter.drawLine(40,30,42,32);
    painter.drawText(20,30,tr("人数"));
    /*
    for(int i = 1;i<5;i++)
    {
        painter.drawLine(-1,-i*50,1,-i*50);
        painter.drawText(-20,-i*50,tr("%1").arg(i*5));
    }
    */
    //x坐标轴
    painter.drawLine(x0,y0,540,250);
    painter.drawLine(538,248,540,250);
    painter.drawLine(540,250,538,252);
    painter.drawText(545,250,tr("部门"));

    int posD = x0+20;
    int row;
    for(row = 0;row < model()->rowCount(rootIndex());row++)
    {
        QModelIndex index = model()->index(row,0,rootIndex());
        QString dep = model()->data(index).toString();
        painter.drawText(posD,y0+20,dep);
        posD += 50;
    }
    //男
    int posM = x0+20;
    for(row = 0;row<model()->rowCount(rootIndex());row++)
    {
        QModelIndex index = model()->index(row,1,rootIndex());
        int male = model()->data(index).toDouble();
        int width = 10;
        if(m_selections->isSelected(index))
        {
            painter.setBrush(QBrush(Qt::blue,Qt::Dense3Pattern));
        }
        else
        {
            painter.setBrush(Qt::blue);
        }
        painter.drawRect(QRect(posM,y0-male*10,width,male*10));
        QRegion regionM(posM,y0-male*10,width,male*10);
        m_MRegionList << regionM;
        posM += 50;
    }
    //女
    int posF = x0+30;
    for(row = 0;row<model()->rowCount(rootIndex());row++)
    {
        QModelIndex index = model()->index(row,2,rootIndex());
        int female = model()->data(index).toDouble();
        int width = 10;
        if(m_selections->isSelected(index))
        {
            painter.setBrush(QBrush(Qt::red,Qt::Dense3Pattern));
        }
        else
        {
            painter.setBrush(Qt::red);
        }
        painter.drawRect(QRect(posF,y0-female*10,width,female*10));
        QRegion regionF(posF,y0-female*10,width,female*10);
        m_FRegionList<<regionF;
        posF += 50;
    }
    //退休
    int posS = x0+40;
    for(row = 0;row < model()->rowCount(rootIndex());row++)
    {
        QModelIndex index = model()->index(row,3,rootIndex());
        int retire = model()->data(index).toDouble();
        int width = 10;
        if(m_selections->isSelected(index))
        {
            painter.setBrush(QBrush(Qt::green,Qt::Dense3Pattern));
        }
        else
        {
            painter.setBrush(Qt::green);
        }
        painter.drawRect(QRect(posS,y0-retire*10,width,retire*10));
        QRegion regionS(posS,y0-retire*10,width,retire*10);
        m_SRegionList << regionS;
        posS += 50;
    }
    painter.end();
}

void HistogramView::mousePressEvent(QMouseEvent *event)
{
    QAbstractItemView::mousePressEvent(event);
    setSelection(QRect(event->pos().x(),event->pos().y(),1,1),QItemSelectionModel::SelectCurrent);
}

QRegion HistogramView::itemRegion(QModelIndex index)
{
    QRegion region;
    if(index.column() == 1)
    {
        region = m_MRegionList[index.row()];//男
    }
    if(index.column() == 2)
    {
        region = m_FRegionList[index.row()];//女
    }
    if(index.column() == 3)
    {
        region = m_SRegionList[index.row()];//退休
    }
    return region;
}

void HistogramView::selectionChanged(const QItemSelection &selected, const QItemSelection &deselected)
{
    viewport()->update();
}

void HistogramView::dataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight, const QVector<int> &roles)
{
    QAbstractItemView::dataChanged(topLeft,bottomRight);
    viewport()->update();
}

QModelIndex HistogramView::moveCursor(QAbstractItemView::CursorAction cursorAction, Qt::KeyboardModifiers modifiers)
{

}

int HistogramView::horizontalOffset() const
{

}

int HistogramView::verticalOffset() const
{

}

bool HistogramView::isIndexHidden(const QModelIndex &index) const
{

}

void HistogramView::setSelection(const QRect &rect, QItemSelectionModel::SelectionFlags command)
{
    int rows = model()->rowCount(rootIndex());//获取总行数
    int columns = model()->columnCount(rootIndex());//获取总列数
    QModelIndex selectedIndex;//用于保存被选中项的index值,这里只是单击选择,没有实现鼠标拖曳框的选择.若要实现框选,则需要用QModelIndexList来保存被选项
    for(int row = 0;row < rows;++row)
    {
        for(int column = 1;column<columns;++column)
        {
            QModelIndex index = model()->index(row,column,rootIndex());
            QRegion region = itemRegion(index);
            if(!region.intersected(rect).isEmpty())
            {
                selectedIndex = index;
            }
        }
    }
    if(selectedIndex.isValid())
    {
        m_selections->select(selectedIndex,command);
    }
    else
    {
        QModelIndex noIndex;
        m_selections->select(noIndex,command);
    }
}

QRegion HistogramView::visualRegionForSelection(const QItemSelection &selection) const
{

}

    (3)运行项目

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

  

posted @ 2024-06-25 12:27  蜡笔小新Pointer  阅读(54)  评论(0)    收藏  举报