在控件graphicsView中实现绘图功能(三)

前言:

本文,对之前没有完成的绘图固定,文本添加,清屏,图片反转,以及自由画图功能进行了完善。也基本上实现了大部分简易绘图的功能,后面也会对一些细节和技术进行继续学习。

效果展示:

1.图片展示

示例图片1
示例图片2
出现bug,但是很好看,也保存了

2.视频展示

视频示例:

基础夯实:

一.文本框焦点:

在图形用户界面(GUI)编程中,文本框(或其他可编辑的控件,如文本编辑区、输入框等)获得焦点(focus)意味着该控件是当前接收键盘输入的目标。当文本框获得焦点时,用户可以通过键盘输入文本、修改文本或执行与该文本框相关联的其他键盘操作(如复制、粘贴等),而不会影响到界面上的其他控件。

焦点是GUI中用户交互的一个重要概念。在大多数图形界面中,一次只能有一个控件获得焦点。这意味着,如果用户尝试通过键盘与界面交互(例如,通过按键输入文本),只有获得焦点的控件会响应这些按键事件。

对于文本框来说,获得焦点通常伴随着一些视觉上的变化,以提示用户该控件是当前的活动控件。这些变化可能包括文本框边框颜色的变化、文本光标的出现或闪烁等。

在Qt中,您可以通过几种方式使文本框获得焦点:

程序性设置:您可以直接调用文本框的setFocus()方法来强制它获得焦点。这通常在您希望控件在显示时立即响应用户输入时很有用。
用户交互:用户可以通过点击文本框来使其获得焦点。在Qt中,当用户在图形视图(如QGraphicsView)中点击与QGraphicsTextItem相关联的文本框时,如果该文本框是可编辑的,并且没有其他控件阻止焦点传递,那么文本框应该会获得焦点。
键盘导航:在支持键盘导航的界面中,用户可以通过Tab键在控件之间切换焦点。如果您的文本框是键盘导航路径的一部分,那么用户可以通过按Tab键来使其获得焦点。
信号和槽:在Qt的信号和槽机制中,您可以连接一个信号(如鼠标点击事件)到一个槽函数,然后在该槽函数中调用setFocus()来使文本框获得焦点。
然而,需要注意的是,在某些情况下,文本框可能无法直接通过setFocus()方法获得焦点,特别是当它被嵌入到更复杂的控件或视图中时。在这些情况下,您可能需要检查控件的焦点策略或重写某些事件处理器来确保文本框能够按预期获得焦点。

二.QGraphicsItems:

在Qt框架中,QGraphicsItem 是所有图形项(包括二维图形项)的基类,用于在 QGraphicsScene 中表示一个项目,这些项目随后可以被 QGraphicsView 渲染和显示。QGraphicsItem 类本身不直接用于创建图形,而是提供了一套接口,用于管理这些图形项的位置、变换、选择、可见性等。下面是对您提到的几个特定派生类的详细解释:

1.QGraphicsRectItem

QGraphicsRectItem 是用于表示矩形的图形项。通过它可以很容易地在场景中添加、移动、缩放或旋转矩形。创建时,可以指定矩形的位置、大小、边框笔刷(QPen)和填充刷(QBrush)等属性。

cpp
QGraphicsRectItem *rectItem = nullptr;  
// 创建并初始化  
rectItem = new QGraphicsRectItem(QRectF(0, 0, 100, 50), nullptr); // 位置(0,0),大小100x50  
rectItem->setBrush(Qt::red); // 设置填充颜色为红色

2.QGraphicsLineItem

QGraphicsLineItem 用于表示直线段。通过它可以添加直线到场景中,并设置线条的样式、颜色等属性。

QGraphicsLineItem *lineItem = nullptr;  
// 创建并初始化  
lineItem = new QGraphicsLineItem(QLineF(0, 0, 100, 50), nullptr); // 从(0,0)到(100,50)的直线  
lineItem->setPen(QPen(Qt::blue, 2)); // 设置线条颜色和宽度

3.QGraphicsEllipseItem

QGraphicsEllipseItem 用于表示椭圆或圆。通过它可以向场景中添加椭圆或圆,并设置其边框和填充属性。

