1. 程式人生 > >Qt模型/檢視原理(4):自定義檢視

Qt模型/檢視原理(4):自定義檢視

Qt模型/檢視原理(4):自定義檢視

若對C++語法不熟悉,建議參閱《C++語法詳解》一書,電子工業出版社出版,該書語法示例短小精悍,對查閱C++知識點相當方便,並對語法原理作了詳細講解。

自定義檢視的基本原則如下
1)、檢視需要自行繪製,通常在paintEvent()函式內完成,所以除了必須實現的純虛擬函式外,paintEvent()也應重新實現。另外若需要對單元格進行重新繪製、更新滾動條等,還需要重新實現resizeEvent()函式。
2)、自定義檢視需要完成顯示的單元格的大小和位置的計算、單元格輪廓線的繪製、滾動的計算、對單元格的選擇作出處理、若有必要還需要繪製標頭。
3)、另外需要記住的是檢視就是一個QFrame,也就是說直接使用show()顯示檢視,那麼檢視只是一個什麼也沒有的視窗而已,視窗中的內容需要由程式設計師設計,也就是說你也可以完全不繼承QAbstractItemView類,而子類化QFrame類來實現檢視中內容的繪製,當然這樣會失去對QAbstractItemView類中由Qt已實現的內部函式的使用。
示例8.11為最小的自定義檢視實現,也就是說以下程式碼實現的檢視功能並不完善。完整的實現需要比較複雜的計算量,以下程式碼主要是要讓讀者明白,檢視究竟用來做什麼,做了什麼。從而加深對模型/檢視結構的理解。作為最小實現,本示例只處理單元格輪廓線的繪製、單元格中資料項的繪製,以及簡單的選擇操作
示例8.11的基本規則:使用paintEvent()函式完成由visualRect()函式返回的矩形的輪廓線及其資料項的繪製,其中資料項呼叫委託繪製。

示例8.11:簡單的自定義檢視
//m.h檔案的內容
#ifndef M_H
#define M_H
#include<QtWidgets>

class V:public QAbstractItemView{
public:
//1、以下函式用於計算專案所佔據的矩形(即位置和大小)
 	QRect visualRect(const QModelIndex &index) const{
   		//該函式在初次執行時便會由Qt呼叫,呼叫次數依模型而定,本例3*3的表格模型,
//該函式會被呼叫18次。引數index包含模型的索引,index會在呼叫時迴圈傳遞。
   		//比如對於本例,第一次呼叫時的索引為(0,0),第二次為(0,1),第3次為(0,2)...

   		//計算專案的矩形:專案大小始終為(110,33),位置隨索引而不同。
   		return QRect(index.column()*110,index.row()*33+20,110,33);		}

 //2、以下函式返回滑鼠游標所在位置的專案的索引
QModelIndex indexAt(const QPoint &point) const{		//該函式在點選滑鼠時Qt會呼叫。
    		//引數point包含了滑鼠游標的座標位置(檢視座標)
      	int r=(point.y()-20)/33;			//計算游標位於哪一行。
      	int c=point.x()/110;  				//計算游標位於哪一列。
     	return model()->index(r,c);  } 		//返回該專案的索引。

//3、以下兩個函式主要用於處理對專案的選擇,當選擇檢視中的專案時,Qt才會呼叫他們。
//當不需要選擇專案時,以下兩個函式可以不用實現。
void setSelection(const QRect &rect, QItemSelectionModel::SelectionFlags flags) {
    		//引數rect包含了所選專案的矩形(位置和大小,使用檢視座標)
    		//引數flags包含了選擇專案時的選擇標誌。
    		int r=(rect.y()-20)/33;			//計算選中的是哪一行。
     	int c=rect.x()/110;			//計算選中的是哪一列。
          selectionModel()->select(model()->index(r,c),flags);	}    //選擇所選中的索引。

 	QRegion visualRegionForSelection(const QItemSelection &s) const {
    		//此函式用於計算所有被選擇的專案佔據的區域(即位置和大小)。
    		//引數s包含了所選擇的專案的範圍。
    		return QRegion();    }

//4、以下函式用於計算檢視的滾動,本例是最小實現,不需要滾動,所以不需要實現以下3個函式。
     int horizontalOffset() const{      return 0;	}
     int verticalOffset() const	{      return 0; 	}
void scrollTo(const QModelIndex &index, ScrollHint hint = EnsureVisible){}

//5、本示例不需要隱藏專案,所以以下函式直接返回0即可。
      bool isIndexHidden(const QModelIndex &index) const{    return 0;   }

//6、以下函式用於處理鍵盤按鍵(比如按下左鍵應返回左側的專案索引等),本示例不處理鍵盤按鍵,
//所以不需要實現。
QModelIndex moveCursor(CursorAction cursorAction ,Qt::KeyboardModifiers modifiers){  
 			return QModelIndex(); 	}

//7、以下函式是核心,用於繪製檢視的外觀,也就是說若沒有以下函式,則檢視什麼也不會顯示。
    	void paintEvent(QPaintEvent *e){
        	QPainter pt(viewport());   //在視口上繪製圖形
    	//使用QAbstractItemView::viewOptions()獲取需要繪製的圖形的資訊(此步驟比較重要)
        	QStyleOptionViewItem po=viewOptions();
    //迴圈遍歷模型的大小。
    		for(int r=0;r<model()->rowCount();r++)
        	for(int c=0;c<model()->columnCount();c++){
        		QModelIndex i=model()->index(r,c);
        		QRect rect=visualRect(i);        //獲取索引i所指專案的矩形(位置和大小)
        		po.rect=visualRect(i);
        //處理專案被選擇的情形
        		if(selectionModel()->isSelected(i)){  po.state |= QStyle::State_Selected;}
        //使用代理繪製資料項(即專案),這裡也可使用自定義的代理(若已新增)
         		itemDelegate()->paint(&pt,po,i);
        //以下程式碼用於繪製檢視單元格的輪廓線。
       		pt.save();
        		pt.setPen(QPen(QColor(111,1,1)));  //建立畫筆。
        		pt.drawLine(rect.bottomLeft(),rect.bottomRight());
        		pt.drawLine(rect.bottomRight(),rect.topRight());
        		pt.restore(); 		}  //for結束
    		}  //paintEvent結束
};

class B:public QWidget{    Q_OBJECT
public:    QStandardItemModel *d;    V *pv;
    B(QWidget *p=0):QWidget(p){
   		d=new QStandardItemModel(3,3);
    		V* pv=new V;    		//使用自定義的檢視
	//向模型中新增資料
    		d->setData(d->index(0,0),"AAA");    d->setData(d->index(0,1),"BBB");
    		d->setData(d->index(1,0),"CCC");    d->setData(d->index(1,2),"DDD");
    		d->setData(d->index(1,1),"EEE");    d->setData(d->index(2,0),"FFF");
    		d->setData(d->index(1,1),QIcon("F:/1i.png"),Qt::DecorationRole);

//向檢視中新增標籤,以用於檢視的標頭,以下程式碼主要演示檢視還可以像使用一個QFrame那樣使用。
    		QLabel *pp=new QLabel("111",pv);
    		pp->setAutoFillBackground(1);  //使標籤不透明。
    		pp->setAlignment(Qt::AlignCenter);   pp->resize(111,20);
    		QLabel *pp1=new QLabel("222",pv);    pp1->setAutoFillBackground(1);
    		pp1->setAlignment(Qt::AlignCenter);  pp1->resize(111,20);   pp1->move(111,0);
    		QLabel *pp2=new QLabel("333",pv);	pp2->setAutoFillBackground(1);
    		pp2->setAlignment(Qt::AlignCenter); 	pp2->resize(111,20);	pp2->move(222,0);

    		//pv->setItemDelegate(pt);   //也可以新增自定義代理,以使用自定義代理繪製資料項。
    		pv->setModel(d);    pv->resize(333,222);    pv->show();         }	
};
#endif // M_H

//m.cpp檔案內容
#include "m.h"
int main(int argc, char *argv[]){ QApplication app(argc,argv); B w;  return app.exec(); }

執行結果及說明見圖8-37
在這裡插入圖片描述

本文作者:黃邦勇帥(原名:黃勇)