Python Tkinter get () значение combobox не меняется - PullRequest
0 голосов
/ 30 апреля 2020

Заранее спасибо за помощь.

Я создаю GUI, и одна его часть поставила меня в тупик - я создаю Combobox, используя Tkinter, и значения взяты из файла CSV - тогда я разрешаю пользователь выбирает одно из имен и удаляет этого человека, и это приводит к тому, что строка для этого пользователя в CSV-файле удаляется, а выпадающий список обновляется только для оставшихся пользователей - эта часть работает нормально - моя проблема возникает, когда я go удалить второго пользователя - мое значение get для Combobox всегда остается значением первого пользователя.

Я нуб python, которого вы быстро определите по моему коду, но я не смог найти решение того, что я делаю неправильно, которое, я уверен, очень простое.

Вот CSV-файл, из которого я читаю:

id,name,username,password,val1,val2
1,Billy,bsmith,GoodPassword,JZ4Z3ATP6,Test1
2,Amanda,asmith,GoodPassword1,NRROAZ6JP6,Test2
3,Sammy,ssmith,GoodPassword2,NRRZ3ATP6,Test3

Мой python код находится здесь:

from tkinter import *
from tkinter import ttk
import tkinter as tk
from tkinter import messagebox as msg
import os.path
from csv import reader, writer
import csv
import shutil

root = Tk()
root.title('Why am I an idiot')


labelFrame3 = ttk.LabelFrame(text = "Delete Button")
labelFrame3.grid(column = 0, row = 2, padx = 20, pady = 20)

def clicking(event):
    to_delete = delete_combo.get()
    print("CLICKING")
    print(to_delete)

def read_connections_file(conns_dict):
    conns = conns_dict
    with open('file.csv', 'r') as read_obj:
        csv_reader = reader(read_obj)
        header = next(csv_reader)
        # Check file as empty
        if header != None:
            # Iterate over each row after the header in the csv
            for row in csv_reader:
                # row variable is a list that represents a row in csv
                conns[row[0]] = {'name':row[1], 'username':row[2], 'password':row[3], 'val1':row[4], 'val2':row[5]}

def get_button_names():
    conns = {}
    button_names = []
    read_connections_file(conns)
    for i in conns:
        name_ = conns[i]["name"]
        button_names.append(name_)
    return button_names

buttons1 = get_button_names()
delete_combo = ttk.Combobox(labelFrame3, value=buttons1)
delete_combo.bind("<<ComboboxSelected>>", clicking)
delete_combo.grid(row=0, column=1, columnspan=3, padx=10, pady=10)

def clicking(event):
    to_delete = delete_combo.get()
    print("CLICKING")
    print(to_delete)

def create_delete_dropdown():
    buttons2 = get_button_names()
    print(buttons2)
    delete_combo = ttk.Combobox(labelFrame3, value=buttons2)
    delete_combo.bind("<<ComboboxSelected>>", clicking)
    delete_combo.grid(row=0, column=1, columnspan=3, padx=10, pady=10)
    print("FUNC")
    help = delete_combo.get()
    print(help)

def delete_saved_button():
    original = r'file.csv'
    target = r'file.bak'
    shutil.copyfile(original, target)
    to_delete = delete_combo.get()
    print(to_delete)

    answer = msg.askyesno("Delete Button", ("Are you sure you want to Delete " + to_delete + "?"))

    if answer == True:
        conns = []
        with open('file.csv', 'r') as read_obj:
            csv_reader = reader(read_obj)
            header = next(csv_reader)
            # Check file as empty
            if header != None:
                # Iterate over each row after the header in the csv
                for row in csv_reader:
                    # row variable is a list that represents a row in csv
                    conns.append({'id':row[0],'name':row[1], 'username':row[2], 'password':row[3], 'val1':row[4], 'val2':row[5]})

        new_list = None
        for item in conns:
            if item['name'] == to_delete:
                new_list = item
                break
        new_cons = conns.copy()
        new_cons.remove(new_list)

        i = 1
        for d in new_cons:
            d['id'] = i
            i += 1

        msg.showinfo("Button Deleted", (to_delete + " Button Deleted"))

        csv_file_name = "file.csv"
        with open(csv_file_name, 'w', newline='') as csvfile:
            writer = csv.DictWriter(csvfile, fieldnames=header)
            writer.writeheader()
            for data in new_cons:
                writer.writerow(data)

    create_delete_dropdown()

# LabelFrame 3 Content
delete_label = ttk.Label(labelFrame3, text = "Delete Who?: ")
delete_label.grid(row=0, column=0)

deleteButton = tk.Button(master=labelFrame3, text="Delete", command=delete_saved_button)
deleteButton.grid(row=1, column=0)

root.mainloop()

Я оставил несколько утверждений для печати, чтобы попытаться помочь мне понять, что я делаю неправильно.

Если я сначала удалю Билли, он будет удален из CSV и выпадающего списка, который я s замечательно.

Если я затем выберу Аманду и попытаюсь удалить ее вторую, я получу ошибку - мой вывод из этой последовательности, включая мои операторы печати:

CLICKING
Billy
Billy
['Amanda', 'Sammy']
FUNC

