二十二、简易画图工具

news2024/9/21 16:45:32


目录

一、设计需求

二、示例代码

三、代码解析

        1、setAutoFillBackground函数的作用

        2、resizeEvent被调用的时机

四、Qt5双缓冲机制

五、总结


一、设计需求

        实现一个简单的绘图工具,可以选择线型、线宽及颜色等基本要素。

二、示例代码

mainwindow.cpp

#include "mainwindow.h"
#include <QToolBar>
#include <QColorDialog>

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
{
    drawWidget =new DrawWidget;   	//新建一个DrawWidget对象
    setCentralWidget(drawWidget);	//新建的DrawWidget对象作为主窗口的中央窗体

    createToolBar();                //实现一个工具栏

    setMinimumSize(600,400);       	//设置主窗口的最小尺寸

    ShowStyle();                    //初始化线型,设置控件中的当前值作为初始值
    drawWidget->setWidth(widthSpinBox->value()); 	//初始化线宽
    drawWidget->setColor(Qt::black);             	//初始化颜色
}

MainWindow::~MainWindow()
{
    
}
/**
 * @brief MainWindow::createToolBar
 */
void MainWindow::createToolBar()
{
    QToolBar *toolBar = addToolBar("Tool");   	//为主窗口新建一个工具栏对象

    styleLabel =new QLabel(tr("线型风格:"));   	//创建线型选择控件
    styleComboBox =new QComboBox;
    styleComboBox->addItem(tr("SolidLine"),static_cast<int>(Qt::SolidLine));
    styleComboBox->addItem(tr("DashLine"),static_cast<int>(Qt::DashLine));
    styleComboBox->addItem(tr("DotLine"),static_cast<int>(Qt::DotLine));
    styleComboBox->addItem(tr("DashDotLine"),static_cast<int>(Qt::DashDotLine));
    styleComboBox->addItem(tr("DashDotDotLine"),static_cast<int>(Qt::DashDotDotLine));
    connect(styleComboBox,SIGNAL(activated(int)),this,SLOT(ShowStyle())); //关联相应的槽函数

    widthLabel =new QLabel(tr("线宽:"));    		//创建线宽选择控件
    widthSpinBox =new QSpinBox;
    connect(widthSpinBox,SIGNAL(valueChanged(int)),drawWidget,SLOT(setWidth(int)));
    colorBtn =new QToolButton;                  //创建颜色选择控件
    QPixmap pixmap(20,20);
    pixmap.fill(Qt::black);
    colorBtn->setIcon(QIcon(pixmap));
    connect(colorBtn,SIGNAL(clicked()),this,SLOT(ShowColor()));

    clearBtn =new QToolButton();               	//创建清除按钮
    clearBtn->setText(tr("清除"));
    connect(clearBtn,SIGNAL(clicked()),drawWidget,SLOT(clear()));

    toolBar->addWidget(styleLabel);
    toolBar->addWidget(styleComboBox);
    toolBar->addWidget(widthLabel);
    toolBar->addWidget(widthSpinBox);
    toolBar->addWidget(colorBtn);
    toolBar->addWidget(clearBtn);
}

void MainWindow::ShowStyle()
{
    drawWidget->setStyle(styleComboBox->itemData(styleComboBox->currentIndex(),Qt::UserRole).toInt());
}
void MainWindow::ShowColor()
{
    QColor color = QColorDialog::getColor(static_cast<int>(Qt::black),this);	//使用标准颜色对话框QColorDialog获得一个颜色值
    if(color.isValid())
    {
        drawWidget->setColor(color);
        QPixmap p(20,20);
        p.fill(color);
        colorBtn->setIcon(QIcon(p));
    }
}

mainwindow.h

#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>
#include <QToolButton>
#include <QLabel>
#include <QComboBox>
#include <QSpinBox>
#include "drawwidget.h"

class MainWindow : public QMainWindow
{
    Q_OBJECT
    
public:
    MainWindow(QWidget *parent = 0);
    ~MainWindow();
    void createToolBar();
public slots:
    void ShowStyle();
    void ShowColor();
private:
    DrawWidget *drawWidget;
    QLabel *styleLabel;
    QComboBox *styleComboBox;
    QLabel *widthLabel;
    QSpinBox *widthSpinBox;
    QToolButton *colorBtn;
    QToolButton *clearBtn;
};

#endif // MAINWINDOW_H

drawwidget.cpp

#include "drawwidget.h"
#include <QtGui>
#include <QPen>

