# !/usr/bin/python 3.6.5
# coding=utf-8
import serial
import serial.tools.list_ports
import threading
'''
1、掃描串口列表
2、打開串口
3、向串口寫數據
4、從串口讀數據
5、關閉串口
init 初始化串口
--> 創建線程:
1. 如果串口未打開,則用於實時掃描串口,檢測是否有新設備插入
有新設備插入:添加到設備列表,並刷新UI的設備列表提供選擇
沒有新設備:繼續掃描
2. 如果串口已打開,則用於監測串口是否有接收到數據
如果有則讀取數據,併發送給UI
如果沒有則繼續掃描
參數:主窗口,端口列表,接收窗口
返回:線程實例(用於關閉窗口前,先關閉線程)
open 打開串口
--> 參數:端口號,波特率,超時時間
返回:串口實例(打開成功)/ None(打開失敗)
close 關閉串口
--> 參數:串口實例
返回:0(關閉成功)/ -1(關閉失敗)
write 寫數據
read 讀數據
'''
SERIAL_IS_OPEN = False # 默認串口關閉
port_name_list = [] # 端口名稱列表
port_com_list = [] # 端口號列表
MySerial = None # 打開的串口
def 掃描串口():
port_list = list(serial.tools.list_ports.comports())
if len(port_list) > 0:
return port_list
else:
return None
def 打開串口(port="COM4", bps=115200, timex=5):
try:
# 打開串口
ser = serial.Serial(port, bps, timeout=timex)
if ser.is_open:
global SERIAL_IS_OPEN
SERIAL_IS_OPEN = True
print("--- 串口打開 ---")
return ser
except Exception as e:
print("--- 打開異常 ---: ", e)
return None
def 發送數據(ser, text, code="utf-8"):
try:
result = ser.write(text.encode(code))
if result == len(text):
print("--- 發送成功 ---:", text)
return result
else:
print("--- 發送錯誤 ---:", "data len:", len(text), "send len:", result)
return None
except Exception as e:
print("--- 發送異常 ---:", e)
def 讀取數據(ser, code="utf-8"):
if ser.in_waiting:
str = ser.read(ser.in_waiting).decode(code)
print("--- 讀到數據 ---:", str)
return str
else:
return None
def 關閉串口(ser):
if ser.is_open:
try:
global SERIAL_IS_OPEN
SERIAL_IS_OPEN = False
ser.close()
print("--- 串口關閉 ---")
return 0
except Exception as e:
print("--- 關閉異常 ---:", e)
return -1
else:
print("--- 錯誤 ---:串口未打開!")
return -1
class SERIAL:
def __init__(self):
self.ser = None
self.get_str = ''
self.master = None
self.show_com = None
self.read_text_win = None
self.serialThread = None
def serial_thread(self, master, list_port, text_read):
global SERIAL_IS_OPEN, port_name_list, port_com_list
global MySerial
while True:
if SERIAL_IS_OPEN:
self.get_str = 讀取數據(MySerial)
if self.get_str:
# print(self.get_str)
text_read.insert(tk.END, self.get_str)
master.update()
else:
port_list = 掃描串口()
if len(port_name_list) is not len(port_list): # 只判斷列表長度,不可靠。需修改爲列表內容判斷比較可靠
port_name_list.clear()
port_com_list.clear()
for i in range(0, len(port_list)):
port_name_list.append(port_list[i].description)
port_com_list.append(port_list[i].device)
list_port["values"] = port_name_list
if list_port.get() is "": # 如果當前UI中的端口列表爲空,則指定顯示第一個
list_port.current(0)
master.update()
@classmethod
def init(cls, master, list_port, text_read):
cls.master = master
cls.show_com = list_port
cls.read_text_win = text_read
cls.serialThread = threading.Thread(target=cls.serial_thread,
args=(SERIAL, cls.master, cls.show_com, cls.read_text_win))
cls.serialThread.start()
return cls.serialThread
@classmethod
def open(cls, port=None, bps=115200, timex=5):
global port_name_list, port_com_list, MySerial
if not port:
port_name = cls.show_com.get()
index = port_name_list.index(port_name)
MySerial = 打開串口(port_com_list[index], bps, timex)
else:
MySerial = 打開串口(port, bps, timex)
@staticmethod
def write(text, coding="gbk"):
global MySerial, SERIAL_IS_OPEN
if SERIAL_IS_OPEN:
發送數據(MySerial, text, coding)
@staticmethod
def read(coding="gbk"):
global MySerial, SERIAL_IS_OPEN
str = None
if SERIAL_IS_OPEN:
str = 讀取數據(MySerial, coding)
return str
@staticmethod
def close():
global MySerial, SERIAL_IS_OPEN
if SERIAL_IS_OPEN and MySerial:
關閉串口(MySerial)
if __name__ == "__main__":
import tkinter as tk
from tkinter import ttk
root = tk.Tk()
root.title("測試窗口")
root.geometry("300x300")
list_box = ttk.Combobox(root, width=22, textvariable=port_name_list, state="readonly")
list_box.place(x=10, y=10)
text_box = tk.Text(root, width=25, heigh=10)
text_box.place(x=10, y=50)
def open_serial():
SERIAL.open()
def send_date():
SERIAL.write("hello python serial\n", coding="gbk")
def close_serial():
SERIAL.close()
SERIAL.init(root, list_box, text_box)
button0 = tk.Button(root, text="打開串口", command=open_serial)
button0.place(x=10, y=200)
button1 = tk.Button(root, text="發送數據", command=send_date)
button1.place(x=70, y=200)
button2 = tk.Button(root, text="關閉串口", command=close_serial)
button2.place(x=130, y=200)
root.mainloop()