Как получить значения из определенного местоположения сетки в tkinter? - PullRequest
1 голос
/ 20 апреля 2019

Я работаю над своим первым проектом с графическим интерфейсом, и я разместил свой код внизу поста (эта работа еще не завершена, поэтому, пожалуйста, смиритесь с любым уродством или неэффективностью кода).

Я создаю таблицу персонажей 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 Ответ

1 голос
/ 22 апреля 2019

Несколько замечаний сразу:

  • Комментарий Stovfl отвечает на вопрос так, как он написан
  • Я полностью согласен с комментарием фура о полном отделении графического интерфейса от логики. Ваш код должен быть реорганизован - так, чтобы графический интерфейс Sheet был отделен от персонажа в виде абстрактной коллекции статистики, а также отделен от кода, который выполняет / управляет GUI (который в настоящее время все обрабатывается под эгидой Character_sheet класс).

Несмотря на то, что я оставлю вам полностью распутывание Character_sheet, мы, по крайней мере, сможем начать с разработки шаблона для получения доступа к значениям в графическом интерфейсе.

Каждая из первых 4 строк представляет статистику, которую пользователь может изменить и относиться к метке, которую вы уже создали. Две статистики имеют модификатор стоимости 10, а две другие имеют модификатор 20.

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

( collections.OrderedDict )

Предположительно, каждый данный Лист символов будет иметь свои собственные независимые виджеты и значения для этой статистики. Опять же, вам следует переписать код, чтобы он был более отсоединенным, но сейчас мы сохраним как можно больше вашего кода.

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

Благодаря этим дополнениям у нас теперь есть структура для обращения к строкам виджета, которые вы создаете, и для определения модификатора стоимости для этой статистики.

## 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 дает вам доступ к различным типам сигналов; специально для нашего использования tkinter Variables может быть привязан с помощью метода трассировки . Используя режим 'w', при каждом изменении Variable будет вызываться данный обратный вызов (функция). Используя это, мы можем сделать графический интерфейс более отзывчивым, избавившись от необходимости постоянно нажимать Generate Button.

## 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, чтобы он действительно функционировал:

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

И это немного приблизит вас к отделению графического интерфейса пользователя от логики программирования, в то же время позволяя ссылаться на эти значения в строках и столбцах, как вы пытались это сделать (т.е. - self.stats['Strength']['rank'].get())

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