我想在视图发生变化时重新绘制画布(自定义滚动大图像)。
目前,我挂接了几个函数来实现这一点: xview_moveto、yview_moveto、scan_dragto
有没有事件可以用来做这个清理工作?或者是另一种方式呢?
当前代码类似于:
class CustomCanvas(Tkinter.Canvas):
def xview(self, *args):
r = Tkinter.Canvas.xview(self, *args)
if args:
self.event_generate("<<ScrollEvent>>")
return r
def yview(self, *args):
r = Tkinter.Canvas.yview(self, *args)
if args:
self.event_generate("<<ScrollEvent>>")
return r
def xview_moveto(self, *args):
Tkinter.Canvas.xview_moveto(self, *args)
self.event_generate("<<ScrollEvent>>")
def yview_moveto(self, *args):
Tkinter.Canvas.yview_moveto(self, *args)
self.event_generate("<<ScrollEvent>>")
def scan_dragto(self, *args):
Tkinter.Canvas.scan_dragto(self, *args)
self.event_generate("<<ScrollEvent>>")发布于 2013-03-05 07:16:38
如果你不反对一些开箱即用的想法,你可以用一些自定义的Tcl代码来解决这个问题。我写这篇文章并不是因为它本身就是最好的解决方案,而是因为它很有趣。
解决方案是这样的:当您滚动时,最终调用的是底层tk小部件的一个子命令来实际执行滚动。例如,self.canvas.xview_moveto(...)会生成一个类似于.123455.234123 xview moveto ...的tcl命令。看起来很奇怪的一系列数字和点是小部件的内部名称。它也是实现滚动行为的命令的名称。"xview“在tcl术语中被称为一个子命令,尽管它可以被认为是小部件对象上的一个方法。
现在,Tcl最酷的事情是,您可以重命名任何命令并将其替换为不同的命令。因为这个小部件上发生的所有事情都调用这个命令,所以我们可以创建一个代理,所有的命令都通过它发送。
在您的示例中,您希望每当滚动画布时触发一个事件。我们知道,每当使用"xview“或"yview”子命令调用widget命令时,它都会滚动。因此,通过将widget命令替换为代理,并让代理查找这些子命令,我们就可以完成这件事。
下面是一个使用python 2.7的工作示例:
# use 'tkinter' instead of 'Tkinter' if using python 3.x
import Tkinter as tk
class CustomCanvas(tk.Canvas):
def __init__(self, *args, **kwargs):
'''A custom canvas that generates <<ScrollEvent>> events whenever
the canvas scrolls by any means (scrollbar, key bindings, etc)
'''
tk.Canvas.__init__(self, *args, **kwargs)
# replace the underlying tcl object with our own function
# so we can generate virtual events when the object scrolls
tcl='''
proc widget_proxy {actual_widget args} {
set result [$actual_widget {*}$args]
set command [lindex $args 0]
set subcommand [lindex $args 1]
if {$command in {xview yview} && $subcommand in {scroll moveto}} {
# widget has been scrolled; generate an event
event generate {widget} <<ScrollEvent>>
}
return $result
}
rename {widget} _{widget}
interp alias {} ::{widget} {} widget_proxy _{widget}
'''.replace("{widget}", str(self))
self.tk.eval(tcl)
class Example(tk.Frame):
def __init__(self, *args, **kwargs):
tk.Frame.__init__(self, *args, **kwargs)
# create an instance of the custom canvas. Make sure it
# has a largeish scroll region, for demonstration purposes
self.canvas = CustomCanvas(self, width=400, height=400,
borderwidth=0, scrollregion=(0,0,1000,1000))
self.vsb = tk.Scrollbar(self, orient="vertical", command=self.canvas.yview)
self.hsb = tk.Scrollbar(self, orient="horizontal", command=self.canvas.xview)
self.canvas.configure(xscrollcommand=self.hsb.set, yscrollcommand=self.vsb.set)
self.canvas.grid(row=0, column=0, sticky="nsew")
self.vsb.grid(row=0, column=1, sticky="ns")
self.hsb.grid(row=1, column=0, sticky="ew")
self.grid_rowconfigure(0, weight=1)
self.grid_columnconfigure(0, weight=1)
# this binds to the virtual event that is sent by the proxy
self.canvas.bind("<<ScrollEvent>>", self.on_scroll)
# some data, just so that we can see that the canvas
# really is scrolling
for y in range(0, 1000, 100):
for x in range(0, 1000, 100):
self.canvas.create_text(x, y, text="%s/%s" % (x,y), anchor="nw")
def on_scroll(self, event):
print "widget scrolled..."
if __name__ == "__main__":
root = tk.Tk()
view = Example(root)
view.pack(side="top", fill="both", expand=True)
root.mainloop()注意事项:这只在你滚动区域时有效,即使你用键盘滚动(例如:向上翻页,向下翻页等),它也应该可以工作。如果调整窗口大小,事件将不会触发。您可以通过绑定到<Configure>来处理这种情况。此外,为了简洁起见,我省略了错误检查,尽管它应该相当健壮。最后,您只能在程序中使用此特定实现一次,因为我对"widget_proxy“进行了硬编码,而不是使其更独特。这是留给读者的练习。
https://stackoverflow.com/questions/15207636
复制相似问题