Python Forum
[Tkinter] Toplevel window
Thread Rating:
  • 0 Vote(s) - 0 Average
  • 1
  • 2
  • 3
  • 4
  • 5
[Tkinter] Toplevel window
#1
Is there a way of getting all toplevel window textvariables without having to name them one at a time?
Any direction is much appreciated.
I welcome all feedback.
The only dumb question, is one that doesn't get asked.
My Github
How to post code using bbtags


Reply
#2
Keep references to them in a container (list, set, dict, whatever is appropriate)? That is, when you create them and add them to the window, also put them in a container.
Reply
#3
Why do you need to know? You should have data, and you should have view, and the less these two know about each other, the better.

In this example I use a tkinter variable to separate data from view. Both windows have a view for data, but neither knows about the other, or how they know about the data.
import tkinter as tk

class MainWindow(tk.Tk):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.title("Main")
        self.entry = tk.Entry(self, width=30)
        self.entry.pack(padx=10, pady=10)

class OtherWindow(tk.Toplevel):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.title("Other")
        self.entry = tk.Entry(self, width=30)
        self.entry.pack(padx=10, pady=10)

app = MainWindow()
other = OtherWindow()
data = tk.StringVar(app, "I am some data")
app.entry.config(textvariable=data)
other.entry.config(textvariable=data)
app.mainloop()
Type in either window the value is automatically updated in the other.
Reply
#4
I was wanting the toplevel window to gain access to the model for saving data. I figured a way out to do that.
I have to windows using the same model.
I welcome all feedback.
The only dumb question, is one that doesn't get asked.
My Github
How to post code using bbtags


Reply
#5
The view should not save data. Your data model should save/restore data.

I cannot find anything in tkinter that returns a list of variables, but you can get all the variables for a window in Tk.

https://tcl.tk/man/tcl8.6/TclCmd/variable.htm

In this example I make a bunch of variables and forget to keep a handle to the variable. I use a call to tk to get a list of all variables that match the pattern "PY_*" and use these names to get the variable values.
import tkinter as tk

root = tk.Tk()
tk.Entry(root, textvariable=tk.StringVar(root, "One")).pack()
tk.Entry(root, textvariable=tk.IntVar(root, 1)).pack()
tk.Entry(root, textvariable=tk.DoubleVar(root, 1.0)).pack()

for var in root.tk.call("info", "vars", "PY_*"):
    print(var, type(var), root.tk.globalgetvar(var))
Output:
PY_VAR0 <class '_tkinter.Tcl_Obj'> One PY_VAR1 <class '_tkinter.Tcl_Obj'> 1 PY_VAR2 <class '_tkinter.Tcl_Obj'> 1.0
Reply
#6
The model and controller does. The model handles all data entries, delete, and edit. The view does only view and the controller communicates with both. I know there are better ways of doing this but, it's helping me get my head around the mvc concept.
I've ran into a problem I thought I already fixed. Had every thing working and started to work on the add new character
method and going to use the same form for both create and edit. My problem came back. I have a delete and create method in the backend.py (where all the model function are). If I use both the delete and create together it does update when changing the guild class. If I change anything else seems to work fine. Just can't change the guild class.

I'll post all the updated code in hope maybe someone can see what I'm missing. If the don't mind looking through a ton of code.


Well I have it all working now. Still a few bugs when entering skills and weapons. converting a list to a dict. (in the convert function). Probably some bugs I haven't encountered yet but, overall seems to work ok.
I have updated the code. Just noticed that I have not added a window to create guilds.


card.py
import tkinter as tk
from tkinter.scrolledtext import ScrolledText
from tkinter import ttk, messagebox
import json
import backend
from os import sys

# The name of our database file
file = 'card.json'

class BasicModel:
    '''
    BasicModel controls all of the methods for calling backend functions that:
    create, delete, and edit. It gets data for displaying in the tkinter view window.
    '''
    def __init__(self, file):
        '''
        Set the instance variables and create the database if it doesn't exist.
        '''
        self.file = file
        backend._create_database(self.file)

    def create_guild(self, guild):
        '''
        Method for creating guilds
        '''
        backend._create_guild(self.file, guild)

    def create_character(self, character):
        '''
        Method for creating characters
        '''
        backend._create(self.file, character)

    def delete_character(self, character, guild):
        '''
        Method for deleting characters
        '''
        backend._delete(self.file, character, guild)

    def get_character(self, name):
        '''
        Method for getting character data by name
        '''
        return backend._get_character(self.file, name)

    def get_all(self):
        '''
        Method for getting all character names
        '''
        return backend._get_all(self.file)

    def get_guilds(self):
        '''
        Method for getting all guilds
        '''
        return backend.get_guilds(self.file)

    def get_character_by_guild(self, guild):
        '''
        Method for getting a character by guild name
        '''
        return backend.get_character_by_guild(self.file, guild)


class View:
    '''
    This is the main view window. It displays all data and buttons
    '''
    def __init__(self, parent):
        '''
        Set some instance variables and build the view
        '''
        self.parent = parent
        self.parent.title('Card Collection')
        self.parent.columnconfigure(0, weight=1)
        self.parent.rowconfigure(0, weight=1)

        # Container
        self.container = tk.Frame(self.parent)
        self.container['borderwidth'] = 1
        self.container['highlightbackground'] = 'lightgray'
        self.container['highlightcolor'] = 'lightgray'
        self.container['highlightthickness'] = 1
        self.container.grid(column=0, row=0, sticky='news', padx=8, pady=8)
        self.container.grid_columnconfigure(0, weight=3)

        # Header
        self.title_label = tk.Label(self.container, text='Card Collection')
        self.title_label['bg'] = 'gray86'
        self.title_label['font'] = ('"" 16 bold')
        self.title_label['relief'] = 'groove'
        self.title_label.grid(column=0, row=0, sticky='new', pady=4, padx=4, ipady=4)

        # Search Frame
        self.search_frame = tk.Frame(self.container, padx=4, pady=4)
        self.search_frame['highlightcolor'] = 'gray80'
        self.search_frame['highlightbackground'] = 'gray80'
        self.search_frame['highlightthickness'] = 1
        self.search_frame.grid(column=0, row=1, sticky='news', pady=4, padx=5)
        self.search_frame.grid_columnconfigure(0, weight=0)
        self.search_frame.grid_columnconfigure(1, weight=2)
        self.search_frame.grid_columnconfigure(2, weight=3)


        # Search Boxes
        self.search_label = tk.Label(self.search_frame, text='Search by ...')
        self.search_label.grid(column=0, row=0, sticky='new', padx=(2, 2))

        # Create combobox
        self.show_ = tk.StringVar()
        self.menu = ttk.Combobox(self.search_frame, justify='left', state='readonly', \
        cursor='hand2', textvariable=self.show_)
        self.menu.grid(column=1, row=0, sticky='new', padx=(2, 1))

        # Instrcution label
        info_label = tk.Label(self.search_frame, bg='lightyellow')
        info_label['text'] = 'Doubleclick a name to view stats.'
        info_label['relief'] = 'groove'
        info_label.grid(column=2, row=0, sticky='new', padx=(1, 0), ipady=1)

        # Mid Frame Container
        self.mid_container = tk.Frame(self.container)
        self.mid_container.grid(column=0, row=2, sticky='new', pady=4, padx=4)
        self.mid_container.grid_columnconfigure(0, weight=3, uniform='box')
        self.mid_container.grid_columnconfigure(1, weight=3, uniform='box')

        # Container for the listbox
        self.list_container = tk.Frame(self.mid_container)
        self.list_container.grid(column=0, row=0, sticky='news')
        self.list_container.grid_columnconfigure(0, weight=3)

        # listbox for left side
        self.listbox = tk.Listbox(self.list_container, height=15, selectmode='single')
        self.listbox.grid(column=0, row=0, sticky='news')

        # Create a scrollbar for the list
        self.scrollbar = tk.Scrollbar(self.list_container)
        self.scrollbar.grid(column=1, row=0, sticky='ns')
        self.listbox.configure(yscrollcommand = self.scrollbar.set)
        self.scrollbar.config(command = self.listbox.yview)

        # Button Conainer
        self.btn_container = tk.Frame(self.container)
        self.btn_container['highlightcolor'] = 'gray86'
        self.btn_container['highlightbackground'] = 'gray86'
        self.btn_container['highlightthickness'] = 1
        self.btn_container.grid(column=0, row=3, sticky='news', padx=4, pady=4)
        for i in range(4):
            self.btn_container.grid_columnconfigure(i, weight=3, uniform='buttons')

        # Create some buttons
        self.add = tk.Button(self.btn_container, text='Add')
        self.add['cursor'] = 'hand2'
        self.add['bg'] = 'skyblue'
        self.add.grid(column=0, row=0, sticky='new', padx=2, pady=4)

        self.edit = tk.Button(self.btn_container, text='Edit')
        self.edit['cursor'] = 'hand2'
        self.edit['bg'] = 'orange'
        self.edit.grid(column=1, row=0, sticky='new', padx=2, pady=4)

        self.delete = tk.Button(self.btn_container, text='Delete')
        self.delete['cursor'] = 'hand2'
        self.delete['bg'] = 'orangered'
        self.delete.grid(column=2, row=0, sticky='new', padx=2, pady=4)

        self.exit = tk.Button(self.btn_container, text='Exit')
        self.exit['bg'] = 'tomato'
        self.exit['command'] = sys.exit
        self.exit['cursor'] = 'hand2'
        self.exit.grid(column=3, row=0, sticky='new', padx=2, pady=4)


