gui: reformat, add license information, use correct script name

This commit is contained in:
Ian Burgwin
2020-04-02 09:58:31 -07:00
parent 43ae023000
commit 9c1709922a

152
gui.py
View File

@@ -1,10 +1,17 @@
#A gui for custom-install.py # This file is a part of custom-install.py.
#By LyfeOnEdge #
# Copyright (c) 2019-2020 Ian Burgwin
# This file is licensed under The MIT License (MIT).
# You can find the full license text in LICENSE.md in the root of this project.
# A gui for custom-install.py
# By LyfeOnEdge
import os, sys, platform, subprocess, threading import os, sys, platform, subprocess, threading
import tkinter as tk import tkinter as tk
import tkinter.filedialog as tkfiledialog import tkinter.filedialog as tkfiledialog
import style import style
# BUTTON_COLOR = # BUTTON_COLOR =
# BUTTON_HIGHLIGHT_COLOR = # BUTTON_HIGHLIGHT_COLOR =
# BUTTON_FONT = # BUTTON_FONT =
@@ -15,16 +22,17 @@ class themedFrame(tk.Frame):
def __init__(self, frame, **kw): def __init__(self, frame, **kw):
tk.Frame.__init__(self, frame, **kw) tk.Frame.__init__(self, frame, **kw)
if not (kw.get("background") or kw.get("bg")): if not (kw.get("background") or kw.get("bg")):
self.configure(bg = style.BACKGROUND_COLOR) self.configure(bg=style.BACKGROUND_COLOR)
if not kw.get("borderwidth"): if not kw.get("borderwidth"):
self.configure(borderwidth = 0) self.configure(borderwidth=0)
if not kw.get("highlightthickness"): if not kw.get("highlightthickness"):
self.configure(highlightthickness = 0) self.configure(highlightthickness=0)
class Button(tk.Label): class Button(tk.Label):
"""Cross-platform button""" """Cross-platform button"""
def __init__(self,frame,callback,**kw):
def __init__(self, frame, callback, **kw):
self.callback = callback self.callback = callback
self.background = "#aaaaaa" self.background = "#aaaaaa"
self.selected = False self.selected = False
@@ -33,8 +41,8 @@ class Button(tk.Label):
self.configure(background=self.background) self.configure(background=self.background)
self.configure(highlightthickness=1) self.configure(highlightthickness=1)
if not "font" in kw.keys(): if not "font" in kw.keys():
self.configure(font = style.BUTTON_FONT) self.configure(font=style.BUTTON_FONT)
self.configure(highlightbackground = "#999999") self.configure(highlightbackground="#999999")
self.bind('<Button-1>', self.on_click) self.bind('<Button-1>', self.on_click)
# Use callback when our makeshift "button" clicked # Use callback when our makeshift "button" clicked
@@ -61,23 +69,25 @@ class Button(tk.Label):
if not self.selected: if not self.selected:
self.configure(background=self.background) self.configure(background=self.background)
class PathEntry(tk.Entry): class PathEntry(tk.Entry):
"""Tkinter entry widget with a button to set the file path using tkinter's file dialog""" """Tkinter entry widget with a button to set the file path using tkinter's file dialog"""
def __init__(self, frame, dir = False, filetypes = None, *args, **kw):
def __init__(self, frame, dir=False, filetypes=None, *args, **kw):
self.dir = dir self.dir = dir
self.filetypes = filetypes self.filetypes = filetypes
container = themedFrame(frame) container = themedFrame(frame)
self.button = Button(container, self.set_path, text = "...") self.button = Button(container, self.set_path, text="...")
self.button.place(relheight = 1, relx = 1, x = - style.BUTTONSIZE, width = style.BUTTONSIZE) self.button.place(relheight=1, relx=1, x=- style.BUTTONSIZE, width=style.BUTTONSIZE)
tk.Entry.__init__(self, container, *args, **kw) tk.Entry.__init__(self, container, *args, **kw)
self.text_var = tk.StringVar() self.text_var = tk.StringVar()
self.configure(textvariable = self.text_var) self.configure(textvariable=self.text_var)
self.configure(background = style.ENTRY_COLOR) self.configure(background=style.ENTRY_COLOR)
self.configure(foreground = style.ENTRY_FOREGROUND) self.configure(foreground=style.ENTRY_FOREGROUND)
self.configure(borderwidth = 0) self.configure(borderwidth=0)
self.configure(highlightthickness = 2) self.configure(highlightthickness=2)
self.configure(highlightbackground = style.BUTTON_COLOR) self.configure(highlightbackground=style.BUTTON_COLOR)
super().place(relwidth = 1, relheight = 1, width = - style.BUTTONSIZE) super().place(relwidth=1, relheight=1, width=- style.BUTTONSIZE)
self.container = container self.container = container
def clear(self): def clear(self):
@@ -97,18 +107,21 @@ class PathEntry(tk.Entry):
def set_path(self): def set_path(self):
if not self.dir: if not self.dir:
self.set(tkfiledialog.askopenfilename(filetypes = self.filetypes)) self.set(tkfiledialog.askopenfilename(filetypes=self.filetypes))
else: else:
self.set(tkfiledialog.askdirectory()) self.set(tkfiledialog.askdirectory())
class LabeledPathEntry(PathEntry): class LabeledPathEntry(PathEntry):
"""Gives the PathEntry class a label""" """Gives the PathEntry class a label"""
def __init__(self, frame, text, *args, **kw): def __init__(self, frame, text, *args, **kw):
self.xtainer = themedFrame(frame) self.xtainer = themedFrame(frame)
label = tk.Label(self.xtainer, text = text, background = style.BACKGROUND_COLOR, foreground = style.LABEL_COLOR) label = tk.Label(self.xtainer, text=text, background=style.BACKGROUND_COLOR, foreground=style.LABEL_COLOR)
label.place(width = label.winfo_reqwidth(), relheight = 1) label.place(width=label.winfo_reqwidth(), relheight=1)
PathEntry.__init__(self, self.xtainer, *args, **kw) PathEntry.__init__(self, self.xtainer, *args, **kw)
PathEntry.place(self, relwidth = 1, relheight = 1, width = - (label.winfo_reqwidth() + 5), x = label.winfo_reqwidth() + 5) PathEntry.place(self, relwidth=1, relheight=1, width=- (label.winfo_reqwidth() + 5),
x=label.winfo_reqwidth() + 5)
def place(self, **kw): def place(self, **kw):
self.xtainer.place(**kw) self.xtainer.place(**kw)
@@ -145,10 +158,10 @@ class AutoScroll(object):
if m[0] != '_' and m not in ('config', 'configure'): if m[0] != '_' and m not in ('config', 'configure'):
setattr(self, m, getattr(master, m)) setattr(self, m, getattr(master, m))
@staticmethod @staticmethod
def _autoscroll(sbar): def _autoscroll(sbar):
'''Hide and show scrollbar as needed.''' '''Hide and show scrollbar as needed.'''
def wrapped(first, last): def wrapped(first, last):
first, last = float(first), float(last) first, last = float(first), float(last)
if first <= 0 and last >= 1: if first <= 0 and last >= 1:
@@ -156,6 +169,7 @@ class AutoScroll(object):
else: else:
sbar.grid() sbar.grid()
sbar.set(first, last) sbar.set(first, last)
return wrapped return wrapped
def __str__(self): def __str__(self):
@@ -165,12 +179,14 @@ class AutoScroll(object):
def _create_container(func): def _create_container(func):
'''Creates a tk Frame with a given master, and use this new frame to '''Creates a tk Frame with a given master, and use this new frame to
place the scrollbars and the widget.''' place the scrollbars and the widget.'''
def wrapped(cls, master, **kw): def wrapped(cls, master, **kw):
container = themedFrame(master) container = themedFrame(master)
container.bind('<Enter>', lambda e: _bound_to_mousewheel(e, container)) container.bind('<Enter>', lambda e: _bound_to_mousewheel(e, container))
container.bind( container.bind(
'<Leave>', lambda e: _unbound_to_mousewheel(e, container)) '<Leave>', lambda e: _unbound_to_mousewheel(e, container))
return func(cls, container, **kw) return func(cls, container, **kw)
return wrapped return wrapped
@@ -216,6 +232,7 @@ class ScrolledText(AutoScroll, tk.Text):
tk.Text.__init__(self, master, **kw) tk.Text.__init__(self, master, **kw)
AutoScroll.__init__(self, master) AutoScroll.__init__(self, master)
# from https://stackoverflow.com/questions/3221956/how-do-i-display-tooltips-in-tkinter # from https://stackoverflow.com/questions/3221956/how-do-i-display-tooltips-in-tkinter
@@ -223,6 +240,7 @@ class CreateToolTip(object):
''' '''
create a tooltip for a given widget create a tooltip for a given widget
''' '''
def __init__(self, widget, text='widget info'): def __init__(self, widget, text='widget info'):
self.widget = widget self.widget = widget
self.text = text self.text = text
@@ -240,10 +258,10 @@ class CreateToolTip(object):
self.tw.wm_overrideredirect(True) self.tw.wm_overrideredirect(True)
self.tw.wm_geometry("+%d+%d" % (x, y)) self.tw.wm_geometry("+%d+%d" % (x, y))
label = tk.Label(self.tw, text=self.text, justify='left', label = tk.Label(self.tw, text=self.text, justify='left',
background='gray', foreground = style.LABEL_COLOR, background='gray', foreground=style.LABEL_COLOR,
relief='solid', borderwidth=2, relief='solid', borderwidth=2,
font=("times", "12", "normal"), font=("times", "12", "normal"),
wraplength = self.widget.winfo_width()) wraplength=self.widget.winfo_width())
label.pack(ipadx=1) label.pack(ipadx=1)
def close(self, event=None): def close(self, event=None):
@@ -256,8 +274,9 @@ class threader_object:
things can be called asyncronously (you cannot start things can be called asyncronously (you cannot start
a new thread from within a tkinter callback so you a new thread from within a tkinter callback so you
must call it from an object that exists outside)""" must call it from an object that exists outside)"""
def do_async(self, func, arglist = []):
threading.Thread(target = func, args = arglist).start() def do_async(self, func, arglist=[]):
threading.Thread(target=func, args=arglist).start()
class gui(tk.Tk): class gui(tk.Tk):
@@ -267,53 +286,60 @@ class gui(tk.Tk):
self.minsize(300, 400) self.minsize(300, 400)
self.title("custom-install gui") self.title("custom-install gui")
self.f = themedFrame(self) self.f = themedFrame(self)
self.f.place(relwidth = 1, relheight = 1) self.f.place(relwidth=1, relheight=1)
outer_frame = themedFrame(self.f) outer_frame = themedFrame(self.f)
outer_frame.place(relwidth = 1, relheight = 1, x = + style.STANDARD_OFFSET, width = - 2 * style.STANDARD_OFFSET, y = + style.STANDARD_OFFSET, height = - 2 * style.STANDARD_OFFSET) outer_frame.place(relwidth=1, relheight=1, x=+ style.STANDARD_OFFSET, width=- 2 * style.STANDARD_OFFSET,
y=+ style.STANDARD_OFFSET, height=- 2 * style.STANDARD_OFFSET)
self.sd_box = LabeledPathEntry(outer_frame, "Path to SD root -", dir = True) self.sd_box = LabeledPathEntry(outer_frame, "Path to SD root -", dir=True)
self.sd_box.place(relwidth = 1, height = 20, x = 0) self.sd_box.place(relwidth=1, height=20, x=0)
CreateToolTip(self.sd_box.xtainer, "Select the root of the sd card you wish to install the cias to.") CreateToolTip(self.sd_box.xtainer, "Select the root of the sd card you wish to install the cias to.")
self.sed_box = LabeledPathEntry(outer_frame, "Path to movable.sed file -", filetypes = [('sed file', '*.sed')]) self.sed_box = LabeledPathEntry(outer_frame, "Path to movable.sed file -", filetypes=[('sed file', '*.sed')])
self.sed_box.place(relwidth = 1, height = 20, x = 0, y = 30) self.sed_box.place(relwidth=1, height=20, x=0, y=30)
CreateToolTip(self.sed_box.xtainer, "Select movable.sed file, this can be dumped from a 3ds") CreateToolTip(self.sed_box.xtainer, "Select movable.sed file, this can be dumped from a 3ds")
self.boot9_box = LabeledPathEntry(outer_frame, "Path to boot9 file -", filetypes = [('boot9 file', '*.bin')]) self.boot9_box = LabeledPathEntry(outer_frame, "Path to boot9 file -", filetypes=[('boot9 file', '*.bin')])
self.boot9_box.place(relwidth = 1, height = 20, x = 0, y = 60) self.boot9_box.place(relwidth=1, height=20, x=0, y=60)
CreateToolTip(self.boot9_box.xtainer, "Select the path to boot9.bin, this can be dumped from a 3ds") CreateToolTip(self.boot9_box.xtainer, "Select the path to boot9.bin, this can be dumped from a 3ds")
#------------------------------------------------- # -------------------------------------------------
cia_container = themedFrame(outer_frame, borderwidth = 0, highlightthickness = 0) cia_container = themedFrame(outer_frame, borderwidth=0, highlightthickness=0)
cia_container.place(y = 90, relwidth = 1, height = 115) cia_container.place(y=90, relwidth=1, height=115)
cia_label = tk.Label(cia_container, text = "cia paths - ", foreground = style.LABEL_COLOR, background = style.BACKGROUND_COLOR) cia_label = tk.Label(cia_container, text="cia paths - ", foreground=style.LABEL_COLOR,
cia_label.place(relwidth = 1, height = 20) background=style.BACKGROUND_COLOR)
self.cia_box = tk.Listbox(cia_container, highlightthickness = 0, bg = style.ENTRY_COLOR, foreground = style.ENTRY_FOREGROUND) cia_label.place(relwidth=1, height=20)
self.cia_box.place(relwidth = 1, height = 70, y = 20) self.cia_box = tk.Listbox(cia_container, highlightthickness=0, bg=style.ENTRY_COLOR,
CreateToolTip(cia_label, "Select the cias you wish to install to the sd card. The `add folder` button will add all cias in the selected folder, but will not check subdirs. The `remove cia` button will remove the currently selected file from the listbox.") foreground=style.ENTRY_FOREGROUND)
self.cia_box.place(relwidth=1, height=70, y=20)
CreateToolTip(cia_label,
"Select the cias you wish to install to the sd card. The `add folder` button will add all cias in the selected folder, but will not check subdirs. The `remove cia` button will remove the currently selected file from the listbox.")
add_cia_button = Button(cia_container, self.add_cia, text = "add cia", font = style.monospace) add_cia_button = Button(cia_container, self.add_cia, text="add cia", font=style.monospace)
add_cia_button.place(relx = 0, relwidth = 0.333, height = 20, y = 92, width = - 6) add_cia_button.place(relx=0, relwidth=0.333, height=20, y=92, width=- 6)
add_cia_folder_button = Button(cia_container, self.add_cia_folder, text = "add folder", font = style.monospace) add_cia_folder_button = Button(cia_container, self.add_cia_folder, text="add folder", font=style.monospace)
add_cia_folder_button.place(relx = 0.333, relwidth = 0.333, height = 20, y = 92, x = + 3, width = - 6) add_cia_folder_button.place(relx=0.333, relwidth=0.333, height=20, y=92, x=+ 3, width=- 6)
remove_cia_button = Button(cia_container, self.remove_cia, text = "remove cia", font = style.monospace) remove_cia_button = Button(cia_container, self.remove_cia, text="remove cia", font=style.monospace)
remove_cia_button.place(relx = 0.666, relwidth = 0.333, height = 20, y = 92, x = + 6, width = - 6) remove_cia_button.place(relx=0.666, relwidth=0.333, height=20, y=92, x=+ 6, width=- 6)
#------------------------------------------------- # -------------------------------------------------
self.skip_contents = tk.IntVar() self.skip_contents = tk.IntVar()
skip_contents_checkbutton = tk.Checkbutton(outer_frame, text="Skip contents? (only add title info)", variable=self.skip_contents, background = style.BACKGROUND_COLOR, foreground = style.LABEL_COLOR, borderwidth = 0, highlightthickness = 0) skip_contents_checkbutton = tk.Checkbutton(outer_frame, text="Skip contents? (only add title info)",
skip_contents_checkbutton.place(relwidth = 1, y = 205, height = 20) variable=self.skip_contents, background=style.BACKGROUND_COLOR,
foreground=style.LABEL_COLOR, borderwidth=0, highlightthickness=0)
skip_contents_checkbutton.place(relwidth=1, y=205, height=20)
console_label = tk.Label(outer_frame, text = "Console:", background = "black", foreground = "white", font = style.boldmonospace, borderwidth = 0, highlightthickness = 0) console_label = tk.Label(outer_frame, text="Console:", background="black", foreground="white",
console_label.place(relwidth = 1, height = 20, y = 230) font=style.boldmonospace, borderwidth=0, highlightthickness=0)
self.console = ScrolledText(outer_frame, background = "black", foreground = "white", highlightthickness = 0) console_label.place(relwidth=1, height=20, y=230)
self.console.place(relwidth = 1, relheight = 1, y = 250, height = - 272) self.console = ScrolledText(outer_frame, background="black", foreground="white", highlightthickness=0)
run_button = Button(outer_frame, self.run, text = "run", font = style.boldmonospace) self.console.place(relwidth=1, relheight=1, y=250, height=- 272)
run_button.place(relwidth = 1, rely = 1, y = - 22) run_button = Button(outer_frame, self.run, text="run", font=style.boldmonospace)
run_button.place(relwidth=1, rely=1, y=- 22)
def run(self): def run(self):
argstring = "" argstring = ""
@@ -322,7 +348,8 @@ class gui(tk.Tk):
boot9 = self.boot9_box.get() boot9 = self.boot9_box.get()
if not boot9: if not boot9:
self.output_to_console("Warning - boot9 not selected, if it's not set externally you may run into problems.\n") self.output_to_console(
"Warning - boot9 not selected, if it's not set externally you may run into problems.\n")
argstring += f"-b {boot9} " argstring += f"-b {boot9} "
sed = self.sed_box.get() sed = self.sed_box.get()
@@ -355,14 +382,15 @@ class gui(tk.Tk):
self.console.see('end') self.console.see('end')
def add_cia(self): def add_cia(self):
cia_to_add = tkfiledialog.askopenfilename(filetypes = [('cia file', '*.cia')]) cia_to_add = tkfiledialog.askopenfilename(filetypes=[('cia file', '*.cia')])
if cia_to_add: if cia_to_add:
self.cia_box.insert('end', cia_to_add) self.cia_box.insert('end', cia_to_add)
def add_cia_folder(self): def add_cia_folder(self):
cia_dir_to_add = tkfiledialog.askdirectory() cia_dir_to_add = tkfiledialog.askdirectory()
if cia_dir_to_add: if cia_dir_to_add:
cias_to_add = [f for f in os.listdir(cia_dir_to_add) if (os.path.isfile(os.path.join(cia_dir_to_add, f)) and f.endswith(".cia"))] cias_to_add = [f for f in os.listdir(cia_dir_to_add) if
(os.path.isfile(os.path.join(cia_dir_to_add, f)) and f.endswith(".cia"))]
if cias_to_add: if cias_to_add:
for cia_to_add in cias_to_add: for cia_to_add in cias_to_add:
self.cia_box.insert('end', cia_to_add) self.cia_box.insert('end', cia_to_add)
@@ -385,7 +413,7 @@ class gui(tk.Tk):
def execute_script(argstring, printer): def execute_script(argstring, printer):
"""Wrapper function to pipe install script output to a printer""" """Wrapper function to pipe install script output to a printer"""
try: try:
args = [sys.executable, '-u', os.path.join(os.path.dirname(__file__), "custom-install.py")] args = [sys.executable, '-u', os.path.join(os.path.dirname(__file__), "custominstall.py")]
for arg in argstring.split(): for arg in argstring.split():
args.append(arg.strip()) args.append(arg.strip())
p = subprocess.Popen(args, p = subprocess.Popen(args,