PyQt实战——随机涂格子的特色进度条(十一)

系类往期文章:
PyQt5实战——多脚本集合包,前言与环境配置(一)
PyQt5实战——多脚本集合包,UI以及工程布局(二)
PyQt5实战——多脚本集合包,程序入口QMainWindow(三)
PyQt5实战——操作台打印重定向,主界面以及stacklayout使用(四)
PyQt5实战——UTF-8编码器UI页面设计以及按钮连接(五)
PyQt5实战——UTF-8编码器功能的实现(六)
PyQt5实战——翻译器的UI页面设计以及代码实现(七)
PyQt5实战——翻译的实现,第一次爬取微软翻译经验总结(八)
PyQt5实战——翻译的实现,成功爬取微软翻译(可长期使用)经验总结(九)
PyQt实战——使用python提取JSON数据(十)

前言

这是实现音频编解码器功能模块的第二篇文章,在本文中,我们将实现自定义的特色进度条以及简单介绍编码器的UI布局,本文主要实现特色进度条,在本文中,我们会涉及到的知识点有:特色进度条的实现原理,一个python生成器的用法,PyQt的控件绘制机制。本文仅介绍编解码器UI的布局,因为实现UI时会用到开线程的部分,因此本文先不展示。

本次笔者设计的特色进度条,是涂格子形式的进度条,当进度增加时,进度条内的方格会被随机涂上颜色,供大家参考。

UI框架

如下图所示:

请添加图片描述

  • 编码器的UI布局同样为垂直分布

  • 在第一层级,由经典的选择文件构成,这个选择文件的布局样式已经在本系列中多次出现

  • 一个下拉选择框,目前只有ADPCM一个算法可供选择

  • 两个按钮水平布局,编码按钮启动编码器,解码按钮启动解码器

  • 编解码器读取选中的文件,从文件中读取数据。注意,文件最好是txt文件格式,数据是十六进制数1字节为单位,以空格分隔开,可选择一帧数据一行,如下图所示:

    请添加图片描述

  • 解码器会在workspaces目录下生成pcm.txt数据文件,其中包含了解码后的PCM数据,以文本形式保存,后续将其放入pcm构建器中可生成二进制文件,在PCM播放器中播放即可。

  • 编码器会在workspaces目录下生成adpcm.txt数据文件,其中包含了编码后的ADPCM数据,以文本形式保存,后续如何处理数据,可自行决定。

  • 最下方是进度条,由20*5共100个方格组成,在未启动状态下为灰色,当开始解码或编码时,编码进度每增加1%,那么就会随机涂蓝一个格子,在进度为100%时,即编码完成,则所有格子全部为蓝色。

  • 当重新开始编码或解码时,格子会重新初始化,全部被重新涂成灰色。

进度条展示

下面是正在解码过程中的进度条展示,在右边的日志展示区中可以看到,正在进行ADPCM解码,在功能区中,下方进度条在随机选择格子涂蓝,将这些以显示当前进度

请添加图片描述

下面是解码结束后的进度条样式,可以看到,在右边的日志区中,显示ADPCM decode end,adpcm解码完成,下方的进度条已被完全涂成蓝色。

请添加图片描述

代码详解

下面给出进度条的代码:

