是否可以使Tkinter滚动条在不同的顶层窗口中彼此独立移动?

时间:2019-04-26 12:28:37

标签: python-3.x tkinter

想象一下,有两个Tkinter.Toplevel()窗口,分别称为Window_1和Window_2,可以通过单击同一按钮(称为Button_0)来打开它们。

按下

Button_0并弹出Window_1。在Window_1中,我可以使用鼠标垫(MAC OS)上下滚动。之后,我将Window_1保持打开状态。

再次按下

Button_0并弹出Window_2,而Window_1保持打开状态。在Window_2中,我可以再次向上和向下滚动。

现在,我回到Window_1并尝试使用鼠标垫滚动,Window_1中的内容不要移动,而Window_2中的内容不要移动。

然后我关闭Window_2,并尝试在Window_1上滚动,这一次,我收到错误消息,要求在Window_2上使用画布。

我确实绑定了功能,

def on_vertical(canvas,event):
     canvas.yview_scroll(-3 * event.delta, 'units')

在每个窗口内的画布上。据我所知,似乎无法同时使用两次此功能(两个窗口均打开)。

我想当两个Windows都保持打开时的方式。在每个窗口上,我都可以上下滚动,而另一个窗口不动。可以编码吗?

这是代码示例(请注意,窗口名称未更正标签。)

from tkinter import *


######################## FUNCTIONS (DEF) ########################
def on_vertical(canvas,event):
    canvas.yview_scroll(-3 * event.delta, 'units')

######################## FUNCTIONS (CLASS) ########################
class Window(Frame):

    def __init__(self, master=None):
        Frame.__init__(self, master)
        self.master = master
        self.init_window()

    #INITIAL WINDOW
    def init_window(self):

        self.master.title("Main Window")
        self.pack(fill=BOTH, expand=1)

        Button(self, text="Button_0",command = self.load_and_print).place(x = 7, y = 95)

        # creating a button instance
        Button(self, text="EXIT PROGRAM", command=self.client_exit).place(x=500, y=250)


    #OPEN A NEW WINDOW CONTAINING STOCK LISTS
    def load_and_print(self):

        new_window = Toplevel(self)
        new_window.title("Window")

        canvas = Canvas(new_window, width = 800, height = 500, scrollregion = (0, 0, 0, 2500))
        frame = Frame(canvas)

        vbar = Scrollbar(new_window, orient = VERTICAL, command = canvas.yview)
        vbar.pack(side = RIGHT,fill = Y)

        canvas.create_window(0,0, window = frame, anchor = NW)
        canvas.config(yscrollcommand = vbar.set)
        canvas.pack(side = TOP,expand = True,fill = BOTH)
        canvas.bind_all('<MouseWheel>', lambda event, canvas=canvas: on_vertical(canvas,event))

    #MAKE PROGRAM EXIT
    def client_exit(self):
        exit()

######################## MAIN PROGRAMME ########################
#call window
root = Tk()

#size of the window
root.geometry("700x300")

app = Window(root)
root.mainloop()
root.update()

1 个答案:

答案 0 :(得分:1)

问题在于,您在鼠标滚轮事件中使用的是bind_all而不是bind

因为使用的是bind_all,所以每次创建新窗口时,它都会用新的绑定替换旧的绑定。无论哪个窗口获取事件,您的函数将始终仅对要创建的最后一个窗口起作用。而且,当然,当该窗口被破坏时,由于画布不再存在,因此鼠标绑定将引发错误。

使用绑定

一种解决方案很简单:使用bind而不是bind_all

canvas.bind_all('<MouseWheel>', lambda event, canvas=canvas: on_vertical(canvas,event))

使用bind_all

如果您想要bind_all的好处-即即使鼠标悬停在其他小部件上也可以进行滚动,则需要修改on_vertical以确定要在哪个画布上滚动运行时间,而不是传入画布。

您可以进行一些自省。例如,event对象知道哪个窗口小部件接收了该事件。从中可以确定鼠标位于哪个窗口中,并可以确定要滚动到哪个画布。

例如,将装订向上移动到__init__并像这样进行更改:

self.bind_all('<MouseWheel>', on_vertical)

接下来,更改on_vertical以找出要滚动的画布。在下面的示例中,我假设每个顶层都只有一个画布,并且您始终想要滚动该画布(即:您失去了滚动文本小部件和列表框的功能)

如果不是这种情况,则可以添加任何逻辑以找出要滚动的窗口小部件。

def on_vertical(event):
    top = event.widget.winfo_toplevel()
    for child in top.winfo_children():
        if child.winfo_class() == "Canvas":
            child.yview_scroll(-3 * event.delta, 'units')
            break