Qt 2D繪圖之六:圖形視圖框架的事件處理與傳播


一、簡介

圖形視圖框架中的事件都是首先由視圖進行接收,然后傳遞給場景,再由場景傳遞給相應的圖形項。而對於鍵盤事件,它會傳遞給獲得焦點的圖形項,可以使用QGraphicsScene類的setFocusItem()函數或者圖形項自身調用setFocus()函數來設置焦點圖形項。默認的,如果場景沒有獲得焦點,那么所有的鍵盤事件都會被丟棄。場景中的圖形項獲得了焦點,場景也會自動獲得焦點。

對於鼠標懸停效果,QGraphicsScene會調度懸停事件。如果一個圖形項可以接收懸停事件,那么當鼠標進入它的區域之中時,它就會收到一個GraphicsSceneHoverEnter事件。如果鼠標繼續在圖形項的區域之中進行移動,那么QGraphicsScene就會向該圖形項發送GraphicsSceneHoverMove事件。當鼠標離開圖形項的區域時,它將會收到一個GraphicsSceneHoverLeave事件。圖形項默認是無法接收懸停事件的,可 以使用QGraphicsItem類的setAcceptHoverEvents()函數使圖形項可以接收懸停事件。

所有的鼠標事件都會傳遞到當前鼠標抓取的圖形項,一個圖形項如果可以接收鼠標事件(默認可以)而且鼠標在它的上面被按下,那么它就會成為場景的鼠標抓取的圖形項。


二、實例

下面來看一個例子。新建空的Qt項目,名稱為myView。完成后先向本項目中添加一個Myltem自定 義圖形項,然后在myitem.h文件中聲明boundingRect()和paint()兩個純虛函數,再聲明並定義一個公共的用於設置圖形項填充色的函數和變量,最后添加一些事件處理函數的聲明,完成后myitem.h文件內容如下:

#ifndef MYITEM_H
#define MYITEM_H

#include <QGraphicsItem>

class MyItem : public QGraphicsItem
{
public:
    MyItem();

    //返回要繪制圖形項的矩形區域
    QRectF boundingRect() const;

    //用來執行實際的繪圖操作
    void paint(QPainter *painter, const QStyleOptionGraphicsItem *option,
                  QWidget *widget);

    //用來設置圖形項填充色
    void setColor(const QColor &color) { brushColor = color; }

protected:
    //鼠標按下事件處理函數,設置被點擊的圖形項獲得焦點,並改變光標外觀
    void mousePressEvent(QGraphicsSceneMouseEvent *event);

    //鍵盤按下事件處理函數,判斷是否是向下方向鍵,如果是,則向下移動圖形項
    void keyPressEvent(QKeyEvent *event);

    //懸停事件處理函數,設置光標外觀和提示
    void hoverEnterEvent(QGraphicsSceneHoverEvent *event);

    //右鍵菜單事件處理函數,為圖形項添加一個右鍵菜單
    void contextMenuEvent(QGraphicsSceneContextMenuEvent *event);

private:
    QColor brushColor; //畫刷顏色

};

#endif // MYITEM_H

使用了變量作為畫刷的顏色,就可以動態指定圖形項的填充色了,默認情況下圖形項的填充色設置為紅色。


然后myitem.cpp文件修改如下:

#include "myitem.h"
#include <QPainter>
#include <QCursor>
#include <QKeyEvent>
#include <QGraphicsSceneHoverEvent>
#include <QGraphicsSceneContextMenuEvent>
#include <QMenu>

MyItem::MyItem()
{
    brushColor = Qt::red;

    setFlag(QGraphicsItem::ItemIsFocusable);
    setFlag(QGraphicsItem::ItemIsMovable);
    setAcceptHoverEvents(true);
}

//返回要繪制圖形項的矩形區域
QRectF MyItem::boundingRect() const
{
    qreal adjust = 0.5;
    return QRectF(-10 - adjust, -10 - adjust,
                  20 + adjust, 20 + adjust);
}

//用來執行實際的繪圖操作
void MyItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option,
                   QWidget *widget)
{
    Q_UNUSED(option); //標明該參數沒有使用
    Q_UNUSED(widget);

    //根據圖形項是否獲得焦點來使用不同顏色繪制圖形項的輪廓
    if(hasFocus())
    {
        painter->setPen(QPen(QColor(255,255,255,200)));
    }
    else
    {
        painter->setPen(QPen(QColor(100,100,100,100)));
    }

    //動態指定圖形項的填充色
    painter->setBrush(brushColor);
    painter->drawRect(-10, -10, 20, 20);
}

