Made sprite updating a background task
Also make it use the correct folders for bundled builds
This commit is contained in:
parent
0372896bc6
commit
f44d78d82f
78
Gui.py
78
Gui.py
|
@ -8,7 +8,7 @@ from tkinter import Checkbutton, OptionMenu, Toplevel, LabelFrame, PhotoImage, T
|
|||
from urllib.parse import urlparse
|
||||
from urllib.request import urlopen
|
||||
|
||||
from GuiUtils import ToolTips
|
||||
from GuiUtils import ToolTips, set_icon, BackgroundTaskProgress
|
||||
from Main import main, __version__ as ESVersion
|
||||
from Rom import Sprite
|
||||
from Utils import is_bundled, local_path, output_path, open_file
|
||||
|
@ -292,12 +292,6 @@ def guiMain(args=None):
|
|||
|
||||
mainWindow.mainloop()
|
||||
|
||||
def set_icon(window):
|
||||
er16 = PhotoImage(file=local_path('data/ER16.gif'))
|
||||
er32 = PhotoImage(file=local_path('data/ER32.gif'))
|
||||
er48 = PhotoImage(file=local_path('data/ER32.gif'))
|
||||
window.tk.call('wm', 'iconphoto', window._w, er16, er32, er48)
|
||||
|
||||
class SpriteSelector(object):
|
||||
def __init__(self, parent, callback):
|
||||
if is_bundled():
|
||||
|
@ -326,6 +320,7 @@ class SpriteSelector(object):
|
|||
button.pack(side=LEFT)
|
||||
|
||||
set_icon(self.window)
|
||||
self.window.focus()
|
||||
|
||||
def icon_section(self, frame_label, path, no_results_label):
|
||||
frame = LabelFrame(self.window, text=frame_label, padx=5, pady=5)
|
||||
|
@ -349,23 +344,72 @@ class SpriteSelector(object):
|
|||
|
||||
def update_official_sprites(self):
|
||||
# need to wrap in try catch. We don't want errors getting the json or downloading the files to break us.
|
||||
self.window.destroy()
|
||||
self.parent.update()
|
||||
def work(task):
|
||||
resultmessage=""
|
||||
successful = True
|
||||
|
||||
def finished():
|
||||
task.close_window()
|
||||
if successful:
|
||||
messagebox.showinfo("Sprite Updater", resultmessage)
|
||||
else:
|
||||
messagebox.showerror("Sprite Updater", resultmessage)
|
||||
SpriteSelector(self.parent, self.callback)
|
||||
|
||||
try:
|
||||
task.update_status("Downloading official sprites list")
|
||||
sprites_arr = json.loads(temp_sprites_json)
|
||||
current_sprites = [os.path.basename(file) for file in glob('data/sprites/official/*')]
|
||||
except Exception as e:
|
||||
resultmessage = "Error getting list of official sprites. Sprites not updated.\n\n%s: %s" % (type(e).__name__, e)
|
||||
successful = False
|
||||
task.queue_event(finished)
|
||||
return
|
||||
|
||||
try:
|
||||
task.update_status("Determining needed sprites")
|
||||
current_sprites = [os.path.basename(file) for file in glob(self.official_sprite_dir+'/*')]
|
||||
official_sprites = [(sprite['file'], os.path.basename(urlparse(sprite['file']).path)) for sprite in sprites_arr]
|
||||
needed_sprites = [(sprite_url, filename) for (sprite_url, filename) in official_sprites if filename not in current_sprites]
|
||||
|
||||
for (sprite_url, filename) in needed_sprites:
|
||||
target = os.path.join('data/sprites/official',filename)
|
||||
with urlopen(sprite_url) as response, open(target, 'wb') as out:
|
||||
shutil.copyfileobj(response, out)
|
||||
bundled_sprites=[]
|
||||
|
||||
official_filenames = [filename for (_, filename) in official_sprites]
|
||||
obsolete_sprites = [sprite for sprite in current_sprites if sprite not in official_filenames]
|
||||
for sprite in obsolete_sprites:
|
||||
os.remove(os.path.join('data/sprites/official', sprite))
|
||||
except Exception as e:
|
||||
resultmessage = "Error Determining which sprites to update. Sprites not updated.\n\n%s: %s" % (type(e).__name__, e)
|
||||
successful = False
|
||||
task.queue_event(finished)
|
||||
return
|
||||
|
||||
self.window.destroy()
|
||||
SpriteSelector(self.parent, self.callback)
|
||||
updated = 0
|
||||
for (sprite_url, filename) in needed_sprites:
|
||||
try:
|
||||
task.update_status("Downloading needed sprite %g/%g" % (updated + 1, len(needed_sprites)))
|
||||
target = os.path.join(self.official_sprite_dir, filename)
|
||||
with urlopen(sprite_url) as response, open(target, 'wb') as out:
|
||||
shutil.copyfileobj(response, out)
|
||||
except Exception as e:
|
||||
resultmessage = "Error downloading sprite. Not all sprites updated.\n\n%s: %s" % (type(e).__name__, e)
|
||||
successful = False
|
||||
updated += 1
|
||||
|
||||
deleted = 0
|
||||
for sprite in obsolete_sprites:
|
||||
try:
|
||||
task.update_status("Removing obsolete sprite %g/%g" % (deleted + 1, len(obsolete_sprites)))
|
||||
os.remove(os.path.join(self.official_sprite_dir, sprite))
|
||||
except Exception as e:
|
||||
resultmessage = "Error removing obsolete sprite. Not all sprites updated.\n\n%s: %s" % (type(e).__name__, e)
|
||||
successful = False
|
||||
deleted += 1
|
||||
|
||||
if successful:
|
||||
resultmessage = "official sprites updated sucessfully"
|
||||
|
||||
task.queue_event(finished)
|
||||
|
||||
BackgroundTaskProgress(self.parent, work, "Updating Sprites")
|
||||
|
||||
|
||||
def browse_for_sprite(self):
|
||||
|
|
75
GuiUtils.py
75
GuiUtils.py
|
@ -1,5 +1,79 @@
|
|||
import queue
|
||||
import threading
|
||||
import tkinter as tk
|
||||
|
||||
from Utils import local_path
|
||||
|
||||
def set_icon(window):
|
||||
er16 = tk.PhotoImage(file=local_path('data/ER16.gif'))
|
||||
er32 = tk.PhotoImage(file=local_path('data/ER32.gif'))
|
||||
er48 = tk.PhotoImage(file=local_path('data/ER32.gif'))
|
||||
window.tk.call('wm', 'iconphoto', window._w, er16, er32, er48)
|
||||
|
||||
# Although tkinter is intended to be thread safe, there are many reports of issues
|
||||
# some which may be platform specific, or depend on if the TCL library was compiled without
|
||||
# multithreading support. Therefore I will assume it is not thread safe to avoid any possible problems
|
||||
class BackgroundTask(object):
|
||||
def __init__(self, window, code_to_run):
|
||||
self.window = window
|
||||
self.queue = queue.Queue()
|
||||
self.running = True
|
||||
self.process_queue()
|
||||
self.task=threading.Thread(target=code_to_run , args=(self,))
|
||||
self.task.start()
|
||||
|
||||
def stop(self):
|
||||
self.running = False
|
||||
|
||||
#safe to call from worker
|
||||
def queue_event(self, event):
|
||||
self.queue.put(event)
|
||||
|
||||
def process_queue(self):
|
||||
try:
|
||||
while True:
|
||||
if not self.running:
|
||||
return
|
||||
event = self.queue.get_nowait()
|
||||
event()
|
||||
if self.running:
|
||||
#if self is no longer running self.window may no longer be valid
|
||||
self.window.update_idletasks()
|
||||
except queue.Empty:
|
||||
pass
|
||||
if self.running:
|
||||
self.window.after(100, self.process_queue)
|
||||
|
||||
class BackgroundTaskProgress(BackgroundTask):
|
||||
def __init__(self, parent, code_to_run, title):
|
||||
self.parent = parent
|
||||
self.window = tk.Toplevel(parent)
|
||||
self.window['padx'] = 5
|
||||
self.window['pady'] = 5
|
||||
|
||||
self.window.attributes("-toolwindow",1)
|
||||
|
||||
self.window.wm_title(title)
|
||||
self.labelVar = tk.StringVar()
|
||||
self.labelVar.set("")
|
||||
self.label = tk.Label(self.window, textvariable = self.labelVar, width=50)
|
||||
self.label.pack()
|
||||
self.window.resizable(width=False, height=False)
|
||||
|
||||
set_icon(self.window)
|
||||
self.window.focus()
|
||||
super().__init__(self.window, code_to_run)
|
||||
|
||||
#safe to call from worker thread
|
||||
def update_status(self, text):
|
||||
self.queue_event(lambda text=text: self.labelVar.set(text))
|
||||
|
||||
# only call this in an event callback
|
||||
def close_window(self):
|
||||
self.stop()
|
||||
self.window.destroy()
|
||||
|
||||
|
||||
|
||||
class ToolTips(object):
|
||||
# This class derived from wckToolTips which is available under the following license:
|
||||
|
@ -41,6 +115,7 @@ class ToolTips(object):
|
|||
widget.bind_class(cls.tag, "<Enter>", cls.enter)
|
||||
widget.bind_class(cls.tag, "<Leave>", cls.leave)
|
||||
widget.bind_class(cls.tag, "<Motion>", cls.motion)
|
||||
widget.bind_class(cls.tag, "<Destroy>", cls.leave)
|
||||
|
||||
# pick suitable colors for tooltips
|
||||
try:
|
||||
|
|
Loading…
Reference in New Issue