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

浙公网安备 33010602011771号