首页 > 解决方案 > 几秒钟后程序出现故障(tkinter)

问题描述

我正在编写一个程序,它应该显示一些通过 CanBus 的数据(数据每 200 毫秒刷新一次),但是当我像这样从我的类中创建一个对象时:

batteryDegree = PlaceInfo(root,img='battery.png',fotox=0.23,fotoy=0.35,foto_y_resize=180,foto_x_resize=180,anchor='center', text="100*C",textx=0.23,texty=0.38,canid=0x1000400,byte=2,size=30)
motorDegree = PlaceInfo(root,img='motor.png',fotox=0.75,fotoy=0.35,foto_y_resize=200,foto_x_resize=200,anchor='center', text="100*C",textx=0.76,texty=0.37,canid=0x1000400,byte=0,size=30)

这是我的课:

bus = can.interface.Bus(channel='can0')
root = Tk()
root.geometry("800x480")
root.config(bg='red')
canvas = Canvas(root,width=800,height=480,highlightthickness=0)
canvas.pack() 
class PlaceInfo():
    def __init__(self,root, **kwargs):     
        self.root = root  
        if 'img' in kwargs:
            if 'foto_x_resize' in kwargs and 'foto_y_resize' in kwargs:
                self.png = ImageTk.PhotoImage(Image.open('imgs/' + kwargs['img']).resize((kwargs['foto_x_resize'],kwargs['foto_y_resize']),Image.ANTIALIAS))
            else:
                self.png = ImageTk.PhotoImage(Image.open('imgs/' + kwargs['img']))
            self.image = canvas.create_image(800 * kwargs['fotox'],480 * kwargs['fotoy'] ,image=self.png,anchor='center') 

        
        if 'text' in kwargs:
            self.text = canvas.create_text(kwargs['textx'] * 800, 
                                            kwargs['texty'] * 480,
                                            text=kwargs['text'],
                                            fil=kwargs['kleur'] if 'kleur' in kwargs else 'white',
                                            font=('arial',kwargs['size'] if 'size' in kwargs else 10))
                                            
        if 'canid' in kwargs and 'index' not in kwargs:
            self.changeData(kwargs['canid'], kwargs['byte'],kwargs['textx'], kwargs['texty'],kwargs['size'])

        elif 'canid' in kwargs and 'index' in kwargs:
            self.changeDataWithIndex(kwargs['canid'], kwargs['byte'],kwargs['textx'], kwargs['texty'],kwargs['size'],kwargs['index'])
    
    def changeData(self,canid,byte,textx,texty,size):
        try:
            msg = bus.recv(1)
            canvas.delete(self.text)
            if msg.arbitration_id == canid:
                self.data = msg.data[byte]
                self.text = canvas.create_text(textx * 800, 
                                                texty * 480,
                                                text=str(self.data),
                                                fill='white',
                                                font=('arial',size))
        except Exception as e:
            print(e)
        self.root.after(250,lambda: self.changeData(canid,byte,textx,texty,size)) 

该程序运行了几秒钟,但之后中断,我的意思是它只是停止显示数据,当我移动窗口时,颜色到处都是。(当我在我的函数中打印某些东西时,它仍然会打印)

标签: pythontkinter

解决方案


由于您有多个实例PlaceInfo,因此在读取 CanBus 的任务后有多个实例,这可能会导致竞争条件。同样在任务 A 之后可以读取任务 B 的消息,反之亦然。

您需要集中读取 CanBus 并在线程中执行读取,因为这recv()是一个阻塞任务。

因此,您可以创建一个CanReader扩展threading.Thread并调用recv()run()函数的类:

from threading import Thread
from queue import SimpleQueue

class CanReader(Thread):
    def __init__(self, channel):
        super().__init__(daemon=True)
        self._bus = can.interface.Bus(channel=channel)
        self._queues = {}

    def register(self, canid):
        if canid not in self._queues:
            self._queues[canid] = []
        queue = SimpleQueue()
        self._queues[canid].append(queue)
        return queue

    def run(self):
        while True:
            msg = self._bus.recv()
            for q in self._queues[msg.arbitration_id]:
                q.put(msg)

然后你可以创建一个CanReader如下的实例:

bus = CanReader('can0')
bus.start()

CanReader提供了一个函数register()PlaceInfo注册和获取一个对象,该SimpleQueue对象保存CanReader.

以下为修改PlaceInfo

class PlaceInfo():
    def __init__(self, root, **kwargs):     
        self.root = root  
        img = kwargs.get('img', None)
        if img:
            image = Image.open('images/'+img)
            foto_x_resize, foto_y_resize = kwargs.get('foto_x_resize'), kwargs.get('foto_y_resize')
            if foto_x_resize and foto_y_resize:
                image = image.resize((foto_x_resize, foto_y_resize), Image.ANTIALIAS)
            self.png = ImageTk.PhotoImage(image)
            self.image = canvas.create_image(800*kwargs['fotox'], 480*kwargs['fotoy'], image=self.png, anchor='center') 

        textx = kwargs.get('textx', None)
        texty = kwargs.get('texty', None)
        fill = kwargs.get('kleur', 'white')
        size = kwargs.get('size', 10)
        text = kwargs.get('text', None)
        if text:
            self.text = canvas.create_text(textx*800, texty*480, text=text, fill=fill, font=('arial',size))

        canid = kwargs.get('canid', None)
        if canid:
            self.queue = bus.register(canid) # register to CanReader using canid
            if 'index' not in kwargs:
                self.changeData(canid, kwargs['byte'])
            else:
                self.changeDataWithIndex(canid, kwargs['byte'], kwargs['index'])
    
    def changeData(self, canid, byte):
        try:
            msg = self.queue.get_nowait() # read message
            self.data = msg.data[byte]
            canvas.itemconfigure(self.text, text=str(self.data))
        except Exception as e:
            print(e)
        self.root.after(250, self.changeData, canid, byte)

希望这可以帮助您解决问题。


推荐阅读