Python Forum
Finished CookBook Project
Thread Rating:
  • 0 Vote(s) - 0 Average
  • 1
  • 2
  • 3
  • 4
  • 5
Finished CookBook Project
#1
Just finished my cookbook project using tkinter.
I have it connected to a public database. Unfortunately I do not have fiber optic so my upload speed is slow. If you do not want to wait the few seconds for the queries, there is only one table with the required fields. I was using the database created by django. Any how the fields needed in the table are: id, title, prep_time, cook_time, ingredients, and .directions.
Constructive criticism welcome.

#! /usr/bin/env python3.8

### Do imports ###
import tkinter as tk
from tkinter import ttk
from tkinter import messagebox
from PIL import Image, ImageTk
import string
from functools import partial
import pymysql
import webbrowser

### Database class for connecting and querying our database
class Database():
    ### Define some global vars
    HOST = 'recipes.phpshelf.net'
    USER = 'public'
    PASSWD = 'public'
    DB = 'public'
    PORT = 3306

    ### Initiate our connection
    def __init__(self):
        try:
            self.conn = pymysql.connect(self.HOST, self.USER, self.PASSWD, self.DB, self.PORT)
        except pymysql.OperationalError as e:
            if e:
                print('Error: Please check your database connection settings')
                msg = messagebox.showerror(title = 'Database Connection', message = 'Error! Please check database connection settings.')
                quit()



    ### Query the database for our titles
    def TitleQuery(self,letter):
        try:
            query = f'select id, title from homepages_post where title like "{letter}%"'
            with self.conn.cursor() as cursor:
                cursor.execute(query)
                results = cursor.fetchall()
            return results
        except pymysql.InternalError as e:
            print('Database Error! Please check your database query for errors.')
            msg = messagebox.showerror(title = 'Database Error', message = 'Error! Please check query format settings.')
            quit()

    ### Query the database for recipes
    def IDQuery(self, id):
        try:
            query = f'select * from homepages_post where id = "{id}"'
            with self.conn.cursor() as cursor:
                cursor.execute(query)
                results = cursor.fetchall()
            return results
        except pymysql.InternalError as e:
            msg = messagebox.showerror(title = 'Database Error', message = 'Error! Please check query format settings.')
            quit()


### Our main window class
class MainWindow(ttk.Frame):
    def __init__(self, master):
        self.master = master
        self.master.grid_columnconfigure(0, weight = 1)
        self.master.grid_rowconfigure(0, weight = 1)

        ### Get the screen width and height
        self.screenwidth = master.winfo_screenwidth()
        self.screenheight = master.winfo_screenheight()


        ### MainFrame holds all other grids and widgets
        self.MainFrame = ttk.Frame(self.master)
        self.MainFrame.grid(column = 0, row = 0, sticky = ('new'))
        self.MainFrame.grid_columnconfigure(0, weight = 1)
        self.MainFrame.grid_rowconfigure(0, weight = 1)

########### Main container frames start ############################################################
        ### This frame grid will hold the logo panel
        ### Parent MainFrame
        self.Logo_Frame = ttk.Frame(self.MainFrame)
        self.Logo_Frame.grid(column = 0, row = 0, sticky = 'new')
        self.Logo_Frame.grid_columnconfigure(0, weight = 3)

        ### This frame grid will the letter menu
        ### Parent MainFrame
        self.Letter_Menu_Frame = ttk.Frame(self.MainFrame, relief = 'raised', padding = 5)
        self.Letter_Menu_Frame.grid(column = 0, row = 1, sticky = 'new')
        for i in range(26):
            self.Letter_Menu_Frame.grid_columnconfigure(i, weight = 3)

        ### This frame grid will hold two subframe/canvas grids
        ### Parent MainFrame
        self.MultiGrid_Frame = ttk.Frame(self.MainFrame)
        self.MultiGrid_Frame.grid(column = 0, row = 2, sticky = 'nw')
        self.MultiGrid_Frame.grid_columnconfigure(0, weight = 3)

        ### This frame grid will hold the footer widget
        ### Parent MainFrame
        fstyle = ttk.Style()
        fstyle.configure('F.TFrame', relief = 'raised', padd = 8)
        self.Footer_Frame = ttk.Frame(self.MainFrame, style = 'f.TFrame')
        self.Footer_Frame.grid(column = 0, row = 3, sticky = 'new')
        self.Footer_Frame.grid_columnconfigure(0, weight = 3)

