当前位置: 首页 > 知识库问答 >
问题:

如何在tkinter应用程序中读取串行端口数据?

仲绍晖
2023-03-14

我正在尝试制作一个Tkinter应用程序,它连接到选定的串行端口,并向文本小部件显示发送和接收的数据。我不知道如何在这样的应用程序中处理来自串行端口的数据的持续检查,因为我最初的方法会导致应用程序窗口在几秒钟后没有响应。更具体地说,在用户选择一个端口并连接到它之后,我尝试连续运行两个函数。一个用于从串行端口读取任何可用数据(receive_data()),另一个用于用任何新数据刷新文本小部件(refresh_comms())。在此问题上的任何帮助都将不胜感激,同时也欢迎对整个代码发表评论!代码如下:

import serial
import tkinter as tk
from tkinter import ttk
import time
import threading




class Application(tk.Tk):
    rel_entrywidth_y= 0.02
    baud_rate_list = [1200, 1800, 2400, 4800, 7200, 9600, 14400, 19200]
    HEIGHT = 650
    WIDTH = 750
    
    def __init__(self):
        super().__init__()
        self.title("SERIAL COMMUNICATION")
        self.default_com = 'COM13'
        self.connect_state = "Not Connected"
        self.data = []
        self.temp = []
        self.createWidgets()





    def createWidgets(self):
        self.canvas = tk.Canvas(self, width=self.WIDTH, height=self.HEIGHT, bg='#adadad')
        self.canvas.pack()
        
        self.port_label = tk.Label(self.canvas, text="Select Port:", font="Courier 12", bg = '#adadad')
        self.port_label.place(anchor='nw', relx=0.01, rely=self.rel_entrywidth_y, relwidth=0.18, relheight=0.05)

        self.port_entry = tk.Entry(self.canvas, text='COM1', font="Courier 12", bg='#A6CCF0', justify='center')
        self.port_entry.insert(0, self.default_com)
        self.port_entry.place(anchor='nw', relx=0.21, rely=self.rel_entrywidth_y, relwidth=0.18, relheight=0.05)

        self.baud_rate_var = tk.StringVar(self.canvas)
        self.baud_rate_var.set(self.baud_rate_list[5])           # set default baud rate

        self.baud_label = tk.Label(self.canvas, text="  Baud Rate:", font="Courier 12", bg = '#adadad')
        self.baud_label.place(anchor='nw', relx=0.4, rely=self.rel_entrywidth_y, relwidth=0.18, relheight=0.05)

        self.baud_menu = tk.OptionMenu(self, self.baud_rate_var, *self.baud_rate_list)
        self.baud_menu.config(font='Courier 12', bg='#A6CCF0')
        self.baud_menu.place(anchor='nw', relx=0.6, rely=self.rel_entrywidth_y, relwidth=0.18, relheight=0.05)

        self.connect_button = tk.Button(self.canvas, text='Connect', font='Courier 12 bold', command=self.connect_func)
        self.connect_button.place(anchor='nw', relx=0.815, rely=self.rel_entrywidth_y, relwidth=0.15, relheight=0.05)

        self.canvas.create_line(0, 60, self.WIDTH, 60, width=1)

        #self.canvas.create_line(0, 570, WIDTH, 570, width=1)

        #self.separator1 = ttk.Separator(self.canvas, orient='horizontal', bg='#adadad')
        #self.separator1.place(relx=0, rely=0.5, relwidth=1, relheight=0.5)


        self.comm_text = tk.Text(self, width=40, height=10, font='Courier 12', state='disabled')
        self.comm_text.place(anchor='n', relx=0.63, rely=0.1, relwidth=0.7, relheight=0.75)
        self.comm_vsb = tk.Scrollbar(self, orient='vertical', command=self.comm_text.yview)
        self.comm_vsb.place(anchor='n', relx=0.985, rely=0.101, relwidth=0.03, relheight=0.749)

        self.send_button = tk.Button(self.canvas, text='Send', font='Courier 12 bold', command=threading.Thread(target=self.send_data).start())
        self.send_button.place(anchor='n', relx=0.9, rely=0.89, relwidth=0.15, relheight=0.07)
        
        self.data_entry = tk.Entry(self.canvas, font="Courier 14", bg='#d1d1d1', justify='left')
        self.data_entry.place(anchor='n', relx=0.41, rely=0.875, relwidth=0.8, relheight=0.1)

        
        self.img = tk.PhotoImage(file='serial.png')
        self.img = self.img.subsample(9)
        self.canvas.create_image(50, 500, anchor='nw', image=self.img)




    def connect_func(self):
        if self.connect_state == "Not Connected":
            self.com_port = self.port_entry.get()
            self.baud_rate = self.baud_rate_var.get()
            self.connect_button['text'] = "Close"
            self.update_idletasks()               # making sure it changes to "Close"
            print("COM PORT IS : ", self.com_port)
            print("BAUD RATE IS : ", self.baud_rate)
            try:
                self.ser = serial.Serial(self.com_port, self.baud_rate)
                self.connect_state = "Connected"
                print("Connected to", self.com_port, "successfully!")
                self.after(500, threading.Thread(target=self.receive_data()).start())
    
            except Exception as e:
                print("Error connecting to port ", self.com_port, " ... ", e)
                self.connect_button['text'] = "Connect"
                self.connect_state = "Not Connected"
                


        elif self.connect_state == "Connected":
            self.ser.close()
            print("Closing serial connection with port", self.com_port)
            self.connect_button['text'] = "Connect"
            self.connect_state = "Not Connected"
  



    def send_data(self):
        if self.connect_state == "Connected":
            self.send_data = self.data_entry.get()
            self.ser.write(self.send_data.encode())
            print("SENDING... : " + self.send_data)
            self.data.append(self.send_data)
            self.refresh_comms(self.send_data)
        else:
            print("Connect to a serial port first...")



    def refresh_comms(self, d):
        #print("temp : ", self.temp)
        #print("data : ", d)
        if len(self.temp) < len(d):             # if data has been added
            self.comm_text.configure(state='normal')
            self.comm_text.insert('end', d[len(d)-1] + '\n')
            self.comm_text.see('end')                     # scroll text
            self.comm_text.configure(state='disabled')
            self.update_idletasks()
            self.temp.append(d[len(d)-1]) 
        
        self.update_idletasks()
        self.after(500, self.receive_data)




    def receive_data(self):
        if self.ser.in_waiting:
            received = self.ser.readline().decode()
            self.data.append(str(self.com_port) + ' : ' + received)
            print("I received :", received)
        
        self.update_idletasks()
        self.after(500, self.refresh_comms(self.data))





app = Application()
app.resizable(False, False)

app.mainloop()

共有1个答案

东方俊明
2023-03-14

为了回答我自己的问题,在对这个主题进行了一些研究之后,我使它成功了。这就成功了:

  1. 为receive_data函数创建一个线程,为更新gui创建另一个线程,在连接到串行端口后调用这两个线程

连接后在线程中调用两个函数:

self.thread1 = threading.Thread(target=self.receive_data)
self.thread1.daemon = True
self.thread1.start()
                
self.thread2 = threading.Thread(target=self.update_gui)
self.thread2.daemon = True
self.thread2.start()

接收\u数据功能和更新\u gui功能本身:

    def update_gui(self):
        while True:
            try:
                if len(self.temp) < len(self.data) and self.ser != None:             # if data has been added
                    self.comm_text.configure(state='normal')
                    self.comm_text.insert('end', self.data[len(self.data)-1] + '\n')
                    self.comm_text.see('end')                     # scroll text
                    self.comm_text.configure(state='disabled')
                    self.temp.append(self.data[len(self.data)-1]) 
                time.sleep(0.2)
            except:
                pass


    def receive_data(self):
        while True:
            if self.ser != None:
                try:
                    self.received = self.ser.readline().decode().replace('\n', '')
                    self.data.append(str(self.com_port) + ': ' + self.received)
                    print("I received :", self.received)
                except:
                    pass
            time.sleep(0.2)
 类似资料:
  • 问题内容: 我想在我的Tkinter主窗口中嵌入一个终端。我想有一个终端(基于Bash的终端)可以运行的子窗口。我还希望能够让我的程序与终端交互,至少我想阅读当前的工作目录和/或设置它。 我不知道这是否真的不可能。过去我可以使用Perl / Tk做到这一点,所以也许可以在这里复制它。 我当时使用的代码是这样的: Tk主窗口在哪里。 当然,我完全同意Bryan的观点:尽管我以前从未使用GUI库进行编

  • 问题内容: 我有一个Java程序,必须读取Arduino发送的信息。我从这里获取了Java代码。现在,我不太了解它是如何工作的,但是我尝试对其进行修改,并且得到了以下信息: 我创建一个对象串行COM口,我需要在主程序,然后我使用和当我需要它。 效果很好,Arduino获取数据并将其显示在LCD显示屏中。问题是。程序运行时,它会不断从串行端口读取数据(大约每40毫秒一次),但这并不意味着Arduin

  • 我是python和tkinter的新手。。。我使用Tkinter显示仪表并通过串行com接收信息。我已经准备好GUI,现在需要读取序列值。 我面临的问题是我不能连续读取串行COM。我遇到了,但它仍然不起作用。基本上它不会在控制台上显示任何值。知道可能出了什么问题吗? 这是主要代码。我还有一个文件meter.py

  • 问题内容: 我对读取和写入串行端口有些困惑。我在Linux中有一个使用FTDI USB串行设备转换器驱动程序的USB设备。当我插入它时,它将创建:/ dev / ttyUSB1。 我认为用C打开和读取它很简单。我知道波特率和奇偶校验信息,但是似乎没有标准吗? 我是否缺少某些东西,或者有人可以指出正确的方向? 问题答案: 您必须调用一个从获得。你不能零了,配置它,然后将用。如果使用归零方法,则会遇到

  • 我做了一个python程序,从串行端口读取gps数据。GPS冰球流NMEA数据语句连续插入USB时。我的程序打开端口,然后尝试读取数据,解析它,然后将其与从Arduino提取的其他数据一起写入文本文件。 我遇到的问题是,当我第一次运行程序时,有时它无法读取数据。我放入了一些Try/Exception捕获,发现以某种方式无法从GPS串行端口读取数据 如果我点击Cntrl-C几次,这似乎可以解决它遇到

  • 我正试图通过pySDL2将SDL2窗口嵌入到Tkinter应用程序中。如何设置pySDL2窗口、渲染器,使渲染或绘图显示在嵌入的帧中? 其他例子已经显示了pyplay,但是我发现我的pyplay版本目前不能正确地与SDL2一起工作。我知道还有其他pyplay的实现试图实现SDL2,但是与SDL2的兼容性对我来说是最重要的。 一个正确工作的例子是Tkinter窗口中的一个帧,该窗口有一个屏幕,当单击