class TopWindow:
    def __init__(self):
        '''
        TopWindow contains the form and buttons for creating and editing characters
        '''
        self.window = tk.Toplevel()
        self.window.geometry('480x208+200+200')
        self.window.title(None)
        self.window['padx'] = 8
        self.window['pady'] = 4
        self.window.resizable(False, False)

        self.window.columnconfigure(0, weight=1)
        self.window.rowconfigure(0, weight=1)

        self.container = tk.Frame(self.window)
        self.container.grid(column=0, row=0, sticky='new')
        self.container.columnconfigure(0, weight=3)

        self.header = tk.Label(self.container)
        self.header['font'] = '"" 14 italic'
        self.header['bg'] = 'silver'
        self.header['relief'] = 'groove'
        self.header.grid(column=0, row=0, sticky='new')

        mid_frame = tk.Frame(self.container)
        mid_frame.grid(column=0, row=1, sticky='new')
        mid_frame.grid_columnconfigure(0, weight=1)
        mid_frame.grid_columnconfigure(1, weight=2)

        self.name_header = tk.Label(mid_frame, text='Name:', anchor='w')
        self.name_header['font'] = '"" 10 bold'
        self.name_header['highlightcolor'] = 'gray'
        self.name_header['highlightbackground'] = 'gray'
        self.name_header['highlightthickness'] = 1
        self.name_header['borderwidth'] = 1
        self.name_header.grid(column=0, row=0, sticky='new', padx=(1,2))

        self.namevar = tk.StringVar()
        self.name_label = tk.Entry(mid_frame)
        self.name_label['textvariable'] = self.namevar
        self.name_label['highlightcolor'] = 'gray'
        self.name_label['highlightbackground'] = 'gray'
        self.name_label['highlightthickness'] = 1
        self.name_label['borderwidth'] = 1
        self.name_label.grid(column=1, row=0, sticky='new', padx=(2,2))



        age_header = tk.Label(mid_frame, text='Age:', anchor='w')
        age_header['font'] = '"" 10 bold'
        age_header['highlightcolor'] = 'gray'
        age_header['highlightbackground'] = 'gray'
        age_header['highlightthickness'] = 1
        age_header['borderwidth'] = 1
        age_header.grid(column=0, row=1, sticky='new', padx=(1,2), pady=(2,2))

        self.agevar = tk.StringVar()
        self.age = tk.Entry(mid_frame)
        self.age['textvariable'] = self.agevar
        self.age['highlightcolor'] = 'gray'
        self.age['highlightbackground'] = 'gray'
        self.age['highlightthickness'] = 1
        self.age['borderwidth'] = 1
        self.age.grid(column=1, row=1, sticky='new', padx=(2,2), pady=(2,2))

        guild_header = tk.Label(mid_frame, text='Guild:', anchor='w')
        guild_header['font'] = '"" 10 bold'
        guild_header['highlightcolor'] = 'gray'
        guild_header['highlightbackground'] = 'gray'
        guild_header['highlightthickness'] = 1
        guild_header['borderwidth'] = 1
        guild_header.grid(column=0, row=2, sticky='new', padx=(1,2), pady=(2,2))

        self.guildvar = tk.StringVar()
        self.guild = ttk.Combobox(mid_frame, state='readonly', cursor='hand2')
        self.guild['textvariable'] = self.guildvar
        self.guild.grid(column=1, row=2, sticky='new', padx=(2,2), pady=(2,2))

        skills_header = tk.Label(mid_frame, text='Skills:', anchor='w')
        skills_header['font'] = '"" 10 bold'
        skills_header['highlightcolor'] = 'gray'
        skills_header['highlightbackground'] = 'gray'
        skills_header['highlightthickness'] = 1
        skills_header['borderwidth'] = 1
        skills_header.grid(column=0, row=3, sticky='new', padx=(1,2), pady=(2,2))

        self.skillsvar = tk.StringVar()
        self.skills = tk.Entry(mid_frame)
        self.skills['textvariable'] = self.skillsvar
        self.skills['highlightcolor'] = 'gray'
        self.skills['highlightbackground'] = 'gray'
        self.skills['highlightthickness'] = 1
        self.skills['borderwidth'] = 1
        self.skills.grid(column=1, row=3, sticky='new', padx=(2,2), pady=(2,2))

        weapons_header = tk.Label(mid_frame, text='Weapons:', anchor='w')
        weapons_header['font'] = '"" 10 bold'
        weapons_header['highlightcolor'] = 'gray'
        weapons_header['highlightbackground'] = 'gray'
        weapons_header['highlightthickness'] = 1
        weapons_header['borderwidth'] = 1
        weapons_header.grid(column=0, row=4, sticky='new', padx=(1,2), pady=(2,2))

        self.weaponsvar = tk.StringVar()
        self.weapons = tk.Entry(mid_frame)
        self.weapons['textvariable'] = self.weaponsvar
        self.weapons['highlightcolor'] = 'gray'
        self.weapons['highlightbackground'] = 'gray'
        self.weapons['highlightthickness'] = 1
        self.weapons['borderwidth'] = 1
        self.weapons.grid(column=1, row=4, sticky='new', padx=(2,2), pady=(2,2))

        btn_container = tk.Frame(mid_frame)
        btn_container.grid(column=0, columnspan=2, row=5, sticky='new')
        btn_container.grid_columnconfigure(0, weight=3, uniform='buttons')
        btn_container.grid_columnconfigure(1, weight=3, uniform='buttons')

        self.save_button = tk.Button(btn_container)
        self.save_button['text'] = 'Save'
        self.save_button['bg'] = 'skyblue'
        self.save_button['cursor'] = 'hand2'
        self.save_button.grid(column=0, row=0, sticky='new', padx=(0,1), pady=(4.0))

        self.cancel_button = tk.Button(btn_container)
        self.cancel_button['text'] = 'Cancel'
        self.cancel_button['cursor'] = 'hand2'
        self.cancel_button['bg'] = 'orangered'
        self.cancel_button.grid(column=1, row=0, sticky='new', padx=(1,0), pady=(4,0))


