Qt5开发从入门到精通——第八篇三节( 模型/视图结构—— 视图 (View))
2023-09-27 14:19:51 时间
CSDN话题挑战赛第2期
参赛话题:学习笔记
欢迎小伙伴的点评✨✨,相互学习c/c++应用开发。🍳🍳🍳
博主🧑🧑 本着开源的精神交流Qt开发的经验、将持续更新续章,为社区贡献博主自身的开源精神👩🚀
目录
前言
本章节会给大家带来模型/视图结构—— 视图 (View)的开发实例详解。
一、视图 (View)概述
实现自定义的 View, 可继承自 QAbstractltemView 类,对所需的纯虚函数进行重定义与实现,对于QAbstractltemView 类中的纯虚函数,在子类中必须进行重定义,但不一定要实现,可根据需要选择实现。
二、效果实例
图一
图二
新建histogram.txt 文本 另保存为ANSI格式 并将以下内容拷贝到文本中
一部,12,3,5
二部,16,4,0
三部,18,4,2
四部,10,3,1
五部,11,4,3
六部,12,2,4
七部,14,3,5
八部,9,1,1
三、原码解析
histogramview.h
#ifndef HISTOGRAMVIEW_H
#define HISTOGRAMVIEW_H
#include <QAbstractItemView>
#include <QItemSelectionModel>
#include <QRegion>
#include <QMouseEvent>
#include <QWidget>
#include <QTableView>
#include <QMenuBar>
#include <QMenu>
#include <QAction>
#include <QSplitter>
#include <QFileDialog>
#include <QFile>
#include <QTextStream>
#include <QStringList>
class HistogramView : public QAbstractItemView
{
Q_OBJECT
public:
HistogramView(QWidget *parent=0);
//虚函数声明
/*QAbstractltemView 类中的纯虚
函数。这些纯虚函数不一定都要实现,可以根据需要选择性地实现,但一定要声明。*/
QRect visualRect(const QModelIndex &index)const;
void scrollTo(const QModelIndex &index,ScrollHint hint = EnsureVisible);
/*当鼠标在视图中单击或位置发生改变
时被触发,它返回鼠标所在点的 QModellndex 值。若鼠标处在某个数据项的区域中,则返回此数据项的 Index 值,否则返回一个空的 Index 。*/
QModelIndex indexAt (const QPoint &point) const;
//为 selections 赋初值
void setSelectionModel(QItemSelectionModel *selectionModel);
QRegion itemRegion(QModelIndex index);
void paintEvent(QPaintEvent*);
void mousePressEvent(QMouseEvent *event); /*柱状统计图可以被鼠标单击选择,选中后以不同的方式显示。*/
protected slots:
/*当数据项选择发生变化时,此槽函数将响应。*/
void selectionChanged(const QItemSelection &selected,const QItemSelection &deselected);
/*当模型中的数据发生变更时,此槽函数将响应。*/
void dataChanged(const QModelIndex &topLeft,const QModelIndex &bottornRight);
protected:
//虚函数声明
QModelIndex moveCursor(QAbstractItemView::CursorAction cursorAction,Qt::KeyboardModifiers modifiers);
int horizontalOffset()const;
int verticalOffset () const;
bool isIndexHidden(const QModelIndex &index)const;
/*将位于
QRect 内的数据项按照 SelectionFlags (描述被选择的数据项以何种方式进行更新)指定的方式进
行更新。 QltemSelectionModel 类提供多种可用的 SelectionFlags, 常用的有 QltemSelectionModel::
Select 、 QltemSelectionModel: :Current 等。*/
void setSelection (const QRect &rect, QItemSelectionModel::SelectionFlags flags); //(f)
QRegion visualRegionForSelection(const QItemSelection &selection) const;
private:
QItemSelectionModel *selections; //用于保存与视图选择项相关的内容 。
QList<QRegion> MRegionList; //用于保存其中某一类型柱状图的区域范围,而每个区域是 QList 中的一个值。
QList<QRegion> FRegionList;
QList<QRegion> SRegionList;
};
#endif // HISTOGRAMVIEW_H
mainwindow.h
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include <QStandardItemModel>
#include <QTableView>
#include <QMenuBar>
#include <QMenu>
#include <QAction>
#include <QSplitter>
#include <QFileDialog>
#include <QFile>
#include <QTextStream>
#include <QStringList>
#include "histogramview.h"
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = nullptr);
~MainWindow();
void createAction();
void createMenu();
void setupModel();
void setupView();
void openFile(QString);
private:
QMenu *fileMenu;
QAction *openAct;
QStandardItemModel *model;
QTableView *table;
QSplitter *splitter;
HistogramView *histogram;
public slots:
void slotOpen();
};
#endif // MAINWINDOW_H
histogramview.cpp
#include "histogramview.h"
#include <QPainter>
HistogramView::HistogramView(QWidget *parent):QAbstractItemView(parent)
{
}
//paintEvent ()函数具体完成柱状统计图的绘制工作
void HistogramView::paintEvent(QPaintEvent *)
{
QPainter painter(viewport()); //以 viewport()作为绘图设备新建一个 QPainter 对象。
painter.setPen(Qt::black);
int x0=40;
int y0=250;
/*完成了 x 、 y 坐标轴的绘制,并标注坐标轴的变量*/
//y 坐标轴
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;
}
/*完成了表格第 1 列数据的柱状统计图的绘制*/
//男
int posM=x0+20;
MRegionList.clear();
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(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); //将此数据所占据的区域保存到 MRegionList 列表中,为后面的数据项选择做准备。
MRegionList.insert(row,regionM);
posM+=50;
}
/*完成了表格第 2 列数据的柱状统计图的绘制*/
//女
/*完成了表格第 2 列数据的柱状
统计图的绘制。同样,使用不同的画刷颜色区别选中与未被选中的数据项,同时保存每个数据
项所占的区域至 FRegionList 列表中。*/
int posF=x0+30;
FRegionList.clear();
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(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);
FRegionList.insert(row,regionF);
posF+=50;
}
/*完成了表格第 3 列数据的柱状统计图的绘制*/
//退休
/*完成了表格第 3 列数据的柱状
统计图的绘制。同样,使用不同的画刷颜色区别选中与未被选中的数据项,同时保存每个数据
项所占的区域至 SRegionList 列表中。*/
int posS=x0+40;
SRegionList.clear();
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(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);
SRegionList.insert(row,regionS);
posS+=50;
}
}
/*dataChangedO 函数实现当 Model 中的数据更改时,调用绘图设备的 updateO 函数进行更新,
反映数据的变化*/
void HistogramView::dataChanged(const QModelIndex &topLeft,const QModelIndex &bottomRight)
{
QAbstractItemView::dataChanged(topLeft,bottomRight);
viewport()->update();
}
/*setSelectionModelO 函数为 selections 赋初值*/
void HistogramView::setSelectionModel(QItemSelectionModel *selectionModel)
{
selections=selectionModel;
}
/*selectionChanged() 函数中完成当数据项发生变化时调用 update() 函数,重绘绘图设备即可工
作。此函数是将其他 View 中的操作引起的数据项选择变化反映到自身 View 的显示中。*/
void HistogramView::selectionChanged(const QItemSelection &selected,const QItemSelection &deselected)
{
viewport()->update();
}
/*鼠标按下事件函数 mousePressEvent(), 在调用 setSelection()函数时确定鼠标单击点是否在某
个数据项的区域内,并设置选择项。*/
void HistogramView::mousePressEvent(QMouseEvent *event)
{
QAbstractItemView::mousePressEvent(event);
setSelection(QRect(event->pos().x(),event->pos().y(),1,1),QItemSelectionModel::SelectCurrent);
}
void HistogramView::setSelection(const QRect &rect,QItemSelectionModel::SelectionFlags flags)
{
int rows= model()->rowCount(rootIndex()); //获取总行数
int columns = model() ->columnCount (rootIndex ()); //获取总列数
/*用千保存被选中的数据项的 Index 值。此处只实现用鼠标
单击选择,而没有实现用鼠标拖曳框选,因此,鼠标动作只可能选中一个数据项。若需实现框
选,则可使用 QModellndexList 来保存所有被选中的数据项的 Index 值。*/
QModelIndex selectedindex;
/*确定在 rect 中是否含有数据项 。 此处采用遍历的方式将每个数据项的区域与 rect 区域进行
intersected 操作,获得两者之间的交集 。 若此交集不为 空 ,则说明此数据项被选中,将它的 Index
值赋给 selectedlndex 。*/
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); //返回指定 index 的数据项所占用的区域 。
if (!region.intersected(rect).isEmpty())
selectedindex = index;
}
}
if (selectedindex. isValid ())
/*完成 select()函数的调用,即完成最后对选择项的设置工作 。 select()函数是在实现 setSelection()函数时必须调用的.*/
selections->select(selectedindex,flags);
else
{
QModelIndex noindex;
selections->select(noindex,flags);
}
}
QModelIndex HistogramView::indexAt(const QPoint&point)const
{
QPoint newPoint(point.x(),point.y());
QRegion region;
//男 列
foreach(region,MRegionList) //检查当前点是否处千第 1 列(男)数据的区域中 。
{
if(region.contains(newPoint))
{
int row= MRegionList.indexOf(region);
QModelIndex index = model ()->index (row, 1, rootIndex ());
return index;
}
}
//女 列
foreach(region,FRegionList) //检查当前点是否处千第 2 列(女)数据的区域中 。
{
if(region.contains(newPoint))
{
int row= FRegionList.indexOf(region);
QModelIndex index = model()->index (row, 2,rootIndex());
return index;
}
}
//合计 列
/*检查当前点是否处于第 3 列(合计)数据的区域中 。*/
foreach(region,SRegionList)
{
if(region.contains(newPoint))
{
int row= SRegionList.indexOf(region);
QModelIndex index = model ()->index (row,3,rootIndex ());
return index;
}
}
return QModelIndex();
}
QRegion HistogramView::itemRegion(QModelIndex index)
{
QRegion region;
if(index.column() == 1) //男
region= MRegionList[index.row()];
if(index.column() == 2) //女
region = FRegionList[index.row()];
if (index.column() == 3) //退休
region = SRegionList[index.row()];
return region;
}
/*由于本例未用到以下函数的功能,所以没有实现具体内容,但仍然要写出函数体的框架,否则引用具有虚函数的类,会报错*/
QRect HistogramView::visualRect(const QModelIndex &index)const{}
void HistogramView::scrollTo(const QModelIndex &index,ScrollHint) {}
QModelIndex HistogramView::moveCursor(QAbstractItemView::CursorAction cursorAction, Qt::KeyboardModifiers modifiers){}
int HistogramView::horizontalOffset() const{}
int HistogramView::verticalOffset() const {}
bool HistogramView::isIndexHidden(const QModelIndex &index)const{}
QRegion HistogramView::visualRegionForSelection (const QItemSelection & selection)const{}
main.cpp
#include "mainwindow.h"
#include <QApplication>
#include "histogramview.h"
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
MainWindow w;
w.show();
return a.exec();
}
mainwindow.cpp
#include "mainwindow.h"
#include <QItemSelectionModel>
#include "histogramview.h"
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent)
{
createAction();
createMenu();
setupModel();
setupView();
setWindowTitle(tr("View Example"));
resize (600,600) ;
}
MainWindow::~MainWindow()
{
}
void MainWindow::createAction()
{
openAct = new QAction(tr(" 打开 "),this);
connect(openAct,SIGNAL(triggered()) ,this,SLOT(slotOpen()));
}
void MainWindow:: createMenu()
{
fileMenu = new QMenu(tr("文件"),this);
fileMenu->addAction(openAct);
menuBar()->addMenu(fileMenu);
}
//setupModel() 函数新建一个 Model, 并设置表头数据
void MainWindow::setupModel()
{
model= new QStandardItemModel(4,4,this);
model->setHeaderData(0,Qt::Horizontal,tr("部门"));
model->setHeaderData(1,Qt::Horizontal,tr("男"));
model->setHeaderData(2,Qt::Horizontal,tr("女"));
model->setHeaderData(3,Qt::Horizontal,tr("退休"));
}
void MainWindow::setupView()
{
splitter= new QSplitter;
splitter->setOrientation(Qt::Vertical);
histogram = new HistogramView(splitter);
//新建一个 HistogramView 对象
histogram->setModel (model); //为 HistogramView 对象设置相同的 Model
table= new QTableView; //新建一个 QTableView 对象
table->setModel(model); //为 QTableView 对象设置相同的 Model
QItemSelectionModel *selectionModel=new QItemSelectionModel(model);
table->setSelectionModel(selectionModel);
histogram->setSelectionModel(selectionModel); //新建的 QItemSelectionModel 对象作为QTableView 对象和 HistogramView 对象使用的选择模型 。
connect(selectionModel,SIGNAL(selectionChanged(QItemSelection,
ItemSelection)),table,SLOT(selectionChanged(QItemSelection,QItemSelection)));
splitter= new QSplitter;
splitter->setOrientation(Qt::Vertical);
splitter->addWidget(table);
splitter->addWidget(histogram);
setCentralWidget(splitter);
/*连接选择模型的 selection
Changed()信号与 HistogramView 对象的 selectionChanged()槽函数,以便使 QTableView 对象中的
选择变化能够反映到自定义的压stogramView 对象的显示中。*/
connect(selectionModel,SIGNAL(selectionChanged(QItemSelection,QItemSelection)), table,
SLOT(selectionChanged(QItemSelection,QItemSelection)));
connect(selectionModel,SIGNAL(selectionChanged(QItemSelection,QItemSelection)), histogram,
SLOT(selectionChanged(QItemSelection,QItemSelection)));
}
//slotOpenO槽函数完成打开标准文件对话框
void MainWindow::slotOpen()
{
QString name;
name = QFileDialog::getOpenFileName(this," 打开 ",".","histogram files(*.txt)");
if (!name. isEmpty ())
openFile(name);
}
//openFile() 函数完成打开所选的文件内容
void MainWindow::openFile(QString path)
{
if (!path.isEmpty())
{
QFile file (path);
if (file.open(QFile::ReadOnly | QFile::Text))
{
QTextStream stream(&file);
QString line;
model->removeRows(0,model->rowCount (QModelIndex ()),
QModelIndex ()) ;
int row= 0;
do{
line= stream.readLine();
if (!line.isEmpty ())
{
model->insertRows (row, 1, QModelIndex ());
QStringList pieces= line.split(",", QString
:: SkipEmptyParts);
model->setData (model->index (row, 0, QModelIndex ()),
pieces.value(0));
model->setData (model->index (row, 1, QModelIndex ()),
pieces.value(1));
model->setData(model->index(row, 2, QModelIndex()),
pieces.value(2));
model->setData (model->index (row, 3, QModelIndex ()),
pieces.value(3));
row++;
}
}while(!line.isEmpty());
file.close();
}
}
}
四、总结
模型/视图结构—— 视图 (View)会在应用程序开发中经常用到的
相关文章
- Qt5开发从入门到精通——第四篇八节(进度条)
- Qt5开发从入门到精通——第十篇三节(Qt5 网络与通信—— 基于 TCP 的网络聊天室程序)
- Qt5开发从入门到精通——第九篇三节( Qt5 文件及磁盘处理—— 文件大小及路径获取实例)
- Qt5开发从入门到精通——第八篇四节( 模型/视图结构—— 代理 (Delegate))
- Qt5开发从入门到精通——第八篇一节(模型/视图结构—— 文件目录浏览器)
- Qt5开发从入门到精通——第五篇三节( 文本编辑器 Easy Word 开发 V1.2详解 )
- LLBL Gen Pro 5.0 企业应用开发入门
- Hibernate入门
- AngularJS快速入门指南16:Bootstrap
- Node.js开发入门—使用cookie保持登录
- [050] 微信公众平台开发入门视频教程已公布
- 程序员人生:如何开始,如何入门,如何前进
- flink入门(三)——DataStream API
- JQuery第一天——入门概述与选择器
- PHP+MySQL动态网站开发从入门到精通(视频教学版)
- Servlet Filter(过滤器)、Filter是如何实现拦截的、Filter开发入门
- Node.js 入门手册:那些最流行的 Web 开发框架
- Node.js从入门到放弃(一)
- struts2系列(一):struts2入门(struts2的产生、struts2的工作流程、搭建struts2开发环境)
- 《ASP.NET Core项目开发实战入门》带你走进ASP.NET Core开发
- Node.js + Express + MongoDB 实战 TodoList 基础入门
- Oracle19C入门到熟练007-简单查询
- ElasticSearcho从入门到放弃:(二)操作, 编程, 架构原理, ES SQL