QT学习:视图(View)练习
通過利用自定義的View,實現一個對TableModel的表格數據進行顯示的柱狀統計圖例子,以此介紹如何應用自定義的View。
具體實現步驟如下。
(1)完成主窗體,以便顯示View的內容。MainWindow 類繼承自QMainWindow類,作為主窗體。以下是頭文件“mainwindow.h”的具體代碼。
(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對象設置相同的ModelQItemSelectionModel *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”中添加代碼如下:
(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”的具體代碼。
(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));elsepainter.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));elsepainter.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));elsepainter.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對象設置相同的Modeltable = 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對象的顯示中。 }運行效果如圖所示:
總結
以上是生活随笔為你收集整理的QT学习:视图(View)练习的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: QT学习:模型练习
- 下一篇: QT学习:代理(Delegate)练习