class Controller:
    def __init__(self, model, view):
        '''
        Controller handles all communiction and data transfer between the View and BasicModel
        '''
        # Set some instance variables
        self.model = model
        self.view = view

        # Give our buttons in view some functionality
        self.view.edit['command'] = lambda: Controller2(self.model, self.view, target='edit', action='edit')
        self.view.add['command'] = lambda: Controller2(self.model, self.view, target='add')
        self.view.delete['command'] = lambda: self.delete_character()

    def populate(self):
        '''
        populate does just that. It populates the combobox in View and sets a bind
        to display character names in the listbox.
        '''
        items = ['Names']
        items = items + self.model.get_guilds()
        self.view.menu['values'] = items
        self.view.menu.current(0)
        self.view.menu.bind('<<ComboboxSelected>>', lambda x: self.get_guild())

        for i, data in enumerate(self.model.get_all()):
            self.view.listbox.insert(i, data.title())
        self.view.listbox.select_set(0)
        self.view.listbox.bind('<Double-Button-1>', lambda x: self.get_character())

        # Call self.body to give the label_frame on the right labels and data display
        self.body()

    def get_guild(self):
        '''
        get_guild changes the listbox display by guild name in the menu combobox
        '''
        self.view.listbox.delete(0, tk.END)
        guild = self.view.menu.get().lower()
        if guild == 'names':
            data = self.model.get_all()
        else:
            data = self.model.get_character_by_guild(guild)

        for i, name in enumerate(data):
            self.view.listbox.insert(i, name.title())
        self.view.listbox.select_set(0)
        self.body()

    def get_character(self):
        '''
        Calls self.body to display labels and character info
        '''
        self.body()

    def delete_character(self):
        '''
        Method calls BasicModel to delete a character
        '''
        try:
            character = self.view.listbox.get(self.view.listbox.curselection()[0]).lower()
            data = self.model.get_character(character)

            confirm = messagebox.askyesno('Warning!',f'Are you sure you want to delete {character.title()}?')
            if confirm:
                self.model.delete_character(character, data['guild'])
                self.view.listbox.delete(0, tk.END)
                self.view.menu.current(0)
                self.label_frame.destroy()
                self.populate()
                messagebox.showinfo('Character Deleted', f'{character.title()} has been deleted.')
            else:
                print('Action has been canceled.')
        except IndexError:
            messagebox.showerror('No Character', 'There is no character to delete.')

    def frame(self):
        '''
        Creates a frame for self.body to have a container for the labels
        '''
        self.label_frame = tk.Frame(self.view.mid_container, pady=2, padx=4)
        self.label_frame['highlightcolor'] = 'gray80'
        self.label_frame['highlightbackground'] = 'gray80'
        self.label_frame['highlightthickness'] = 1
        self.label_frame.grid(column=1, row=0, sticky='news', padx=(4,0))
        self.label_frame.grid_columnconfigure(0, weight=0)
        self.label_frame.grid_columnconfigure(1, weight=3)

    def body(self):
        '''
        Method for formatting and displaying character data
        '''
        error = False
        try:
            character = self.view.listbox.get(self.view.listbox.curselection()[0])
        except IndexError:
            error = True

        if error:
            pass
        else:
            data = self.model.get_character(character.lower())
            self.frame()

            name_label_list = []
            ability_label_list = []
            spell_data_list = []
            weapons_data_list = []

            for i, item in enumerate(data):
                name_label_list.append(tk.Label(self.label_frame, anchor='w'))
                ability_label_list.append(tk.Label(self.label_frame, anchor='w', wraplength=250))

                if isinstance(data[item], dict) and item == 'skills':
                    for key, value in data[item].items():
                        spell_data_list.append(f'{key.title()}: {value}')
                        value = ', '.join(spell_data_list)

                elif isinstance(data[item], dict) and item == 'weapons':
                    for key, value in data[item].items():
                        weapons_data_list.append(f'{key.title()}: {value}')
                        value = ', '.join(weapons_data_list)

                else:
                    value = data[item].title() if isinstance(data[item], str) else data[item]
                name_label_list[i]['text'] = f'{item.upper()}:'
                name_label_list[i].grid(column=0, row=i, sticky='new')
                ability_label_list[i]['text'] = value
                ability_label_list[i].grid(column=1, row=i, sticky='new',padx=8)


