Кто-нибудь может объяснить, что происходит с размещением сетки здесь? (Python Ткинтер) - PullRequest
0 голосов
/ 25 марта 2020

Я пытаюсь составить фитнес-программу для практики, и я только начинаю изучать Tkinter и Python в целом. В настоящее время я борюсь с тем, чтобы разместить фреймы и виджеты так, как я хочу, а также заставить программу работать в одном окне (не создавая новые объекты Tk ()). Вот код:

from tkinter import *

SelectionSwitch = False
root = Tk()
root.minsize(width=800, height=600)
root.maxsize(width=800, height=600)

xrcslist = ['Pushup', 'Pullup', 'Boxjump', 'Squat', 'Wristcurl', 'Bicepcurl', 'Crunch', 'Lunge', 'Plank']
xrcslist.sort()

BasicFont = 'Calibri Bold'

TextStart = Label(root, height=3, text='Welcome', bg='#F2D7D2', fg='black', borderwidth=4, relief='groove', width=100)

ButtonStart = Button(root, width=25, height=4, text='Exercises', bg='#C70039', command=lambda: pressed())

frameSelection = Frame(root, bg='#DFF5DC', width=800, height=600) # frame on which all of the contents of Second Screen are placed

frameSpace1 = Frame(frameSelection, height=30, width=5, bg='blue') # intentionally made in blue color to see it(this is an empty frame simply to make a gap between the Label and Listbox)

frameSpace2 = Frame(frameSelection, height=280, width=5, bg='yellow') #intentionally made in yellow color to see it, but it seems like it's hidden behind the Listbox(this frame is supposed to separate two Listboxes, but it's not behaving as expected so I didn't add the second one yet)

frameSpace3 = Frame(frameSelection, height=30, width=5, bg='red') # intentionally made in red color to see it(another empty frame to separate listboxes from the button in the lower left corner)

ButtonBackSelection = Button(frameSelection, text='Back', width=15, height=4, command=lambda: main.YetAgain())

frameSelectionLists = Frame(frameSelection)

TextSelection = Label(frameSelection, width=100, height=3, text='Select exercises', bg='#F2D7D2', borderwidth=4, relief='groove')

lstbx1 = Listbox(frameSelection, width=17, height=8)
for i in xrcslist:
    lstbx1.insert(END, i)

scroll = Scrollbar(root, command=lstbx1.yview) # binding the scrollbar to the listbox

#configs
TextSelection.config(font=(BasicFont,11))
TextStart.config(font=(BasicFont, 11))

ButtonBackSelection.config(font=(BasicFont, 11))
ButtonStart.config(font=(BasicFont, 11))
lstbx1.config(yscrollcommand=scroll.set) 



class main(Frame):
    def __init__(self):
        MainScreen()

    def YetAgain():
        global SelectionSwitch
        if SelectionSwitch == True: # this 'if' section checks if a window other than the first one is currently displayed
            frameSelection.grid_forget()
            SelectionSwitch = False
            MainScreen()
        else:
            MainScreen()


class Exercise(object):
    def __init__(self, type, musclegroup, calpersec, equipment):
        self.type = type
        self.musclegroup = musclegroup
        self.calpersec = calpersec
        self.equipment = equipment


def pressed(): # replaces the current window with the selection window when ButtonStart is pressed
    global SelectionSwitch
    SelectionSwitch = True
    TextStart.grid_forget()
    ButtonStart.grid_forget()
    frameSelection.grid()
    TextSelection.grid()
    frameSpace1.grid()
    lstbx1.grid(row=2,column=0)
    scroll.grid()
    scroll.place(in_=lstbx1, relx=1.0, relheight=1.0, bordermode="outside")
    frameSpace2.grid(row=2, column=1)
    frameSpace3.grid(row=3)
    ButtonBackSelection.grid(row=4)
    ButtonBackSelection.place(relx=0, rely=1, anchor=SW)


def MainScreen(): # window that is showed on launch
    TextStart.grid(row=0)
    ButtonStart.grid(row=1)
    ButtonStart.place(relx=0.5, rely=0.5, anchor=CENTER)
    root.mainloop()



#list of exercises
Pushup = Exercise('hypertrophy', ['chest', 'triceps'], None, None)

