Python Forum
Thread Rating:
  • 0 Vote(s) - 0 Average
  • 1
  • 2
  • 3
  • 4
  • 5
Current Project
#1
I like the post from here and thought I would give it a try using tkinter.
Only got a basic text version at the moment. Uses a json file for the database.
I will probably break the search function down into three separate methods when I get ready to apply it to a tkinter gui.
Planning on adding a add and delete button as well. Anyhow here is my attempt at a search for the characters.

json file
{
    "wizard": [
        {
            "name": "ralph",
            "age": 20,
            "spells": {"fireball": 10, "firestorm": 15}
        },

        {
            "name": "george",
            "age": 20,
            "spells": {"firestorm": 15, "meteor": 30}
        }
    ],

    "cleric": [
        {
            "name": "fred",
            "age": 18,
            "spells": {"heal": 5, "heal all": 12}
        }
    ]
}
python file
import json

# Get json file
file = "character.json"

# Open json file and load
with open(file, 'r') as rfile:
    data = json.load(rfile)

# Define function for searching and returning data
def search(term=None, card=None):

    # If card == name, we're seaching by name
    if card == 'name':

        # Create an empty return list
        ret = []

        # Get key, value pair from json file
        for info, stat in data.items():

            # Loop over stat to find the name and append to mylist all data related
            for item in stat:
                if item['name'] == term.lower():
                    ret.append(f'character type: {info}')
                    mylist = [item for item in item.items()]

        # The list has dict, so we loop over and format the dicts then append to
        # the ret list along with data that is not a dict
        for items in mylist:
            tmp = []
            if isinstance(items[1], dict):
                for key, value in items[1].items():
                    tmp.append(f'{key}: {value}')
                string = ', '.join(tmp)
                ret.append(f'{items[0]}: {string}')
            else:
                string = f'{items[0]}: {items[1]}'
                ret.append(string)

    # Elif the card is a guild search, we loop over the json file and return all
    # data for that character type
    elif card == 'guild':
        ret = []
        for items in data[term]:
            ret.append(f'character type: {term}')
            tmp = []
            for key, val in items.items():
                if isinstance(val, dict):
                    for item, strength in val.items():
                        tmp.append(f'{item}: {strength}')
                    string = ', '.join(tmp)
                    ret.append(f'{key}: {string}')
                else:
                    string = f'{key}: {val}'
                    ret.append(string)

    # Else we want to return all characters
    else:
        ret = []
        for _type, stats in data.items():
            for items in stats:
                ret.append(f'character type: {_type}')
                tmp = []
                for item, stat in items.items():
                    if isinstance(stat, dict):
                        for key, val in stat.items():
                            tmp.append(f'{key}: {val}')
                        string = ', '.join(tmp)
                        ret.append(string)
                    else:
                        string = f'{item}: {stat}'
                        ret.append(string)

    return ret

# Displays all characters
# i = 0
# for items in search():
#     print(items)
#     if i > 2:
#         print()
#         i = 0
#     else:
#         i += 1

# Returns all guild type characters
card = search('cleric', 'guild')
i = 0
for items in card:
    print(items)
    if i > 2:
        print()
        i = 0
    else:
        i += 1

# Returns single character data
# card = search('fred', 'name')
# for data in card:
#     print(data)
Output:
character type: cleric name: fred age: 18 spells: heal: 5, heal all: 12
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
Here is one of the re-wites. This is my backend.py
Soon I will start writing the classes to use the functions.
Any thoughts much appreciated.

import json
import os

# json file
file = 'card.json'

# Check if card.json exists. If not create it
if os.path.exists(file):
    pass
else:
    with open(file, 'w') as out_file:
        characters = {}
        json.dump(characters, out_file, indent=4)

