PyQt5 "PyTuning"调试软件从0开发总结

北航3系大四要调小车在赛道上跑,小车单片机用的K60,老师提供的代码里还有串口收发的库,就想着用蓝牙模块再开发上位机调试软件进行远程调试,正好借此机会学习了一番PyQt。现在从头总结一下开发流程~想从0开发的可以参考。

代码功能:显示直观的调试界面,分两个线程,主线程负责处理用户事件以及显示,子线程处理串口通讯并向主线程上报数据。

放一下效果图~~
文末有全部代码链接

开发流程

  • PyQt5 "PyTuning"调试软件从0开发总结
  • 一、图形界面搭建(躯壳)
    • 1,designer的安装与打开
    • 2,designer使用
    • 3,ui文件转py文件
  • 二、功能实现(灵魂)
    • 1、继承ui转出的py躯壳
    • 2、按键回调函数
    • 3、内容显示与读取
      • (1) 文字读写
      • (2) 图片显示
      • (3) 几何绘图
    • 4、信号槽与多线程
      • (1) 信号槽理解与实例
      • (2) 多线程创建
  • 三、结语
    • 项目源码:

一、图形界面搭建(躯壳)

1,designer的安装与打开

这里博主用了designer进行图形界面开发,可以手拖控件,比纯手编直观多了,强烈推荐快速开发Qt简单图形界面的用这种方法。designer安装及打开方法:

pip install PyQt5
pip install pyqt-tools

如果用的是Anaconda,在Anaconda Prompt上运行上面两行后,找到anaconda3的安装目录并在下面找anaconda3 > Library > bin > designer,建议发送一个快捷方式到桌面上,之后更容易打开。

2,designer使用

下面简单说明一下designer的使用,由于设计的已经非常亲民了,就简略说下。基本你想干嘛,第一反应的操作就能实现。

① Widget Box
里面有各种各样的控件可以用,需要啥就把他往最中间要的位置拖就行了。
一些对控件选择的小建议:
1,大的框框用Group Box或 Frame. Group Box能方便的写小标题,Frame能显示明显的框框,虽然这些用最基础的Widget也能实现,但那两个更亲民直观好用。
2,文本提示用Label,好用
3,文本输入用Text Edit或Plain Text Edit,好用
4,大量文本输出用Text Browser,够大,能滚动(虽然其他也能)
5,!!!!想显示图片或视频,可以用Label!!!!很方便
6,尽量不要把所有东西都没有组织地堆到主界面上,不然后期调起来很麻烦,建议多创建几个框框,把同一个功能用到的控件放到这个框框里,再把框框在主界面上拖。

② Main Window
这里是你的创作画布,一个字:拖!!!
(除了拖还可以双击输入内容,反正操作非常直观)

③ 对象查看器
这里你可以看你所有控件的结构,跟文件浏览器似的,双击可以改对象名。
你并不想你的C盘所有文件全铺在表面,所以再次建议搭一个比较合理的结构,方便之后写代码。

④ 属性编辑器
这里列了好多控件常用的属性,点一个控件就会显示他的属性,从上到下依次是该控件对象的亲戚关系,越往下越是子对象。
在这里细调控件的位置很方便!!!

3,ui文件转py文件

designer保存的文件后缀.ui实际上是XML文件,想转成.py文件开始各种功能的编写很容易:在Anaconda Prompt里执行:

pyuic5 -o 文件路径\文件名.py 文件路径\文件名.ui

文件路径和文件名写自己的,.py文件就出来了,整个软件的图形界面骨架就能用了!!
建议把这段代码存记事本里!因为你肯定会再在designer里调整界面的hhhh,每次都得再执行一遍

二、功能实现(灵魂)

下面是PyQt界面开发的重头戏,上面designer造出来的只是个空壳子,现在需要注入灵魂让他动起来了。

1、继承ui转出的py躯壳

这步很重要!!不要直接在刚刚捏出来的py文件上编!!(他可能有几百行看起来很相似的代码,直接搞得你不想动他~)
以下博主就用自己的文件名做例子了。躯壳文件叫Racing_Tool.py,在同目录下创建新的文件,输入以下代码,跑,你会发现你已经继承了那具躯壳。(import了好多东西是我整个程序用的,先列在这里)

