1

I am trying to implement a flow layout in a ScrollableFrame from customtkinter. Specifically, instead of managing letters, the layout needs to manage small Frames in which names are written. I have to keep in mind that the size of the names and therefore the width of the small Frames can vary. For that reason, using grid() as usually recommended was not suitable and I preferred using place for more precision.

The main problem I encountered is that, although not specified, padding is added between the widgets and it seems related to the size of the last widget. Meaning that if the last widget was big because of a long name, the spacing between itself and the next widget is also going to be big. This is strange because the function I use sets the same constant padding between all widgets. Furthermore, the fact that additional padding is seen also seems to invalidate the calculations of the layout function (do_layout in the following example). This means that whilst the function 'thinks' there still is remaining space for another widget in the line, the additional padding is not taken into account. The last widget of a line is also only partially visible or not visible at all.

Here is a simplified version of my script:

import customtkinter as ctk

class PersonFrame(ctk.CTkFrame):
    # (I need to keep it as a Frame for future implementations)
    """ Frame that shows the name of the Registered Person """ 
    def __init__(self, master, name):
        super().__init__(master, fg_color="#444455", corner_radius=8)
        ctk.CTkLabel(self, text=name).pack(padx=10, pady=6)

class FlowFrame(ctk.CTkFrame):
    """ Frame responsible of the flow layout """
    def __init__(self, master, padx=0, pady=0): 
    # The problem persists even with paddings of 0
        super().__init__(master)
        self.padx = padx
        self.pady = pady
        self.widgets = []
        # Binding the function 'do_layout' to the scaling of the Frame
        self.bind("<Configure>", self.do_layout) 

    def add_widget(self, widget):
        self.widgets.append(widget)
        widget.place(in_=self)

    def do_layout(self, event=None):
        """ Calculates the height that the Frame should have
        and moves children widgets accordingly to a flow layout """
        max_width = self.winfo_width()
        if max_width <= 1: # handling of initial sizing
            return

        x = self.padx
        y = self.pady
        row_height = 0

        for widget in self.widgets:
            w = widget.winfo_reqwidth()
            h = widget.winfo_reqheight()

            # Expected behavior: widgets should wrap correctly 
            # with constant spacing of self.padx
            if x + w + self.padx > max_width:
                x = self.padx
                y += row_height + self.pady
                row_height = 0
            # place() is required here to manually implement a flow layout 
            # (pack and grid are not suitable)
            widget.place(x=x, y=y)
            x += w + self.padx
            row_height = max(row_height, h)

        self.configure(height=y + row_height + self.pady)

class RegisteredFrame(ctk.CTkScrollableFrame):
    """ Main display Frame containing the FlowFrame """
    def __init__(self, master):
        super().__init__(master)
        self.flow = FlowFrame(self)
        self.flow.pack(fill="x", expand=True)

        for name in (
            "LooooooooooooooongNaaaaame",
            "Name",
            "OtherName",
            "OtherVeryLongName",
        ):
            self.flow.add_widget(PersonFrame(self.flow, name))

class App(ctk.CTk):
    def __init__(self):
        super().__init__()
        self.geometry("600x400")
        self.title("Flow layout spacing issue")

        RegisteredFrame(self).pack(fill="both", expand=True)

if __name__ == "__main__":
    App().mainloop()

Here is an image of the result I get that illustrates the problem I am encountering: Result get when the code is run

We can see in the picture that the space between 'LoooongName' and 'Name' is far bigger than between 'Name' and 'OtherName'. Which is not intended because the padding 'self.padx' is constant in the script.

Here is also an image of the result I get when I scale the window in order to decrease it's width:

Result I get when the code is run and the window is scaled

Here we see that the last widget of the first line ('OtherName') wasn't moved to the second line yet altough it is nearly not visible anymore. As said above, I suppose this is because the do_layout function does not take this unexpected padding between the widgets into account and assumes there still is remaining space in the line. However, this could also be a different and unrelated issue.

I have tried checking if the width of the internal Canvas of the ScrollableFrame differed from the width of the FlowFrame but it didn't. I also tried checking if there was a difference between the self.winfo_width and the self.winfo_reqwidth of the PersonFrame but it also didn't.

9
  • It's not really clear to me what you mean with "padding is added between the widgets and it seems related to the size of the last widget". If I run your code, it looks like the desired output (juging from your code). It could be helpful if you add an image with some clarifications. One person even voted to close the question, cause they found it not reproducible. The difference of your code to this (dbs.cs.uni-duesseldorf.de/lehre/docs/java/javabuch/html/images/…) is that you don't take the "doubled padding" into accoutn Commented Dec 27, 2025 at 3:10
  • @Thingamabobs Thank you very much for your clarification advice. I have tried adding two pictures to better illustrate my problem. If it remains not reproducible to you, please let me know and i will try to adress it correctly. Regarding the example you gave me, I assume by double padding you mean setting a padding before and after each widget instead of just one like I do. Indeed, I did not do that because I found it unnecessary to the goal I was trying to achieve. Commented Dec 27, 2025 at 9:28
  • 1
    You simply spawn a new frame for each row, within you can pack them next to each other as you like. Commented Dec 27, 2025 at 17:00
  • 2
    A really simple way to get a flow layout with tkinter is to use a text widget as a container. It will wrap embedded widgets the same way that it wraps words. Commented Dec 27, 2025 at 18:05
  • 1
    @Thingamabobs: for a quick example, see this answer: stackoverflow.com/a/13076829/7432 Commented Dec 27, 2025 at 18:56

0

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.