# Function for creating classes
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 create_character(file, character):
    '''
    This function will create a character for a chosen class.
    It will check if the entered name exist. If it exists a
    message is returned. Other wise the character is created
    '''
    with open(file, 'r+') as json_file:
        data = json.load(json_file)
        add_character = True

        for info in data[character[0]]:
            if character[1]['name'].lower() in info.values():
                add_character = False
                print(f'{character[1]["name"].title()} already exists in the database.')

        if add_character:
            data[character[0]].append(character[1])
            json_file.seek(0)
            json.dump(data, json_file, indent=4)
            print(f'{character[1]["name"].title()} has been created.')

def delete_character(file, name):
    '''
    This function searches for a entered name and if it
    exist, will be deleted
    '''
    with open(file, 'r+') as json_file:
        data = json.load(json_file)

        for _ in data:
            for i in range(len(data[_])-1):
                if name.lower() == data[_][i]['name']:
                    data[_].pop(i)

    with open(file, 'w') as json_file:
        json.dump(data, json_file, indent=4)

    print(f'character 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 update_character(file, old_character, name, age, skills):
def update_character(file, *args, **kwargs):
    '''
    This function will update a created character.
    It gets the old character by name and stores the data in
    a variable to be compared and updated with the new data.
    The old character is deleted and a new one created from
    the new updated data.
    '''
    character = old[1]
    guild = old[0]
    name = old[1]['name']
    character.update(new[1])
    character = (guild, character)
    delete_character(file, name)
    create_character(file, character)
    print('character has been updated.')

'''
The coding below is what I been using to test the functions
'''

# old = get_character(file, 'tabol')
# new = ('wizard', {'name':'tabol', 'age': 27, 'skills':{'firestorm':90, 'fireball': 20}})
# update_character(file, old, new)

# print(get_character(file, 'tony'))

# delete_character(file, 'tony')

# character = (
# 'wizard',
# {
# 'name': 'tony', 'age': 24, 'skills':{'fireball': 10, 'meteor': 8}
# })
#
# create_character(file, character)

# create_class(file, 'wizard')
# create_class(file, 'cleric')
# create_class(file, 'warrior')
I welcome all feedback.
The only dumb question, is one that doesn't get asked.
My Github
How to post code using bbtags


Reply
#3
Think I got the model and backend part setup. Now I'll start tackling the view and contoller part.
Re-wrote the backend a little.
Updated code. As always I welcome feedback.

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

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.')
card.py
import tkinter as tk
import json
import backend

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)


class View:
    def __init__(self):
        pass



class Controller:
    def __init__(self):
        pass


def main():
    test = BasicModel(file)

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


Reply
#4
I think this will be my final version for now.
I have tried to optimize the code a little.
Everything seems to work. The spells section doesn't use a dict anymore. Just a string.
Anyhow here is the code

import tkinter as tk
from tkinter import ttk, messagebox
import json
from os import sys, path
from itertools import chain
from collections import OrderedDict
from random import randint


