0

I get an error similar to: invalid command name ".91418712.91418792" when I click the Quit button in my program. I have googled this but an unsure how to fix the issue.

The issue apparently is that my thread is trying to update GUI elements that no longer exists which is why I put a sleep in before doing the destroy. The following is closely related to my issue _tkinter.TclError: invalid command name ".4302957584"

A condensed version of the code:

#!/usr/bin/python
import Adafruit_GPIO as GPIO
import Adafruit_GPIO.FT232H as FT232H
from time import sleep
from threading import Thread
import Tkinter as tk
import tkFont

# Temporarily disable the built-in FTDI serial driver on Mac & Linux platforms.
FT232H.use_FT232H()

# Create an FT232H object that grabs the first available FT232H device found.
device = FT232H.FT232H()

d0 = 0
d1 = 1

device.setup(d0, GPIO.IN)
device.setup(d1, GPIO.IN)

class Application():
    def __init__(self):
        self.root = tk.Tk()
        self.root.title("Show Voltage")
        self.root.grid()
        self.createWidgets()
        self.stop = False
        self.watcher = Thread(target = self.watcher, name="watcher")
        self.watcher.start()
        self.root.mainloop()

    def changeBackground(self, cell):
        if cell == 0:
            self.la_d0.config(bg="green")
        elif cell == 1:
            self.la_d1.config(bg="green")

    def returnBackground(self, cell):
        if cell == 0:
            self.la_d0.config(bg="black")
        elif cell == 1:
            self.la_d1.config(bg="black")

    def quit(self):
        self.stop = True
        self.watcher.join()
        sleep(0.3)
        self.root.destroy()

    def watcher(self):
        while not self.stop:
            self.wa_d0 = device.input(d0)
            self.wa_d1 = device.input(d1)

            if self.wa_d0 == GPIO.HIGH:
                self.changeBackground(0)
            else:
                self.returnBackground(0)

            if self.wa_d1 == GPIO.HIGH:
                self.changeBackground(1)
            else:
                self.returnBackground(1)
            sleep(0.0125)

    def createWidgets(self):
        self.font = tkFont.Font(family='Helvetica', size=50, weight='bold')
        self.bt_font = tkFont.Font(family='Helvetica', size=18, weight='bold')

        self.fr_d0 = tk.Frame(height=100, width=100)
        self.la_d0 = tk.Label(self.fr_d0, text="d0", bg="black", fg="red", font=self.font)
        self.fr_d1 = tk.Frame(height=100, width=100)
        self.la_d1 = tk.Label(self.fr_d1, text="d1", bg="black", fg="red", font=self.font)
        self.fr_quit = tk.Frame(height=40, width=200)
        self.bt_quit = tk.Button(self.fr_quit, text="Quit!", bg="white", command=self.quit, font=self.bt_font)

        self.fr_d0.grid(row=0, column=0)
        self.fr_d0.pack_propagate(False)
        self.la_d0.pack(fill="both", expand=1)
        self.fr_d1.grid(row=0, column=2)
        self.fr_d1.pack_propagate(False)
        self.la_d1.pack(fill="both", expand=1)
        self.fr_quit.grid(row=1, column=0, columnspan=3)
        self.fr_quit.pack_propagate(False)
        self.bt_quit.pack(fill="both", expand=1)

app = Application()
C. Braun
  • 5,061
  • 19
  • 47
