QT学习:视图(View)练习

通过利用自定义的View,实现一个对TableModel的表格数据进行显示的柱状统计图例子,以此介绍如何应用自定义的View。
具体实现步骤如下。
(1)完成主窗体,以便显示View的内容。MainWindow 类继承自QMainWindow类,作为主窗体。以下是头文件“mainwindow.h”的具体代码。

#include <QMainWindow>
#include <QStandardItemModel>
#include <QTableView>
#include <QMenuBar>
#include <QMenu>
#include <QAction>
#include <QSplitter>
class MainWindow : public QMainWindow
{
    Q_OBJECT
public:
    MainWindow(QWidget *parent = 0);
    ~MainWindow();
    void createAction();
    void createMenu();
    void setupModel();
    void setupView();
private:
    QMenu *fileMenu;
    QAction *openAct;
    QStandardItemModel *model;
    QTableView *table;
    QSplitter *splitter;
};

(2)下面是源文件“mainwindow.cpp”中的具体代码:

#include "mainwindow.h"
#include <QItemSelectionModel>
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);
}
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("退休"));
}

setupView()函数的具体实现代码如下:

void MainWindow::setupView()
{
    table = new QTableView;			//新建一个QTableView对象
    table->setModel(model);			//为QTableView对象设置相同的Model
    QItemSelectionModel *selectionModel=new QItemSelectionModel(model);	//新建一个QItemSelectionModel对象作为QTableView对象使用的选择模型。
    table->setSelectionModel(selectionModel);
    connect(selectionModel,SIGNAL(selectionChanged(QItemSelection, ItemSelection)),table,SLOT(selectionChanged(QItemSelection,QItemSelection)));	//连接选择模型的selectionChanged()信号与QTableView对象的selectionChanged()槽函数,以便使自定义的HistogramView对象中的选择变化能够反映到QTableView对象的显示中
    splitter = new QSplitter;
    splitter->setOrientation(Qt::Vertical);
    splitter->addWidget(table);
    setCentralWidget(splitter);
}

此时运行效果如图所示:
在这里插入图片描述
以上只是实现了简单的主窗体框架显示,还没有完成事件。具体实现步骤如下。
(1)在头文件“mainwindow.h”中添加代码如下:

public:
   void openFile(QString);
public slots:
   void slotOpen();

(2)在源文件mainwindow.cpp中添加代码如下:

#include <QFileDialog>
#include <QFile>
#include <QTextStream>
#include <QStringList>

其中,在createAction()函数中添加代码如下:

connect(openAct,SIGNAL(triggered()),this,SLOT(slotOpen()));

slotOpen()槽函数完成打开标准文件对话框,具体代码如下:

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();
         }
    }
}

新建一个文本文件,命名为“histogram.txt”,保存在项目build-ViewEx-Desktop_Qt_5_9_0_MinGW_32bit-Debug目录下,加载文件数据后的运行效果如图所示。
在这里插入图片描述
具体实现步骤如下。
(1)自定义HistogramView类继承自QAbstractItemView类,用于对表格数据进行柱状图显示。下面是头文件“histogramview.h”的具体代码。

#include <QAbstractItemView>
#include <QItemSelectionModel>
#include <QRegion>
#include <QMouseEvent>
class HistogramView : public QAbstractItemView
{
    Q_OBJECT
public:
    HistogramView(QWidget *parent=0);
    //虚函数声明											//visualRect()、scrollTo()、indexAt()、moveCursor()、horizontalOffset()、verticalOffset()、isIndexHidden()、setSelection()和visualRegionForSelection():QAbstractItemView 类中的纯虚函数。这些纯虚函数不一定都要实现,可以根据需要选择性地实现,但一定要声明。
    QRect visualRect(const QModelIndex &index)const;
    void scrollTo(const QModelIndex &index,ScrollHint hint= EnsureVisible);
    QModelIndex indexAt(const QPoint &point)const;		//当鼠标在视图中单击或位置发生改变时被触发,它返回鼠标所在点的QModelIndex值。
   //为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 &bottomRight);				//当模型中的数据发生变更时,此槽函数将响应。
protected:
    //虚函数声明
    QModelIndex moveCursor(QAbstractItemView::CursorAction cursorAction,
          Qt::KeyboardModifiers modifiers);
    int horizontalOffset()const;
    int verticalOffset()const;
    bool isIndexHidden(const QModelIndex &index)const;
    void setSelection(const QRect &rect,QItemSelectionModel:: SelectionFlags flags);															//将位于QRect内的数据项按照SelectionFlags(描述被选择的数据项以何种方式进行更新)指定的方式进行更新。
    QRegion visualRegionForSelection(const QItemSelection &selection) const;
    private:
    QItemSelectionModel *selections;					//用于保存与视图选择项相关的内容。
    QList<QRegion> MRegionList;							//用于保存其中某一类型柱状图的区域范围,而每个区域是QList中的一个值。
    QList<QRegion> FRegionList;
    QList<QRegion> SRegionList;
};