class Model:
    '''
    Model Handles all of the crud methods
    '''
    def __init__(self, db_file):
        '''
        Intialize the instance and set the db_file
        '''
        self.db_file = db_file

    def create_database(self):
        '''
        Check if database exists, if not, create it
        '''
        if path.exists(self.db_file):
            print('database already exist. Continuing')
            pass
        else:
            with open(self.db_file, 'w') as out_file:
                database = {}
                json.dump(database, out_file, indent=4)
                print('database created')

    def create_guild(self, arg):
        with open(self.db_file, 'r+') as json_file:
            data = json.load(json_file)

            if arg in data:
                print(f'{arg} already exist')
            else:
                data[arg] = []
                json_file.seek(0)
                json.dump(data, json_file, indent=4)
                print(f'{arg} has been created')

    def delete_guild(self, arg):
        with open(self.db_file, 'r') as json_file:
            data = json.load(json_file)
            if arg in data:
                data.pop(arg)

        with open (self.db_file, 'w') as json_file:
            json.dump(data, json_file, indent=4)

    def get_guilds(self):
        with open(self.db_file, 'r') as json_file:
            data = json.load(json_file)
            guilds = [guild for guild in data]
        return guilds

    def create_character(self, arg):
        '''
        Create the character
        '''
        with open(self.db_file, 'r+') as json_file:
            data = json.load(json_file)
            data[arg['guild'].lower()].append(arg)
            json_file.seek(0)
            json.dump(data, json_file, indent=4)

    def delete_character(self, *args):
        with open(self.db_file, 'r+') as json_file:
            data = OrderedDict(json.load(json_file))
            for i, character in enumerate(data[args[1]]):
                if args[0].lower() == data[args[1]][i]['name']:
                    data[args[1]].pop(i)
        with open(self.db_file, 'w') as json_file:
            json.dump(data, json_file, indent=4)

    def edit_character(self, arg):
        print('edit character')

    def check_character(self, arg):
        with open(self.db_file, 'r') as json_file:
            data = json.load(json_file)
            if arg['name'].lower() in [c.get('name') for c in chain(*data.values())]:
                return True
            else:
                return False

    def get_character(self, arg):
        with open(self.db_file, 'r') as json_file:
            data = json.load(json_file)
            for character in chain(*data.values()):
                if arg.lower() == character['name']:
                    return character

    def get_guild_characters(self, arg):
        with open(self.db_file) as json_file:
            data = json.load(json_file)
            names = []
            if arg in [character.get('guild') for character in chain(*data.values())]:
                for character in data[arg]:
                    if character:
                        names.append(character['name'])
            return names


