如何从tkinter中的特定网格位置检索值?

时间:2019-04-20 13:02:31

标签: python python-3.x tkinter

我正在做我的第一个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()

1 个答案:

答案 0 :(得分:1)

马上要注意的几件事:

  • stovfl的评论回答了所写的问题
  • 我完全同意furas关于将gui与逻辑完全分离的评论。您应该对代码进行重构-以便工作表GUI应该与Character分开,作为统计信息的抽象集合,并且还应该与执行/管理GUI的代码分开(目前,所有代码都在伞下{ {1}}类。

尽管我将与Character_sheet完全分开,但我们至少可以让您起步,同时开发一种可访问GUI中值的模式。

前4行中的每行代表用户可以更改并与您已经创建的标签相关的统计信息。其中两个统计的成本修正为10,另外两个统计的修正为20。

Character_sheet

collections.OrderedDict

大概,每个给定的字符表都会有自己的独立小部件和这些统计信息的值。同样,您应该重写代码以使其更加分离,但是现在我们将保留尽可能多的代码。

## 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}

enumerate

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