0

I'm using a Kvaser Leaf Professional and trying to read the can-bus data from a motorcycle through Python coding to be able to use the data to see where values change and which values stays constant.

At the moment I'm struggling to display the data on tkinter and to update it after each time the data is read/updated again, because it changes every few microseconds. Normal ways of using .after() doesn't seem to work 100%, or I'm just doing something wrong. Or is there a better way to display updated data than tkinter?

The other problem I have is when I display the tkinter it uses a root.mailoop() to run the window and when that happens it enters that main loop and doesn't exit it and run the rest of the program and also won't be able to update the existing data displayed on the window. Is there a way to bypass the root.mainloop() maybe?

Any insight will be helpful and/or small errors I've made in my code. I started using Python only 2 days ago and I'm still learning a lot and don't know all the in's and out's yet. Thank you in advance.

Here is my whole program so far:

import keyboard
import time
import sys
import tkinter as tk
import binascii

from canlib import canlib, Frame
from canlib.canlib import ChannelData

root = tk.Tk()

def setUpChannel(channel,
                 openFlags=canlib.canOPEN_ACCEPT_VIRTUAL,
                 bitrate=canlib.canBITRATE_500K,
                 bitrateFlags=canlib.canDRIVER_NORMAL):
    ch = canlib.openChannel(channel, openFlags)
    print("Using channel: %s, EAN: %s" % (ChannelData(channel).channel_name,
                                          ChannelData(channel).card_upc_no)
                                                )
    ch.setBusOutputControl(bitrateFlags)
    ch.setBusParams(bitrate)
    ch.busOn()
    return ch

def tearDownChannel(ch):
    ch.busOff()
    ch.close()

def text(t):
    tx = binascii.hexlify(t).decode('utf-8')
    n = 2
    txt = [tx[i:i+n] for i in range(0, len(tx), n)]
    return txt

def counter():
    while True:
        try:
            cnt = 1
            frame = ch0.read()
            firstID = frame.id
            while True:
                frame = ch0.read()
                cnt += 1
                if frame.id == firstID:
                    break
            pass    
        except (canlib.canNoMsg):
            break        
        except (canlib.canError):
            print("Rerun")
    pass
    return cnt

print("canlib version:", canlib.dllversion())

ch0 = setUpChannel(channel=0)
ch1 = setUpChannel(channel=1)

frame = Frame(id_=100, data=[1, 2, 3, 4], flags=canlib.canMSG_EXT)
ch1.write(frame)
print(frame)

cnt = counter()
print("Counter: %d" %(cnt))
time.sleep(1)

while True:
    while True:
        try:
            show = ""
            i = 1
            while i <= cnt:
                frame = ch0.read()
                show = show +("%s\t%s\n" %(frame.id, text(frame.data)))
                i += 1
            print(show)          
            T = tk.Text(root, height=6, width=80)
            T.config(state="normal")
            T.insert(tk.INSERT, show)
            T.pack()
            root.mainloop()
            break
        except (canlib.canNoMsg) as ex:
            pass
        except (canlib.canError) as ex:
            print(ex)
        pass

tearDownChannel(ch0)
tearDownChannel(ch1)

You can edited my code as you want, it will help a lot if I can see how to implement the code better.

  • Read [While Loop Locks Application](https://stackoverflow.com/questions/28639228/python-while-loop-locks-application) and [Tkinter understanding mainloop](https://stackoverflow.com/questions/29158220/tkinter-understanding-mainloop) – stovfl Dec 12 '19 at 12:22
  • You might be able to improve things by using multiple threads. See my answer ot the question [Freezing/Hanging tkinter Gui in waiting for the thread to complete](https://stackoverflow.com/questions/53696888/freezing-hanging-tkinter-gui-in-waiting-for-the-thread-to-complete) for an example. Note I doubt it's possible to update a tkinter GUI every few microseconds — so the best you can probably do will be at a much slower rate. – martineau Dec 12 '19 at 12:25

1 Answers1

0

You dont need to use multi-threading or anything convoluted. Just use the widget.after() method that calls another function/method of your choosing after the desired time. In the case below, the time is 1 second -> 1000 milliseconds. Just call the class before the mainloop and it will start working.

class UI:
    def __init__(self, parent):
        # variable storing time
        self.seconds = 0
        # label displaying time
        self.label.configure(text="%i s" % self.seconds)
        # start the timer
        self.label.after(1000, self.refresh_label)


    def refresh_label(self):
        #refresh the content of the label every second
        # increment the time
        self.seconds += 1
        # display the new time
        self.label.configure(text="%i s" % self.seconds)
        # request tkinter to call self.refresh after 1s (the delay is given in ms)
        self.label.after(1000, self.refresh_label)
Simo Todorov
  • 26
  • 1
  • 6
  • I tried implementing your example but it throws out even more errors now and I don't know enough about Python to completely understand what it is I'm doing wrong. Is it possible that you can show me how you would implement it with my code I already have? I moved my display code to a function and call it in the main loop, is that the function that I should call through the `.after()`? – Johannes Appel Dec 13 '19 at 07:05