qt自绘标尺,鼠标拖动画线测量两点距离

news2025/6/20 19:12:29

效果如图:
在这里插入图片描述

图像显示窗口元素组成:
在这里插入图片描述


图像显示窗口整体构成:
在这里插入图片描述

长度测量窗口ui:
在这里插入图片描述


思路:
首先自定了一个RulerWidget,其中有一个布局,布局中包含自定义的水平Ruler、自定义垂直Ruler、单位QLabel以及空白的QWidget。将这个空白的QWidget设置为不可见,避免遮挡图像显示区域上的图片。
然后将该RulerWidget放置在图像显示区域的前端。
长度测量中的像素坐标,是鼠标点击在图像显示区域的坐标映射到原图像的坐标。


相关代码:

#ifndef RULERWIDGET_H
#define RULERWIDGET_H

#include <QWidget>
#include <QLabel>
#include <QLayout>
#include <QPaintEvent>
#include <QPainter>
#include "measurewidget.h"

class Ruler : public QWidget
{
    Q_OBJECT

public:
    explicit Ruler(Qt::Orientations orientation, QWidget *parent = nullptr);
    ~Ruler();
    void UpdateImgSize(QSize size);
    void UpdatePixel(double pixel);

protected:
    void paintEvent(QPaintEvent *event)override;


private:
    QColor backgroundColor{"#F2F2F2"};//背景色
    QColor textAndLineColor{Qt::black};//文本和刻度颜色
    QFont font{"微软雅黑",7};
    Qt::Orientations orientation;
    QSize imgSize;
    double pixelLen;
};



class RulerWidget : public QWidget
{
    Q_OBJECT

public:
    explicit RulerWidget(QWidget *parent=nullptr);
    ~RulerWidget();

    void setVisible(bool visible) override;
    void UpdateRulerSize(QSize size);

signals:
    void sig_wid_ruler_hide();
    void sig_wid_measure_update_pos(int, int, int, int);

public slots:
    void slot_wid_measure_pos(int, int, int, int);


private:
    Ruler *horizontal_ruler;
    Ruler *vertical_ruler;
    QLabel *unit_lab;
    QWidget *empty_wid;
    MeasureWidget *measure_wid;         //标尺测量数据窗口
};


#endif // RULERWIDGET_H

#include "rulerwidget.h"


Ruler::Ruler(Qt::Orientations orientation,QWidget *parent)
    : QWidget(parent),
      orientation(orientation)
{
    orientation == Qt::Horizontal ? setFixedHeight(30) : setFixedWidth(30);
    pixelLen = 1.0;
}

Ruler::~Ruler()
{

}

void Ruler::UpdateImgSize(QSize size)
{
    imgSize = size;
    update();
}


void Ruler::UpdatePixel(double pixel)
{
    pixelLen = pixel;
    update();
}


void Ruler::paintEvent(QPaintEvent *event)
{
    auto rect = event->rect();
    auto height = rect.height();
    auto width = rect.width();


    QPainter painter(this);
    painter.setRenderHint(QPainter::Antialiasing);
    painter.fillRect(rect,backgroundColor);

    //绘制刻度
    painter.save();
    auto length = orientation == Qt::Horizontal ? rect.width() : rect.height();
    double scale = orientation == Qt::Horizontal ? ((double)imgSize.width() / length) : ((double)imgSize.height() / length);    
    painter.setPen(QPen(textAndLineColor,1));
    painter.setFont(font);

    if(orientation == Qt::Horizontal)
    {
        auto shortLine_y = height * 0.75;
        auto longLine_y = height * 0.5;
        auto middleLine_y = height * 0.625;
        auto halfHeight = height / 2;
        int temp = 0;
        for(int i = 0;i < length;i+=10)
        {
            if(temp % 10 == 0)//画中线
            {
                painter.drawLine(QPointF(i,middleLine_y),QPointF(i,height));
            }
            else if(temp % 5 == 0)//画长线
            {
                painter.drawLine(QPointF(i,longLine_y),QPointF(i,height));
                painter.drawText(QRectF(i-50,0,100,halfHeight),Qt::AlignCenter | Qt::TextWordWrap, QString::asprintf("%.1f", i*scale*pixelLen));
            }
            else//画短线
            {
                painter.drawLine(QPointF(i,shortLine_y),QPointF(i,height));
            }
            ++temp;
        }
    }
    else
    {
        auto shortLineWidth = width * 0.25;
        auto longLineWidth = width * 0.5;
        auto middleLineWidth = width * 0.375;
        auto harfWidth = width / 2;
        int temp = 0;
        for(int i = 0;i < length;i+=10)
        {
            if(temp % 10 == 0)
            {
                painter.drawLine(QPointF(middleLineWidth,i),QPointF(0,i));
            }
            else if(temp % 5 == 0)
            {
                painter.drawLine(QPointF(longLineWidth,i),QPointF(0,i));
                painter.rotate(90);
                painter.drawText(i-10, -harfWidth-5, QString::asprintf("%.1f", i*scale*pixelLen));
                painter.rotate(-90);
            }
            else
            {
                painter.drawLine(QPointF(shortLineWidth,i),QPointF(0,i));
            }
            ++temp;
        }
    }
    painter.drawRect(0,0, this->width(), this->height());
    painter.restore();

    QWidget::paintEvent(event);
}



RulerWidget::RulerWidget(QWidget *parent)
    : QWidget(parent)
{

    horizontal_ruler = new Ruler(Qt::Horizontal, this);
    vertical_ruler = new Ruler(Qt::Vertical, this);
    unit_lab = new QLabel("um", this);
    unit_lab->setStyleSheet("background-color:#F2F2F2;color:black;");
    empty_wid = new QWidget(this);
    empty_wid->setVisible(false);

    QGridLayout *grid_layout = new QGridLayout(this);
    grid_layout->addWidget(unit_lab,0,0,1,1);
    grid_layout->addWidget(vertical_ruler,1,0,1,1);
    grid_layout->addWidget(horizontal_ruler,0,1,1,1);
    grid_layout->addWidget(empty_wid,1,1,1,1);
    grid_layout->setSpacing(0);



    measure_wid = new MeasureWidget(this);
    measure_wid->hide();
    connect(this, SIGNAL(sig_wid_measure_update_pos(int,int,int,int)), measure_wid, SLOT(slot_pos_update(int,int,int,int)));
    connect(measure_wid, &MeasureWidget::sig_close, this, [=](){
        this->setVisible(false);
        emit sig_wid_ruler_hide();
    });
    connect(measure_wid, &MeasureWidget::sig_set, this, [=](QString unit, QString pixel){
        unit_lab->setText(unit);
        if(pixel.toDouble()>0){
            horizontal_ruler->UpdatePixel(pixel.toDouble());
            vertical_ruler->UpdatePixel(pixel.toDouble());
        }
    });

    this->setVisible(false);


}

RulerWidget::~RulerWidget()
{
    delete horizontal_ruler;
    delete vertical_ruler;
    delete unit_lab;
    delete empty_wid;
    delete measure_wid;
}

void RulerWidget::setVisible(bool visible)
{
    measure_wid->setVisible(visible);
    QWidget::setVisible(visible);
}


void RulerWidget::UpdateRulerSize(QSize size)
{
    horizontal_ruler->UpdateImgSize(size);
    vertical_ruler->UpdateImgSize(size);
}

void RulerWidget::slot_wid_measure_pos(int StartX, int StartY, int endX, int endY)
{
    emit sig_wid_measure_update_pos(StartX, StartY, endX, endY);
}

#ifndef MEASUREWIDGET_H
#define MEASUREWIDGET_H

#include <QWidget>


namespace Ui {
class MeasureWidget;
}

