Python Forum
Help in finding ExceptionError
Thread Rating:
  • 0 Vote(s) - 0 Average
  • 1
  • 2
  • 3
  • 4
  • 5
Help in finding ExceptionError
#1
The script is working as expected but, I keep getting the error in the console. I've not been able to silence or correct it.
Any help would be great. Thanks
Error:
Exception in Tkinter callback Traceback (most recent call last): File "/usr/lib/python3.8/tkinter/__init__.py", line 1886, in __call__ return self.func(*args) TypeError: set() missing 1 required positional argument: 'last' Exception in Tkinter callback Traceback (most recent call last): File "/usr/lib/python3.8/tkinter/__init__.py", line 1886, in __call__ return self.func(*args) TypeError: set() missing 1 required positional argument: 'last' Exception in Tkinter callback Traceback (most recent call last): File "/usr/lib/python3.8/tkinter/__init__.py", line 1886, in __call__ return self.func(*args) TypeError: set() missing 1 required positional argument: 'last' Exception in Tkinter callback Traceback (most recent call last): File "/usr/lib/python3.8/tkinter/__init__.py", line 1886, in __call__ return self.func(*args) TypeError: set() missing 1 required positional argument: 'last'
Relevant code
class AddRecipe:
    '''doc'''
    def __init__(self):
        pass

    def my_form(self):
        '''docstring'''

# More code .....

        btn_style = ttk.Style()
        btn_style.configure('Submit.TButton')
        submit_btn = ttk.Button(bottom_frame, text='Submit Recipe', style='Submit.TButton', \
        command=partial(self.submit_values, title_entry, ingredients_entry, \
        instructions_entry, prep_time, cook_time))
        submit_btn.grid(column=5, row=0)

    def submit_values(self, title, ingredients, instructions, prep_time, cook_time):
        '''Docstring'''
        if not title.get() or not ingredients.get('1.0', 'end-1c') or not \
        instructions.get('1.0', 'end-1c'):
            messagebox.showerror(title='Input Error', \
            message='Required fields: Title, Ingredients, and Instructions')
            self.top.destroy()
            self.my_form()

        print(title.get())
        print(ingredients.get('1.0', 'end-1c'))
        print(instructions.get('1.0', 'end-1c'))
        print(prep_time.get())
        print(cook_time.get())
        msg = messagebox.askyesno(title='Add Recipe', message='Add another recipe?')
        if msg is True:
            self.top.destroy()
            self.my_form()
        if msg is False:
            self.top.destroy()
Reply
#2
Do you get all those errors when the program starts or do some of them pop up when you push buttons etc? If the latter, what are you doing that makes it pop up?
Reply
#3
It's when I push the button to open a new top window.
I forgot to add the button for opening the window

    def my_footer(self, footerframe):
        '''footer'''
        style = ttk.Style()
        style.map('Footer.TLabel', \
        foreground=[('pressed', 'firebrick'), ('active', 'red')], \
        background=[('pressed', '!disabled', 'gray86'), ('active', 'gray86')] \
        )
        style.configure('Footer.TLabel', foreground='blue', \
        font=('Times', 12, 'normal', 'underline'))
        footer = ttk.Button(footerframe, text='Register for Johnny\'s CookBook', \
        style='Footer.TLabel', cursor='hand2', command=partial(opensite))
        footer.grid(column=0, row=0, ipady=3, padx=10, sticky='nw')


        form = ttk.Button(footerframe, text='Add Recipe', style='Footer.TLabel', cursor='hand2', \
        command=partial(AddRecipe().my_form))
        form.grid(column=1, row=0, padx=10)
Reply
#4
Possibly this?
command=partial(AddRecipe().my_form))
This creates an instance of AddRecipe (from now on referred to as "original_recipe") and gets the my_form method for that instance. When you press the button it will call original_recipe.my_form(). It does not create a new instance of AddRecipe.

Is that what you want?
Reply
#5
Let me see if I understand correctly.
When the program is first ran it creates the instance of he class,
when clicking the button to open the new window it's trying to call the original instance of that window which was created when the script was first ran.
So in order to get rid of the Exception, I need to create a new instance of the window?
Is that the root window or the Toplevel window?
Will creating a new instance take care of the exception?
How do I go about creating a new instance?
Reply
#6
When you do this:
command=partial(AddRecipe().my_form))
It is really doing this:
original_recipe = AddRecipe()
command = partial(original_recipe.my_form)
I don't know if that is what you want. Since I cannot run you code and can only see a few snippets, I don't know if this is generating your exception or not.

