全网最适合入门的面向对象编程教程:18 类和对象的 Python 实现-多重继承与 PyQtGraph 串口数据绘制曲线图

news/2024/11/16 2:22:41/文章来源:https://www.cnblogs.com/FreakEmbedded/p/18306418

全网最适合入门的面向对象编程教程:18 类和对象的 Python 实现-多重继承与 PyQtGraph 串口数据绘制曲线图

摘要:

本文主要介绍了 Python 中创建自定义类时如何使用多重继承、菱形继承的概念和易错点,同时讲解了如何使用 PyQtGraph 库对串口接收的数据进行绘图。

原文链接:

FreakStudio的博客

往期推荐:

学嵌入式的你,还不会面向对象??!

全网最适合入门的面向对象编程教程:00 面向对象设计方法导论

全网最适合入门的面向对象编程教程:01 面向对象编程的基本概念

全网最适合入门的面向对象编程教程:02 类和对象的 Python 实现-使用 Python 创建类

全网最适合入门的面向对象编程教程:03 类和对象的 Python 实现-为自定义类添加属性

全网最适合入门的面向对象编程教程:04 类和对象的Python实现-为自定义类添加方法

全网最适合入门的面向对象编程教程:05 类和对象的Python实现-PyCharm代码标签

全网最适合入门的面向对象编程教程:06 类和对象的Python实现-自定义类的数据封装

全网最适合入门的面向对象编程教程:07 类和对象的Python实现-类型注解

全网最适合入门的面向对象编程教程:08 类和对象的Python实现-@property装饰器

全网最适合入门的面向对象编程教程:09 类和对象的Python实现-类之间的关系

全网最适合入门的面向对象编程教程:10 类和对象的Python实现-类的继承和里氏替换原则

全网最适合入门的面向对象编程教程:11 类和对象的Python实现-子类调用父类方法

全网最适合入门的面向对象编程教程:12 类和对象的Python实现-Python使用logging模块输出程序运行日志

全网最适合入门的面向对象编程教程:13 类和对象的Python实现-可视化阅读代码神器Sourcetrail的安装使用

全网最适合入门的面向对象编程教程:全网最适合入门的面向对象编程教程:14 类和对象的Python实现-类的静态方法和类方法

全网最适合入门的面向对象编程教程:15 类和对象的 Python 实现-__slots__魔法方法

全网最适合入门的面向对象编程教程:16 类和对象的Python实现-多态、方法重写与开闭原则

全网最适合入门的面向对象编程教程:17 类和对象的Python实现-鸭子类型与“file-like object“

更多精彩内容可看:

给你的 Python 加加速:一文速通 Python 并行计算

一文搞懂 CM3 单片机调试原理

肝了半个月,嵌入式技术栈大汇总出炉

电子计算机类比赛的“武林秘籍”

一个MicroPython的开源项目集锦:awesome-micropython,包含各个方面的Micropython工具库

文档和代码获取:

可访问如下链接进行对文档下载:

https://github.com/leezisheng/Doc

image

本文档主要介绍如何使用 Python 进行面向对象编程,需要读者对 Python 语法和单片机开发具有基本了解。相比其他讲解 Python 面向对象编程的博客或书籍而言,本文档更加详细、侧重于嵌入式上位机应用,以上位机和下位机的常见串口数据收发、数据处理、动态图绘制等为应用实例,同时使用 Sourcetrail 代码软件对代码进行可视化阅读便于读者理解。

相关示例代码获取链接如下:https://github.com/leezisheng/Python-OOP-Demo

正文

在 python 中一个类能继承自不止一个父类,这叫做 python 的多重继承,多重继承的语法与单继承类似:

class SubclassName(BaseClass1, BaseClass2, BaseClass3, ...):pass

当然,子类所继承的所有父类同样也能有自己的父类,这样就可以得到一个继承关系机构图如下图所示:

image

多重继承最常见的用途就是用于创建包含两组完全不同行为的对象。例如,设计一个对象用于连接扫描器并将扫描的文件通过传真发送出去,这一对象可能继承自两个完全独立的 scanner 和 faxer 对象。

对于 MasterClass 来说,我们希望它可以具有绘图功能,能够将串口接收到的传感器数据动态绘制曲线,这里我们借助 PyQtGraph 库来完成,PyQtGraph 是纯 Python 图形 GUI 库,它充分利用 PyQt 和 PtSide 的高质量的图形表现水平和 NumPy 的快速科学计算与处理能力,在数学、科学和工程领域都有广泛的应用。

PyQtGraph 相比于 matplotlib 更加适合于数据采集和分析应用。

