0

I have created a code that takes random "cocktail of the day" from JSON. Everything works great:

  • click on image refreshes data,
  • click on Fav icon stores current data in sql (like an local archive),
  • click on the header changes from online/local version and vice versa.

Now, data manipulation works great, but on the header I have two labels: "label_online" whick text changes from online to archive depending on source, and "Category" which shows cocktail category. They both work as they should, but the problem is they randomly disappear when I click on header. On every click they act randomly, sometimes they disappear, sometimes they don't.

import io
import json
import tkinter as tk
import urllib.request
from PIL import Image, ImageTk
from urllib.request import urlopen
import requests
import sqlite3
import datetime

now = datetime.datetime.now()
datetime_str = now.strftime("%Y%m%d%H%M%S")
id = "4d4-" + datetime_str
date_str = now.strftime("%d.%m.%Y.")

conn = sqlite3.connect('cocktails.db')
cursor = conn.cursor()
cursor.execute('''
          CREATE TABLE IF NOT EXISTS cocktails_4d4 (
              id TEXT PRIMARY KEY,
              name_4d4 TEXT,
              instructions_4d4 TEXT,
              photo_path_4d4 TEXT,
              ingredients_4d4 TEXT,
              category_4d4 TEXT,
              date_4d4 TEXT
          )
          ''')

def do_resize():  # <-- return to original size
    root.geometry('520x280')

def do_refresh():
    root.geometry('520x281')   # <-- temporary change window size by 1 px to avoid label disappear
    root.after(5, do_resize)  # <-- return it to original size

def change_title(new_title):
    root.title(new_title)

def update_cocktail_image(event=None):
    global id, cocktail, instructions, ingredients, category, cocktail_photo_path, date_str, datetime_str, image2

    now = datetime.datetime.now()
    datetime_str = now.strftime("%Y%m%d%H%M%S")
    id = "4d4-" + datetime_str
    date_str = now.strftime("%d.%m.%Y.")

    url = 'https://www.thecocktaildb.com/api/json/v1/1/random.php'
    response = urlopen(url)
    data_json = json.loads(response.read())

    cocktail_photo_fav1.configure(image=image_fav1)

    cocktail = data_json['drinks'][0]['strDrink']
    instructions = data_json['drinks'][0]['strInstructions']
    cocktail_name.config(text=cocktail)
    cocktail_inst.config(text=instructions)

    change_title(f'Cocktail of the day - {cocktail}')
    
    cocktail_photo_path = data_json['drinks'][0]['strDrinkThumb']
    response = requests.get(cocktail_photo_path)
    image_data = response.content
    image2 = Image.open(io.BytesIO(image_data)).resize((120, 120))
    image2 = ImageTk.PhotoImage(image2)
    cocktail_photo.configure(image=image2)
    cocktail_photo.image = image2

    ingredients = ""
    for i in range(1, 16):
        ingredient = data_json['drinks'][0].get(f'strIngredient{i}')
        measure = data_json['drinks'][0].get(f'strMeasure{i}')
        if ingredient and measure:
            ingredients += f"{measure} {ingredient}\n"
    cocktail_ingredients.configure(text=ingredients)
    #print(ingredients)
    category=data_json['drinks'][0]['strCategory']  
    category_lbl.configure(text=f'Category: {category}')   
    #print(datetime_str)
    do_refresh()

cocktail_displayed = False


def save_or_delete_cocktail(event):
    if header.cget('bg') == 'darkred':
        confirm_delete()
    else:
        save_cocktail()


def confirm_delete():
    global confirm_label, confirm_button, cancel_button
    confirm_label = tk.Label(footer, text="Delete?", font=('Segoe UI', 10), bg='darkgoldenrod', fg='white')
    confirm_label.pack(padx=20, pady=10, side='left')
    confirm_button = tk.Button(footer, text="Yes, delete it", font=('Segoe UI', 10), bg='darkgoldenrod', fg='white', command=cocktail_delete)
    confirm_button.pack(padx=10,pady=10,side='left')

    def cancel_deletion():
        #confirm_window.destroy()
        confirm_label.pack_forget()
        confirm_button.pack_forget()
        cancel_button.pack_forget()
        confirm_button.pack_forget() 
        cocktail_photo_fav1.configure(image=image_fav2)
        
    cancel_button = tk.Button(footer, text="No, cancel", font=('Segoe UI', 10), bg='darkgoldenrod', fg='white', command=cancel_deletion)
    cancel_button.pack(padx=20, pady=10, side='left')

def cocktail_delete():
    global id
    conn = sqlite3.connect('cocktails.db')
    cursor = conn.cursor()
    curr_id=cocktail_id.cget('text')
    #print(curr_id)
    
    cursor.execute("DELETE FROM cocktails_4d4 WHERE id=?", (curr_id,))
    conn.commit()

    #print(f"Cocktail {curr_id} deleted")
    previous_cocktail()
    confirm_label.pack_forget()
    confirm_button.pack_forget()
    cancel_button.pack_forget()
    confirm_button.pack_forget() 
    cocktail_photo_fav1.configure(image=image_fav2)

def save_cocktail():
    #cocktail_id = str(uuid.uuid4())
    cursor.execute('''
            INSERT INTO cocktails_4d4 (id, name_4d4, instructions_4d4, photo_path_4d4, ingredients_4d4, category_4d4, date_4d4)
            VALUES (?, ?, ?, ?, ?, ?, ?)
            ''', (id, cocktail, instructions, cocktail_photo_path, ingredients, category, date_str))

    conn.commit()
    #conn.close()
    cocktail_photo_fav1.configure(image=image_fav2)
    global cocktail_displayed
    cocktail_displayed = True
    print(f'cocktail {cocktail} saved')

def update_cocktail_labels(row):
    global cocktail_id
    cocktail_id.configure(text=row[0])
    cocktail_name.configure(text=row[1])
    cocktail_inst.configure(text=row[2])
    #------------------------------------
    cocktail_photo_path = row[3]
    response = requests.get(cocktail_photo_path)
    image_data = response.content
    imagea = Image.open(io.BytesIO(image_data)).resize((120, 120))
    imagea = ImageTk.PhotoImage(imagea)
    cocktail_photo.configure(image=imagea)
    cocktail_photo.image = imagea
    #------------------------------------
    cocktail_ingredients.configure(text=row[4])
    category_lbl.configure(text=row[5])
    #print(row[3])

def previous_cocktail():
    global current_cocktail_index
    current_cocktail_index -= 1
    conn = sqlite3.connect('cocktails.db')
    cursor = conn.cursor()
    cursor.execute("SELECT * FROM cocktails_4d4")
    rows = cursor.fetchall()
    if current_cocktail_index < 0:
        current_cocktail_index = len(rows) - 1
    update_cocktail_labels(rows[current_cocktail_index])

def next_cocktail():
    global current_cocktail_index
    current_cocktail_index += 1
    conn = sqlite3.connect('cocktails.db')
    cursor = conn.cursor()
    cursor.execute("SELECT * FROM cocktails_4d4")
    rows = cursor.fetchall()
    if current_cocktail_index >= len(rows):
        current_cocktail_index = 0
    update_cocktail_labels(rows[current_cocktail_index])


def change_header():
    if header.cget('bg') == "darkgreen":
        header.config(bg="darkred")
        category_lbl.config(bg="darkred")
        header_online.config(text='Location: | Archive |', bg='darkred')
        do_refresh()

        #print(header.cget('bg'))

        conn = sqlite3.connect('cocktails.db')
        cursor = conn.cursor()
        cursor.execute("SELECT * FROM cocktails_4d4")
        rows = cursor.fetchall()
        
        global current_cocktail_index
        current_cocktail_index = 0
        update_cocktail_labels(rows[current_cocktail_index])
        cocktail_photo_fav1.configure(image=image_fav2)
        # Add the "previous" and "next" buttons
        previous_button.pack(side="left", padx=5)
        next_button.pack(side="right", padx=5)
        footer.configure(text='')
        do_refresh()

    else:
        header.config(bg="darkgreen")
        category_lbl.config(bg="darkgreen")
        header_online.config(text='Location: | Online |', bg='darkgreen')
        do_refresh()
        update_cocktail_image()
        previous_button.pack_forget()
        next_button.pack_forget()
        footer.configure(text="Hit Me    ")
        #print(header.cget('bg'))


#############################################################################################################

url='https://www.thecocktaildb.com/api/json/v1/1/random.php'

response=urlopen(url)
data_json=json.loads(response.read())

root = tk.Tk()

#json
cocktail=data_json['drinks'][0]['strDrink']# <-- name of the random cocktail
instructions=data_json['drinks'][0]['strInstructions']
ingredients = ""
for i in range(1, 16):
    ingredient = data_json['drinks'][0].get(f'strIngredient{i}')
    measure = data_json['drinks'][0].get(f'strMeasure{i}')
    if ingredient and measure:
        ingredients += f"{measure} {ingredient}\n"
category=data_json['drinks'][0]['strCategory']        


#start
root.title(f'Cocktail of the day - {cocktail}')

root.geometry('520x280')

header = tk.Label(root, text="My Cocktail of the Day  ", font=("Segoe UI", 14, 'bold'), bg="darkgreen", fg="white", height=2, anchor='ne', justify='right')
header.pack(side="top", fill="x")
header.pack_propagate(False)
header.bind("<Button-1>", lambda event: change_header())

header_online=tk.Label(header, text='Location: | Online |',font=('Segoe UI', 10), bg='darkgreen', fg='white')
header_online.place(x=15, y=0)

category_lbl=tk.Label(header, text=f'Category: {category}', font=('Segoe UI', 9, "bold"), bg="darkgreen", fg='white')
category_lbl.place(x=302, y=25)