DrawWidget::DrawWidget(QWidget *parent) :
    QWidget(parent)
{
    setAutoFillBackground(true);    //对窗体背景色的设置,填充背景色的一种方式
    setPalette(QPalette(Qt::white));

    pix =new QPixmap(size());      	//此QPixmap对象用来准备随时接收绘制的内容
    pix->fill(Qt::white);           //填充背景色为白色
    setMinimumSize(600,400);      	//设置绘制区窗体的最小尺寸
}

/**
 * @brief DrawWidget::setStyle 接收主窗体传来的线型风格参数
 * @param s
 */
void DrawWidget::setStyle(int s)
{
    style = s;
}

/**
 * @brief DrawWidget::setWidth 接收主窗体传来的线宽参数值
 * @param w
 */
void DrawWidget::setWidth(int w)
{
    weight = w;
}

/**
 * @brief DrawWidget::setColor 接收主窗体传来的画笔颜色值
 * @param c
 */
void DrawWidget::setColor(QColor c)
{
    color = c;
}
/**
 * @brief DrawWidget::mousePressEvent
 * @param e
 * @note  重新定义鼠标按下事件,记录当前鼠标的位置
 */
void DrawWidget::mousePressEvent(QMouseEvent *e)
{
    startPos = e->pos();
}
/**
 * @brief DrawWidget::mouseMoveEvent 重写鼠标移动事件
 * @param e
 */
void DrawWidget::mouseMoveEvent(QMouseEvent *e)
{
    //创建Qpainter对象
    QPainter *painter = new QPainter;

    QPen pen;
    pen.setStyle((Qt::PenStyle)style);
    pen.setWidth(weight);
    pen.setColor(color);

    //开始对绘画设备进行绘制
    painter->begin(pix);
    painter->setPen(pen);
    painter->drawLine(startPos,e->pos());
    painter->end();

    //更新鼠标当前位置,为下次绘制做准备
    startPos =e->pos();
    //触发paintEvent,重新绘制窗体
    update();
}
/**
 * @brief DrawWidget::paintEvent 重写绘画事件
 */
void DrawWidget::paintEvent(QPaintEvent *)
{
    //构造函数调用begin(),析构函数调用end()
    QPainter painter(this);
    painter.drawPixmap(QPoint(0,0),*pix);
}

/**
 * @brief DrawWidget::resizeEvent
 * @param event
 */
void DrawWidget::resizeEvent(QResizeEvent *event)
{
    //调整绘制区域的大小
    if(height()>pix->height()||width()>pix->width())
    {
        QPixmap *newPix = new QPixmap(size());
        newPix->fill(Qt::white);
        QPainter p(newPix);
        p.drawPixmap(QPoint(0,0),*pix);
        pix = newPix;
    }
    //完成其余的操作
    QWidget::resizeEvent(event);
}

/**
 * @brief DrawWidget::clear
 */
void DrawWidget::clear()
{
    QPixmap *clearPix =new QPixmap(size());
    clearPix->fill(Qt::white);
    pix = clearPix;
    update();
}

 drawwidget.h

#ifndef DRAWWIDGET_H
#define DRAWWIDGET_H

#include <QWidget>
#include <QtGui>
#include <QMouseEvent>
#include <QPaintEvent>
#include <QResizeEvent>
#include <QColor>
#include <QPixmap>
#include <QPoint>
#include <QPainter>
#include <QPalette>

class DrawWidget : public QWidget
{
    Q_OBJECT
public:
    explicit DrawWidget(QWidget *parent = 0);
    void mousePressEvent(QMouseEvent *);
    void mouseMoveEvent(QMouseEvent *);
    void paintEvent(QPaintEvent *);
    void resizeEvent(QResizeEvent *);

signals:
    
public slots:
    void setStyle(int);
    void setWidth(int);
    void setColor(QColor);
    void clear();
private:
    QPixmap *pix;
    QPoint startPos;
    QPoint endPos;
    int style;
    int weight;
    QColor color;
};

#endif // DRAWWIDGET_H

main.cpp

#include "mainwindow.h"
#include <QApplication>
#include <QFont>
int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    QFont font("ZYSong18030",12);
    a.setFont(font);
    MainWindow w;
    w.show();
    
    return a.exec();
}

