首页 > 解决方案 > 尝试仅在 tkinter GUI 中水平移动(而不是垂直移动)时冻结标签

问题描述

在此处输入图像描述有一个名为 top_wl 的主窗口。并在其中创建了 frame_wl。我有一组在 frame_wl(位于 canvas_wl 上)中创建的标签,并且我正在主窗口(top_wl)中创建另一个小框架(fram_s),它位于单独的画布(canvas_s)上。在 canvas_s 中创建了另一个框架(名为 frame_s1)。我在 fram_s(小框架)上创建了一个标签。单独为 canvas_wl 设置了一个水平滚动条。我在单个垂直滚动条上设置了 canvas_wl 和 canvas_s(命名为:scroll_bar_v)。这背后的想法是冻结小框架中的标签,同时水平移动它与垂直滚动条一起移动的位置。现在的问题是小框架(fram_s)上的标签在垂直和水平移动时都被冻结了。有人可以帮我解决问题吗?或者请告诉我哪里出错了?

top_wl = tk.Tk()# tk.Tk()  
top_wl.title("Work Shop Layout")

fram_wl =tk.Frame(top_wl,width=1500,height=5000,bg = 'khaki1',relief= SUNKEN)

# Creating small frame

fram_s = tk.Frame(top_wl,width = 300,height = 500,bg = 'sky blue')
fram_s.place( x = 15, y = 90)

canvas_s = tk.Canvas(fram_s)
frame_s1 = tk.Frame(canvas_s)


l_12 = tk.Label(fram_s, text = 'Hello There',bd = 2, width = 30,bg = 'white',font = ('Arial',10,'bold'),relief = "solid")
l_12. place(x = 10, y = 30)

# finished samll frame
canvas_wl = tk.Canvas(fram_wl,width=1500,height=4000,bg = 'lime green')

# top frame portion     
scroll_bar_v = tk.Scrollbar(fram_wl,orient='vertical') 
scroll_bar_v.config(command = canvas_wl.yview )


scroll_bar_h = Scrollbar(fram_wl,orient='horizontal') 
scroll_bar_h.config(command = canvas_wl.xview)
scroll_bar_h.pack( side = BOTTOM, fill = X )


t1 = tk.Label(fram_wl)#, text = "Hello There: Number_1",width = 50,height = 100,bg = 'Sky blue',font = ('Arial',10,'bold'),relief = "sunken") #(x = 710, y = 50)


scroll_bar_v.pack( side = RIGHT, fill = Y ) 
t1.pack(side = RIGHT)

scrollable_frame_wl = tk.Frame(canvas_wl,bg = 'moccasin')#, bg = 'cyan2')
canvas_wl.create_window(0, 0, window=scrollable_frame_wl, anchor='nw')

left_wl = tk.Frame(scrollable_frame_wl, borderwidth=2, height = 5000,width = 10000,relief="sunken") #
left_wl. pack(side = 'left',padx=10, pady=10)


left_wl.pack_propagate(0)   

scrollable_frame_wl.update()

canvas_s.configure(yscrollcommand = scroll_bar_h.set,scrollregion = canvas_s.bbox("all"))
canvas_s.create_window((0,0),window =frame_s1, anchor = 'nw' ) 

canvas_wl.configure(xscrollcommand= scroll_bar_h.set,yscrollcommand= scroll_bar_v.set,scrollregion=canvas_wl.bbox("all")) 
canvas_wl.pack(fill = 'both')


fram_wl.pack()

top_wl.mainloop()


标签: pythonuser-interfacetkinterfrontend

解决方案


此代码垂直滚动画布和水平滚动更大的画布。

代码很混乱,所以我以不同的方式组织代码以进行排序。

首先创建滚动条(两个画布都将使用)

接下来我用画布和内框创建更大的框架。

接下来以同样的方式,我用画布和内框创建较小的框架。

接下来,我将一些元素添加到内部框架中 - 我在内部框架的顶部和底部都添加了标签。这样我会看看它是否滚动到最后。

最后,我将滚动条分配给画布以移动它们。接下来,我将画布分配给滚动条以将滚动条调整为正确的大小。

它有效

只有一个问题。此时两个框架仅显示画布高度的 1/3 - 因此垂直滚动条需要显示具有相同大小(1/3 高度)的滑块 - 但是当您调整窗口大小时,更大的框架显示更多画布,滚动条也显示更长的滑块但是较小的框架仍显示 1/3 的画布,它需要更小的滑块 - 并且会产生冲突。它将在短时间内显示较长的滑块(对于较大的帧),并在较长的时间显示下一个较长的滑块(对于较短的帧)。向下滚动时调整大小也会产生问题 - 首先它在较大的框架中显示底部标签,但它仍然需要鼠标移动才能在较小的框架中显示底部标签。