我们使用如下两条语句完成 PyQtGraph 库和其依赖库 PyQt5 的安装:

**pip install pyqtgraph **

**pip install PyQt5 **

pip install numpy

这里,我们首先定义一个绘图类及其方法,示例代码如下:

class PlotClass:_# 绘图类初始化_def __init__(self,wintitle:str="Basic plotting examples",plottitle:str="Updating plot",width:int=1000,height:int=600):_# Qt应用实例对象_self.app        = None_# 窗口对象_self.win        = None_# 设置窗口标题_self.title      = wintitle_# 设置窗口尺寸_self.width      = widthself.height     = height_# 传感器数据_self.value      = 0_# 计数变量_self.__count    = 0_# 传感器数据缓存列表_self.valuelist  = []_# 绘图曲线_self.curve      = None_# 图层对象_self.plotob     = None_# 图层标题_self.plottitle  = plottitle_# Qt应用和窗口初始化_self.appinit()_# 应用程序初始化_def appinit(self):_# 创建一个Qt应用,并返回该应用的实例对象_self.app = pg.mkQApp("Plotting Example")_# 生成多面板图形__# show:(bool) 如果为 True,则在创建小部件后立即显示小部件。__# title:(str 或 None)如果指定,则为此小部件设置窗口标题。_self.win = pg.GraphicsLayoutWidget(show=True, title=self.title)_# 设置窗口尺寸_self.win.resize(self.width, self.height)_# 进行窗口全局设置,setConfigOptions一次性配置多项参数__# antialias启用抗锯齿,useNumba对图像进行加速_pg.setConfigOptions(antialias=True, useNumba=True)_# 添加图层_self.plotob = self.win.addPlot(title=self.plottitle)_# 添加曲线_self.curve = self.plotob.plot(pen='y')_# 接收数据_def GetValue(self,value):self.value = value_# 加入数据缓存列表_self.valuelist.append(value)_# 更新曲线数据_def DataUpdate(self):_# 模拟绘制正弦曲线__# 计数变量更新_self.__count = self.__count + 0.1self.value = np.sin(self.__count)self.GetValue(self.value)_# 将数据转化为图形_self.curve.setData(self.valuelist)_# 设置定时更新_def SetUpdate(self,time:int = 100):_# 创建定时器对象_timer = QtCore.QTimer()_# 定时器结束,触发DataUpdate方法_timer.timeout.connect(self.DataUpdate)_# 启动定时器_timer.start(time)_# 进入主事件循环并等待_pg.exec()if __name__ == '__main__':_# 创建PlotClass对象,自动完成初始化_p = PlotClass()_# 设置定时更新任务_p.SetUpdate()

这里,我们定义了如下属性和方法:

属性/方法 作用
wintitle 窗口标题
plottitle 图层标题
width 窗口宽度
height 窗口高度
app Qt 应用实例对象
win 窗口对象
value 传感器数据
__count 计数变量
valuelist 传感器数据缓存列表
curve 绘图曲线
plotob 图层对象
appinit(self) 用于 qt 应用程序初始化,添加窗口、曲线和图层
GetValue(self,value) 用于接收传感器数据,加入缓存列表
DataUpdate(self) 用于定时进行曲线更新,这里模拟绘制正弦曲线
SetUpdate(self,time:int = 100) 设置定时更新任务

首先,我们在__init__和 appinit()中完成初始化操作,包括对类内属性、Qt 应用实例、窗口图层及曲线的初始化:

image

接着我们在 SetUpdate 方法中创建定时器对象并设置定时任务,当设置的延时时间到达时,调用 DataUpdate 方法,在其中对数据曲线并进行更新,示例中,我们利用__count 属性每次递增,使得其绘制正弦曲线。

同时设置进入主事件循环并等待吗,以使得 Qt 界面保持显示:

image

这里,我们在主函数中创建对象并启动运行定时刷新曲线,如下为结果:

image

这里,我们想要使得 MasterClass 类同时具备串口收发和绘图功能,因此要用到多重继承,MasterClass 类同时继承于 SerialClass 和 PlotClass。通过多重继承,一个子类就可以同时获得多个父类的所有功能。

示例代码如下:

