Reputation: 39
One question: How do you dynamically draw to a Tkinter Canvas object from a button click?
This topic was posted originally by user339860 (see 1) as a two part question, but the second portion of the question has not yet been addressed. I'm experiencing the same problem, specifically I cannot draw to a Canvas object from a button event. Getting this question answered will help two people, myself and user339860; please take a look.
The application creates two frames positioned left and right, the second frame contains the Canvas object. I have a button in the first frame bound to a function/method named drawRectangle. The application runs fine, it even draws a rectangle in the Canvas object using the create_rectangle method, until you click the button. When you click the button you get a message with the following;
tkinter_app_27Nov2010.py", line 25, in drawRectangle self.myCan.create_rectangle(64,64,110,110,fill='blue') AttributeError: 'NoneType' object has no attribute 'create_rectangle'
I thought it might have something to do with the scope of the Canvas object, so I created a class level variable set to None, but that didn't fix the issue. I thought about the Canvas display list (see 2), but the Tk manual pages don't reflect a method of adding a new object that I could find.
# tkinter_app_27Nov2010.py
from Tkinter import *
class Application(Frame):
myCan = None
def createWidgets(self):
uiFrame = Frame(self,width=300,height=30)
uiFrame.configure(background='#e0e0e0')
uiFrame.grid(row=0,column=0,sticky=N+S)
outputFrame = Frame(self,width=300,height=300,background='#C0C0C0')
outputFrame.grid(row=0,column=1)
newBtn = Button(uiFrame,text="testing",command=self.drawRectangle)
newBtn.grid(row=0,column=0)
fillLbl = Label(uiFrame,text='-').grid(row=1,sticky=N+S)
newLBL = Label(outputFrame,text="another testing",background='#C0C0C0')
newLBL.grid(row=0)
myCan = Canvas(outputFrame,width=300,height=300,borderwidth=1,relief='sunken')
myCan.grid(row=1)
myCan.create_rectangle(34,34,50,50,fill='red')
def drawRectangle(self):
self.myCan.create_rectangle(64,64,110,110,fill='blue')
def __init__(self,master):
Frame.__init__(self,master)
self.pack()
self.createWidgets()
root = Tk() myApp = Application(master=root) root.title("Tkinter Testing!") myApp.mainloop()
There has to be a way to get a handle on the "damage/repair display model" (see 3) the Tkinter Canvas object uses to update itself. Please help!
stackoverflow.com/questions/2824041/dynamically-add-items-to-tkinter-canvas
effbot.org/tkinterbook/canvas.htm#performance-issues
Upvotes: 0
Views: 5117
Reputation: 123622
This is a Python problem, not a tkinter
one. You have defined local variables inside createWidgets
, but you haven't set them as instance attributes. You need to use self.foo
for that:
>>> class Foo:
... def __init__(self):
... bar = "baz"
...
>>> class Bar:
... def __init__(self):
... self.bar = "baz"
...
>>> foo = Foo()
>>> foo.bar
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: Foo instance has no attribute 'bar'
>>> bar = Bar()
>>> bar.bar
'baz'
Notice that you are indeed correct: the problem is to do with the scope of the Canvas
. Or more precisely, with the scope of the myCan
variable. If you hadn't defined the class variable myCan
, the lookup self.myCan
would have raised a revealing AttributeError
.
Upvotes: 2
Reputation: 29
There is no object "self.myCan". You have to create it as a canvas object or whatever before you can use it. You may also have to call update_idletasks() depending on what you do.
Upvotes: 1