How to make Tkinter columns of equal width when widgets span multiple columns?
Asked Answered
R

3

5

In the following, the buttons labelled 'ONE', 'TWO', and 'THR' do not get evenly spaced out. It seems to me that the root of the problem is that Tk is assuming a default minimum width for any column containing part of a widget that spans multiple columns. However, this behaviour appears to be undocumented, so I am unsure how to accommodate for or adjust it in order to get the columns to be of equal width - including the two columns spanned by the text widget and the single column not spanned by the text widget - and thus space out the buttons evenly. I could kludge it by trial and error, i.e. padding out the latter column until it matches the former two, but that seems a poor solution.

Edit: Following discussion below with @jwillis0720, I've added an extra column (3) and button ('FIV') to make the problem clearer. This question is about how to get columns the same width when some of those columns are spanned by multi-column widgets and others are not.

import Tkinter

master = Tkinter.Tk()

Tkinter.Button(master, text='ONE').grid(row=0, column=0)
Tkinter.Button(master, text='TWO').grid(row=0, column=1)
Tkinter.Button(master, text='THR').grid(row=0, column=2)
Tkinter.Button(master, text='FOU').grid(row=1, column=2)
Tkinter.Button(master, text='FIV').grid(row=0, column=3) # added as per above edit
Tkinter.Text(master).grid(row=1, column=0, columnspan=2)

master.mainloop()

Please note that using grid_columnconfigure with uniform does not solve this problem. Inserting the following lines (see answer to similar question here: How to create equal-width grid columns with Tkinter?) simply makes the columns stretchy; they remain unevenly sized:

master.grid_columnconfigure(0, weight=1, uniform='a')
master.grid_columnconfigure(1, weight=1, uniform='a')
master.grid_columnconfigure(2, weight=1, uniform='a')
master.grid_columnconfigure(3, weight=1, uniform='a') # added as per above edit
Retarded answered 9/1, 2014 at 0:7 Comment(7)
In addition to my answer, I know it seems like a hassle at first, but if you continue to build complicated gui apps with tkinter, consider putting things in classes and embedding lots of frames. For instance, I would of 'watched' how much geometry that tkinter laid out by turning those buttons into frames and then coloring them. Then I would of put the buttons inside of the frames.Cruiserweight
@Cruiserweight Doesn't help as much as you might think; enforcing equal visual column widths without keeping everything in a single grid with actual uniform groups is really hard. That was why we added uniform groups in the first place…Leucomaine
@Cruiserweight Thanks for the tip about coloured frames - I'll experiment with that. For the sake of this question, though, I'm concerned to understand the general issues, i.e. the way in which columnspan relates to column width in Tkinter's grid layout algorithms, and how this might be changed or compensated for without a trial and error process of experimenting with different layout parameters.Retarded
@Retarded It looks like its just because the text widget takes up so much more space than the button widgets. So it pushes the grid they are in over to the right and makes it appear squished. I think this is just a consequence of the grid manager.Cruiserweight
For instance, check out what happens if you just replace the txt box with another button widget. Everything is cool.Cruiserweight
@Cruiserweight Thanks very much! You're right - the problem is not that (as I thought) that there is a minimum width for a column containing part of a multi-column widget, but that there is an assumed minimum width both (a) for a text widget and (b) for any part of a text widget. I haven't managed to find any documentation explaining what this minimum width is; I probably need to look in the source code. I'll try to edit my question later so that this can be the answer.Retarded
Yes, you will probably have to find it in the actual tcl documentation, which is well above my head.Cruiserweight
C
5

I think you might want to use the sticky option.

sticky= Defines how to expand the widget if the resulting cell is larger than the widget itself. This can be any combination of the constants S, N, E, and W, or NW, NE, SW, and SE.

For example, W (west) means that the widget should be aligned to the left cell border. W+E means that the widget should be stretched horizontally to fill the whole cell. W+E+N+S means that the widget should be expanded in both directions. Default is to center the widget in the cell.

import Tkinter

master = Tkinter.Tk()

Tkinter.Button(master, text='ONE').grid(row=0, column=0, sticky='NW')
Tkinter.Button(master, text='TWO').grid(row=0, column=1, sticky='NW')
Tkinter.Button(master, text='THR').grid(row=0, column=2, sticky='NW')
Tkinter.Button(master, text='FOU').grid(row=1, column=2)
Tkinter.Text(master).grid(row=1, column=0, columnspan=2)


master.mainloop()

Edit

What does it look like. Mine looks like this evenly spaced except the text widget takes up two columns as specified.

Does it look like this

