tkinter.Frame.Grid sizing not displaying correctly

不羁的心 提交于 2020-04-18 06:07:02

问题


Hello I am making a table with a header for text to name the columns, cells to store data, and scrolling.

The problem I'm having is that my Table is not displaying correctly. What should happen is the Headers should display above with no other space below them. (other than the small padding I added) The cells display correctly right below the headers.

The scrolling works for the cells in the y direction. The scrolling also works in the x direction for both the cells and headers.

The function the adds the cells to the frame simply does a create then add using grid(row, column) The header only has 1 row so the blank space should not be there.

import tkinter as tk
import collections
from enum import Enum

window = tk.Tk() # Root (main) window

def main():
    window.title('Table')
    window.geometry("1024x600")
    window.update_idletasks()
    table_frame = tk.Frame(window, background='black')
    table_frame.pack(fill='x')
    table = Table(table_frame, 30, 15)
    print(id(table))

    window.mainloop()

class Table:

    def __init__(self, frame, rowCount, columnCount):
        self._rowCount = rowCount
        self._columnCount = columnCount

        main_frame = tk.Frame(frame, bg='blue')
        main_frame.pack(fill='both')

        self._headerCanvas = tk.Canvas(main_frame)
        self._headerCanvas.grid(row=0, column=0, pady=1, sticky='ew')

        self._cellCanvas = tk.Canvas(main_frame)
        self._cellCanvas.grid(row=1, column=0, sticky='ew')

        scroll_bar_y = tk.Scrollbar(main_frame, orient=tk.VERTICAL, command=self._cellCanvas.yview)
        scroll_bar_y.grid(row=1, column=1, padx=1, sticky='ns')

        scroll_bar_x = tk.Scrollbar(main_frame, orient=tk.HORIZONTAL, command=self.xViewScroll)
        scroll_bar_x.grid(row=2, column=0, pady=1, sticky='ew')

        main_frame.grid_columnconfigure(0, weight=1)
        main_frame.grid_rowconfigure(1, weight=1)

        self._cellCanvas.configure(xscrollcommand=scroll_bar_x.set, yscrollcommand=scroll_bar_y.set) #, width=(main_frame.winfo_width()-scroll_bar_y.winfo_width()))

        header_frame = tk.Frame(self._headerCanvas)
        self._headers = Table.ColumnHeaders(header_frame, self._columnCount)
        cell_frame = tk.Frame(self._cellCanvas)
        self._cells = Table.Cells(cell_frame, self._rowCount, self._columnCount)

        self._headerCanvas.create_window(0, 0, window=header_frame, anchor='nw')
        self._headerCanvas.update_idletasks()
        self._cellCanvas.create_window(0, 0, window=cell_frame, anchor='nw')
        self._cellCanvas.update_idletasks()
        self._headerCanvas.configure(scrollregion=self._cellCanvas.bbox("all"))
        self._cellCanvas.configure(scrollregion=self._cellCanvas.bbox("all"))

    def xViewScroll(self, *args):
        self._headerCanvas.xview(*args)
        self._cellCanvas.xview(*args)


    class Cells:

        class Types(Enum):
            Entry = 0
            Button = 1

        class Cell:
            def __init__(self, widget, text=''):
                self._text = text
                self._widget = widget


            def getWidget(self):
                return self._widget

            def setWidget(self, widget):
                self._widget = widget

            widget = property(getWidget, setWidget, "Get and set the widget of a cell.")


        def __init__(self, frame, rows, columns, cellTypes=Types.Entry):
            self._cells = [[],[]]

            for r in range(rows):
                self._cells.append([])
                for c in range(columns):
                    self._cells[r].append(c)
                    if cellTypes == Table.Cells.Types.Entry:
                        self._cells[r][c] = Table.Cells.Cell(tk.Entry(frame, width=15))
                    elif cellTypes == Table.Cells.Types.Button:
                        self._cells[r][c] = Table.Cells.Cell(tk.Button(frame, width=12))

                    self._cells[r][c].widget.grid(row=r, column=c)


        def getCell(self, row, column):
            return self._cells[row][column]

        def setCell(self, row, column, cell):
            self._cells[row][column] = cell

        cells = property(getCell, setCell, "Get and set a cell in the table.")


    class ColumnHeaders:
        def __init__(self, widget, columnCount):
            self._widget = widget
            self._columnCount = columnCount
            self._headers = Table.Cells(self._widget, 1, self._columnCount, cellTypes=Table.Cells.Types.Button)
main()

This is how they are currently showing up.

After re sizing the screen this is what the original should look like. With the headers just above the cells below with only the padding space.

If I shrink it a little more it causes another problem the scrollbar and the headers disappear from view. The scrollbar on the right never disappears however. ( I'm guessing this is because I can't shrink the screen anymore.

This is what happens with main_frame.grid_rowconfigure(1, weight=1) It causes this after resizes to a smaller window.


回答1:


Question: Frame Grid sizing not displaying correctly

You are misguided, the header_frame is not layouted using .grid(...).
Using .create_window(... add the widget to the Canvas at the fixed position 0, 0, anchor='nw'. Therefore, no "Grid Layout Manager" magic, like auto resizing, happens.


Why do you get this Layout?
Note: main_frame == 'blue', _headerCanvas == 'lightgreen', _cellCanvas == 'red':


Layout: A                             Layout: B                           Layout: C                             Layout: D

        self._headerCanvas = tk.Canvas(main_frame, bg='lightgreen')
        self._headerCanvas.grid(row=0, column=0, pady=1, sticky='ew')

        main_frame.grid_rowconfigure(1, weight=1)

        self._cellCanvas = tk.Canvas(main_frame, bg='red')
        self._cellCanvas.grid(row=1, column=0, sticky='ew')

Layout: A: You create both Canvas with the default height. Therefore you get a similar layout using "The Grid Layout Manager".

Layout: B: Adding itmes to a Canvas, here the Cells Frame, does not change anything at the Layout.


Layout: C: Sync the Canvas height with the header_frame height results in.

  1. Using .bind('<Configure>'

    header_frame = tk.Frame(self._headerCanvas)
    header_frame.bind('<Configure>', self.on_configure)
    
    def on_configure(self, event):
        # Sync _headerCanvas height with header_frame height
        w = event.widget
        self._headerCanvas.configure(height=w.winfo_height())
    
  2. Using .update_idletask()

        header_frame = tk.Frame(self._headerCanvas)
        self._headers = Table.ColumnHeaders(header_frame, self._columnCount)
        root.update_idletasks()
        self._headerCanvas.configure(height=header_frame.winfo_height())
    

Still not what you want?
Layout: D: You have allowed the .grid(row=1 to grow as much as possible by doing:

    main_frame.grid_rowconfigure(1, weight=1)

But you don't allow the Canvas to grow using sticky='ns'. This result in the 'blue' space above/bottom. Change to sticky='nsew'.



来源:https://stackoverflow.com/questions/59295469/tkinter-frame-grid-sizing-not-displaying-correctly

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!