Tkinter/Python与Arduino

1 投票
1 回答
1954 浏览
提问于 2025-04-17 22:54

我有一个简单的项目,目的是从我的电位器和Arduino获取串口读数。然后我把这个读数转换一下,应该能改变一个空白窗口的颜色。

但是不知为什么,我的窗口就是不更新,而我对Tkinter这个库真的不太熟悉。谢谢大家的帮助。

import Tkinter
import serial

ser = serial.Serial("/dev/ttyACM0", 9600, timeout = 2)
def update():
    while 1:
        line = ser.readline()
        color = "#%02x%02x%02x" %(000, 000, int(line))
        return color

root = Tkinter.Tk()
root.geometry("500x500")
root.configure(background = update())
root.mainloop()

1 个回答

1

我最近做了一个项目,目的是监控一个串口设备的输入。你需要在串口数据准备好时能够接收这些数据,同时还要让Tk的事件循环保持运行。你可以选择用一个工作线程来读取串口数据,然后把这些输入发送到UI线程进行显示,或者使用Twisted框架。在我的情况下,我选择了Twisted,这样可以避免从串口读取数据时出现阻塞,并且只用一个简单的线图在画布上绘制这些值。

#!/usr/bin/python
import sys
from Tkinter import *
from ttk import *
from twisted.internet import tksupport, reactor
from twisted.internet.serialport import SerialPort
from twisted.protocols import basic

class DeviceProtocol(basic.LineReceiver):
    #delimiter = "\r\n" # default delimiter is crlf
    def __init__(self, callback):
        self.callback = callback
    def lineReceived(self, data):
        self.callback.OnDataReceived(data)

class MainWindow(Frame):
    def __init__(self, parent, title):
        self.data = [(n,1) for n in range(100)]
        self.sample = len(self.data)
        Frame.__init__(self, parent)
        parent.wm_withdraw()
        parent.wm_title(title)
        self.CreateUI()
        self.grid(sticky = (N,S,W,E))
        parent.wm_protocol("WM_DELETE_WINDOW", self.onDestroy)
        parent.grid_rowconfigure(0, weight = 1)
        parent.grid_columnconfigure(0, weight = 1)
        parent.wm_deiconify()

    def CreateUI(self):
        canvas = Canvas(self, background="white")
        canvas.create_line((0, 0, 1, 1), tag='Plot', fill='red', smooth=True)
        canvas.grid(sticky = (N,S,W,E))
        self.canvas = canvas
        self.grid_rowconfigure(0, weight=1)
        self.grid_columnconfigure(0, weight=1)

    def onDestroy(self):
        reactor.stop() # required to cleanly shutdown twisted event loop

    def Monitor(self, port, baudrate = 9600):
        self.serial = SerialPort(DeviceProtocol(self),
                                 port, reactor, baudrate=baudrate)

    def OnDataReceived(self, data):
        print data
        for v in data.split():
            self.data.append((self.sample, int(v)))
        self.sample += 1
        if len(self.data) > 100:
            self.data = self.data[-100:]
        self.after_idle(self.OnUpdatePlot)

    def OnUpdatePlot(self):
        width = self.canvas.winfo_width()
        height = self.canvas.winfo_height()
        xS,yS = self.data[0]
        xE,yE = self.data[-1]
        coords = []
        fx = width / (xE - xS)
        fy = height / 100 # y values are 0 < y < 100
        for (x,y) in self.data:
            coords.append(fx * (x - xS))
            coords.append(fy * (100 - y))
        self.canvas.coords('Plot', *coords)

def main(argv = None):
    if argv is None:
        argv = sys.argv
    if len(argv) != 2:
        print "usage: plot_serial <serialport>"
        return 1
    root = Tk()
    tksupport.install(root) # register the Tk event loop with twisted
    main = MainWindow(root, "Plot serial data")
    main.Monitor(argv[1])
    reactor.run() # use twisted instead of root.mainloop()
    return 0

if __name__ == '__main__':
    sys.exit(main())

撰写回答