我正在做我的第一个GUI项目,我已将代码放在帖子的底部(这是一项正在进行的工作,因此请耐心等待代码中的任何丑陋或无效之处)。
我正在制作一个GURPS字符表,它将自动为玩家创建字符,然后(尽管尚未实现)吐出格式正确的PDF。
程序当前的工作方式,我具有一些功能,这些功能可以根据所需的属性,派生属性或技能等级进行成本计算。然后,按下“计算”按钮,将获得所需属性或技能的点成本花在身上。
我使用靠近类定义末尾的while循环生成行。循环调用函数,该函数告诉程序创建执行某种类型的计算的行。
通过选择,所有输出值都出现在每一行的第4列中。我想知道是否有一种方法可以轻松地查找那些列和行的值,而无需跟踪它们的值。也许是.grid(column,row).get()之类的方法,或者会返回某些特定网格位置中任何内容的方法。
class Character_sheet:
#Our default class which will house our character sheet.
def __init__(self):
#Total Point Calculator?
def sum_of_values():
list = self.grid_slaves(column=3)
sum = 0
for each in list:
sum += int(each["text"])
total_cost.set(sum)
#Generators for Rows and Columns.
def attr_widget_10(index):
#The below syntax/structure works.
def attr_10():
cost.set((rank.get()-10)*10)
return None
rank = IntVar()
rank.set(10)
cost = IntVar()
input = ttk.Entry(self.window, textvariable = rank).grid(column=2, row=index)
ttk.Button(self.window, text='Calculate', command=attr_10).grid(column=3,row=index)
ttk.Label(self.window, width=7, textvariable=cost).grid(column=4,row=index)
return None
def attr_widget_20(index):
def attr_20():
cost.set((rank.get()-10)*20)
return None
rank = IntVar()
rank.set(10)
cost = IntVar()
input = ttk.Entry(self.window, textvariable = rank).grid(column=2, row=index)
ttk.Button(self.window, text='Calculate', command=attr_20).grid(column=3,row=index)
ttk.Label(self.window, width=7, textvariable=cost).grid(column=4,row=index)
def derived_attr_widget(dictionary, index):
return None
def skill_widget(dictionary, index):
return None
def total_cost():
return None
#Basic window functions.
self.root = tk.Tk()
self.root.title('GURPS Character Sheet')
self.window = ttk.Frame(self.root)
self.window.grid()
self.root.columnconfigure(0, weight=1)
self.root.rowconfigure(0, weight=1)
"""Core Functionality:
Below are labels for set attributes. Each references an appropriate calculator.
This does not address skills.
For now, inputs start on row 1.
"""
#Labels for attributes and derived attributes.
#ATTRIBUTES
ttk.Label(self.window, width=10, text='Strength').grid(column=1, row=1)
ttk.Label(self.window, width=10, text='Health').grid(column=1, row=2)
ttk.Label(self.window, width=10, text='Intelligence').grid(column=1, row=3)
ttk.Label(self.window, width=10, text='Dexterity').grid(column=1, row=4)
#DERIVED ATTRIBUTES
ttk.Label(self.window, width=10, text='HP').grid(column=1,row=5)
ttk.Label(self.window, width=10, text='FP').grid(column=1,row=6)
ttk.Label(self.window, width=10, text='Will').grid(column=1,row=7)
ttk.Label(self.window, width=10, text='Perception').grid(column=1,row=8)
ttk.Label(self.window, width=10, text='Basic Speed').grid(column=1,row=9)
ttk.Label(self.window, width=10, text='Basic Move').grid(column=1,row=10)
index = 1
while index <= 2:
attr_widget_10(index)
index += 1
while index <= 4:
attr_widget_20(index)
index += 1
total_cost = IntVar()
#ttk.Button(self.window, text='Total Cost', command=sum_of_values).grid(column=2,row=index+1)
#ttk.Label(self.window, width=7, textvariable=total_cost).grid(column=4,row=index+1)
###CREATES WINDOW###
self.window.mainloop()
答案 0 :(得分:1)
马上要注意的几件事:
尽管我将与Character_sheet
完全分开,但我们至少可以让您起步,同时开发一种可访问GUI中值的模式。
前4行中的每行代表用户可以更改并与您已经创建的标签相关的统计信息。其中两个统计的成本修正为10,另外两个统计的修正为20。
Character_sheet
大概,每个给定的字符表都会有自己的独立小部件和这些统计信息的值。同样,您应该重写代码以使其更加分离,但是现在我们将保留尽可能多的代码。
## Place in the global space for the time being
BASE_STATISTICS = ["Strength","Health","Intelligence","Will"]
## Note that prior to Python 3.7 dictionary order was not guaranteed, so
## collections.OrderedDict would be preferable for versions before that
STATISTIC_COSTS = {"Strength":10,"Health":10,"Intelligence":20,"Will":20}
有了这些新增功能,我们现在有了一个框架,既可以引用您正在创建的小部件行,又可以确定这些统计信息的成本修饰符。
## Place at the top of Character_sheet.__init__
## The value for each stat is a dictionary in order to store arbitrary data until the code is reworked further
self.base_stats = {stat:{} for stat in BASE_STATISTICS}
Tkinter使您可以访问不同的信号类型;专门用于我们的用途,可以使用trace method来绑定## This will replace the Label and attr_widget_X loops and functions
## You can place it where the Attributes labels currently are, and delete both attr_widget_x functions
## enumerate pairs each element of an iterable with a sequential integer
for i,stat in enumerate(BASE_STATISTICS):
## These IntVars are useful, so we'll keep them around
rank = IntVar()
rank.set(10)
cost = IntVar()
## We'll set up the gui just like you did, just with a minor tweak
ttk.Label(self.window, width=10, text=stat).grid(column=1, row=i)
ttk.Entry(self.window, textvariable = rank).grid(column=2, row=i)
## I've removed the Generate button for reasons I'll get into below
ttk.Label(self.window, width=7, textvariable=cost).grid(column=3,row=i)
## Here we save all our references so that we can come back to them later
## self.base_stats[stat]['row'] will tell us which row of the grid the widgets are located
## self.base_stats[stat]['rank'] will now give us direct access to the rank IntVar at all times
## self.base_stats[stat]['cost'] likewise gives us easy access to the cost IntVar whenever we need it
self.base_stats[stat].update({'row':i,'rank': rank,'cost':cost})
。通过使用tkinter Variables
模式,只要'w'
发生更改,就会调用给定的回调(函数)。使用此功能,可以摆脱不断打Variable
的需求,从而使GUI更具响应性。
Generate Button
(lambda)
现在我们可以添加## This should go right after "cost = IntVar()"
## The lambda statement here is technically the function that is being passed to trace
## The lambda itself is capturing all information it gets passed as e
## stat = stat creates a reference within the lambda definition to the current value of stat
## (as you iterate, the stat value in the local scope will change, so we need to preserve it)
## and then calling self.updatestat and passing that the stat we're updating.
rank.trace('w',lambda *e,stat = stat: self.updatestat(stat))
使其真正起作用:
Character_sheet.updatestat
这使您可以更接近于将GUI与编程逻辑分开,同时允许您像尝试做的那样在行和列中引用这些值(即-def updatestat(self,stat):
""" Queries the current value of the stat's rank and then sets the cost appropriately """
## Get the IntVar for the given stat from your stats dict
rankvar = self.base_stats[stat]['rank']
## Since we're using an Entry (instead of e.g.- a spinbox), there's
## no garauntee that it contains a valid integer, so we use try/except
## to catch the mistake
try:
rank = rankvar.get()
rank = int(rank)
except:
## We'll reset the value if it's invalid
rank = 10
rankvar.set(rank)
## Use STATISTIC_COSTS to determine the cost modifier
## Calculate cost
cost = (rank - 10)*STATISTIC_COSTS[stat]
## find our IntVar for the given stat
costvar = self.base_stats[stat]['cost']
## Set it to cost
costvar.set(cost)
## Note that "return None" is the implicit default
)