首页 文章

tkinter带网格的画布滚动条?

提问于
浏览
4

Tkinter和Python相对较新 . 亲切地忍受我 .

我试图显示以下GUI,并希望在Frame2中有一个滚动条,一次只显示5x5按钮 . 看起来Tkinter Frames不支持滚动条,因此添加了一个画布(嵌入了框架)和父框架'FMas'中的滚动条 . 但由于某种原因,滚动条会移到屏幕的右端,不会进行任何滚动 .

画布不应该在Frame2的边缘结束,滚动条就在它旁边吗?此外,我尝试使用rowspan来增加滚动条的高度以匹配5x5按钮的高度 . 那也行不通 .

enter image description here

代码(使用Python3.2):

from tkinter import *
import tkinter.ttk as ttk

mGui = Tk()

mGui.geometry("630x600")
mGui.configure(background="Gray")

mGui.columnconfigure(0, weight=1)
mGui.rowconfigure(0, weight=1)

FMas = Frame(mGui, bg="Gray")
FMas.grid(sticky=(N,E,S,W))

FMas.columnconfigure(0, weight=1)

L1 = Label(FMas, text="Frame 1 Contents")
L1.grid(row=0, column=0, pady=5, sticky=(N,W))

F1 = Frame(FMas, bg="Green", bd=2, relief=GROOVE)
F1.grid(row=1, column=0, sticky=(N,W))

ChkBox1=IntVar()
CB1 = Checkbutton(F1, text="StartCheckBox", variable=ChkBox1)
CB1.grid(row=0,column=0,padx=2)

L2 = Label(FMas, text="Frame 2 Contents")
L2.grid(row=2, column=0, pady=5, sticky=(N,W))

Can1 = Canvas(FMas, bg="Yellow")
Can1.grid(row=3, column=0, sticky=(N,W))

F2 = Frame(Can1, bg="Blue", bd=2, relief=GROOVE)
F2.grid(row=0, column=0, sticky=(N,W))

rows = 10
for i in range(1,rows):
    for j in range(1,6):
        button = Button(F2, padx=7, pady=7, text="[%d,%d]" % (i,j))
        button.grid(row=i, column=j, sticky='news')

vsbar = Scrollbar(FMas, orient="vertical", command=Can1.yview)
vsbar.grid(row=3, column=1)

Can1.configure(yscrollcommand=vsbar.set, scrollregion=Can1.bbox("all"))

L3 = Label(FMas, text="Frame 3 Contents")
L3.grid(row=4, column=0, pady=5, sticky=(N,W))

F3 = Frame(FMas, bg="Red", bd=2, relief=GROOVE)
F3.grid(row=5, column=0, sticky=(N,W))

ChkBox2=IntVar()
CB2 = Checkbutton(F3, text="EndCheckBox", variable=ChkBox2)
CB2.grid(row=0,column=0,padx=2)

mGui.mainloop()
sys.exit()

2 回答

  • 1

    滚动条的高度没有告诉它要坚持北方和南方 .grid(..., sticky='ns')

    然后,此处描述了您要实现的滚动行为:Adding a Scrollbar to a group of widgets

    另请参阅@ martineau的答案,了解更加通用的面向对象的2D滚动解决方案(水平和垂直)

    scrolling example

    import tkinter as tk
    
    root = tk.Tk()
    root.grid_rowconfigure(0, weight=1)
    root.columnconfigure(0, weight=1)
    
    frame_main = tk.Frame(root, bg="gray")
    frame_main.grid(sticky='news')
    
    label1 = tk.Label(frame_main, text="Label 1", fg="green")
    label1.grid(row=0, column=0, pady=(5, 0), sticky='nw')
    
    label2 = tk.Label(frame_main, text="Label 2", fg="blue")
    label2.grid(row=1, column=0, pady=(5, 0), sticky='nw')
    
    label3 = tk.Label(frame_main, text="Label 3", fg="red")
    label3.grid(row=3, column=0, pady=5, sticky='nw')
    
    # Create a frame for the canvas with non-zero row&column weights
    frame_canvas = tk.Frame(frame_main)
    frame_canvas.grid(row=2, column=0, pady=(5, 0), sticky='nw')
    frame_canvas.grid_rowconfigure(0, weight=1)
    frame_canvas.grid_columnconfigure(0, weight=1)
    # Set grid_propagate to False to allow 5-by-5 buttons resizing later
    frame_canvas.grid_propagate(False)
    
    # Add a canvas in that frame
    canvas = tk.Canvas(frame_canvas, bg="yellow")
    canvas.grid(row=0, column=0, sticky="news")
    
    # Link a scrollbar to the canvas
    vsb = tk.Scrollbar(frame_canvas, orient="vertical", command=canvas.yview)
    vsb.grid(row=0, column=1, sticky='ns')
    canvas.configure(yscrollcommand=vsb.set)
    
    # Create a frame to contain the buttons
    frame_buttons = tk.Frame(canvas, bg="blue")
    canvas.create_window((0, 0), window=frame_buttons, anchor='nw')
    
    # Add 9-by-5 buttons to the frame
    rows = 9
    columns = 5
    buttons = [[tk.Button() for j in xrange(columns)] for i in xrange(rows)]
    for i in range(0, rows):
        for j in range(0, columns):
            buttons[i][j] = tk.Button(frame_buttons, text=("%d,%d" % (i+1, j+1)))
            buttons[i][j].grid(row=i, column=j, sticky='news')
    
    # Update buttons frames idle tasks to let tkinter calculate buttons sizes
    frame_buttons.update_idletasks()
    
    # Resize the canvas frame to show exactly 5-by-5 buttons and the scrollbar
    first5columns_width = sum([buttons[0][j].winfo_width() for j in range(0, 5)])
    first5rows_height = sum([buttons[i][0].winfo_height() for i in range(0, 5)])
    frame_canvas.config(width=first5columns_width + vsb.winfo_width(),
                        height=first5rows_height)
    
    # Set the canvas scrolling region
    canvas.config(scrollregion=canvas.bbox("all"))
    
    # Launch the GUI
    root.mainloop()
    
  • 7

    虽然这是一个有点过时的问题,但这里有一个不同的答案,它不使用tkinter事件处理,这需要不必要的开销 .

    尽管代码是从OP 's, I' ve派生出来的,但是它更改了一些代码格式,因此它更符合PEP 8 - Style Guide for Python Code,这导致许多变量名称被更改 . 我还修改了体系结构,因此应用程序是根 tkinter.Tk 窗口窗口小部件类的子类 . 我做了这些事情,希望结果更容易理解,并为编写类似的基于_1378418的应用程序提供更好的模板 .

    就像@ Josselin的回答一样,它将 Canvas 和每个 Scrollbar 小部件嵌套在另一个 Frame 中,这使得它们可以使用 tkintergrid() 布局管理器轻松地在垂直和水平方向上并排放置 .

    代码也已扩展,因此网格也是一个水平滚动条,允许在该方向以及向上和向下滚动其内容 .

    import tkinter as tk
    
    LABEL_BG = "#ccc"  # Light gray.
    ROWS, COLS = 10, 6  # Size of grid.
    ROWS_DISP = 3  # Number of rows to display.
    COLS_DISP = 4  # Number of columns to display.
    
    class MyApp(tk.Tk):
        def __init__(self, title="Sample App", *args, **kwargs):
            tk.Tk.__init__(self, *args, **kwargs)
    
            self.title(title)
            self.configure(background="Gray")
            self.columnconfigure(0, weight=1)
            self.rowconfigure(0, weight=1)
    
            master_frame = tk.Frame(self, bg="Light Blue", bd=3, relief=tk.RIDGE)
            master_frame.grid(sticky=tk.NSEW)
            master_frame.columnconfigure(0, weight=1)
    
            label1 = tk.Label(master_frame, text="Frame1 Contents", bg=LABEL_BG)
            label1.grid(row=0, column=0, pady=5, sticky=tk.NW)
    
            frame1 = tk.Frame(master_frame, bg="Green", bd=2, relief=tk.GROOVE)
            frame1.grid(row=1, column=0, sticky=tk.NW)
    
            cb_var1 = tk.IntVar()
            checkbutton1 = tk.Checkbutton(frame1, text="StartCheckBox", variable=cb_var1)
            checkbutton1.grid(row=0, column=0, padx=2)
    
            label2 = tk.Label(master_frame, text="Frame2 Contents", bg=LABEL_BG)
            label2.grid(row=2, column=0, pady=5, sticky=tk.NW)
    
            # Create a frame for the canvas and scrollbar(s).
            frame2 = tk.Frame(master_frame)
            frame2.grid(row=3, column=0, sticky=tk.NW)
    
            # Add a canvas in that frame.
            canvas = tk.Canvas(frame2, bg="Yellow")
            canvas.grid(row=0, column=0)
    
            # Create a vertical scrollbar linked to the canvas.
            vsbar = tk.Scrollbar(frame2, orient=tk.VERTICAL, command=canvas.yview)
            vsbar.grid(row=0, column=1, sticky=tk.NS)
            canvas.configure(yscrollcommand=vsbar.set)
    
            # Create a horizontal scrollbar linked to the canvas.
            hsbar = tk.Scrollbar(frame2, orient=tk.HORIZONTAL, command=canvas.xview)
            hsbar.grid(row=1, column=0, sticky=tk.EW)
            canvas.configure(xscrollcommand=hsbar.set)
    
            # Create a frame on the canvas to contain the buttons.
            buttons_frame = tk.Frame(canvas, bg="Red", bd=2)
    
            # Add the buttons to the frame.
            for i in range(1, ROWS+1):
                for j in range(1, COLS+1):
                    button = tk.Button(buttons_frame, padx=7, pady=7, relief=tk.RIDGE,
                                       text="[%d, %d]" % (i, j))
                    button.grid(row=i, column=j, sticky='news')
    
            # Create canvas window to hold the buttons_frame.
            canvas.create_window((0,0), window=buttons_frame, anchor=tk.NW)
    
            buttons_frame.update_idletasks()  # Needed to make bbox info available.
            bbox = canvas.bbox(tk.ALL)  # Get bounding box of canvas with Buttons.
            #print('canvas.bbox(tk.ALL): {}'.format(bbox))
    
            # Define the scrollable region as entire canvas with only the desired
            # number of rows and columns displayed.
            w, h = bbox[2]-bbox[1], bbox[3]-bbox[1]
            dw, dh = int((w/COLS) * COLS_DISP), int((h/ROWS) * ROWS_DISP)
            canvas.configure(scrollregion=bbox, width=dw, height=dh)
    
            label3 = tk.Label(master_frame, text="Frame3 Contents", bg=LABEL_BG)
            label3.grid(row=4, column=0, pady=5, sticky=tk.NW)
    
            frame3 = tk.Frame(master_frame, bg="Blue", bd=2, relief=tk.GROOVE)
            frame3.grid(row=5, column=0, sticky=tk.NW)
    
            cb_var2 = tk.IntVar()
            checkbutton2 = tk.Checkbutton(frame3, text="EndCheckBox", variable=cb_var2)
            checkbutton2.grid(row=0, column=0, padx=2)
    
    
    if __name__ == "__main__":
        app = MyApp("Scrollable Canvas")
        app.mainloop()
    

    以下是它的运行情况:

    screenshot of what it looks like running

相关问题