I do know that instance variables like "top" are probably (I cannot see where assigned) being overwritten and you may be mixing strange combinations of old and new.
Reply
#7
Here is all of the code
#! /usr/bin/env python3.8
'''Docstring'''

###### Do the imports ######
import tkinter as tk
from tkinter import ttk, messagebox
import string
from functools import partial
import webbrowser
from  modules import database as db, time_converter as tc
import settings


### Check if Recipe.db exists ###
db.Database().check_for_database()

def opensite():
    '''Docstring'''
    webbrowser.open_new('http://recipes.phpshelf.net')

###### Intiate the window ######
class RootWindow:
    '''Docstring'''
    def __init__(self, master):
        self.master = master
        self.master.columnconfigure(0, weight=1)
        self.master.rowconfigure(0, weight=1)

        ###### Intiate all of our main containerframes ######
        self.logo_frame()
        self.letter_frame()
        self.container_frame()
        self.title_frame()
        self.recipe_frame()
        self.footer_frame()

        ###### Initate the widgets ######
        Child(self.logoframe, self.footerframe)
        Child2().lettermenu(self.letterframe, self.titleframe, self.recipeframe)
        Child3().titlemenu(self.titleframe, self.recipeframe)
        Child4().recipe(self.recipeframe)

    ###### This sets all the container frames ######
    ###### Need to create a class for generic ######
    ###### frames to eliminate repetitive code #####
    def logo_frame(self):
        '''logo frame'''
        self.logoframe = ttk.Frame(self.master, border=5, relief='ridge')
        self.logoframe.grid(column=0, row=0, sticky='new')
        self.logoframe.grid_columnconfigure(0, weight=3)

    def letter_frame(self):
        '''letterframe'''
        self.letterframe = ttk.Frame(self.master, border=5, relief='ridge')
        self.letterframe.grid(column=0, row=1, sticky='new')
        for i in range(26):
            self.letterframe.grid_columnconfigure(i, weight=3)

    def container_frame(self):
        '''Container frame will hold two frames. Title frame and recipe frame'''
        self.containerframe = ttk.Frame(self.master)
        self.containerframe.grid(column=0, row=2, sticky='nw')
        self.containerframe.grid_columnconfigure(0, weight=3)

    def title_frame(self):
        '''Title Frame'''
        style = ttk.Style()
        style.configure('Title.TLabel', background='gray86')
        self.titleframe = ttk.Frame(self.containerframe, style='Title.TLabel', \
        border=5, relief='ridge')
        self.titleframe.grid(column=0, row=0, sticky='nw')
        self.titleframe.grid_columnconfigure(0, weight=3)

    def recipe_frame(self):
        '''Recipe Frame'''
        self.recipeframe = ttk.Frame(self.containerframe, border=5, relief='ridge')
        self.recipeframe.grid(column=1, row=0, sticky='new')
        self.recipeframe.grid_columnconfigure(0, weight=3)

    def footer_frame(self):
        '''footer'''
        self.footerframe = ttk.Frame(self.master, border=5, relief='ridge')
        self.footerframe.grid(column=0, row=3, sticky='new')
        self.footerframe.grid_columnconfigure(0, weight=3)

###### This class defines and displays header and footer ######
class Child:
    '''logo'''
    def __init__(self, logoframe, footerframe):
        self.my_logo(logoframe)
        self.my_footer(footerframe)

    def my_logo(self, logoframe):
        '''doc'''
        imgfile = tk.PhotoImage(file=settings.IMG)
        self.logo = ttk.Label(logoframe, image=imgfile)
        self.logo.image = imgfile
        self.logo.grid(column=0, row=0)

    def my_footer(self, footerframe):
        '''footer'''
        style = ttk.Style()
        style.map('Footer.TLabel', \
        foreground=[('pressed', 'firebrick'), ('active', 'red')], \
        background=[('pressed', '!disabled', 'gray86'), ('active', 'gray86')] \
        )
        style.configure('Footer.TLabel', foreground='blue', \
        font=('Times', 12, 'normal', 'underline'))
        footer = ttk.Button(footerframe, text='Register for Johnny\'s CookBook', \
        style='Footer.TLabel', cursor='hand2', command=partial(opensite))
        footer.grid(column=0, row=0, ipady=3, padx=10, sticky='nw')

        form = ttk.Button(footerframe, text='Add Recipe', style='Footer.TLabel', cursor='hand2', \
        command=partial(AddRecipe().my_form))
        form.grid(column=1, row=0, padx=10)