class MainWindow:
    def __init__(self, parent):
        # Set and configure the parent container
        self.parent = parent
        self.parent.columnconfigure(0, weight=1)
        self.parent.rowconfigure(0, weight=1)

        # Create all containers to hold various widgets
        container = tk.Frame(self.parent)
        container.grid(column=0, row=0, sticky='news', padx=8, pady=4)
        container.grid_columnconfigure(0, weight=3)

        header_container = tk.Frame(container)
        header_container['highlightbackground'] = 'gray'
        header_container['highlightcolor'] = 'gray'
        header_container['highlightthickness'] = 1
        header_container['borderwidth'] = 1
        header_container.grid(column=0, row=0, sticky='new')
        header_container.grid_columnconfigure(0, weight=3)

        top_container = tk.Frame(container)
        top_container.grid(column=0, row=1, sticky='new', pady=(4, 2))
        top_container.grid_columnconfigure(0, weight=3)
        top_container.grid_columnconfigure(1, weight=3)

        mid_container = tk.Frame(container)
        mid_container.grid(column=0, row=2, sticky='new', pady=(2, 4))
        mid_container.grid_columnconfigure(0, weight=3, uniform='mid')
        mid_container.grid_columnconfigure(1, weight=3, uniform='mid')

        # Create a frame for the listbox
        left_frame = tk.Frame(mid_container)
        left_frame['highlightcolor'] = 'gray'
        left_frame['highlightbackground'] = 'gray'
        left_frame['highlightthickness'] = 1
        left_frame['borderwidth'] = 1
        left_frame.grid(column=0, row=0, sticky='news', padx=(0, 2))
        left_frame.grid_columnconfigure(0, weight=3)
        left_frame.grid_rowconfigure(0, weight=3)

        # Create a frame as a placeholder for a label
        self.right_frame = tk.Frame(mid_container)
        self.right_frame['highlightcolor'] = 'gray'
        self.right_frame['highlightbackground'] = 'gray'
        self.right_frame['highlightthickness'] = 1
        self.right_frame['borderwidth'] = 1
        self.right_frame.grid(column=1, row=0, sticky='news', padx=(2, 0))
        self.right_frame.grid_columnconfigure(0, weight=3)

        btm_container = tk.Frame(container)
        btm_container['highlightbackground'] = 'gray'
        btm_container['highlightcolor'] = 'gray'
        btm_container['highlightthickness'] = 1
        btm_container['borderwidth'] = 1
        btm_container.grid(column=0, row=3, sticky='new', pady=8)

        # Create the header
        self.text = 'Character Collection'
        self.header = tk.Canvas(header_container, height=80, bg='ivory2')
        self.shadow_mytext = self.header.create_text(0, 0, text=self.text, fill='gray', angle=4.5, font=('"" 40 bold'), tags=['event'])
        self.mytext = self.header.create_text(0, 0, text=self.text, fill='coral', angle=4.5, font=('"" 40 bold'), tags=['event'])
        self.header.grid(column=0, row=0, sticky='new')
        self.header.bind('<Configure>', self.move_text)

        # Create the combobox
        _guild = tk.StringVar()
        self.combobox = ttk.Combobox(top_container)
        self.combobox['textvariable'] = _guild
        self.combobox['cursor'] = 'hand2'
        self.combobox['state'] = 'readonly'
        self.combobox.grid(column=0, row=0, sticky='news', padx=(0, 2))

        info = tk.Label(top_container)
        info['text'] = 'Double click a name to view character data.'
        info['relief'] = 'groove'
        info['bg'] = 'lightyellow'
        info.grid(column=1, row=0, sticky='new', padx=(2, 0))

        # Create a listbox
        self.listbox = tk.Listbox(left_frame)
        self.listbox['selectmode'] = 'single'
        self.listbox.grid(column=0, row=0, sticky='news')

        scrollbar = tk.Scrollbar(left_frame)
        scrollbar.grid(column=1, row=0, sticky='news')
        self.listbox.configure(yscrollcommand = scrollbar.set)
        scrollbar.config(command = self.listbox.yview)

        # create a list of buttons
        self.buttons = [
        'Create Character', 'Edit Character', 'Delete Character',
        'Create Guild', 'Delete Guild', 'Exit'
        ]

        # List to display different colors for buttons
        self.button_colors = ['skyblue', 'orange', 'tomato', 'skyblue', 'tomato', 'red']

        # Make sure all buttons have the same width
        for i in range(len(self.buttons)):
            btm_container.grid_columnconfigure(i, weight=3, uniform='buttons')

        # Create the buttons
        for i in range(len(self.buttons)):
            self.buttons[i] = tk.Button(btm_container, text=self.buttons[i])
            self.buttons[i]['bg'] = self.button_colors[i]
            self.buttons[i]['cursor'] = 'hand2'
            self.buttons[i].grid(column=i, row=0, sticky='new', padx=2, pady=8)

    # Method for centering text on the canvas header
    def move_text(self, event):
        if event.width:
            xpos = (event.width/2)-4
            self.header.coords(self.mytext, xpos, 40)
            self.header.coords(self.shadow_mytext, (xpos+1.5), 41.5)