//鼠標按下事件處理函數,設置被點擊的圖形項獲得焦點,並改變光標外觀
void MyItem::mousePressEvent(QGraphicsSceneMouseEvent *event)
{
    Q_UNUSED(event);

    setFocus();
    setCursor(Qt::ClosedHandCursor);
}

//鍵盤按下事件處理函數,判斷是否是向下方向鍵,如果是,則向下移動圖形項
void MyItem::keyPressEvent(QKeyEvent *event)
{
    //實現視圖的下移
    if(event->key() == Qt::Key_Down)
        moveBy(0, 10);
}

//懸停事件處理函數,設置光標外觀和提示
void MyItem::hoverEnterEvent(QGraphicsSceneHoverEvent *event)
{
    Q_UNUSED(event);

    setCursor(Qt::OpenHandCursor);
    setToolTip("I am item");
}

//右鍵菜單事件處理函數,為圖形項添加一個右鍵菜單
void MyItem::contextMenuEvent(QGraphicsSceneContextMenuEvent *event)
{
    QMenu menu;
    QAction *moveAction = menu.addAction("move back");
    QAction *selectedAction = menu.exec(event->screenPos());

    if(selectedAction == moveAction)
    {
        setPos(0, 0);
    }
}
  • 在鼠標按下事件處理函數中,為鼠標單擊的圖形項設置了焦點,這樣當按下鍵盤時該圖形項就會接收到按鍵事件。
  • 然后是鍵盤按下事件處理函數,如果按下了鍵盤的向下方向鍵,那么獲得焦點的圖形項就會向下移動,這里使用了 moVeBy(qreal dx, qreal dy)函數,它用來進行相對移動,就是相對於當前位置在水平方向移動dx,在垂直方向移動dy。在進行項目移動時,經常使用到該函數。
  • 然后是懸停事件處理函數,如果將鼠標光標移動到一個圖形項上,可以看到光標外觀改變了,而且出現了工具提示。
  • 然后是右鍵菜單事件,在一個圖形項上右擊,就會彈出一個菜單,如果選中該菜單,那么圖形項會移動到場景原點。

然后myview.h文件修改如下:

#ifndef MYVIEW_H
#define MYVIEW_H

#include <QGraphicsView>

class MyView : public QGraphicsView
{
    Q_OBJECT
public:
    explicit MyView(QWidget *parent = 0);

protected:
	//鍵盤按下事件處理函數
    void keyPressEvent(QKeyEvent *event);

signals:

public slots:

};

#endif // MYVIEW_H

然后myview.cpp文件修改如下:

#include "myview.h"
#include <QKeyEvent>

MyView::MyView(QWidget *parent) :
    QGraphicsView(parent)
{
}

//鍵盤按下事件處理函數
void MyView::keyPressEvent(QKeyEvent *event)
{
    switch (event->key())
    {
        //數字“-”鍵
        case Qt::Key_Plus :
            scale(1.2, 1.2);
        break;

        //數字“+”鍵
        case Qt::Key_Minus :
            scale(1 / 1.2, 1 / 1.2);
        break;

        //右方向鍵
        case Qt::Key_Right :
            rotate(30);
        break;
    }

    QGraphicsView::keyPressEvent(event);
}

這里使用不同的按鍵來實現視圖的縮放和旋轉等操作。一定要注意,在視圖的事件處理函數的最后一定要調用QGraphicsView類的keyPressEvent()函數,不然在場景或者圖形項中就無法再接收到該事件了。


最后修改main.cpp文件,並且將其內容更改如下:

#include <QApplication>
#include "myitem.h"
#include "myview.h"
#include <QTime>

int main(int argc,char* argv[ ])
{
    QApplication app(argc,argv);

    qsrand(QTime(0, 0, 0).secsTo(QTime::currentTime()));

    QGraphicsScene scene;
    scene.setSceneRect(-200, -150, 400, 300);
    
    for(int i = 0; i < 5; ++i) 
    {
        MyItem *item = new MyItem;
        item->setColor(QColor(qrand() % 256, qrand() % 256, qrand() % 256));
        item->setPos(i * 50 - 90, -50);
        scene.addItem(item);
    }

    MyView view;
    view.setScene(&scene);
    view.setBackgroundBrush(QPixmap("../myView/background.png"));
    view.show();

    return app.exec();
}

這里在場景中添加了 5個圖形項,分別為它們設置了隨機顏色,然后將它們排成一行。可以使用鍵盤上的“ + ”和“-”鍵來放大和縮小視圖,也可以使用向右方向鍵來旋轉視圖。


三、效果

執行程序,最初的效果如下:


按右方向鍵后,旋轉視圖后:


鼠標拖動圖形項后:



免責聲明!

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



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