###### Class produces the letter menu ######
class Child2:
    '''doc'''
    def __init__(self):
        pass

    def lettermenu(self, letterframe, titleframe, recipeframe):
        '''lettermenu'''
        letters = string.ascii_uppercase
        i = 0
        style = ttk.Style()
        style.configure('Btn.TButton', width=3)
        for letter in letters:
            button = ttk.Button(letterframe, text=letter, style='Btn.TButton', \
            command=partial(Child3().titlemenu, titleframe, recipeframe, letter=letter))
            button.grid(column=i, row=0, sticky='new')
            i += 1


###### This class retrieves and displays recipe titles ######
class Child3:
    '''doc'''
    def __init__(self):
        pass

    def titlemenu(self, titleframe, recipeframe, letter='a'):
        '''title menu'''
        ###### Query the database for recipe titles from our letter menu defaults to a ######
        data = db.Database().title_query(letter=letter)

        ###### Create the canvas and scrollbar ######
        canvas = tk.Canvas(titleframe, highlightcolor='gray87')
        scrollbar = ttk.Scrollbar(titleframe, orient='vertical', command=canvas.yview)
        scrollable_frame = ttk.Frame(canvas)

        scrollable_frame.bind(
            '<Configure>',
            lambda e: canvas.configure(scrollregion=canvas.bbox('all'))
        )

        canvas.create_window((0, 0), window=scrollable_frame, anchor='nw')
        canvas.configure(yscrollcommand=scrollbar.set)

        ###### If there are not any results returned, display message ######
        if not data:
            msg = messagebox.showerror(title='No listing', \
            message='Sorry, there have not been any recipes added starting with ' + letter)
            if msg == 'ok':
                return True
        else:
            ###### Setup our look for the recipe menu ######
            style = ttk.Style()
            style.map('L.TLabel', \
            background=[('pressed', '!disabled', 'gray87'), ('active', '#ffffee')], \
            foreground=[('pressed', 'red'), ('active', 'red')])

            style.configure('L.TLabel', relief='flat', padding=2, foreground='blue')

            ###### Loop through and display the return results from the database ######

            for recipe_id, title in data:
                title = ttk.Button(scrollable_frame, text=title.title(), \
                style='L.TLabel', cursor='hand2', \
                command=partial(Child4().recipe, recipeframe, recipe_id=recipe_id))
                title.grid(column=0, row=recipe_id, sticky='nw')

            scrollbar.grid(column=0, row=0, sticky='nsw')
            canvas.grid(column=1, row=0, sticky='nsw', padx=8)


class Child4:
    '''Docstring'''
    def __init__(self):
        pass

    def recipe(self, recipeframe, recipe_id=1):
        '''Docstring'''
        data = db.Database().id_query(recipe_id)

        canvas = tk.Canvas(recipeframe, width=600)
        canvas.configure(border=5, highlightcolor='gray87')

        scrollbar = ttk.Scrollbar(recipeframe, orient='vertical', command=canvas.yview)
        scrollable_frame = ttk.Frame(canvas)

        scrollable_frame.bind(
            '<Configure>',
            lambda e: canvas.configure(scrollregion=canvas.bbox('all'))
        )

        canvas.create_window((0, 0), window=scrollable_frame, anchor='nw')
        canvas.configure(yscrollcommand=scrollbar.set)

        style = ttk.Style()
        ### Title
        style.configure('Title.TLabel', \
        font=('Sans', 12, 'bold', 'underline'), \
        foreground='blue', padding=5, relief='raised')
        title = ttk.Label(scrollable_frame, text=f'{data[1]}', style='Title.TLabel')
        title.grid(columnspan=2, column=0, row=0, sticky='new')

        style2 = ttk.Style()
        style2.configure('Time.TLabel', \
        foreground='blue', relief='raised', padding=5)

        times = ttk.Label(scrollable_frame, \
        text=f' Prep Time: {tc.time_converter(data[4])} | \
        Cook Time: {tc.time_converter(data[5])} \
        | Total Time: {tc.time_converter((data[4]+data[5]))}', style='Time.TLabel')
        times.grid(columnspan=2, column=0, row=1, sticky='new')

        ### Mini Headers for ingredients and instructions
        style3 = ttk.Style()
        style3.configure('MiniHeader.TLabel', \
        font=('Sans', 10, 'bold', 'underline', \
        'italic'), foreground='blue', relief='raised', padding=5)

        mini_header1 = ttk.Label(scrollable_frame, \
        text=' Ingredients', style='MiniHeader.TLabel')
        mini_header1.grid(column=0, row=2, sticky='new')

        mini_header2 = ttk.Label(scrollable_frame, \
        text=' Instructions', style='MiniHeader.TLabel')
        mini_header2.grid(column=1, row=2, sticky='new')

        style4 = ttk.Style()
        style4.configure('Recipe.TLabel', \
        padding=8, border=8, relief='sunken', wraplength=291)

        ingredients = ttk.Label(scrollable_frame, \
        text=data[2], style='Recipe.TLabel')
        ingredients.grid(column=0, row=3, sticky='nw')

        instructions = ttk.Label(scrollable_frame, \
        text=data[3], style='Recipe.TLabel')
        instructions.grid(column=1, row=3, sticky='nw')

        scrollbar.grid(column=0, row=0, sticky='nsw')
        canvas.grid(column=1, row=0, sticky='nsw')