import sys
import random
from PyQt5.QtCore import Qt, QTimer
from PyQt5.QtGui import QColor, QPainter, QPainterPath
from PyQt5.QtWidgets import QApplication, QWidget, QVBoxLayout, QPushButton
import queue
class ProgressBar(QWidget):def __init__(self):super().__init__()self.setFixedSize(640, 160)  # 设置进度条的大小self.grid_size = 32  # 每个方格的宽度和高度self.total_grids = (self.width() // self.grid_size) * (self.height() // self.grid_size)  # 方格总数self.lit_grids = 0  # 当前已点亮的方格数量self.grid_status = [False] * self.total_grids  # 每个方格是否被点亮def init_grids(self):self.lit_grids = 0  # 当前已点亮的方格数量self.grid_status = [False] * self.total_gridsself.update()  # 更新进度条绘制def update_progress(self,q):"""随机点亮一个未点亮的方格"""while True:percent = q.get()if self.lit_grids < self.total_grids and percent <= 100:# 随机选择一个未点亮的方格unlit_grids = [i for i, lit in enumerate(self.grid_status) if not lit]random_grid = random.choice(unlit_grids)self.grid_status[random_grid] = True  # 点亮该方格self.lit_grids += 1  # 增加已点亮的方格数量self.update()  # 更新进度条绘制if percent == 100:breakelse:breakdef paintEvent(self, event):"""自定义绘制进度条"""painter = QPainter(self)painter.setRenderHint(QPainter.Antialiasing)  # 启用抗锯齿效果,使边角更平滑# 绘制方格for row in range(self.height() // self.grid_size):for col in range(self.width() // self.grid_size):x = col * self.grid_sizey = row * self.grid_size# 计算当前方格的索引index = row * (self.width() // self.grid_size) + colif self.grid_status[index]:color = QColor(144, 203, 251)   # 已点亮的部分,蓝色else:color = QColor(224, 224, 224) # 未点亮的部分,灰色painter.fillRect(x, y, self.grid_size, self.grid_size, color)  # 绘制方格painter.end()

我们来解释一下上面的代码:

  • __init__方法中,设置了一些基础配置,进度条的大小,方格的大小,方格的总数,已点亮的方格数,每个方格的状态(是否被点亮了),值得注意的是,方格的总是是通过进度条的长除以方格的长,进度条的宽除以方格的宽然后相乘的来的,这里直接翻译为20*5 = 100
  • init_grids方法重新初始化方格的基础配置,归零已点亮的方格数量,擦除方格的状态,并重新绘制进度条,即恢复初始状态
  • update_progress方法用以更新进度,可以看到,该函数接收一个q参数,该参数为一个队列对象,用以线程之间的沟通(线程方面我们下篇文章讨论),此处做一个while的死循环,从队列中获取来自另一个线程的消息,这个操作是阻塞的,也就是说,当未从队列中获取新的消息,则线程不会继续进行下一条指令。
  • 如果已经点亮的方格数小于全部方格数,且进度≤100%,则执行以下操作,随机选择一个未点亮的方格,标志为点亮
  • unlit_grids = [i for i, lit in enumerate(self.grid_status) if not lit],解释一下这一段代码:
    • enumerate(self.grid_status)将会返回一个生成器,生成的是每个元素的索引i和对应的值lit,例如:假设self.grid_status = [True, False, True, Flase],那么生成器将会返回,(0,True),(1,False),(2,True),(3,False)
    • for i, lit in enumerate(self.grid_status)是一个for循环,用来遍历enumerate(self.grid_status)返回的每一个(i,lit)元组,i是索引,litself.grid_status中对应的状态值
    • if not lit这个条件判断会检查lit是否为False,如果为False,就会被加入到列表unlit_grids
    • 总的来说,这是整个列表推导式的语法,它会创建一个包含所有符合条件if not lit的元素索引i的新列表
  • random.choice方法在unlit_grids中随机选择一个方格
  • 将选择的的方格状态设置为点亮
  • 增加已点亮的方格数量
  • 更新进度条绘制
  • 如果进度条达到100%,则直接退出该函数,不再循环,也不再接收来自其它线程的消息
  • paintEvent方法是用以绘制进度条的,创建一个在PyQt中可绘制的对象QPainter。
  • 两层for循环,计算现在的位置,确定这个位置的方格是以点亮还是未点亮,确定颜色,绘制方格。
  • painter.end方法结束绘制

总结

进度条的更新实际上是对控件的重新绘制

paintEvent在什么时候被调用?