class AddEditWindow:
    def __init__(self, target=None, title=None):
        # Set instance variables for the form
        self.window = tk.Toplevel(None)
        self.window.columnconfigure(0, weight=1)
        self.window.rowconfigure(0, weight=1)
        self.window['padx'] = 8
        self.window['pady'] = 8
        self.window.title(title)

        self.namevar = tk.StringVar()
        self.agevar = tk.StringVar()
        self.villagevar = tk.StringVar()
        self.weaponvar = tk.StringVar()
        self.spellvar = tk.StringVar()

        if target == 'edit':
            self.form()
        if target == 'create':
            self.form()

    def form(self):
        container = tk.Frame(self.window)
        container['highlightcolor'] = 'gray'
        container['highlightbackground'] = 'gray'
        container['highlightthickness'] = 1
        container['borderwidth'] = 1
        container.grid(column=0, row=0, sticky='new', ipadx=8, ipady=5)
        container.grid_columnconfigure(0, weight=1)
        container.grid_columnconfigure(1, weight=3)

        label = tk.Label(container, text='Name:', anchor='w')
        label.grid(column=0, row=1, sticky='new', padx=(0, 1), pady=4)
        self.name_entry = tk.Entry(container, textvariable=self.namevar)
        self.name_entry.grid(column=1, row=1, sticky='new', padx=(1, 4), pady=4)

        label = tk.Label(container, text='Age:', anchor='w')
        label.grid(column=0, row=2, sticky='new', padx=(0, 1), pady=4)
        self.age_field = tk.Entry(container, textvariable=self.agevar)
        self.age_field.grid(column=1, row=2, sticky='new', padx=(1, 4), pady=4)

        label = tk.Label(container, text='Guild:', anchor='w')
        label.grid(column=0, row=3, sticky='new', padx=(0, 1), pady=4)
        self.guild_field = ttk.Combobox(container)
        self.guild_field['state'] = 'readonly'
        self.guild_field['cursor'] = 'hand2'
        self.guild_field.grid(column=1, row=3, sticky='new', padx=(1, 4), pady=4)

        label = tk.Label(container, text='Home Village:', anchor='w')
        label.grid(column=0, row=4, sticky='new', padx=(0, 1), pady=4)
        self.village = tk.Entry(container, textvariable = self.villagevar)
        self.village.grid(column=1, row=4, sticky='new', padx=(1, 4), pady=4)

        label = tk.Label(container, text='Weapon:', anchor='w')
        label.grid(column=0, row=5, sticky='new', padx=(0, 1), pady=4)
        self.weapon_field = tk.Entry(container, textvariable=self.weaponvar)
        self.weapon_field.grid(column=1, row=5, sticky='new', padx=(1, 4), pady=4)

        label = tk.Label(container, text='Spells:', anchor='w')
        label.grid(column=0, row=6, sticky='new', padx=(0, 1), pady=4)
        self.spell_field = tk.Entry(container, textvariable=self.spellvar)
        self.spell_field.grid(column=1, row=6, sticky='new', padx=(1, 4), pady=4)

        btnframe = tk.Frame(container)
        btnframe.grid(column=0, columnspan=2, row=7, sticky='new', pady=4)
        btnframe.grid_columnconfigure(0, weight=3, uniform='button')
        btnframe.grid_columnconfigure(1, weight=3, uniform='button')


        self.save_btn = tk.Button(btnframe, text='Save')
        self.save_btn['bg'] = 'skyblue'
        self.save_btn['cursor'] = 'hand2'
        self.save_btn.grid(column=0, row=0, sticky='new', padx=(2, 2), pady=4)

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


class AddDeleteGuild:
    def __init__(self):
        self.guildvar = tk.StringVar()

    def form(self):
        self.title = None
        self.window = tk.Toplevel(None)
        self.window.title(self.title)
        self.window['padx'] = 8
        self.window['pady'] = 4
        self.container = tk.Frame(self.window)
        self.container['borderwidth'] = 1
        self.container['highlightcolor'] = 'gray'
        self.container['highlightbackground'] = 'gray'
        self.container['highlightthickness'] = 1
        self.container.grid(column=0, row=0, sticky='new', padx=4, pady=2)
        self.container.grid_columnconfigure(0, weight=1)
        self.container.grid_columnconfigure(1, weight=3)

        btn_container = tk.Frame(self.container)
        btn_container.grid(column=0, columnspan=2, row=1, sticky='new', padx=2, pady=8)
        btn_container.grid_columnconfigure(0, weight=3, uniform='button')
        btn_container.grid_columnconfigure(1, weight=3, uniform='button')

        label = tk.Label(self.container, text='Guild Name:')
        label.grid(column=0, row=0, sticky='new', padx=(2, 1), pady=4)

        self.button = tk.Button(btn_container, text='Save')
        self.button['cursor'] = 'hand2'
        self.button.grid(column=0, row=0, sticky='new', padx=(2, 1))

        cancel_btn = tk.Button(btn_container, text='Cancel')
        cancel_btn['bg'] = 'orangered'
        cancel_btn['cursor'] = 'hand2'
        cancel_btn['command'] = self.window.destroy
        cancel_btn.grid(column=1, row=0, sticky='new', padx=(1, 2))