########## Main container frames end ###############################################################

        ### This subframe will hold a canvas for scrolling and a recipe title list
        ### Parent MultiGrid_Frame
        self.Menu_Title_Frame = ttk.Frame(self.MultiGrid_Frame, padding = 8, border = 3, relief = 'ridge')
        self.Menu_Title_Frame.grid(column = 0, row = 0, sticky = 'nsw')
        self.Menu_Title_Frame.grid_columnconfigure(0, weight = 3)
        self.Menu_Title_Frame.grid_rowconfigure(0, weight = 3)

        ### This subframe will hold a canvas for scrolling and recipe display
        ### Parent MultiGrid_Frame
        self.GetRecipe_Frame = ttk.Frame(self.MultiGrid_Frame, padding = 8, border = 3, relief = 'ridge')
        self.GetRecipe_Frame.grid(column = 1, row = 0, sticky = 'nsw')
        self.GetRecipe_Frame.grid_columnconfigure(0, weight = 3)
        self.GetRecipe_Frame.grid_rowconfigure(0, weight = 3)

        ### Start Init Methods/Functions ##########################################################
        self.Logo()
        self.LetterMenu()
        self.TitleMenu()
        self.GetRecipe(id = 1)
        self.Footer()
        ### End Method/Function Init ##############################################################

    def Footer(self):
        style = ttk.Style()
        style.map('Footer.TLabel')
        style.configure('Footer.TLabel', foreground = 'blue', font = ('Sans', 9, 'normal'), padding = 8, border = 2)
        footer = ttk.Button(self.Footer_Frame, text = 'Register @ Johnny\'s CookBook', style = 'Footer.TLabel', cursor = 'hand2', command = self.Web)
        footer.grid(column = 0, row = 0, sticky = 'n')
        footer.grid_columnconfigure(0, weight = 3)

    ### Method for opening a webbrowser
    def Web(self):
        webbrowser.open_new('http://recipes.phpshelf.net')

    ### Function/Method for displaying the recipe
    def GetRecipe(self, id):
        canvas = tk.Canvas(self.GetRecipe_Frame, width = 600)
        canvas.configure(border = 5, highlightcolor = 'gray87')
        scrollbar = ttk.Scrollbar(self.GetRecipe_Frame, 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)


        data = Database().IDQuery(id = id)

        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[0][1]}', style = 'Title.TLabel')
        title.grid(columnspan = 2, column = 0, row = 0, sticky = 'new')

        ### Prep/Cook/Total Times
        prep =  self.TimeConvert(data[0][5])
        cook = self.TimeConvert(data[0][6])
        total = self.TimeConvert(data[0][5] + data[0][6])

        style2 = ttk.Style()
        style2.configure('Time.TLabel', foreground = 'blue', relief = 'raised', padding = 5)
        times = ttk.Label(scrollable_frame, text = f' Prep Time: {prep} | Cook Time: {cook} | Total Time: {total}' , 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 = 300)
        ingredients = ttk.Label(scrollable_frame, text = data[0][2], style = 'Recipe.TLabel')
        ingredients.grid(column = 0, row = 3, sticky = 'nw')

        instructions = ttk.Label(scrollable_frame, text = data[0][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')

    ### Convert Times
    def TimeConvert(self, nums):
        h, m = divmod(nums, 60)
        hours = 'hr'
        if nums > 60:
            if h > 1:
                hours = 'hrs'
            if m > 0:
                rtime = f'{h} {hours} {m} min.'
            else:
                rtime = f'{h} {hours}'
        else:
            rtime = f'{nums} min.'
        return rtime

    ### Function/Method for getting all recipe titles
    def TitleMenu(self, letter = 'a'):
        canvas = tk.Canvas(self.Menu_Title_Frame)
        canvas.configure(border = 5, highlightcolor = 'gray87')
        scrollbar = ttk.Scrollbar(self.Menu_Title_Frame, 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)

        data = Database().TitleQuery(letter = letter)
        if len(data) > 0:
            self.old_letter = letter

        if len(data) == 0:
            msg = messagebox.showerror(title = 'No listing', message = 'Sorry, there have not been any recipes added starting with ' + letter)
            if msg == 'ok':
                self.TitleMenu(self.old_letter)

        style = ttk.Style()
        style.map('L.TLabel',
            background = [('pressed', '!disabled', 'gray86'),('active','#ffffee')],
            foreground = [('pressed', 'red'),('active', 'red')]
        )
        style.configure('L.TLabel', relief = 'flat', padding = 2, foreground = 'blue')
        for id, title in data:
            title = ttk.Button(scrollable_frame, text = title.title(), style = 'L.TLabel', cursor = 'hand2', command = partial(self.GetRecipe, id))
            title.grid(column = 0, row = id, sticky = 'nw')

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

    ### Function/Method for getting logo image and displaying
    def Logo(self):
        # self.imgfile = tk.PhotoImage(file = '/home/johnny/Desktop/play/cookbook_logo.png')
        # logo = ttk.Label(self.Logo_Frame, image = self.imgfile, anchor = 'n')
        style = ttk.Style()
        style.configure('Logo.TLabel', foreground = 'blue2', font = ('Sans', 16, 'bold'), padding = 8, border = 5)
        logo = ttk.Label(self.Logo_Frame, text = 'Johnny\'s CookBook', anchor = 'n', style = 'Logo.TLabel')
        logo.grid(column = 0, row = 0, sticky = 'new')

    ### Function/Method for displaying the letter menu
    def LetterMenu(self):
        letters = string.ascii_uppercase
        i = 0
        for letter in letters:
            button_style = ttk.Style()
            button_style.configure('Button.TButton')
            button = ttk.Button(self.Letter_Menu_Frame, text = letter, width = 1, style = 'Button.TButton', command = partial(self.TitleMenu, letter))
            button.grid(column = i, row = 0, sticky = 'new', padx = 2)
            button.grid_columnconfigure(i, weight = 3)
            i += 1


def main():
    root = tk.Tk()
    root.title('Johnny\'s CookBook')
    imgfile = tk.PhotoImage(file = '/home/johnny/Desktop/play/cookbook_logo.png')
    root.configure(width = imgfile.width(), border = 5, relief = 'ridge')
    root.resizable(width = False, height = False)
    app = MainWindow(root)
    root.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


Reply


Forum Jump:

User Panel Messages

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