paintEvent 是一个 Qt 事件处理函数,它在 需要重新绘制组件时 被自动调用。Qt 会在以下几种情况下触发 paintEvent

  1. 窗口或控件首次显示时
    • 当窗口或控件第一次显示时,paintEvent 会被触发,负责绘制控件的初始状态。
  2. 控件大小发生变化时
    • 如果控件的大小发生了变化(例如,窗口调整大小),paintEvent 会被调用来重新绘制控件,以适应新的尺寸。
  3. 调用 update()repaint()
    • 当你调用 update()repaint() 方法时,Qt 会标记该控件为“需要重绘”,并会在下一个事件循环中触发 paintEvent
  4. 其他因素
    • 如果控件的内容发生了变化,或者某些部分被遮挡并随后暴露出来,Qt 会重新触发 paintEvent 来重新绘制这些区域。例如,当窗口被部分遮挡后再显示出来时,Qt 会调用 paintEvent 来刷新被遮挡的部分。

在上面的代码中,每次更新进度条的状态时,调用self.update,重新绘制控件。

如果不适用self.update,而是直接调用paintEvent绘制进度条可以吗?

直接调用 paintEvent 来重绘进度条并不是一种推荐的做法。原因在于,Qt 的事件机制是自动管理绘制过程的,而直接调用 paintEvent 会绕过事件系统,可能导致一些问题。下面我将详细解释原因和影响。

  1. Qt 的绘制机制
  • 在 Qt 中,paintEvent 由事件循环自动管理,通常不需要直接调用。当你调用 self.update() 时,Qt 会将控件标记为“需要重绘”,并在合适的时机调用 paintEvent
  • self.update() 会触发一个绘制事件,但不会立即调用 paintEvent,而是将重绘请求加入事件队列,稍后由 Qt 的事件循环处理。这样做的好处是,Qt 会合理地合并多个绘制请求,避免频繁的重复重绘,提高性能。
  1. 直接调用 paintEvent 的问题
  • 如果你直接调用 paintEvent,就会绕过 Qt 的事件循环和绘制机制。paintEvent 通常是由系统在适当时机(如控件需要重绘时)自动触发的。
  • 直接调用paintEvent可能会导致:
    • 绘制不一致:因为 Qt 的绘制机制已经负责了缓存和重绘的时机,直接调用 paintEvent 可能会与其他重绘请求冲突,导致绘制不稳定。
    • 不符合最佳实践:Qt 推荐使用 update() 来请求重绘,因为它利用了 Qt 内部的绘制优化策略。如果你直接调用 paintEvent,可能会破坏这些优化。

通过消息队列传递更新消息

进度条的更新实际上需要与编解码器的编解码进度相关,因为编解码器吃算力,因此不建议将编解码器放在主线程中运行,所以对编解码器开了子线程运行,消息的互传需要通过消息队列来进行。关于线程问题,我们将在下一期内容时更详细地讲解。

祝你变得更强!

因为语音编解码器功能模块的实现较为复杂,而且也增加了一些新的UI设计,因此知识点与代码都无法在一篇文章中全部呈现,但将代码分散在不同的文章里又让一些基础比较薄弱的同学难以快速上手,因此,如若对此模块感兴趣的人比较多,笔者将在这三篇文章的基础上,单独开一篇新的博文,梳理代码的布局以及如何在自己的机器上跑起来,让新手小白也能复制即用。

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

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

相关文章

tryhackme-Cyber Security 101-Linux Shells(linux命令框)

目的&#xff1a;了解脚本和不同类型的 Linux shell。 任务1&#xff1a;Introduction to Linux Shells&#xff08;Linux Shell 简介&#xff09; 作为操作系统的常规用户&#xff0c;我们都广泛使用图形用户界面 &#xff08;GUI&#xff09; 来执行大多数操作。只需点击几…

全面Kafka监控方案:从配置到指标

文章目录 1.1.监控配置1.2.监控工具1.3.性能指标系统相关指标GC相关指标JVM相关指标Topic相关指标Broker相关指标 1.4.性能指标说明1.5.重要指标说明 1.1.监控配置 开启JMX服务端口&#xff1a;kafka基本分为broker、producer、consumer三个子项&#xff0c;每一项的启动都需要…

VirtualBox下ubuntu23.04使用主机串口以及使用 minicom 进行串口调试

VirtualBox下ubuntu23.04使用主机串口以及使用 minicom 进行串口调试 一、打开设备管理器看主机&#xff08;Window系统&#xff09;是否识别出串口&#xff0c;我这边显示的串行通信端口是COM3 二、打开VirtualBox&#xff0c;设置串口和USB设备 串口设置&#xff1a; 启用…

解决PDF.js部署到IIS服务器上后报错mjs,.ftl 404 (Not Found)

一、报错问题描述&#xff1a;部署到IIS服务器上后,浏览器控制台报错报错mjs,.ftl 404 (Not Found)&#xff0c;pdf也浏览不了 二、解决方法&#xff1a;在IIS服务器添加MIME类型 将下面类型添加即可 .mjs application/javascript .ftl application/octet-stream保存后&…

Jmeter下载安装配置教程(多版本)

目录 一、介绍 JMeter的主要特点&#xff1a; 使用场景&#xff1a; 二、下载 (一)下载最新版本 (二)下载历史版本 (三)配置环境变量 ​(四)查看版本 (五)启动方式 一、介绍 Apache JMeter 是一款开源的性能测试工具&#xff0c;主要用于对各种服务进行负载测试和性…

PTA数据结构编程题7-1最大子列和问题

我参考的B站up的思路 题目 题目链接 给定K个整数组成的序列{ N 1 ​ , N 2 ​ , …, N K ​ }&#xff0c;“连续子列”被定义为{ N i ​ , N i1 ​ , …, N j ​ }&#xff0c;其中 1≤i≤j≤K。“最大子列和”则被定义为所有连续子列元素的和中最大者。例如给定序列{ -2, 1…

【路径规划】原理及实现

路径规划&#xff08;Path Planning&#xff09;是指在给定地图、起始点和目标点的情况下&#xff0c;确定应该采取的最佳路径。常见的路径规划算法包括A* 算法、Dijkstra 算法、RRT&#xff08;Rapidly-exploring Random Tree&#xff09;等。 目录 一.A* 1.算法原理 2.实…

在 Vue3 项目中实现计时器组件的使用(Vite+Vue3+Node+npm+Element-plus,附测试代码)

一、概述 记录时间 [2024-12-26] 本文讲述如何在 Vue3 项目中使用计时器组件。具体包括开发环境的配置&#xff0c;ViteVue 项目的创建&#xff0c;Element Plus 插件的使用&#xff0c;以及计时器组件的创建和使用。 想要直接实现计时器组件&#xff0c;查看文章的第四部分。…

简单园区网拓扑实验

1.实验拓扑 2.实验要求 1、按照图示的VLAN及IP地址需求&#xff0c;完成相关配置 2、要求SW1为VLAN 2/3的主根及主网关 SW2为vlan 20/30的主根及主网关 SW1和SW2互为备份 3、可以使用super vlan 4、上层通过静态路由协议完成数据通信过程 5、AR1为企业出口路由器 6、要求全网可…

jetson Orin nx + yolov8 TensorRT 加速量化 环境配置

参考【Jetson】Jetson Orin NX纯系统配置环境-CSDN博客 一 系统环境配置&#xff1a; 1.更换源&#xff1a; sudo vi /etc/apt/sources.list.d/nvidia-l4t-apt-source.list2.更新源&#xff1a; sudo apt upgradesudo apt updatesudo apt dist-upgrade sudo apt-get updat…

音视频入门基础:MPEG2-TS专题(22)——FFmpeg源码中,获取TS流的音频信息的实现

音视频入门基础&#xff1a;MPEG2-TS专题系列文章&#xff1a; 音视频入门基础&#xff1a;MPEG2-TS专题&#xff08;1&#xff09;——MPEG2-TS官方文档下载 音视频入门基础&#xff1a;MPEG2-TS专题&#xff08;2&#xff09;——使用FFmpeg命令生成ts文件 音视频入门基础…