class Controller2(Controller):
    '''
    Controller2 handles all communication between TopWindow and BasicModel
    '''
    def __init__(self, model, view, target=None, action=None):
        '''
        Set some instance variables
        '''
        super().__init__(model, view)
        self.model = model
        self.view = view

        # self.action is a variable for determining if we want to create or edit a character
        self.action = action

        # Open TopWindow and fill the guild combobox and set functionality for the buttons
        self.topwindow = TopWindow()
        self.topwindow.guild['values'] = self.model.get_guilds()
        self.topwindow.guild.current(0)
        self.topwindow.cancel_button['command'] = self.cancel
        self.topwindow.save_button['command'] = self.save

        # Do we want to create or edit
        if target == 'edit':
            self.edit()

        if target == 'add':
            self.add()

    def edit(self):
        '''
        Populates the TopWindow form with character data if editing.
        Disables the name field if editing
        '''
        if self.action == 'edit':
            self.topwindow.name_label['state'] = 'disabled'
        self.topwindow.window.title('Edit Character')
        character = self.view.listbox.get(self.view.listbox.curselection())
        self.data = self.model.get_character(character)
        self.topwindow.header['text'] = f'Editing {character}'
        self.topwindow.namevar.set(self.data['name'])
        self.topwindow.agevar.set(self.data['age'])
        self.topwindow.agevar.trace_add('write', self.check_age)
        self.topwindow.guildvar.set(self.data['guild'])

        # try block to see if any data exist for skills. If not places unknown in the field
        try:
            skill_list = []
            for skill, level in self.data['skills'].items():
                skill_list.append(f'{skill.title()}: {level}')
            self.topwindow.skillsvar.set(', '.join(skill_list))
        except:
            self.topwindow.skillsvar.set('Unknown')

        # Same as above but with the weapons field
        try:
            weapons_list = []
            for weapon, level in self.data['weapons'].items():
                weapons_list.append(f'{weapon}: {level}')
            self.topwindow.weaponsvar.set(', '.join(weapons_list))
        except:
            self.topwindow.weaponsvar.set('No Weapons')

    def check_age(self, agevar, index, mode):
        '''
        Method for entering a valid age. Limited to three characters. e.g. 999 is max
        '''
        agevar = self.topwindow.agevar.get()
        if agevar:
            if len(agevar) > 3:
                messagebox.showerror(title='Error!', message='Age can\'t be more than 3 digits long')
                self.topwindow.agevar.set(self.data['age'])
            try:
                agevar = int(agevar)
            except:
                messagebox.showerror('Error!', 'Only numbers can be entered.')
                self.topwindow.agevar.set(self.data['age'])

    def cancel(self):
        '''
        We don't want to continue with editing/creating a character.
        Destroy TopWindow
        '''
        self.topwindow.window.destroy()

    def add(self):
        '''
        Sets window and header text
        '''
        self.topwindow.window.title('Add new character')
        self.topwindow.header['text'] = 'Create a character'

    def save(self):
        '''
        Method for saving a character
        '''
        # Get character data from TopWindow
        name = self.topwindow.namevar.get()
        guild = self.topwindow.guildvar.get().lower()
        age = self.topwindow.agevar.get()
        skills = self.topwindow.skillsvar.get()
        weapons = self.topwindow.weaponsvar.get()

        # If no name was entered display a message. Character must have a name to be created
        if not name:
            messagebox.showerror('No Name', 'Your character must have a name')
        else:
            # Set some variables
            self.guild = guild.title()
            self.name = name

            # Creating a dict to be entered into the json file
            character = {
            'name': name,
            'guild': guild,
            'age': age,
            'skills': self.convert(skills),
            'weapons': self.convert(weapons)
            }

            # If there are any empty fields, fill with unknown
            for key, value in character.items():
                if len(character[key]) == 0:
                    character[key] = 'unknown'

            # If editing delete the old character and create the new with updated fields.
            # Else we are creating a new character and don't need to delete.
            if self.action == 'edit':
                old_guild = self.model.get_character(name)
                self.model.delete_character(name, old_guild['guild'])
                self.model.create_character(character)
                messagebox.showinfo('Updated', f'{name.title()} has been updated')
            else:
                self.model.create_character(character)
                messagebox.showinfo('Created', f'{name.title()} has been created')

            # Call self.populate
            self.populate()

            # Destroy TopWindow
            self.topwindow.window.destroy()

    def get_character(self):
        '''
        Same as in Controller
        '''
        self.body()

    def body(self):
        '''
        Same as in Controller. Populates the View and goes to the new or edited character
        '''
        error = False
        # try block for checking if there is any characters in the listbox
        try:
            character = self.view.listbox.get(self.view.listbox.curselection()[0])
        except IndexError:
            error = True

        # If there is an error, ignore
        # Else no error format and populate the View
        if error:
            pass
        else:
            data = self.model.get_character(character.lower())
            self.frame()

            name_label_list = []
            ability_label_list = []
            spell_data_list = []
            weapons_data_list = []

            for i, item in enumerate(data):
                name_label_list.append(tk.Label(self.label_frame, anchor='w'))
                ability_label_list.append(tk.Label(self.label_frame, anchor='w', wraplength=250))

                if isinstance(data[item], dict) and item == 'skills':
                    for key, value in data[item].items():
                        spell_data_list.append(f'{key.title()}: {value}')
                        value = ', '.join(spell_data_list)

                elif isinstance(data[item], dict) and item == 'weapons':
                    for key, value in data[item].items():
                        weapons_data_list.append(f'{key.title()}: {value}')
                        value = ', '.join(weapons_data_list)

                else:
                    value = data[item].title() if isinstance(data[item], str) else data[item]
                name_label_list[i]['text'] = f'{item.upper()}:'
                name_label_list[i].grid(column=0, row=i, sticky='new')
                ability_label_list[i]['text'] = value
                ability_label_list[i].grid(column=1, row=i, sticky='new',padx=8)

    def convert(self, string):
        '''
        Converts a string to dict for weapons and skills
        Has some bugs that need to be fixed
        '''
        if string == 'No Weapons':
            return 'No Weapons'
        words = []
        for word in string.split(' '):
            words.append(word.strip(':').strip(','))

        it = iter(words)
        result = dict(zip(it,it))
        return result

    def populate(self):
        '''
        Populates the View with data
        '''
        items = ['Names']
        items = items + self.model.get_guilds()
        self.view.menu['values'] = items

        for i, guild in enumerate(items):
            if self.guild == guild:
                self.view.menu.current(i)

        self.view.listbox.delete(0, tk.END)

        self.get_guild(guild)

        index = 0
        for i, name in enumerate(self.view.listbox.get(0, tk.END)):
            if name.lower() == self.name.lower():
                index = i
        self.view.listbox.select_set(index)

        self.body()

    def get_guild(self, guild):
        '''
        Populates the menu of View
        '''
        self.view.listbox.delete(0, tk.END)
        guild = self.view.menu.get().lower()
        if guild == 'names':
            data = self.model.get_all()
        else:
            data = self.model.get_character_by_guild(guild)

        for i, name in enumerate(data):
            self.view.listbox.insert(i, name.title())
        self.body()

