為了示範Graphics View程式基本結構,本文建立一個執行個體,是一個以QMainWindow為基類的視窗程式,其運作時界面如圖所示。
執行個體程式的主要功能包括以下幾點。
- 工作區是一個從QGraphicsView繼承的自定義類QWGraphicsView,作為繪圖的視圖元件。 建立一個QGraphicsScene場景,場景的大小就是圖中的實線矩形框的大小。
- 改變視窗大小,當視圖大于場景時,矩形框總是居于圖形視圖的中央;當視圖小于場景時, 在視圖視窗自動出現卷滾條。
- 藍色橢圓正好處于場景的中間,紅色圓形位于場景的右下角。當圖形項位置不在場景的矩形框中時,圖形項也是可以顯示的。
- 當滑鼠在視窗上移動時,會在狀态欄顯示目前光标位置的視圖坐标和場景坐标,在某個圖形項上單擊滑鼠時,還會顯示在圖形項中的局部坐标。
這個執行個體示範了 Graphics View繪圖幾個類的基本使用方法,示範視圖、場景和繪圖項3個坐标系的關系,以及它們之間的坐标轉換。
自定義圖形視圖元件
QGraphicsView是Qt的圖形視圖元件,可以在UI設計器的Display Widgets分組裡可以拖放一個 QGraphicsView元件到視窗上。但是本執行個體中需要實作滑鼠在QGraphicsView上移動時就顯示目前光标的坐标,這涉及mouseMoveEvent()事件的處理。QGraphicsView沒有與mouseMoveEvent()相關的信号,因而無法定義槽函數與此事件相關聯。
為此,從QGraphicsView繼承定義一個類 GraphicsView,實作mouseMoveEvent()事件和mousePressEvent()事件,并把滑鼠事件轉換為信号,這樣就可以在主程式裡設計槽函數響應這些滑鼠事件。
建立一個檔案,選擇“C++”類别下的“C++ Class”,然後點選“Choose”按鈕,如下圖所示:
類名設定為GraphicsView,基類輸入為QGraphicsView,然後點選“Next”按鈕完成添加。
下面是QWGraphicsView類的定義:
class GraphicsView : public QGraphicsView
{
Q_OBJECT
protected:
void mouseMoveEvent(QMouseEvent * event);
void mousePressEvent(QMouseEvent *event);
public:
GraphicsView(QWidget *parent = 0);
signals:
void mouseMovePoint(QPoint point);
void mouseClicked(QPoint point);
};
mouseMoveEvent()是滑鼠移動事件,其實作代碼如下:
void GraphicsView::mouseMoveEvent(QMouseEvent *event)
{//滑鼠移動事件
QPoint point=event->pos(); //QGraphicsView 的坐标
emit mouseMovePoint(point); //發射信号
QGraphicsView::mouseMoveEvent(event);
}
在此事件響應代碼裡,通過事件的pos()函數擷取滑鼠光标在視圖中的坐标point,然後作為參數發射mouseMovePoint(point)信号。這樣,若在其他地方編寫槽函數與此信号關聯,就可以對滑鼠移動事件作出響應。
mousePressEvent()是滑鼠按鍵按下的事件,其實作代碼如下:
void GraphicsView::mousePressEvent(QMouseEvent *event)
{ //滑鼠左鍵按下事件
if (event->button()==Qt::LeftButton){
QPoint point=event->pos(); //QGraphicsView 的坐标
emit mouseClicked(point);//發射信号
}
QGraphicsView::mousePressEvent(event);
}
在此事件響應代碼裡,首先判斷是否是滑鼠左鍵按下,然後通過事件的pos()函數擷取滑鼠光标在視圖中的坐标point,然後作為參數發射mouseClicked(point)信号。
主視窗類定義與QGraphicsView元件更新
主視窗是一個從QMainWindow繼承的類,重定義了resizeEvent()事件,對視窗改變大小的事件作出響應。槽函數on_mouseMovePoint()響應滑鼠在圖形視圖上移動的事件信号,顯示視圖坐标和場景坐标;槽函數on_mouseClicked()響應滑鼠單擊信号,顯示圖形項的局部坐标;iniGraphicsSystem()用于建立Graphics View結構的各個對象。
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
MainWindow(QWidget *parent = nullptr);
~MainWindow();
private:
Ui::MainWindow *ui;
QGraphicsScene *scene;
QLabel *labViewCord;
QLabel *labSceneCord;
QLabel *labItemCord;
void iniGraphicsSystem(); //建立Graphics View 的各項
protected:
void resizeEvent(QResizeEvent *event);
private slots:
void on_mouseMovePoint(QPoint point);
void on_mouseClicked(QPoint point);
};
主視窗是采用UI設計器進行可視化設計的。在設計界面時,先從元件面闆裡放一個QGraphicsView元件到視窗上。但是我們要用的是從QGraphicsView繼承的自定義圖形視圖元件GraphicsView, 需要将 QGraphicsView 更新為 GraphicsView。
在設計的視窗上選中放置的QGraphicsView元件,單擊右鍵,在快捷菜單中選擇“Promote to...”,出現如下圖的對話框。
在其中選擇基類名稱QGraphicsView,更新類名稱輸入GraphicsView, 然後單擊“Add”和“Promote”按鈕,就可以将視窗上的QGraphicsView元件更新為GraphicsView元件。
視窗初始化
下面是主視窗的構造函數的代碼:
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent), ui(new Ui::MainWindow)
{
ui->setupUi(this);
labViewCord=new QLabel ("View 坐标:");
labViewCord->setMinimumWidth(150);
ui->statusbar->addWidget(labViewCord);
labSceneCord=new QLabel ("Scene 坐标:");
labSceneCord->setMinimumWidth(150);
ui->statusbar->addWidget(labSceneCord);
labItemCord=new QLabel ("Item 坐标:");
labItemCord->setMinimumWidth(150);
ui->statusbar->addWidget(labItemCord);
ui->View->setCursor(Qt::CrossCursor);
ui->View->setMouseTracking(true);
ui->View->setDragMode(QGraphicsView::RubberBandDrag);
QObject::connect(ui->View,SIGNAL(mouseMovePoint(QPoint)), this, SLOT(on_mouseMovePoint(QPoint)));
QObject::connect(ui->View,SIGNAL(mouseClicked(QPoint)), this, SLOT(on_mouseClicked(QPoint)));
iniGraphicsSystem();
}
槽函數on_mouseMovePoint()響應滑鼠在圖形視圖上移動的mouseMovePoint()信号,實作代碼如下:
void MainWindow::on_mouseMovePoint(QPoint point)
{//滑鼠移動事件,point是GraphicsView的坐标,實體坐标
labViewCord->setText(QString::asprintf("View 坐标:%d,%d", point.x(),point.y()));
QPointF pointScene=ui->View->mapToScene(point); //轉換到 Scene 坐标
labSceneCord->setText(QString::asprintf("Scene 坐标:%.0f,%.0f", pointScene.x(),pointScene.y()));
}
信号傳遞的參數point就是滑鼠在圖形視圖中的坐标,使用視圖元件的mapToScene()函數可以将此坐标轉換為場景中的坐标,這兩個坐标在狀态欄上顯示。
槽函數on_mouseClicked()響應滑鼠在圖形視圖上單擊的mouseClicked()信号,代碼如下:
void MainWindow::on_mouseClicked(QPoint point)
{//滑鼠單擊事件
QPointF pointScene=ui->View->mapToScene(point); //轉換到 Scene 坐标
QGraphicsItem *item=NULL;
item=scene->itemAt(pointScene, ui->View->transform () ); //擷取光标下的圖形項
if(item != NULL) //有圖形項
{
QPointF pointItem=item->mapFromScene(pointScene); //圖形項局部坐标
labItemCord->setText(QString::asprintf("Item 坐标:%.0f,%.0f", pointItem.x(), pointItem.y()));
}
}
信号傳遞的參數point就是滑鼠光标在圖形視圖中的坐标,先使用視圖元件的mapToScene() 函數将此坐标轉換為場景中的坐标pointScene,然後通過場景對象的itemAt()函數獲得光标下的圖形項。如果滑鼠光标下有圖形項,就用圖形項的mapFromScene()函數将pointScene轉換為圖形項的局部坐标pointltem,并在狀态欄上顯示。
另外還定義了視窗的resizeEvent()事件的響應函數,以便在視窗變化大小時,顯示視圖區域的大小,以及場景的大小資訊。其代碼如下:
void MainWindow::resizeEvent(QResizeEvent *event)
{//視窗變化大小時的事件
ui->labViewSize->setText(QString::asprintf("Graphics View 坐标,左上角總是(0,0),寬度=%d,高度=%d",
ui->View->width(),ui->View->height()));
QRectF rectF=ui->View->sceneRect(); //Scene 的矩形區
ui->labSceneRect->setText(QString::asprintf("QGraphicsView::sceneRect=(Left,Top,Width,Height)=%.0f,%.0f,%.0f,%.0f",
rectF.left(),rectF.top(), rectF.width(), rectF.height()));
}
Graphics View系統初始化
構造函數中調用的iniGraphicsSystem()用于建立Graphics View結構中的其他元素,包括場景和多個圖形項。其代碼如下:
void MainWindow::iniGraphicsSystem()
{ //構造Graphics View的各項
QRectF rect(-200,-100,400,200);//左上角坐标,寬度,高度
scene=new QGraphicsScene(rect); //scene 邏輯坐标系定義
ui->View->setScene(scene);
//畫一個矩形框,大小等于scene
QGraphicsRectItem *item=new QGraphicsRectItem(rect);
item->setFlags(QGraphicsItem::ItemIsSelectable //設定 flags
| QGraphicsItem::ItemIsFocusable);
QPen pen;
pen.setWidth(2);
item->setPen(pen);
scene->addItem(item);
//畫一個位于scene中心的橢圓,測試局部坐标
QGraphicsEllipseItem *item2=new QGraphicsEllipseItem(-100,-50,200,100);
item2->setPos(0,0);
item2->setBrush(QBrush(Qt::blue));
item2->setFlags(QGraphicsItem::ItemIsMovable
| QGraphicsItem::ItemIsSelectable
| QGraphicsItem::ItemIsFocusable);
scene->addItem(item2);
//畫一個圓,中心位于scene的邊緣
QGraphicsEllipseItem *item3=new QGraphicsEllipseItem(-50,-50,100,100);
item3->setPos(rect.right(),rect.bottom());
item3->setBrush(QBrush(Qt::red));
item3->setFlags(QGraphicsItem::ItemIsMovable
| QGraphicsItem::ItemIsSelectable
| QGraphicsItem::ItemIsFocusable);
scene->addItem(item3);
scene->clearSelection();
}
可視化設計窗體時,将GraphicsView類對象的名稱命名為View,并且自動填充主視窗的工作區。建立場景并與View關聯的代碼如下:
QRectF rect(-200,-100,400,200);//左上角坐标,寬度,高度
scene=new QGraphicsScene(rect); //scene 邏輯坐标系定義
ui->View->setScene(scene);
這裡用一個矩形定義了建立的場景的坐标系統,表示場景的左上角坐标是(-200, -100),場景 寬度為400,高度為200,這樣,場景的中心點是(0,0),這是場景的坐标系。
建立了一個矩形框圖形項item,矩形框的大小就等于建立的場景的大小,矩形框不能移動。
建立的第二個圖形項item2是一個橢圓,橢圓的左上角坐标是(-100,-50),寬度200,高度100, 是以橢圓的中心是(0,0),這是圖形項的局部坐标系。再采用setPos(0,0)設定橢圓在場景中的位置,若不調用setPos()函數設定圖形項在場景中的位置,預設為位置為(0,0)。橢圓設定為可移動、可選擇、可以獲得焦點。
建立的第三個圖形項item3是一個圓,圓的左上角坐标是(-50,-50),寬度100,高度100,所 以圓的中心是(0,0),這是圖形項的局部坐标系。再采用setPos()設定圓在場景中的位置。
item3->setPos(rect.right(),rect.bottom());
其中心位置是場景的右下角,圓的一部分區域是超出了場景的矩形區域的,但是整個圓還是 可以正常顯示的。
從這個執行個體程式可以看到Graphics View結構中場景與視圖的關系,如何建立圖形項元件,場景,視圖,圖形項各自的坐标系以及坐标系之間的轉換關系。