Natjo
Natjo

Reputation: 2128

Python: Attribute error when trying to access thread owned variable

I create a simple window with tkinter and start it as thread to keep the main program running next to the window. This is a shortened version of it:

import tkinter as tk
import threading

class mainWindow(threading.Thread):
    def __init__(self, winWidth=500, winHeight=300):
        threading.Thread.__init__(self)
        self.winWidth = winWidth
        self.winHeight = winHeight

        # Save all drawn objects, to move or delete them later
        self.bricks = []

        self.start()                    #start thread

    def run(self):
        # parent object for all windows
        self.master = tk.Tk()
        self.master.protocol("WM_DELETE_WINDOW", self.callback)
        self.show()

    def callback(self):
        self.master.quit()

    # Initialize everything important
    def show(self, tileSize=10):
        # create main window
        self.w = tk.Canvas(
                self.master,
                width=self.winWidth,
                height=self.winHeight,
                background="white")

        self.w.pack()

        # draw brick
        color = "gray49"
        posX = 200
        posY = 100
        self.bricks.append(self.w.create_rectangle(posX, posY, posX+20, posY+20, fill=color))
        tk.mainloop()

    def move_brick(self, x,y):
        self.w.move(self.brick, x, y)


mainWindow = mainWindow()
mainWindow.move_brick(100,100)

When I run the shown code the window opens correctly, but when I try to move the rectangle with move_brick(...) I get this error:

AttributeError: 'mainWindow' object has no attribute 'w'

Why can't the object find my Canvas w?

Upvotes: 0

Views: 636

Answers (1)

Bryan Oakley
Bryan Oakley

Reputation: 386285

You probably have a race condition, which is common for threaded applications. The main thread is likely calling move_brick before the worker thread has a chance to create the widget.

You can probably see this happening if you add print statements right before and after creating the widget, and in your move_brick function.

Even if you fix this, this code likely won't work because all tkinter code needs to run in a single thread. Creating the GUI in one thread and calling move_brick in another thread is not the right way to use tkinter.

Upvotes: 1

Related Questions