Tkinter GUI和读取序列

时间:2015-05-25 22:36:59

标签: python tkinter

我是python和tkinter的新手......我正在使用Tkinter来显示一个仪表并通过串口com接收信息。 我准备好了GUI,现在需要读取序列值。

我面临的问题是我无法连续读取串行COM。 我遇到了self.after,但它仍然不起作用。基本上它不会在控制台上显示任何值。知道什么可能是错的吗?

这是主要代码。我有另一个具有仪表设计的文件meter.py

import tkinter as tk
import meter as m
import serial

class Mainframe(tk.Frame):

   def __init__(self,master,*args,**kwargs):
      super(Mainframe,self).__init__(master,*args,**kwargs)

      self.meter = m.Meter(self,height = 400,width = 400)
      self.meter.setrange(20,90)
      self.meter.pack()

      tk.Scale(self,width = 15 ,from_ = 20, to = 90
      ,orient = tk.HORIZONTAL
      ,command = self.setmeter).pack()

      tk.Button(self,text = 'Quit',width = 15,command = master.destroy).pack()
      tk.Button(self,text = 'Zoom',width = 15).pack()

   def setmeter(self,value):
      value = int(value)
      self.meter.set(value)   

class App(tk.Tk):

    def __init__(self):
        super(App,self).__init__()

        self.title('Try Meter')

        Mainframe(self).pack()

    def serie():
        ser = serial.Serial('COM2', 2400, timeout=1)
        line = ser.readline()   # read a '\n' terminated line
        print (line)
        self.after(100,serie) 


App().mainloop()

2 个答案:

答案 0 :(得分:1)

搞定了。这是代码

class Mainframe(tk.Frame):

    def __init__(self,master,*args,**kwargs):
        super(Mainframe,self).__init__(master,*args,**kwargs)

        self.meter = Meter(self,height = 400,width = 400)
        self.meter.setrange(20,90)
        self.meter.pack()

        tk.Scale(self, width = 15, from_ = 20, to = 90, orient = tk.HORIZONTAL, 
                 command = self.setmeter).pack()

        tk.Button(self,text = 'Quit',width = 15,command = master.destroy).pack()
        tk.Button(self,text = 'Zoom',width = 15).pack()

        self.setmeter(15)

    def setmeter(self,value):
        value = int(value)
        self.meter.set(value)

class App(tk.Tk):

    def __init__(self):
        super(App,self).__init__()
        self.title('My Meter')     
        Mainframe(self).pack() 

root = App()       

serBuffer =b"" 
aux=""

def readSerial():
    serBuffer=b""
    while True:
        c = ser.read() # attempt to read a character from Serial

        #was anything read?
        if len(c) == 0:
            #print("cero")
            break
        #else:
            #print (len(c))

        if c == b'\n' :
            #we have all the info
            aux = serBuffer.split(b" ")
            print (aux[0]," ",aux[1])

            miinstance= Mainframe()
            miinstance.setmeter(50)

            #root.update()

            serBuffer = "" # empty the buffer
        else:
            serBuffer+=c# add to the buffer

        #aux= str(serBuffer, 'utf-8')


    root.after(700, readSerial) # check serial again soon

root.after(1000, readSerial)

root.mainloop()
顺便说一句,知道我正在努力应对仪表更新。当我收到一个完整的字符串(由\ n检测到)时,我想将该值传递给窗口小部件以更新仪表。设置值的函数是setmeter,但是我的代码是

miinstance= Mainframe()
miinstance.setmeter(50) <--put this value as test

我收到此错误。第一行是收到的序列号(正确)

b'90'   b'0'
Exception in Tkinter callback
Traceback (most recent call last):
  File "C:\Program Files\Python 3.5\lib\tkinter\__init__.py", line 1535, in __call__
return self.func(*args)
  File "C:\Program Files\Python 3.5\lib\tkinter\__init__.py", line 582, in callit
    func(*args)
  File "C:/Users/MARTIN/Documents/Personal/phyton/Nuevo anemometer/read_serie_v2.py", line 258, in readSerial
    miinstance= Mainframe()
TypeError: __init__() missing 1 required positional argument: 'master'

我试着把#34; master&#34;作为一个参数,但它说&#34; master&#34;没有定义。

我拥有的代码(最初是通过网络获得的代码),在GUI中有一个比例,

tk.Scale(self,width = 15 ,from_ = 20, to = 90
  ,orient = tk.HORIZONTAL
  ,command = self.setmeter).pack()

当我移动它时,它会更新仪表,但我不需要它。仪表应该更新并收到序列信息。

知道可能出现什么问题吗?

答案 1 :(得分:0)

您的代码无法按预期工作的原因是您从不调用serie()类的App方法。如果您修改代码以添加此调用,可能在__init__()方法的末尾,您应该看到串行数据正在打印到控制台。

但是,在同一个线程中处理串行IO和用户界面并不是一个好主意。如果没有任何数据要从串口读取,readline()将阻塞最多1秒(初始化串口时设置的超时值)。这将阻止tk主循环,您的用户界面将完全无响应。在我看来,解决这个问题的最好方法是使用其中一个异步IO库。我喜欢Twisted,因为它内置了对tk(example)和串行通信的支持,但还有其他几个选项。如果这感觉有点过分,您还可以分别使用threadingmultiprocessing模块将串行轮询代码移动到自己的线程或进程。

更新

回答问题的第二部分:

readSerial()中,您创建了Mainframe()的新实例,但我认为您实际要做的是使用您在__init__()方法中创建的实例App }类。您需要使用self.main_frame = Mainframe(self)之类的代码将其存储为属性,然后在readSerial()中,您可以使用root.main_frame.setmeter(50)访问它。