尝试在 python 和 tkinter 中创建带有滚动条的小部件网格

问题描述 投票:0回答:1

我很困惑(正如你从我的代码中看到的那样,它目前是来自各种来源的大杂烩)。我正在尝试使用可滚动的小部件网格(当前为 tk.Entry's)在放大时填充框架。目前我只是想添加一个垂直滚动条,但我打算在了解它的工作原理后添加一个水平滚动条。

我在 HeaderCol 的二维数组中有两行标题。这是为了管理标题中的合并单元格,这或多或少是有效的。我的最终目标是拥有一个类似电子表格的界面,但通过按钮、下拉菜单和网格中的条目进行受控交互。

欢迎任何建议!

from tkinter import Menu, Entry, Canvas, Frame, Tk, Label, Scrollbar


    class HeaderCol:

    MAX_COL = 0
    HEADERS = [[]]

    def __init__(self, name, row, col, width=1, header_row=0):
        if row == 0:
            HeaderCol.MAX_COL += col
        self.name = name
        self.row = row
        self.col = col
        self.width = width
        if len(HeaderCol.HEADERS) <= header_row:
            HeaderCol.HEADERS.append([])
        HeaderCol.HEADERS[header_row].append(self)


class TransactionsGrid:
    # create the header information
    HEADER_TOP_ROW = 0
    HEADER_SECOND_ROW = 1
    col = 0
    ENTRY = HeaderCol('Entry', HEADER_TOP_ROW, col, 2)
    col += ENTRY.width
    DUE = HeaderCol('Due', HEADER_TOP_ROW, col, 4)
    col += DUE.width
    PAID = HeaderCol('Paid', HEADER_TOP_ROW, col, 7)
    col += PAID.width
    TENANT_STATUS = HeaderCol('Tenant Status', HEADER_TOP_ROW, col)
    col += TENANT_STATUS.width
    MANAGEMENT = HeaderCol('Management', HEADER_TOP_ROW, col, 2)
    col += MANAGEMENT.width
    NET = HeaderCol('Net', HEADER_TOP_ROW, col)
    col += NET.width
    NOTES = HeaderCol('Notes', HEADER_TOP_ROW, col)
    HeaderCol.MAX_COL = col + NOTES.width

    col = 0
    DATE = HeaderCol('Date', HEADER_SECOND_ROW, col, 1, 1)
    col += DATE.width
    EVENT = HeaderCol('Event', HEADER_SECOND_ROW, col, 1, 1)
    col += EVENT.width
    FEES = HeaderCol('Fees & Charges', HEADER_SECOND_ROW, col, 1, 1)
    col += FEES.width

    def __init__(self, root):
        root.grid_rowconfigure(0, weight=1)
        root.columnconfigure(0, weight=1)

        self.frame_main = Frame(root)
        self.frame_main.grid(sticky="news")

        self.frame_canvas = Frame(self.frame_main)
        self.frame_canvas.grid(row=2, column=0, pady=(5, 0), sticky='nw')
        self.frame_canvas.rowconfigure(0, weight=1)
        self.frame_canvas.columnconfigure(0, weight=1)
        # self.frame_canvas.grid_propagate(False)
        canvas = Canvas(self.frame_canvas, bg="yellow")
        canvas.grid(row=0, column=0, sticky="news")

        # Link a scrollbar to the canvas
        vsb = Scrollbar(self.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 = Frame(canvas, bg="blue")
        canvas.create_window((0, 0), window=frame_buttons, anchor='nw')
        self.frame_main.pack(fill='none', expand=False)
        canvas.place(relx=.5, rely=.5, anchor="center")
        self.buttons = []
        self.add_headers()

        # add a bunch of empty tk.Entry's
        for row in range(5):
            button_row = []

            for col in range(HeaderCol.MAX_COL):
                button = Entry(self.frame_canvas)
                button.configure(highlightthickness=0)
                button_row.append(button)
                if col == 0:
                    button.insert(0, 'A')

                button.grid(column=col, row=row + 1, sticky="")

            self.buttons.append(button_row)
        frame_buttons.update_idletasks()

    def add_headers(self):
        button_row = []

        for header in HeaderCol.HEADERS[0]:
            button = Label(self.frame_canvas)
            button_row.append(button)
            button.config(text=header.name)
            button.grid(column=header.col, row=0, columnspan=header.width, sticky="")

        # self.buttons.append(button_row)

        button_row = []

        for header in HeaderCol.HEADERS[1]:
            button = Label(self.frame_canvas)
            button_row.append(button)
            button.config(text=header.name)
            button.grid(column=header.col, row=1, columnspan=header.width, sticky="")

        # self.buttons.append(button_row)

    def menubar(self):
        menu = Menu(self.master)
        self.master.config(menu=menu)
        file_menu = Menu(menu)
        menu.add_cascade(label="File", menu=file_menu)

        edit_menu = Menu(menu)
        menu.add_cascade(label="Edit", menu=edit_menu)
        file_menu.add_command(label="Item")
        file_menu.add_command(label="Exit", command=self.exit_program)
        edit_menu.add_command(label="Undo")
        edit_menu.add_command(label="Redo")

    @staticmethod
    def exit_program():
        exit(0)


def main():
    root = Tk()
    # transaction_table = TransactionsTable(root)
    transaction_table = TransactionsGrid(root)
    #transaction_table = AnotherOne(root)

    """
    frame = tk.Frame(root)
    frame.pack()

    pt = Table(frame)
    pt.show()
    """
    root.mainloop()


if __name__ == '__main__':
    main()

感谢@Derek 的建议,我创建了以下带有可选水平滚动条的类。它或多或少起作用,但是当水平滚动时,它会移动一些小部件网格,但并不是所有小部件都显示在右侧:

self.frame_main = ScrollableFrame(root, horizontal=True)

这里是修改后的代码:

from tkinter import Frame, Scrollbar, Canvas
from tkinter.constants import RIGHT, Y, LEFT, BOTH, NW, BOTTOM, X

class ScrollableFrame(Frame):
    """
       Make a frame scrollable with scrollbar on the right.
       After adding or removing widgets to the scrollable frame,
       call the update() method to refresh the scrollable area.
    """

    def __init__(self, frame, width=16, horizontal=False):
        self.y_scrollbar = Scrollbar(frame, width=width)
        self.y_scrollbar.pack(side=RIGHT, fill=Y, expand=False)

        if horizontal:
            self.x_scrollbar = Scrollbar(frame, width=width, orient='horizontal')
            self.x_scrollbar.pack(side=BOTTOM, fill=X, expand=False)
            self.canvas = Canvas(frame,
                                 yscrollcommand=self.y_scrollbar.set,
                                 xscrollcommand=self.x_scrollbar.set)
        else:
            self.canvas = Canvas(frame, yscrollcommand=self.y_scrollbar.set)

        self.canvas.pack(side=LEFT, fill=BOTH, expand=True)

        if horizontal:
            self.canvas.pack(side=BOTTOM, fill=BOTH, expand=True)
            self.x_scrollbar.config(command=self.canvas.xview)

        self.y_scrollbar.config(command=self.canvas.yview)
        self.canvas.bind('<Configure>', self.__fill_canvas)

        # base class initialization
        Frame.__init__(self, frame)

        # assign this obj (the inner frame) to the windows item of the canvas
        self.windows_item = self.canvas.create_window(0, 0, window=self, anchor=NW)

    def __fill_canvas(self, event):
        """Enlarge the windows item to the canvas width"""
        canvas_width = event.width
        self.canvas.itemconfig(self.windows_item, width=canvas_width, height=event.height)

    def update(self):
        """Update the canvas and the scrollregion"""
        self.update_idletasks()
        self.canvas.config(scrollregion=self.canvas.bbox(self.windows_item))

这是我清理过的使用 ScrollableFrame 的代码:

def __init__(self, root):
    self.frame_main = ScrollableFrame(root, horizontal=True)
    self.buttons = [[]]
    self.add_headers()

    for row in range(25):
        button_row = []

        for col in range(HeaderCol.MAX_COL):
            button = Entry(self.frame_main)
            button.configure(highlightthickness=0)
            button_row.append(button)
            button.grid(column=col, row=row + 2, sticky="")

        self.buttons.append(button_row)
    self.frame_main.update()
python tkinter grid scrollbar
1个回答
0
投票

这应该会有所帮助,它将创建一个带有滚动条的

Entry
小部件列表。 我在相关地方插入了评论。 还有一些改进可能会有所帮助。

import tkinter as tk

root = tk.Tk()
root.rowconfigure(0, weight = 1)
root.columnconfigure(0, weight = 1)

container = tk.Frame(root)
container.grid(row = 0, column = 0, sticky = tk.NSEW)
container.rowconfigure(0, weight = 1)
container.columnconfigure(0, weight = 1)

# NOTE: yscrollincrement must be defined for smooth scroll effects
canvas = tk.Canvas(container, yscrollincrement = 19, highlightthickness = 0)
canvas.grid(row = 0, column = 0, sticky = tk.NSEW)

scrollbar = tk.Scrollbar(
   root, orient = tk.VERTICAL, width = 14, command = canvas.yview)
scrollbar.grid(row = 0, column = 1, sticky = tk.NS)
canvas.configure(yscrollcommand = scrollbar.set)

# frame contained in canvas
frame = tk.Frame(canvas)
# dynamically change canvas scrollregion
frame.bind(
   "<Configure>", lambda e: canvas.configure(scrollregion=canvas.bbox("all")))

# canvas window widget contains frame
canvas.create_window((0, 0), window = frame, anchor = tk.NW)
wide, high = 20, 22 # Set width, height of Entry widgets
for i in range(20):

   K = tk.Entry(frame, width = wide, textvariable = tk.StringVar())
   K.grid(row = i, column = 0, sticky = tk.NSEW)

# use access[n].get() to retrieve text from Entry widgets
access = frame.children

root.geometry(f"{wide*7}x{high*7}")
root.minsize(wide*7, high*7)

root.mainloop()
© www.soinside.com 2019 - 2024. All rights reserved.