0

I am using Tkinter to visualize sensor data. I have serial data being read, and I want to use it to move a square on the screen.

from tkinter import Tk, Canvas
import tkinter
import serial

#GUI
root = Tk()
c = Canvas(root, width=1000, height=50)
c.configure(bg='grey', highlightthickness=0)

#Position Parameters
posX=10
posY=20
lenght=10

subject = c.create_rectangle(posX, posY, posX+lenght, posY+lenght, outline='black', fill='black')

#Serial Data Read
ser = serial.Serial('COM5', baudrate=9600, timeout=0.2)  # open serial port
print(ser.name)                                          # check which port was really used

#Failed Live Visual
while True:
    while (ser.inWaiting()==0):
        pass
    dataPacket=ser.readline()
    dataPacket=str(dataPacket, 'utf-8')                 #Incoming data from arduino into string
    dataPacket=int(dataPacket.strip(' \r\n'))           #Formatting

    posX=dataPacket
    c.coords(subject, posX, posY, posX+lenght, posY+lenght)
    c.pack()
    root.mainloop()

My issue is: that Tkinter will display but not update the position of the square. I've tried different approaches such as using the c.move() setter, as well as creating a function and calling it with the root.after() command but none seem to work. I don't think this should be so difficult, so that's why I ask.

Thank you for your time!

  • It seems you got mostly correct code, but I think you're trying to move `subject` instead of the Canvas window `c` itself, correct? Also, calling `root.mainloop()` will put you into a forever loop and nothing will update. Check out [Tkinter Mainloop](https://stackoverflow.com/questions/29158220/tkinter-understanding-mainloop) – HWW Aug 02 '22 at 00:32
  • @HWW: Nope — the OP is trying to move the `subject` *on* the canvas. It is also untrue that calling `mainloop()` prevents things from updating — quite the contrary. – martineau Aug 02 '22 at 17:48

1 Answers1

0

The way you're using mainloop() is wrong — it's typically only called once in a tkinter application because it is the loop that drives the whole GUI which is event-driven.

One way to work around that limitation in this case would be to use its universal after() widget method to schedule periodic checks of the serial port for input.

I've made changes to your code to do this, including adding a Start button to initiate the periodic polling process. Obviously I couldn't fully test it, but it ought to give you the overall idea.

import tkinter as tk
import serial

#GUI
root = tk.Tk()

start_btn = tk.Button(root, text='Start')
start_btn.pack(anchor=tk.NW)

c = tk.Canvas(root, width=1000, height=50)
c.configure(bg='grey', highlightthickness=0)
c.pack()  # ADDED

#Position Parameters
posX=10
posY=20
lenght=10

subject = c.create_rectangle(posX, posY, posX+lenght, posY+lenght, outline='black', fill='black')

#Serial Data Read
ser = serial.Serial('COM5', baudrate=9600, timeout=0.2)
print(ser.name)  # check which port was really used

def check_data():
    if ser.inWaiting():
        dataPacket=ser.readline()
        dataPacket=str(dataPacket, 'utf-8')  #Incoming data from arduino into string
        dataPacket=int(dataPacket.strip(' \r\n'))  #Formatting

        posX=dataPacket
        c.coords(subject, posX, posY, posX+lenght, posY+lenght)
    root.after(250, check_data)  # Check again in 250 ms.

start_btn.config(command=check_data)
root.mainloop()

martineau
  • 119,623
  • 25
  • 170
  • 301