使用PyQt5绘制带有刻度的温度计控件

前言:进入学习Python开发上位机界面的第二阶段,学习如何开发自定义控件,从常用的控件入手学习,本期主要学习如何使用PyQt5绘制带有刻度的温度计控件。

1. 先找到一篇参考文章

参考文章:Qt编写自定义控件5-柱状温度计

在这里插入图片描述

参考文章代码是C++写的,改成python写的代码。修改代码成功后,本人生成的温度计控件显示效果如下所示:

在这里插入图片描述

2. 学习背后涉及的知识点

参考文章:实战PyQt5: 123-详解QPainter绘图

2.1 如何设置圆形图案的文本在圆形图案中居中显示

drawEllipse(): 绘制一个椭圆,当设置椭圆的宽度和高度相等,它实际上是一个圆形。

在这里插入图片描述

使用drawText(x, y, text_width, text_height, Qt.AlignCenter, text) 方法绘制文本,其中x是计算出的起始X坐标,y是你希望文本基线的位置(通常是你绘制区域的中心线或稍上方)。

 def draw_centered_text(self, qp):  # 设置字体  font = QFont('Arial', 20)  qp.setFont(font)  # 要绘制的文本  text = "Hello, PyQt5!"  # 获取文本的宽度和高度  fm = QFontMetrics(font)  text_width = fm.width(text)  text_height = fm.height()  # 计算文本居中绘制的起始位置  # 注意:这里假设我们要在整个QWidget的区域内居中  x = (self.width() - text_width) // 2  y = (self.height() + text_height) // 2  # 向上偏移文本高度的一半以垂直居中  # 绘制文本  qp.drawText(x, y, text_width, text_height, Qt.AlignCenter, text)  

2.2 绘制温度计刻度线

drawPoint(): 使用当前笔的颜色在给定位置绘制一个点。
drawLine(): 绘制一条线。

在这里插入图片描述

2.3 绘制标识当前温度值的三角形箭头

drawPolygon(): 绘制可填充一个多边形。

在这里插入图片描述

3. 温度计控件待优化点

3.1 温度计刻度显示的温度数值存在精度问题

温度计的数值范围是0℃-100℃,在某些数值下温度计的刻度箭头指示是不够准确的,例如下图所示:37℃的箭头应该指示在目前位置的偏下面一点。

在这里插入图片描述

原因分析:用于显示用户设置温度数值的三角形箭头Y轴位置计算是有精度误差的。

 # 计算标尺的高度rulerHeight = self.height() - 2 * self.radius# 计算每一格移动多少increment = rulerHeight / (self.maxValue - self.minValue)self.barX = self._get_bar_x_pos()self.barY = (self.maxValue - self.userValue + self.minValue) * increment

这个是因为python进行除法运算就存在计算精度问题:

在这里插入图片描述

3.2 用户设置温度为标尺的最大刻度值存在显示问题

原因分析:温度计标尺的最大刻度Y轴位置正好是温度计控件所在界面的高度。

在这里插入图片描述

4. 带有刻度的温度计控件的完整代码

