zoukankan      html  css  js  c++  java
  • Qt5模型/视图结构-视图(View)

    实现自定义的View,可继承自QAbstractItemView类,对所需的纯虚函数进行重定义与实现,对于QAbstractItemView类中的纯虚函数,在子类中必须进行重定义,但不一定要实现,可根据需要选择实现。

    DEMO

    mainwindow.h

    #ifndef MAINWINDOW_H
    #define MAINWINDOW_H
    
    #include <QMainWindow>
    #include <QStandardItem>
    #include <QTableView>
    #include <QMenuBar>
    #include <QMenu>
    #include <QAction>
    #include <QSplitter>
    #include "histogramview.h"
    
    class MainWindow : public QMainWindow
    {
        Q_OBJECT
    
    public:
        MainWindow(QWidget *parent = 0);
        ~MainWindow();
        void creatAction();
        void creatMenu();
        void setupModel();
        void setupView();
        void openFile(QString);
    public slots:
        void slotOpen();
    private:
        QMenu *fileMenu;
        QAction *openAct;
        QStandardItemModel *model;
        QTableView *table;
        QSplitter *splitter;
        HistogramView *histogram;
    };
    
    #endif // MAINWINDOW_H
    
    

    histogramview.h

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

    mainwindow.cpp

    #include "mainwindow.h"
    #include <QFileDialog>
    #include <QFile>
    #include <QTextStream>
    #include <QStringList>
    
    MainWindow::MainWindow(QWidget *parent)
        : QMainWindow(parent)
    {
        creatAction();
        creatMenu();
        setupModel();
        setupView();
        setWindowTitle(tr("View Example"));
        resize(600,600);
    }
    
    MainWindow::~MainWindow()
    {
    
    }
    
    void MainWindow::creatAction()
    {
        openAct=new QAction(tr("打开"),this);
        connect(openAct,SIGNAL(triggered(bool)),
                this,SLOT(slotOpen()));
    }
    
    void MainWindow::creatMenu()
    {
        fileMenu=new QMenu(tr("文件"),this);
        fileMenu->addAction(openAct);
        menuBar()->addMenu(fileMenu);
    }
    //新建一个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()
    {
        /*
        table=new QTableView;  //新建一个QTableView
        table->setModel(model);  //为QTableView对象设置相同的Model
        QItemSelectionModel *selectionModel=new QItemSelectionModel(model);
        table->setSelectionModel(selectionModel);
        //连接选择模型的selectionModel()信号与QTabelView对象的selectionChanged()槽函数
        //以便使自定义的HistogramView对象中的选择变化能够反映到QTabelView对象的显示中
        connect(selectionModel,SIGNAL(selectionChanged(QItemSelection,
                                                       QItemSelection)),
                                      table,SLOT(selectionChanged(QItemSelection,
                                                                  QItemSelection)));
        splitter=new QSplitter;
        splitter->setOrientation(Qt::Vertical);
        splitter->addWidget(table);
        setCentralWidget(splitter);
        */
    
        splitter=new QSplitter;
        splitter->setOrientation(Qt::Vertical);
    
        histogram=new HistogramView(splitter);  //新建一个Histogram对象
        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);
    
        //连接槽函数,以便使QTableView对象中的选择变化能够反映到自定义的HistogramView对象的显示中
        connect(selectionModel,SIGNAL(selectionChanged(QItemSelection,QItemSelection)),
                table,SLOT(selectionChanged(QItemSelection,QItemSelection)));
        connect(selectionModel,SIGNAL(selectionChanged(QItemSelection,QItemSelection)),
                histogram,SLOT(selectionChanged(QItemSelection,QItemSelection)));
    
    
    }
    //slotOpne()槽函数完成打开标准文件对话框
    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();
            }
        }  //end if(!path.isEmpty())
    }
    

    histogramview.cpp

    #include "histogramview.h"
    #include <QPainter>
    
    HistogramView::HistogramView(QWidget *parent):QAbstractItemView(parent)
    {
    
    }
    //paintEvent()函数完成柱状统计图绘制的工作
    void HistogramView::paintEvent(QPaintEvent *)
    {
        //以viewport()作为绘图设备新建一个QPainter对象
        QPainter painter(viewport());
        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=0;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))
            {
                //Qt::Dense3Pattern是QBrush style
                //如果被选中,则画刷的style改变
                painter.setBrush(QBrush(QColor(91,75,0,255),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;
        } //end for(row=0;row<model()->rowCount(rootIndex());row++)
    
    
        /*完成了表格第2列数据的柱状统计图绘制*/
        //女
        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;
        }  //end for(row=0;row<=model()->rowCount(rootIndex());row++)
        /*完成了表格第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;
        }  //end for(row=0;row<=model()->rowCount(rootIndex());row++)
    }
    //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;
    }
    //selectionChanged()函数中完成当数据项发生变化时调用update()函数
    //重绘绘图设备即可工作
    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值,此处只实现了用鼠标单击选择,而没有实现
        //鼠标拖曳框选,因此,鼠标动作只可能选中一个数据项。若实现框选,则可使用
        //QModelIndexList来保存所有被选中的数据项的Index值
        QModelIndex selectedIndex;
        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())
            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)  //检查当前点是否处于第一列(男)数据的区域中
        {
            if(region.contains(newPoint))
            {
                int row=MRegionList.indexOf(region);
                QModelIndex index=model()->index(row,1,rootIndex());
                return index;
            }
        }
        //女 列
        foreach(region,FRegionList)
        {
            if(region.contains(newPoint))
            {
                int row=FRegionList.indexOf(region);
                QModelIndex index=model()->index(row,2,rootIndex());
                return index;
            }
        }
        //合计 列
        foreach(region,FRegionList)
        {
            if(region.contains(newPoint))
            {
                int row=FRegionList.indexOf(region);
                QModelIndex index=model()->index(row,2,rootIndex());
                return index;
            }
        }
        return QModelIndex();
    }
    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{}
    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;
    }
    

    main.cpp

    #include "mainwindow.h"
    #include <QApplication>
    
    int main(int argc, char *argv[])
    {
        QApplication a(argc, argv);
        MainWindow w;
        w.show();
    
        return a.exec();
    }
    
    

    注意.pro文件如下

    #-------------------------------------------------
    #
    # Project created by QtCreator 2018-09-05T19:29:45
    #
    #-------------------------------------------------
    
    QT       += core gui
    
    greaterThan(QT_MAJOR_VERSION, 4): QT += widgets
    
    TARGET = CH803
    TEMPLATE = app
    
    DEFINES += QT_DEPRECATED_WARNINGS  #新增加的
    
    SOURCES += main.cpp
            mainwindow.cpp 
        histogramview.cpp
    
    HEADERS  += mainwindow.h 
        histogramview.h
    
    

    在该目录下新建文件histogram.txt

    E:QTCH803uild-CH803-Desktop_Qt_5_6_2_MinGW_32bit-Debug
    

    内容如下

    一部,12,3,5
    二部,16,4,0
    三部,18,4,2
    四部,10,3,1
    五部,11,4,3
    六部,12,2,4
    七部,14,3,5
    八部,9,1,1
    

    运行效果如下

    备注
    这个程序里有几个函数我还是没搞懂,不太清楚其中的逻辑

    void HistogramView::setSelection(const QRect &rect,
                                     QItemSelectionModel::SelectionFlags flags)
    
    QModelIndex HistogramView::indexAt(const QPoint &point) const
    

    参考资料《Qt5开发及实例》

  • 相关阅读:
    MyBatis学习之二----应用
    网逛收藏
    Dubbo+Zookeeper 入门Demo
    React + umi +antd+antv/g6 实现力图
    npm、yarn 简单使用记录
    React yarn安装umi后 umi -v查询版本失败
    Eclipse 快速打开文件所在的本地目录
    Windows激活工具
    Win7 node多版本管理gnvm采坑记录
    自定义环形进度条RoundProgressBar
  • 原文地址:https://www.cnblogs.com/Manual-Linux/p/9679342.html
Copyright © 2011-2022 走看看