Apr-17-2022, 06:36 AM
Apr-17-2022, 10:08 AM
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.
Apr-17-2022, 03:09 PM
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.
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.
Apr-18-2022, 12:22 AM
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 have to windows using the same model.
Apr-18-2022, 04:38 AM
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.
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
Apr-18-2022, 06:01 PM
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
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 namescard.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 } } ] }