三、代码解析

        1、setAutoFillBackground函数的作用

        在 Qt 中,setAutoFillBackground 函数用于控制窗口部件(如 QWidget)的背景是否自动填充。这个函数的作用主要与窗口部件的背景颜色或背景图像有关。

        如果设置为 true,Qt 会自动填充窗口部件的背景颜色或背景图像。如果设置为 false,则不会自动填充背景,背景颜色或图像的处理需要由程序员手动管理(在paintEvent中绘制背景)。

        如果你在 paintEvent 中手动绘制背景,可能会选择禁用自动填充背景。在这种情况下,你可以通过 setAutoFillBackground(false) 来避免重复的背景填充操作。

#include <QApplication>
#include <QWidget>
#include <QPainter>

class CustomWidget : public QWidget {
public:
    CustomWidget(QWidget *parent = nullptr) : QWidget(parent) {
        // 禁用自动填充背景
        setAutoFillBackground(false);
        // 设置调色板背景颜色
        QPalette palette = this->palette();
        palette.setColor(QPalette::Background, Qt::red);
        setPalette(palette);
    }

protected:
    void paintEvent(QPaintEvent *event) override {
        // 手动绘制背景
        QPainter painter(this);
        painter.fillRect(rect(), palette().background());
        // 绘制其他内容
        painter.setBrush(Qt::blue);
        painter.drawRect(50, 50, 100, 100);
    }
};

int main(int argc, char *argv[]) {
    QApplication app(argc, argv);
    CustomWidget widget;
    widget.resize(200, 200);
    widget.show();
    return app.exec();
}

        2、resizeEvent被调用的时机

        在 Qt 中,resizeEvent 是一个虚拟函数,用于处理窗口部件(QWidget)的大小调整事件。resizeEvent 在窗口部件大小发生变化后被调用。

        在 resizeEvent 函数中,直接调用 width() 和 height() 确实能够获取到最新的窗口部件的宽度和高度。原因如下:

  • resizeEvent 调用时机:resizeEvent 在窗口部件的尺寸发生变化后被调用,此时窗口部件已经调整到了新的尺寸。
  • width() 和 height() 方法:这些方法返回的是窗口部件当前的宽度和高度。因为 resizeEvent 是在尺寸调整完成后触发的,所以这些方法在 resizeEvent 中返回的是调整后的最新尺寸。

四、Qt5双缓冲机制

        Qt 的双缓冲机制是一种用于提高图形界面绘制性能和减少闪烁现象的技术。在许多图形程序中,直接在屏幕上绘制可能会导致不平滑的视觉效果,因为图形操作可能会在不同的时间点进行,从而导致图像的部分更新和画面闪烁。

双缓冲的工作原理:
        双缓冲机制的基本思想是使用两个缓冲区来缓冲图形的绘制过程:

  • 前台缓冲区:这是用户实际看到的缓冲区,显示在屏幕上。
  • 后台缓冲区:这是一个隐藏的缓冲区,用于绘制即将显示的内容。

工作流程:

  • 绘制到后台缓冲区:所有的绘图操作先在后台缓冲区中完成。这意味着用户在后端进行绘制操作时并不会看到任何变化。
  • 交换缓冲区:一旦后台缓冲区的内容准备好,Qt 会进行缓冲区交换,将后台缓冲区的内容切换到前台缓冲区,用户就可以看到新的内容。
  • 清空后台缓冲区:在交换后的下一帧,后台缓冲区将被清空准备下一次绘制。

实现:

  • 重写 paintEvent 方法:在自定义的 QWidget 或 QGraphicsView 中,你可以重写 paintEvent 方法进行绘制。
  • 使用 QPixmap 或 QImage 作为后台缓冲区:你可以创建一个 QPixmap 或 QImage,先在这个图像上绘制所有内容,再将其绘制到窗口中。

示例代码:

#include <QApplication>
#include <QWidget>
#include <QPainter>

class DoubleBufferedWidget : public QWidget {
public:
    DoubleBufferedWidget(QWidget *parent = nullptr) : QWidget(parent) {
        setFixedSize(400, 300);
    }

protected:
    void paintEvent(QPaintEvent *event) override {
        // 创建一个 QPixmap 作为后台缓冲区
        QPixmap buffer(size());
        buffer.fill(Qt::white); // 用白色填充

        // 在后台缓冲区上进行绘制
        QPainter painter(&buffer);
        painter.setBrush(Qt::blue);
        painter.drawRect(50, 50, 100, 100); // 绘制一个蓝色矩形

        // 将后台缓冲区内容绘制到窗口
        QPainter windowPainter(this);
        windowPainter.drawPixmap(0, 0, buffer);
    }
};

