文章目錄
Graphics View(圖形視圖)框架結構取代了之前Qt版本中的QCanvas模塊,它提供了基於圖元的模型/視圖編程,類似於QtInterView的模型/視圖結果,只是這裏的數據是圖形。
圖形視圖體系結果
Graphics View框架結構的特點主要包含元素及座標系統。
Graphics View的特點
Graphics View框架結構的主要特點如下:
- Graphics View框架結構中,系統可以利用Qt繪圖系統的反鋸齒、OpenGL工具來改善繪圖性能。
- Graphics View支持事件傳播體系,可以使圖元在場景(scene)中的交互能力提高1倍,圖元能夠處理鍵盤事件和鼠標事件。其中,鼠標事件包含鼠標按下、移動、釋放和雙擊,還可以跟蹤鼠標的移動。
- 在Graphics View框架中,通過二元空間劃分樹(Binary Space Partitioning, BSP)提供快速的圖元查找,這樣能夠實時顯示包含上百萬個圖元的大場景。
Graphics View的三元素
Graphics View框架結構主要包含三個類:場景類(QGraphicsScene)、視圖類(QGraphicsView)和圖元類(QGraphicsItem),統稱爲“三元素”。其中,場景類提供了一個用於管理位於其中的衆多圖元容器,視圖類用於顯示場景中的圖元,一個場景可以通過多個視圖表現,一個場景包括多個幾何圖形。它們三者這間關係如下:
-
場景類:QGraphicsScene類
這是一個用於放置圖元的容器,本身是不可見的,必須通過與之相連的視圖類來顯示及與外界進行互操作。通過 QGraphicsScene::addItem()可以添加一個圖元到場景中。圖元可以通過多個函數進行檢索。QGraphicsScene::items()和一些重載函數可以返回和點、矩形、多邊形或向量路徑相交的所有圖元。QGraphicsScene::itemAt()返回指定點的頂層圖元。
場景類主要完成的工作包括提供對它包含的圖元的操作接口和傳遞事件、管理各個圖元的狀態(如選擇和焦點處理)、提供無變換的繪製功能(如打印)等。
事件傳播體系結構將場景事件發送給圖元,同時管理圖元之間的事件傳遞。如果場景接收到了在某一點的鼠標單擊事件,場景會將事件傳給這一點的圖元。
管理各個圖元的狀態(如選擇和焦點處理)。可以通過QGraphicsScene::setSelectionArea()函數旋轉圖元,選擇區域可以是任意的形狀,使用QPainterPath表示。若要得到當前選擇的圖元列表,則可以使用函數QGraphicsScene::selectedItems()。可以通過QGraphicsScene::setFocusItem()函數或QGraphicsScene::setFocus()函數來設置圖元的焦點,獲得當前具有焦點的圖元使用函數QGraphicsScene::focusItem()。
如果需要將場景內容繪製到特定的繪圖設備,則可以使用QGraphicsScene::render()函數在繪圖設備上繪製場景。 -
視圖類:QGraphicsView類
它提供一個可視的窗口,用於顯示場景的圖元。在同一個場景中可以有多個視圖,也可以爲相同的數據提供幾種不同的視圖。
QGraphicsView是可滾動的窗口部件,可以提供滾動條來瀏覽大的場景。如果需要使用OpenGL,則可以使用QGraphicsView::setViewport()將視圖設置成爲QGLWideget。
視圖接收鍵盤和鼠標的輸入時間,並將它們翻譯爲場景事件(將座標轉換爲場景的座標)。使用變換矩陣函數QGraphicsView::matrix()可以變換場景的座標,實現場景的縮放和旋轉。QGraphicsView提供QGraphicsView::mapToScene()和QGraphicsView::mapFromScene()用於與場景的座標進行轉換。 -
圖元類:QGraphicsItem類
它是場景中各個圖元的基類,在它的基礎上可以繼承出各種圖元類,Qt已經預置的包括直線(QGraphicsLineItem)、橢圓(QGraphicsEllipseItem)、文本圖元(QGraphicsTextItem)、矩形(QGraphicsRectItem)等。當然,也可以在QGraphicsItem類的基礎上實現自定義的圖元類。用戶可以繼承QGraphicsItem實現符合自己需要的圖元。
圖元有自己的座標系,也提供場景和圖元。圖元還可以通過QGraphicsItem::matrix()來進行自身的變換,可以包含子圖元。
QGraphicsItem主要有以下功能:- 處理鼠標按下、移動、釋放、雙擊、懸停、滾輪和右鍵菜單事件。
- 處理鍵盤輸入事件。
- 處理拖拽事件。
- 分組。
- 碰撞檢測。
QGraphicsView的座標系統
QGraphicsView座標基於笛卡爾座標系,一個圖元的場景具有x座標和y座標。當使用沒有變換的視圖觀察場景時,場景中的一個單元對應屏幕上的一個像素。
三個Graphics View基本類有各自不同的座標系,場景座標、視圖座標和圖元座標。Graphics View提供了三個座標系統之間的轉換函數。在繪製圖形時,Graphics View的場景座標對應QPainter的邏輯座標、視圖座標和設備座標。
- 場景座標
場景座標是所有圖元的基礎座標系統。場景座標系統描述了頂層的圖元,每個圖元都有場景座標和相應的包容框。場景座標的原點在場景中心,座標原點是x軸正方形向右,y軸正方向向下。
QGraphicsScene類的座標系以中心爲原點(0,0)。
- 視圖座標
視圖座標是窗口部件的座標。視圖座標的單位是像素。QGraphicsView視圖的左上角(0,0),x軸正方向向右,y軸正方向向下。所有的鼠標事件最開始都是使用視圖座標。
QGraphicsView類繼承自QWidget類,因此它與其他的QWidget類一樣,以窗口的左上角作爲自己座標系的原點。
- 圖元座標
圖元使用自己的本地座標,這個座標系統通常以圖元中心爲原點,這也是所有變換的原點。圖元座標方向是x軸正方向向右,y軸正方向向下。創建圖元后,只需要注意圖元座標即可,QGraphicsScene和QGraphicsView會完成所有的變換。
QGraphicsItem類的座標系,若在調用QGraphicsView類的paint()函數重繪圖元時,則以此座標系爲基準,如下:
根據需要,Qt提供了這三個座標系之間的互相轉換函數,以及圖元與圖元之間的轉換函數,若需要從QGraphicsItem座標系中的某一點座標轉換到場景中的座標,則可調用QGraphicsItem的mapToScene()函數進行映射。而QGraphicsItem的mapToParent()函數則可將QGraphicsItem座標系中的某點座標映射到它的上一級座標系中,有可能是場景座標,也可能是另一個QGraphicsItem座標。
Graphics View框架提供了多種座標變換函數。如下:
映射函數 | 轉換類型 |
---|---|
QGraphicsView::mapToScene | 視圖到場景 |
QGraphicsView::mapFromScene | 場景到視圖 |
QGraphicsItem::mapFromScene | 場景到圖元 |
QGraphicsItem::mapToScene | 圖元到場景 |
QGraphicsItem::mapToParent | 子圖元到父圖元 |
QGraphicsItem::mapRectFromParent | 父圖元到子圖元 |
QGraphicsItem::mapToItem | 本圖元到其他圖元 |
QGraphicsItem::mapRectFromItem | 其他圖元到本圖元 |
圖形視圖
同示例介紹如何進行自定義QGraphicsItem,以及如何通過利用定時器來實現QGraphicsItem動畫效果。
飛舞的蝴蝶【示例】
設計界面,一個蝴蝶在屏幕上不停地上下飛舞。
- 新建一個Qt Widgets Application項目,項目名稱爲“Butterfly”,基類選擇“QMainWindow”,類名命名爲“mainwindow”,取消“創建界面”複選框。
- 添加新建類“Butterfly”,基類旋轉“QObject”。
- butterfly.h
#ifndef BUTTERFLY_H
#define BUTTERFLY_H
#include <QObject>
#include <QGraphicsItem>
#include <QPainter>
#include <QGraphicsScene>
#include <QGraphicsView>
class Butterfly : public QObject, public QGraphicsItem
{
Q_OBJECT
public:
explicit Butterfly(QObject *parent = nullptr);
void timerEvent(QTimerEvent *event);//定時器實現動畫的原理是在定時器的timerEvent()中對QGraphicsItem進行重繪
QRectF boundingRect()const;//爲圖元限定區域範圍,所有繼承自QGraphicsItem的自定義圖元都必須實現此函數
signals:
public slots:
protected:
void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget = nullptr);//重繪函數
private:
bool up;
QPixmap pix_up;
QPixmap pix_down;
qreal angle;
};
#endif // BUTTERFLY_H
- butterfly.cpp
#include "butterfly.h"
#include <math.h>
const static double PI = 3.1416;
Butterfly::Butterfly(QObject *parent) : QObject(parent)
{
up = true;//標誌蝴蝶翅膀位置的變量賦初值
pix_up.load("up.png");//加載圖片
pix_down.load("down.png");
startTimer(100);
}
QRectF Butterfly::boundingRect() const
{
qreal adjust = 2;
return QRectF(-pix_up.width()/2 - adjust, -pix_up.height()/2-adjust, pix_up.width() + adjust*2, pix_up.height() + adjust*2);
}
void Butterfly::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
{
if(up)
{
painter->drawPixmap(boundingRect().topLeft(), pix_up);
up = !up;
}
else{
painter->drawPixmap(boundingRect().topLeft(), pix_down);
up = !up;
}
}
void Butterfly::timerEvent(QTimerEvent *event)
{
//邊界控制
qreal edgex = scene()->sceneRect().right() + boundingRect().width() / 2;//限定蝴蝶飛舞的右邊界
qreal edgetop = scene()->sceneRect().top() + boundingRect().height() / 2;//限定蝴蝶飛舞的上邊界
qreal edgebottom = scene()->sceneRect().bottom() + boundingRect().height() / 2;//限定蝴蝶飛舞的下邊界
if(pos().x() >= edgex)//超過右邊界,水平回到最左邊
setPos(scene()->sceneRect().left(), pos().y());
if(pos().y() <= edgetop)//超過上邊界,回到底邊
setPos(pos().x(), scene()->sceneRect().bottom());
if(pos().y() >= edgebottom)//超過底邊,回到上邊界
setPos(pos().x(), scene()->sceneRect().top());
angle += (qrand() % 10) / 20.0;
qreal dx = fabs(sin(angle * PI) * 10.0);
qreal dy = fabs(qrand()%20) - 10.0;
setPos(mapToParent(dx, dy));//完成飛行路徑。
}
- main.cpp
#include "mainwindow.h"
#include <QApplication>
#include <QGraphicsScene>
#include "butterfly.h"
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
QGraphicsScene *scene = new QGraphicsScene;
scene->setSceneRect(QRectF(-200, -200, 400, 400));
Butterfly *butterfly = new Butterfly;
butterfly->setPos(-100, 0);
scene->addItem(butterfly);
QGraphicsView *view = new QGraphicsView;
view->setScene(scene);
view->resize(400, 400);
view->show();
// MainWindow w;
// w.show();
return a.exec();
}
- 執行結果
地圖瀏覽器【示例】
實現一個地圖瀏覽器的基本功能,來介紹使用Graphics View框架。
- 新建Qt Widgets Application,項目名稱“MapWidget”,基類選擇“QMainWindow”,類名命名爲“MainWindow”,取消“創建界面”複選框。
- 新建類“MapWidget”,繼承基類“QGraphicsView”。
- mapwidget.h
#ifndef MAPWIDGET_H
#define MAPWIDGET_H
#include <QObject>
#include <QGraphicsView>
#include <QMouseEvent>
#include <QLabel>
class MapWidget : public QGraphicsView
{
Q_OBJECT
public:
MapWidget();
void readMap();//讀取地圖信息
QPointF mapToMap(QPointF);//用於實現場景座標系與地圖座標之間的映射,以獲得某點的經緯度值。
public slots:
void slotZoom(int);
protected:
void drawBackground(QPainter *painter, const QRectF &rect);//完成地圖顯示功能
void mouseMoveEvent(QMouseEvent *event);
private:
QPixmap map;
qreal zoom;
QLabel *viewCoord;
QLabel *sceneCoord;
QLabel *mapCoord;
double x1, y1;
double x2, y2;
};
#endif // MAPWIDGET_H
- mapwidget.cpp
#include "mapwidget.h"
#include <QSlider>
#include <QGridLayout>
#include <QFile>
#include <QTextStream>
#include <QGraphicsScene>
#include <math.h>
MapWidget::MapWidget()
{
//讀取地圖信息
readMap();//用於讀取描述地圖信息的文件
zoom = 50;
int width = map.width();
int height = map.height();
QGraphicsScene *scene = new QGraphicsScene(this);//新建一個對象爲主窗口連接一個場景
//限定場景的顯示區域爲地圖的大小
scene->setSceneRect(-width/2, -height/2, width, height);
setScene(scene);
setCacheMode(CacheBackground);
//用於地圖縮放的滑動條
QSlider *slider = new QSlider;
slider->setOrientation(Qt::Vertical);
slider->setRange(1, 100);
slider->setTickInterval(10);
slider->setValue(50);
connect(slider, SIGNAL(valueChanged(int)), this, SLOT(slotZoom(int)));
QLabel *zoominLabel = new QLabel;
zoominLabel->setScaledContents(true);
zoominLabel->setPixmap(QPixmap("zoomin.png"));
QLabel *zoomoutLabel = new QLabel;
zoomoutLabel->setScaledContents(true);
zoomoutLabel->setPixmap(QPixmap("zoomout.png"));
//座標值顯示區
QLabel *label1 = new QLabel(tr("GraphicsView:"));
viewCoord = new QLabel;
QLabel *label2 = new QLabel(tr("GraphicsScene:"));
sceneCoord = new QLabel;
QLabel *label3 = new QLabel(tr("map:"));
mapCoord = new QLabel;
//座標顯示區佈局
QGridLayout *gridLayout = new QGridLayout;
gridLayout->addWidget(label1, 0, 0);
gridLayout->addWidget(viewCoord, 0, 1);
gridLayout->addWidget(label2, 1, 0);
gridLayout->addWidget(sceneCoord, 1, 1);
gridLayout->addWidget(label3, 2, 0);
gridLayout->addWidget(mapCoord, 2, 1);
gridLayout->setSizeConstraint(QLayout::SetFixedSize);
QFrame *coordFrame = new QFrame;
coordFrame->setLayout(gridLayout);
//縮放控制子佈局
QVBoxLayout *zoomLayout = new QVBoxLayout;
zoomLayout->addWidget(zoominLabel);
zoomLayout->addWidget(slider);
zoomLayout->addWidget(zoomoutLabel);
//座標顯示區域佈局
QVBoxLayout *coordLayout = new QVBoxLayout;
coordLayout->addWidget(coordFrame);
coordLayout->addStretch();
//主佈局
QHBoxLayout *mainLayout = new QHBoxLayout;
mainLayout->addLayout(zoomLayout);
mainLayout->addLayout(coordLayout);
mainLayout->addStretch();
mainLayout->setMargin(30);
mainLayout->setSpacing(10);
setLayout(mainLayout);
setWindowTitle(tr("Map Widget"));
setMinimumSize(600, 400);
}
void MapWidget::readMap()
{
QString mapName;
QFile mapFile("maps.text");//新建一個QFile對象, 描述地圖信息文本
int ok = mapFile.open(QIODevice::ReadOnly);//以只讀方式打開文件
if(ok)
{
QTextStream ts(&mapFile);
if(!ts.atEnd())
{
ts >> mapName;
ts >> x1 >> y1 >> x2 >> y2;
}
}
map.load(mapName);//將地圖讀取到私有變量map中。
}
void MapWidget::slotZoom(int value)
{
qreal s;
if(value > zoom)
{//放大
s = pow(1.01, (value - zoom));
}
else
{//縮小
s = pow(1/1.01, (zoom - value));
}
scale(s, s);
zoom = value;
}
void MapWidget::drawBackground(QPainter *painter, const QRectF &rect)
{
painter->drawPixmap(int(sceneRect().left()), int(sceneRect().top()), map);
}
void MapWidget::mouseMoveEvent(QMouseEvent *event)
{
//QGraphicsView座標
QPoint viewPoint = event->pos();
viewCoord->setText(QString::number(viewPoint.x()) + "," + QString::number(viewPoint.y()));
//QGraphicsScene座標
QPointF scenePoint = mapToScene(viewPoint);
sceneCoord->setText(QString::number(scenePoint.x()) + "," + QString::number(scenePoint.y()));
//地圖座標
QPointF latLon = mapToMap(scenePoint);
mapCoord->setText(QString::number(latLon.x()) + "," + QString::number(latLon.y()));
}
QPointF MapWidget::mapToMap(QPointF p)
{
QPointF latLon;
qreal w = sceneRect().width();
qreal h = sceneRect().height();
qreal lon = y1 - ((h/2 + p.y()) * abs(y1 - y2)/h);
qreal lat = x1 + ((w/2 + p.x()) + abs(x1 - x2)/w);
latLon.setX(lat);
latLon.setY(lon);
return latLon;
}
- main.cpp
#include "mainwindow.h"
#include <QApplication>
#include "mapwidget.h"
#include <QFont>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
QFont font("ARPL KaitiM GB", 12);
font.setBold(true);
a.setFont(font);
MapWidget mapWidget;
mapWidget.show();
// MainWindow w;
// w.show();
return a.exec();
}
- 執行結果
圖元創建【示例】
通過一個示例來演示各個圖元的創建。代碼結構如下:
具體代碼如下:
- main.cpp
#include "mainwindow.h"
#include <QApplication>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
MainWindow w;
w.show();
return a.exec();
}
- mainwindow.h
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include <QGraphicsScene>
#include <QGraphicsView>
#include <QMenuBar>
#include <QGraphicsEllipseItem>
#include "flashitem.h"
#include "startitem.h"
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
MainWindow(QWidget *parent = nullptr);
~MainWindow();
//初始化場景
void initScene();
//創建主窗體的所有動作
void createActions();
//創建主窗體的菜單欄
void createMenus();
public slots:
void slotNew();//新建顯示窗體
void slotClear();//清除場景中所有的圖元
void slotAddEllipseItem();//添加一個橢圓形圖元
void slotAddPolygonItem();//添加一個多邊形圖元
void slotAddTextItem(); //在場景中加入一個文字圖元
void slotAddRectItem(); //在場景中加入一個長方形圖元
void slotAddAlphaItem(); //在場景中加入一個透明蝴蝶圖片
void slotAddFlashItem();//添加flash圖元
void slotAddAnimationItem();//添加動畫圖元
private:
QGraphicsScene *scene;
QAction *newAct;
QAction *clearAct;
QAction *exitAct;
QAction *addEllipseItemAct;
QAction *addPolygonItemAct;
QAction *addTextItemAct;
QAction *addRectItemAct;
QAction *addAlphaItemAct;
QAction *addFlashItemAct;
QAction *addAnimItemAct;
};
#endif // MAINWINDOW_H
- mainwindow.cpp
#include "mainwindow.h"
#include <QGraphicsItemAnimation>
#include <QTimeLine>
#include <QList>
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
{
//創建主窗體的所有動作
createActions();
//創建主窗體的菜單欄
createMenus();
scene = new QGraphicsScene;
scene->setSceneRect(-200, -200, 400, 400);
//初始化場景
initScene();
QGraphicsView *view = new QGraphicsView;
view->setScene(scene);
view->setMinimumSize(400, 400);
view->show();
setCentralWidget(view);
resize(550, 450);
setWindowTitle(tr("Graphics Items"));
}
MainWindow::~MainWindow()
{
}
void MainWindow::createActions()
{
//創建主窗體的所有動作
newAct = new QAction(tr("新建"), this);
clearAct = new QAction(tr("清除"), this);
exitAct = new QAction(tr("退出"), this);
addEllipseItemAct = new QAction(tr("加入 橢圓"),this);
addPolygonItemAct = new QAction(tr("加入 多邊形"),this);
addTextItemAct = new QAction(tr("加入 文字"),this);
addRectItemAct = new QAction(tr("加入 長方形"),this);
addAlphaItemAct = new QAction(tr("加入 圖片"),this);
connect(newAct,SIGNAL(triggered()),this,SLOT(slotNew()));
connect(clearAct,SIGNAL(triggered()),this,SLOT(slotClear()));
connect(exitAct,SIGNAL(triggered()),this,SLOT(close()));
connect(addEllipseItemAct,SIGNAL(triggered()),this,SLOT (slotAddEllipseItem()));
connect(addPolygonItemAct,SIGNAL(triggered()),this,SLOT (slotAddPolygonItem()));
connect(addTextItemAct,SIGNAL(triggered()),this,SLOT (slotAddTextItem()));
connect(addRectItemAct,SIGNAL(triggered()),this,SLOT (slotAddRectItem()));
connect(addAlphaItemAct,SIGNAL(triggered()),this,SLOT (slotAddAlphaItem()));
addFlashItemAct = new QAction(tr("加入閃爍圓"),this);
connect(addFlashItemAct,SIGNAL(triggered()),this,SLOT(slotAddFlashItem()));
addAnimItemAct = new QAction(tr("加入 移動星星"),this);
connect(addAnimItemAct,SIGNAL(triggered()),this,SLOT(slotAddAnimationItem()));
}
void MainWindow::createMenus()
{
QMenu *fileMenu = menuBar()->addMenu(tr("文件"));
fileMenu->addAction(newAct);
fileMenu->addAction(clearAct);
fileMenu->addSeparator();
fileMenu->addAction(exitAct);
QMenu *itemMenu = menuBar()->addMenu(tr("元素"));
itemMenu->addAction(addEllipseItemAct);
itemMenu->addAction(addPolygonItemAct);
itemMenu->addAction(addTextItemAct);
itemMenu->addAction(addRectItemAct);
itemMenu->addAction(addAlphaItemAct);
itemMenu->addAction(addFlashItemAct);
itemMenu->addAction(addAnimItemAct);
}
void MainWindow::initScene()
{
//初始化場景
for(int i = 0; i < 3; i++)
slotAddEllipseItem();
for(int i = 0; i < 3; i++)
slotAddPolygonItem();
for(int i = 0; i < 3; i++)
slotAddTextItem();
for(int i = 0; i < 3; i++)
slotAddRectItem();
for(int i = 0; i < 3; i++)
slotAddAlphaItem();
for(int i = 0; i < 3; i++)
slotAddFlashItem();
for(int i = 0; i < 3; i++)
slotAddAnimationItem();
}
void MainWindow::slotNew()
{
//新建一個顯示窗體
slotClear();
initScene();
MainWindow *newWin = new MainWindow;
newWin->show();
}
void MainWindow::slotClear()
{
//清除場景中的所有圖元
QList<QGraphicsItem*> listItem = scene->items();
while(!listItem.empty())
{
scene->removeItem(listItem.at(0));
listItem.removeAt(0);
}
}
void MainWindow::slotAddEllipseItem()
{
//在場景中加入橢圓形圖元
QGraphicsEllipseItem *item = new QGraphicsEllipseItem(QRectF(0, 0, 80, 60));
item->setPen(Qt::NoPen);
item->setBrush(QColor(qrand() % 256, qrand() % 256, qrand() % 256));
item->setFlag(QGraphicsItem::ItemIsMovable);
scene->addItem(item);
item->setPos((qrand() % int(scene->sceneRect().width())) - 200, (qrand() % int(scene->sceneRect().height())) - 200);
}
void MainWindow::slotAddPolygonItem()
{
QVector<QPoint> v;
v << QPoint(30, -15) << QPoint(0, -30) << QPoint(-30, -15) << QPoint(-30, 15) << QPoint(0, 30) << QPoint(30, 15);
QGraphicsPolygonItem *item = new QGraphicsPolygonItem(QPolygonF(v));
item->setBrush(QColor(qrand() % 256, qrand() % 256, qrand() % 256));
item->setFlag(QGraphicsItem::ItemIsMovable);
scene->addItem(item);
item->setPos((qrand() % int(scene->sceneRect().width())) - 200, (qrand() % int(scene->sceneRect().height())) - 200);
}
void MainWindow::slotAddTextItem()
{
//在場景中添加一個文字圖元
QFont font("Times", 16);
QGraphicsTextItem *item = new QGraphicsTextItem("Hello Qt!");
item->setFont(font);
item->setFlag(QGraphicsItem::ItemIsMovable);
item->setDefaultTextColor(QColor(qrand() % 256, qrand() % 256, qrand() % 256));
scene->addItem(item);
item->setPos((qrand() % int(scene->sceneRect().width())) - 200, (qrand() % int(scene->sceneRect().height())) - 200);
}
void MainWindow::slotAddRectItem()
{
//添加長方形圖元
QGraphicsRectItem *item = new QGraphicsRectItem(QRectF(0, 0, 60, 60));
QPen pen;
pen.setColor(QColor(qrand() % 256, qrand() % 256, qrand() % 256));
item->setPen(pen);
item->setBrush(QColor(qrand() % 256, qrand() % 256, qrand() % 256));
item->setFlag(QGraphicsItem::ItemIsMovable);
scene->addItem(item);
item->setPos((qrand() % int(scene->sceneRect().width())) - 200, (qrand() % int(scene->sceneRect().height())) - 200);
}
void MainWindow::slotAddAlphaItem()
{
//添加一個透明蝴蝶圖片
QGraphicsPixmapItem *item = scene->addPixmap(QPixmap("image.png"));
item->setFlag(QGraphicsItem::ItemIsMovable);
item->setPos((qrand() % int(scene->sceneRect().width())) - 200, (qrand() % int(scene->sceneRect().height())) - 200);
}
void MainWindow::slotAddFlashItem()
{
//添加閃爍圖元
FlashItem *item = new FlashItem;
scene->addItem(item);
item->setPos((qrand() % int(scene->sceneRect().width())) - 200, (qrand() % int(scene->sceneRect().height())) - 200);
}
void MainWindow::slotAddAnimationItem()
{
//添加動畫星星
StartItem *item = new StartItem;
QGraphicsItemAnimation *anim = new QGraphicsItemAnimation;
anim->setItem(item);
QTimeLine *timeLine = new QTimeLine(4000);
timeLine->setCurveShape(QTimeLine::SineCurve);
timeLine->setLoopCount(0);
anim->setTimeLine(timeLine);
int y = (qrand() % 400) - 200;
for(int i = 0; i < 400; i++)
{
anim->setPosAt(i/400.0, QPointF(i-200, y));
}
timeLine->start();
scene->addItem(item);
}
- startitem.h
#ifndef STARTITEM_H
#define STARTITEM_H
#include <QGraphicsItem>
#include <QPainter>
class StartItem : public QGraphicsItem
{
public:
StartItem();
QRectF boundingRect() const;
void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget = nullptr);
private:
QPixmap pix;
};
#endif // STARTITEM_H
- startitem.cpp
#include "startitem.h"
StartItem::StartItem()
{
pix.load("start.png");
}
QRectF StartItem::boundingRect() const
{
return QRectF(-pix.width()/2, -pix.height()/2, pix.width(), pix.height());
}
void StartItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
{
painter->drawPixmap(boundingRect().topLeft(), pix);
}
- flashitem.h
#ifndef FLASHITEM_H
#define FLASHITEM_H
#include <QObject>
#include <QGraphicsItem>
#include <QPainter>
#include <QTimer>
class FlashItem : public QObject, public QGraphicsItem
{
Q_OBJECT
public:
explicit FlashItem(QObject *parent = 0);
QRectF boundingRect() const;
void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget = nullptr);
void timerEvent(QTimerEvent *event);
private:
bool flash;
QTimer *timer;
};
#endif // FLASHITEM_H
- flashitem.cpp
#include "flashitem.h"
FlashItem::FlashItem(QObject *parent)
{
flash = true;//爲顏色切換標識。
setFlag(ItemIsMovable);
startTimer(1000);//啓動定時器
}
QRectF FlashItem::boundingRect() const
{
qreal adjust = 2;
return QRectF(-10-adjust, -10-adjust, 43 + adjust, 43 + adjust);
}
void FlashItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
{
painter->setPen(Qt::NoPen);
painter->setBrush(Qt::darkGray);//深灰色
painter->drawEllipse(-7, -7, 40, 40);
painter->setPen(QPen(Qt::black, 0));
//閃爍區邊線爲黑色
painter->setBrush(flash ? (Qt::red) : (Qt::yellow));
painter->drawEllipse(-10, -10, 40, 40);
}
void FlashItem::timerEvent(QTimerEvent *event)
{
flash = !flash;
update();
}
- 執行效果:
圖元的旋轉、縮放、切變和位移【示例】
通過實現一個示例演示將圖元進行旋轉、縮放、切變和位移
- main.cpp
#include "mainwidget.h"
#include <QApplication>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
MainWidget w;
w.show();
return a.exec();
}
- mainwidget.h
#ifndef MAINWIDGET_H
#define MAINWIDGET_H
#include <QWidget>
#include <QGraphicsView>
#include <QGraphicsScene>
#include <QFrame>
#include "pixitem.h"
class MainWidget : public QWidget
{
Q_OBJECT
public:
MainWidget(QWidget *parent = 0);
~MainWidget();
void createControlFrame();
private:
int angle;
qreal scaleValue;
qreal shearValue;
qreal translateValue;
QGraphicsView *view;
QFrame *ctrlFrame;
PixItem *pixItem;
public slots:
void slotRotate(int);
void slotScale(int);
void slotShear(int);
void slotTranslate(int);
};
#endif // MAINWIDGET_H
- mainwidget.cpp
#include "mainwidget.h"
#include <QHBoxLayout>
#include <QVBoxLayout>
#include <QSlider>
#include <QGroupBox>
#include <math.h>
MainWidget::MainWidget(QWidget *parent)
: QWidget(parent)
{
angle = 0;
scaleValue = 5;
shearValue = 5;
translateValue = 50;
QGraphicsScene *scene = new QGraphicsScene;
//限定新建QGraphicsScene對象的顯示區域
scene->setSceneRect(-200,-200,400,400);
QPixmap *pixmap = new QPixmap("image.png");
pixItem = new PixItem(pixmap);
scene->addItem(pixItem);
pixItem->setPos(0,0);
view = new QGraphicsView; //新建一個視圖對象
view->setScene(scene); //將視圖對象與場景相連
view->setMinimumSize(400,400); //設置視圖的最小尺寸爲(400,400)
ctrlFrame = new QFrame;
createControlFrame(); //新建主窗體右側的控制面板區
//主窗口布局
QHBoxLayout *mainLayout = new QHBoxLayout;
mainLayout->setMargin(10);
mainLayout->setSpacing(20);
mainLayout->addWidget(view);
mainLayout->addWidget(ctrlFrame);
setLayout(mainLayout);
setWindowTitle(tr("Graphics Item Transformation"));//設置主窗體的標題
}
void MainWidget::createControlFrame()
{
//旋轉控制
QSlider *rotateSlider = new QSlider;
rotateSlider->setOrientation(Qt::Horizontal);
rotateSlider->setRange(0,360);
QHBoxLayout *rotateLayout = new QHBoxLayout;
rotateLayout->addWidget(rotateSlider);
QGroupBox *rotateGroup = new QGroupBox(tr("Rotate"));
rotateGroup->setLayout(rotateLayout);
//縮放控制
QSlider *scaleSlider = new QSlider;
scaleSlider->setOrientation(Qt::Horizontal);
scaleSlider->setRange(0,2*scaleValue);
scaleSlider->setValue(scaleValue);
QHBoxLayout *scaleLayout = new QHBoxLayout;
scaleLayout->addWidget(scaleSlider);
QGroupBox *scaleGroup = new QGroupBox(tr("Scale"));
scaleGroup->setLayout(scaleLayout);
//切變控制
QSlider *shearSlider = new QSlider;
shearSlider->setOrientation(Qt::Horizontal);
shearSlider->setRange(0,2*shearValue);
shearSlider->setValue(shearValue);
QHBoxLayout *shearLayout = new QHBoxLayout;
shearLayout->addWidget(shearSlider);
QGroupBox *shearGroup = new QGroupBox(tr("Shear"));
shearGroup->setLayout(shearLayout);
//位移控制
QSlider *translateSlider = new QSlider;
translateSlider->setOrientation(Qt::Horizontal);
translateSlider->setRange(0,2*translateValue);
translateSlider->setValue(translateValue);
QHBoxLayout *translateLayout = new QHBoxLayout;
translateLayout->addWidget(translateSlider);
QGroupBox *translateGroup = new QGroupBox(tr("Translate"));
translateGroup->setLayout(translateLayout);
connect(rotateSlider,SIGNAL(valueChanged(int)),this,SLOT(slotRotate(int)));
connect(scaleSlider,SIGNAL(valueChanged(int)),this,SLOT(slotScale(int)));
connect(shearSlider,SIGNAL(valueChanged(int)),this,SLOT(slotShear(int)));
connect(translateSlider,SIGNAL(valueChanged(int)),this,SLOT(slotTranslate(int)));
//控制面板佈局
QVBoxLayout *frameLayout = new QVBoxLayout;
frameLayout->setMargin(10);
frameLayout->setSpacing(20);
frameLayout->addWidget(rotateGroup);
frameLayout->addWidget(scaleGroup);
frameLayout->addWidget(shearGroup);
frameLayout->addWidget(translateGroup);
ctrlFrame->setLayout(frameLayout);
}
void MainWidget::slotRotate(int value)
{
view->rotate(value-angle);
angle = value;
}
void MainWidget::slotScale(int value)
{
qreal s;
if(value>scaleValue)
s=pow(1.1,(value-scaleValue));
else
s=pow(1/1.1,(scaleValue-value));
view->scale(s,s);
scaleValue=value;
}
void MainWidget::slotShear(int value)
{
view->shear((value-shearValue)/10.0,0);
shearValue=value;
}
void MainWidget::slotTranslate(int value)
{
view->translate(value-translateValue,value-translateValue);
translateValue=value;
}
MainWidget::~MainWidget()
{
}
- pixitem.h
#ifndef PIXITEM_H
#define PIXITEM_H
#include <QGraphicsItem>
#include <QPixmap>
#include <QPainter>
class PixItem : public QGraphicsItem
{
public:
PixItem(QPixmap *pixmap);
QRectF boundingRect() const;
void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget);
private:
QPixmap pix; //作爲圖元顯示的圖片
};
#endif // PIXITEM_H
- pixitem.cpp
#include "pixitem.h"
PixItem::PixItem(QPixmap *pixmap)
{
pix = *pixmap;
}
QRectF PixItem::boundingRect() const
{
return QRectF(-2-pix.width()/2,-2-pix.height()/2,pix.width()+4, pix. height()+4);
}
void PixItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option,QWidget *widget)
{
painter->drawPixmap(-pix.width()/2,-pix.height()/2,pix);
}
- 執行結果