class MasterClass(SerialClass,PlotClass):_# 类变量:__#   BUSY_STATE  -忙碌状态-0__#   IDLE_STATE  -空闲状态-1_BUSY_STATE, IDLE_STATE = (0, 1)_# 类变量:__#   START_CMD       - 开启命令      -0__#   STOP_CMD        - 关闭命令      -1__#   SENDID_CMD      - 发送ID命令    -2__#   SENDVALUE_CMD   - 发送数据命令   -3_START_CMD, STOP_CMD, SENDID_CMD, SENDVALUE_CMD = (0, 1, 2, 3)_# 类的初始化_def __init__(self,state:int = IDLE_STATE,port:str = "COM17",wintitle:str="Basic plotting examples",plottitle:str="Updating plot",width:int=1000,height:int=600):_# 分别调用不同父类的__init__方法_SerialClass.__init__(self,port)PlotClass.__init__(self,wintitle,plottitle,width,height)self.valuequeue   = queue.Queue(10)self.__masterstatue = state_# 初始化完成的标志量_self.INIT_FLAG = False@classmethoddef MasterInfo(cls):print("Info : "+str(cls))_# 开启主机_def StartMaster(self):super().OpenSerial()logging.info("START MASTER :"+self.dev.port)_# 停止主机_def StopMaster(self):super().CloseSerial()logging.info("CLOSE MASTER :" + self.dev.port)_# 接收传感器ID号_def RecvSensorID(self):sensorid = super().ReadSerial()logging.info("MASTER RECIEVE ID : " + str(sensorid))return sensorid_# 接收传感器数据_def RecvSensorValue(self):data = super().ReadSerial()logging.info("MASTER RECIEVE DATA : " + str(data))self.valuequeue.put(data)return data_# 主机发送命令_def SendSensorCMD(self,cmd):super().WriteSerial(str(cmd))logging.info("MASTER SEND CMD : " + str(cmd))_# 主机返回工作状态-_def RetMasterStatue(self):return self.__masterstatue_# 重写父类的DataUpdate方法_def DataUpdate(self):self.SendSensorCMD(self.SENDVALUE_CMD)self.value = self.RecvSensorValue()self.WriteSerial("Recv:"+str(self.value))self.GetValue(self.value)self.curve.setData(self.valuelist)if __name__ == "__main__":_# 初始化对象_m = MasterClass(state = MasterClass.IDLE_STATE,port = "COM17",wintitle = "Basic plotting examples",plottitle = "Updating plot",width = 1000,height = 600)m.StartMaster()m.SendSensorCMD(MasterClass.SENDID_CMD)m.RecvSensorID()_# 设置定时更新任务_m.SetUpdate()

我们可以看到两个父类和子类关系及不同类的属性和方法如下:

image

首先,我们使用如下语句表明 MasterClass 继承于 SerialClass 和 PlotClass:

class MasterClass(SerialClass,PlotClass):

接着,我们在 MasterClass 的初始化方法中分别调用不同父类的__init__方法:

SerialClass.__init__(self,port)PlotClass.__init__(self,wintitle,plottitle,width,height)

同时我们在 MasterClass 中重写父类的 DataUpdate 方法,首先发送查询数据指令,接着等待接收数据,完成数据接收后发送接收到的数据并存入数据缓存列表,在设置定时任务后,定时更新曲线。

image

如下为运行效果,我们可以看到接收到数据后正常完成曲线的更新:

image

在测试过程中,我们可以看到 Qt 窗口会有无法响应的情况出现,这是由于界面主线程是单线程,如果在 UI 主线程中执行耗时操作,例如点击按钮,响应函数去数据库查询数据,数据量比较大时,查询需要几秒钟甚至几十秒的时间,如果 UI 主线程一直等待响应函数返回,阻塞在响应函数内部,就无法响应界面的其他消息或者事件,界面就会卡死,无响应。这种情况可以利用 Python 的多线程或多进程得以避免,这个情况将在后面讲到。

可以看到,在创建包含两组完全不同行为的对象的情况下,两个类接口不同,子类完全可以正常运行,但是如果两个类的接口有重叠,同时继承就可能造成混乱。最好的方法就是避免这种情况,重新分析系统,看看是否能够去掉多重继承关系并用其他的关联或组合设计替代。

同时切记,尽量不要在子类的初始化方法中手动调用父类对象的初始化方法,会导致导致菱形继承无法被正确处理,尽量使用 Python 内置的 super() 函数,并且为 Python 类规定了标准的方法解析顺序 MRO 。使用 super() 函数初始化父类可以确保菱形继承体系中的共同超类只初始化一次。MRO 则可以确定超类之间的初始化顺序。

关于多重继承中调用同名方法时的具体情况和调用顺序可以查看如下链接:

https://pythonhowto.readthedocs.io/zh-cn/latest/object.html#id29

image

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

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

相关文章

【笔记】圆方树

【笔记】圆方树 1 定义 仙人掌: 所有边都至多被包含在一个环中。2 构建 给一个点和它所在的所在的所有点双连边,同时,我们定义方点为虚点(即表示点双的点),圆点为原图上的点。 注意,是所有点双,所以一个割点会连向多个点双。同时,由定义得,该图有且仅有圆方边,因为圆…

