tkinter липкий не работает для некоторых фреймов

Я использую tkinter для написания карточной игры, и у меня проблемы с «липкой» конфигурацией диспетчера компоновки сетки. Я хотел бы помочь исправить мой код, чтобы фреймы отображались в желаемом месте. В моем коде и иллюстрации ниже есть кадр (b2), который содержит два других (один зеленый, b2a; и один красный; b2b) кадра. Я хотел бы отобразить фрейм b2 внизу родительского фрейма (фрейм b). Я пробовал различные комбинации N + S + E + W в качестве аргументов для «липкости» как для кадра b2, так и для дочерних кадров b2a и b2b. Однако мне не удалось заставить кадр b2 (и, что более важно, b2a и b2b) отображаться в желаемом месте (нижнее изображение ниже с правильным размещением было сделано в Illustrator).

В частности, кажется, что липкие аргументы в строках 27, 36 и 37 не влияют на размещение кадра b2, b2a и b2b внутри кадра b.

from tkinter import *
from PIL import Image, ImageTk

def main(root):
    cons = Frame(root)
    cons.grid()

    frameDict = setup_frames(cons)
    populate_frames(frameDict)

def setup_frames(cons):
    frame = {}
    # Parental Frames
    frame['a'] = Frame(cons, borderwidth=2, relief='groove')
    frame['b'] = Frame(cons, borderwidth=2, relief='groove')
    frame['c'] = Frame(cons, borderwidth=2, relief='groove')

    frame['a'].grid(row=0, column=0, sticky=N+S+E+W)
    frame['b'].grid(row=0, column=1, sticky=N+S+E+W)
    frame['c'].grid(row=1, column=0, columnspan=2)

    # Progeny 0 Frames:
    frame['b1'] = Frame(frame['b'], borderwidth=2, relief='groove')
    frame['b2'] = Frame(frame['b'], borderwidth=2, relief='groove')

    frame['b1'].grid(row=0, column=0, sticky=N+S+E+W)
    frame['b2'].grid(row=1, column=0, sticky=N+S+E+W)

    # Progeny 1 Frames:

    frame['b2a'] = Frame(frame['b2'], borderwidth=2, relief='groove',
                         background='green')
    frame['b2b'] = Frame(frame['b2'], borderwidth=2, relief='groove',
                         background='red')

    frame['b2a'].grid(row=0, column=0, sticky=S)
    frame['b2b'].grid(row=0, column=1, sticky=SW)

    return frame

def populate_frames(fr):

    # Populating 'a' frame
    aLab = Label(fr['a'], image=img[0])
    aLab.grid()

    # Populating b2a & b2b frames
    bLab = Label(fr['b2a'], image=img[1])
    bLab.grid(row=0, column=0)

    bLab = Label(fr['b2b'], image=img[2])
    bLab.grid(row=0, column=1)

    # Populating c1 frame
    cLab = Label(fr['c'], image=img[3])
    cLab.grid()

if __name__ == '__main__':
    root = Tk()
    img = []
    w = [40,  160, 80, 480]
    h = [180, 60,  60, 60]
    for i in range(4):
        a = Image.new('RGBA', (w[i], h[i]))
        b = ImageTk.PhotoImage(a)
        img.append(b)
    main(root)

На изображениях ниже показано, где отображаются неправильные кадры (зеленый и красный) (вверху) и где я бы хотел, чтобы они отображались (внизу).

Может ли кто-нибудь помочь мне отобразить кадр b2 (и, в конечном итоге, b2a и b2b) в правильном положении (редактировать: внизу кадра b и от правой стороны кадра a до правой стороны кадра c)?

иллюстрация неправильного и правильного размещения фрейма

Обновление: я решил обе проблемы (вертикальное размещение и горизонтальное выравнивание кадра b2), используя веса сетки, как предложил Брайан. Решение проблемы вертикального размещения простое, но я бы не предсказал решение проблемы горизонтального выравнивания.

Я решил проблему вертикального размещения, присвоив вес = 1 строке 0 в кадре b (в результате получилась верхняя панель рисунка ниже).

Я решил проблему горизонтального выравнивания (где кадры b1 и b2 не растягивались, чтобы заполнить кадр b), присвоив вес = 1 столбцу 0 в кадре b. Контуры рамки на рисунке ниже показывают, что рамка b уже растянута от правой стороны рамки a до правой стороны рамки c. Мне странно, что для того, чтобы дочерние фреймы могли заполняться по горизонтали, потребовалось бы присвоить вес единственному столбцу во фрейме. В любом случае, я вставил свой рабочий код ниже. Строки 40 и 41 решили мою проблему.

from tkinter import *
from PIL import Image, ImageTk

def main(root):
    cons = Frame(root)
    cons.grid()

    frameDict = setup_frames(cons)
    populate_frames(frameDict)

def setup_frames(cons):
    frame = {}
    # Parental Frames
    frame['a'] = Frame(cons, borderwidth=2, relief='groove')
    frame['b'] = Frame(cons, borderwidth=2, relief='groove')
    frame['c'] = Frame(cons, borderwidth=2, relief='groove')

    frame['a'].grid(row=0, column=0, sticky=N+S+E+W)
    frame['b'].grid(row=0, column=1, sticky=N+S+E+W)
    frame['c'].grid(row=1, column=0, columnspan=2)

    # Progeny 0 Frames:
    frame['b1'] = Frame(frame['b'], borderwidth=2, relief='groove')
    frame['b2'] = Frame(frame['b'], borderwidth=2, relief='groove')

    frame['b1'].grid(row=0, column=0, sticky=N+S+E+W)
    frame['b2'].grid(row=1, column=0, sticky=N+S+E+W)

    # Progeny 1 Frames:

    frame['b2a'] = Frame(frame['b2'], borderwidth=2, relief='groove',
                         background='green')
    frame['b2b'] = Frame(frame['b2'], borderwidth=2, relief='groove',
                         background='red')

    frame['b2a'].grid(row=0, column=0, sticky=S)
    frame['b2b'].grid(row=0, column=1, sticky=SW)

    # Weighting
    frame['b'].grid_rowconfigure(0, weight=1)
    frame['b'].grid_columnconfigure(0, weight=1)

    return frame