我没有看到这个冲突的好的解决方案——最终你将不得不调整较小的框架以显示相同的画布比例。


import tkinter as tk
 
top_wl = tk.Tk()
top_wl.title("Work Shop Layout")

# --- scrolls outside big frame (directly in window) ---

scroll_bar_v = tk.Scrollbar(top_wl, orient='vertical') 
scroll_bar_v.pack(side='right', fill='y')

scroll_bar_h = tk.Scrollbar(top_wl, orient='horizontal') 
scroll_bar_h.pack(side='bottom', fill='x')

# --- big frame (directly in window)  ---

# external frame
fram_wl = tk.Frame(top_wl, width=500, height=500, bg='khaki1', relief='sunken')
fram_wl.pack(side='right', expand=True, fill='both')
fram_wl.pack_propagate(False)  # don't resize external frame to canvas size

# inne canvas with size bigger then external frame
canvas_wl = tk.Canvas(fram_wl, width=1500, height=1500, bg='lime green')
canvas_wl.pack(fill='both')

# inner frame with size bigger then frame (scrollbars will use this size to scroll it)
scrollable_frame_wl = tk.Frame(canvas_wl, width=1500, height=1500, bg='moccasin')#, bg = 'cyan2')
canvas_wl.create_window(0, 0, window=scrollable_frame_wl, anchor='nw')

# --- small frame (inside big frame so it doesn't hide scrollbars when window is smaller) ---

# external frame 
#fram_s = tk.Frame(top_wl, width=300, height=300, bg='sky blue') # when it is directly in top_wl then it may hide scrollbars when window is smaller
fram_s = tk.Frame(fram_wl, width=300, height=300, bg='sky blue')
fram_s.place(x=0, y=100)
fram_s.pack_propagate(False)  # don't resize external frame to canvas size

# inne canvas with size bigger then external frame
canvas_s = tk.Canvas(fram_s, width=900, height=900, bg='blue')
canvas_s.pack()

# inner frame with size bigger then frame (scrollbars will use this size to scroll it)
scrollable_frame_s = tk.Frame(canvas_s, width=900, height=900, bg='red')#, bg = 'cyan2')
canvas_s.create_window(0, 0, window=scrollable_frame_s, anchor='nw')

# ---

# add to inner frame in frame WL
label_wl_1 = tk.Label(scrollable_frame_wl, text='WL-Frame Top', bd=2, width=30, bg='white', font=('Arial', 10, 'bold'), relief="solid")
label_wl_1.place(x=10, y=30)
label_wl_2 = tk.Label(scrollable_frame_wl, text='WL-Frame Bottom', bd=2, width=30, bg='white', font=('Arial', 10, 'bold'), relief="solid")
label_wl_2.place(x=10, y=1500-30)

#label_wl_1.pack()

# add to inner frame in frame S
label_s_1 = tk.Label(scrollable_frame_s, text='S-Frame Top', bd=2, width=30, bg='white', font=('Arial', 10, 'bold'), relief="solid")
label_s_1.place(x=10, y=30)
label_s_2 = tk.Label(scrollable_frame_s, text='S-Frame Bottom', bd=2, width=30, bg='white', font=('Arial', 10, 'bold'), relief="solid")
label_s_2.place(x=10, y=900-30)
#label_s_1.pack()

# --- set scrollbars to move canvas ---

# to scroll vertically both canvas 
def scroll_two_canvas(*args):
    #print(args)
    canvas_s.yview(*args)
    canvas_wl.yview(*args)
scroll_bar_v.config(command=scroll_two_canvas)  # it has to scroll two canvas

# to scroll horizontally one one canvas 
scroll_bar_h.config(command=canvas_wl.xview)    # it scroll only one canvas

# --- set canvas to resize scrollbars ---

# to update scrollbars when canvas is moved 
canvas_wl.configure(xscrollcommand=scroll_bar_h.set)
canvas_wl.configure(yscrollcommand=scroll_bar_v.set)
# do't use it because it will conflict with previous 
#canvas_s.configure(yscrollcommand=scroll_bar_v.set)

# get current size of element on canvas - if elements on canvas may change size (inner frame can change size) then you may need `bind('<Configure>')`
canvas_wl.configure(scrollregion=canvas_wl.bbox("all"))
canvas_s.configure(scrollregion=canvas_s.bbox("all"))

#def update_config(event):
#    print(event)
#    canvas_wl.configure(scrollregion=canvas_wl.bbox("all"))
#    canvas_s.configure(scrollregion=canvas_s.bbox("all"))

#top_wl.bind('<Configure>', update_config)

# ---

top_wl.mainloop()

在此处输入图像描述

在此处输入图像描述


编辑:

当大框架改变高度时调整小框架大小的代码。这样,两个框架都显示相同百分比的画布高度,滚动条效果更好。

import tkinter as tk
 