C0deGyver
  • 41
  • 1
  • 7
  • 1
    Do not use `sleep()` in tkinter. All this will do is freeze the entire program. If you need to provide a delay on a command then use the `after()` method. – Mike - SMT Mar 27 '18 at 20:27
  • 1
    @Mike-SMT: in this particular case it doesn't really matter since they are sleeping a) in another thread, and b) when the program is exiting. You can't use `after` in the thread, and the point is moot when shutting down. In general, though, your advice is correct. – Bryan Oakley Mar 27 '18 at 20:44
  • @BryanOakley Hum. It looks like `sleep()` is being used in the `quit()` method. Is this not in the tkinter instance? – Mike - SMT Mar 27 '18 at 20:56
  • @Mike-SMT: yes, it's in the tkinter instance. Like I said, it's while the program is shutting down so it's relatively harmless in this specific case. – Bryan Oakley Mar 27 '18 at 20:57
  • @BryanOakley I don't fully understand the threading thing. I have not had a chance to really use it myself. I am assuming Tkinter runs in its own single thread and the fact that its separate from the other processes being done in another thread. So this and the fact that sleep is being used at the point the program closes is why sleep is not affecting the overall program? – Mike - SMT Mar 27 '18 at 21:01
  • @Mike-SMT: no, tkinter does not run in its own thread. It runs in the thread where the root window is created. In this specific case, the main thread has tkinter, and the other thread has the infinite loop with a sleep in it. – Bryan Oakley Mar 27 '18 at 21:05
  • @BryanOakley sorry I was not very accurate with my words. I did mean that tkinter is in the main thread where root was created. I was not suggesting that tkinter begins its own thread when its created. What I mean is tkinter is a single thread program. At least I don't think tkinter has multi threading capability built in. – Mike - SMT Mar 27 '18 at 21:10
  • @Mike-SMT: correct, tkinter is single-threaded. This specific program, however, uses multiple threads - one for tkinter, one for "watcher". – Bryan Oakley Mar 27 '18 at 21:16
  • @BryanOakley thanks for the clarification. I will need to spend some time with threading soon to better assist with these matters down the road. – Mike - SMT Mar 27 '18 at 21:20
  • Is it on purpose that your function and your thread object refer to the same name? (`self.watcher == func && self.watcher == thread`) this could raise the issue at self.watcher.join() – R4PH43L Mar 28 '18 at 10:04
  • @R4PH43L I see what you are saying... took me a second... going to rename the thread var. @ Mike-SMT I am using sleep in the quit method to try to give the thread time to fully stop before destroying the root instance. I will try and switch it out with after. – C0deGyver Mar 28 '18 at 21:39

1 Answers1

0

not sure exactly what fixed it but this no longer creates the error

#!/usr/bin/python
import Adafruit_GPIO as GPIO
import Adafruit_GPIO.FT232H as FT232H
from time import sleep
from threading import Thread
import Tkinter as tk
import tkFont

# Temporarily disable the built-in FTDI serial driver on Mac & Linux platforms.
FT232H.use_FT232H()

# Create an FT232H object that grabs the first available FT232H device found.
device = FT232H.FT232H()

d0 = 0
d1 = 1

device.setup(d0, GPIO.IN)
device.setup(d1, GPIO.IN)

class Application():
    def __init__(self):
        self.root = tk.Tk()
        self.root.title("Show Voltage")
        self.root.grid()
        self.createWidgets()
        self.stop = False
        self.watcherThread = Thread(target = self.watcher, name="watcher")
        self.watcherThread.start()
        self.root.mainloop()

    def changeBackground(self, cell):
        if cell == 0:
            self.la_d0.config(bg="green")
        elif cell == 1:
            self.la_d1.config(bg="green")

    def returnBackground(self, cell):
        if cell == 0:
            self.la_d0.config(bg="black")
        elif cell == 1:
            self.la_d1.config(bg="black")

    def destroy(self):
        self.root.destroy()

    def quit(self):
        self.stop = True
        self.watcherThread.join()
        self.root.after(300, self.destroy)

    def watcher(self):
        while not self.stop:
            self.wa_d0 = device.input(d0)
            self.wa_d1 = device.input(d1)

            if self.wa_d0 == GPIO.HIGH:
                self.changeBackground(0)
            else:
                self.returnBackground(0)

            if self.wa_d1 == GPIO.HIGH:
                self.changeBackground(1)
            else:
                self.returnBackground(1)
            sleep(0.0125)

    def createWidgets(self):
        self.font = tkFont.Font(family='Helvetica', size=50, weight='bold')
        self.bt_font = tkFont.Font(family='Helvetica', size=18, weight='bold')

        self.fr_d0 = tk.Frame(height=100, width=100)
        self.la_d0 = tk.Label(self.fr_d0, text="d0", bg="black", fg="red", font=self.font)
        self.fr_d1 = tk.Frame(height=100, width=100)
        self.la_d1 = tk.Label(self.fr_d1, text="d1", bg="black", fg="red", font=self.font)
        self.fr_quit = tk.Frame(height=40, width=200)
        self.bt_quit = tk.Button(self.fr_quit, text="Quit!", bg="white", command=self.quit, font=self.bt_font)

        self.fr_d0.grid(row=0, column=0)
        self.fr_d0.pack_propagate(False)
        self.la_d0.pack(fill="both", expand=1)
        self.fr_d1.grid(row=0, column=2)
        self.fr_d1.pack_propagate(False)
        self.la_d1.pack(fill="both", expand=1)
        self.fr_quit.grid(row=1, column=0, columnspan=3)
        self.fr_quit.pack_propagate(False)
        self.bt_quit.pack(fill="both", expand=1)

app = Application()

thank you all for all the input!! :)

C0deGyver
  • 41
  • 1
  • 7