Apr-16-2022, 10:31 AM
(This post was last modified: Apr-16-2022, 10:31 AM by menator01.
Edit Reason: Updated code card.py
)
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()
I welcome all feedback.
The only dumb question, is one that doesn't get asked.
My Github
How to post code using bbtags
Download my project scripts
The only dumb question, is one that doesn't get asked.
My Github
How to post code using bbtags
Download my project scripts