class Controller:
    def __init__(self, model, mainwindow,):
        '''
        Set instance vairables
        '''
        self.model = model
        self.mainwindow = mainwindow
        self.guildwindow = AddDeleteGuild()

        # Set button commands for mainview buttons
        self.mainwindow.buttons[0]['command'] = self.create_character_form
        self.mainwindow.buttons[1]['command'] = self.edit_character_form
        self.mainwindow.buttons[2]['command'] = self.delete_character
        self.mainwindow.buttons[3]['command'] = self.create_guild_form
        self.mainwindow.buttons[4]['command'] = self.delete_guild_form
        self.mainwindow.buttons[5]['command'] = sys.exit

        # Make a frame for character data and populate everything.
        self.make_frame()
        self.populate()


    def create_character_form(self):
        '''
        Creates the form and sets some variables for the toplevel window
        for creating characters
        '''
        guilds = [guild.title() for guild in self.model.get_guilds()]
        self.createwindow = AddEditWindow(target='create', title='Create New Character')
        self.createwindow.form()
        self.createwindow.guild_field['values'] = guilds
        self.createwindow.guild_field.current(0)
        self.createwindow.save_btn['command'] = self.save_character
        self.createwindow.cancel_btn['command'] = lambda: self.cancel(self.createwindow.window)
        self.mainwindow.parent.iconify()

    def edit_character_form(self):
        '''
        Get list of guilds and character data for editing. All but name can be edited.
        Data is displayed in the edit form
        '''
        guilds = [guild.title() for guild in self.model.get_guilds()]
        check = True
        try:
            self.character = self.model.get_character(self.mainwindow.listbox.get(self.mainwindow.listbox.curselection()[0]))
        except:
            check = False
            messagebox.showerror('No Character', 'You have to select a character before, you can edit')
        if check:
            self.editwindow = AddEditWindow(target='edit', title='Edit Character')
            self.editwindow.form()
            self.editwindow.name_entry['state'] = 'disabled'
            self.editwindow.namevar.set(self.character['name'])
            self.editwindow.agevar.set(self.character['age'])
            self.editwindow.guild_field['values'] = guilds
            guild = self.mainwindow.combobox.current()
            self.editwindow.guild_field.current(guild)
            self.editwindow.villagevar.set(self.character['home village'])
            self.editwindow.weaponvar.set(self.character['weapon'])
            self.editwindow.spellvar.set(self.character['spells'])
            self.editwindow.save_btn['command'] = self.edit_character
            self.editwindow.cancel_btn['command'] = lambda: self.cancel(self.editwindow.window)
            self.mainwindow.parent.iconify()

    def cancel(self, window):
        '''
        destroy the window. Create/Edit has been camceled.
        '''
        window.destroy()
        self.mainwindow.parent.deiconify()

    def edit_character(self):
        '''
        Deletes old character and creates new character data
        '''
        character = {}
        character['name'] = self.editwindow.namevar.get()
        character['age'] = self.editwindow.agevar.get()
        character['guild'] = self.editwindow.guild_field.get().lower()
        character['home village'] = self.editwindow.villagevar.get()
        character['weapon'] = self.editwindow.weaponvar.get()
        character['strength'] = randint(10, 25)
        character['agility'] = randint(10, 25)
        character['stamina'] = randint(30, 100)
        character['spells'] = self.editwindow.spellvar.get()

        self.model.delete_character(character['name'], self.character['guild'].lower())
        self.model.create_character(character)
        self.editwindow.window.destroy()
        self.mainwindow.parent.deiconify()

        for i, guild in enumerate(self.model.get_guilds()):
            if guild.lower() == character['guild']:
                index = i
        self.populate(index)

        list_index = 0
        for i, name in enumerate(self.mainwindow.listbox.get(0, tk.END)):
            if character['name'].lower() == name.lower():
                list_index = i
        self.mainwindow.listbox.select_clear(0, tk.END)
        self.mainwindow.listbox.select_set(list_index)
        self.display_character()

    def delete_character(self):
        '''
        Gets character name and guild for deleting
        '''
        try:
            character = self.mainwindow.listbox.get(self.mainwindow.listbox.curselection()[0]).lower()
            guild = self.mainwindow.combobox.get().lower()

            verify = messagebox.askyesno('Warning!', f'You are about to delete {character.title()}. Do you wish to continue?')
            if verify:
                self.model.delete_character(character, guild)
                self.populate()
            else:
                messagebox.showinfo('Canceled', f'Deletion of {character.title()} canceled.')
        except:
            pass

    def save_character(self):
        '''
        saves new character data
        '''
        character = {}

        character['name'] = self.createwindow.name_entry.get().lower()
        character['guild'] = self.createwindow.guild_field.get().lower()
        character['age'] = self.createwindow.age_field.get().lower()
        character['home village'] = self.createwindow.village.get().lower()
        character['weapon'] = self.createwindow.weapon_field.get().lower()
        character['strength'] = randint(10, 25)
        character['agility'] = randint(10, 25)
        character['stamina'] = randint(30, 50)
        character['spells'] = self.createwindow.spell_field.get().lower()
        fields = []
        error = 0
        for key, value in character.items():
            if not character['weapon']:
                character['weapon'] = 'None'
            if not character['spells']:
                character['spells'] = 'None'
            if not value:
                error += 1
                fields.append(key)
        if error > 0:
            check = messagebox.showerror('Error!', f'These fields can not be empty.\n{", ".join(fields)}')
        else:
            verify = True
            if_name_exist = self.model.check_character(character)

            if if_name_exist:
                messagebox.showerror('Name Error!', f'{character["name"].title()} already exists. Please choose another name.')
                self.createwindow.deiconify()
                verify = False

            if not character['age'].isnumeric():
                messagebox.showerror('Age Error!', 'Age must be a whole number.')
                verify = False

            if verify:
                messagebox.showinfo('Character Created', f'The character {character["name"].title()} has been created.')
                self.createwindow.window.destroy()
                self.mainwindow.parent.deiconify()

                self.model.create_character(character)

                for i, guild in enumerate(self.model.get_guilds()):
                    if guild.lower() == character['guild']:
                        index = i
                self.populate(index)

                list_index = 0
                for i, name in enumerate(self.mainwindow.listbox.get(0, tk.END)):
                    if character['name'].lower() == name.lower():
                        list_index = i
                self.mainwindow.listbox.select_clear(0, tk.END)
                self.mainwindow.listbox.select_set(list_index)
                self.display_character()

    def create_guild_form(self):
        '''
        Form for creating new guilds
        '''
        self.guildwindow = AddDeleteGuild()
        self.guildwindow.title='Delete Guild'
        self.guildwindow.form()
        self.guildwindow.entry = tk.Entry(self.guildwindow.container)
        self.guildwindow.entry.grid(column=1, row=0, sticky='new', padx=(1, 0), pady=4)
        self.guildwindow.button['text'] = 'Save'
        self.guildwindow.button['bg'] = 'skyblue'
        self.guildwindow.button['command'] = self.create_guild

    def create_guild(self):
        '''
        Creates new guild for form data
        '''
        if self.guildwindow.entry.get():
            guild = self.guildwindow.entry.get().lower().strip()
            self.model.create_guild(guild)
            self.guildwindow.window.destroy()
            self.populate()
        else:
            messagebox.showerror('Error!', 'You must enter a guild name to create it.')

    def delete_guild_form(self):
        '''
        A form for chosen guild
        '''
        self.guildwindow.title = 'Delete Guild'
        self.guildwindow.form()
        self.guildwindow.menu = ttk.Combobox(self.guildwindow.container)
        self.guildwindow.menu.grid(column=1, row=0, sticky='new', padx=(1, 2), pady=4)
        self.guildwindow.menu['values'] = self.model.get_guilds()
        self.guildwindow.menu.current(0)

        self.guildwindow.button['text'] = 'Delete'
        self.guildwindow.button['bg'] = 'skyblue'
        self.guildwindow.button['command'] = self.delete_guild

    def delete_guild(self):
        '''
        Deletes chosen guild and all of it's characters
        '''
        guild = self.guildwindow.menu.get()
        ok = messagebox.askyesno('Warning!', f'You are abount to delete the guild {guild} and all of it\'s characters.\n Do you wish to continue? ')
        if ok:
            self.model.delete_guild(guild)
            self.populate()
            self.guildwindow.window.destroy()
        else:
            messagebox.showinfo('Deletion Aborted', f'The deletion of the {guild} guild was canceled.')

    def get_guild_characters(self):
        '''
        Method for populating the listbox in mainwindow and setting first item
        self.frame.destroy is used to remove any data in the character display.
        Remake the data frame with self.make_frame
        Re-populate the listbox and then display character data with self.display_character
        '''
        self.frame.destroy()
        self.make_frame()
        guild = self.mainwindow.combobox.get().lower()
        characters = self.model.get_guild_characters(guild)
        self.mainwindow.listbox.delete(0, tk.END)

        for i, name in enumerate(characters):
            self.mainwindow.listbox.insert(i, name.title())
        self.mainwindow.listbox.select_set(0)
        self.mainwindow.listbox.bind('<Double-Button-1>', lambda x: self.display_character())
        self.display_character()

    def make_frame(self):
        '''
        Method to build a container frame for the character.
        Can be destroyed to remove the data and rebuilt when needed
        '''
        self.frame = tk.Frame(self.mainwindow.right_frame)
        self.frame.grid(column=0, row=0, sticky='new')
        self.frame.grid_columnconfigure(0, weight=0)
        self.frame.grid_columnconfigure(1, weight=3)

    def display_character(self):
        '''
        Method for displaying the selected character data
        '''
        error = False
        try:
            character = self.mainwindow.listbox.get(self.mainwindow.listbox.curselection()[0])
        except IndexError:
            error = True
        if error:
            pass
        else:
            data = self.model.get_character(character.lower())
            i = 0
            for key, value in data.items():
                key_label = tk.Label(self.frame, text=key.title(), anchor='w', padx=4)
                key_label['relief'] = 'groove'
                key_label.grid(column=0, row=i, sticky='new', ipadx=8)

                value = value.title() if isinstance(value, str) else value

                value_label = tk.Label(self.frame, text=value, anchor='w', padx=4)
                value_label['relief'] = 'groove'
                value_label.grid(column=1, row=i, sticky='new')

                i += 1

    def populate(self, index=0):
        '''
        Get all guild and populate the combobox. Set the first entry
        Call self.get_guild_characters to populate the listbox
        '''
        guilds = self.model.get_guilds()
        guilds = [guild.title() for guild in guilds]
        self.mainwindow.combobox['values'] = guilds
        self.mainwindow.combobox.current(index)
        self.mainwindow.combobox.bind('<<ComboboxSelected>>', lambda x: self.get_guild_characters())
        self.get_guild_characters()
        self.display_character()

if __name__ == '__main__':
    app = tk.Tk()
    controller = Controller(Model(db_file='card2.json'), MainWindow(app))
    app.mainloop()
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
  Current project - Simple tkinter login system menator01 3 1,540 Sep-01-2023, 05:56 PM
Last Post: menator01

Forum Jump:

User Panel Messages

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