1

I have a simple project that takes the serial reading from my potentiometer and Arduino. After I get the reading that is converted and is supposed to change the color of a blank window.

For some reason I can't get the window to update and I am REALLY unfamiliar with Tkinter. Thanks for any input.

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()
2
  • Are you getting an error? If I comment out the serial calls and make the value of color "red", it works fine for me. If I had to guess, I'd say color should be giving you an "unknown color name" error, and you need to format your color name to a Tkinter acceptable string. Commented Mar 19, 2014 at 1:34
  • I do get some errors about the color, however, I think it is just due to timing with the serial. The color works fine after I discovered how to convert the RGB color to html color which Tkinter will accept. Commented Mar 20, 2014 at 19:24

1 Answer 1

1

I recently did just this to monitor input from a serial device. You need to be able to receive serial data when it is ready and also keep the Tk event-loop running. You can either use a worker thread to read the serial data and post the inputs to the UI thread for display or use the Twisted framework instead. In my case I used twisted to avoid any blocking reads from the serial port and plot the values only a simple line graph using a canvas line.

#!/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())
Sign up to request clarification or add additional context in comments.

2 Comments

Thank's for the response @patthoyts, I'll give it a try. I also found this site Which I believe shows that it could be a problem with the blocking that the Serial was doing. This stopped the Tkinter loop from update; or so I think.
Correct. You cannot hold up events in a GUI program so you should arrange things to be event oriented in much the same way you would try to get inputs to your arduino raising interrupts. The twisted framework allows you to register various interrupt sources (serial data ready, a windows event, a socket event) and dispatches them appropriately. All event handlers should return promptly and if there is more work to do, use a state engine and post additional events. That lets the work be interleaved with the other events without blocking anything for too long.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.