简体   繁体   English

Tkinter Grid 动态布局

[英]Tkinter Grid Dynamic Layout

I am wanting to create a grid layout, with a grid that fills the first row until it runs out of space in the window, and will dynamically move items to the row below (like text line-wrapping).我想创建一个网格布局,用一个网格填充第一行,直到它在窗口中用完空间,并将项目动态移动到下面的行(如文本换行)。 As the window width is adjusted, the grid adjusts to fit.随着窗口宽度的调整,网格也会调整以适应。 The boxes resizing is not desired.不需要调整大小的框。 I intend to maintain each small box's size, but change where the layout puts each box.我打算保持每个小盒子的大小,但改变布局放置每个盒子的位置。

I imagine this functionality is possible by measuring the width of the frame, and if the (number of boxes)*(width of each box) exceeds the width, move to the next row.我想通过测量框架的宽度可以实现此功能,如果(框数)*(每个框的宽度)超过宽度,则移至下一行。 I was just wondering if there was a better way built in that I'm not understanding.我只是想知道是否有更好的方法内置我不理解。

If the above is the only option, what is the best way to update that?如果以上是唯一的选择,那么更新它的最佳方法是什么? Do I have to set an event on window resize or something?我是否必须在窗口调整大小或其他方面设置事件? It seems like I shouldn't have to rework a layout manager, which is what that feels like.看起来我不应该重新设计布局管理器,这就是感觉。 I just want to check if similar functionality is already built in. Grid seems like a powerful layout manager, but I have not been able to find that option.我只是想检查是否已经内置了类似的功能。网格看起来像一个强大的布局管理器,但我一直找不到那个选项。

The below pics describes the behavior I want using the same set of 6 boxes on a single frame using grid layout.下面的图片描述了我想要使用网格布局在单个框架上使用相同的一组 6 个框的行为。

Window is wide enough to hold all 6 boxes, so they all fit on row 1. They then adjust as window size changes.窗口的宽度足以容纳所有 6 个框,因此它们都适合第 1 行。然后它们会随着窗口大小的变化而调整。 在此处输入图片说明

在此处输入图片说明 在此处输入图片说明

If you plan on forcing each box to be a uniform size, the simplest solution is to use the text widget as the container since it has the built-in ability to wrap.如果您计划强制每个框具有统一的大小,最简单的解决方案是使用文本小部件作为容器,因为它具有内置的换行能力。

Here is a working example.这是一个工作示例。 Click on the "add" button to add additional boxes.单击“添加”按钮以添加其他框。 Resize the window to see that they automatically wrap as the window grows and shrinks.调整窗口大小以查看它们随着窗口的增长和收缩而自动换行。

import Tkinter as tk
import random

class DynamicGrid(tk.Frame):
    def __init__(self, parent, *args, **kwargs):
        tk.Frame.__init__(self, parent, *args, **kwargs)
        self.text = tk.Text(self, wrap="char", borderwidth=0, highlightthickness=0,
                            state="disabled")
        self.text.pack(fill="both", expand=True)
        self.boxes = []

    def add_box(self, color=None):
        bg = color if color else random.choice(("red", "orange", "green", "blue", "violet"))
        box = tk.Frame(self.text, bd=1, relief="sunken", background=bg,
                       width=100, height=100)
        self.boxes.append(box)
        self.text.configure(state="normal")
        self.text.window_create("end", window=box)
        self.text.configure(state="disabled")

class Example(object):
    def __init__(self):
        self.root = tk.Tk()
        self.dg = DynamicGrid(self.root, width=500, height=200)
        add_button  = tk.Button(self.root, text="Add", command=self.dg.add_box)

        add_button.pack()
        self.dg.pack(side="top", fill="both", expand=True)

        # add a few boxes to start
        for i in range(10):
            self.dg.add_box()

    def start(self):
        self.root.mainloop()

Example().start()

Here's a working example:这是一个工作示例:

import Tkinter as tk

class AutoGrid(tk.Frame):
    def __init__(self, master=None, **kwargs):
        tk.Frame.__init__(self, master, **kwargs)
        self.columns = None
        self.bind('<Configure>', self.regrid)

    def regrid(self, event=None):
        width = self.winfo_width()
        slaves = self.grid_slaves()
        max_width = max(slave.winfo_width() for slave in slaves)
        cols = width // max_width
        if cols == self.columns: # if the column number has not changed, abort
            return
        for i, slave in enumerate(slaves):
            slave.grid_forget()
            slave.grid(row=i//cols, column=i%cols)
        self.columns = cols

class TestFrame(tk.Frame):
    def __init__(self, master=None, **kwargs):
        tk.Frame.__init__(self, master, bd=5, relief=tk.RAISED, **kwargs)

        tk.Label(self, text="name").pack(pady=10)
        tk.Label(self, text=" info ........ info ").pack(pady=10)
        tk.Label(self, text="data\n"*5).pack(pady=10)

def main():
    root = tk.Tk()
    frame = AutoGrid(root)
    frame.pack(fill=tk.BOTH, expand=True)

    TestFrame(frame).grid() # use normal grid parameters to set up initial layout
    TestFrame(frame).grid(column=1)
    TestFrame(frame).grid(column=2)
    TestFrame(frame).grid()
    TestFrame(frame).grid()
    TestFrame(frame).grid()
    root.mainloop()

if __name__ == '__main__':
    main()

Note this will ruin the rowspan and columnspan features of the grid manager.请注意,这会破坏网格管理器的 rowspan 和 columnspan 功能。

Here's a streamlined version of Bryan's answer without classes and a few extra comments for anyone who is confused and is trying to implement this quickly into their own project.这是 Bryan 答案的简化版本,没有课程,还有一些额外的评论,供那些感到困惑并试图将其快速实施到他们自己的项目中的人使用。

from tkinter import *
import tkinter as tk

#Create main window
root = tk.Tk()

#Create WidgetWrapper
widgetWrapper = tk.Text(root, wrap="char", borderwidth=0,highlightthickness=0,state="disabled", cursor="arrow") 
#state = "disabled" is to disable text from being input by user
#cursor = "arrow" is to ensure when user hovers, the "I" beam cursor (text cursor) is not displayed

widgetWrapper.pack(fill="both", expand=True)

def additem():
    item = Label(bd = 5, relief="solid", text="O", bg="red") #Create the actual widgets
    widgetWrapper.window_create("end", window=item) #Put it inside the widget wrapper (the text)

# add a few boxes to start
for i in range(10):
    additem()

#Not needed to implement in other code, just an add button
add_button  = tk.Button(root, text="Add", command=additem)
add_button.pack()

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

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