当前位置: 首页 > article >正文

QT图形/视图架构详解(二)

上一章节示例程序仅演示了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;   //获取移动的向量,方向指的是newpoint
        Translate(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_OBJECT

protected:
    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;   //获取移动的向量,方向指的是newpoint
        Translate(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_NAMESPACE

class MainWindow : public QMainWindow
{
    Q_OBJECT

private:
    const   quint32 boundValue=100;     //随机数上限值
    //    QRandomGenerator* random;           //随机数发生器

    const int ItemId = 1;            //绘图项自定义数据的key
    const int ItemDesciption = 2;    //绘图项自定义数据的key

    int seqNum=0;   //用于图形项的编号,每个图形项有一个编号
    int backZ=0;    //用于bring to front
    int frontZ=0;   //用于bring to back

    QGraphicsScene  *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,0
    QGraphicsRectItem   *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++开发指南


http://www.kler.cn/a/444126.html

相关文章:

  • TDesign:NavBar 导航栏
  • sql server索引优化语句
  • golang断言
  • 【服务器】MyBatis是如何在java中使用并进行分页的?
  • 基于SpringBoot+html+vue实现的林业产品推荐系统【源码+文档+数据库文件+包部署成功+答疑解惑问到会为止】
  • python学opencv|读取图像(十七)认识alpha通道
  • Oracle 技术精选学习
  • VScode使用教程(菜鸟版)
  • Day26下 - BERT项目实战
  • 2024 年的科技趋势
  • vue-cli 5接入模块联邦 module federation
  • 【GO环境安装】mac系统+GoLand使用
  • nginx 记录完整的 request 及 response
  • 使用JustAuth实现gittee登录
  • 中型项目下的 MySQL 挑战与应对
  • 利用Python爬虫实现数据收集与挖掘
  • 音视频入门基础:MPEG2-TS专题(18)——PES流简介
  • HTML基本标签详解
  • MySQL Explain 分析SQL语句性能
  • PostgreSQL: 事务年龄
  • python学opencv|读取图像(十四)BGR图像和HSV图像通道拆分
  • 医院与医疗设备供应商网络安全事故综述
  • ElasticSearch学习7
  • 粘包由应用层协议解决
  • django模型——ORM模型2
  • 校园点餐订餐外卖跑腿Java源码