MySQL45讲 第三十六讲 为什么临时表可以重名?——阅读总结

文章目录 MySQL45讲 第三十六讲 为什么临时表可以重名&#xff1f;——阅读总结一、引言二、临时表与内存表的区别&#xff08;一&#xff09;内存表&#xff08;二&#xff09;临时表 三、临时表的特性&#xff08;一&#xff09;可见性与生命周期&#xff08;二&#xff09;与…

MATLAB符号计算-符号表达式基础运算操作

1.1.2符号变量取值域的限定 默认复数域 【例1-1-2】解不等式 1.1.3创建符号表达式 对符号对象进行各种运算&#xff08;算术运算、关系运算、逻辑运算&#xff09;&#xff0c;即可创建符号表达式。 1.算术运算与转置 【例1-1-3】 f5是f4的共轭转置 f6是f4的转置 2.关系…

深度学习-78-大模型量化之Quantization Aware Training量化感知训练QAT

文章目录 1 量化感知训练1.1 QAT的核心思想1.2 QAT的工作原理1.2.1 第一个维度1.2.2 第二个维度2 大模型的1-bits时代BitNet2.1 BitLinear层2.2 权重量化2.3 激活量化2.4 反量化3 大模型处于1.58Bits状态3.1 零值的作用3.2 量化3.3 效果4 参考附录1 量化感知训练 PTQ方法的一个…

(亲测)frp对外提供简单的文件访问服务-frp静态文件效果

话说有一天&#xff0c;希望将软件安装包放到网上&#xff0c;希望类似如下效果&#xff0c;正好在调试frp docker版&#xff0c;看到frp有个【对外提供简单的文件访问服务】功能&#xff0c;网上搜索也没相关效果图&#xff0c;所以顺手测试一下&#xff0c;截了几张图&#x…

基于YOLOV5+Flask安全帽RTSP视频流实时目标检测

1、背景 在现代工业和建筑行业中&#xff0c;安全始终是首要考虑的因素之一。特别是在施工现场&#xff0c;工人佩戴安全帽是确保人身安全的基本要求。然而&#xff0c;人工监督难免会有疏漏&#xff0c;尤其是在大型工地或复杂环境中&#xff0c;确保每个人都佩戴安全帽变得非…

LabVIEW数字式气压计自动检定系统

开发了一个基于LabVIEW开发的数字式气压计自动检定系统。在自动化检定PTB220和PTB210系列数字气压计&#xff0c;通过优化硬件组成和软件设计&#xff0c;实现高效率和高准确度的检定工作&#xff0c;有效降低人力成本并提升操作准确性。 项目背景 随着自动气象站的广泛部署&a…

FPGA的DMA应用——pcileech

硬件通过pcie总线&#xff0c;访存本机的内存&#xff0c;并进行修改&#xff0c;可以进行很多操作。 学习视频&#xff1a;乱讲DMA及TLP 1-pcileech项目简介和自定义模块介绍_哔哩哔哩_bilibili vivado2024.1的下载文章链接和地址&#xff1a;AMD-Xilinx Vivado™ 2024.1 现…

【漫话机器学习系列】022.微积分中的链式求导法则(chain rule of Calculus)

链式求导法则&#xff08;Chain Rule of Calculus&#xff09; 链式求导法则是微积分中的重要工具&#xff0c;用于处理复合函数的求导。它描述了如何计算一个函数的函数&#xff08;复合函数&#xff09;的导数。 1. 链式法则的定义 假设有一个复合函数 y f(g(x))&#xff…

TP5 动态渲染多个Layui表格并批量打印所有表格

记录&#xff1a; TP5 动态渲染多个Layui表格每个表格设置有2行表头&#xff0c;并且第一行表头在页面完成后动态渲染显示内容每个表格下面显示统计信息可点击字段排序一次打印页面上的所有表格打印页面上多个table时,让每个table单独一页 后端代码示例&#xff1a; /*** Nod…