Python Tkinter画布滚动/拖动通知

Python Tkinter画布滚动/拖动通知,python,events,canvas,scroll,tkinter,Python,Events,Canvas,Scroll,Tkinter,每当视图改变时,我想重新绘制画布(自定义滚动大图像) 目前,我使用了几个函数来实现这一点:xview_moveto、yview_moveto、scan_dragto 是否有可用于执行此操作的事件?还是另一种方法 当前代码类似于: class CustomCanvas(Tkinter.Canvas): def xview(self, *args): r = Tkinter.Canvas.xview(self, *args) if args:

每当视图改变时,我想重新绘制画布(自定义滚动大图像)

目前,我使用了几个函数来实现这一点: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>>")
class CustomCanvas(Tkinter.Canvas):
def xview(自身,*参数):
r=Tkinter.Canvas.xview(self,*args)
如果参数为:
self.event_generate(“”)
返回r
def yview(自,*参数):
r=Tkinter.Canvas.yview(self,*args)
如果参数为:
self.event_generate(“”)
返回r
def xview_moveto(自身,*参数):
Tkinter.Canvas.xview_moveto(self,*args)
self.event_generate(“”)
def yview_moveto(自身,*参数):
Tkinter.Canvas.yview_moveto(self,*args)
self.event_generate(“”)
def扫描绘图(自身,*参数):
Tkinter.Canvas.scan_dragto(self,*args)
self.event_generate(“”)

如果你不反对一些开箱即用的想法,你可以用一点定制的Tcl代码来解决这个问题。我写这篇文章不是因为它本身是最好的解决方案,而是因为它是一个有趣的解决方案

解决方案是这样的:当您滚动时,最终调用的是底层tk小部件的子命令,以实际执行滚动。例如,
self.canvas.xview\u moveto(…)
生成一个类似于
.123455.234123 xview moveto…
的tcl命令。奇怪的数字和点序列是小部件的内部名称。它也是实现滚动行为的命令的名称。“xview”在tcl术语中被称为子命令,尽管它可以被认为是小部件对象上的一个方法

现在,Tcl最酷的地方是,您可以重命名任何命令,并用其他命令替换它。由于这个小部件上发生的一切都调用这个命令,所以我们可以创建一个代理,通过它发送所有命令

在您的情况下,您希望在画布滚动时触发事件。我们知道,只要使用“xview”或“yview”子命令调用小部件命令,它就会滚动。因此,通过用代理替换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()
#如果使用python 3.x,请使用'tkinter'而不是'tkinter'
将Tkinter作为tk导入
类CustomCanvas(tk.Canvas):
定义初始化(self,*args,**kwargs):
''自定义画布,可随时生成事件
画布可以通过任何方式滚动(滚动条、键绑定等)
'''
画布。初始化(self,*args,**kwargs)
#用我们自己的函数替换底层tcl对象
#因此,我们可以在对象滚动时生成虚拟事件
tcl=''
proc小部件_代理{实际_小部件args}{
设置结果[$actual_widget{*}$args]
set命令[lindex$args 0]
set子命令[lindex$args 1]
如果{xview yview}中的{命令&&$子命令{scroll moveto}{
#小部件已滚动;生成事件
事件生成{widget}
}
返回$result
}
重命名{widget}{widget}
interp别名{}::{widget}{}widget\u代理{widget}
''。替换(“{widget}”,str(self))
自我知识评估(tcl)
类示例(tk.Frame):
定义初始化(self,*args,**kwargs):
tk.Frame.\uuuu init\uuuuu(self,*args,**kwargs)
#创建自定义画布的实例。确保
#具有较大的滚动区域,用于演示
self.canvas=CustomCanvas(self,宽度=400,高度=400,
borderwidth=0,scrollregion=(0,010001000))
self.vsb=tk.Scrollbar(self,orient=“vertical”,command=self.canvas.yview)
self.hsb=tk.Scrollbar(self,orient=“horizontal”,command=self.canvas.xview)
configure(xscrollcommand=self.hsb.set,yscrollcommand=self.vsb.set)
self.canvas.grid(行=0,列=0,sticky=“nsew”)
self.vsb.grid(行=0,列=1,sticky=“ns”)
self.hsb.grid(行=1,列=0,sticky=“ew”)
self.grid_rowconfigure(0,权重=1)
self.grid\u column配置(0,权重=1)
#这将绑定到代理发送的虚拟事件
self.canvas.bind(“,self.on_滚动)
#一些数据,只是为了让我们可以看到画布
#真的是滚动
对于范围(0、1000、100)内的y:
对于范围(0、1000、100)内的x:
self.canvas.create_text(x,y,text=“%s/%s”%”(x,y),anchor=“nw”)
def on_滚动(自身、事件):
打印“小部件滚动…”
如果名称=“\uuuuu main\uuuuuuuu”:
root=tk.tk()
视图=示例(根)
view.pack(side=“top”,fill=“both”,expand=True)
root.mainloop()

注意事项:这只适用于滚动区域,但即使使用键盘滚动也应该有效(例如:向上翻页、向下翻页等)。如果调整窗口大小,则不会触发事件。您可以通过绑定到
来处理这种情况。此外,为了简洁起见,我省略了错误检查,尽管它应该相当健壮。最后,您只能在一个程序中使用这个特定的实现一次,因为我硬编码了“widget\u proxy”,而不是使它更独特。这是留给读者的练习

类似于
canvas.bind(“,redraw)
?我不熟悉TKinter事件系统,但这就是如何重新绘制画布以模拟在较大图像上的“滚动”:@markE TKinter是Python模块,而不是JavaScript;)为了避免混淆,我将重新标记这个问题。这对scan_dragto有效吗?否则,它在TCL中与我现在在Python-hooking中所做的相同。@Phelix: