tkinter带网格的画布滚动条?

时间:2017-05-02 06:50:06

标签: python canvas tkinter grid scrollbar

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 个答案:

答案 0 :(得分:10)

滚动条的高度与按钮框架高度不匹配,因为您没有告诉它坚持南北.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()

答案 1 :(得分:1)

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

尽管代码源自OP,但我已经进行了大量的代码格式更改,因此它更符合PEP 8 - Style Guide for Python Code,导致许多变量名称被更改。我还修改了体系结构,因此应用程序是根tkinter.Tk窗口窗口小部件类的子类。我做了这些事情,希望结果更容易理解,并为编写类似tkinter的类似应用程序提供更好的模板。

与@ Josselin的答案一样,它将Canvas和每个Scrollbar小部件嵌套在另一个Frame内,这样可以轻松地将它们放在一起垂直和水平使用tkinter的{​​{1}}布局管理器。

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

grid()

以下是它的运行情况:

screenshot of what it looks like running