def populate_frames(fr):

    # Populating 'a' frame
    aLab = Label(fr['a'], image=img[0])
    aLab.grid()

    # Populating b2a & b2b frames
    bLab = Label(fr['b2a'], image=img[1])
    bLab.grid(row=0, column=0)

    bLab = Label(fr['b2b'], image=img[2])
    bLab.grid(row=0, column=1)

    # Populating c1 frame
    cLab = Label(fr['c'], image=img[3])
    cLab.grid()

if __name__ == '__main__':
    root = Tk()
    img = []
    w = [40,  160, 80, 480]
    h = [180, 60,  60, 60]
    for i in range(4):
        a = Image.new('RGBA', (w[i], h[i]))
        b = ImageTk.PhotoImage(a)
        img.append(b)
    main(root)

В соответствии с советом Брайана, на самом деле кажется хорошим практическим правилом назначать вес по крайней мере одному столбцу и одной строке в каждом контейнере.

Вот до и после того, как я исправил проблему горизонтального выравнивания:

до и после горизонтального выравнивания

Использование Python 3.4, Йосемити


person mRotten    schedule 05.08.2015    source источник
comment
grid (row = 0) = вверху См. раздел Обработка изменения размера и следующие примеры на странице tkdocs.com/ tutorial / grid.html   -  person    schedule 06.08.2015


Ответы (1)


Вы должны присвоить некоторым строкам и столбцам вес, чтобы tkinter знал, как выделить дополнительное пространство.

Как правило, при использовании grid каждый контейнер, использующий сетку, должен давать как минимум одну строку и один столбец веса.

Я бы начал все сначала. Будьте методичны. Прежде чем приступать к решению других проблем, сначала поработайте три основные области. Эту проблему сложно решить из-за того, что все работает неправильно, поэтому вы пытаетесь настроить сразу несколько вещей. Сосредоточьтесь на одной области за раз, добейтесь того, чтобы она работала правильно, а затем двигайтесь дальше.

Учитывая вашу диаграмму, pack кажется гораздо более простым решением, чем использование сетки для дочерних элементов корневого окна. Использование сетки внутри фреймов внутри других фреймов с использованием сетки может сбивать с толку.

Похоже, что кадр C - это своего рода строка состояния, которая тянется по дну, так что сначала упакуйте ее. Выше у вас есть две области: рамка a находится слева и имеет фиксированную ширину, а рамка c находится справа и занимает все дополнительное пространство. Используя pack, это будет выглядеть так:

frame['c'].pack(side="bottom", fill="x")
frame['a'].pack(side="left", fill="y")
frame['b'].pack(side="right", fill="both", expand=True)

Конечно, вы можете получить точно такой же внешний вид с сеткой, но это займет еще пару строк кода, так как вы должны присвоить столбцу 1 и строке 1 вес.

Это должно заставить три основные области работать нормально. Теперь все, о чем вам нужно беспокоиться, - это содержимое кадра B.

Ваша диаграмма показывает, что вы хотите, чтобы b2a и b2b находились внизу кадра b, а над ним было больше виджетов. Это правильно? В этом случае вам нужно оставить как минимум одну дополнительную строку над ней, чтобы заполнить лишнее пространство.

Пустая строка с положительным весом заставит все виджеты переместиться в нижнюю часть области. Они будут занимать ровно столько места, сколько им нужно по вертикали, а пустая строка с ненулевым весом займет все лишнее.

Тогда вам нужно будет беспокоиться только о горизонтальном размещении. Неясно, чего именно вы ожидаете, но решение опять же сводится к приданию веса столбцам. Если вы хотите, чтобы и b2a, и b2b расширялись одинаково, присвойте обоим столбцам одинаковый вес. Если вы хотите, чтобы b2a выполняла все раскрытие, задайте вес только нулевому столбцу.

person Bryan Oakley    schedule 05.08.2015
comment
Спасибо, Брайан. Я полагаю, это следует отметить как повторяющийся вопрос, поскольку, похоже, он имеет тот же ответ, что и предыдущий, на который вы ответили. Тем не менее, у меня все еще есть небольшая проблема - я добавил frame['b'].grid_rowconfigure(0, weight=1), и это перемещает b2 в нижнюю часть кадра b. Но добавление frame['b2'].grid_columnconfigure(0, weight=1) по-прежнему не позволяет кадру b2 расширяться до правой стороны кадра b. - person mRotten; 06.08.2015
comment
@mRotten: Я обновил свой ответ несколькими примерами и дополнительными советами. - person Bryan Oakley; 06.08.2015
comment
Вопрос дополнен рабочим кодом, иллюстрация решенной в рабочем коде проблемы горизонтального выравнивания. Спасибо Брайану за то, что указал мне в правильном направлении. Как он также упомянул, pack() может быть более подходящим для этого приложения, но я думаю, что стоит показать, что даже когда в контейнере есть только один столбец, отсутствие веса этого столбца может привести к неожиданному поведению со стороны grid(). - person mRotten; 06.08.2015