import Racing_Tool
from Vision import Vision, WeightsTuner, RecordDot
from PyQt5.QtWidgets import QApplication, QMainWindow
from PyQt5.QtCore import QCoreApplication, QThread, pyqtSignal
from PyQt5.QtGui import QImage, QPixmapimport sys
import serial
import re
import cv2
import numpy as np
from time import perf_counterracing_tool = Racing_Tool.Ui_Main_Windowclass RacingMain(QMainWindow, racing_tool):def __init__(self):QMainWindow.__init__(self)racing_tool.__init__(self)self.setupUi(self)if __name__ == "__main__":app = QApplication(sys.argv)window = RacingMain()window.show()sys.exit(app.exec_())

2、按键回调函数

本程序博主只用了按键回调函数,还有按下enter的回调函数等,逻辑类似。

def __init__(self):QMainWindow.__init__(self)racing_tool.__init__(self)self.setupUi(self)self.button_functioning()def button_functioning(self):self.send_steer.clicked.connect(self.on_send_steer)self.send_motor.clicked.connect(self.on_send_motor)self.send_track.clicked.connect(self.on_send_track)self.open_UART.clicked.connect(self.on_open_uart)self.data_query.clicked.connect(self.on_data_query)self.debug_clear.clicked.connect(self.on_clear_debug)self.quit_button.clicked.connect(QCoreApplication.instance().quit)self.weights_tuner_button.clicked.connect(self.on_set_weights)self.record_button.clicked.connect(self.on_record_tracks)

这样各种按键按下后就都会执行后面的对应的函数了。self.onXXX是函数名,需要之后自己编写具体功能。

3、内容显示与读取

(1) 文字读写

Q是各种文本控件的名字
写:
设置字符串:Q.setPlainText(“string”)
追加字符串:Q.insertPlainText(“string”)
读:
读取字符串:buffer = Q.toPlainText()

(2) 图片显示

以QLabel 对象为例:

// 图片显示
self.img = np.zeros((self.CAMERA_H, self.CAMERA_W, 3), np.uint8) //你的图片,注意是np.uint8!!!!不是float请用0~255不要0.0~1.0
resized_img = cv2.resize(self.img, None, fx=8, fy=8, interpolation=cv2.INTER_CUBIC) //更改图片大小
show_image = QImage(resized_img, resized_img.shape[1], resized_img.shape[0],resized_img.shape[1] * 3,QImage.Format_RGB888)
self.MyQLabel.drawPixmap(QRect(0, 0, 640, 480), QPixmap(show_image))

博主这里用的是uint8表示的像素的RGB值,float形式的会出问题,还没研究出来咋弄。

(3) 几何绘图

核心对象:QPainter
这里博主参考了一堆零零散散的文章才明白各个语句的逻辑关系,在这里统一总结细讲一下从0实现绘图,方便大家理解使用,以显示小红点为例,先上基础代码:
先建一个新的类,继承QLabel:

from PyQt5.QtWidgets import QLabel
from PyQt5.QtCore import Qt, QRect, QPoint
from PyQt5.QtGui import QPainter, QColor, QPen, QBrush
import pyqtgraph as pg
import numpy as npclass RecordDot(QLabel):  //继承QLabeldef __init__(self, parent_widget):        //创建实例的时候,给出父对象//功能就相当于designer里把这个控件拖到另一个控件上super(RecordDot, self).__init__(parent_widget)  //初始化父对象self.name = "RecordDot"      //给自己起个名字self.left = 300       //以下四行为定义自己的位置大小,是不是很像designer里的self.top = 0        //其实你可以先在designer里拖个QLabel出来看效果,记住几何信息self.width = 40self.height = 40self.show = False    //非必要,博主自己的小功能标志位self.initUI()   //调用UI初始化,其实就是下面这个函数def initUI(self):    //下面这些你会发现,designer转出来的py文件全是这个,照搬就行self.setGeometry(QRect(self.left, self.top, self.width, self.height))self.setText("")self.setObjectName(self.name)def paintEvent(self, qp): //接下来的参考下文解释if not self.show:returnqp = QPainter()qp.begin(self)brush = QBrush(Qt.red, Qt.SolidPattern)qp.setBrush(brush)qp.drawEllipse(QPoint(20, 20), 10, 10)qp.end()

接下来着重讲解一下这个paintEvent函数。首先定义的时候多给了一个qp参数,这个不用太纠结,名字随意,给就行了,不给会有小报错。
插播一个许多新手很关心的问题:paintEvent()是在外面对象实例化之后(记实例为Q),执行Q.update()系统自动调用一次的!!!!每次update画一次

if not self.show:return

paintEvent函数会在对象实例化的时候被系统自动执行一次,所以为了不让他瞎画,直接先让他return。当然这句话广义的功能是,如果我没选择让他显示,在调用paintEvent后,会清空这个对象上画的所有内容(每次调这个函数会清空之前内容)。

// An highlighted block
qp = QPainter()
qp.begin(self)
//具体绘图的代码
qp.end()

这三行首先创建了QPainter对象,再指定了开始与结束绘图的位置。必要!!

具体绘图方法:

① 选择自己想要的画笔

brush = QBrush(Qt.red, Qt.SolidPattern)
qp.setBrush(brush)

如果想画实心图形,用QBrush,如果想画线条或几何轮廓,用QPen。QBrush创建的时候建议用a = QBrush(QColor, style)方法。注意!!!!不要把画笔颜色RGB直接以列表或元胞形式贴到第一个参数上,要

brush = QBrush(QColor(color[0], color[1], color[2]), Qt.SolidPattern)

然后告诉QPainter使用这个画笔(qp.setBrush(brush) / qp.setPen(pen))
如果只设置了QBrush则轮廓(QPen)默认为黑色,宽度一像素

②选择自己想要绘制的图形

// 推荐绘图代码示例
qp.drawEllipse(QPoint(x, y), rx, ry)            //(椭)圆
qp.drawRect(QRect(left, top, width, height))    //矩形
qp.drawLine(QPoint(start_x, start_y), QPoint(end_x, end_y))     //直线段
qp.drawPixmap(QRect(left, top, width, height), QPixmap(image))      //图片显示

③绘图代码放到正确的地方

把所有要画的东西,包括图片显示!!!!!!!重点强调,都依次放进qp.begin(self)和qp.end()之间,这样每次这个对象被update,就会显示想要的东西了。

④主程序里调用绘图

class RacingMain(QMainWindow, racing_tool):def __init__(self):QMainWindow.__init__(self)racing_tool.__init__(self)self.start_recorded = False       //博主自己的标志位,非必要self.setupUi(self)self.button_functioning()self.RecordDot = RecordDot(self.record_panel)  //对象实例化,self.record_panel是他所在的框框def button_functioning(self):      self.record_button.clicked.connect(self.on_record_tracks)def on_record_tracks(self):if not self.start_recorded:self.record_button.setText("结束录制")self.RecordDot.show = True        //设为需要绘图self.RecordDot.update()         //这一行调用了paintEventself.start_recorded = Trueelse:self.record_button.setText("开始录制")self.RecordDot.show = False      //设为不用绘图self.RecordDot.update()         //调用paintEvent直接return清空绘图self.start_recorded = False

录像小红点就有了!!

⑤ 色图设置(实用功能,非必要)

博主为了直观显示某些量的大小,想采用类似Matlab里colormap的方法,这里提供一下核心代码和实例实现代码:

核心代码及理解:

self.colormap = pg.ColorMap([0, 0.5, 1], [[255, 0, 0], [200, 200, 0], [0, 255, 0]])
self.lookup_table = self.colormap.getLookupTable()
color = self.lookup_table[index]

可以这么想象一个色图:

第一句:

self.colormap = pg.ColorMap([0, 0.5, 1], [[255, 0, 0], [200, 200, 0], [0, 255, 0]])

作用就是生成这个图,先提供一组要插值得点横坐标[0, 0.5, 1],再在一个列表里列出对应的RGB值[[255, 0, 0], [200, 200, 0], [0, 255, 0]],pyqtgraph库的ColorMap方法就能插出来上面那样子的图。

第二句:

self.lookup_table = self.colormap.getLookupTable()

作用就是把图离散化,给每一个离散出来的颜色配一个索引值(默认0 ~ 511),类似这样:

第三句:

color = self.lookup_table[int(511 * self.weights[i])]

作用就是从离散色图取颜色,和取列表元素是一个道理,其实就是个列表。
self.lookup_table[index]就能取色,注意index是0~511的int。总之理解成列表就行了。

实例代码及效果:

from PyQt5.QtWidgets import QLabel
from PyQt5.QtCore import Qt, QRect, QPoint
from PyQt5.QtGui import QPainter, QColor, QPen, QBrush
import pyqtgraph as pg
import numpy as np
from numpy import signclass WeightsTuner(QLabel):def __init__(self, parent_widget):super(WeightsTuner, self).__init__(parent_widget)self.name = "WeightsTuner"self.left = 0self.top = 0self.width = 100self.height = 480self.weights = []self.colormap = pg.ColorMap([0, 0.5, 1], [[255, 0, 0], [200, 200, 0], [0, 255, 0]])self.lookup_table = self.colormap.getLookupTable()self.initUI()def initUI(self):self.setGeometry(QRect(self.left, self.top, self.width, self.height))self.setText("")self.setObjectName(self.name)def set_weight_values(self, coef):self.weights = []for i in range(60):self.weights.append(np.float_power(i / 60, coef))def paintEvent(self, qp):if len(self.weights) == 0:returnqp = QPainter()qp.begin(self)for i in range(60):color = self.lookup_table[int(511 * self.weights[i])]brush = QBrush(QColor(color[0], color[1], color[2]), Qt.SolidPattern)qp.setBrush(brush)width = int(100 * self.weights[i])qp.drawRect(QRect(0, i * 8, width, 8))qp.end()

实现效果:

其实色图实现还有好多参数可以设置,可以更灵活,博主只是从新手的角度出发,提取了最省心实用的使用方法。

4、信号槽与多线程

这一部分主要讲怎么利用PyQt5的信号槽机制为多线程提供信息交互的方法,并利用QThread创建多线程。

(1) 信号槽理解与实例

信号槽是PyQt里传递信息的一个比较稳定实用的方式,逻辑清晰。需要调用pyqtsignal库。信号响应直观理解:

具体实例如下:

①创建信号对象

class RacingMain(QMainWindow, racing_tool):# signal transmittingCMD_data_query = pyqtSignal(str)def __init__(self):...

建议在class底下直接创建,不要用self。
pyqtSignal()里面的内容表达要传递的信号类型,如果信号类型比较复杂,可以拿type()看一下,或者直接pyqtSignal(object)

②绑定回调函数

def transmission_functioning(self):self.CMD_data_query.connect(self.serial_thread.handle_send_request)

博主把所有绑定放进一个函数里执行了,在各个对象创建完毕后,记得调用这个绑定初始化函数。
有没有觉得这个connect和按键回调函数里的差不多?没错,本质是一样的。
connect()里你可以接任意函数名,当信号发射之后就会触发这个函数,并把之前pyqtSignal(object)里的具体的object当做参数传递给这个回调函数。

③信号发送

self.CMD_send_steer.emit(args)

emit()里的内容类型要对应上之前的pyqtSignal(object)里的类型。

④回调函数编写

这部分就和PyQt关系不大了hhh,自由发挥就好~

(2) 多线程创建

PyQt多线程很方便,继承QThread类自己创建个类就好:

class SerialThread(QThread):transmit_img = pyqtSignal(object)transmit_tracks = pyqtSignal(object)transmit_data = pyqtSignal(str)handle_acknowledge = pyqtSignal()serial_bug_report = pyqtSignal(str)state_report = pyqtSignal(bool)def __init__(self, port, baud):super().__init__()def __del__(self):self.wait()def run(self)://你要在子线程里执行的代码

博主这里把我在子线程里用的一些信号槽列了一下,对多线程创建本身没啥用,就想推荐一下用这种方法和主线程沟通hhhh.

下面三个函数前两个好理解,第三个run(self):
这个函数是在多线程实例被创建出来的时候系统自动开始跑的!!所以不用再纠结怎么让子线程工作了,创建出来他就开始跑run里的代码了!

三、结语

至此,博主用的所有与PyQt5有关的操作就整理完了!再加上各种功能的实现,最后已经是个比较酷炫实用的调试界面了。

所有的源码,包括designer的ui文件我放在下面链接里了,如需自取~

项目源码:

Github: https://github.com/Apokli/PyTuning

百度网盘:https://pan.baidu.com/s/13e4BoRPIoGG53FnI2R_CPg
密码:8u5c