class MeasureWidget : public QWidget
{
    Q_OBJECT
public:
    explicit MeasureWidget(QWidget *parent = nullptr);
    ~MeasureWidget();

protected:
    void closeEvent(QCloseEvent *e);

    void changeEvent(QEvent *event);

signals:
    void sig_close();
    void sig_set(QString unit, QString pixel);

private slots:
    void slot_btn_set_clicked();

public slots:
    void slot_pos_update(int pos1_x, int pos1_y, int pos2_x, int pos2_y);

private:
    Ui::MeasureWidget *ui;

    double pixel = 1.0;
    double len_h = 0.0;
    double len_v = 0.0;
    double len_c = 0.0;
};

#endif // MEASUREWIDGET_H

#include "measurewidget.h"
#include "ui_measurewidget.h"


MeasureWidget::MeasureWidget(QWidget *parent)
    : QWidget(parent),
    ui(new Ui::MeasureWidget)
{
    ui->setupUi(this);

    this->setFixedSize(400,300);
    this->setWindowIcon(QIcon(":/icon/logo/res/icon/logo/logo.ico"));
    this->setAttribute(Qt::WA_QuitOnClose, false);
    this->setWindowFlag(Qt::Window);

    ui->com_unit->insertItems(0, QStringList()<< "um" << "mm" << "cm");
    ui->com_unit->setCurrentIndex(0);

    connect(ui->btn_set, SIGNAL(clicked()), this, SLOT(slot_btn_set_clicked()));
}

MeasureWidget::~MeasureWidget()
{

}

void MeasureWidget::closeEvent(QCloseEvent *e)
{
    emit sig_close();

    QWidget::closeEvent(e);
}

void MeasureWidget::changeEvent(QEvent *event)
{
    if(event->type() == QEvent::LanguageChange){
        this->ui->retranslateUi(this);
    }
    QWidget::changeEvent(event);
}



void MeasureWidget::slot_btn_set_clicked()
{
    if(!ui->ledit_pixel->text().isEmpty()){

        pixel = ui->ledit_pixel->text().toDouble();
        if(pixel > 0){
            ui->lab_len_horizontal->setText(QString::asprintf("%.2f", len_h * pixel));
            ui->lab_len_vertical->setText(QString::asprintf("%.2f", len_v * pixel));
            ui->lab_len_cartesian->setText(QString::asprintf("%.2f", len_c * pixel));

            emit sig_set(ui->com_unit->currentText(), ui->ledit_pixel->text());
        }                
    }

}


void MeasureWidget::slot_pos_update(int pos1_x, int pos1_y, int pos2_x, int pos2_y)
{
    len_h = abs(pos1_x - pos2_x)+1;
    len_v = abs(pos1_y - pos2_y)+1;
    len_c = sqrt(pow(len_h,2) + pow(len_v,2));

    ui->lab_pos_start->setText(QString("(%1,%2)").arg(pos1_x).arg(pos1_y));
    ui->lab_pos_end->setText(QString("(%1,%2)").arg(pos2_x).arg(pos2_y));
    ui->lab_len_horizontal->setText(QString::asprintf("%.2f", len_h * pixel));
    ui->lab_len_vertical->setText(QString::asprintf("%.2f", len_v * pixel));
    ui->lab_len_cartesian->setText(QString::asprintf("%.2f", len_c * pixel));
}

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

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

相关文章

洗地机怎么选|洗地机哪款好用?添可、希亦、美的洗地机哪个最耐用质量好?

在现代生活中&#xff0c;屋内清洁是一项必不可少的工作&#xff0c;但也是一项费时费力的工作。随着科技的进步&#xff0c;家庭清洁工具也正经历着革命性的变革。洗地机&#xff0c;一种集吸尘、拖地、清洗于一体的智能家居清洁工具&#xff0c;正逐渐成为现代家庭必备的家电…

15. 三数之和 - 力扣