QGraphicsEllipseItem *ellipseItem = nullptr;  
// 创建并初始化  
ellipseItem = new QGraphicsEllipseItem(QRectF(-50, -25, 100, 50), nullptr); // 中心在(0,0),x轴半径50,y轴半径25的椭圆  
ellipseItem->setBrush(Qt::green); // 设置填充颜色为绿色

4.QGraphicsTextItem

QGraphicsTextItem 用于表示可编辑的文本项。它支持富文本格式,可以嵌入图片、链接等。使用它可以向场景中添加文本,并允许用户进行编辑。

QGraphicsTextItem *textItem = nullptr;  
// 创建并初始化  
textItem = new QGraphicsTextItem("Hello, QGraphicsTextItem!", nullptr);  
textItem->setDefaultTextColor(Qt::black); // 设置默认文本颜色

5.QGraphicsPathItem

QGraphicsPathItem 允许使用 QPainterPath 来绘制复杂的形状。QPainterPath 提供了一套命令来创建路径,包括线条、曲线、矩形、椭圆等,甚至可以通过组合和变换这些基本形状来创建更复杂的图形。

QGraphicsPathItem *pathItem = nullptr;  
QPainterPath path;  
path.addEllipse(QRectF(-50, -50, 100, 100)); // 添加一个椭圆  
pathItem = new QGraphicsPathItem(path, nullptr);  
pathItem->setBrush(Qt::yellow); // 设置填充颜色为黄色

这些类都是Qt图形视图框架中非常重要的部分,它们提供了丰富的接口来创建和管理图形项,使得开发者能够轻松地在Qt应用程序中实现复杂的图形界面。

三.鼠标按键基础:

推荐了解一下鼠标按键,再来实现代码:
在控件graphicsView中实现绘图功能(一)
在控件graphicsView中实现绘图功能(二)
QT中鼠标事件示例(包含点击,点击之后移动,释放的坐标获取)

实现功能:

如图所示,该实例实现了绘制直线,绘制椭圆,绘制矩形,绘制文字,自由绘画,清屏以及旋转等功能。

遇到问题:

文本无法输入,文本输入无法定位,清屏之后依然有残余。上述问题都已经解决

期待实现的功能:

连接数据库,将绘画好的图片保存到数据库,添加导入导出功能。

核心代码:

customgraphicsview.h:

#ifndef CUSTOMGRAPHICSVIEW_H
#define CUSTOMGRAPHICSVIEW_H

#include <QGraphicsView>
#include <QPointF>
#include <QMouseEvent>
#include <QPainterPath>
#include <QGraphicsTextItem>

class CustomGraphicsView : public QGraphicsView
{
    Q_OBJECT

public:
    enum DrawMode { RectMode,
                    LineMode,
                    EllipseMode,
                    FreeDrawMode,
                    TextHintMode
    };

    explicit CustomGraphicsView(QWidget *parent = nullptr);

    void setDrawMode(DrawMode mode);

signals:
    void mouseClicked(const QPointF &pos);
    void mouseMoved(const QPointF &pos);
    void mouseReleased(const QPointF &pos);

protected:
    void mousePressEvent(QMouseEvent *event) override;
    void mouseMoveEvent(QMouseEvent *event) override;
    void mouseReleaseEvent(QMouseEvent *event) override;

private:
    DrawMode currentDrawMode;
    bool isDrawing = false;
    QPointF startPoint;
    QGraphicsRectItem *rectItem = nullptr;
    QGraphicsLineItem *lineItem = nullptr;
    QGraphicsEllipseItem *ellipseItem = nullptr;
    QGraphicsTextItem *textItem = nullptr;
    QGraphicsPathItem *pathItem = nullptr;
    QPainterPath currentPath;
};

#endif // CUSTOMGRAPHICSVIEW_H

customgraphicsview.cpp:

#include "CustomGraphicsView.h"
#include <QGraphicsRectItem>
#include <QGraphicsScene>
#include <QMouseEvent>

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

void CustomGraphicsView::setDrawMode(DrawMode mode)
{
    currentDrawMode = mode;
}