class AddRecipe:
    '''doc'''
    def __init__(self):
        pass

    def my_form(self):
        '''docstring'''
        self.top = tk.Toplevel()
        self.top.configure(border=8, relief='ridge')
        self.top.resizable(width=False, height=False)
        self.top.title('Add a Recipe')
        frame = tk.Frame(self.top)
        frame.grid(column=0, row=0, padx=25, pady=25, sticky='new')

        header_style = ttk.Style()
        header_style.configure('Header.TLabel', font=('Sans', 18, 'bold'), padding=0)
        header = ttk.Label(frame, text='Add a Recipe', style='Header.TLabel')
        header.grid(columnspan=2, column=0, row=0)

        title_frame = ttk.Frame(frame, border=5, relief='ridge', padding=8)
        title_frame.grid(columnspan=2, column=0, row=1, ipadx=2)

        title = ttk.Label(title_frame, text='Title:')
        title.grid(column=0, row=0, sticky='nw')
        title_entry = tk.Entry(title_frame, width=100)
        title_entry.grid(column=0, row=1)

        ingredients_frame = ttk.Frame(frame, border=5, relief='ridge', padding=8)
        ingredients_frame.grid(column=0, row=2, sticky='nw')

        ingredients = ttk.Label(ingredients_frame, text='Ingredients:')
        ingredients.grid(columnspan=2, column=0, row=0, sticky='nw')
        ingredients_entry = tk.Text(ingredients_frame, width=46, height=20)

        ingr_scrollbar = tk.Scrollbar(ingredients_frame)
        ingr_scrollbar.configure(command=ingredients_entry.yview)
        ingredients_entry.configure(yscrollcommand=ttk.Scrollbar.set)
        ingr_scrollbar.grid(column=0, row=1, sticky='ns')

        ingredients_entry.grid(column=1, row=1, sticky='nw')

        instruction_frame = ttk.Frame(frame, border=5, relief='ridge', padding=8)
        instruction_frame.grid(column=1, row=2, sticky='nw')

        instructions = ttk.Label(instruction_frame, text='Instructions:')
        instructions.grid(columnspan=2, column=0, row=0, sticky='nw')
        instructions_entry = tk.Text(instruction_frame, width=47, height=20)

        instr_scrollbar = tk.Scrollbar(instruction_frame)
        instr_scrollbar.configure(command=instructions_entry.yview)
        instructions_entry.configure(yscrollcommand=ttk.Scrollbar.set)
        instr_scrollbar.grid(column=0, row=1, sticky='ns')

        instructions_entry.grid(column=1, row=1, sticky='nw')

        # ### Bottom frame ###
        btm_frame_style = ttk.Style()
        btm_frame_style.configure('BFrame.TFrame')
        bottom_frame = ttk.Frame(frame, border=5, relief='ridge', padding=8, \
        style='BFrame.TFrame')
        bottom_frame.grid(columnspan=2, column=0, row=3, sticky='new')

        prep = tk.Label(bottom_frame, text='Prep. Time in min.')
        prep.grid(column=0, row=0, sticky='w', padx=10)
        prep_time = tk.Spinbox(bottom_frame, from_=0, to=320, width=5)
        prep_time.grid(column=1, row=0, sticky='w')

        cook = ttk.Label(bottom_frame, text='Cook Time in min.')
        cook.grid(column=2, row=0, padx=10)
        cook_time = tk.Spinbox(bottom_frame, from_=0, to=320, width=5)
        cook_time.grid(column=3, row=0)

        spacer = ttk.Frame(bottom_frame, width=300)
        spacer.grid(column=4, row=1)


        btn_style = ttk.Style()
        btn_style.configure('Submit.TButton')
        submit_btn = ttk.Button(bottom_frame, text='Submit Recipe', style='Submit.TButton', \
        command=partial(self.submit_values, title_entry, ingredients_entry, \
        instructions_entry, prep_time, cook_time))
        submit_btn.grid(column=5, row=0)

    def submit_values(self, title, ingredients, instructions, prep_time, cook_time):
        '''Docstring'''
        if not title.get() or not ingredients.get('1.0', 'end-1c') or not \
        instructions.get('1.0', 'end-1c'):
            messagebox.showerror(title='Input Error', \
            message='Required fields: Title, Ingredients, and Instructions')
            self.top.destroy()
            self.my_form()

        print(title.get())
        print(ingredients.get('1.0', 'end-1c'))
        print(instructions.get('1.0', 'end-1c'))
        print(prep_time.get())
        print(cook_time.get())
        msg = messagebox.askyesno(title='Add Recipe', message='Add another recipe?')
        if msg is True:
            self.top.destroy()
            self.my_form()
        if msg is False:
            self.top.destroy()


