Qt开源控件:图像刻度轴绘制器 (附源码)工程项目私信博主

news2024/12/26 9:31:26

项目简介

图像刻度轴绘制器是一款基于 Qt/C++ 开发的小型绘图工具,旨在实现带有刻度轴的图像显示功能。该项目主要用于需要精确测量或标注图像坐标的场景。通过左侧和底部的坐标轴以及对应的刻度线,可以直观地了解图像内容在二维空间中的位置。


项目功能

  1. 支持背景图像显示:用户可以设置任意背景图像,图像内容会紧贴刻度轴绘制。
  2. 动态刻度轴绘制
    • 左侧和底部的刻度线由用户设置的密度动态生成。
    • 刻度线支持长短交替显示,便于区分主要刻度与次级刻度。
  3. 数字刻度绘制
    • 刻度数字根据实际像素位置生成,确保刻度数字对齐并完整显示。
    • 刻度数字自动右对齐,且为四位数字预留充足空间。
  4. 绘制主轴线:X轴和Y轴的主轴线贯穿整个窗口,与刻度线相连,提供明确的坐标参考。
  5. 完全响应式
    • 图像与刻度轴的绘制适配窗口大小变化。
    • 图像内容不因窗口缩放而变形。

代码讲解

1. 主体构造与初始化

EdgeScaleDrawer 类的构造函数中完成初始化。包括设置默认的刻度密度、长短刻度长度和留白空间:

EdgeScaleDrawer::EdgeScaleDrawer(QWidget *parent)
    : QWidget(parent), scaleDensity(50), longRatio(10), shortRatio(5), margin(40)
{
}
  • scaleDensity:刻度间隔,默认为每 50 像素绘制一个刻度。
  • longRatioshortRatio:控制长短刻度线的长度。
  • margin:绘图区域的留白,用于绘制刻度数字和主轴线。

2. 设置背景图片

通过 setBackgroundImage 函数设置背景图片。调用 update() 刷新窗口,确保新图片立即生效:

void EdgeScaleDrawer::setBackgroundImage(const QPixmap &pixmap)
{
    background = pixmap;
    update();
}

3. 绘制事件

paintEvent 是整个绘图逻辑的核心,主要包括以下步骤:

3.1 绘制背景图像

通过 QPainter::drawPixmap 将背景图像绘制到坐标轴包围的区域:

QRect imageRect(margin, 0, width() - margin, height() - margin);
if (!background.isNull()) {
    painter.drawPixmap(imageRect, background);
}
  • margin 保证图像内容紧贴刻度轴,留出足够的刻度绘制空间。

3.2 绘制主轴线

通过 QPainter::drawLine 绘制贯穿窗口的 X 和 Y 轴主轴线:

painter.drawLine(margin, height() - margin, width(), height() - margin); // X轴
painter.drawLine(margin, 0, margin, height() - margin);                 // Y轴

3.3 绘制刻度线和数字

对于 左侧刻度线,从左下角开始绘制,数字显示在刻度线外侧。代码逻辑如下:

for (int y = 0; y <= imgHeight; y += scaleDensity)
{
    int length = (y % (scaleDensity * 5) == 0) ? longRatio : shortRatio;

    // 转换图像坐标到窗口坐标
    int yPos = height() - margin - (y * windowHeight / imgHeight);

    if (yPos >= margin && yPos <= height() - margin)
    {
        // 绘制刻度线
        painter.drawLine(margin - length, yPos, margin, yPos);

        // 绘制刻度数字
        if (y % (scaleDensity * 5) == 0)
        {
            QString text = QString::number(y);

            // 计算文字区域
            QFontMetrics metrics(painter.font());
            int textWidth = metrics.horizontalAdvance("0000");
            QRect textRect(margin - length - textWidth, yPos - 10, textWidth, 20);

            // 文字右对齐显示
            painter.drawText(textRect, Qt::AlignRight | Qt::AlignVCenter, text);
        }
    }
}

底部刻度线的绘制逻辑类似,但绘制方向为水平,刻度数字显示在刻度线下方。


如何使用

1. 引入类文件

EdgeScaleDrawer.hEdgeScaleDrawer.cpp 文件添加到您的项目中,并在需要的地方包含头文件:

#include "EdgeScaleDrawer.h"
2. 创建窗口实例

在主函数中创建 EdgeScaleDrawer 实例,并设置相关参数:

#include <QApplication>
#include "EdgeScaleDrawer.h"

