上一章节示例程序仅演示了Graphics View 的基本结构和三个坐标系的概念,在本章程序中演示其更多的功能。
这个示例程序具有如下的功能:
● 可以创建矩形、椭圆、圆形、三角形、梯形、直线、文字等基本图形项。
● 每个图形项都可以被选择和拖动。
● 图形项或整个视图可以缩放和旋转。
● 图形项重叠时,可以调整前置或后置。
● 多个图形项可以组合,也可以解除组合。
● 可以删除选择的图形项。
● 鼠标在视图上移动时,会在状态栏显示视图坐标和场景坐标。
● 鼠标单击某个图形项时,会显示图形项的局部坐标,还会显示图形项的文字描述和编号。
● 双击某个图形项时,会根据图形项的类型调用颜色对话框或字体对话框,设置图形项的填充颜色、 线条颜色或文字的字体。
● 选中某个图形项时,可以进行按键操作,Delete 键删除图形项,PgUp 放大,PgDn 缩小,空格键旋转 90 度,上下左右光标键移动图形项。
● 鼠标右键长按拖拽场景,在视图小于场景的时候可以拖拽以显示感兴趣内容,而无需拖动滚动条
● 鼠标滚轮可以控制视图的缩放。
TGraphicsView类修改
首先重写TGraphicsView。修改实现的功能如下:
鼠标滚轮视图缩放
添加一个wheelEvent事件,用来获取鼠标滚轮量,正值表示滚轮远离,负值表示滚轮靠近,根据滚轮的正负值进行视图的缩放。
//添加对视图的缩放
void TGraphicsView::wheelEvent(QWheelEvent *event){// 滚轮的滚动量//在Qt 5中,delta() 函数用于获取滚轮的滚动量。但在Qt 6中,你应该使用 angleDelta() 函数来替代 delta() 函数。QPoint scrollAmount = event->angleDelta();// 正值表示滚轮远离使用者放大负值表示朝向使用者缩小scrollAmount.y() > 0 ? ZoomIn() : ZoomOut();
}//放大
void TGraphicsView::ZoomIn(){Zoom(1.1);
}//缩小
void TGraphicsView::ZoomOut(){Zoom(0.9);
}void TGraphicsView::Zoom(float scaleFactor){// 防止过小或过大qreal factor = transform().scale(scaleFactor, scaleFactor).mapRect(QRectF(0, 0, 1, 1)).width();if (factor < 0.07 || factor > 100)return;scale(scaleFactor, scaleFactor);
}
Graphics View的transform函数可以对视图进行从常见的变换,包括平移、缩放、旋转等。setTransform方法可以设置视图的变换矩阵,也可以通过scale、rotate、translate等方法来实现对视图的变换。变换矩阵可以通过QTransform类来表示。QGraphicsView的变换可以对视图中的所有图形项生效,也可以对指定的图形项进行局部变换。
场景拖拽
当视图缩小比场景还小时,有些图形项会看不到,这时候可以通过鼠标右键来拖拽场景使其部分窗口显示在视图中。
设置一个变量m_isTranslate来跟踪鼠标右键状态,右键按下时设置为true,右键松开设置为false,然后在鼠标mouseMoveEvent事件中来拖拽场景。
void TGraphicsView::mousePressEvent(QMouseEvent *event)
{ //鼠标左键按下事件if (event->button()==Qt::RightButton) //右键键长按用于拖拽场景{m_isTranslate = true; //按下标志m_lastMousePos = event->pos();this->setCursor(Qt::OpenHandCursor);}QGraphicsView::mousePressEvent(event);
}//鼠标释放事件
void TGraphicsView::mouseReleaseEvent(QMouseEvent *event){if (event->button() == Qt::RightButton){m_isTranslate = false; //松开标志this->setCursor(Qt::CrossCursor);}QGraphicsView::mouseReleaseEvent(event);}void TGraphicsView::mouseMoveEvent(QMouseEvent *event)
{ //鼠标移动事件if(m_isTranslate){//获取QPointF mouseDelta = event->pos()-m_lastMousePos; //获取移动的向量,方向指的是newpointTranslate(mouseDelta);}m_lastMousePos = event->pos();}void TGraphicsView::Translate(QPointF delta)
{int w = viewport()->rect().width(); //获取视图宽度int h = viewport()->rect().height(); //获取视图高度// (w / 2,h / 2)即为视图的中心QPoint newCenter(w / 2. - delta.x()+0.5, h / 2. - delta.y()+0.5); //获取中心的偏移量centerOn(mapToScene(newCenter)); //反馈到场景当中,mapToScene将坐标转为场景坐标
}
代码中通过按下后当前点和上一次的点进行向量计算,得到拖拽的偏移量,然后转成中心点的偏移量,然后将中心坐标映射到场景中心,重新设置场景中心就相当于拖拽了场景。
完整的自定义Graphics View类代码如下所示:
/.h文件///
#ifndef TGRAPHICSVIEW_H
#define TGRAPHICSVIEW_H#include <QObject>
#include <QGraphicsView>class TGraphicsView : public QGraphicsView
{Q_OBJECTprotected:void mouseMoveEvent(QMouseEvent *event);void mousePressEvent(QMouseEvent *event);void mouseDoubleClickEvent(QMouseEvent *event);void keyPressEvent(QKeyEvent *event);void wheelEvent(QWheelEvent *event);void mouseReleaseEvent(QMouseEvent *event);public:TGraphicsView(QWidget *parent = nullptr);void ZoomIn();void ZoomOut();void Zoom(float scaleFactor);void Translate(QPointF delta);signals:void mouseMovePoint(QPoint point); //鼠标移动void mouseClicked(QPoint point); //鼠标单击void mouseDoubleClick(QPoint point); //双击事件void keyPress(QKeyEvent *event); //按键事件private:bool m_isTranslate;QPoint m_lastMousePos;};#endif // TGRAPHICSVIEW_H//.cpp文件
#include "tgraphicsview.h"
#include <QMouseEvent>
#include <QPoint>void TGraphicsView::mouseMoveEvent(QMouseEvent *event)
{ //鼠标移动事件if(m_isTranslate){//获取QPointF mouseDelta = event->pos()-m_lastMousePos; //获取移动的向量,方向指的是newpointTranslate(mouseDelta);}m_lastMousePos = event->pos();QPoint point=event->pos(); //QGraphicsView的坐标emit mouseMovePoint(point); //发射信号QGraphicsView::mouseMoveEvent(event);
}void TGraphicsView::mousePressEvent(QMouseEvent *event)
{ //鼠标左键按下事件if (event->button()==Qt::RightButton) //右键键长按用于拖拽场景{m_isTranslate = true; //按下标志m_lastMousePos = event->pos();this->setCursor(Qt::OpenHandCursor);}if(event->button()==Qt::LeftButton){ //左键长按用于移动图形项目QPoint point=event->pos(); //QGraphicsView的坐标emit mouseClicked(point); //发射信号}QGraphicsView::mousePressEvent(event);
}void TGraphicsView::mouseDoubleClickEvent(QMouseEvent *event)
{ //鼠标双击事件if (event->button()==Qt::LeftButton){QPoint point=event->pos(); //QGraphicsView的坐标emit mouseDoubleClick(point); //发射信号}QGraphicsView::mouseDoubleClickEvent(event);
}void TGraphicsView::keyPressEvent(QKeyEvent *event)
{ //按键事件emit keyPress(event); //发射信号QGraphicsView::keyPressEvent(event);
}//鼠标释放事件
void TGraphicsView::mouseReleaseEvent(QMouseEvent *event){if (event->button() == Qt::RightButton){m_isTranslate = false; //松开标志this->setCursor(Qt::CrossCursor);}QGraphicsView::mouseReleaseEvent(event);}//添加对视图的缩放
void TGraphicsView::wheelEvent(QWheelEvent *event){// 滚轮的滚动量//在Qt 5中,delta() 函数用于获取滚轮的滚动量。但在Qt 6中,你应该使用 angleDelta() 函数来替代 delta() 函数。QPoint scrollAmount = event->angleDelta();// 正值表示滚轮远离使用者放大负值表示朝向使用者缩小scrollAmount.y() > 0 ? ZoomIn() : ZoomOut();QGraphicsView::wheelEvent(event);
}//放大
void TGraphicsView::ZoomIn(){Zoom(1.1);
}//缩小
void TGraphicsView::ZoomOut(){Zoom(0.9);
}void TGraphicsView::Zoom(float scaleFactor){// 防止过小或过大qreal factor = transform().scale(scaleFactor, scaleFactor).mapRect(QRectF(0, 0, 1, 1)).width();if (factor < 0.07 || factor > 100)return;scale(scaleFactor, scaleFactor);
}void TGraphicsView::Translate(QPointF delta)
{int w = viewport()->rect().width(); //获取视图宽度int h = viewport()->rect().height(); //获取视图高度// (w / 2,h / 2)即为视图的中心QPoint newCenter(w / 2. - delta.x()+0.5, h / 2. - delta.y()+0.5); //获取中心的偏移量centerOn(mapToScene(newCenter)); //反馈到场景当中,mapToScene将坐标转为场景坐标
}TGraphicsView::TGraphicsView(QWidget *parent):QGraphicsView(parent)
{}
主界面窗口程序
主窗口类
头文件如下所示
///mainwindow.h///#ifndef MAINWINDOW_H
#define MAINWINDOW_H#include <QMainWindow>#include <QGraphicsScene>
#include <QLabel>
#include <QRandomGenerator>QT_BEGIN_NAMESPACE
namespace Ui { class MainWindow; }
QT_END_NAMESPACEclass MainWindow : public QMainWindow
{Q_OBJECTprivate:const quint32 boundValue=100; //随机数上限值// QRandomGenerator* random; //随机数发生器const int ItemId = 1; //绘图项自定义数据的keyconst int ItemDesciption = 2; //绘图项自定义数据的keyint seqNum=0; //用于图形项的编号,每个图形项有一个编号int backZ=0; //用于bring to frontint frontZ=0; //用于bring to backQGraphicsScene *scene; //场景QLabel *labViewCord; //用于状态栏QLabel *labSceneCord;QLabel *labItemCord;QLabel *labItemInfo;void setItemProperty(QGraphicsItem* item, QString desciption); //设置图形项的属性
public:MainWindow(QWidget *parent = nullptr);~MainWindow();
private slots://自定义槽函数void do_mouseMovePoint(QPoint point); //鼠标移动void do_mouseClicked(QPoint point); //鼠标单击void do_mouseDoubleClick(QPoint point); //鼠标双击void do_keyPress(QKeyEvent *event); //按键void on_actItem_Rect_triggered();void on_actItem_Ellipse_triggered();void on_actItem_Polygon_triggered();void on_actEdit_Delete_triggered();void on_actZoomIn_triggered();void on_actZoomOut_triggered();void on_actRestore_triggered();void on_actRotateLeft_triggered();void on_actRotateRight_triggered();void on_actEdit_Front_triggered();void on_actEdit_Back_triggered();void on_actItem_Line_triggered();void on_actItem_Text_triggered();void on_actGroup_triggered();void on_actGroupBreak_triggered();void on_actItem_Circle_triggered();void on_actItem_Triangle_triggered();// void on_actBackBrush_triggered();void on_actHelp_triggered();
private:Ui::MainWindow *ui;
};#endif // MAINWINDOW_H
● 常数 boundValue 用于设置随机数发生器产生随机数的数值上限值。
● 常数 ItemId 和 ItemDesciption 是用于设置图形项的自定义数据时用到的键。
● 变量 seqNum 用于给每个图形项编号,每个图形项有一个唯一编号。
● 变量 frontZ 用于设置图形项的叠放顺序,数值越大,越在前面显示。
● 变量 backZ 用于设置图形项的叠放顺序,数值越小,越在后面显示。
私有函数 setItemProperty()用于在场景图形项后,设置图形项的属性。
在主窗口构造函数中,为视图创建场景对象scene,并与界面上的组件关联,连接视图发送的鼠标和按键对应的响应槽函数。注意这些信号是视图中自动监听事件发生后传递给外界的,视图本生的事件函数处理的是对于视图自身的操作,而这些信号是发送给其他组件的,在这个例子中是mainwindow组件用于组件中的通信(这些信号也可以被视图自生使用,这里没有使用)
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);labItemInfo=new QLabel("ItemInfo: ");labItemInfo->setMinimumWidth(200);ui->statusBar->addWidget(labItemInfo);scene=new QGraphicsScene(-300,-200,600,400); //创建QGraphicsScene// scene->setBackgroundBrush(QBrush(Qt::white));ui->view->setScene(scene); //scene与view关联ui->view->setCursor(Qt::CrossCursor); //设置鼠标光标// ui->view->setCursor(Qt::DragMoveCursor); //设置鼠标光标ui->view->setMouseTracking(true); //设置鼠标跟踪ui->view->setDragMode(QGraphicsView::RubberBandDrag); //设置拖动模式this->setCentralWidget(ui->view);connect(ui->view,&TGraphicsView::mouseMovePoint,this, &MainWindow::do_mouseMovePoint);connect(ui->view,&TGraphicsView::mouseClicked,this, &MainWindow::do_mouseClicked);connect(ui->view,&TGraphicsView::keyPress, this, &MainWindow::do_keyPress);connect(ui->view,&TGraphicsView::mouseDoubleClick,this, &MainWindow::do_mouseDoubleClick);// QObject::connect(ui->view,SIGNAL(mouseMovePoint(QPoint)), this, SLOT(do_mouseMovePoint(QPoint)));// QObject::connect(ui->view,SIGNAL(mouseClicked(QPoint)),this, SLOT(do_mouseClicked(QPoint)));// QObject::connect(ui->view,SIGNAL(mouseDoubleClick(QPoint)), this, SLOT(do_mouseDoubleClick(QPoint)));// QObject::connect(ui->view,SIGNAL(keyPress(QKeyEvent*)),this, SLOT(do_keyPress(QKeyEvent*)));
}
鼠标与键盘信号槽函数
鼠标移动:鼠标在视图上移动时,在状态栏显示光标处的视图坐标和场景坐标。
鼠标左键单击:在视图上单击鼠标选中一个图形项时,程序会在状态栏上显示图形项的局部坐标,并提取其自定义信息并显示。获得的视图坐标 point 需要转换为场景中的坐标 pointScene,再利用 QGraphicsScene 的itemAt()函数获得光标处的图形项。利用 QGraphicsItem 的 mapFromScene()函数将 pointScene 转换为图形项的局部坐标 pointItem。用 data()函数提取图形项的自定义数据。
鼠标左键双击:当鼠标双击某个图形项时,当图形项是矩形、圆形、梯形等有填充色的对象时打开一个颜色选择对话框,设置其填充颜色;当图形项是直线时,设置其线条颜色;当图形项是文字时,打开一个字体对话框,设置其字体。
键盘按键操作:在选中一个图形项之后,我们可以通过键盘按键实现一些快捷操作,例如缩放、旋转、移动等。
这四个槽函数对应的定义如下所示:
//鼠标移动,point是视图的坐标,物理坐标
void MainWindow::do_mouseMovePoint(QPoint point)
{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()));
}//鼠标单击事件
void MainWindow::do_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()));labItemInfo->setText(item->data(ItemDesciption).toString()+", ItemId="+item->data(ItemId).toString());}
}template<class T> void setBrushColor(T *item)
{QColor color=item->brush().color();color=QColorDialog::getColor(color,NULL,"选择填充颜色");if (color.isValid())item->setBrush(QBrush(color));
}//鼠标双击事件,调用相应的对话框,设置填充颜色、线条颜色或字体
void MainWindow::do_mouseDoubleClick(QPoint point)
{QPointF pointScene=ui->view->mapToScene(point); //转换到Scene坐标QGraphicsItem *item=NULL;item=scene->itemAt(pointScene,ui->view->transform()); //获取光标下的图形项if (item == NULL)return;switch (item->type()) //图形项的类型{case QGraphicsRectItem::Type: //矩形框,QGraphicsRectItem的枚举值Type{QGraphicsRectItem *theItem =qgraphicsitem_cast<QGraphicsRectItem*>(item);setBrushColor(theItem);break;}case QGraphicsEllipseItem::Type: //椭圆或圆{QGraphicsEllipseItem *theItem =qgraphicsitem_cast<QGraphicsEllipseItem*>(item);setBrushColor(theItem);break;}case QGraphicsPolygonItem::Type: //梯形或三角形{QGraphicsPolygonItem *theItem=qgraphicsitem_cast<QGraphicsPolygonItem*>(item);setBrushColor(theItem);break;}case QGraphicsLineItem::Type: //直线,设置线条颜色{QGraphicsLineItem *theItem=qgraphicsitem_cast<QGraphicsLineItem*>(item);QPen pen=theItem->pen();QColor color=theItem->pen().color();color=QColorDialog::getColor(color,this,"选择线条颜色");if (color.isValid()){pen.setColor(color);theItem->setPen(pen);}break;}case QGraphicsTextItem::Type: //文字,设置字体{QGraphicsTextItem *theItem=qgraphicsitem_cast<QGraphicsTextItem*>(item);QFont font=theItem->font();bool ok=false;font=QFontDialog::getFont(&ok,font,this,"设置字体");if (ok)theItem->setFont(font);break;}}
}//按键事件
void MainWindow::do_keyPress(QKeyEvent *event)
{if (scene->selectedItems().count()!=1)return; //没有选中的图形项,或选中的多于1个QGraphicsItem *item=scene->selectedItems().at(0);if (event->key()==Qt::Key_Delete) //删除scene->removeItem(item);else if (event->key()==Qt::Key_Space) //顺时针旋转90度item->setRotation(90+item->rotation());else if (event->key()==Qt::Key_PageUp) //放大item->setScale(0.1+item->scale());else if (event->key()==Qt::Key_PageDown)//缩小item->setScale(-0.1+item->scale());else if (event->key()==Qt::Key_Left) //左移item->setX(-1+item->x());else if (event->key()==Qt::Key_Right) //右移item->setX(1+item->x());else if (event->key()==Qt::Key_Up) //上移item->setY(-1+item->y());else if (event->key()==Qt::Key_Down) //下移item->setY(1+item->y());
}
图形项的创建
setItemProperty()函数用于设置图形项的属性,如是否可移动,可选中,可获得焦点、叠放顺序、在场景中的位置,图形项编号、描述;最后再将该图形项添加到场景中(场景是一个容器);
void MainWindow::setItemProperty(QGraphicsItem *item,QString desciption)
{item->setFlags(QGraphicsItem::ItemIsMovable //可移动| QGraphicsItem::ItemIsSelectable //可选中| QGraphicsItem::ItemIsFocusable); //可以获得焦点item->setZValue(++frontZ); //叠放顺序号quint32 v1=QRandomGenerator::global()->bounded(boundValue);quint32 v2=QRandomGenerator::global()->bounded(boundValue);item->setPos(v1,v2); //在场景中的位置item->setData(ItemId,++seqNum); //图形项编号item->setData(ItemDesciption,desciption); //图形项描述scene->addItem(item); //添加到场景scene->clearSelection();item->setSelected(true);
}
setZValue()函数设置图形项的 Z 值,Z 值控制叠放顺序,当有多个图形项叠放在一起时,Z 值最大
的显示在最前面。这里意思是越新创建的叠放是在最前面。
setPos(x, y)函数设置图形项的位置,如果图形项有父容器项,坐标(x, y)是父容器的坐标,否则就是图形场景的坐标。
程序中使用了全局的随机数发生器 QRandomGenerator::global(),使用函数 bounded() 产生限制范围的随机数。将随机数生成的值作为图形项的放置位置。。
setData()函数用于设置图形项的自定义数据,这个函数的原型定义如下:
void QGraphicsItem::setData(int key, const QVariant &value)
参数 key 是数据名称,value 是具体的数据内容,可以是任何类型。key 和 value 是一个键值对使 用 setData()一次可以设置一个键值对,可以为一个图形项设置多个自定义键值对。
创建矩形
void MainWindow::on_actItem_Rect_triggered()
{ //添加一个矩形//x,y 为左上角的图元局部坐标,图元中心点为0,0QGraphicsRectItem *item=new QGraphicsRectItem(-50,-25,100,50);item->setBrush(QBrush(Qt::yellow));setItemProperty(item, "矩形");
}
创建椭圆
void MainWindow::on_actItem_Ellipse_triggered()
{ //添加一个椭圆QGraphicsEllipseItem *item=new QGraphicsEllipseItem(-50,-30,100,60);item->setBrush(QBrush(Qt::blue)); //填充颜色setItemProperty(item, "椭圆");
}
创建梯形
void MainWindow::on_actItem_Polygon_triggered()
{ //添加一个梯形QGraphicsPolygonItem *item=new QGraphicsPolygonItem;QPolygonF points;points.append(QPointF(-40,-40)); //添加顶点坐标points.append(QPointF(40,-40));points.append(QPointF(100,40));points.append(QPointF(-100,40));item->setPolygon(points); //创建多边形item->setBrush(QBrush(Qt::green));setItemProperty(item,"梯形");
}
创建直线
void MainWindow::on_actItem_Line_triggered()
{//添加直线QGraphicsLineItem *item=new QGraphicsLineItem(-100,0,100,0);QPen pen(Qt::red);pen.setWidth(3);item->setPen(pen);setItemProperty(item,"直线");
}
创建文字
void MainWindow::on_actItem_Text_triggered()
{ //添加文字QString str=QInputDialog::getText(this,"输入文字","请输入文字");if (str.isEmpty())return;QGraphicsTextItem *item=new QGraphicsTextItem(str);QFont font=this->font();font.setPointSize(20);font.setBold(true);item->setFont(font); //设置字体setItemProperty(item,"文字");
}
创建圆形
void MainWindow::on_actItem_Circle_triggered()
{ //添加圆形QGraphicsEllipseItem *item=new QGraphicsEllipseItem(-50,-50,100,100);item->setBrush(QBrush(Qt::cyan));setItemProperty(item,"圆形");
}
创建三角形
void MainWindow::on_actItem_Triangle_triggered()
{ //添加三角形QGraphicsPolygonItem *item=new QGraphicsPolygonItem;QPolygonF points;points.append(QPointF(0,-40)); //添加顶点坐标points.append(QPointF(60,40));points.append(QPointF(-60,40));// points.append(QPointF(0,0)); //添加顶点坐标// points.append(QPointF(50,0));// points.append(QPointF(0,50));item->setPolygon(points); //三角形就是一种多边形item->setBrush(QBrush(Qt::magenta));setItemProperty(item,"三角形");
}
图形项的操作
主窗口上水平工具栏上的一些按钮实现图形项的缩放、旋转、组合等操作。
缩放
图形项的缩放使用 QGraphicsItem 的 setScale()函数,参数大于 1 是放大,小于 1 是缩小。下面是放大和缩小按钮关联的槽函数代码:
void MainWindow::on_actZoomIn_triggered()
{ //放大int cnt=scene->selectedItems().count(); //选中图形项的个数if (cnt==1) //缩放单个图形项{QGraphicsItem *item;item=scene->selectedItems().at(0);item->setScale(0.1+item->scale());}else //缩放视图ui->view->scale(1.1, 1.1);
}void MainWindow::on_actZoomOut_triggered()
{//缩小int cnt=scene->selectedItems().count(); //选中图形项的个数if (cnt==1) //缩放单个图形项{QGraphicsItem *item;item=scene->selectedItems().at(0);item->setScale(item->scale()-0.1);}else //缩放视图ui->view->scale(0.9,0.9);
}
QGraphicsScene 的 selectedItems()函数返回场景中选中的图形项的列表。如果只有一个图形项被选 中,就用 QGraphicsItem 的 setScale()函数对图形项进行缩放;如果选中的图形项个数大于 1个,或没有图形项被选中,就用 QGraphicsView 的 scale()函数对绘图视图进行缩放。
旋转
图形项的旋转使用 QGraphicsItem 的 setRotation()函数,参数为角度值,正值表示顺时针旋转,负值表示逆时针旋转。下面是左旋转和右旋转按钮关联的槽函数代码:
void MainWindow::on_actRotateRight_triggered()
{//顺时针旋转,右旋转int cnt=scene->selectedItems().count();if (cnt==1) //单个图形项旋转{QGraphicsItem* item=scene->selectedItems().at(0);item->setRotation(+30+item->rotation());}else //视图旋转ui->view->rotate(+30);
}void MainWindow::on_actEdit_Front_triggered()
{ //bring to front,前置int cnt=scene->selectedItems().count();if (cnt>0){ //只处理选中的第1个图形项QGraphicsItem* item=scene->selectedItems().at(0);item->setZValue(++frontZ);}
}
恢复坐标变换
缩放和旋转都是坐标变换,要取消所有变换恢复初始状态,调用 QGraphicsItem 或QGraphicsView 的 resetTransform()函数。下面是“恢复”按钮关联的槽函数代码:
void MainWindow::on_actRestore_triggered()
{//取消所有变换int cnt=scene->selectedItems().count(); //选中图形项的个数if (cnt==1) //针对单个图形项{QGraphicsItem* item=scene->selectedItems().at(0);// item->resetTransform(); //不起作用item->setRotation(0);item->setScale(1.0);}else //针对视图ui->view->resetTransform();
}
改变叠放顺序
void MainWindow::on_actEdit_Front_triggered()
{ //bring to front,前置int cnt=scene->selectedItems().count();if (cnt>0){ //只处理选中的第1个图形项QGraphicsItem* item=scene->selectedItems().at(0);item->setZValue(++frontZ);}
}void MainWindow::on_actEdit_Back_triggered()
{//bring to back,后置int cnt=scene->selectedItems().count();if (cnt>0){//只处理选中的第1个图形项QGraphicsItem* item=scene->selectedItems().at(0);item->setZValue(--backZ);}}
frontZ 和 backZ 是在 MainWindow 类中定义的私有变量,专门用于存储叠放次序的编号。frontZ只
增加,所以每增加一次都是最大值,设置该值的图形项就可以显示在最前面;backZ 只减少,所以每减小一次都是最小值,设置该值的图形项就可以显示在最后面。
图形项组合与拆解
可以将多个图形项组合为一个图形项,当做一个整体进行操作,如同 PowerPoint 软件里图形组合 功能一样。使用 QGraphicsItemGroup 类实现多个图形项的组合,QGraphicsItemGroup 是QGraphicsItem 的子类,所以,实质上也是一个图形项。
当有多个图形项被选择时,程序创建一个QGraphicsItemGroup类型的对象group,并添加到场景中,然后将选中的图形项逐一添加到 group 中。这样创建的 group 就是场景中的一个图形项,可以对其进行缩放、旋转等操作。
一个组合对象也可以被打散,使用 QGraphicsScene 的 destroyItemGroup()函数可以打散一个组合对象。这个函数打散组合,删除组合对象,但是不删除原来组合里的图形项。
组合与拆解的槽函数如下所示:
void MainWindow::on_actGroup_triggered()
{ //组合int cnt=scene->selectedItems().count();if (cnt>1){QGraphicsItemGroup* group =new QGraphicsItemGroup; //创建组合scene->addItem(group); //添加到场景中for (int i=0;i<cnt;i++) //将选择的图形项添加到组合中{QGraphicsItem* item=scene->selectedItems().at(0);item->setSelected(false); //取消选择item->clearFocus(); //清除焦点状态group->addToGroup(item); //添加到组合}setItemProperty(group, "组合"); //设置特性}
}void MainWindow::on_actGroupBreak_triggered()
{ //break group,打散组合int cnt=scene->selectedItems().count();if (cnt==1){QGraphicsItemGroup *group;group=(QGraphicsItemGroup*)scene->selectedItems().at(0);scene->destroyItemGroup(group); //打散组合}
}
图形项的删除
使用 QGraphicsScene 的 removeItem()函数从场景中移除某个图形项,函数如下:
void MainWindow::on_actEdit_Delete_triggered()
{ //删除所有选中的图形项int cnt=scene->selectedItems().count();for (int i=0;i<cnt;i++){QGraphicsItem* item=scene->selectedItems().at(0);scene->removeItem(item); //移除图形项delete item; //删除对象,释放内存}
}
注意此处scene->removeItem(item);只是从场景中移除了图形项,表示场景不再管理该图形项,但是其占据的内存依然存在,因此需要使用delete来彻底消除。添加到场景中的图形项最后无需手工删除,在场景被删除时,其中的图形项也会自动被删除。
额外技巧:在设计好一款软件后,需要为该软件写一个使用说明文档,可以通过点击界面的按钮来触发打开相应的文档。示例代码如下:
void MainWindow::on_actHelp_triggered()
{QString helpFile=QCoreApplication::applicationDirPath()+"/说明文档.pdf";QDesktopServices::openUrl(QUrl::fromLocalFile(helpFile));
}
参考
Qt 6 C++开发指南