Как избежать глобальных переменных в динамически генерации. форма ткинтер? Значения, вставленные функциями, не будут сохранены, значения, введенные вручную, будут изменены после изменения кода - PullRequest
0 голосов
/ 12 ноября 2018

Я пытаюсь удалить глобальную переменную, на которую опирался, вместо того, чтобы передать / вернуть необходимые значения. Вроде работает, частично, но результаты меня смущают.

Есть ли способ избежать глобальной переменной, не переключаясь на объектно-ориентированный? *

С глобальной переменной, содержащей виджеты, форма работает как нужно. Он создает пустую форму по умолчанию и может перестроить форму со значениями, загруженными из файла, и сохраняет значения виджета независимо от того, были ли введены значения, загружены из файла, отредактированы ли после загрузки и т. Д.

Однако, когда я пытаюсь передать / вернуть список виджетов вместо использования глобальной переменной, сохраняются только введенные вручную значения, и как только вызывается функция load_file, любой вызов save_file просто сохраняет последние введенные вручную значения. (Чтобы увидеть разницу, переключите текущее комментирование, его отсутствие для строк, отмеченных встроенными комментариями).

Мне бы хотелось помочь понять, что здесь не так, и варианты правильного выполнения.

import tkinter as tk

root = tk.Tk()
root.geometry('900x800')

form_frame = tk.Frame(root) 
button_frame = tk.Frame(root)

form_frame.grid(row = 0)
button_frame.grid(row = 1)

# default form, or form that matches opened dataset 
def build_form(dataset = None): 
    global entry_objects        #<==== Comment out this line (1/4)...
    entry_objects = []
    if dataset == None:            
        rowcount = 2   
    else:
        rowcount = len(dataset)      
    for row_i in range(rowcount):
        entry_list = []
        if dataset is not None:
            data_row = dataset[row_i]     
        for col_i in range(3):   
            entry = tk.Entry(form_frame)
            if dataset is not None:
                entry.insert(0, str(data_row[col_i]))
            entry_list.append(entry)
            entry.grid(row = row_i, column = col_i)    
        entry_objects.append(entry_list)
    #return(entry_objects)      #<==== ... uncomment this line (2/4)...

def open_file():    # open_file(), save_file() are just substitutes.
    test_data = [['a1', 'a2', 'a3'],['b1', 'b2', 'b3'],['c1', 'c2', 'c3']]
    build_form(test_data)

def save_file(entry_objects):
    entry_values =  [[j.get() for j in i]  for i in entry_objects]
    print('--> Saved to csv file: ')
    print(entry_values)

build_form()                    #<==== ... comment this line (3/4)...   
#entry_objects = build_form()   #<==== ... and uncomment this line (4/4).   


open_button = tk.Button(button_frame, text = 'Load Test Data',
                     command = open_file)
save_button = tk.Button(button_frame, text = 'Save', 
                     command = lambda: save_file(entry_objects))
exit_button = tk.Button(button_frame, 
                     text = 'Exit', command=root.quit)

open_button.pack(side = 'left')
save_button.pack(side = 'left')
exit_button.pack(side = 'left')

root.mainloop()

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

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

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

1 Ответ

0 голосов
/ 12 ноября 2018

Из того, что я могу сказать, это то, что я могу сказать.

С тем, что код (глобальный entry_objects не комментируется), все в порядке. При запуске функции save_file переменная entry_objects устанавливается на обновленное значение и выдает правильные данные.

Когда функция build_form запускается с оператором return, функция open_file не обновляется, чтобы принимать build_file в качестве значения, скорее она ожидает, что это будет оператор.

def open_file():    # this, save_file are just substitutes.
    test_data = [['a1', 'a2', 'a3'],['b1', 'b2', 'b3'],['c1', 'c2', 'c3']]
    build_form(test_data)    # this does nothing

Что нужно включить в глобальный оператор (потому что он изменяет значение вне функции) global entry_objects и ему нужно установить entry_objects в значение, которое дает функция build_form, что означает entry_objects = build_form(test_data). Вот как выглядит обновленная функция:

# updated open_file for when build_form returns a value rather than changing the value by itself
def open_file():
    # this makes changes to entry_objects visible to things outside the function
    global entry_objects
    test_data = [['a1', 'a2', 'a3'],['b1', 'b2', 'b3'],['c1', 'c2', 'c3']]
    entry_objects = build_form(test_data)    # this sets the returned value to entry_objects

По сути, я говорю (в этом большом количестве слов), что необходимо изменить entry_objects, и единственный способ сделать это изменение и спроецировать обновленное значение на все другие вызовы этой переменной - это использовать global и сделать все изменения в entry_objects видимыми для всего остального, включая

save_btn = tk.Button(
    button_frame, text='Save',
    command=lambda: save_file(entry_objects)
).pack(side='left')

в конце.

Просто небольшой совет, попробуйте обернуть ваши строки до 74 символов, так как это помогает другим с маленькими экранами видеть всю картинку, а не прокручивать вокруг:)

Если я не понял, не стесняйтесь рассказать мне, что мне нужно, чтобы объяснить больше. Отличная работа и над этой формой: D

...