Robot controller code: Error when loading - python

I had written a simple serial controller using serial--actually, it is more Frankstein code, pieces others have written and I pieced together. Feel free to poke fun at my ineptness, but any direction would be appreciated. It runs fine on my Linux running Backtrack, Python 2.6, but when I try to run it on the Raspberry Pi, Python 2.7, I get these errors:
Traceback (most recent call last):Exception in thread Thread-2:
Traceback (most recent call last):
File "/usr/lib/python2.7/threading.py", line 551, in __bootstrap_inner
self.run()
File "/usr/lib/python2.7/threading.py", line 504, in run
self.__target(*self.__args, **self.__kwargs)
File "/home/pi/Zombie Python 1.2 (Modified for Pi) (from Adapt 1.7).py", line 147, in rx
self.ser = serial.Serial('/dev/ttyUSB0', 9600, timeout=1, bytesize=8, stopbits=1)
File "/usr/local/lib/python2.7/dist-packages/serial/serialutil.py", line 260, in __init__
self.open()
File "/usr/local/lib/python2.7/dist-packages/serial/serialposix.py", line 276, in open
raise SerialException("could not open port %s: %s" % (self._port, msg))
SerialException: could not open port /dev/ttyUSB0: [Errno 2] No such file or directory: '/dev/ttyUSB0'
File "/home/pi/Zombie Python 1.2 (Modified for Pi) (from Adapt 1.7).py", line 185, in <module>
client = ThreadedClient(root)
File "/home/pi/Zombie Python 1.2 (Modified for Pi) (from Adapt 1.7).py", line 113, in __init__
self.periodicCall()
File "/home/pi/Zombie Python 1.2 (Modified for Pi) (from Adapt 1.7).py", line 119, in periodicCall
self.gui.processIncoming()
AttributeError: GuiPart instance has no attribute 'processIncoming'
My code is:
import Tkinter
import time
import threading
import random
import Queue
import serial
import readline
#import xbee
import sys
x=""
class GuiPart:
def __init__(self, master, queue, endApplication):
self.sonar = Tkinter.StringVar() # Feeds sonar sensor data to label
self.lm = Tkinter.StringVar() # Feeds left motor speed to label
self.rm = Tkinter.StringVar() # Feeds right motor speed to label
self.queue = queue
# Set up the GUI
frame1 = Tkinter.Frame(master, bd=200) #Setup frame.
frame1.bind("<Key>", key) # Allow frame to handle keypresses.
frame1.focus_set() #Set focus of frame so that keypresses activate event.
frame1.pack() #Show it.
#Button
console = Tkinter.Button(frame1, text='Close', command=endApplication)
console.pack()
# Add more GUI stuff here
self.lm.set(0) # Initializes left motor label
self.rm.set(0) # Initializes right motor label
self.sonar.set(0) # Initializes sonar label
#Sonar label
sonarLbl = Tkinter.Label(frame1, textvariable=self.sonar)
sonarLbl.pack()
#Right motor label
rmLbl = Tkinter.Label(frame1, text="Left Motor Speed: ", textvariable=self.rm)
rmLbl.pack()
#Left motor label
lmLbl = Tkinter.Label(frame1, textvariable=self.lm)
lmLbl.pack()
def key(self, event):
#print "pressed", repr(event.char)
#self.sonar = repr(event.char) <------ This should be the line to handle keypresses
global x
x = repr(event.char)
def processIncoming(self):
"""
Handle all the messages currently in the queue (if any).
"""
while self.queue.qsize():
try:
msg = self.queue.get(0)
# Check contents of message and do what it says
# As a test, we simply print it
# Below is where I will parse the "msg" variable, splitting
# it (msg.rsplit) to pull out sensor data and update labels.
lm, rm, sonar, mknt = msg.rsplit(",")
lm = "Left Motor Speed: "+lm
rm = "Right Motor Speed: "+rm
sonar = "Sonar: "+sonar+" CMs away"
self.sonar.set(sonar) # Setting the labels with latest sensor info.
self.lm.set(lm) # Setting the labels with latest sensor info.
self.rm.set(rm) # Setting the labels with latest sensor info.
except Queue.Empty:
pass
class ThreadedClient:
"""
Launch the main part of the GUI and the worker thread. periodicCall and
endApplication could reside in the GUI part, but putting them here
means that you have all the thread controls in a single place.
"""
def __init__(self, master):
"""
Start the GUI and the asynchronous threads. We are in the main
(original) thread of the application, which will later be used by
the GUI. We spawn a new thread for the worker.
"""
self.master = master
# Create the queue
self.queue = Queue.Queue()
# Set up the GUI part
self.gui = GuiPart(master, self.queue, self.endApplication)
# Set up the thread to do asynchronous I/O
# More can be made if necessary
self.running = 1
self.thread1 = threading.Thread(target=self.workerThread1)
self.thread1.start()
#Start receiving thread.
self.rx = threading.Thread(target=self.rx)
self.rx.start()
# Start the periodic call in the GUI to check if the queue contains
# anything
self.periodicCall()
def periodicCall(self):
"""
Check every 100 ms if there is something new in the queue.
"""
self.gui.processIncoming()
if not self.running:
# This is the brutal stop of the system. You may want to do
# some cleanup before actually shutting it down.
import sys
sys.exit(1)
self.master.after(100, self.periodicCall)
def workerThread1(self):
"""
This is where we handle the asynchronous I/O. For example, it may be
a 'select()'.
One important thing to remember is that the thread has to yield
control.
"""
while self.running:
# To simulate asynchronous I/O, we create a random number at
# random intervals. Replace the following 2 lines with the real
# thing.
time.sleep(rand.random() * 0.3)
msg = rand.random()
#self.queue.put(msg)
# Continuously read and print packets
def rx(self):
global x
self.ser = serial.Serial('/dev/ttyUSB0', 9600, timeout=1, bytesize=8, stopbits=1)
self.ser.flush()
while(1):
response = str(self.ser.readline())
# Send movement codes.
if x == "'a'" or x == "'A'": # or "'A'": # Turn left.
self.ser.write('4')
elif x == "'w'" or x == "'W'": #Go forward.
self.ser.write("3")
elif x == "'d'" or x == "'D'": #Turn right.
self.ser.write("2")
elif x == "'s'" or x == "'S'": #Go back.
self.ser.write("1")
elif x == "'x'" or x == "'X'": #Stop.
self.ser.write("5")
elif x == "'1'": #Raise speed.
self.ser.write("7")
elif x == "'2'": #Lower speed.
self.ser.write("6")
x = ""
if len(response) > 10:
self.ser.flushInput() #If you don't flush the buffer, then it'll try to read out all
#the sensor readings, so the reaction time will be extrordinary.
time.sleep(.1)# This scales back the CPU usage.
self.queue.put(response)
def endApplication(self):
print "Closing"
self.running = 0
self.ser.close()
sys.exit(1)
rand = random.Random()
root = Tkinter.Tk()
client = ThreadedClient(root)
root.mainloop()

Your self.gui is GuiPart. GuiPart doesn't have processIncoming.

In your code snippet displayed above the lines
def key(self, event):
and
def processIncoming(self):
are no longer indented by four blank spaces as they probably should to become methods of your class GuiPart.
Python newcomers coming from other languages often miss the detail that indentation is a very important part of the syntax in the Python programming language. Therefore it is also very essential to avoid mixing TABs and spaces.
(Especially when copying code snippets from various sources which is otherwise
a great opportunity to learn and experiment with new things)
Another thing is: In Python you can also define module level global functions.
The syntax to do this is practically the same as the syntax used to define a method within a class (with the exception of the indentation level).
So in your example code snippet the former methods key() and processIncoming() had become module global function definitions instead of methods simply by wrong indentation.
A method is an attribute of a class object. These two methods were moved
from the class name space one level up to module name space.
Hence the following error message:
AttributeError: GuiPart instance has no attribute 'processIncoming'

Related

tkinter, master window does not loop after I start a thread [duplicate]

My interface is freezing on pressing the button. I am using threading but I am not sure why is still hanging. Any help will be appreciated. Thanks in advance
class magic:
def __init__(self):
self.mainQueue=queue.Queue()
def addItem(self,q):
self.mainQueue.put(q)
def startConverting(self,funcName):
if(funcName=="test"):
while not self.mainQueue.empty():
t = Thread(target = self.threaded_function)
t.start()
t.join()
def threaded_function(self):
time.sleep(5)
print(self.mainQueue.get())
m=magic()
def helloCallBack():
m.addItem("asd")
m.startConverting("test") //this line of code is freezing
B = tkinter.Button(top, text ="Hello", command = helloCallBack)
B.pack()
top.mainloop()
Here's a recipe for doing an asynchronous task with a tkinter-based GUI. I adapted it from a recipe in the cited book. You should be able to modify it to do what you need.
To keep the GUI responsive requires not interfering with its mainloop() by doing something like join()ing a background thread—which makes the GUI "hang" until the thread is finished. This is accomplished by using the universal after() widget method to poll a Queue at regular intervals.
# from "Python Coobook 2nd Edition", section 11.9, page 439.
# Modified to work in Python 2 & 3.
from __future__ import print_function
try:
import Tkinter as tk, time, threading, random, Queue as queue
except ModuleNotFoundError: # Python 3
import tkinter as tk, time, threading, random, queue
class GuiPart(object):
def __init__(self, master, queue, end_command):
self.queue = queue
# Set up the GUI
tk.Button(master, text='Done', command=end_command).pack()
# Add more GUI stuff here depending on your specific needs
def processIncoming(self):
""" Handle all messages currently in the queue, if any. """
while self.queue.qsize():
try:
msg = self.queue.get_nowait()
# Check contents of message and do whatever is needed. As a
# simple example, let's print it (in real life, you would
# suitably update the GUI's display in a richer fashion).
print(msg)
except queue.Empty:
# just on general principles, although we don't expect this
# branch to be taken in this case, ignore this exception!
pass
class ThreadedClient(object):
"""
Launch the main part of the GUI and the worker thread. periodic_call()
and end_application() could reside in the GUI part, but putting them
here means that you have all the thread controls in a single place.
"""
def __init__(self, master):
"""
Start the GUI and the asynchronous threads. We are in the main
(original) thread of the application, which will later be used by
the GUI as well. We spawn a new thread for the worker (I/O).
"""
self.master = master
# Create the queue
self.queue = queue.Queue()
# Set up the GUI part
self.gui = GuiPart(master, self.queue, self.end_application)
# Set up the thread to do asynchronous I/O
# More threads can also be created and used, if necessary
self.running = True
self.thread1 = threading.Thread(target=self.worker_thread1)
self.thread1.start()
# Start the periodic call in the GUI to check the queue
self.periodic_call()
def periodic_call(self):
""" Check every 200 ms if there is something new in the queue. """
self.master.after(200, self.periodic_call)
self.gui.processIncoming()
if not self.running:
# This is the brutal stop of the system. You may want to do
# some cleanup before actually shutting it down.
import sys
sys.exit(1)
def worker_thread1(self):
"""
This is where we handle the asynchronous I/O. For example, it may be
a 'select()'. One important thing to remember is that the thread has
to yield control pretty regularly, be it by select or otherwise.
"""
while self.running:
# To simulate asynchronous I/O, create a random number at random
# intervals. Replace the following two lines with the real thing.
time.sleep(rand.random() * 1.5)
msg = rand.random()
self.queue.put(msg)
def end_application(self):
self.running = False # Stops worker_thread1 (invoked by "Done" button).
rand = random.Random()
root = tk.Tk()
client = ThreadedClient(root)
root.mainloop()
For anyone having a problem with sys.exit(1) in #martineau's code - if you replace sys.exit(1) with self.master.destroy() the program ends gracefully. I lack the reputation to add a comment, hence the seperate answer.

Tkinter GUI updating depending on serial connection

Good afternoon thank you all who look at my issue,
I am building a script to do initial configuration on switches through console port, I can make a connection to the console port the issue comes when building a GUI in TKINTER.
I want the gui to connect through a specified some port when the button is pressed and when the connection is made make a light(using canvas) go green showing a successful connection went through.
My issue is getting the light to change to green and stay when a connection is made I have tried
my initally also tried to use global then realized the window loop constantly set it back to red
nested while loop--- breaks Tkinker
importing a file I created called variableset which stores variable to set green but since it constantly has a new instance it just sets the variable in tinker back to red.
any help would be greatly appreciated
GUI CODE
import tkinter as tk
from tkinter import *
import connect
import variableset
setting = variableset.setting
window = tk.Tk()
window.title("Network Wizard 1")
window.geometry('300x500')
# setting =tk.IntVar()
# setting.set(variableset.setting)
#serial port pick the right one
serialportlabel = tk.Label(text="COM Number")
serialport = tk.Entry(width= 7)
serialportlabel.pack()
serialport.pack()
#color alerting!! red bad
alert = Canvas(window, width=50, height=20)
alert.pack()
if setting == 0:
a=alert.create_rectangle(5, 0, 50, 50, fill='red')
else:
a=alert.create_rectangle(5, 0, 50, 50, fill='green')
# #connection part woop woop
connection = tk.Button(text="connect", command = lambda: bus())
connection.pack()
# #firmwarecheck
# firmwarecheck = tk.Button(text="Firmware check")
# firmwarecheck.pack()
# #firmware update
# firmwareupdate= tk.Button(text="Firmware update")
# firmwareupdate.pack()
# #software update
# software = tk.Button(text="software update")
# connect.pack()
# #vmlans
# vlanupdate = tk.Button(text="Vlanupdate")
# vlanupdate.pack()
# disconnect = tk.Button(text="disconnect")
# disconnect.pack()
# qut = tk.Button(text="quite")
# qut.pack()
def bus():
global ser
ser = connect.connect(serialport.get())
global setting
setting = variableset.initial
# def firmware(ser):
# if ser.isOpen() == true :
window.mainloop()
Connect code
import serial
import time
import sys
#connect
def connect(com):
ser = serial.Serial(
port = com, #COM
baudrate=9600,
parity='N',
stopbits=1,
bytesize=8,
timeout=8
)
ser.isOpen()
print(ser.name)
#set variables
enter = str.encode('\r\n') #enter
user = str.encode('admin#sytem\r\n') #default user name
pwd = str.encode('\r\n') #defualt password
qut = str.encode('quit\r')
time.sleep(1.0)
# ser.inWaiting()
ser.write(enter) #promt login
time.sleep(0.5)
ser.write(user) #enter user name
time.sleep(0.5)
ser.write(pwd) #enter password
time.sleep(0.5)
ser.write(enter)
time.sleep(0.5)
ser.write(enter)
time.sleep(0.5)
ser.write(str.encode("sytem\r\n"))
time.sleep(0.5)
ser.write(qut)
ser.write(qut)
input_read = ser.read(500)
input_read = input_read.decode("utf-8","ignore")
print(input_read)
ser.close()
return ser
def write(ser):
ser.write(str.encode(''+'\r\n'))
def disconnect(ser):
ser.write(str.encode('quit\r\n'))
time.sleep(.2)
ser.write(str.encode('quit\r\n'))
time.sleep(.2)
ser.write(str.encode('quit\r\n'))
time.sleep(.2)
ser.close()
Variableset Code
global setting
setting = 0
def initial():
global setting
setting = 1
return setting
def unset():
global setting
setting = 0
return setting
Any help greatly appreciated
Your if-else statement only runs once, before the setting variable has been set, as far as we can tell. It's not in a loop or a function that gets called from somewhere else. What exactly is the problem you're having with the code that you posted? The issues you described seem to relate to a loop setting it back to red in a different version of the code.
In any event, I suspect that your problem is trying to use infinite loops to keep checking the setting, which prevents the tkinter mainloop() from running, and therefore blocks the GUI from updating. If so, the best method to fix it is probably to change your code for "check the setting and then change the color" into its own function, which gets called for the first time at the end of your connect function. Then, the end of the check setting function should schedule itself to be run after a time delay using the tkinter after() method, which is non-blocking (asnychronous) and allows the mainloop() to keep running. Alternative methods include a separate thread to run the check-setting code, or using the tkinter update() method, but after() is easiest.
Note that your sleep() functions in the connect code will also block the mainloop while they're running. I'm also not clear what you're trying to do with the variableset code; it appears to set the setting to 1 (aka green light) when you call initial(), regardless of the actual status on the serial port.