Pullup = Exercise('hypertrophy', ['upper back','biceps'], None, 'pullup bar')

Boxjump = Exercise('hypertrophy', ['quads', 'glutes', 'hamstrings'], 0.16, 'box or an elevation')

Squat =  Exercise('hypertrophy', ['quads', 'glutes'], 0.15, None)

Wristcurl = Exercise('hypertrophy', ['forearms'], None, 'dumbbell or barbell')

Bicepcurl = Exercise('hypertrophy', ['biceps','brachialis','forearms'], None, 'dumbbell or barbell')

Crunch = Exercise('hypertrophy', ['abdominals', 'obliques'], 0.09, None)

Lunge = Exercise('hypertrophy',['quadriceps', 'glutes', 'hamstrings'], 0.1, None)

Plank = Exercise('hypertrophy', ['abdominals', 'lower back'], 0.05, None)


main()

Вот что происходит при запуске программы:

Main Screen

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

Однако при нажатии ButtonStart происходит нечто странное:

Second Screen

Есть несколько вопросов, которые я имеют:

  • Почему frameSelection не имеет размера root, даже если его размеры указаны как 800x600, так же, как root?
  • Почему виджеты полностью запутались второй экран, даже если я назначил строки и столбцы, где это необходимо (для TextSelection и frameSpace1 это не имеет значения, так как я хочу, чтобы они в любом случае go первый и второй, я думаю)?
  • Будет ли это лучше разместить на кадре виджеты, которые отображаются при запуске, а не root напрямую? Я пытался сделать это, и позиционирование было беспорядочным и не реагировало на row, column, rowconfigure и columnconfigure, как на втором экране.

1 Ответ

0 голосов
/ 25 марта 2020

Основная проблема заключается в том, что команды grid и pack предназначены для того, чтобы заставить содержащийся виджет расти или уменьшаться вокруг своих дочерних элементов. Это называется распространение геометрии . Хотя эту функцию можно отключить, это почти никогда не является хорошей идеей.

Итак, первая проблема заключается в том, что вы не знаете об этом поведении. Неважно, если вы даете размер кадра. Когда вы вызываете grid для его дочерних элементов, размер кадра будет игнорироваться, а кадр будет увеличиваться или уменьшаться, чтобы соответствовать размеру.

Вторая проблема заключается в том, что вы сильно полагаетесь на значения по умолчанию при вызове grid. Как правило, вы должны всегда указывать строку и столбец. Иначе, куда уходит виджет, зависит от того, в каком порядке он был добавлен в окно.

По той же схеме вы полагаетесь на значение по умолчанию опции sticky. Вы также должны всегда указывать опцию sticky (если вы не хотите, чтобы виджет центрировался в его строке и столбце), чтобы каждый виджет правильно использовал выделенное ему пространство. Без опции sticky и из-за поведения «сжать, чтобы подогнать» ваши кадры не заполняют предоставленное им пространство.

Третья проблема заключается в том, что вы не даете никаких строк или столбцы а weight. Это контролирует, как tkinter обрабатывает случай, когда места больше, чем необходимо. Tkinter выделит дополнительное пространство на основе weight отдельных строк и столбцов. Строки и столбцы с нулевым весом (по умолчанию) не получат никакого дополнительного пространства. Еще одно практическое правило заключается в том, что для любого содержащего виджета (обычно фрейма), использующего сетку, всегда следует присваивать положительный вес хотя бы одной строке и одному столбцу.

Будет ли это лучше размещать виджеты, которые показываются при запуске, вместо фрейма root напрямую?

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


Кроме того, вы, кажется, используете minsize и maxsize, чтобы установить размер окна в целом. Лучший способ сделать это - использовать метод geometry окна. Например:

root.geometry("800x600")

Используя метод geometry, вы оставляете окончательный контроль в руках пользователя. Это также помогает отладке, поскольку возможность изменения размера окна помогает вам наблюдать за поведением виджетов: вы можете увеличивать и уменьшать окно, чтобы убедиться, что ваши виджеты правильно расширяются или уменьшаются по вашему желанию.

В вашем В этом случае вы должны вставлять кадры только в root. Один для запуска запуска, один для виджетов на следующем экране и так далее.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...