Cruiserweight answered 9/1, 2014 at 14:21 Comment(6)
A sticky of ew is likely to be highly relevant in this case.Leucomaine
I just used NW as as an example as they will all line up, which I believe was the only stipulation in the question.Cruiserweight
Maybe, but it's usually best to take a guess at what they're really after and give them that. Just sayin'…Leucomaine
Thanks for this answer. Please note that I didn't write that I wanted the buttons to 'line up', but that I wanted them to be 'evenly spaced out' and 'to get the columns to be of equal width'. I may be doing something wrong, but the above code does not appear to achieve this when I run it. Column 2 is still much narrower than columns 0 and 1, and consequently the buttons are not evenly spaced out. 'Evenly spaced out' was a rather ambiguous phrase, so I'm sorry if this has confused the issue - but question itself says 'columns of equal width', and this is what I'm really concerned with.Retarded
Thanks for the above edit; now I understand. That's what it looks like for me too. As I understand it, the even spacing problem is solved by the sticky='NW' option in the above only because the different-sized column is the last one on the right. However, my question is about how to make all columns the same width, so what your solution fixes is an effect of the problem rather than the problem itself (and it does so with regard to my exact example; it wouldn't work for other layouts). I'm sorry for confusing matters by expanding on the question with the red herring about even spacing.Retarded
I would seriously consider learning packing geometry manager which will solve all this for you. You can always mix them both. Consider just posting your code and someone can take a look at it for youCruiserweight
A
3

old post I know, but I also struggled to get the columns and rows to maintain a common width/height so I thought I would share my solution

newish to python and tkinter, so if there are any mistakes please let me know

I created a grid manager, this allowed the main window and any frame to be setup with evenly spaced columns and rows, it's not 100% but for what I was using it for it worked well, it was especially useful during the building phase

one downside when creating a frame is the maximum number of rows/columns of the frame must be equal to or less than the number or rows/columns it is spanning, otherwise it goes a bit weird (nut sure why)

hope this helps

import tkinter

class grid_manager:
    def __init__(self, Frame, colour = "gray94"):
        self.Frame = Frame
        self.Colour = colour

    def set_grid(self, numofRows, numofColumns, borderwidth = 1):
        self.numofRows = numofRows
        self.numofColumns = numofColumns
        self.borderwidth = borderwidth
        for i in range(numofRows):
            for j in range(numofColumns):
                canvas = tkinter.Canvas(self.Frame)
                canvas.config(relief="raised", borderwidth=self.borderwidth)   #comment out to hide grid layout
                canvas.grid(row=i, column=j)
                canvas.config(background=self.Colour)
                self.Frame.columnconfigure(j, weight=1)
            self.Frame.rowconfigure(i, weight=1)

mainwindow = tkinter.Tk()

mainwindow.title("Test")
mainwindow.geometry("640x480-8-200")
mainGrid = grid_manager(mainwindow)
mainGrid.set_grid(10, 10)

header_Frame = tkinter.Frame(mainwindow)
header_Frame.grid(row=0, column=0, columnspan=10, sticky="nsew")
headerGrid = grid_manager(header_Frame)
headerGrid.set_grid(numofRows=1, numofColumns=10, borderwidth=5)

footerFrame = tkinter.Frame(mainwindow)
footerFrame.grid(row=9, column=0, columnspan=10, sticky="nsew")
footerGrid = grid_manager(footerFrame, "red")
footerGrid.set_grid(numofRows=1, numofColumns=10, borderwidth=5)

rightFrame = tkinter.Frame(mainwindow)
rightFrame.grid(row=1, column=5, rowspan=5, columnspan=5, sticky="nsew")
rightGrid = grid_manager(rightFrame, "blue")
rightGrid.set_grid(numofRows=5, numofColumns=5, borderwidth=2)

leftFrame = tkinter.Frame(mainwindow)
leftFrame.grid(row=3, column=0, rowspan=5, columnspan=4, sticky="nsew")
leftGrid = grid_manager(leftFrame, "yellow")
leftGrid.set_grid(numofRows=5, numofColumns=4, borderwidth=2)

mainwindow.mainloop()

enter image description here

Amusement answered 29/11, 2018 at 15:3 Comment(0)
S
-3
import tkinter

master = tkinter.Tk()

tkinter.Button(master, text='ONE                ').grid(row=0, column=3, sticky='NW')
tkinter.Button(master, text='TWO               ').grid(row=1, column=3, sticky='NW')
tkinter.Button(master, text='THR                ').grid(row=2, column=3, sticky='NW')
tkinter.Button(master, text='FOU                ').grid(row=3, column=3, sticky='NW')
tkinter.Text(master).grid(column=30, columnspan=10)
Screening answered 18/7, 2016 at 7:11 Comment(2)
Isn't this really the same as @jwillis0720's answer, above? Could you include some written explanation of how it is different?Retarded
This is NAA as it has the exact same code as the above answer, which was posted few years agoPerseus

© 2022 - 2024 — McMap. All rights reserved.