Calling a Ptoaster function within a Tkinter GUI class

The Problem
I'm working on a Python GUI, using Tkinter. I'm also trying to add "toaster" messages, using Ptoaster. Here's an example of what I'm trying to achieve:
from tkinter import *
import ptoaster
PADDING = 20
class MyInterface:
def __init__(self):
self.root = Tk()
self.label = self.make_label()
print_welcome()
def make_label(self):
result = Label(self.root, text="Hello, world!")
result.pack(padx=PADDING, pady=PADDING)
return result
def run_me(self):
self.root.mainloop()
def print_welcome():
message = "Hello again!"
ptoaster.notify("Hello!", message)
interface = MyInterface()
interface.run_me()
If I try to run the above code, one of two things will happen:
The command line will spit out the following error:
[xcb] Unknown sequence number while processing queue
[xcb] Most likely this is a multi-threaded client and XInitThreads has not been called
[xcb] Aborting, sorry about that.
python3: ../../src/xcb_io.c:260: poll_for_event: Assertion `!xcb_xlib_threads_sequence_lost' failed.
XIO: fatal IO error 25 (Inappropriate ioctl for device) on X server ":0"
after 207 requests (207 known processed) with 2 events remaining.
Aborted (core dumped)
My whole laptop will freeze, necessitating a hard reset.
However, if I move the call print_welcome() from outside of MyInterface, so that it's called before this class is initialised, then none of the above errors crop up.
What I'd Like to Know
How to call a function, from within a Tkinter GUI class, which causes a "toaster" message to be displayed, without causing the whole platform to crash.
Why the above errors are cropping up.
Documentation states it needs to be verified that the ptoaster.notify is called from the main program.
IMPORTANT - you need to make sure you call notify from the main program
Working code for me:
from tkinter import *
import ptoaster
PADDING = 20
class MyInterface:
def __init__(self):
self.root = Tk()
self.label = self.make_label()
print_welcome()
def make_label(self):
result = Label(self.root, text="Hello, world!")
result.pack(padx=PADDING, pady=PADDING)
return result
def run_me(self):
self.root.mainloop()
def print_welcome():
message = "Hello again!"
ptoaster.notify("Hello!", message)
if __name__ == '__main__':
interface = MyInterface()
interface.run_me()
Documentation (See: Sample Program)

Freezing/Hanging tkinter GUI in waiting for the thread to complete

My interface is freezing on pressing the button. I am using threading but I am not sure why is still hanging. Any help will be appreciated. Thanks in advance
class magic:
def __init__(self):
self.mainQueue=queue.Queue()
def addItem(self,q):
self.mainQueue.put(q)
def startConverting(self,funcName):
if(funcName=="test"):
while not self.mainQueue.empty():
t = Thread(target = self.threaded_function)
t.start()
t.join()
def threaded_function(self):
time.sleep(5)
print(self.mainQueue.get())
m=magic()
def helloCallBack():
m.addItem("asd")
m.startConverting("test") //this line of code is freezing
B = tkinter.Button(top, text ="Hello", command = helloCallBack)
B.pack()
top.mainloop()
Here's a recipe for doing an asynchronous task with a tkinter-based GUI. I adapted it from a recipe in the cited book. You should be able to modify it to do what you need.
To keep the GUI responsive requires not interfering with its mainloop() by doing something like join()ing a background thread—which makes the GUI "hang" until the thread is finished. This is accomplished by using the universal after() widget method to poll a Queue at regular intervals.
# from "Python Coobook 2nd Edition", section 11.9, page 439.
# Modified to work in Python 2 & 3.
from __future__ import print_function
try:
import Tkinter as tk, time, threading, random, Queue as queue
except ModuleNotFoundError: # Python 3
import tkinter as tk, time, threading, random, queue
class GuiPart(object):
def __init__(self, master, queue, end_command):
self.queue = queue
# Set up the GUI
tk.Button(master, text='Done', command=end_command).pack()
# Add more GUI stuff here depending on your specific needs
def processIncoming(self):
""" Handle all messages currently in the queue, if any. """
while self.queue.qsize():
try:
msg = self.queue.get_nowait()
# Check contents of message and do whatever is needed. As a
# simple example, let's print it (in real life, you would
# suitably update the GUI's display in a richer fashion).
print(msg)
except queue.Empty:
# just on general principles, although we don't expect this
# branch to be taken in this case, ignore this exception!
pass
class ThreadedClient(object):
"""
Launch the main part of the GUI and the worker thread. periodic_call()
and end_application() could reside in the GUI part, but putting them
here means that you have all the thread controls in a single place.
"""
def __init__(self, master):
"""
Start the GUI and the asynchronous threads. We are in the main
(original) thread of the application, which will later be used by
the GUI as well. We spawn a new thread for the worker (I/O).
"""
self.master = master
# Create the queue
self.queue = queue.Queue()
# Set up the GUI part
self.gui = GuiPart(master, self.queue, self.end_application)
# Set up the thread to do asynchronous I/O
# More threads can also be created and used, if necessary
self.running = True
self.thread1 = threading.Thread(target=self.worker_thread1)
self.thread1.start()
# Start the periodic call in the GUI to check the queue
self.periodic_call()
def periodic_call(self):
""" Check every 200 ms if there is something new in the queue. """
self.master.after(200, self.periodic_call)
self.gui.processIncoming()
if not self.running:
# This is the brutal stop of the system. You may want to do
# some cleanup before actually shutting it down.
import sys
sys.exit(1)
def worker_thread1(self):
"""
This is where we handle the asynchronous I/O. For example, it may be
a 'select()'. One important thing to remember is that the thread has
to yield control pretty regularly, be it by select or otherwise.
"""
while self.running:
# To simulate asynchronous I/O, create a random number at random
# intervals. Replace the following two lines with the real thing.
time.sleep(rand.random() * 1.5)
msg = rand.random()
self.queue.put(msg)
def end_application(self):
self.running = False # Stops worker_thread1 (invoked by "Done" button).
rand = random.Random()
root = tk.Tk()
client = ThreadedClient(root)
root.mainloop()
For anyone having a problem with sys.exit(1) in #martineau's code - if you replace sys.exit(1) with self.master.destroy() the program ends gracefully. I lack the reputation to add a comment, hence the seperate answer.

Dynamically updating Tkinter window based on serial data

I'm trying to write a program that gets data from a serial port connection and automatically updates the Tkinter window in real time based on that data.
I tried to create a separate thread for the window that periodically gets the current data from the main thread and updates the window, like this:
serialdata = []
data = True
class SensorThread(threading.Thread):
def run(self):
serial = serial.Serial('dev/tty.usbmodem1d11', 9600)
try:
while True:
serialdata.append(serial.readline())
except KeyboardInterrupt:
serial.close()
exit()
class GuiThread(threading.Thread):
def __init__(self):
threading.Thread.__init__(self)
self.root = Tk()
self.lbl = Label(self.root, text="")
def run(self):
self.lbl(pack)
self.lbl.after(1000, self.updateGUI)
self.root.mainloop()
def updateGUI(self):
msg = "Data is True" if data else "Data is False"
self.lbl["text"] = msg
self.root.update()
self.lbl.after(1000, self.updateGUI)
if __name == "__main__":
SensorThread().start()
GuiThread().start()
try:
while True:
# A bunch of analysis that sets either data = True or data = False based on serialdata
except KeyboardInterrupt:
exit()
Running it gives me this error:
Exception in thread Thread-2:
Traceback (most recent call last):
File "/System/Library/Frameworks/Python.framework/Versions/2.6/lib/python2.6/threading.py", line 522, in __bootstrap_inner
self.run()
File "analysis.py", line 52, in run
self.lbl1.pack()
File "/System/Library/Frameworks/Python.framework/Versions/2.6/lib/python2.6/lib-tk/Tkinter.py", line 1764, in pack_configure
+ self._options(cnf, kw))
RuntimeError: main thread is not in main loop
When I google this error, I mostly get posts where people are trying to interact with the window from two different threads, but I don't think I'm doing that. Any ideas? Thanks so much!
Don't run the TK gui from a thread - run it from the main process. I mashed your example into something that demonstrates the principle
from time import sleep
import threading
from Tkinter import *
serialdata = []
data = True
class SensorThread(threading.Thread):
def run(self):
try:
i = 0
while True:
serialdata.append("Hello %d" % i)
i += 1
sleep(1)
except KeyboardInterrupt:
exit()
class Gui(object):
def __init__(self):
self.root = Tk()
self.lbl = Label(self.root, text="")
self.updateGUI()
self.readSensor()
def run(self):
self.lbl.pack()
self.lbl.after(1000, self.updateGUI)
self.root.mainloop()
def updateGUI(self):
msg = "Data is True" if data else "Data is False"
self.lbl["text"] = msg
self.root.update()
self.lbl.after(1000, self.updateGUI)
def readSensor(self):
self.lbl["text"] = serialdata[-1]
self.root.update()
self.root.after(527, self.readSensor)
if __name__ == "__main__":
SensorThread().start()
Gui().run()
You need to put the GUI in the main thread, and use a separate thread to poll the serial port. When you read data off of the serial port you can push it onto a Queue object.
In the main GUI thread you can set up polling to check the queue periodically, by using after to schedule the polling. Call a function which drains the queue and then calls itself with after to effectively emulate an infinite loop.
If the data that comes from the sensor comes at a fairly slow rate, and you can poll the serial port without blocking, you can do that all in the main thread -- instead of pushing and pulling from the queue, your main thread can just see if there's data available and read it if there is. You can only do this if it's possible to read without blocking, otherwise your GUI will freeze while it waits for data.
For example, you could make it work like this:
def poll_serial_port(self):
if serial.has_data():
data = serial.readline()
self.lbl.configure(text=data)
self.after(100, self.poll_serial_port)
The above will check the serial port 10 times per second, pulling one item off at a time. You'll have to adjust that for your actual data conditions of course. This assumes that you have some method like has_data that can return True if and only if a read won't block.

Categories

Resources