1. 题目 给你一个整数数组 nums &#xff0c;判断是否存在三元组 [nums[i], nums[j], nums[k]] 满足 i ! j、i ! k 且 j ! k &#xff0c;同时还满足 nums[i] nums[j] nums[k] 0 。请 你返回所有和为 0 且不重复的三元组。 注意&#xff1a;答案中不可以包含重复的三元组。 …

华容道问题求解_详细设计(四)之查找算法2_BFS

&#xff08;续上篇&#xff09; 利用BFS查找&#xff0c;会找到最短路径&#xff08;没有权重的图&#xff09;&#xff0c;这个道理比较简单&#xff0c;这是由于寻找路径的方法都是从起点或者接近起点的位置开始的。查找过程如果画出图来&#xff0c;类似于一圈圈的放大&…

C++初阶:类与对象(中篇)

目录 2. 类的六个默认成员函数2.1 构造函数2.1.1 构造函数的定义方式 2.2 析构函数2.2.1 析构函数定义方式 2.3拷贝构造函数2.3.1 拷贝构造函数的定义方式2.3.2 深拷贝与浅拷贝 2.4 赋值运算符的重载2.4.1 运算符重载2.4.2 运算符的重载的定义方式2.4.3 默认成员函数&#xff1…

Redis 常见数据类型(对象类型)和应用案列

前言: 每次你在游戏中看到玩家排行榜&#xff0c;或者在音乐应用中浏览热门歌单&#xff0c;有没有想过这个排行榜是如何做到实时更新的&#xff1f;当然&#xff0c;依靠 Redis 即可做到。 在技术领域&#xff0c;我们经常听到「键值存储」 这个词。但在 Redis 的世界里&…

VBA技术资料MF128:批量创建文件夹及子文件夹

我给VBA的定义&#xff1a;VBA是个人小型自动化处理的有效工具。利用好了&#xff0c;可以大大提高自己的工作效率&#xff0c;而且可以提高数据的准确度。“VBA语言専攻”提供的教程一共九套&#xff0c;分为初级、中级、高级三大部分&#xff0c;教程是对VBA的系统讲解&#…

低密度奇偶校验码LDPC(十)——LDPC码的密度进化

一、密度进化的概念 二、规则LDPC码的密度进化算法(SPA算法) 算法变量表 VN更新的密度进化 CN更新的密度进化 算法总结 程序仿真 参考文献 [1] 白宝明 孙韶辉 王加庆. 5G 移动通信中的信道编码[M]. 北京: 电子工业出版社, 2018. [2] William E. Ryan, Shu Lin. Channel Co…

【C语言】linux内核ip_local_out函数

一、讲解 这个函数 __ip_local_out 是 Linux 内核网络子系统中的函数&#xff0c;部分与本地出口的 IPv4 数据包发送相关。下面讲解这段代码的每一部分&#xff1a; 1. 函数声明 int __ip_local_out(struct net *net, struct sock *sk, struct sk_buff *skb)&#xff1a; -…

AcWing 1262. 鱼塘钓鱼(每日一题)

目录 暴力枚举法&#xff1a; 贪心&#xff1a; 原题链接&#xff1a;1262. 鱼塘钓鱼 - AcWing题库 有 N个鱼塘排成一排&#xff0c;每个鱼塘中有一定数量的鱼&#xff0c;例如&#xff1a;N5 时&#xff0c;如下表&#xff1a; 鱼塘编号12345第1分钟能钓到的鱼的数量&…

k8s-生产级的k8s高可用(2) 25

部署containerd k8s2、k8s3、k8s4在配置前需要重置节点&#xff08;reset&#xff09;在上一章已完成 禁用所有节点docker和cri-docker服务 所有节点清除iptables规则 重置后全部节点重启 由于之前部署过docker&#xff0c;因此containerd默认已安装 修改配置 启动containe…

专业140+总分430+西南交通大学924信号与系统考研经验电子信息与通信工程,真题,大纲,参考书

今年报考西南交通大学&#xff0c;考研分数专业课924信号与系统140&#xff0c;总分430&#xff0c;各门分数都还是比较均衡&#xff0c;经过一年的复习&#xff0c;有得有失&#xff0c;总结一下自己的复习经历&#xff0c;希望给大家有点帮助&#xff0c;在复习中做的更好&am…

十七、IO流——综合练习

综合练习 目录 一、制造假数据1.1自己写代码1.2 利用糊涂包生成假数据 二、随机点名器2.1随机点名器12.2 随机点名器22.3 随机点名器32.4 随机点名器42.5 随机点名器5 三、登录注册3.1 登录注册13.2 登录注册23.3 登录注册3 一、制造假数据 需求&#xff1a;制造假数据也是开发…

BadUsb制作

BadUsb制作 一个树莓派pico kali监听 需要的文件 https://pan.baidu.com/s/1_kyzXIqk9JWHGHstTgq7sQ?pwd6666 1.将pico插入电脑 2.将Bad USB固件中的文件复制到pico中&#xff0c;pico会重启 3.将Bad USB目录文件复制进去&#xff08;打开Bad USB目录文件复制&#xff09; …

MySQL--explain执行计划详解

什么是执行计划&#xff1f; SQL的执行计划&#xff0c;通俗来说就是SQL的执行情况&#xff0c;一条SQL语句扫描哪些表&#xff0c;那个子查询先执行&#xff0c;是否用到了索引等等&#xff0c;只有当我们知道了这些情况之后才知道&#xff0c;才可以更好的去优化SQL&#xf…

如何将MathType嵌入到word中 word打开MathType显示错误

当我们编辑好mathtype公式以后&#xff0c;有时候需要将这个公式导入到word中&#xff0c;但是有不少用户们不清楚mathtype如何嵌入到word中。今天小编就给大家说明一下mathtype公式导入word的两种不同方法&#xff0c;有需要的用户们赶紧来看一下吧。 一、mathtype如何嵌入到…

(产品之美系列三)小红书投票组建,利用用户好奇心,增大互动

小红书发布笔记或者视频&#xff0c;可以带一个投票功能。此投票功能与其他的有什么不同呢&#xff1f; 发布一个话题:你觉得王维和李白哪个更帅&#xff1f; 如果你自己不投票&#xff0c;就是看不到结果。当你投票之后: 可以知道选择王维的有百分之八十二。 启发:小红书投…

Git分布式管理-头歌实验远程版本库

Git的一大特点就是&#xff0c;能为不同系统下的开发者提供了一个协作开发的平台。而团队如果要基于Git进行协同开发&#xff0c;就必须依赖远程版本库。远程版本库允许&#xff0c;我们将本地版本库保存在远端服务器&#xff0c;而且&#xff0c;不同的开发者也是基于远程版本…

OJ:循环队列

622. 设计循环队列 - 力扣&#xff08;LeetCode&#xff09; 思路 思路&#xff1a;首先循环队列的意思是&#xff1a;空间固定&#xff0c;就是提前开辟好&#xff0c;满了就不能插入了&#xff0c;但是删除数据后仍有空间&#xff0c;删除循环队列里面的数据后&#xff0c;保…

Python学习日记之学习turtle库(上 篇)

一、初步认识turtle库 turtle 库是 Python 语言中一个很流行的绘制图像的函数库&#xff0c;想象一个小乌龟&#xff0c;在一个横 轴为 x、纵轴为 y 的坐标系原点&#xff0c;(0,0)位置开始&#xff0c;它根据一组函数指令的控制&#xff0c;在这个平面 坐标系中移动&#xff0…

如果编程语言是一种武器……

对程序员来说&#xff0c;编程语言就是武器&#xff0c;但有的武器好用&#xff0c;有的武器不好用&#xff0c;有的武器甚至会杀了自己 C语言是M1式加兰德步枪&#xff0c;很老但可靠。 C是双截棍&#xff0c;挥舞起来很强悍&#xff0c;很吸引人&#xff0c;但需要你多年的磨…