int main(int argc, char *argv[])
{
    QApplication app(argc, argv);

    // 创建刻度绘制窗口
    EdgeScaleDrawer drawer;
    drawer.setBackgroundImage(QPixmap(":/images/background.jpg")); // 替换为实际图片路径
    drawer.setScaleDensity(50); // 刻度间隔
    drawer.setScaleRatios(10, 5); // 长短刻度长度
    drawer.resize(800, 600); // 窗口大小
    drawer.show();

    return app.exec();
}

工程源码

头文件 (EdgeScaleDrawer.h)

#ifndef EDGESCALEDRAWER_H
#define EDGESCALEDRAWER_H

#include <QWidget>
#include <QPixmap>

/**
 * @brief The EdgeScaleDrawer class
 * 图像刻度轴绘制器
 * 
 * 用于在窗口中显示带刻度的图像内容,支持 X 轴和 Y 轴的刻度线和数字绘制。
 */
class EdgeScaleDrawer : public QWidget
{
    Q_OBJECT

public:
    /**
     * @brief 构造函数
     * @param parent 父级窗口
     */
    explicit EdgeScaleDrawer(QWidget *parent = nullptr);

    /**
     * @brief 设置背景图像
     * @param pixmap 要显示的背景图像
     */
    void setBackgroundImage(const QPixmap &pixmap);

    /**
     * @brief 设置刻度线间隔
     * @param density 刻度线间隔,以像素为单位
     */
    void setScaleDensity(int density);

    /**
     * @brief 设置长短刻度线的长度
     * @param longRatio 长刻度线的长度
     * @param shortRatio 短刻度线的长度
     */
    void setScaleRatios(int longRatio, int shortRatio);

protected:
    /**
     * @brief 重写绘制事件,用于绘制背景图像、刻度线、轴线和刻度数字
     * @param event 绘制事件
     */
    void paintEvent(QPaintEvent *event) override;

private:
    QPixmap background;  // 背景图片
    int scaleDensity;    // 刻度线间隔(以像素为单位)
    int longRatio;       // 长刻度线的长度
    int shortRatio;      // 短刻度线的长度
    int margin;          // 留白,用于绘制轴线和刻度数字的空间
};

#endif // EDGESCALEDRAWER_H

实现文件 (EdgeScaleDrawer.cpp)

#include "EdgeScaleDrawer.h"
#include <QPainter>
#include <QPaintEvent>

EdgeScaleDrawer::EdgeScaleDrawer(QWidget *parent)
    : QWidget(parent), scaleDensity(50), longRatio(10), shortRatio(5), margin(40)
{
}

void EdgeScaleDrawer::setBackgroundImage(const QPixmap &pixmap)
{
    background = pixmap;
    update();
}

void EdgeScaleDrawer::setScaleDensity(int density)
{
    if (density > 0) {
        scaleDensity = density;
        update();
    }
}

void EdgeScaleDrawer::setScaleRatios(int longRatio, int shortRatio)
{
    if (longRatio > 0 && shortRatio > 0) {
        this->longRatio = longRatio;
        this->shortRatio = shortRatio;
        update();
    }
}

void EdgeScaleDrawer::paintEvent(QPaintEvent *event)
{
    Q_UNUSED(event);

    QPainter painter(this);

    QRect imageRect(margin, 0, width() - margin, height() - margin);
    if (!background.isNull()) {
        painter.drawPixmap(imageRect, background);
    }

    if (background.isNull()) {
        return;
    }

    int imgWidth = background.width();
    int imgHeight = background.height();

    int windowWidth = width() - margin;
    int windowHeight = height() - margin;

    QPen pen(Qt::black, 1);
    painter.setPen(pen);
    QFont font = painter.font();
    font.setPointSize(8);
    painter.setFont(font);

    painter.drawLine(margin, height() - margin, width(), height() - margin);
    painter.drawLine(margin, 0, margin, height() - margin);

    for (int y = 0; y <= imgHeight; y += scaleDensity)
    {
        int length = (y % (scaleDensity * 5) == 0) ? longRatio : shortRatio;

        int yPos = height() - margin - (y * windowHeight / imgHeight);

        if (yPos >= margin && yPos <= height() - margin)
        {
            painter.drawLine(margin - length, yPos, margin, yPos);

            if (y % (scaleDensity * 5) == 0)
            {
                QString text = QString::number(y);

                QFontMetrics metrics(painter.font());
                int textWidth = metrics.horizontalAdvance("0000");
                QRect textRect(margin - length - textWidth, yPos - 10, textWidth, 20);

                painter.drawText(textRect, Qt::AlignRight | Qt::AlignVCenter, text);
            }
        }
    }

    for (int x = 0; x <= imgWidth; x += scaleDensity) {
        int length = (x % (scaleDensity * 5) == 0) ? longRatio : shortRatio;

        int xPos = margin + (x * windowWidth / imgWidth);

        if (xPos >= margin && xPos <= width() - margin) {
            painter.drawLine(xPos, height() - margin, xPos, height() - margin + length);

            if (x % (scaleDensity * 5) == 0) {
                painter.drawText(xPos - 10, height() - margin + length + 15, QString::number(x));
            }
        }
    }
}

