我有一个名为 Bones
的类(class)我有5个Bones
在我的skeleton
字典。然而,在我的实际实现中,有 300 多个骨骼,这就是我今天在 stackoverflow 上问这个问题的原因。
每个Bone
有:
-
ID
: 一个用于识别骨骼的 int -
w
:w 位置(在-1
和1
之间 float ) -
x
: x 位置(在-1
和1
之间 float ) -
y
: y 位置(在-1
和1
之间 float ) -
z
: z 位置(在-1
和1
之间 float )
Bone.py
INCREMENT = 0.01
class Bone:
def __init__(self, boneId, w, x, y, z):
self.id = boneId
self.w = w
self.x = x
self.y = y
self.z = z
def shouldChangePos(self, num):
if (num >= 1 or num <= -1):
return False
return True
def incrW(self):
if(self.shouldChangePos(self.w)):
self.w = self.w + INCREMENT
def decrW(self):
if(self.shouldChangePos(self.w)):
self.w = self.w - INCREMENT
def incrX(self):
if(self.shouldChangePos(self.x)):
self.x = self.x + INCREMENT
def decrX(self):
if(self.shouldChangePos(self.x)):
self.x = self.x - INCREMENT
def incrY(self):
if(self.shouldChangePos(self.y)):
self.y = self.y + INCREMENT
def decrY(self):
if(self.shouldChangePos(self.y)):
self.y = self.y - INCREMENT
def incrZ(self):
if(self.shouldChangePos(self.z)):
self.z = self.z + INCREMENT
def decrZ(self):
if(self.shouldChangePos(self.z)):
self.z = self.z - INCREMENT
<小时/>
问题说明
我正在尝试制作一个 tkinter
GUI 看起来像这样:
图例:
- 绿色 - 代表
Frame
(只是我的注释来解释) - 红色 - 是对象的属性(只是我的注释来解释)
- 黑色 - 是对象的方法(只是我的注释来解释)
- 蓝色 - 向我显示文本和按钮
如您所见,它显示 ID
, w
, x
, y
, z
。在它的下面,有一个+按钮和一个-按钮。每次单击这些按钮时,我想减少对象中的相应值并更新 tkinter
显示号码。我知道如何手动执行此操作,但根据我的要求,我有 300+ Bones
。我无法手动制作这些框架。
如何在循环中创建这些框架,并在单击 + 或 - 按钮时更新 GUI 和对象上显示的值?强>
<小时/> main.py
from tkinter import *
from tkinter import ttk
from Bone import *
skeleton = {
1: Bone(-0.42, 0.1, 0.02, 0.002, 0.234),
4: Bone(4, 0.042, 0.32, 0.23, -0.32),
11: Bone(11, 1, -0.23, -0.42, 0.42),
95: Bone(95, -0.93, 0.32, 0.346, 0.31),
}
root = Tk()
root.geometry('400x600')
boneID = Label(root, text="ID: 1")
boneID.grid(row=1, column=1, sticky=W, padx=(0, 15))
w = Label(root, text="-0.42")
w.grid(row=1, column=2, sticky=W)
x = Label(root, text="0.02")
x.grid(row=1, column=4, sticky=W)
y = Label(root, text="0.002")
y.grid(row=1, column=6, sticky=W)
z = Label(root, text="0.234")
z.grid(row=1, column=8, sticky=W)
wPlusBtn = Button(root, text="+")
wPlusBtn.grid(row=2, column=2)
wMinusBtn = Button(root, text="-")
wMinusBtn.grid(row=2, column=3, padx=(0, 15))
xPlusBtn = Button(root, text="+")
xPlusBtn.grid(row=2, column=4)
xMinusBtn = Button(root, text="-")
xMinusBtn.grid(row=2, column=5, padx=(0, 15))
yPlusBtn = Button(root, text="+")
yPlusBtn.grid(row=2, column=6)
yMinusBtn = Button(root, text="-")
yMinusBtn.grid(row=2, column=7, padx=(0, 15))
zPlusBtn = Button(root, text="+")
zPlusBtn.grid(row=2, column=8)
zMinusBtn = Button(root, text="-")
zMinusBtn.grid(row=2, column=9, padx=(0, 15))
root.mainloop()
最佳答案
TL;DR - 将一个大问题分解为几个较小的问题,然后分别解决每个问题。
<小时/>主窗口
首先查看 UI 的整体设计。您有两个部分:一个包含骨骼的面板和一个包含随机文本的面板。所以我要做的第一件事就是将这些面板创建为框架:
root = tk.Tk()
bonePanel = tk.Frame(root, background="forestgreen", bd=2, relief="groove")
textPanel = tk.Frame(root, background="forestgreen", bd=2, relief="groove")
当然,您还需要使用pack
或grid
将它们放置在窗口上。我推荐 pack
因为只有两个框架,而且它们是并排的。
显示骨骼
对于骨骼面板,每个骨骼似乎都有一行。因此,我建议创建一个类来表示每一行。它可以继承自 Frame,并负责该行内发生的所有事情。通过继承 Frame
,您可以将其视为自定义小部件,并将其布局在屏幕上。
您的 UI 代码的目标是如下所示:
bones = (
Bone(boneId=1, w=-0.42, x=0.02, y=0.002, z=0.234),
Bone(boneId=4, w=0.042, x=0.32, y=0.23, z=-0.32),
Bone(boneId=11, w=1, x=-0.23, y=-0.42, z=0.42),
...
)
bonePanel = tk.Frame(root)
for bone in bones:
bf = BoneFrame(bonePanel, bone)
bf.pack(side="top", fill="x", expand=True)
同样,如果需要,您可以使用 grid
,但 pack
似乎是自然的选择,因为行是从上到下堆叠的。
显示单个骨骼
现在,我们需要解决每个 BoneFrame
的作用。它似乎由五个部分组成:一个显示 id 的部分,然后是四个几乎相同的属性部分。由于这些部分之间的唯一区别是它们表示的属性,因此将每个部分表示为类的实例是有意义的。同样,如果该类继承自 Frame
,我们可以将其视为自定义小部件。
这一次,我们应该传入骨骼,也许还有一个字符串告诉它要更新哪个 id。
所以,它可能开始看起来像这样:
class BoneFrame(tk.Frame):
def __init__(self, master, bone):
tk.Frame.__init__(self, master)
self.bone = bone
idlabel = tk.Label(self, text="ID: {}".format(bone.id))
attr_w = BoneAttribute(self, self.bone, "w")
attr_x = BoneAttribute(self, self.bone, "x")
attr_y = BoneAttribute(self, self.bone, "y")
attr_z = BoneAttribute(self, self.bone, "z")
pack
在这里是一个不错的选择,因为这些部分都是从左到右排列的,但如果您愿意,也可以使用 grid
。唯一真正的区别是使用grid
需要多行代码来配置行权重和列权重。
属性按钮和标签的小部件
最后,我们必须处理 BoneAttribute
类。这是我们最终添加按钮的地方。
它非常简单并且遵循相同的模式:创建小部件,然后将它们布局。不过,还有一点。我们需要连接按钮来更新骨骼,并且还需要在骨骼发生变化时更新标签。
我不会详细介绍所有细节。您所需要做的就是创建一个标签、几个按钮以及供按钮调用的函数。另外,我们需要一个函数来在值更改时更新标签。
让我们从更新标签的函数开始。由于我们知道属性的名称,因此我们可以进行简单的查找来获取当前值并更改标签:
class BoneAttribute(tk.Frame):
...
def refresh(self):
value = "{0:.4f}".format(getattr(self.bone, self.attr))
self.value.configure(text=value)
这样,我们就可以随时更新标签。
现在只需定义按钮的功能即可。有更好的方法可以做到这一点,但一个简单、直接的方法是只使用一些 if
语句。增量函数可能如下所示:
...
plus_button = tk.Button(self, text="+", command=self.do_incr)
...
def do_incr(self):
if self.attr == "w":
self.bone.incrW()
elif self.attr == "x":
self.bone.incrX()
elif self.attr == "y":
self.bone.incrY()
elif self.attr == "z":
self.bone.incrZ()
self.refresh()
do_decr
函数是相同的,只是它调用了一次递减函数。
就是这样。这里的关键点是将较大的问题分解为较小的问题,然后一次解决一个较小的问题。无论您有 3 个骨骼还是 300 个骨骼,您唯一需要编写的额外代码是最初创建骨骼对象的位置。 UI 代码保持完全相同。
关于python - 如何在循环中制作 tkinter 框架并更新对象值?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/58331886/