PyQt5 “PyTuning“调试软件从0开发总结相关推荐

  1. 【博客8】缤果PyQt5串口调试助手V1.1(初级篇)

    超级好用的Python QT GUI串口调试助手 目录 前言 一.软件概要: 二.软件界面: 1.App动态演示 2.其他扩展展示 三.main.py源码: 1.PyQt5_Serial_Debug_ ...

  2. 地平线黄畅:软件2.0时代,数据驱动进化,算力将成为智能化的基石丨MEET2021...

    编辑部 整理自 MEET 2021 量子位 报道 | 公众号 QbitAI 当下,AI芯片将迎来什么样的挑战? 随着软件步入"2.0时代",数据开始驱动AI进化,算力也逐渐成为智能 ...

  3. 更新-LabVIEW固高函数库源码文档调试软件-2019年4月14日

    函数名:dir.mnu      dir.mnu     GT 2D Compare Clear Data.vi     GT 2D Compare Clear.vi     GT 2D Compar ...

  4. LabWindows CVI 2017开发笔记--串口调试软件实例

    一.新建工程 打开LabWindows CVI软件,在桌面新建SerialDebug文件夹用来保存工程文件,在欢迎页点击New–>Project 或者在软件首页点击File–>New–&g ...

  5. 基于Qt Designer和PyQt5的桌面软件开发--环境搭建和入门例子

      本文介绍了如何使用技术栈PyCharm+Qt Designer+PyQt5来开发桌面软件,从环境搭建.例子演示到对容易混淆概念的解释.文中用到的全部软件+代码下载链接为:https://url39 ...

  6. From 《visual C++ 6.0开发工具与调试》

    From <visual C++ 6.0开发工具与调试> 1.          如何快速地规范代码缩进格式 选中所需要规范的代码,按shift+F8 2.          如何在Rel ...

  7. qt5.9.0调试如何查看变量的值_从0开发3D引擎(四):搭建测试环境

    大家好,本文介绍了3D引擎的测试方法,搭建了本地的测试环境. 上一篇博文 wonder-yyc:从0开发3D引擎(三):搭建开发环境​zhuanlan.zhihu.com 下一篇博文 wonder-y ...

  8. 用终端访问路由器设置端口开发_serial for mac(终端管理软件)v2.0.3

    原标题:serial for mac(终端管理软件)v2.0.3 serial for mac是应用在Mac上的一款终端管理软件,可以帮助您连接和控制串行设备,如服务器,路由器或调制解调器等网络设备, ...

  9. 用opencv和vc++6.0开发的五子棋游戏软件

    用opencv和vc++6.0开发的五子棋软件 需要的工具如下 1)opencv1.0 2)vc++ 6.0 // wuziqi.cpp : Defines the entry point for t ...

最新文章

  1. c语言重定义不同的基类型_简述usb连接器输出类型定义和不同环境下的用法
  2. 开启LeetCode之路
  3. Tomcat10 开机启动 Linux环境
  4. 用栈实现计算器c语言报告,请问,用c语言做一个计算器 包括+-*/()的运算 用栈 该怎么做...
  5. mysql 8.0 重置数据库,Mysql 8.0安装及重置密码问题
  6. 365锦鲤助手 砍价小程序源码 流量主引流裂变
  7. [IE编程] IE的Killbit 技术详解
  8. SpringMVC @ModelAttribute注解
  9. 获得OnOK退出控制
  10. 3D模型欣赏:美杜莎女妖 角色设计完整 造型独特
  11. 细化(thinning)
  12. FATAL: Atom .R<CYM 383>.A<H 11> does not have a type.
  13. 荐读|自己的事情自己做,不要总给别人添麻烦!受益匪浅
  14. Java学习笔记(五):一张图总结完JVM8基础概念
  15. 【TensorFlow】官方例子mnist_with_summaries.py在windows下运行tensorboard
  16. linux E667 同步失败
  17. 【VUE】二维码解析
  18. 广告拦截—Adblocks Plus (F*cking shit Ads!
  19. 速卖通商标授权怎么弄?速卖通官方授权模板书分享
  20. serverlet 原理_容器原理架构详解(全)

热门文章

  1. linux strace 命令
  2. CiscoAIR-AP1832I-H-K9最全刷机步骤和WEB页面及控制器模式CLI配置。
  3. Oracle 存储过程,Hibernate 调用存储过程,JDBC调用存储过程,Oracle 动态SQL
  4. zsh: command not found: adb问题分析
  5. python小工具,15行代码秒出工资条
  6. 宿舍小助手NABCD
  7. Special Adjustment Method
  8. 带你读AI论文丨ACGAN-动漫头像生成
  9. 6个不为人知的黑科技资源网站,绝对让你大开眼见!
  10. 「Ac.000」 如约而至的送书福利