使用说明

  1. 添加头文件和实现文件:将上述 .h.cpp 文件添加到项目中。
  2. 创建并显示窗口
    在主函数中创建 EdgeScaleDrawer 的实例,并调用相关接口设置背景图像、刻度密度和刻度线长度。

本文由mdnice多平台发布

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

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

相关文章

集成学习综合教程

一、前置知识 一个分类器的分类准确率在60%-80%&#xff0c;即&#xff1a;比随机预测略好&#xff0c;但准确率却不太高&#xff0c;我们可以称之为 “弱分类器”&#xff0c;比如CART&#xff08;classification and regression tree 分类与回归树&#xff09;。 反之&#x…

渗透测试--Windows凭证收集

在渗透测试过程中&#xff0c;我们终究会遇到攻陷了某台加域Windows主机的情况。而这种情况下&#xff0c;我们很需要搜集当前主机的凭证信息。为进一步利用这些相互信任的凭证来进行横向打下基础。 在凭证收集中&#xff0c;我们主要有以下场景&#xff1a; 1.lsass.exe的DMP文…

云开发 Copilot ——让开发变得更简单

声明&#xff1a;本篇博客为云开发 Copilot体验文章&#xff0c;非广告 目录 前言&#xff1a; 游客体验 云开发 Copilot实战&#xff1a; 一、图片生成需求 二、云开发 Copilot实现需求 三、AI生成低代码页面 Copilot 的亮点功能 使用场景 云开发 Copilot开发的前景…

【Docker】创建Docker并部署Web站点

要在服务器上创建Docker容器&#xff0c;并在其中部署站点&#xff0c;你可以按照以下步骤操作。我们将以Flask应用为例来说明如何完成这一过程。 1. 准备工作 确保你的服务器已经安装了Docker。如果没有&#xff0c;请根据官方文档安装&#xff1a; Docker 安装指南 2. 创…

【Java】Switch语句、循环语句(for、while、do...while)

Switch语句&#xff1a;针对某个表达式的值进行判断&#xff0c;从而决定执行哪一段代码 语法格式&#xff1a; switch(表达式){ case 目标值1: 执行语句1 break; case 目标值2: …

第一部分:基础知识 3. 数据类型 --[MySQL轻松入门教程]

第一部分:基础知识 3. 数据类型 --[MySQL轻松入门教程] MySQL 支持多种数据类型,这些数据类型可以分为几大类:数值类型、字符串类型、日期和时间类型、二进制类型以及枚举和集合。每种类型都有其特定的用途和存储需求。以下是 MySQL 中常用的数据类型的详细介绍: 1. 数值…

uniapp 添加loading

在uniapp中添加loading可以使用uni的API uni.showLoading 方法。以下是一个简单的示例代码 // 显示loading uni.showLoading({title: 加载中 });// 假设这里是异步操作&#xff0c;比如网络请求 setTimeout(function () {// 隐藏loadinguni.hideLoading(); }, 2000);

HTTP(超文本传输协议)

HTTP是万维网通信的基础构成&#xff0c;是一个简单的请求相应协议&#xff0c;基于TCP之上80号端口 通信原理 DNS解析 将域名甩个DNS服务器解析&#xff0c;将域名化为IP访问 建立TCP连接 如图&#xff0c;客户端先发送一个sys置位seq为x&#xff08;任意值&#xff09;的…

(78)MPSK基带调制通信系统瑞利平坦衰落信道传输性能的MATLAB仿真

文章目录 前言一、MATLAB仿真1.仿真代码2.仿真结果 二、子函数与完整代码总结 前言 本文给出瑞利平坦衰落信道上的M-PSK通信系统性能仿真的MATLAB源代码与仿真结果。其中&#xff0c;调制方式M-PSK包括BPSK、QPSK、8-PSK、16-PSK、32-PSK等方式。 一、MATLAB仿真 1.仿真代码 …

JAVA |日常开发中JSTL标签库详解

