简体   繁体   English

tkinter带网格的画布滚动条?

[英]tkinter Canvas Scrollbar with Grid?

Relatively new to Tkinter and Python. Tkinter和Python相对较新。 So kindly bear with me. 亲切地忍受我。

I am trying to display the following GUI and want to have a scrollbar in Frame2 to display only 5x5 buttons at a time. 我试图显示以下GUI,并希望在Frame2中有一个滚动条,一次只显示5x5按钮。 Looks like Tkinter Frames don't support scrollbar and hence added a canvas (within which the frame is embedded) and a scrollbar in the parent frame 'FMas'. 看起来Tkinter Frames不支持滚动条,因此添加了一个画布(嵌入了框架)和父框架'FMas'中的滚动条。 But for some reason the scroll bar goes to the right end of the screen and doesn't do any scrolling. 但由于某种原因,滚动条会移到屏幕的右端,不会进行任何滚动。

Shouldn't the canvas end at the edge of the Frame2 and the scroll bar be right next to it? 画布不应该在Frame2的边缘结束,滚动条就在它旁边吗? Also, I tried rowspan to increase the height of the scrollbar to match the height of 5x5 buttons. 此外,我尝试使用rowspan来增加滚动条的高度以匹配5x5按钮的高度。 That too doesn't work. 那也行不通。

在此输入图像描述

CODE (Using Python3.2): 代码(使用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()

The height of your scrollbar didn't match the buttons frame height because you did't tell it to stick North and South .grid(..., sticky='ns') 滚动条的高度与按钮框架高度不匹配,因为你没有告诉它坚持北和南.grid(..., sticky='ns')

Then, the scrolling behavior you want to achieve is described here: Adding a Scrollbar to a group of widgets 然后,此处描述了您要实现的滚动行为: 将滚动条添加到一组窗口小部件

See also @ martineau 's answer for a more general object-oriented solution with 2D scrolling (horizontal & vertical) 另请参阅@ martineau的答案,了解更加通用的面向对象的2D滚动解决方案(水平和垂直)

滚动示例

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()

Although this is a somewhat dated question, here's a different answer which doesn't use tkinter event handling thereby avoiding the unnecessary overhead it requires. 虽然这是一个有点过时的问题,但这里有一个不同的答案,它不使用tkinter事件处理,从而避免了它所需的不必要的开销。

Although the code is derived from the OP's, I've made a number of code formatting changes so it conforms better to the PEP 8 - Style Guide for Python Code which resulted in many variable names being changed. 尽管代码是从OP派生的,但我已经进行了大量的代码格式更改,因此它更符合PEP 8 - Python Code样式指南,这导致许多变量名称被更改。 I've also modified the architecture so the application is a subclass of the root tkinter.Tk window widget class. 我还修改了体系结构,因此应用程序是root tkinter.Tk窗口窗口小部件类的子类。 I did these things with the hope that the results will be more understandable and provide a better template for writing similar tkinter -based applications. 我做了这些事情,希望结果更容易理解,并为编写类似tkinter的应用程序提供更好的模板。

Like @ Josselin 's answer, it nests the Canvas and each of the Scrollbar widgets inside another Frame which allows the them to easily be positioned alongside one another both vertically and horizontally using tkinter 's grid() layout manager. 就像@Josselin的回答一样,它将Canvas和每个Scrollbar小部件嵌套在另一个Frame ,这使得它们可以使用tkintergrid()布局管理器轻松地在垂直和水平方向上并排放置。

The code has been further extended so the grid also has a horizontal scrollbar allowing scrolling of its contents in that direction as well as vertically. 代码已进一步扩展,因此网格还具有水平滚动条,允许在该方向和垂直方向上滚动其内容。

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()

Here's what it looks like running: 以下是它的运行情况:

它看起来像运行的截图

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM