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
90
Gui.py
90
Gui.py
|
@ -8,7 +8,7 @@ from tkinter import Checkbutton, OptionMenu, Toplevel, LabelFrame, PhotoImage, T
|
||||||
from urllib.parse import urlparse
|
from urllib.parse import urlparse
|
||||||
from urllib.request import urlopen
|
from urllib.request import urlopen
|
||||||
|
|
||||||
from GuiUtils import ToolTips
|
from GuiUtils import ToolTips, set_icon, BackgroundTaskProgress
|
||||||
from Main import main, __version__ as ESVersion
|
from Main import main, __version__ as ESVersion
|
||||||
from Rom import Sprite
|
from Rom import Sprite
|
||||||
from Utils import is_bundled, local_path, output_path, open_file
|
from Utils import is_bundled, local_path, output_path, open_file
|
||||||
|
@ -292,12 +292,6 @@ def guiMain(args=None):
|
||||||
|
|
||||||
mainWindow.mainloop()
|
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):
|
class SpriteSelector(object):
|
||||||
def __init__(self, parent, callback):
|
def __init__(self, parent, callback):
|
||||||
if is_bundled():
|
if is_bundled():
|
||||||
|
@ -326,6 +320,7 @@ class SpriteSelector(object):
|
||||||
button.pack(side=LEFT)
|
button.pack(side=LEFT)
|
||||||
|
|
||||||
set_icon(self.window)
|
set_icon(self.window)
|
||||||
|
self.window.focus()
|
||||||
|
|
||||||
def icon_section(self, frame_label, path, no_results_label):
|
def icon_section(self, frame_label, path, no_results_label):
|
||||||
frame = LabelFrame(self.window, text=frame_label, padx=5, pady=5)
|
frame = LabelFrame(self.window, text=frame_label, padx=5, pady=5)
|
||||||
|
@ -349,23 +344,72 @@ class SpriteSelector(object):
|
||||||
|
|
||||||
def update_official_sprites(self):
|
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.
|
# need to wrap in try catch. We don't want errors getting the json or downloading the files to break us.
|
||||||
sprites_arr = json.loads(temp_sprites_json)
|
|
||||||
current_sprites = [os.path.basename(file) for file in glob('data/sprites/official/*')]
|
|
||||||
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)
|
|
||||||
|
|
||||||
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))
|
|
||||||
|
|
||||||
self.window.destroy()
|
self.window.destroy()
|
||||||
SpriteSelector(self.parent, self.callback)
|
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)
|
||||||
|
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]
|
||||||
|
bundled_sprites=[]
|
||||||
|
|
||||||
|
official_filenames = [filename for (_, filename) in official_sprites]
|
||||||
|
obsolete_sprites = [sprite for sprite in current_sprites if sprite not in official_filenames]
|
||||||
|
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
|
||||||
|
|
||||||
|
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):
|
def browse_for_sprite(self):
|
||||||
|
|
75
GuiUtils.py
75
GuiUtils.py
|
@ -1,5 +1,79 @@
|
||||||
|
import queue
|
||||||
|
import threading
|
||||||
import tkinter as tk
|
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):
|
class ToolTips(object):
|
||||||
# This class derived from wckToolTips which is available under the following license:
|
# 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, "<Enter>", cls.enter)
|
||||||
widget.bind_class(cls.tag, "<Leave>", cls.leave)
|
widget.bind_class(cls.tag, "<Leave>", cls.leave)
|
||||||
widget.bind_class(cls.tag, "<Motion>", cls.motion)
|
widget.bind_class(cls.tag, "<Motion>", cls.motion)
|
||||||
|
widget.bind_class(cls.tag, "<Destroy>", cls.leave)
|
||||||
|
|
||||||
# pick suitable colors for tooltips
|
# pick suitable colors for tooltips
|
||||||
try:
|
try:
|
||||||
|
|
Loading…
Reference in New Issue