def main():
    app = tk.Tk()
    app.geometry('600x400+200+200')
    controller = Controller(BasicModel(file), View(app))
    controller.populate()
    app.mainloop()


if __name__ == '__main__':
    main()
backend.py
from os import path
import json
from itertools import chain
from collections import OrderedDict

file = 'card.json'

def _create_database(file):
    '''
    Check for or create the database
    '''
    if path.exists(file):
        pass
    else:
        with open(file, 'w') as out_file:
            characters = {}
            json.dump(characters, out_file, indent=4)

def _create_guild(file, guild):
    '''
    Function checks the json file for already existing guildss.
    If one exist it return a message. If one does not exist it
    will create the guild
    '''
    with open(file, 'r+') as json_file:
        data = json.load(json_file)
        if guild in data:
            print('Guild already exists')
        else:
            data[guild] = []
            json_file.seek(0)
            json.dump(data, json_file, indent=4)
            print(f'Guild {guild} created')

def _check_name(file, name):
    '''
    Checks to see if a character already exists in the database
    '''
    with open(file, 'r') as json_file:
        data = json.load(json_file)
        if name.lower() in [c.get('name') for c in chain(*data.values())]:
            return True
        return False

def _create(file, character):
    '''
    Check if character name already exists. If it does return true otherwise,
    return false and create the character
    '''
    check = _check_name(file, character['name'])
    if check:
        print(f'{character["name"].title()} already exists in the database.')
    else:
        with open(file, 'r+') as json_file:
            data = json.load(json_file)
            data[character['guild']].append(character)
            json_file.seek(0)
            json.dump(data, json_file, indent=4)
            print(f'{character["name"].title()} has been created.')

