动态在Tkinter画布上绘图的问题

0 投票
2 回答
5058 浏览
提问于 2025-04-16 07:41

一个问题: 如何在点击按钮时动态地在Tkinter的Canvas对象上绘图?

这个话题最初是由用户339860提出的(见1),分成了两个部分,但第二部分的问题还没有得到解决。我也遇到了同样的问题,具体来说我无法通过按钮事件在Canvas对象上绘图。解决这个问题对我和用户339860都有帮助,请大家看看。

这个应用程序创建了两个框架,分别放在左边和右边,第二个框架里包含了Canvas对象。我在第一个框架里有一个按钮,这个按钮绑定了一个名为drawRectangle的函数。应用程序运行得很好,它甚至能通过create_rectangle方法在Canvas对象上绘制一个矩形,直到你点击按钮。当你点击按钮时,会出现以下消息:

tkinter_app_27Nov2010.py", 第25行,在drawRectangle中 self.myCan.create_rectangle(64,64,110,110,fill='blue') AttributeError: 'NoneType'对象没有'create_rectangle'属性

我想这可能和Canvas对象的作用域有关,所以我创建了一个类级别的变量并设置为None,但这并没有解决问题。我考虑过Canvas的显示列表(见2),但Tk的手册里没有找到我能找到的添加新对象的方法。

代码:


# 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 测试!") myApp.mainloop()


一定有办法获取Tkinter Canvas对象用来更新自己的“损坏/修复显示模型”(见3)。请帮帮我!

参考资料:

  1. stackoverflow.com/questions/2824041/dynamically-add-items-to-tkinter-canvas

  2. www.tcl.tk/man/tcl8.4/TkCmd/canvas.htm#M16

  3. effbot.org/tkinterbook/canvas.htm#performance-issues

2 个回答

1

这里没有叫“self.myCan”的对象。在你使用它之前,得先把它创建成一个画布对象或者其他什么东西。根据你做的事情,你可能还需要调用一下update_idletasks()。

2

这是一个关于Python的问题,不是关于tkinter的。你在createWidgets里面定义了局部变量,但没有把它们设置为实例属性。你需要用self.foo来做到这一点:

>>> 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'

注意,你说得没错:问题确实和Canvas的作用域有关。更准确地说,是和myCan这个变量的作用域有关。如果你没有定义类变量myCan,那么查找self.myCan时就会出现一个很明显的AttributeError错误。

撰写回答