用Visual Basic或者C#, LabVIEW等软件开发的上位机界面,和文本式交互界面比较而言,就是可以用图形方式来显示数据。那么python可不可以用图形来显示数据呢?Of course! 和串口通讯,保存文件一样,绘制曲线对于python而言也是易如反掌。接下来,就让大伙儿看看我的神操作。

绘制曲线,我们可以使用python自带的Turtle(海龟画图)模块。绘图的基本步骤如下:

  1. 导入Turtle模块
  2. 设定画笔的宽度,速度,颜色等
  3. 抬起画笔,移动画笔到起点,放下画笔,移动画笔到终点。
  4. 重复2、3步骤,直到图形画完。

Turtle的常用的绘图函数有:
turtle.home() - 将位置和方向恢复到初始状态,位置初始坐标为(0,0),方向初始为("standard"模式为right向右即东,"logo"模式是up向上即北)
turtle.dot(size, *color) - 按给定直径size画圆点, color圆点颜色
turtle.speed(speed) – 设置画笔的速度,speed为0-10的整数(1-10越来越快,但是0表示最快)
turtle.pencolor(*args) - 设置或返回画笔颜色(设置后海龟箭头的外轮廓也是这个颜色)
turtle.pensize(width) – 设置画笔宽度
turtle.pendown() - 画笔落下,海龟移动时将画线
turtle.penup() - 画笔抬起,海龟移动时不画线
turtle.goto(x,y) - 前往一个绝对坐标
turtle.left(angle) - 画笔方向向左转,angle为左转的角度
turtle.right(angle) - 画笔方向向右转,angle为右转的角度
turtle.write(arg, move=False, align=“left”, font=(“Arial”, 8, “normal”)) - 绘制文字,arg为要绘制的纹波,align为对齐方式,font为字体
turtle.showturtle() - 显示画笔(即海龟箭头)
turtle.hideturtle() - 隐藏画笔(即海龟箭头)
turtle.clear() - 清屏并且画笔形状也恢复默认,保持画笔位置和方向不变

这些函数的命名真是简单直观而易懂。和我们用笔画图的动作也非常相似。相信大家几分钟之内就能掌握,并且画出自己的第一个图形。
当python从串口中读取到数据存储到列表data中以后,我们就可以把data中的数据按照时间顺序,把这些数据用图形显示出来:

  1. 先绘制x轴和y轴,x轴表示的是时间,y轴表示收到的数据的值的大小。
turtle.speed(0)      #画笔速度,10为最慢
turtle.goto(200,0)   #画时间轴
turtle.write("时间")  #标注为时间轴
turtle.penup()       #抬起画笔
turtle.goto(0,200)   #到y轴的终点
turtle.write("数据")  #标注为数据轴
turtle.pendown()     #画笔落下
turtle.home()        #绘制y轴,同时画笔回到原点,
  1. 选择画笔颜色
turtle.pencolor("blue")  #重新选择画笔颜色为蓝色
  1. 计算每个数据点的x坐标和y坐标
for i in range(len(data)):  #把所有的数据循环一遍
    x = i*20           #设置时间值,比如20ms一个点
    y = int(data[i])   #获取y的值
  1. 绘制每一个点
turtle.goto(x,y)   #画笔移动到x,y坐标
turtle.dot(5,"red")#在该点绘制一个红色圆点
  1. 抬起画笔,回到原点,绘图结束
turtle.penup()  #画笔抬起
turtle.home()   #画笔回到原点,画线结束

下图是整个程序的源代码:

import serial  #导入串口模块
import time    #导入时间模块,时间模块是Python自带模块,无需安装
import csv     #导入csv模块,csv模块是Python自带模块,无需安装
import turtle  #导入Turtle画图模块,此模块为Python自带,无需安装
try:
    SCI1 = serial.Serial("com15",9600,timeout=50)  #打开串口,并且获得串口对象
    #判断是否打开成功
    if (SCI1.isOpen()==True):
        print("串口已经打开!")
except Exception as exc:
    print("串口打开异常:",exc)
while True:
    commandFromECU = input("请输入控制命令,c-向ECU发送命令;r-从ECU中读取数据。")  #从键盘上输入一个命令
    SCI1.write(str(commandFromECU).encode("utf-8"))  #将键盘输入的控制命令发送给ECU上的单片机
    if commandFromECU == 'r':          #如果是读取数据,则从串口中接收数据。
        data = []                      #将data列表清空,准备存放新的数据
        time.sleep(1)                  #等待1秒,等待接收ECU上单片机返回的数据. ECU会依次发送0x30-0x39等10个数据
        bufferSize = SCI1.in_waiting   #接收ECU上单片机发过来的数据,并且返回数据的大小
        print("接收到",str(bufferSize),'个数据') #打印缓冲区中接收到数据的个数 
        for i in range(bufferSize):
            data.append(SCI1.read(1).hex())     #将接收缓冲区中数据读取到data中
        print(data)
        #将数据保存到csv文件中
        with open ('C:\\Users\\tti\\Desktop\\python\\test.csv', 'a+',newline='') as file1: #打开文件,在文件末尾,追加数据
            writer = csv.writer(file1) #创建一个实例
            writer.writerow(data)      #给csv文件写入一行数据
        #把接收到的数据打印成曲线输出
        turtle.speed(0)      #画笔速度,10为最慢
        turtle.goto(200,0)   #画时间轴
        turtle.write("时间")  #标注为时间轴
        turtle.penup()       #抬起画笔
        turtle.goto(0,200)   #到y轴的终点
        turtle.write("数据")  #标注为数据轴
        turtle.pendown()     #画笔落下
        turtle.home()        #绘制y轴,同时画笔回到原点,
        turtle.pencolor("blue")  #重新选择画笔颜色为蓝色
        for i in range(len(data)):  #把所有的数据循环一遍
            x = i*20           #设置时间值,比如20ms一个点
            y = int(data[i])   #获取y的值 
            turtle.goto(x,y)   #画笔移动到x,y坐标
            turtle.dot(5,"red")#在该点绘制一个红色圆点
        turtle.penup()  #画笔抬起
        turtle.home()   #画笔回到原点,画线结束
    time.sleep(1)                      #等待1秒
    if(input("如果退出,请输入n,如果继续发送命令,请按其他键 ") == 'n'):
        break
SCI1.close()    #关闭端口