Apr-16-2022, 10:31 AM
Is it possible to have a controller to control two different views?
I wasn't really wanting to post the whole code but, to show the goal.
I have four buttons that will open a toplevel window. My goal is to get this window to interact with the controller.
Any thoughts or suggestion are appreciated.
I've updated the code with the solution I found. Also added images of my progress
card.json
I wasn't really wanting to post the whole code but, to show the goal.
I have four buttons that will open a toplevel window. My goal is to get this window to interact with the controller.
Any thoughts or suggestion are appreciated.
I've updated the code with the solution I found. Also added images of my progress
card.json
{ "wizard": [ { "class": "wizard", "name": "tabol", "age": 23, "skills": { "fireball": 20, "meteor": 15 } }, { "class": "wizard", "name": "zac", "age": 25, "skills": { "fireball": 10, "meteor": 5 } }, { "class": "wizard", "name": "ralph", "age": 20, "skills": { "fireball": 15, "meteor": 5 } } ], "cleric": [ { "class": "cleric", "name": "harold", "age": 18, "skills": { "heal": 15, "heal all": 20 } } ], "warrior": [ { "class": "warrior", "name": "tony", "age": 25, "skills": { "knives": 8, "hand to hand": 20, "swords": 15 } } ], "mage": [ { "class": "mage", "name": "henry", "age": 21, "skills": { "bolt": 23, "dart": 16 } } ] }backend.py
from os import path import json from itertools import chain file = 'card.json' def _create_database(file): if path.exists(file): pass else: with open(file, 'w') as out_file: characters = {} json.dump(characters, out_file, indent=4) def _create_class(file, character_class): ''' Function checks the json file for already existing classes. If one exist it return a message. If one does not exist it will create the class ''' with open(file, 'r+') as json_file: data = json.load(json_file) if character_class in data: print('Class already exists') else: data[character_class] = [] json_file.seek(0) json.dump(data, json_file, indent=4) print(f'Class {character_class} created') def _check_name(file, name): 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 = _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['class']].append(character) json_file.seek(0) json.dump(data, json_file, indent=4) print(f'{character["name"].title()} has been created.') def _delete(file, name): with open(file, 'r+') as json_file: data = json.load(json_file) ok = _check_name(file, name) if ok: for _ in data: for i in range(len(data[_])): if name.lower() in data[_][i]['name']: data[_].pop(i) with open(file, 'w') as json_file: json.dump(data, json_file, indent=4) print(f'{name.title()} has been deleted') else: print(f'{name.title()} is not in the database') 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): with open(file, 'r') as json_file: data = json.load(json_file) characters = [c for c in chain(*data.values())] return characters def _update(file, old, new): character = old.copy() character.update(new) _delete(file, old['name']) _create(file, character) print(f'{character["name"].title()} has been updated.') def get_classes(file): classes = [] with open(file, 'r') as json_file: data = json.load(json_file) for _ in data: classes.append(_.title()) return classes def get_character_by_guild(file, guild): with open(file, 'r') as json_file: data = json.load(json_file) names = [] if guild.lower() in [character.get('class') for character in chain(*data.values())]: for character in data[guild]: if character: names.append(character['name']) return namescard.py
import tkinter as tk from tkinter.scrolledtext import ScrolledText from tkinter import ttk, messagebox import json import backend from os import sys file = 'card.json' class BasicModel: def __init__(self, file): self.file = file backend._create_database(self.file) def create_class(self, character_class): backend._create_class(self.file, character_class) def create_character(self, character): backend._create(self.file, character) def get_character(self, name): return backend._get_character(self.file, name) def get_all(self): return backend._get_all(self.file) def update(self, old, new): return backend._update(self.file, old, new) def get_classes(self): return backend.get_classes(self.file) def get_character_by_guild(self, guild): return backend.get_character_by_guild(self.file, guild) class View: def __init__(self, parent): self.parent = parent self.parent.title('Card Collection') self.parent.columnconfigure(0, weight=1) self.parent.rowconfigure(0, weight=1) # Container container = tk.Frame(self.parent) container['borderwidth'] = 1 container['highlightbackground'] = 'lightgray' container['highlightcolor'] = 'lightgray' container['highlightthickness'] = 1 container.grid(column=0, row=0, sticky='news', padx=8, pady=8) container.grid_columnconfigure(0, weight=3) # Header self.title_label = tk.Label(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 search_frame = tk.Frame(container, padx=4, pady=4) search_frame['highlightcolor'] = 'gray80' search_frame['highlightbackground'] = 'gray80' search_frame['highlightthickness'] = 1 search_frame.grid(column=0, row=1, sticky='news', pady=4, padx=4) # Search Boxes show_ = tk.StringVar() self.menu = ttk.Combobox(search_frame, textvariable=show_) self.menu.grid(column=0, row=0, sticky='new') # Mid Frame Container mid_container = tk.Frame(container) mid_container.grid(column=0, row=2, sticky='new', pady=4, padx=4) mid_container.grid_columnconfigure(0, weight=3, uniform='box') mid_container.grid_columnconfigure(1, weight=3, uniform='box') list_container = tk.Frame(mid_container) list_container.grid(column=0, row=0, sticky='news') list_container.grid_columnconfigure(0, weight=3) # Textbox for left side self.left_listbox = tk.Listbox(list_container, height=15, selectmode='single') self.left_listbox.grid(column=0, row=0, sticky='news') scrollbar = tk.Scrollbar(list_container) scrollbar.grid(column=1, row=0, sticky='ns') self.left_listbox.configure(yscrollcommand = scrollbar.set) scrollbar.config(command = self.left_listbox.yview) # Right Side self.label_frame = tk.Frame(mid_container, pady=8) 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') # Button Conainer btn_container = tk.Frame(container) btn_container['highlightcolor'] = 'gray86' btn_container['highlightbackground'] = 'gray86' btn_container['highlightthickness'] = 1 btn_container.grid(column=0, row=3, sticky='news', padx=4, pady=4) for i in range(4): btn_container.grid_columnconfigure(i, weight=3, uniform='buttons') self.add = tk.Button(btn_container, text='Add') self.add['cursor'] = 'hand2' self.add.grid(column=0, row=0, sticky='new', padx=2, pady=4) self.edit = tk.Button(btn_container, text='Edit') self.edit['cursor'] = 'hand2' self.edit.grid(column=1, row=0, sticky='new', padx=2, pady=4) self.delete = tk.Button(btn_container, text='Delete') self.delete['cursor'] = 'hand2' self.delete.grid(column=2, row=0, sticky='new', padx=2, pady=4) self.exit = tk.Button(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, parent, title=None, target=None): self.parent = parent self.window = tk.Toplevel() self.window.geometry('400x300+250+250') self.window.title(title.title()) self.window.columnconfigure(0, weight=1) self.window.rowconfigure(0, weight=1) self.container = tk.Frame(self.window, padx=8) self.container.grid(column=0, row=0, sticky='new') self.container.columnconfigure(0, weight=0) self.container.columnconfigure(1, weight=3) if target == 'create': self.create_character() if target == 'edit': self.edit_character() if target == 'delete': self.delete_character() def create_character(self): self.widget_container() def edit_character(self): ttk.Style().configure('field.TEntry', padding='4 2 2 2') self.agevar = tk.StringVar() name = ttk.Label(self.container, text='Name:', anchor='w') name.grid(column=0, row=1, sticky='new', padx=(4, 0), pady=(8,0)) self.name_field = ttk.Entry(self.container, style='field.TEntry') self.name_field.grid(column=1, row=1, sticky='new', padx=(8, 0), pady=(8,0)) guild = ttk.Label(self.container, text='Class:', anchor='w') guild.grid(column=0, row=2, sticky='new', padx=(4, 0), pady=(8, 0)) self.guild_field = ttk.Entry(self.container, style='field.TEntry') self.guild_field.grid(column=1, row=2, sticky='new', padx=(8, 0), pady=(8, 0)) age = ttk.Label(self.container, text='Age') age.grid(column=0, row=3, sticky='new', padx=(4, 0), pady=(8, 0)) self.age_field = ttk.Entry(self.container, textvariable=self.agevar, style='field.TEntry') self.age_field.grid(column=1, row=3, sticky='new', padx=(8, 280), pady=(8, 0)) skills = ttk.Label(self.container, text='Skills:', anchor='w') skills.grid(column=0, row=4, sticky='new', padx=(4, 0), pady=(8, 0)) self.skills_field = ttk.Entry(self.container, style='field.TEntry') self.skills_field.grid(column=1, row=4, sticky='new', padx=(8, 0), pady=(8, 0)) self.agevar.trace_add('write', self.limit_size) def delete_character(self): print('deleting') def limit_size(self, *args): agevar = self.agevar.get() if len(agevar) > 3: messagebox.showerror(title='Error!', message='Age can\'t be more than 3 digits long') self.age_field.delete(0, tk.END) try: agevar = int(agevar) except: messagebox.showerror('Error!', 'Only numbers can be entered.') self.age_field.delete(0, tk.END) class Controller: def __init__(self, model, view): self.model = model self.view = view data = self.model.get_classes() data.insert(0, 'Show by ...') data.insert(1, 'Names') self.view.menu['values'] = data self.view.menu.current(0) self.view.menu.bind('<<ComboboxSelected>>', self.listbox_update) def show_all(self): characters = self.model.get_all() for i, character in enumerate(characters): name = f'{" ":<2}{character["name"].title()}' self.view.left_listbox.insert(i, name) self.view.left_listbox.bind('<Double-Button-1>', self.show_character) self.view.left_listbox.selection_set(0) char = self.view.left_listbox.get(self.view.left_listbox.curselection()) self.first_name(char) def show_guild(self, guild): characters = self.model.get_character_by_guild(guild.lower()) for i, character in enumerate(characters): name = f'{" ":<2}{character.title()}' self.view.left_listbox.insert(i, name) self.view.left_listbox.selection_set(0) char = self.view.left_listbox.get(self.view.left_listbox.curselection()) self.view.left_listbox.bind('<Double-Button-1>', self.show_character) self.first_name(char) def first_name(self, name): character = name.lower().strip() data = self.model.get_character(character) frame = self.view.label_frame frame.grid_columnconfigure(0, weight=3) row = 0 mylist = [] for key, value in data.items(): if isinstance(value, dict): for k, v in value.items(): mylist.append(f'{k}: {v}') value = ', '.join(mylist) elif isinstance(value, int): value = value else: value = value.title() line = tk.Label(frame, text=f'{key.upper()}: {value}', anchor='w') line['relief'] = 'groove' line.grid(column=0, row=row, sticky='new', padx=8, pady=1) row += 1 def show_character(self, event): character = self.view.left_listbox.get(self.view.left_listbox.curselection()) character = character.lower().strip() data = self.model.get_character(character) frame = self.view.label_frame frame.grid_columnconfigure(0, weight=3) row = 0 mylist = [] for key, value in data.items(): if isinstance(value, dict): for k, v in value.items(): mylist.append(f'{k}: {v}') value = ', '.join(mylist) elif isinstance(value, int): value = value else: value = value.title() self.line = tk.Label(frame, text=f'{key.upper()}: {value}', anchor='w') self.line['relief'] = 'groove' self.line.grid(column=0, row=row, sticky='new', padx=8, pady=1) row += 1 def listbox_update(self, event): search = self.view.menu.get() guilds = self.model.get_classes() self.view.left_listbox.delete(0, tk.END) if search.lower() == 'names': self.show_all() elif search in guilds: self.show_guild(search) else: pass name = self.view.left_listbox.get(self.view.left_listbox.curselection()).strip() self.view.edit['command'] = lambda: Controller2(BasicModel(file), \ TopWindow(self.view.parent, title='edit character', target='edit'), name, target='edit') class Controller2(Controller): def __init__(self, model, topwindow, name=None, target=None): self.model = model self.topwindow = topwindow self.name = name if self.name: data = self.model.get_character(self.name) if target == 'edit': print(data) self.topwindow.name_field.insert(0, data['name']) self.topwindow.guild_field.insert(0, data['class']) self.topwindow.age_field.insert(0, data['age']) self.topwindow.skills_field.insert(0, data['skills']) if target == 'delete': print('delete character here') def main(): app = tk.Tk() app.geometry('600x400+200+200') app.resizable(False, False) controller = Controller(BasicModel(file), View(app)) app.mainloop() if __name__ == '__main__': main()