int main(int argc, char *argv[]) {
    QApplication app(argc, argv);
    DoubleBufferedWidget window;
    window.show();
    return app.exec();
}

优点:

  • 减少闪烁:通过在后台绘制,可以避免用户在屏幕上看到逐帧的绘制过程。
  • 提高性能:在某些情况下,减少重绘的次数也可以提高应用程序的运行效率。

五、总结

        双缓冲机制是 Qt 中一项重要的性能优化技术,对于需要频繁更新界面的应用程序,特别是动画和图形密集型的应用,使用双缓冲可以显著提升用户体验。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2039419.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Dijkstra算法c++详解

算法是一种用于寻找图中两点间最短路径的算法&#xff0c;适用于无负权边的图。下面是一个使用C实现的算法示例&#xff0c;使用优先队列来优化算法效率&#xff1a; #include <iostream> #include <climits> // For INT_MAX #include <vector> #include &l…

ptqt5 打包把资源文件合进exe文件中 单个exe文件即可

目录 安装 PyInstaller 项目结构 编写 spec 文件 修改代码以嵌入资源 打包应用程序 解释 检查打包结果 使用 PyQt5 开发的应用程序可以通过 PyInstaller 工具打包成单个可执行文件(exe),并将所有资源文件(如图像、图标、样式表等)嵌入到 exe 文件中。以下是如何实现…

利用java结合python实现gis在线绘图,主要技术java+python+matlab+idw+Kriging

主要技术javapythonmatlabidwKriging GIS中的等值面和等高线绘图主要用于表达连续空间数据的分布情况&#xff0c;特别适用于需要展示三维空间中某个变量随位置变化的应用场景。 具体来说&#xff0c;以下是一些适合使用GIS等值面和等高线绘图的场景&#xff1a; 地形与地貌分…

spring-security-1-快速入门

1 功能 身份认证(authentication)授权(authorization)防御常见攻击 身份认证&#xff1a;常见账号密码登录&#xff0c;短信登录 授权&#xff1a;什么样的角色&#xff0c;能看见什么菜单&#xff0c;能访问哪些接口。 2 pom <dependency><groupId>org.springf…

开关电源:优化电子产品中的能源使用

电压转换器是许多技术系统的支柱。根据应用的不同&#xff0c;所需的电源单元由变压器、整流器 AC/DC 转换器实现。当高性能开关电源尚未上市时&#xff0c;几乎只使用 50 Hz 变压器解决方案。 电源注意事项 电能几乎完全以三相电流的形式提供&#xff0c;系统电压为 10 ...3…

ThinkPHP5漏洞分析之文件包含

目录 漏洞概要 漏洞环境 环境搭建 配置 测试&#xff1a; 漏洞分析&#xff1a; extract: 参数 Lua::assign 示例 array_merge 说明 返回值 array_merge() 示例 fetch: 漏洞修复 漏洞概要 本次漏洞存在于 ThinkPHP 模板引擎中&#xff0c;在加载模版解析变量时…

代码随想录 day 39 动态规划 打家劫舍

第九章 动态规划part07 今天就是打家劫舍的一天&#xff0c;这个系列不算难&#xff0c;大家可以一口气拿下。 198.打家劫舍 视频讲解&#xff1a;https://www.bilibili.com/video/BV1Te411N7SX https://programmercarl.com/0198.%E6%89%93%E5%AE%B6%E5%8A%AB%E8%88%8D.html…

Ubuntu虚拟机安装及汉化

一、安装 1.勾选典型(推荐)(T)——点击下一步 2.点击浏览找到光盘映像文件打开&#xff08;此文件很重要安装好后安装包不要卸载&#xff0c;放在不容易被删除的地方&#xff09;——点击下一步 3.将信息补充完整——点击下一步 4.点击浏览选择要将虚拟机安装在哪个路径&…

【IT行业研究报告】Internet Technology

一、引言 随着信息技术的飞速发展&#xff0c;IT行业已成为全球经济的重要驱动力。从云计算、大数据、人工智能到物联网&#xff0c;IT技术正深刻改变着各行各业的生产方式、商业模式和人们的生活方式。本报告旨在深入分析IT行业的现状、发展趋势和挑战&#xff0c;探讨其在各…

基于C11的简单log,支持C++的‘<<’风格和C的‘可变参数’风格