(2)源文件“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.insert(row,regionM);            					//将此数据所占据的区域保存到MRegionList列表中,为后面的数据项选择做准备。
        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列数据的柱状统计图的绘制。
    //退休
    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;
    }
}

dataChanged()函数实现当Model中的数据更改时,调用绘图设备的update()函数进行更新,反映数据的变化。具体实现代码如下:

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

setSelectionModel()函数为selections赋初值,具体代码如下:

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

setSelection()函数的具体代码如下:

void HistogramView::setSelection(const QRect &rect,QItemSelectionModel
::SelectionFlags flags)
{
    int rows = model()->rowCount(rootIndex());		//获取总行数
    int columns = model()->columnCount(rootIndex());	//获取总列数
    QModelIndex selectedIndex;				//用于保存被选中的数据项的Index值。此处只实现用鼠标单击选择,而没有实现用鼠标拖曳框选,因此,鼠标动作只可能选中一个数据项。若需实现框选,则可使用QModelIndexList来保存所有被选中的数据项的Index值。

    for(int row=0; row<rows; ++row)			//确定在rect中是否含有数据项。此处采用遍历的方式将每个数据项的区域与rect区域进行intersected操作,获得两者之间的交集。若此交集不为空,则说明此数据项被选中,将它的Index值赋给selectedIndex。
    {
        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);
    }
}

indexAt()函数的具体内容如下:

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;
        }
    }
    //合计 列
    foreach(region,SRegionList)							//检查当前点是否处于第3列(合计)数据的区域中。

    {
        if(region.contains(newPoint))
        {
            int row = SRegionList.indexOf(region);
            QModelIndex index = model()->index(row,3,rootIndex());
            return index;
        }
    }
    return QModelIndex();
}

由于本例未用到以下函数的功能,所以没有实现具体内容,但仍然要写出函数体的框架,代码如下:

QRect HistogramView::visualRect(const QModelIndex &index)const{}
void HistogramView::scrollTo(const QModelIndex &index,ScrollHint){}
QModelIndex HistogramView::moveCursor(QAbstractItemView::CursorAction cursor Action, 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{}

itemRegion()函数的具体代码如下:

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;
}

(4)在头文件“mainwindow.h”中添加代码如下:

#include "histogramview.h"
private:
    HistogramView *histogram;

(5)在源文件“mainwindow.cpp”中添加代码,其中,setupView()函数的代码修改如下:

void MainWindow::setupView()
{
    splitter = new QSplitter;
    splitter->setOrientation(Qt::Vertical);
    histogram = new HistogramView(splitter);
			//新建一个HistogramView对象
    histogram->setModel(model);			//为HistogramView对象设置相同的Model
    table = new QTableView;	
    table->setModel(model);
    QItemSelectionModel *selectionModel=new QItemSelectionModel (model);
    table->setSelectionModel(selectionModel);
    histogram->setSelectionModel(selectionModel);				//新建的QItemSelectionModel对象作为QTableView对象和HistogramView对象使用的选择模型。
    splitter->addWidget(table);
    splitter->addWidget(histogram);
    setCentralWidget(splitter);
connect(selectionModel,SIGNAL(selectionChanged(QItemSelection,QItemSelection)),table,SLOT(selectionChanged(QItemSelection,QItemSelection)));
connect(selectionModel,SIGNAL(selectionChanged(QItemSelection,QItemSelection)),histogram,SLOT(selectionChanged(QItemSelection,QItemSelection)));															//连接选择模型的selection Changed()信号与HistogramView对象的selectionChanged()槽函数,以便使QTableView对象中的选择变化能够反映到自定义的HistogramView对象的显示中。
}

运行效果如图所示:
在这里插入图片描述

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值