def _delete(file, character_name=None, guild=None):
    '''
    Function searches for a character by name and deletes the character
    '''
    ok = _check_name(file, character_name)
    if not ok:
        return 'Chacrater does not exists'
    with open(file, 'r') as json_file:
        data = OrderedDict(json.load(json_file))
        glen = len(data[guild.lower()])

        for i, name in enumerate(data[guild.lower()]):
            if character_name in name.values():
                data[guild.lower()].pop(i)

        with open(file, 'w') as json_file:
            json.dump(data, json_file, indent=4)
        print(f'{character_name.title()} has been deleted')

def _get_character(file, name):
    '''
    This function will return all related data to a character entered.
    '''
    with open(file, 'r+') as json_file:
        data = json.load(json_file)
        for _ in data:
            for i in range(len(data[_])):
                if name.lower() == data[_][i]['name']:
                    character = data[_][i]
                    return character

    return f'{name.title()} does not exist.'

def _get_all(file):
    '''
    Returns all characters
    '''
    with open(file, 'r') as json_file:
        data = json.load(json_file)
        characters = [c.get('name') for c in chain(*data.values())]
        return characters

def get_guilds(file):
    '''
    Returns all guilds
    '''
    guilds = []
    with open(file, 'r') as json_file:
        data = json.load(json_file)
        for _ in data:
            guilds.append(_.title())
    return guilds

