【问题标题】:Can anyone explain what's going on with the grid placement here? (Python Tkinter)谁能解释这里的网格放置发生了什么? (Python Tkinter)
【发布时间】:2020-03-25 11:51:58
【问题描述】:

我一直在尝试制作一个健身类的练习程序,而且我刚刚开始学习 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()

程序启动时会发生以下情况:

这部分工作得很好,我打算稍后再添加一些东西。

但是,当按下 ButtonStart 时,会发生一些奇怪的事情:

我有几个问题:

  • 为什么 frameSelection 不是 root 的大小,即使它的尺寸是 800x600,与 root 相同?
  • 为什么小部件在第二个屏幕上完全搞砸了,即使我已经在需要的地方分配了行和列(对于 TextSelection 和 frameSpace1 没关系,因为我想无论如何我希望它们排在第一和第二)?
  • 将启动时显示的小部件放在框架上而不是直接放在根上会更好吗?我试过这样做,但定位很乱,对rowcolumnrowconfigurecolumnconfigure 没有响应,就像第二个屏幕一样。

【问题讨论】:

    标签: python-3.x tkinter


    【解决方案1】:

    主要问题是gridpack 命令旨在使包含的小部件围绕其子级增长或缩小。这称为几何传播。虽然可以关闭此功能,但这样做几乎不是一个好主意。

    所以,第一个问题似乎是您没有意识到这种行为。如果你给一个框架一个大小,这并不重要。当您在其子项上调用 grid 时,框架的大小将被忽略,并且框架将扩大或缩小以适应。

    第二个问题是您在调用grid 时严重依赖默认值。根据经验,您应该始终指定一行和一列。否则,小部件的去向取决于它添加到窗口的顺序。

    按照同样的思路,您依赖sticky 选项的默认值。您还应该始终指定sticky 选项(除非您希望小部件在其行和列中居中),以便每个小部件正确使用分配给它的空间。如果没有 sticky 选项,并且由于“缩小以适应”行为,您的框架不会填充给它们的空间。

    第三个问题是您没有为任何行或列提供weight。这控制了 tkinter 如何处理空间超过必要的情况。 Tkinter 将根据各个行和列的weight 分配额外空间。权重为零(默认值)的行和列将不会收到任何额外空间。另一条经验法则是,对于任何使用网格的包含小部件(通常是框架),应始终为至少一行和一列分配正权重。

    将启动时显示的小部件放在框架上而不是直接放在根上会更好吗?

    当然,是的。如果您想更换整组小部件,最好将所有这些小部件放在一个框架中。这样,您只需添加或删除一帧。


    顺便说一句,您似乎在使用minsizemaxsize 来设置整个窗口的大小。一个更好的方法是使用窗口的geometry 方法。例如:

    root.geometry("800x600")
    

    通过使用geometry 方法,您可以将最终控制权留给用户。它还有助于调试,因为能够调整窗口大小可以帮助您观察小部件的行为:您可以扩大和缩小窗口,以验证您的小部件是否按照您的需要适当地扩大或缩小。

    在您的情况下,您应该只将框架直接放入root。一个用于启动范围,一个用于下一个屏幕上的小部件,依此类推。

    【讨论】:

      猜你喜欢
      • 2022-11-18
      • 1970-01-01
      • 2023-03-12
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-06-06
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多