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:\QT\CH803\build-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開發及實例》


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM