I am trying to create animations from gif on a tkinter window. The window must be draggable and It is. But dragging creates problems in calling the update function. The code is under below. The problem is on_drag_ functions. Usually only click the window and release it without dragging, the update function call itself weirdly.
on_drag_ functions are called like,
self.window.bind("<Double-Button-1>", self.openMenu)
self.window.bind("<ButtonPress-1>", self.on_drag_start)
self.window.bind("<ButtonPress-3>", self.hide_window)
self.window.bind("<B1-Motion>", self.on_drag_motion)
self.window.bind("<ButtonRelease-1>", self.on_drag_stop)
def on_drag_start(self, event):
print('on_drag_start')
self.drag_start_x = event.x_root
self.drag_start_y = event.y_root
self.initial_window_x = self.window.winfo_x()
self.initial_window_y = self.window.winfo_y()
def on_drag_motion(self, event):
print('on_drag_motion')
self.animation_running = False
deltax = event.x_root - self.drag_start_x
new_x = self.initial_window_x + deltax
new_x = max(min(new_x, 1600), 1200)
deltay = event.y_root - self.drag_start_y
new_y = self.initial_window_y + deltay
new_y = max(min(new_y, 922), 400)
self.window.geometry(f"+{new_x}+{new_y}")
self.x = new_x
self.y = new_y
def on_drag_stop(self, event):
print('on_drag_stop')
if not self.animation_running:
self.animation_running = True
self.window.after(200, self.update)
def update(self):
current_milliseconds = int(time.time() * 1000)
print('update ', current_milliseconds-self.before_ms)
self.before_ms = current_milliseconds
if not self.animation_running:
return
Example output : (update {past miliseconds since the last update function was called})
update 1016
update 1013
update 1009
on_drag_start
on_drag_stop
update 1013
on_drag_start
on_drag_stop
update 1015
on_drag_start
on_drag_stop
on_drag_start
update 1008
on_drag_motion
on_drag_motion
on_drag_motion
on_drag_motion
on_drag_motion
on_drag_stop
update 334
update 131
update 135
update 17
on_drag_start
update 135
on_drag_motion
on_drag_motion
on_drag_stop
update 138
update 119
update 34
update 134
update 37
update 127
update 37
update 130
update 36
update 132
update 0
update 144
update 0
update 147
update 0
update 145
update 0
update 145
import pyautogui
import random
import tkinter as tk
import os
from tkinter import messagebox
from PIL import Image, ImageTk
import pystray
from pystray import MenuItem as item
import threading
import menu
import time
EVENTS = { # eventNumber: [[actionOrderToBeCompleted], [PossibleNextEventNumbers]] }
class DesktopCat:
def __init__(self):
self.animation_running = True
self.falling = False
self.x = 1400
self.y = 922
self.initial_x = 1400
self.initial_y = 922
self.cycle = 0
self.current_event_cycle = random.choice([0, 8, 1, 9, 5, 13])
self.current_event_cycle_index = 0
self.EVENTS = EVENTS
self.event_number = self.EVENTS[self.current_event_cycle][0][0]
self.impath = '\path'
self.fallingPath = '\path'
self.menu_bg_image_path = "\path"
self.fallingGif = []
self.tray_path = '\path'
self.flyPNG = []
self.imageGif = {}
self.images = []
self.position = 0
self.before_ms = 0
self.icon = None
self.icon_created = False
self.command_created = False
self.message = None
self.window = tk.Tk()
self.label = tk.Label(self.window, bd=0, bg='black')
self.error = self.load_images()
if self.error == 0:
self.setup_window()
self.start_animation()
def load_images(self):
self.images = os.listdir(self.impath)
error = 0
i = 0
for image in self.images:
i+=1
try:
defrange = 4
if 'walk' in image or 'goosebumps' in image:
defrange = 8
elif 'touch' in image:
defrange = 6
elif 'jump' in image:
defrange = 7
self.imageGif[image] = [tk.PhotoImage(file=os.path.join(self.impath, image), format=f'gif -index {i}') for i in range(defrange)]
except tk.TclError as e:
print(f"Error loading {os.path.join(self.impath, image)} : {e}")
error = 1
self.imageGif['falling'] = [tk.PhotoImage(file=self.fallingPath, format=f'gif -index {i}') for i in range(4)]
self.images.append('falling')
return error
def on_drag_start(self, event):
print('on_drag_start')
self.drag_start_x = event.x_root
self.drag_start_y = event.y_root
self.initial_window_x = self.window.winfo_x()
self.initial_window_y = self.window.winfo_y()
def on_drag_motion(self, event):
print('on_drag_motion')
self.animation_running = False
deltax = event.x_root - self.drag_start_x
new_x = self.initial_window_x + deltax
new_x = max(min(new_x, 1600), 1200)
deltay = event.y_root - self.drag_start_y
new_y = self.initial_window_y + deltay
new_y = max(min(new_y, 922), 400)
self.window.geometry(f"+{new_x}+{new_y}")
self.x = new_x
self.y = new_y
def on_drag_stop(self, event):
print('on_drag_stop')
if not self.animation_running:
self.animation_running = True
self.window.after(200, self.update)
def setup_window(self):
self.window.config(highlightbackground='black')
self.label.pack()
self.window.overrideredirect(True)
self.window.wm_attributes('-transparentcolor', 'black')
self.window.wm_attributes('-topmost', 1)
self.window.bind("<Double-Button-1>", self.openMenu)
self.window.bind("<ButtonPress-1>", self.on_drag_start)
self.window.bind("<ButtonPress-3>", self.hide_window)
self.window.bind("<B1-Motion>", self.on_drag_motion)
self.window.bind("<ButtonRelease-1>", self.on_drag_stop)
def start_animation(self):
self.window.after(1, self.update)
self.window.mainloop()
def event(self):
if self.event_number in [1, 2, 4, 5] or self.event_number - 10 in [1, 2, 4, 5]:
self.window.after(200, self.update)
elif self.event_number in [8, 9, 0] or self.event_number - 10 in [8, 9, 0]:
self.window.after(150, self.update)
elif self.event_number in [6, 16]:
self.window.after(1000, self.update)
elif self.event_number in [3, 13]:
self.window.after(160, self.update)
elif self.event_number in [7, 17]:
self.window.after(160, self.update)
elif self.event_number == 20:
self.window.after(120, self.update)
def gif_work(self):
if self.cycle < len(self.imageGif[self.images[self.event_number]]) - 1:
self.cycle += 1
else:
self.cycle = 0
self.event_number = self.choose_event_change()
def update(self):
current_milliseconds = int(time.time() * 1000)
print('update ', current_milliseconds-self.before_ms)
self.before_ms = current_milliseconds
if not self.animation_running:
return
if self.y < self.initial_y:
if not self.falling:
self.falling = True
self.current_event_cycle = 21
self.current_event_cycle_index = 0
self.cycle = 0
self.event_number = self.EVENTS[self.current_event_cycle][0][self.current_event_cycle_index]
self.y += 20
elif self.y >= self.initial_y and self.falling:
self.current_event_cycle = random.choice([7,15])
self.current_event_cycle_index = 0
self.cycle = 0
self.event_number = self.EVENTS[self.current_event_cycle][0][self.current_event_cycle_index]
self.falling = False
self.y = self.initial_y
frame = self.imageGif[self.images[self.event_number]][self.cycle]
if self.event_number in [8, 9, 3]:
self.x -= 8
elif self.event_number in [18, 19, 13]:
self.x += 8
if self.animation_running:
self.gif_work()
self.window.geometry('160x160+' + str(self.x) + '+' + str(self.y))
self.label.configure(image=frame)
self.window.after(1, self.event)
def choose_event_change(self):
if self.current_event_cycle_index < len(self.EVENTS[self.current_event_cycle][0]) - 1:
self.current_event_cycle_index += 1
else:
next_event_cycle = random.choice(self.EVENTS[self.current_event_cycle][1])
while self.x > self.initial_x+200 and next_event_cycle in [2, 3, 4]:
next_event_cycle = random.choice(self.EVENTS[self.current_event_cycle][1])
while self.x < self.initial_y-200 and next_event_cycle in [10, 11, 12]:
next_event_cycle = random.choice(self.EVENTS[self.current_event_cycle][1])
self.current_event_cycle = next_event_cycle
self.current_event_cycle_index = 0
return self.EVENTS[self.current_event_cycle][0][self.current_event_cycle_index]
def show_window(self):
print('show_window')
self.window.after(0, self.window.deiconify)
if self.icon_created:
self.icon_created = False
self.icon.stop()
def hide_window(self, event):
print('hide_window')
self.window.withdraw()
self.create_tray_icon()
def exit_application(self):
print('exit_application')
self.icon.stop()
self.window.quit()
def create_tray_icon(self):
print('create_tray_icon')
icon_image = Image.open(self.tray_path)
menu = (item('Show', lambda: self.show_window()), item('Command Menu', self.openMenu), item('Exit', self.exit_application))
self.icon = pystray.Icon("DesktopCat", icon_image, "DesktopCat", menu)
self.icon_created = True
self.icon.run()
def openMenu(self, event):
print('openMenu: ')
if not self.command_created:
print(' command_created')
self.show_window()
self.command_created = True
self.message = menu.PixelArtMessageBoxApp(Image.open(self.menu_bg_image_path).convert("RGBA"))
self.current_event_cycle = 22
self.current_event_cycle_index = 0
self.cycle = 0
self.event_number = self.EVENTS[self.current_event_cycle][0][self.current_event_cycle_index]
self.message.init(self.x-295, self.y-110)
elif self.message != None:
print(' message deleted')
self.command_created = False
self.message.deleteMessageBox()
del self.message
self.message = None
self.current_event_cycle = random.randint(0,20)
self.current_event_cycle_index = 0
self.cycle = 0
self.event_number = self.EVENTS[self.current_event_cycle][0][self.current_event_cycle_index]
if __name__ == '__main__':
desktop_cat = DesktopCat()
I have tryed to threading.Lock() and any little change I can think of. Non of them had work. I could not even debug the code. I do not know what the exact problem is.