缓存穿透、缓存击穿、缓存雪崩的场景以及解决方法

缓存穿透、缓存击穿、缓存雪崩的场景以及解决方法都是缓存惹的祸 在项目开发中,我们的数据都是要持久化到磁盘中去,比如使用 MySQL进行持久化存储,但是呢由于流量越来越大,查询速度也逐渐变慢了起来,于是我们决定!使用缓存!然而使用缓存导致会经常面临三座大山!缓存穿透!!…

CSS Case Insensitive Attribute Selector All In One

CSS Case Insensitive Attribute Selector All In One CSS 大小写敏感的属性选择器CSS Case Insensitive Attribute Selector All In OneCSS 大小写敏感的属性选择器/* case sensitive, only matches "case_sensitive" */ [class=case_sensitive] {background: pink;…

玄机-第一章 应急响应- Linux入侵排查

玄机-第一章 应急响应- Linux入侵排查 简介 账号:root 密码:linuxruqin ssh root@IP 1.web目录存在木马,请找到木马的密码提交 2.服务器疑似存在不死马,请找到不死马的密码提交 3.不死马是通过哪个文件生成的,请提交文件名 4.黑客留下了木马文件,请找出黑客的服务器ip提交…

关于Win10 Penetration系统内置kali虚拟机WSL报错的解决方法

关于Win10 Penetration系统内置kali虚拟机WSL报错的解决方法: Win10 Penetration下载地址:https://pan.baidu.com/s/16b4_j9wuK_81G4uJKhNZyA?pwd=bj7t 提取码:bj7t Win10 Penetration是什么? Windows10 Penetration Suite Toolkit within Kali Linux是一个集成了各种渗透…

JMeter上传文件接口教程—01

Content-Type: multipart/form-data; 格式步骤: 1、添加HTTP Request、填写好HOST、URL等信息,这一步如果没有接口文档参照,可以去F12或者Fiddler抓包,我们这里重点讲没有接口文档的情况下如何做, 如果除了文件以外,还有表单数据,还是需要放在Parameters里边的。 2、上传…

如何在 Android 项目中应用 OpenCV?

如何在 Android 项目中应用 OpenCV? 流程总览导入 OpenCV 库 在 Android 项目中配置 OpenCV 创建人脸识别器 识别并裁剪人脸区域 显示裁剪后的人脸图片具体实现 导入 OpenCV 库先去 OpenCV 官网下载 OpenCV 的 Android 库并解压:https://opencv.org/releases/ 在项目中点击 f…

Android 开发学习笔记

Android 开发学习笔记 基本概念 Android 应用程序由一些零散的有联系的组件组成,通过一个工程 manifest 绑定在一起。在 manifest 中,描述了每一个组件以及组件的作用,其中有 6 个组件,它们是 Android 应用程序的基石。Android 有四大组件(也有说六大组件的,外加 Intent …

Synergy键鼠跨屏幕同步

Synergy 在多台计算机之间使用单个键盘和鼠标,使用一台计算机的键盘、鼠标或触控板来控制附近的计算机,并在它们之间无缝工作 支持Windows Mac Linux 和树莓派,解放桌面空间,减少操作复杂性,多屏操作神器没错了小记 Synergy 在多台计算机之间使用单个键盘和鼠标,使用一台…

基于粒子群优化的图像融合算法matlab仿真

1.程序功能描述基于粒子群优化的图像融合算法,通过PSO优化,得到最优的图像融合权值参数,将彩色模糊图像和清晰的灰度图像进行融合获得彩色清晰图像。2.测试软件版本以及运行结果展示 MATLAB2022a版本运行 3.核心程序for it = 1:Ites(ijj)itfor i=1:Popu% 更新速度 ptls(i)…

一起学RISC-V汇编第3讲之寄存器

寄存器是处理器中最常用的处理单元,RISC-V指令的操作数除了立即数就是寄存器。 RISC-V指令集包含了多种不同类型的寄存器,用于不同目的和功能: 对于rv32imafd架构而言,包含如下寄存器:通用寄存器:32个通用整数寄存器,分别标记为x0-x31,如果是fd扩展,还有32 个独立的浮…

第三期 Plugins Function Calling

大模型的缺陷:没有最新消息:训练周期长且昂贵,GPT3.5/4的知识截至2021-9 没有真逻辑:表现出的逻辑和推理,是训练文本的统计规律,不是真正的逻辑Plugins 订机票、数学计算、日程提醒... 插件选择&使用插件的原理 通过prompt判断是否应该调用插件失败使用门槛高:用户需…