cocktail_photo_path=data_json['drinks'][0]['strDrinkThumb']
response = requests.get(cocktail_photo_path)
image_data = requests.get(cocktail_photo_path).content

image = Image.open(io.BytesIO(image_data)).resize((120, 120))
image = ImageTk.PhotoImage(image)
cocktail_photo = tk.Label(root, image=image, wraplength=100)
cocktail_photo.place(x=20, y=25)
cocktail_photo.bind('<Button-1>', update_cocktail_image)

url_fav1='https://devling.com.hr/webdisk/python/Cocktails/favorite_1.png'
response = requests.get(url_fav1)
image_data_fav1 = requests.get(url_fav1).content
image_fav1 = Image.open(io.BytesIO(image_data_fav1)).resize((20, 25))
image_fav1 = ImageTk.PhotoImage(image_fav1)

url_fav2='https://devling.com.hr/webdisk/python/Cocktails/favorite_2.png'
response = requests.get(url_fav2)
image_data_fav2 = requests.get(url_fav2).content
image_fav2 = Image.open(io.BytesIO(image_data_fav2)).resize((20, 25))
image_fav2 = ImageTk.PhotoImage(image_fav2)

cocktail_photo_fav1 = tk.Label(root, image=image_fav1)
cocktail_photo_fav1.place(x=480, y=50)
cocktail_photo_fav1.bind('<Button-1>', save_or_delete_cocktail)

cocktail_ing=tk.Label(root, text='Ingredients: ', font=('Segoe UI', 9, 'bold'), fg='dimgray')
cocktail_ing.place(x=20, y=150)

cocktail_id=tk.Label(root, text='')
cocktail_id.place(x=0, y=0)
cocktail_id.place_forget()

cocktail_ingredients = tk.Label(root, text=ingredients, font=('SegoeUI', 9), anchor='w', justify='left')
cocktail_ingredients.place(x= 20, y=170)

cocktail_name = tk.Label(root, text=cocktail, font=('SegoeUI', 12, 'bold'))
cocktail_name.place(x= 160, y=60)

cocktail_inst = tk.Label(root, text=instructions, font=('SegoeUI', 10), wraplength=350, anchor='w', justify='left')
cocktail_inst.place(x= 160, y=90)

footer = tk.Label(root, text="Hit Me    ", font=("Segoe UI", 13, "bold"), bg="darkgoldenrod", fg="white", height=1, anchor='e', justify='right')
footer.pack(side="bottom", fill="x")
footer.bind('<Button-1>', update_cocktail_image)

arrow_char = "\u279c"  
previous_button = tk.Button(footer, text="\u2b98", font=("Wingdings", 9, "bold"), height=1, bg="darkgoldenrod", fg="white", command=previous_cocktail)
previous_button.pack(side="left", padx=5)
previous_button.pack_forget()
next_button = tk.Button(footer, text="\u2b9a", font=("Wingdings", 9, "bold"), bg="darkgoldenrod", fg="white", command=next_cocktail)
next_button.pack(side="right", padx=5)
next_button.pack_forget()

#print(f"Moj odabrani koktel je {cocktail}")
root.mainloop()

I've noticed that when they disappear, and I move window border (even for 1px) they appear back, so only solution I could think of was to resize root.geometry by 1px and return it to original size after 5ms.

Here is the code that does that:

def do_resize():  # <-- return to original size
    root.geometry('520x280')

def do_refresh():
    root.geometry('520x281')   # <-- temporary change window size by 1 px to avoid label disappear
    root.after(5, do_resize)  # <-- return it to original size

and the I call function "do_refresh" on every click on header_online, and it works. Is there a better solution, and what am I doing wrong?

marc_s
  • 732,580
  • 175
  • 1,330
  • 1,459
NeoA
  • 1
  • 1
  • Please trim your code to make it easier to find your problem. Follow these guidelines to create a [minimal reproducible example](https://stackoverflow.com/help/minimal-reproducible-example). – Сергей Кох Mar 12 '23 at 20:49
  • Do a network communication in a background thread. Blocking the UI event loop can cause a glitch. – relent95 Mar 13 '23 at 01:09
  • @Сергей Кох Sorry, but I've already trim it as much as I could, since this is part of my 4000+ lines of code, and this is the part that bothers me. There are only 10 function and 10 labels (because I need them), without them maybe problem would disappear, but I need them so my program would work as intended. – NeoA Mar 13 '23 at 10:50
  • @relent95 I've tried it in one of previous versions, but had no luck with it. Do I join it in the end, or leave it ? – NeoA Mar 13 '23 at 10:53
  • See [this answer](https://stackoverflow.com/a/74989468/1186624). – relent95 Mar 13 '23 at 15:17
  • @relent95, tnx, it helped (with my other problems), but didn't solve this one. – NeoA Mar 15 '23 at 20:15
  • Then update the question for your current version which does network communications in a background thread. – relent95 Mar 16 '23 at 00:29

0 Answers0