天天看點

從零開始學Qt(64):Graphics View程式基本結構和功能實作

作者:未來奇兵

為了示範Graphics View程式基本結構,本文建立一個執行個體,是一個以QMainWindow為基類的視窗程式,其運作時界面如圖所示。

從零開始學Qt(64):Graphics View程式基本結構和功能實作

執行個體程式的主要功能包括以下幾點。

  • 工作區是一個從QGraphicsView繼承的自定義類QWGraphicsView,作為繪圖的視圖元件。 建立一個QGraphicsScene場景,場景的大小就是圖中的實線矩形框的大小。
  • 改變視窗大小,當視圖大于場景時,矩形框總是居于圖形視圖的中央;當視圖小于場景時, 在視圖視窗自動出現卷滾條。
  • 藍色橢圓正好處于場景的中間,紅色圓形位于場景的右下角。當圖形項位置不在場景的矩形框中時,圖形項也是可以顯示的。
  • 當滑鼠在視窗上移動時,會在狀态欄顯示目前光标位置的視圖坐标和場景坐标,在某個圖形項上單擊滑鼠時,還會顯示在圖形項中的局部坐标。

這個執行個體示範了 Graphics View繪圖幾個類的基本使用方法,示範視圖、場景和繪圖項3個坐标系的關系,以及它們之間的坐标轉換。

自定義圖形視圖元件

QGraphicsView是Qt的圖形視圖元件,可以在UI設計器的Display Widgets分組裡可以拖放一個 QGraphicsView元件到視窗上。但是本執行個體中需要實作滑鼠在QGraphicsView上移動時就顯示目前光标的坐标,這涉及mouseMoveEvent()事件的處理。QGraphicsView沒有與mouseMoveEvent()相關的信号,因而無法定義槽函數與此事件相關聯。

為此,從QGraphicsView繼承定義一個類 GraphicsView,實作mouseMoveEvent()事件和mousePressEvent()事件,并把滑鼠事件轉換為信号,這樣就可以在主程式裡設計槽函數響應這些滑鼠事件。

建立一個檔案,選擇“C++”類别下的“C++ Class”,然後點選“Choose”按鈕,如下圖所示:

從零開始學Qt(64):Graphics View程式基本結構和功能實作

類名設定為GraphicsView,基類輸入為QGraphicsView,然後點選“Next”按鈕完成添加。

從零開始學Qt(64):Graphics View程式基本結構和功能實作

下面是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...”,出現如下圖的對話框。

從零開始學Qt(64):Graphics View程式基本結構和功能實作

在其中選擇基類名稱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結構中場景與視圖的關系,如何建立圖形項元件,場景,視圖,圖形項各自的坐标系以及坐标系之間的轉換關系。