def main():
    '''Docstring'''
    root = tk.Tk()
    root.title('Johnny\'s CookBook')
    img = tk.PhotoImage(file='/home/johnny/Desktop/CookBook/images/cookbook_logo.png')
    root.configure(width=img.width())
    root.resizable(width=False, height=False)
    RootWindow(root)
    root.mainloop()

if __name__ == '__main__':
    main()
settings.py
'''Settings.py'''
IMG = 'images/cookbook_logo.png'
The imports from module folder
database.py
#! /usr/bin/env python3.8
'''Database connection, queries and timeconverter'''
import sqlite3
from sqlite3 import Error
class Database:
    '''Docstring'''
    def __init__(self):
        pass

    def check_for_database(self):
        '''Docstring'''
        try:
            with open('Recipes.db'):
                print('Files exists')

        except FileNotFoundError:
            try:
                print('Creating database and tables')
                conn = sqlite3.connect('Recipes.db')
                cur = conn.cursor()
                cur.execute('create table recipes( \
                id integer primary key autoincrement, \
                title varchar(100) not null, \
                ingredients text not null, \
                instructions text not null, \
                prep integer not null default 0, \
                cook integer not null default 0)')

                conn.execute('insert into recipes (title, ingredients, instructions) values \
                ("a test recipe","test ingredient\nAnother ingredient", \
                "test instructions instructions")')

                conn.commit()
                conn.close()
            except Error:
                print(Error)
        except Error:
            print(Error)

    def title_query(self, letter):
        '''Docstring'''
        try:
            conn = sqlite3.connect('Recipes.db')
            cursor = conn.cursor()
            cursor.execute(f'select id, title from recipes where title like "{letter}%"')
            results = cursor.fetchall()
            conn.commit()
            conn.close()
            return results
        except Error:
            print(f'Error: {Error}')

    def id_query(self, recipe_id):
        '''Dcostring'''
        try:
            conn = sqlite3.connect('Recipes.db')
            cursor = conn.cursor()
            cursor.execute(f'select * from recipes where id = {recipe_id}')
            result = cursor.fetchone()
            conn.commit()
            conn.close()
            return result
        except Error:
            print(f'Error: {Error}')

    def enter_recipe(self, title, ingredients, instructions, prep, cook):
        '''Docstring'''
        try:
            conn = sqlite3.connect('/home/johnny/Desktop/CookBook/Recipes.db')
            cursor = conn.cursor()
            cursor.execute(f'insert into recipes (title, ingredients, \
            instructions, prep, cook) values ("{title}", \
            "{ingredients}", "{instructions}", "{prep}", "{cook}")')
            conn.commit()
            conn.close()
        except (Error, FileNotFoundError) as error:
            print(error)
time_converter.py
#! /usr/bin/env python3.8

def time_converter(arg):
    '''Convert minutes to hours if above 30'''
    hours, minutes = divmod(arg, 60)
    if arg > 60:
        if hours > 1:
            var = f'{hours} hrs. {minutes} min.'
        else:
            var = f'{hours} hr. {minutes} min.'
    elif arg == 60:
        var = f'{hours} hr.'
    else:
        var = f'{minutes} min.'
    return var
Reply
#8
That looks like too much work. Plus I don't have a recipe database.

How about you change your program to do this:

command = AddRecipe

And change AddRecipe.my_form to AddRecipe.__init__

I find your programming style of using Classes to make a namespace instead of making objects to be very strange. I supposed you could call it a factory pattern.
Reply
#9
You get an exception if this gets executed but no exception if not:
        ingredients_entry.configure(yscrollcommand=ttk.Scrollbar.set)
Sorry for the class red herring. After looking at all you code I see this is a reoccurring pattern. Now how I use classes, but don't use me as a python code barometer or you are in for stormy weather.
Reply
#10
Thank you so much. I've been racking my brain on this.
Reply


Forum Jump:

User Panel Messages

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