top_wl = tk.Tk()
top_wl.title("Work Shop Layout")

# --- scrolls outside big frame (directly in window) ---

scroll_bar_v = tk.Scrollbar(top_wl, orient='vertical') 
scroll_bar_v.pack(side='right', fill='y')

scroll_bar_h = tk.Scrollbar(top_wl, orient='horizontal') 
scroll_bar_h.pack(side='bottom', fill='x')

# --- big frame (directly in window)  ---

# external frame
fram_wl = tk.Frame(top_wl, width=500, height=500, bg='khaki1', relief='sunken')
fram_wl.pack(side='right', expand=True, fill='both')
fram_wl.pack_propagate(False)  # don't resize external frame to canvas size

# inne canvas with size bigger then external frame
canvas_wl = tk.Canvas(fram_wl, width=1500, height=1500, bg='lime green')
canvas_wl.pack(fill='both')

# inner frame with size bigger then frame (scrollbars will use this size to scroll it)
scrollable_frame_wl = tk.Frame(canvas_wl, width=1500, height=1500, bg='moccasin')#, bg = 'cyan2')
canvas_wl.create_window(0, 0, window=scrollable_frame_wl, anchor='nw')

# --- small frame (inside big frame so it doesn't hide scrollbars when window is smaller) ---

# external frame 
#fram_s = tk.Frame(top_wl, width=300, height=300, bg='sky blue') # when it is directly in top_wl then it may hide scrollbars when window is smaller
fram_s = tk.Frame(fram_wl, width=300, height=300, bg='sky blue')
fram_s.place(x=0, y=100)
fram_s.pack_propagate(False)  # don't resize external frame to canvas size

# inne canvas with size bigger then external frame
canvas_s = tk.Canvas(fram_s, width=900, height=900, bg='blue')
canvas_s.pack()

# inner frame with size bigger then frame (scrollbars will use this size to scroll it)
scrollable_frame_s = tk.Frame(canvas_s, width=900, height=900, bg='red')#, bg = 'cyan2')
canvas_s.create_window(0, 0, window=scrollable_frame_s, anchor='nw')

# ---

# add to inner frame in frame WL
label_wl_1 = tk.Label(scrollable_frame_wl, text='WL-Frame Top', bd=2, width=30, bg='white', font=('Arial', 10, 'bold'), relief="solid")
label_wl_1.place(x=10, y=30)
label_wl_2 = tk.Label(scrollable_frame_wl, text='WL-Frame Bottom', bd=2, width=30, bg='white', font=('Arial', 10, 'bold'), relief="solid")
label_wl_2.place(x=10, y=1500-30)

#label_wl_1.pack()

# add to inner frame in frame S
label_s_1 = tk.Label(scrollable_frame_s, text='S-Frame Top', bd=2, width=30, bg='white', font=('Arial', 10, 'bold'), relief="solid")
label_s_1.place(x=10, y=30)
label_s_2 = tk.Label(scrollable_frame_s, text='S-Frame Bottom', bd=2, width=30, bg='white', font=('Arial', 10, 'bold'), relief="solid")
label_s_2.place(x=10, y=900-30)
#label_s_1.pack()

# --- set scrollbars to move canvas ---

# to scroll both canvas 
def scroll_two_canvas(*args):
    #print(args)
    canvas_s.yview(*args)
    canvas_wl.yview(*args)
   
scroll_bar_v.config(command=scroll_two_canvas)  # it has to scroll two canvas
scroll_bar_h.config(command=canvas_wl.xview)    # it scroll only one canvas

# --- set canvas to resize scrollbars ---
# to update scrollbars when canvas is moved 
canvas_wl.configure(xscrollcommand=scroll_bar_h.set)
canvas_wl.configure(yscrollcommand=scroll_bar_v.set)
# do't use it because it will conflict with previous 
#canvas_s.configure(yscrollcommand=scroll_bar_v.set)

# get current size of element on canvas - if elements on canvas may change size (inner frame can change size) then you may need `bind('<Configure>')`
canvas_wl.configure(scrollregion=canvas_wl.bbox("all"))
canvas_s.configure(scrollregion=canvas_s.bbox("all"))


# --- resize smaller frame when bigger frame changes height

# to keep current height and compare with new height when resize, and to calculate scale used to resize smaller framer
frame_wl_height = fram_wl['height']

def update_config(event):
    global frame_wl_height

    if event.widget == fram_wl: #'.':
        scale = event.height/frame_wl_height
        frame_wl_height = event.height
        fram_s['height'] = fram_s['height'] * scale

    #canvas_wl.configure(scrollregion=canvas_wl.bbox("all"))
    #canvas_s.configure(scrollregion=canvas_s.bbox("all"))

top_wl.bind('<Configure>', update_config)

# ---

top_wl.mainloop()

在此处输入图像描述


推荐阅读