从函数设置串行对象(从 tkinter 交互调用)

Posted

技术标签:

【中文标题】从函数设置串行对象(从 tkinter 交互调用)【英文标题】:Setting up the serial object from a function (called from tkinter interaction) 【发布时间】:2021-03-06 21:37:32 【问题描述】:

我设法让我的 tkinter 应用程序在文本字段上显示文本。

我通过硬编码 COM 端口和波特率来做到这一点,然后在我的程序开始时设置一个串行对象。

baudRate = 9600
ser = serial.Serial('COM16', baudRate)

然后我的所有代码都会运行。

但问题在于所有内容都是硬编码的。 我希望用户能够从下拉列表中选择 COM 端口。 当他选择一个端口时,串行通信应该开始了。

所以我就是这样做的。这是我的相关代码。

#hardcoded baud rate
baudRate = 9600

# this is the global variable that will hold the serial object value
ser = 0 #initial  value. will change at 'on_select()'

#this function populates the dropdown on frame1, with all the serial ports of the system
def serial_ports():    
    return serial.tools.list_ports.comports()

#when the user selects one serial port from the dropdown, this function will execute
def on_select(event=None):
    global ser
    COMPort = cb.get()
    string_separator = "-"
    COMPort = COMPort.split(string_separator, 1)[0] #remove everything after '-' character
    COMPort = COMPort[:-1] #remove last character of the string (which is a space)
    ser = serial.Serial(port = COMPort, baudrate=9600)
    return ser
    readSerial() #start reading

#this function reads the incoming data and inserts them into a text frame
def readSerial():
    ser_bytes = ser.readline()
    ser_bytes = ser_bytes.decode("utf-8")
    text.insert("end", ser_bytes)
    if vsb.get()[1]==1.0:
       text.see("end")
    root.after(100, readSerial)

当我从下拉列表中选择一个 COM 端口时会发生什么,我在设备的 LED 按钮上看到了传输。

但是,文本框架上没有显示任何内容。之前,当我对所有内容进行硬编码并在程序开始时设置串行对象,并在程序结束时声明readSerial() 函数时,一切正常。

【问题讨论】:

【参考方案1】:

在您的on_select 函数中,您在调用readSerial() 函数之前调用了returnreturn 语句终止函数的执行,它后面的任何语句都不会被执行。删除它会使程序正常工作。

另外,由于on_select 是下拉列表的回调,return 语句是多余的,因为它没有任何地方可以返回。 global ser 负责将新值全局分配给 ser 变量。

您无需在每次选择新选项时调用readSerial() 函数(否则,每次都会增加预定调用次数)。您可以拥有一个在初始配置后调用它的按钮,然后从on_select 中删除对readSerial() 的调用,或者创建一个标志以确保它只被调用一次。


根据您的评论,我创建了以下示例,您可以尝试这样的操作

from tkinter import *

def on_select(event):
    global value
    value=option.get()
    if stop_:
        start_button['state']='normal'

def start():
    global stop_
    stop_=False
    readSerial()
    start_button['state']='disabled'
    stop_button['state']='normal'

def stop():
    global stop_
    stop_=True
    root.after_cancel(after_id)
    start_button['state']='normal'
    stop_button['state']='disabled'

def readSerial():
    global after_id
    text.insert(END,value)
    after_id=root.after(100,readSerial)

root=Tk()

text=Text(root)
text.pack(side='bottom')

options=['1','2']
option=StringVar()
om=OptionMenu(root,option,*options,command=on_select)
om.pack(side='left')

start_button=Button(root,text='start',command=start,state='disabled')
start_button.pack(side='left')
stop_button=Button(root,text='stop',command=stop,state='disabled')
stop_button.pack(side='left')

stop_=True

root.mainloop()

【讨论】:

很好的解释 我正试图弄清楚。 1.完全删除return语句。关于你的第三段......我不太明白......在我的代码中,我还必须能够停止 readSerial() 函数。因为我的 arduino 程序只会在触发时发送数据。因此,当我从 tkinter 发送 STOP TRANSMIT 命令时,我还必须停止 readSerial()。哇什么是最好的方法。也许是一个通过设置串行对象来启动串行通信的按钮。另一个按钮将启动 readSerial() 并设置一个标志,另一个按钮将发送 StopTransmit 命令到 arduino,但它也必须停止 readSerial(),有可能吗? @user1584421 我已经用一个例子更新了答案,我相信你正在寻找类似的东西,如果有帮助,请告诉我。 此代码有效吗?在 readSerial() 中,当标志 (stop_) 设置为 True 时,这两行将不会执行。所以它不会打印到文本字段。但是如果 root.after(100,readSerial) 已经启动,那么你的 readSerial() 函数将不会停止它的执行。如果已经启动,您的 readSerial() 是否还应该包含主动停止定期执行的代码?我看到 _cancel 是这样做的。所以也许readSerial()会把一个flag转成True,停止按钮会检查是否设置了True,如果是,就会调用id上的_cancel函数。

以上是关于从函数设置串行对象(从 tkinter 交互调用)的主要内容,如果未能解决你的问题,请参考以下文章

在 Tkinter 文本框中显示从串行端口读取的数据时,发出处理数据

将串行从 Raspberry 传递到 Arduino USB HID

如何从 tkinter 滑块/刻度重复输出,以便我可以通过串行向另一台设备发送恒定的数字流?

Tkinter - 在类中使用Button命令从另一个类调用函数

关闭我的 tkinter 串行应用程序,给了我一个例外

Tkinter:在主循环中调用事件