void CustomGraphicsView::mousePressEvent(QMouseEvent *event)
{
    if (event->button() == Qt::LeftButton) {
        isDrawing = true;
        startPoint = mapToScene(event->pos());

        switch (currentDrawMode) {
        case RectMode:
            // 矩形模式下,初始化矩形的两个角(实际上是同一个点,稍后在move事件中更新)
            rectItem = new QGraphicsRectItem(QRectF(startPoint, QSizeF(0, 0)));
            rectItem->setPen(QPen(Qt::blue)); // 示例颜色
            scene()->addItem(rectItem);
            break;

        case LineMode:
            // 线条模式下,不立即绘制线条,只在move事件中根据起点和当前点绘制
            lineItem = nullptr; // 如果之前有线条,则忽略它(或根据需要重置)
            break;

        case EllipseMode:
            ellipseItem = new QGraphicsEllipseItem(QRectF(startPoint, QSizeF(0, 0)));
            ellipseItem->setPen(QPen(Qt::green));
            ellipseItem->setBrush(QBrush(Qt::NoBrush));
            scene()->addItem(ellipseItem);
            break;
        case FreeDrawMode:
            if (!pathItem) {
                pathItem = new QGraphicsPathItem();
                scene()->addItem(pathItem);
            }
            currentPath.clear(); // 清除之前的路径,从头开始
            currentPath.moveTo(startPoint); // 初始化新路径的起点
            break;
        case TextHintMode:
        {
            if (!textItem) {
                textItem = new QGraphicsTextItem(tr("  ")); // 初始提示文本
                textItem->setTextInteractionFlags(Qt::TextEditorInteraction); // 允许文本编辑
                textItem->setFlag(QGraphicsItem::ItemIsMovable, true); // 可选:使文本框可移动
                textItem->setFlag(QGraphicsItem::ItemIsSelectable, true); // 可选:使文本框可选
                scene()->addItem(textItem);
                textItem->setPos(startPoint); // 设置文本框的初始位置
            }
            break;
        }
        default:
            // ...
            break;
        }
        emit mouseClicked(event->pos());
    }

    QGraphicsView::mousePressEvent(event);
}

void CustomGraphicsView::mouseMoveEvent(QMouseEvent *event)
{
    if (isDrawing) {
        QPointF endPoint = mapToScene(event->pos());

        switch (currentDrawMode) {
        case RectMode:
        {
            QPointF topLeft = QPointF(qMin(startPoint.x(), endPoint.x()), qMin(startPoint.y(), endPoint.y()));
            QPointF bottomRight = QPointF(qMax(startPoint.x(), endPoint.x()), qMax(startPoint.y(), endPoint.y()));
            rectItem->setRect(QRectF(topLeft, bottomRight));
            break;
        }

        case LineMode:
        {
            if (!lineItem) {
                lineItem = scene()->addLine(startPoint.x(), startPoint.y(), endPoint.x(), endPoint.y(), QPen(Qt::red));
            } else {
                lineItem->setLine(QLineF(startPoint, endPoint));
            }
            break;
        }

        case EllipseMode:
        {
            QPointF topLeft = QPointF(qMin(startPoint.x(), endPoint.x()), qMin(startPoint.y(), endPoint.y()));
            QPointF bottomRight = QPointF(qMax(startPoint.x(), endPoint.x()), qMax(startPoint.y(), endPoint.y()));
            QRectF ellipseRect(topLeft, QSizeF(bottomRight.x() - topLeft.x(), bottomRight.y() - topLeft.y()));
            ellipseItem->setRect(ellipseRect);
            break;
        }
        case FreeDrawMode:
        {
            currentPath.lineTo(endPoint); // 添加新点到路径
            pathItem->setPath(currentPath); // 更新路径项
            break;
        }

        default:
            // 处理默认情况
            break;
        }
    }
    emit mouseMoved(event->pos());
}

void CustomGraphicsView::mouseReleaseEvent(QMouseEvent *event)
{
    if (event->button() == Qt::LeftButton && isDrawing) {
        isDrawing = false;
    }

    emit mouseReleased(event->pos());
    //重置模型项,以便下一次重写
    switch (currentDrawMode){
        {
            case RectMode:
                rectItem = nullptr;
                break;

            case LineMode:
                lineItem = nullptr;
                break;

            case EllipseMode:
                ellipseItem = nullptr;
                break;
            case FreeDrawMode:
                pathItem = nullptr;
                //清空绘图路径
                break;
            case TextHintMode:
            {
                textItem = nullptr;
                break;
            }
        }
    default:
        break;
    }

    QGraphicsView::mouseReleaseEvent(event);
}

仓库源码:

gitee仓库代码分享

  • 15
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值