JAVA &#xff5c;日常开发中JSTL标签库详解 前言一、JSTL 概述1.1 定义1.2 优势 二、JSTL 核心标签库2.1 导入 JSTL 库2.2 <c:out>标签 - 输出数据2.3 <c:if>标签 - 条件判断2.4 <c:choose>、<c:when>和<c:otherwise>标签 - 多条件选择 结束语优…

NAT traversal 原理 | TCP / UDP/ P2P

注&#xff1a;本文为 “NAT traversal ”相关的几篇文章合辑。 未整理去重。 NAT 穿越技术原理 Li_yy123 于 2020-12-08 18:54:26 发布 一、NAT 由来 为了解决全球公有 IPv4 的稀缺&#xff0c;提出了 NAT 技术。NAT 是 Network Address Translation 网络地址转换的缩写。 …

P3916 图的遍历(Tarjan缩点和反向建边)

P3916 图的遍历 - 洛谷 | 计算机科学教育新生态 写法一&#xff1a;Tarjan 思路&#xff1a;先运用Tarjan算法得到每个连通块中最大的编号&#xff0c;然后对每个连通块进行缩点重新建图&#xff0c;进行dfs&#xff0c;得到缩点后的连通块能够达到的最大编号。 Code: conste…

数据结构与算法学习笔记----堆

数据结构与算法学习笔记----堆 author: 明月清了个风 first publish time: 2024.12.2 revised: 2024.12.3 - 例题标题错误&#xff0c;已修改。 ps⛹从这里开始调整了文章结构&#xff0c;先讲解算法和数据结构基本原理&#xff0c;再给出例题&#xff0c;针对例题中的应用再…

【C++】格式化输出详解:掌握 cout 的进阶用法

博客主页&#xff1a; [小ᶻ☡꙳ᵃⁱᵍᶜ꙳] 本文专栏: C 文章目录 &#x1f4af;前言&#x1f4af;格式化输出的理论概述&#x1f4af;控制输出宽度和填充字符setw 操作符setfill 操作符 &#x1f4af;控制浮点数的显示格式fixed 与 scientificsetprecision &#x1f4af;…

Rust : 生成日历管理markdown文件的小工具

需求&#xff1a; 拟生成以下markdown管理小工具&#xff0c;这也是我日常工作日程表。 可以输入任意时间段&#xff0c;运行后就可以生成以上的markdown文件。 一、toml [package] name "rust-workfile" version "0.1.0" edition "2021"[d…

Spring01——Spring Framework系统架构

spring学习路线 Spring Framework系统架构 相关概念 低耦合&#xff1a;是指系统中各个模块或组件之间的依赖性较低&#xff0c;即它们之间的联系较少、交互简单。这种设计原则的目的是为了提高系统的灵活性和可维护性&#xff0c;便于模块的独立开发、测试和替换。解耦&…

MicroBlaze软核开发(一):Hello World

实现功能&#xff1a;使用 MicroBlaze软核 串口打印 Hello World Vivado版本&#xff1a;2018.3 目录 MicroBlaze介绍 vivado部分&#xff1a; 一、新建工程 二、配置MicroBlaze 三、添加Uart串口IP 四、生成HDL文件编译 SDK部分&#xff1a; 一、导出硬件启动SDK 二、…

ZLMediaKit+wvp (ffmpeg+obs)推拉流测试

这里使用了两种方式: ffmpeg命令和 OBS OBS推流在网上找了些基本没有说明白的, 在ZLMediaKit的issues中看到了一个好大哥的提问在此记录一下 使用OBS推流&#xff0c;rtmp&#xff0c;报鉴权失败 推流 1. ffmpeg命令推流 官方说明文档地址: 推流规则 rtsp://192.168.1.4:10554…

微信小程序之简单的数据中心管理平台(1)

微信小程序之简单的数据中心管理平台&#xff08;1&#xff09; 引言 随着微信小程序的广泛应用&#xff0c;越来越多的企业开始探索如何利用这一技术开发高效、便捷的管理平台。数据中心管理作为信息化建设的重要组成部分&#xff0c;需要一个灵活、可扩展的界面来实现资源的…

【计算机网络】实验13:运输层端口

实验13 运输层端口 一、实验目的 本次实验旨在验证TCP和IP运输层端口号的作用&#xff0c;深入理解它们在网络通信中的重要性。通过实验&#xff0c;我将探讨端口号如何帮助区分不同的应用程序和服务&#xff0c;使得在同一台主机上能够同时运行多个网络服务而不发生冲突。此…