def get_character_by_guild(file, guild):
    '''
    Returns characters by guild
    '''
    with open(file, 'r') as json_file:
        data = json.load(json_file)
        names = []
        if guild.lower() in [character.get('guild') for character in chain(*data.values())]:
            for character in data[guild]:
                if character:
                    names.append(character['name'])
        return names
card.json
{
    "wizard": [
        {
            "name": "tabol",
            "guild": "wizard",
            "age": 23,
            "skills": {
                "fireball": 20,
                "meteor": 15
            }
        },
        {
            "name": "zac",
            "guild": "wizard",
            "age": 25,
            "skills": {
                "fireball": 10,
                "meteor": 5
            }
        },
        {
            "name": "tony",
            "guild": "wizard",
            "age": 25,
            "skills": {
                "fireball": 10,
                "meteor": 5
            }
        },
        {
            "name": "ralph",
            "guild": "wizard",
            "age": 20,
            "skills": {
                "fireball": 15,
                "meteor": 5
            }
        }
    ],
    "cleric": [],
    "warrior": [
        {
            "name": "randy",
            "guild": "warrior",
            "age": 27,
            "skills": {
                "hand to hand": 20,
                "iron fist": 15
            },
            "weapons": {
                "sword": 20,
                "knife": 10
            }
        }
    ]
}
I welcome all feedback.
The only dumb question, is one that doesn't get asked.
My Github
How to post code using bbtags


Reply


Possibly Related Threads…
Thread Author Replies Views Last Post
  Interaction between Matplotlib window, Python prompt and TKinter window NorbertMoussy 3 485 Mar-17-2024, 09:37 AM
Last Post: deanhystad
  pass a variable between tkinter and toplevel windows janeik 10 2,320 Jan-24-2024, 06:44 AM
Last Post: Liliana
  [Tkinter] Open tkinter colorchooser at toplevel (so I can select/focus on either window) tabreturn 4 1,900 Jul-06-2022, 01:03 PM
Last Post: deanhystad
  [Tkinter] Not able to get image as background in a Toplevel window finndude 4 3,908 Jan-07-2022, 10:10 PM
Last Post: finndude
  [Tkinter] Images in Toplevel() finndude 4 4,285 Mar-09-2021, 09:39 AM
Last Post: finndude
  Create image on a Toplevel using tkinter ViktorWong 3 7,806 Jun-13-2020, 03:21 PM
Last Post: deanhystad
  [Tkinter] Connect Toplevel Radiobuttons to root Label/Entry widgets iconit 2 2,440 Apr-28-2020, 06:50 AM
Last Post: iconit
  Transfer Toplevel window entry to root window entry with TKinter HBH 0 4,461 Jan-23-2020, 09:00 PM
Last Post: HBH
  tkinter window and turtle window error 1885 3 6,709 Nov-02-2019, 12:18 PM
Last Post: 1885
  [Tkinter] how can disable menu [About] when Toplevel is active balenaucigasa 0 2,661 Oct-25-2019, 09:49 PM
Last Post: balenaucigasa

Forum Jump:

User Panel Messages

Announcements
Announcement #1 8/1/2020
Announcement #2 8/2/2020
Announcement #3 8/6/2020