#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Author : Logintern09
import sysfrom PyQt5.QtCore import QRectF, QPoint, Qt, QRect, QPointF
from PyQt5.QtGui import (QPainter,QColor,QFont,QPainterPath,QPolygon,
)
from PyQt5.QtWidgets import QWidget, QApplication, QMainWindow, QVBoxLayout"""* 柱状温度计控件* 1:可设置精确度(小数点后几位)* 2:可设置背景色/柱状颜色/线条颜色* 3:可设置长线条步长及短线条步长* 4:可设置温度计数值显示范围值* 5:支持负数刻度值* 6:可设置刻度尺位置 无 左侧 右侧 两侧* 7:可设置用户设定目标值
"""class BasicRulerTemp(QWidget):def __init__(self, parent=None):super(BasicRulerTemp, self).__init__(parent)self.TickPosition = {"TickPosition_Null": 0,  # 不显示"TickPosition_Left": 1,  # 左侧显示"TickPosition_Right": 2,  # 右侧显示"TickPosition_Both": 3,  # 两侧显示}self.minValue = 0  # 最小值self.maxValue = 100  # 最大值self.userValue = 80  # 用户设定值self.userValueColor = QColor("red")  # 用户设定值颜色self.precision = 0  # 精确度, 小数点后几位self.longStep = 10  # 长线条等分步长self.shortStep = 2  # 短线条等分步长self.showUserValue = True  # 是否显示三角箭头显示用户设定值self.lineColor = QColor("black")  # 线条颜色self.barBgColor = QColor("white")  # 柱状背景色self.barColor = QColor("red")  # 柱状颜色self.tickPosition = 3  # 刻度尺位置self.barWidth = 15  # 水银柱宽度self.barHeight = 100  # 水银柱高度self.radius = 30  # 水银柱底部圆半径self.circleX_pos = self.width() / 3  # 水银柱底部圆X轴坐标位置def get_min_value(self):"""获取温度计刻度最小值"""return self.minValuedef get_max_value(self):"""获取温度计刻度最大值"""return self.maxValuedef get_current_value(self):"""获取用户设置的温度计当前刻度值"""return self.userValuedef get_precision(self):"""获取温度计刻度显示的数值精度(保留到小数点后几位)"""return self.precisiondef set_range(self, min_value, max_value):"""设置温度计数值显示范围"""self.minValue = min_valueself.maxValue = max_valuedef set_min_value(self, min_value):"""设置温度计刻度显示最小数值"""self.minValue = min_valuedef set_max_value(self, max_value):"""设置温度计刻度显示最大数值"""self.maxValue = max_valuedef set_value(self, value):"""设置温度计刻度显示的数值"""self.userValue = valuedef set_precision(self, precision):"""设置温度计显示刻度数值的精度(保留到小数点后几位)"""self.precision = precisiondef set_long_step(self, long_step):"""设置温度计刻度线的长线条步长"""self.longStep = long_stepdef set_short_step(self, short_step):"""设置温度计刻度线的短线条步长"""self.shortStep = short_stepdef set_show_user_value(self, show_user_value):"""设置是否显示用户设定值"""self.showUserValue = show_user_valuedef set_bar_bg_color(self, color):"""设置水银柱背景颜色"""self.userValueColor = QColor(color)def set_line_color(self, color):"""设置温度计刻度线的线条颜色"""self.lineColor = QColor(color)def set_tick_position(self, tick_position):"""设置刻度尺位置"""self.tickPosition = self.TickPosition[tick_position]class QRulerTemp(BasicRulerTemp):def __init__(self, parent=None):super(QRulerTemp, self).__init__(parent)self.setWindowFlags(self.windowFlags() | Qt.FramelessWindowHint)self.setAttribute(Qt.WA_TranslucentBackground)self.setFixedSize(500, 500)def paintEvent(self, event):# 绘制准备工作,启用反锯齿painter = QPainter(self)# 保存当前画笔状态painter.save()painter.setRenderHints(QPainter.Antialiasing | QPainter.TextAntialiasing)# 绘制水银柱背景,包含水银柱底部圆self.drawBarBg(painter)# 绘制当前水银柱,包含水银柱底部圆self.drawBar(painter)# 绘制标尺及刻度尺if self.tickPosition == self.TickPosition["TickPosition_Left"]:self.drawRuler(painter, 0)elif self.tickPosition == self.TickPosition["TickPosition_Right"]:self.drawRuler(painter, 1)elif self.tickPosition == self.TickPosition["TickPosition_Both"]:self.drawRuler(painter, 0)self.drawRuler(painter, 1)# 绘制用于显示水银柱当前值的三角箭头if self.showUserValue:self.draw_value_flag(painter)def _get_bar_x_pos(self):# 计算在背景宽度的基础上缩小的百分比, 至少为 2circlePercent = self.radius / 3if circlePercent < 2:circlePercent = 2bar_x_pos = self.circleX_pos + circlePercentreturn bar_x_posdef drawBarBg(self, painter):# 绘制水银柱背景颜色,包含水银柱底部圆painter.save()painter.setPen(Qt.NoPen)painter.setBrush(self.barBgColor)# 计算标尺的高度offset = 4  # 背景形状偏移水银柱的像素barHeight = self.height() - self.radiusbar_x_pos = self._get_bar_x_pos()barX = bar_x_pos - offsetbarY = 1barRect = QRectF(barX, barY, self.barWidth + offset * 2, barHeight)circleX = self.circleX_pos - 10circleY = self.height() - self.radius * 2 - 1circleWidth = self.radius * 2circleRect = QRectF(circleX, circleY, circleWidth, circleWidth)path = QPainterPath()path.addRect(barRect)path.addEllipse(circleRect)path.setFillRule(Qt.WindingFill)painter.drawPath(path)painter.restore()def drawBar(self, painter):# 绘制水银柱及标识当前刻度的三角形箭头painter.save()painter.setPen(Qt.NoPen)painter.setBrush(self.barColor)# 计算在背景宽度的基础上缩小的百分比, 至少为 2circlePercent = self.radius / 3if circlePercent < 2:circlePercent = 2# 计算标尺的高度rulerHeight = self.height() - 2 * self.radius# 计算每一格移动多少increment = rulerHeight / (self.maxValue - self.minValue)self.barX = self._get_bar_x_pos()self.barY = (self.maxValue - self.userValue + self.minValue) * incrementbarRect = QRectF(self.barX,self.barY,self.barWidth,(self.userValue - self.minValue) * increment + self.radius,)circleX = self.circleX_pos# 偏移 2 个像素,使得看起来边缘完整circleY = self.height() - self.radius * 2 - 2circleWidth = self.radius * 2 - circlePercent * 2circleRect = QRectF(circleX, circleY + circlePercent, circleWidth, circleWidth)path = QPainterPath()path.addRect(barRect)path.addEllipse(circleRect)path.setFillRule(Qt.WindingFill)painter.drawPath(path)# 设置水银柱底部圆的文本和文本颜色painter.save()font = QFont()font.setPixelSize(circleRect.width() * 0.55)painter.setFont(font)painter.setPen(Qt.white)painter.drawText(circleRect, Qt.AlignCenter, "%s" % self.userValue)painter.restore()def drawRuler(self, painter, type):# 绘制刻度线painter.save()painter.setPen(self.lineColor)barPercent = self.barWidth / 8  # 水银柱宽度if barPercent < 2:barPercent = 2# 绘制纵向标尺刻度length = self.height() - 2 * self.radius# 计算每一格移动多少increment = length / (self.maxValue - self.minValue)# 长线条短线条长度longLineLen = 10shortLineLen = 7# 绘制纵向标尺线 偏移line_offset像素self.line_offset = 8offset = self.barWidth / 2 + self.line_offset# 左侧刻度尺需要重新计算if type == 0:offset = -1 * offsetlongLineLen = -1 * longLineLenshortLineLen = -1 * shortLineLeninitX = self.barX + self.barWidth / 2 + offsetinitY = barPercenttopPot = QPointF(initX, initY)bottomPot = QPointF(initX, self.height() - 2 * self.radius)painter.drawLine(topPot, bottomPot)# 根据范围值绘制刻度线及刻度值for i in range(self.maxValue, self.minValue - self.shortStep, -1 * self.shortStep):if i % self.longStep == 0:# 绘制长线条leftPot = QPoint(initX + longLineLen, initY)rightPot = QPoint(initX, initY)painter.drawLine(leftPot, rightPot)# 绘制文字strValue = f"{i:.{self.precision}f}"fontHeight = painter.fontMetrics().height()if type == 0:# 左侧刻度线x_offset = 45 + self.precision * 10text_width = 30 + self.precision * 10textRect = QRect(initX - x_offset, initY - fontHeight / 3, text_width, 15)painter.drawText(textRect, Qt.AlignRight, strValue)elif type == 1:# 右侧刻度线text_width = 30 + self.precision * 10textRect = QRect(initX + longLineLen + self.line_offset,initY - fontHeight / 3,text_width,15,)painter.drawText(textRect, Qt.AlignLeft, strValue)else:# 绘制短线条leftPot = QPointF(initX + shortLineLen, initY)rightPot = QPointF(initX, initY)painter.drawLine(leftPot, rightPot)initY += increment * self.shortSteppainter.restore()def draw_value_flag(self, painter):painter.save()painter.setPen(Qt.NoPen)# 绘制用户设定值三角号if self.showUserValue:if (self.tickPosition == self.TickPosition["TickPosition_Left"]or self.tickPosition == self.TickPosition["TickPosition_Both"]):pts = QPolygon()offset = 15initX = self.barX - self.line_offsetinitY = self.barYpts.append(QPoint(initX, initY))pts.append(QPoint(initX - offset, initY - offset / 2))pts.append(QPoint(initX - offset, initY + offset / 2))painter.setBrush(self.userValueColor)painter.drawPolygon(pts)if (self.tickPosition == self.TickPosition["TickPosition_Right"]or self.tickPosition == self.TickPosition["TickPosition_Both"]):pts = QPolygon()offset = 15initX = self.barX + self.barWidth + self.line_offsetinitY = self.barYpts.append(QPoint(initX, initY))pts.append(QPoint(initX + offset, initY - offset / 2))pts.append(QPoint(initX + offset, initY + offset / 2))painter.setBrush(self.userValueColor)painter.drawPolygon(pts)painter.restore()def main():app = QApplication(sys.argv)window = QMainWindow()window.setGeometry(100, 100, 100, 290)window.setWindowTitle("RulerTemp Example")RulerTemp = QRulerTemp()layout = QVBoxLayout()layout.addWidget(RulerTemp)window.setCentralWidget(QWidget())window.centralWidget().setLayout(layout)window.show()sys.exit(app.exec_())if __name__ == "__main__":main()# app = QApplication([])# widget = QRulerTemp()# widget.show()# app.exec_()

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

问deepseek,如何看待ai降低学习成本而导致软件开发岗位需求降低,和工资下降。 软件从业人员何去何从?

它给我的回答是这样的&#xff1a; 思考逻辑 嗯&#xff0c;用户问的是AI如何降低学习成本&#xff0c;进而导致软件开发岗位需求减少和工资下降&#xff0c;以及软件从业人员该怎么办。这个问题挺复杂的&#xff0c;我得先理清楚各个部分。首先&#xff0c;AI确实在改变很多行…

Error: Expected a mutable image

你的函数用了不支持的图片格式比如我的人脸检测&#xff0c;本来要RGB565我却用JPEG所以报错

海思ISP开发说明

1、概述 ISP&#xff08;Image Signal Processor&#xff09;图像信号处理器是专门用于处理图像信号的硬件或处理单元&#xff0c;广泛应用于图像传感器&#xff08;如 CMOS 或 CCD 传感器&#xff09;与显示设备之间的信号转换过程中。ISP通过一系列数字图像处理算法完成对数字…

2.攻防世界PHP2及知识点

进入题目页面如下 意思是你能访问这个网站吗&#xff1f; ctrlu、F12查看源码&#xff0c;什么都没有发现 用kali中的dirsearch扫描根目录 命令如下&#xff0c;根据题目提示以及需要查看源码&#xff0c;扫描以php、phps、html为后缀的文件 dirsearch -u http://61.147.17…

线性数据结构:单向链表

放弃眼高手低&#xff0c;你真正投入学习&#xff0c;会因为找到一个新方法产生成就感&#xff0c;学习不仅是片面的记单词、学高数......只要是提升自己的过程&#xff0c;探索到了未知&#xff0c;就是学习。 目录 一.链表的理解 二.链表的分类&#xff08;重点理解&#xf…

【AI】探索自然语言处理(NLP):从基础到前沿技术及代码实践

Hi &#xff01; 云边有个稻草人-CSDN博客 必须有为成功付出代价的决心&#xff0c;然后想办法付出这个代价。 目录 引言 1. 什么是自然语言处理&#xff08;NLP&#xff09;&#xff1f; 2. NLP的基础技术 2.1 词袋模型&#xff08;Bag-of-Words&#xff0c;BoW&#xff…

书生大模型实战营7

文章目录 L1——基础岛提示词工程实践什么是Prompt(提示词)什么是提示工程提示设计框架CRISPECO-STAR LangGPT结构化提示词LangGPT结构编写技巧构建全局思维链保持上下文语义一致性有机结合其他 Prompt 技巧 常用的提示词模块 浦语提示词工程实践(LangGPT版)自动化生成LangGPT提…

一个开源 GenBI AI 本地代理(确保本地数据安全),使数据驱动型团队能够与其数据进行互动,生成文本到 SQL、图表、电子表格、报告和 BI

一、GenBI AI 代理介绍&#xff08;文末提供下载&#xff09; github地址&#xff1a;https://github.com/Canner/WrenAI 本文信息图片均来源于github作者主页 在 Wren AI&#xff0c;我们的使命是通过生成式商业智能 &#xff08;GenBI&#xff09; 使组织能够无缝访问数据&…

41. 缺失的第一个正数

参考题解&#xff1a;https://leetcode.cn/problems/first-missing-positive/solutions/7703/tong-pai-xu-python-dai-ma-by-liweiwei1419 难点在于时间复杂度控制在O(n)&#xff0c;空间复杂度为常数级。 哈希表时间复杂度符合&#xff0c;但是空间复杂度为O(n) 排序空间复杂…

深入核心:一步步手撕Tomcat搭建自己的Web服务器

介绍&#xff1a; servlet&#xff1a;处理 http 请求 tomcat&#xff1a;服务器 Servlet servlet 接口&#xff1a; 定义 Servlet 声明周期初始化&#xff1a;init服务&#xff1a;service销毁&#xff1a;destory 继承链&#xff1a; Tomcat Tomcat 和 servlet 原理&#x…

final-关键字

一、final修饰的类不能被继承 当final修饰一个类时&#xff0c;表明这个类不能被其他类继承。例如&#xff0c;在 Java 中&#xff0c;String类就是被final修饰的&#xff0c;这保证了String类的不可变性和安全性&#xff0c;防止其他类通过继承来改变String类的行为。 final…

51单片机 01 LED

一、点亮一个LED 在STC-ISP中单片机型号选择 STC89C52RC/LE52RC&#xff1b;如果没有找到hex文件&#xff08;在objects文件夹下&#xff09;&#xff0c;在keil中options for target-output- 勾选 create hex file。 如果要修改编程 &#xff1a;重新编译-下载/编程-单片机重…

知识库建设与知识管理实践对企业发展的助推作用探索

内容概要 在当今瞬息万变的商业环境中&#xff0c;知识库建设与知识管理实践日益成为企业发展的重要驱动力。知识库作为组织内信息和知识的集成&#xff0c;起着信息存储、整理和共享的关键作用。通过有效的知识库建设&#xff0c;企业不仅能够提升员工获取信息的便利性&#…

【Pytorch和Keras】使用transformer库进行图像分类

目录 一、环境准备二、基于Pytorch的预训练模型1、准备数据集2、加载预训练模型3、 使用pytorch进行模型构建 三、基于keras的预训练模型四、模型测试五、参考 现在大多数的模型都会上传到huggface平台进行统一的管理&#xff0c;transformer库能关联到huggface中对应的模型&am…

如何使用 DeepSeek 和 Dexscreener 构建免费的 AI 加密交易机器人?

我使用DeepSeek AI和Dexscreener API构建的一个简单的 AI 加密交易机器人实现了这一目标。在本文中&#xff0c;我将逐步指导您如何构建像我一样的机器人。 DeepSeek 最近发布了R1&#xff0c;这是一种先进的 AI 模型。您可以将其视为 ChatGPT 的免费开源版本&#xff0c;但增加…

ArkTS渲染控制

文章目录 if/else:条件渲染ArkUI通过自定义组件的build()函数和@Builder装饰器中的声明式UI描述语句构建相应的UI。在声明式描述语句中开发者除了使用系统组件外,还可以使用渲染控制语句来辅助UI的构建,这些渲染控制语句包括控制组件是否显示的条件渲染语句,基于数组数据快…

potplayer字幕

看视频学习&#xff0c;实时字幕可以快速过滤水字数阶段&#xff0c;提高效率&#xff0c;但是容易错过一些信息。下面就是解决这一问题。 工具ptoplayer 一.生成字幕 打开学习视频&#xff0c;右键点击视频画面&#xff0c;点选字幕。勾选显示字幕。点选创建有声字幕&#…

deepseek的两种本地使用方式

总结来说 ollama是命令行 GPT4ALL桌面程序。 然后ollamaAnythingLLM可以达到桌面或web的两种接入方式。 一. ollama和deepseek-r1-1.5b和AnythingLLM 本文介绍一个桌面版的deepseek的本地部署过程&#xff0c;其中ollama可以部署在远程。 1. https://www.cnblogs.com/janeysj/p…

海外问卷调查渠道查,如何影响企业的运营

我们注意到&#xff0c;随着信息资源和传播的变化&#xff0c;海外问卷调查渠道查已发生了深刻的变化。几年前&#xff0c;市场调研是业内专家们的事&#xff0c;即使是第二手资料也需要专业人士来完成&#xff1b;但如今的因特网和许许多多的信息数据库&#xff0c;使每个人都…

TensorFlow简单的线性回归任务

如何使用 TensorFlow 和 Keras 创建、训练并进行预测 1. 数据准备与预处理 2. 构建模型 3. 编译模型 4. 训练模型 5. 评估模型 6. 模型应用与预测 7. 保存与加载模型 8.完整代码 1. 数据准备与预处理 我们将使用一个简单的线性回归问题&#xff0c;其中输入特征 x 和标…