CLICKING
Billy
Billy
Exception in Tkinter callback
Traceback (most recent call last):
  File "C:\Users\reeno\AppData\Local\Programs\Python\Python37\lib\tkinter\__init__.py", line 1705, in __call__
    return self.func(*args)
  File ".\fucked.py", line 90, in delete_saved_button
    new_cons.remove(new_list)
ValueError: list.remove(x): x not in list

Вот сценарий запустите:

Gif Running Script

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

Помните, что я новичок, но я также хотел бы получить любые предложения по улучшению моего кода в целом.

Спасибо за вашу помощь !!

1 Ответ

1 голос
/ 30 апреля 2020

Ваша проблема: local и global переменная delete_combo.

Когда вы создаете новый Combobox() в create_delete_dropdown(), он создает локальную переменную delete_combo и предоставляет новый комбинированный список для этого локальная переменная, но она не меняет значение в глобальной переменной delete_combo, которая используется другими функциями - поэтому другие функции по-прежнему используют старый Combobox() (который скрывается за новым Combobox() в grid()).

Вы должны использовать global delete_combo в функции create_delete_dropdown(), чтобы сообщить этой функции, что вы хотите назначить внешнюю / глобальную переменную, а не локальную

def create_delete_dropdown():
    global delete_combo # inform function that you will assign (=) to external/global variable

    buttons2 = get_button_names()
    print(buttons2)

    delete_combo = ttk.Combobox(labelFrame3, value=buttons2)
    delete_combo.bind("<<ComboboxSelected>>", clicking)
    delete_combo.grid(row=0, column=1, columnspan=3, padx=10, pady=10)

    print("FUNC")
    help = delete_combo.get()
    print(help)

Но вместо создания новой Combobox() Вы можете изменить значения в существующих Combobox() как @ TheFluffDragon9, упомянутый в комментарии.

def create_delete_dropdown():

    buttons2 = get_button_names()
    print(buttons2)

    delete_combo['value'] = buttons2
    delete_combo.set('') 

    print("FUNC")
    help = delete_combo.get()
    print(help)

РЕДАКТИРОВАТЬ: Код с другими изменениями.

Я использую DictReader для получения данных в списке строк (словарей), чтобы сохранить исходный порядок строк (старые Python не должны были поддерживать порядок в словаре)

import os.path
import csv
import shutil
import tkinter as tk # PEP8: `import *` is not preferred
from tkinter import ttk
from tkinter import messagebox as msg

# --- constants --- (UPPER_CASE_NAMES)

ORIGINAL = 'file.csv'
BACKUP   = 'file.bak'

# --- function --- (lower_case_names)

def read_data():
    global header # to keep in global variable

    with open(ORIGINAL) as csvfile:
        csv_reader = csv.DictReader(csvfile)
        header = csv_reader.fieldnames
        data = list(csv_reader)
        #print('header:', header)

    return data


def write_data(data, make_backup=True):
    if make_backup:
        shutil.copyfile(ORIGINAL, BACKUP)

    with open(ORIGINAL, 'w', newline='') as csvfile:
        csv_writer = csv.DictWriter(csvfile, fieldnames=header)
        csv_writer.writeheader()
        csv_writer.writerows(data)


def clicking(event):
    print("CLICKING:", delete_combo.get())
    print("CLICKING:", event.widget.get())


def get_column(column="name"):
    data = read_data()
    values = []

    for row in data:
        values.append(row[column])

    return values


def delete_saved_button():
    to_delete = delete_combo.get()
    print('to_delete:', to_delete)

    if not to_delete:
        msg.showinfo("Button Deleted", "You have to select name.")
        return

    answer = msg.askyesno("Delete Button", "Are you sure you want to Delete {}?".format(to_delete))

    if answer == True:
        # read old data
        data = read_data()

        # search element to delete
        number_to_delete = None
        for number, row in enumerate(data):
            if row['name'] == to_delete:
                number_to_delete = number
                break

        if number_to_delete is not None: # it has to compare with None because number can be 0 and bool(0) gives also False
            del data[number_to_delete]
        else:
            msg.showinfo("Button Deleted", "Name '{}' doesn't exist.".format(to_delete))
            return

        for number, row in enumerate(data, 1):
            row['id'] = number

        # write new data
        write_data(data)

        # update dropdown
        delete_combo["value"] = get_column("name")
        delete_combo.set("")

        msg.showinfo("Button Deleted", "{} Button Deleted".format(to_delete))


# --- main --- (lower_case_names)

root = tk.Tk()
root.title('Why am I an idiot')

label_frame = ttk.LabelFrame(text="Delete Button") # 
label_frame.grid(column=0, row=2, padx=20, pady=20) # PEP8: without spaces around =

delete_label = ttk.Label(label_frame, text="Delete Who?: ")
delete_label.grid(row=0, column=0)

names = get_column("name")

delete_combo = ttk.Combobox(label_frame, value=names)
delete_combo.bind("<<ComboboxSelected>>", clicking)
delete_combo.grid(row=0, column=1, columnspan=3, padx=10, pady=10)

delete_button = tk.Button(master=label_frame, text="Delete", command=delete_saved_button)
delete_button.grid(row=1, column=0)

root.mainloop()

PEP 8 - Руководство по стилю для Python Код

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