基于C11的简单log&#xff0c;支持C的‘<<’风格和C的‘可变参数’风格 日志仅由richlog.h单个文件实现功能&#xff0c;软件集成简单。 支持C的std::cout的<<风格的日志打印&#xff0c;也支持C的printf风格的日志打印 日志多线程安全&#xff0c;采用C11 mute…

Adobe PhotoShop - 制图操作

1. 排布照片 菜单 - 视图 - 对齐&#xff1a;打开后图层将会根据鼠标的移动智能对齐 菜单 - 视图 - 标尺&#xff1a;打开后在页面出现横纵标尺&#xff0c;方便图层的对齐与排列 2. 自动生成全景照 在日常处理中&#xff0c;我们常常想要将几张图片进行拼接获得一张全景图&…

Linux:文件管理,目录管理,文件系统,链接类型

1&#xff0c;文件管理 用户&#xff08;标识号&#xff1a;UID&#xff09;&#xff1a;一定资源的使用者&#xff0c;可以创建和管理文件以及访问其他用户文件。可以从属于多个群组。 用户组&#xff08;标识号&#xff1a;GID&#xff09;&#xff1a;由一定数量的对某些文件…

GlobalMapper软件安装流程

目录 一、环境准备 二、安装步骤 三、软件激活 一、环境准备 系统&#xff1a;win7操作系统 安装包下载&#xff1a;链接&#xff1a;https://pan.baidu.com/s/1Vb4VVRFBRYawt3MT-5gYOw 提取码&#xff1a;sxdj 二、安装步骤 1、解压&#xff0c;右键global-mapper-23_1-x…

NVDLA专题5:具体模块介绍——Convolution Buffer

概述 卷积缓冲器(CBUF)是卷积流水线中的一个阶段&#xff0c;module定义在NV_NVDLA_cbuf.v&#xff0c;module信息如下&#xff1a; module NV_NVDLA_cbuf (nvdla_core_clk //|< i,nvdla_core_rstn //|< i,cdma2buf_dat_wr_addr //|< i,cdma2buf_dat_wr_…

轻松提升效率:10款工时管理软件推荐

本文将介绍以下10款工具&#xff1a;PingCode、Worktile、百宝云、华为云项目管理、Yonyou NC、云效、红圈通、Asana、7pace Timetracker、Toggl Track。 管理项目工时时&#xff0c;面对繁杂的数据和紧迫的截止日期&#xff0c;很多人都会感到压力倍增。选择一款合适的项目工时…

各大App又要出年度报告啦,看看往年的,谁家能够打动你。

还有几个月就到年终了&#xff0c;各大app都要出年度报告或者年度账单了&#xff0c;咱们先看看往年的效果&#xff0c;你觉得那家好呢&#xff1f;

阿一网络安全实践演练之查找并利用未使用的 API 端点

描述 为了解决实验室问题&#xff0c;利用一个隐藏的 API 端点购买一件 Lightweight l33t 皮夹克。您可以使用以下凭据登录您自己的账户&#xff1a;wiener:peter。 所需知识 要解决这个实验室问题&#xff0c;您需要了解以下内容&#xff1a; 如何利用错误消息构造有效的请…

凡图公益新篇章:凡图家庭教育携手舜和社区,共绘心理健康蓝图

在这个充满挑战与机遇并存的时代&#xff0c;心理健康作为衡量社会文明与进步的重要标尺&#xff0c;正日益受到社会各界的广泛关注。 正是基于这份对美好生活的共同向往与追求&#xff0c;凡图(山东)教育科技集团有限公司与济南市中区舜和社区正式签署了心理援助协议&#xf…

Linux操作系统安装

Linux操作系统安装 Linux操作系统简介VMware 虚拟机简述安装 VMware 虚拟机创建新的虚拟机 安装 Linux 操作系统&#xff08;以 CentOS 为例&#xff09;远程工具连接 Linux查看Linux的ip链接Linux操作步骤 Linux操作系统简介 Linux内核kernel最初是由芬兰人李纳斯托瓦兹&#…

基于计算机爱心小屋公益机构智慧管理(源码+论文+部署讲解等)

博主介绍&#xff1a; ✌我是阿龙&#xff0c;一名专注于Java技术领域的程序员&#xff0c;全网拥有10W粉丝。作为CSDN特邀作者、博客专家、新星计划导师&#xff0c;我在计算机毕业设计开发方面积累了丰富的经验。同时&#xff0c;我也是掘金、华为云、阿里云、InfoQ等平台的优…