Move GUI to Source folder to avoid conflicts

This commit is contained in:
Mike A. Trethewey
2020-02-27 01:10:49 -08:00
parent a256dc1ae3
commit 74f3e89547
33 changed files with 41 additions and 40 deletions

1
source/__init__.py Normal file
View File

@@ -0,0 +1 @@
# do nothing, just exist to make "source" package

View File

@@ -0,0 +1,347 @@
from tkinter import filedialog, messagebox, Button, Canvas, Label, LabelFrame, Frame, PhotoImage, Scrollbar, Toplevel, ALL, NSEW, LEFT, BOTTOM, X, RIGHT, TOP, HORIZONTAL, EW, NS
from glob import glob
import json
import os
import random
import shutil
from urllib.parse import urlparse
from urllib.request import urlopen
import webbrowser
from GuiUtils import ToolTips, set_icon, BackgroundTaskProgress
from Rom import Sprite
from Utils import is_bundled, local_path, output_path, open_file
class SpriteSelector(object):
def __init__(self, parent, callback, adjuster=False):
if is_bundled():
self.deploy_icons()
self.parent = parent
self.window = Toplevel(parent)
self.window.geometry("800x650")
self.sections = []
self.callback = callback
self.adjuster = adjuster
self.window.wm_title("TAKE ANY ONE YOU WANT")
self.window['padx'] = 5
self.window['pady'] = 5
self.all_sprites = []
def open_official_sprite_listing(_evt):
webbrowser.open("http://alttpr.com/sprite_preview")
def open_unofficial_sprite_dir(_evt):
open_file(self.unofficial_sprite_dir)
def open_spritesomething_listing(_evt):
webbrowser.open("https://artheau.github.io/SpriteSomething/?mode=zelda3/link")
official_frametitle = Frame(self.window)
official_title_text = Label(official_frametitle, text="Official Sprites")
official_title_link = Label(official_frametitle, text="(open)", fg="blue", cursor="hand2")
official_title_text.pack(side=LEFT)
official_title_link.pack(side=LEFT)
official_title_link.bind("<Button-1>", open_official_sprite_listing)
unofficial_frametitle = Frame(self.window)
unofficial_title_text = Label(unofficial_frametitle, text="Unofficial Sprites")
unofficial_title_link = Label(unofficial_frametitle, text="(open)", fg="blue", cursor="hand2")
unofficial_title_text.pack(side=LEFT)
unofficial_title_link.pack(side=LEFT)
unofficial_title_link.bind("<Button-1>", open_unofficial_sprite_dir)
spritesomething_title_link = Label(unofficial_frametitle, text="(SpriteSomething)", fg="blue", cursor="hand2")
spritesomething_title_link.pack(side=LEFT)
spritesomething_title_link.bind("<Button-1>", open_spritesomething_listing)
self.icon_section(official_frametitle, self.official_sprite_dir+'/*', 'Official sprites not found. Click "Update official sprites" to download them.')
self.icon_section(unofficial_frametitle, self.unofficial_sprite_dir+'/*', 'Put sprites in the unofficial sprites folder (see open link above) to have them appear here.')
frame = Frame(self.window)
frame.pack(side=BOTTOM, fill=X, pady=5)
button = Button(frame, text="Browse for file...", command=self.browse_for_sprite)
button.pack(side=RIGHT, padx=(5, 0))
button = Button(frame, text="Update official sprites", command=self.update_official_sprites)
button.pack(side=RIGHT, padx=(5, 0))
button = Button(frame, text="Default Link sprite", command=self.use_default_link_sprite)
button.pack(side=LEFT, padx=(0, 5))
button = Button(frame, text="Random sprite", command=self.use_random_sprite)
button.pack(side=LEFT, padx=(0, 5))
if adjuster:
button = Button(frame, text="Current sprite from rom", command=self.use_default_sprite)
button.pack(side=LEFT, padx=(0, 5))
set_icon(self.window)
self.window.focus()
def icon_section(self, frame_label, path, no_results_label):
frame = LabelFrame(self.window, labelwidget=frame_label, padx=5, pady=5)
canvas = Canvas(frame, borderwidth=0)
y_scrollbar = Scrollbar(frame, orient="vertical", command=canvas.yview)
y_scrollbar.pack(side="right", fill="y")
content_frame = Frame(canvas)
canvas.pack(side="left", fill="both", expand=True)
canvas.create_window((4, 4), window=content_frame, anchor="nw")
canvas.configure(yscrollcommand=y_scrollbar.set)
def onFrameConfigure(canvas):
"""Reset the scroll region to encompass the inner frame"""
canvas.configure(scrollregion=canvas.bbox("all"))
content_frame.bind("<Configure>", lambda event, canvas=canvas: onFrameConfigure(canvas))
frame.pack(side=TOP, fill=X)
sprites = []
for file in glob(output_path(path)):
sprites.append(Sprite(file))
sprites.sort(key=lambda s: str.lower(s.name or "").strip())
i = 0
for sprite in sprites:
image = get_image_for_sprite(sprite)
if image is None:
continue
self.all_sprites.append(sprite)
button = Button(content_frame, image=image, command=lambda spr=sprite: self.select_sprite(spr))
ToolTips.register(button, sprite.name + ("\nBy: %s" % sprite.author_name if sprite.author_name else ""))
button.image = image
button.grid(row=i // 16, column=i % 16)
i += 1
if i == 0:
label = Label(content_frame, text=no_results_label)
label.pack()
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, self.adjuster)
try:
task.update_status("Downloading official sprites list")
with urlopen('https://alttpr.com/sprites') as response:
sprites_arr = json.loads(response.read().decode("utf-8"))
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 = [os.path.basename(file) for file in glob(self.local_official_sprite_dir+'/*')]
# todo: eventually use the above list to avoid downloading any sprites that we already have cached in the bundle.
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 successfully"
task.queue_event(finished)
BackgroundTaskProgress(self.parent, work, "Updating Sprites")
def browse_for_sprite(self):
sprite = filedialog.askopenfilename(
filetypes=[("All Sprite Sources", (".zspr", ".spr", ".sfc", ".smc")),
("ZSprite files", ".zspr"),
("Sprite files", ".spr"),
("Rom Files", (".sfc", ".smc")),
("All Files", "*")])
try:
self.callback(Sprite(sprite))
except Exception:
self.callback(None)
self.window.destroy()
def use_default_sprite(self):
self.callback(None, False)
self.window.destroy()
def use_default_link_sprite(self):
self.callback(Sprite.default_link_sprite(), False)
self.window.destroy()
def use_random_sprite(self):
self.callback(random.choice(self.all_sprites) if self.all_sprites else None, True)
self.window.destroy()
def select_sprite(self, spritename):
self.callback(spritename, False)
self.window.destroy()
def deploy_icons(self):
if not os.path.exists(self.unofficial_sprite_dir):
os.makedirs(self.unofficial_sprite_dir)
if not os.path.exists(self.official_sprite_dir):
shutil.copytree(self.local_official_sprite_dir, self.official_sprite_dir)
@property
def official_sprite_dir(self):
if is_bundled():
return output_path("sprites/official")
return self.local_official_sprite_dir
@property
def local_official_sprite_dir(self):
return local_path("data/sprites/official")
@property
def unofficial_sprite_dir(self):
if is_bundled():
return output_path("sprites/unofficial")
return self.local_unofficial_sprite_dir
@property
def local_unofficial_sprite_dir(self):
return local_path("data/sprites/unofficial")
def get_image_for_sprite(sprite):
if not sprite.valid:
return None
height = 24
width = 16
def draw_sprite_into_gif(add_palette_color, set_pixel_color_index):
def drawsprite(spr, pal_as_colors, offset):
for y, row in enumerate(spr):
for x, pal_index in enumerate(row):
if pal_index:
color = pal_as_colors[pal_index - 1]
set_pixel_color_index(x + offset[0], y + offset[1], color)
add_palette_color(16, (40, 40, 40))
shadow = [
[0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0],
[0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0],
[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
[0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0],
[0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0],
]
drawsprite(shadow, [16], (2, 17))
palettes = sprite.decode_palette()
for i in range(15):
add_palette_color(i + 1, palettes[0][i])
body = sprite.decode16(0x4C0)
drawsprite(body, list(range(1, 16)), (0, 8))
head = sprite.decode16(0x40)
drawsprite(head, list(range(1, 16)), (0, 0))
def make_gif(callback):
gif_header = b'GIF89a'
gif_lsd = bytearray(7)
gif_lsd[0] = width
gif_lsd[2] = height
gif_lsd[4] = 0xF4 # 32 color palette follows. transparant + 15 for sprite + 1 for shadow=17 which rounds up to 32 as nearest power of 2
gif_lsd[5] = 0 # background color is zero
gif_lsd[6] = 0 # aspect raio not specified
gif_gct = bytearray(3 * 32)
gif_gce = bytearray(8)
gif_gce[0] = 0x21 # start of extention blocked
gif_gce[1] = 0xF9 # identifies this as the Graphics Control extension
gif_gce[2] = 4 # we are suppling only the 4 four bytes
gif_gce[3] = 0x01 # this gif includes transparency
gif_gce[4] = gif_gce[5] = 0 # animation frrame delay (unused)
gif_gce[6] = 0 # transparent color is index 0
gif_gce[7] = 0 # end of gif_gce
gif_id = bytearray(10)
gif_id[0] = 0x2c
# byte 1,2 are image left. 3,4 are image top both are left as zerosuitsamus
gif_id[5] = width
gif_id[7] = height
gif_id[9] = 0 # no local color table
gif_img_minimum_code_size = bytes([7]) # we choose 7 bits, so that each pixel is represented by a byte, for conviennce.
clear = 0x80
stop = 0x81
unchunked_image_data = bytearray(height * (width + 1) + 1)
# we technically need a Clear code once every 125 bytes, but we do it at the start of every row for simplicity
for row in range(height):
unchunked_image_data[row * (width + 1)] = clear
unchunked_image_data[-1] = stop
def add_palette_color(index, color):
gif_gct[3 * index] = color[0]
gif_gct[3 * index + 1] = color[1]
gif_gct[3 * index + 2] = color[2]
def set_pixel_color_index(x, y, color):
unchunked_image_data[y * (width + 1) + x + 1] = color
callback(add_palette_color, set_pixel_color_index)
def chunk_image(img):
for i in range(0, len(img), 255):
chunk = img[i:i + 255]
yield bytes([len(chunk)])
yield chunk
gif_img = b''.join([gif_img_minimum_code_size] + list(chunk_image(unchunked_image_data)) + [b'\x00'])
gif = b''.join([gif_header, gif_lsd, gif_gct, gif_gce, gif_id, gif_img, b'\x3b'])
return gif
gif_data = make_gif(draw_sprite_into_gif)
image = PhotoImage(data=gif_data)
return image.zoom(2)

View File

@@ -0,0 +1 @@
# do nothing, just exist to make "source.classes" package

108
source/classes/constants.py Normal file
View File

@@ -0,0 +1,108 @@
CUSTOMITEMS = [
"bow", "progressivebow", "boomerang", "redmerang", "hookshot",
"mushroom", "powder", "firerod", "icerod", "bombos",
"ether", "quake", "lamp", "hammer", "shovel",
"flute", "bugnet", "book", "bottle", "somaria",
"byrna", "cape", "mirror", "boots", "powerglove",
"titansmitt", "progressiveglove", "flippers", "pearl", "heartpiece",
"heartcontainer", "sancheart", "sword1", "sword2", "sword3",
"sword4", "progressivesword", "shield1", "shield2", "shield3",
"progressiveshield", "mail2", "mail3", "progressivemail", "halfmagic",
"quartermagic", "bombsplus5", "bombsplus10", "arrowsplus5", "arrowsplus10",
"arrow1", "arrow10", "bomb1", "bomb3", "bomb10",
"rupee1", "rupee5", "rupee20", "rupee50", "rupee100",
"rupee300", "blueclock", "greenclock", "redclock", "silversupgrade",
"generickeys", "triforcepieces", "triforcepiecesgoal", "triforce", "rupoor",
"rupoorcost"
]
CANTSTARTWITH = [
"triforcepiecesgoal", "triforce", "rupoor",
"rupoorcost"
]
CUSTOMITEMLABELS = [
"Bow", "Progressive Bow", "Blue Boomerang", "Red Boomerang", "Hookshot",
"Mushroom", "Magic Powder", "Fire Rod", "Ice Rod", "Bombos",
"Ether", "Quake", "Lamp", "Hammer", "Shovel",
"Ocarina", "Bug Catching Net", "Book of Mudora", "Bottle", "Cane of Somaria",
"Cane of Byrna", "Magic Cape", "Magic Mirror", "Pegasus Boots", "Power Glove",
"Titans Mitts", "Progressive Glove", "Flippers", "Moon Pearl", "Piece of Heart",
"Boss Heart Container", "Sanctuary Heart Container", "Fighter Sword", "Master Sword", "Tempered Sword",
"Golden Sword", "Progressive Sword", "Blue Shield", "Red Shield", "Mirror Shield",
"Progressive Shield", "Blue Mail", "Red Mail", "Progressive Armor", "Magic Upgrade (1/2)",
"Magic Upgrade (1/4)", "Bomb Upgrade (+5)", "Bomb Upgrade (+10)", "Arrow Upgrade (+5)", "Arrow Upgrade (+10)",
"Single Arrow", "Arrows (10)", "Single Bomb", "Bombs (3)", "Bombs (10)",
"Rupee (1)", "Rupees (5)", "Rupees (20)", "Rupees (50)", "Rupees (100)",
"Rupees (300)", "Blue Clock", "Green Clock", "Red Clock", "Silver Arrows",
"Small Key (Universal)", "Triforce Piece", "Triforce Piece Goal", "Triforce", "Rupoor",
"Rupoor Cost"
]
SETTINGSTOPROCESS = {
"randomizer": {
"item": {
"retro": "retro",
"worldstate": "mode",
"logiclevel": "logic",
"goal": "goal",
"crystals_gt": "crystals_gt",
"crystals_ganon": "crystals_ganon",
"weapons": "swords",
"itempool": "difficulty",
"itemfunction": "item_functionality",
"timer": "timer",
"progressives": "progressive",
"accessibility": "accessibility",
"sortingalgo": "algorithm"
},
"entrance": {
"openpyramid": "openpyramid",
"shuffleganon": "shuffleganon",
"entranceshuffle": "shuffle"
},
"enemizer": {
"potshuffle": "shufflepots",
"enemyshuffle": "shuffleenemies",
"bossshuffle": "shufflebosses",
"enemydamage": "enemy_damage",
"enemyhealth": "enemy_health"
},
"dungeon": {
"mapshuffle": "mapshuffle",
"compassshuffle": "compassshuffle",
"smallkeyshuffle": "keyshuffle",
"bigkeyshuffle": "bigkeyshuffle",
"dungeondoorshuffle": "door_shuffle",
"experimental": "experimental",
"dungeon_counters": "dungeon_counters"
},
"multiworld": {
"names": "names"
},
"gameoptions": {
"hints": "hints",
"nobgm": "disablemusic",
"quickswap": "quickswap",
"heartcolor": "heartcolor",
"heartbeep": "heartbeep",
"menuspeed": "fastmenu",
"owpalettes": "ow_palettes",
"uwpalettes": "uw_palettes"
},
"generation": {
"spoiler": "create_spoiler",
"suppressrom": "suppress_rom",
"usestartinventory": "usestartinventory",
"usecustompool": "custom"
}
}
}

1
source/gui/__init__.py Normal file
View File

@@ -0,0 +1 @@
# do nothing, just exist to make "source.gui" package

View File

@@ -0,0 +1 @@
# do nothing, just exist to make "source.gui.about" package

View File

@@ -0,0 +1 @@
# do nothing, just exist to make "source.gui.adjust" package

View File

@@ -0,0 +1,113 @@
from tkinter import ttk, filedialog, messagebox, IntVar, StringVar, Button, Checkbutton, Entry, Frame, Label, OptionMenu, E, W, LEFT, RIGHT, X, BOTTOM
from AdjusterMain import adjust
from argparse import Namespace
from source.classes.SpriteSelector import SpriteSelector
import source.gui.widgets as widgets
import json
import logging
import os
def adjust_page(top, parent, settings):
# Adjust page
self = ttk.Frame(parent)
# Adjust options
self.widgets = {}
# Adjust option sections
self.frames = {}
self.frames["checkboxes"] = Frame(self)
self.frames["checkboxes"].pack(anchor=W)
self.frames["selectOptionsFrame"] = Frame(self)
self.frames["leftAdjustFrame"] = Frame(self.frames["selectOptionsFrame"])
self.frames["rightAdjustFrame"] = Frame(self.frames["selectOptionsFrame"])
self.frames["bottomAdjustFrame"] = Frame(self)
self.frames["selectOptionsFrame"].pack(fill=X)
self.frames["leftAdjustFrame"].pack(side=LEFT)
self.frames["rightAdjustFrame"].pack(side=RIGHT)
self.frames["bottomAdjustFrame"].pack(fill=X)
with open(os.path.join("resources","app","gui","adjust","overview","widgets.json")) as widgetDefns:
myDict = json.load(widgetDefns)
for framename,theseWidgets in myDict.items():
dictWidgets = widgets.make_widgets_from_dict(self, theseWidgets, self.frames[framename])
for key in dictWidgets:
self.widgets[key] = dictWidgets[key]
packAttrs = {"anchor":E}
if self.widgets[key].type == "checkbox":
packAttrs["anchor"] = W
self.widgets[key].pack(packAttrs)
# Sprite Selection
self.spriteNameVar2 = StringVar()
spriteDialogFrame2 = Frame(self.frames["leftAdjustFrame"])
baseSpriteLabel2 = Label(spriteDialogFrame2, text='Sprite:')
spriteEntry2 = Label(spriteDialogFrame2, textvariable=self.spriteNameVar2)
self.sprite = None
def set_sprite(sprite_param, random_sprite=False):
if sprite_param is None or not sprite_param.valid:
self.sprite = None
self.spriteNameVar2.set('(unchanged)')
else:
self.sprite = sprite_param
self.spriteNameVar2.set(self.sprite.name)
top.randomSprite.set(random_sprite)
def SpriteSelectAdjuster():
SpriteSelector(parent, set_sprite, adjuster=True)
spriteSelectButton2 = Button(spriteDialogFrame2, text='...', command=SpriteSelectAdjuster)
baseSpriteLabel2.pack(side=LEFT)
spriteEntry2.pack(side=LEFT)
spriteSelectButton2.pack(side=LEFT)
spriteDialogFrame2.pack(anchor=E)
adjustRomFrame = Frame(self.frames["bottomAdjustFrame"])
adjustRomLabel = Label(adjustRomFrame, text='Rom to adjust: ')
self.romVar2 = StringVar(value=settings["rom"])
romEntry2 = Entry(adjustRomFrame, textvariable=self.romVar2)
def RomSelect2():
rom = filedialog.askopenfilename(filetypes=[("Rom Files", (".sfc", ".smc")), ("All Files", "*")])
if rom:
settings["rom"] = rom
self.romVar2.set(rom)
romSelectButton2 = Button(adjustRomFrame, text='Select Rom', command=RomSelect2)
adjustRomLabel.pack(side=LEFT)
romEntry2.pack(side=LEFT, fill=X, expand=True)
romSelectButton2.pack(side=LEFT)
adjustRomFrame.pack(fill=X)
def adjustRom():
options = {
"heartbeep": "heartbeep",
"heartcolor": "heartcolor",
"menuspeed": "fastmenu",
"owpalettes": "ow_palettes",
"uwpalettes": "uw_palettes",
"quickswap": "quickswap",
"nobgm": "disablemusic"
}
guiargs = Namespace()
for option in options:
arg = options[option]
setattr(guiargs, arg, self.widgets[option].storageVar.get())
guiargs.rom = self.romVar2.get()
guiargs.baserom = top.pages["randomizer"].pages["generation"].romVar.get()
guiargs.sprite = self.sprite
try:
adjust(args=guiargs)
except Exception as e:
logging.exception(e)
messagebox.showerror(title="Error while creating seed", message=str(e))
else:
messagebox.showinfo(title="Success", message="Rom patched successfully")
adjustButton = Button(self.frames["bottomAdjustFrame"], text='Adjust Rom', command=adjustRom)
adjustButton.pack(side=BOTTOM, padx=(5, 0))
return self,settings

147
source/gui/bottom.py Normal file
View File

@@ -0,0 +1,147 @@
from tkinter import ttk, messagebox, StringVar, Button, Entry, Frame, Label, Spinbox, E, W, LEFT, RIGHT, X
from argparse import Namespace
from functools import partial
import logging
import os
import random
from CLI import parse_arguments, get_settings
from Main import main
from Utils import local_path, output_path, open_file
import source.classes.constants as CONST
import source.gui.widgets as widgets
def bottom_frame(self, parent, args=None):
# Bottom Frame
self = ttk.Frame(parent)
# Bottom Frame options
self.widgets = {}
seedCountFrame = Frame(self)
seedCountFrame.pack()
## Seed #
seedLabel = Label(self, text='Seed #')
savedSeed = parent.settings["seed"]
self.seedVar = StringVar(value=savedSeed)
def saveSeed(caller,_,mode):
savedSeed = self.seedVar.get()
parent.settings["seed"] = int(savedSeed) if savedSeed.isdigit() else None
self.seedVar.trace_add("write",saveSeed)
seedEntry = Entry(self, width=15, textvariable=self.seedVar)
seedLabel.pack(side=LEFT)
seedEntry.pack(side=LEFT)
## Number of Generation attempts
key = "generationcount"
self.widgets[key] = widgets.make_widget(
self,
"spinbox",
self,
"Count",
None,
None,
{"label": {"side": LEFT}, "spinbox": {"side": RIGHT}}
)
self.widgets[key].pack(side=LEFT)
def generateRom():
guiargs = create_guiargs(parent)
# get default values for missing parameters
for k,v in vars(parse_arguments(['--multi', str(guiargs.multi)])).items():
if k not in vars(guiargs):
setattr(guiargs, k, v)
elif type(v) is dict: # use same settings for every player
setattr(guiargs, k, {player: getattr(guiargs, k) for player in range(1, guiargs.multi + 1)})
try:
if guiargs.count is not None:
seed = guiargs.seed
for _ in range(guiargs.count):
main(seed=seed, args=guiargs)
seed = random.randint(0, 999999999)
else:
main(seed=guiargs.seed, args=guiargs)
except Exception as e:
logging.exception(e)
messagebox.showerror(title="Error while creating seed", message=str(e))
else:
messagebox.showinfo(title="Success", message="Rom patched successfully")
## Generate Button
generateButton = Button(self, text='Generate Patched Rom', command=generateRom)
generateButton.pack(side=LEFT)
def open_output():
if args and args.outputpath:
open_file(output_path(args.outputpath))
else:
open_file(output_path(parent.settings["outputpath"]))
openOutputButton = Button(self, text='Open Output Directory', command=open_output)
openOutputButton.pack(side=RIGHT)
## Documentation Button
if os.path.exists(local_path('README.html')):
def open_readme():
open_file(local_path('README.html'))
openReadmeButton = Button(self, text='Open Documentation', command=open_readme)
openReadmeButton.pack(side=RIGHT)
return self
def create_guiargs(parent):
guiargs = Namespace()
# set up settings to gather
# Page::Subpage::GUI-id::param-id
options = CONST.SETTINGSTOPROCESS
for mainpage in options:
for subpage in options[mainpage]:
for widget in options[mainpage][subpage]:
arg = options[mainpage][subpage][widget]
setattr(guiargs, arg, parent.pages[mainpage].pages[subpage].widgets[widget].storageVar.get())
guiargs.enemizercli = parent.pages["randomizer"].pages["enemizer"].enemizerCLIpathVar.get()
guiargs.multi = int(parent.pages["randomizer"].pages["multiworld"].widgets["worlds"].storageVar.get())
guiargs.rom = parent.pages["randomizer"].pages["generation"].romVar.get()
guiargs.custom = bool(parent.pages["randomizer"].pages["generation"].widgets["usecustompool"].storageVar.get())
guiargs.seed = int(parent.frames["bottom"].seedVar.get()) if parent.frames["bottom"].seedVar.get() else None
guiargs.count = int(parent.frames["bottom"].widgets["generationcount"].storageVar.get()) if parent.frames["bottom"].widgets["generationcount"].storageVar.get() != '1' else None
adjustargs = {
"nobgm": "disablemusic",
"quickswap": "quickswap",
"heartcolor": "heartcolor",
"heartbeep": "heartbeep",
"menuspeed": "fastmenu",
"owpalettes": "ow_palettes",
"uwpalettes": "uw_palettes"
}
for adjustarg in adjustargs:
internal = adjustargs[adjustarg]
setattr(guiargs,"adjust." + internal, parent.pages["adjust"].content.widgets[adjustarg].storageVar.get())
customitems = CONST.CUSTOMITEMS
guiargs.startinventory = []
guiargs.customitemarray = {}
guiargs.startinventoryarray = {}
for customitem in customitems:
if customitem not in ["triforcepiecesgoal", "triforce", "rupoor", "rupoorcost"]:
amount = int(parent.pages["startinventory"].content.startingWidgets[customitem].storageVar.get())
guiargs.startinventoryarray[customitem] = amount
for i in range(0, amount):
label = CONST.CUSTOMITEMLABELS[customitems.index(customitem)]
guiargs.startinventory.append(label)
guiargs.customitemarray[customitem] = int(parent.pages["custom"].content.customWidgets[customitem].storageVar.get())
guiargs.startinventory = ','.join(guiargs.startinventory)
guiargs.sprite = parent.pages["randomizer"].pages["gameoptions"].widgets["sprite"]["spriteObject"]
guiargs.randomSprite = parent.randomSprite.get()
guiargs.outputpath = parent.outputPath.get()
return guiargs

View File

@@ -0,0 +1 @@
# do nothing, just exist to make "source.gui.custom" package

View File

@@ -0,0 +1,54 @@
from tkinter import ttk, StringVar, Entry, Frame, Label, N, E, W, LEFT, RIGHT, X, VERTICAL, Y
import source.gui.widgets as widgets
import json
import os
import source.classes.constants as CONST
def custom_page(top,parent):
# Custom Item Pool
self = ttk.Frame(parent)
def create_list_frame(parent, framename):
parent.frames[framename] = Frame(parent)
parent.frames[framename].pack(side=LEFT, padx=(0,0), anchor=N)
parent.frames[framename].thisRow = 0
parent.frames[framename].thisCol = 0
def create_vertical_rule(num=1):
for i in range(0,num):
ttk.Separator(self, orient=VERTICAL).pack(side=LEFT, anchor=N, fill=Y)
def validation(P):
if str.isdigit(P) or P == "":
return True
else:
return False
vcmd=(self.register(validation), '%P')
# Custom Item Pool options
self.customWidgets = {}
# Custom Item Pool option sections
self.frames = {}
create_list_frame(self,"itemList1")
create_vertical_rule(2)
create_list_frame(self,"itemList2")
create_vertical_rule(2)
create_list_frame(self,"itemList3")
create_vertical_rule(2)
create_list_frame(self,"itemList4")
create_vertical_rule(2)
create_list_frame(self,"itemList5")
with open(os.path.join("resources","app","gui","custom","overview","widgets.json")) as widgetDefns:
myDict = json.load(widgetDefns)
for framename,theseWidgets in myDict.items():
dictWidgets = widgets.make_widgets_from_dict(self, theseWidgets, self.frames[framename])
for key in dictWidgets:
self.customWidgets[key] = dictWidgets[key]
for key in CONST.CUSTOMITEMS:
self.customWidgets[key].storageVar.set(top.settings["customitemarray"][key])
return self

76
source/gui/loadcliargs.py Normal file
View File

@@ -0,0 +1,76 @@
from source.classes.SpriteSelector import SpriteSelector as spriteSelector
from source.gui.randomize.gameoptions import set_sprite
from Rom import Sprite
import source.classes.constants as CONST
def loadcliargs(gui, args, settings=None):
if args is not None:
# for k, v in vars(args).items():
# if type(v) is dict:
# setattr(args, k, v[1]) # only get values for player 1 for now
# load values from commandline args
# set up options to get
# Page::Subpage::GUI-id::param-id
options = CONST.SETTINGSTOPROCESS
for mainpage in options:
for subpage in options[mainpage]:
for widget in options[mainpage][subpage]:
arg = options[mainpage][subpage][widget]
gui.pages[mainpage].pages[subpage].widgets[widget].storageVar.set(args[arg])
if subpage == "gameoptions" and not widget == "hints":
hasSettings = settings is not None
hasWidget = ("adjust." + widget) in settings if hasSettings else None
if hasWidget is None:
gui.pages["adjust"].content.widgets[widget].storageVar.set(args[arg])
gui.pages["randomizer"].pages["enemizer"].enemizerCLIpathVar.set(args["enemizercli"])
gui.pages["randomizer"].pages["generation"].romVar.set(args["rom"])
if args["multi"]:
gui.pages["randomizer"].pages["multiworld"].widgets["worlds"].storageVar.set(str(args["multi"]))
if args["seed"]:
gui.frames["bottom"].seedVar.set(str(args["seed"]))
if args["count"]:
gui.frames["bottom"].widgets["generationcount"].storageVar.set(str(args["count"]))
gui.outputPath.set(args["outputpath"])
def sprite_setter(spriteObject):
gui.pages["randomizer"].pages["gameoptions"].widgets["sprite"]["spriteObject"] = spriteObject
if args["sprite"] is not None:
sprite_obj = args.sprite if isinstance(args["sprite"], Sprite) else Sprite(args["sprite"])
r_sprite_flag = args.randomSprite if hasattr(args, 'randomSprite') else False
set_sprite(sprite_obj, r_sprite_flag, spriteSetter=sprite_setter,
spriteNameVar=gui.pages["randomizer"].pages["gameoptions"].widgets["sprite"]["spriteNameVar"],
randomSpriteVar=gui.randomSprite)
def sprite_setter_adj(spriteObject):
gui.pages["adjust"].content.sprite = spriteObject
if args["sprite"] is not None:
sprite_obj = args.sprite if isinstance(args.sprite, Sprite) else Sprite(args.sprite)
r_sprite_flag = args["randomSprite"] if hasattr(args, 'randomSprite') else False
set_sprite(sprite_obj, r_sprite_flag, spriteSetter=sprite_setter_adj,
spriteNameVar=gui.pages["adjust"].content.spriteNameVar2,
randomSpriteVar=gui.randomSprite)
def loadadjustargs(gui, settings):
options = {
"adjust": {
"content": {
"nobgm": "adjust.nobgm",
"quickswap": "adjust.quickswap",
"heartcolor": "adjust.heartcolor",
"heartbeep": "adjust.heartbeep",
"menuspeed": "adjust.menuspeed",
"owpalettes": "adjust.owpalettes",
"uwpalettes": "adjust.uwpalettes"
}
}
}
for mainpage in options:
for subpage in options[mainpage]:
for widget in options[mainpage][subpage]:
key = options[mainpage][subpage][widget]
if key in settings:
gui.pages[mainpage].content.widgets[widget].storageVar.set(settings[key])

View File

@@ -0,0 +1 @@
# do nothing, just exist to make "source.gui.randomize" package

View File

@@ -0,0 +1,38 @@
from tkinter import ttk, IntVar, StringVar, Checkbutton, Frame, Label, OptionMenu, E, W, LEFT, RIGHT
import source.gui.widgets as widgets
import json
import os
def dungeon_page(parent):
# Dungeon Shuffle
self = ttk.Frame(parent)
# Dungeon Shuffle options
self.widgets = {}
# Dungeon Shuffle option sections
self.frames = {}
self.frames["keysanity"] = Frame(self)
self.frames["keysanity"].pack(anchor=W)
## Dungeon Item Shuffle
mscbLabel = Label(self.frames["keysanity"], text="Shuffle: ")
mscbLabel.pack(side=LEFT)
with open(os.path.join("resources","app","gui","randomize","dungeon","keysanity.json")) as keysanityItems:
myDict = json.load(keysanityItems)
dictWidgets = widgets.make_widgets_from_dict(self, myDict, self.frames["keysanity"])
for key in dictWidgets:
self.widgets[key] = dictWidgets[key]
self.widgets[key].pack(side=LEFT)
self.frames["widgets"] = Frame(self)
self.frames["widgets"].pack(anchor=W)
with open(os.path.join("resources","app","gui","randomize","dungeon","widgets.json")) as dungeonWidgets:
myDict = json.load(dungeonWidgets)
dictWidgets = widgets.make_widgets_from_dict(self, myDict, self.frames["widgets"])
for key in dictWidgets:
self.widgets[key] = dictWidgets[key]
self.widgets[key].pack(anchor=W)
return self

View File

@@ -0,0 +1,63 @@
import os
from tkinter import ttk, filedialog, IntVar, StringVar, Button, Checkbutton, Entry, Frame, Label, LabelFrame, OptionMenu, N, E, W, LEFT, RIGHT, BOTTOM, X
import source.gui.widgets as widgets
import json
import os
import webbrowser
def enemizer_page(parent,settings):
def open_enemizer_download(_evt):
webbrowser.open("https://github.com/Bonta0/Enemizer/releases")
# Enemizer
self = ttk.Frame(parent)
# Enemizer options
self.widgets = {}
# Enemizer option sections
self.frames = {}
self.frames["checkboxes"] = Frame(self)
self.frames["checkboxes"].pack(anchor=W)
self.frames["selectOptionsFrame"] = Frame(self)
self.frames["leftEnemizerFrame"] = Frame(self.frames["selectOptionsFrame"])
self.frames["rightEnemizerFrame"] = Frame(self.frames["selectOptionsFrame"])
self.frames["bottomEnemizerFrame"] = Frame(self)
self.frames["selectOptionsFrame"].pack(fill=X)
self.frames["leftEnemizerFrame"].pack(side=LEFT)
self.frames["rightEnemizerFrame"].pack(side=RIGHT)
self.frames["bottomEnemizerFrame"].pack(fill=X)
with open(os.path.join("resources","app","gui","randomize","enemizer","widgets.json")) as widgetDefns:
myDict = json.load(widgetDefns)
for framename,theseWidgets in myDict.items():
dictWidgets = widgets.make_widgets_from_dict(self, theseWidgets, self.frames[framename])
for key in dictWidgets:
self.widgets[key] = dictWidgets[key]
packAttrs = {"anchor":E}
if self.widgets[key].type == "checkbox":
packAttrs["anchor"] = W
self.widgets[key].pack(packAttrs)
## Enemizer CLI Path
enemizerPathFrame = Frame(self.frames["bottomEnemizerFrame"])
enemizerCLIlabel = Label(enemizerPathFrame, text="EnemizerCLI path: ")
enemizerCLIlabel.pack(side=LEFT)
enemizerURL = Label(enemizerPathFrame, text="(get online)", fg="blue", cursor="hand2")
enemizerURL.pack(side=LEFT)
enemizerURL.bind("<Button-1>", open_enemizer_download)
self.enemizerCLIpathVar = StringVar(value=settings["enemizercli"])
enemizerCLIpathEntry = Entry(enemizerPathFrame, textvariable=self.enemizerCLIpathVar)
enemizerCLIpathEntry.pack(side=LEFT, fill=X, expand=True)
def EnemizerSelectPath():
path = filedialog.askopenfilename(filetypes=[("EnemizerCLI executable", "*EnemizerCLI*")], initialdir=os.path.join("."))
if path:
self.enemizerCLIpathVar.set(path)
settings["enemizercli"] = path
enemizerCLIbrowseButton = Button(enemizerPathFrame, text='...', command=EnemizerSelectPath)
enemizerCLIbrowseButton.pack(side=LEFT)
enemizerPathFrame.pack(fill=X)
return self,settings

View File

@@ -0,0 +1,29 @@
from tkinter import ttk, IntVar, StringVar, Checkbutton, Frame, Label, OptionMenu, E, W, LEFT, RIGHT
import source.gui.widgets as widgets
import json
import os
def entrando_page(parent):
# Entrance Randomizer
self = ttk.Frame(parent)
# Entrance Randomizer options
self.widgets = {}
# Entrance Randomizer option sections
self.frames = {}
self.frames["widgets"] = Frame(self)
self.frames["widgets"].pack(anchor=W)
with open(os.path.join("resources","app","gui","randomize","entrando","widgets.json")) as widgetDefns:
myDict = json.load(widgetDefns)
for framename,theseWidgets in myDict.items():
dictWidgets = widgets.make_widgets_from_dict(self, theseWidgets, self.frames[framename])
for key in dictWidgets:
self.widgets[key] = dictWidgets[key]
packAttrs = {"anchor":E}
if self.widgets[key].type == "checkbox":
packAttrs["anchor"] = W
self.widgets[key].pack(packAttrs)
return self

View File

@@ -0,0 +1,78 @@
from tkinter import ttk, IntVar, StringVar, Button, Checkbutton, Entry, Frame, Label, OptionMenu, E, W, LEFT, RIGHT
from functools import partial
import source.classes.SpriteSelector as spriteSelector
import source.gui.widgets as widgets
import json
import os
def gameoptions_page(top, parent):
# Game Options
self = ttk.Frame(parent)
# Game Options options
self.widgets = {}
# Game Options option sections
self.frames = {}
self.frames["checkboxes"] = Frame(self)
self.frames["checkboxes"].pack(anchor=W)
self.frames["leftRomOptionsFrame"] = Frame(self)
self.frames["rightRomOptionsFrame"] = Frame(self)
self.frames["leftRomOptionsFrame"].pack(side=LEFT)
self.frames["rightRomOptionsFrame"].pack(side=RIGHT)
with open(os.path.join("resources","app","gui","randomize","gameoptions","widgets.json")) as widgetDefns:
myDict = json.load(widgetDefns)
for framename,theseWidgets in myDict.items():
dictWidgets = widgets.make_widgets_from_dict(self, theseWidgets, self.frames[framename])
for key in dictWidgets:
self.widgets[key] = dictWidgets[key]
packAttrs = {"anchor":E}
if self.widgets[key].type == "checkbox":
packAttrs["anchor"] = W
self.widgets[key].pack(packAttrs)
## Sprite selection
spriteDialogFrame = Frame(self.frames["leftRomOptionsFrame"])
baseSpriteLabel = Label(spriteDialogFrame, text='Sprite:')
self.widgets["sprite"] = {}
self.widgets["sprite"]["spriteObject"] = None
self.widgets["sprite"]["spriteNameVar"] = StringVar()
self.widgets["sprite"]["spriteNameVar"].set('(unchanged)')
spriteEntry = Label(spriteDialogFrame, textvariable=self.widgets["sprite"]["spriteNameVar"])
def sprite_setter(spriteObject):
self.widgets["sprite"]["spriteObject"] = spriteObject
def sprite_select():
spriteSelector.SpriteSelector(parent, partial(set_sprite, spriteSetter=sprite_setter,
spriteNameVar=self.widgets["sprite"]["spriteNameVar"],
randomSpriteVar=top.randomSprite))
spriteSelectButton = Button(spriteDialogFrame, text='...', command=sprite_select)
baseSpriteLabel.pack(side=LEFT)
spriteEntry.pack(side=LEFT)
spriteSelectButton.pack(side=LEFT)
spriteDialogFrame.pack(anchor=E)
return self
def set_sprite(sprite_param, random_sprite=False, spriteSetter=None, spriteNameVar=None, randomSpriteVar=None):
if sprite_param is None or not sprite_param.valid:
if spriteSetter:
spriteSetter(None)
if spriteNameVar is not None:
spriteNameVar.set('(unchanged)')
else:
if spriteSetter:
spriteSetter(sprite_param)
if spriteNameVar is not None:
spriteNameVar.set(sprite_param.name)
if randomSpriteVar:
randomSpriteVar.set(random_sprite)

View File

@@ -0,0 +1,45 @@
import os
from tkinter import ttk, filedialog, IntVar, StringVar, Button, Checkbutton, Entry, Frame, Label, E, W, LEFT, RIGHT, X
import source.gui.widgets as widgets
import json
import os
def generation_page(parent,settings):
# Generation Setup
self = ttk.Frame(parent)
# Generation Setup options
self.widgets = {}
# Generation Setup option sections
self.frames = {}
self.frames["checkboxes"] = Frame(self)
self.frames["checkboxes"].pack(anchor=W)
with open(os.path.join("resources","app","gui","randomize","generation","checkboxes.json")) as checkboxes:
myDict = json.load(checkboxes)
dictWidgets = widgets.make_widgets_from_dict(self, myDict, self.frames["checkboxes"])
for key in dictWidgets:
self.widgets[key] = dictWidgets[key]
self.widgets[key].pack(anchor=W)
self.frames["baserom"] = Frame(self)
self.frames["baserom"].pack(anchor=W, fill=X)
## Locate base ROM
baseRomFrame = Frame(self.frames["baserom"])
baseRomLabel = Label(baseRomFrame, text='Base Rom: ')
self.romVar = StringVar()
romEntry = Entry(baseRomFrame, textvariable=self.romVar)
self.romVar.set(settings["rom"])
def RomSelect():
rom = filedialog.askopenfilename(filetypes=[("Rom Files", (".sfc", ".smc")), ("All Files", "*")], initialdir=os.path.join("."))
self.romVar.set(rom)
romSelectButton = Button(baseRomFrame, text='Select Rom', command=RomSelect)
baseRomLabel.pack(side=LEFT)
romEntry.pack(side=LEFT, fill=X, expand=True)
romSelectButton.pack(side=LEFT)
baseRomFrame.pack(fill=X)
return self,settings

View File

@@ -0,0 +1,35 @@
from tkinter import ttk, IntVar, StringVar, Checkbutton, Frame, Label, OptionMenu, E, W, LEFT, RIGHT
import source.gui.widgets as widgets
import json
import os
def item_page(parent):
# Item Randomizer
self = ttk.Frame(parent)
# Item Randomizer options
self.widgets = {}
# Item Randomizer option sections
self.frames = {}
self.frames["checkboxes"] = Frame(self)
self.frames["checkboxes"].pack(anchor=W)
self.frames["leftItemFrame"] = Frame(self)
self.frames["rightItemFrame"] = Frame(self)
self.frames["leftItemFrame"].pack(side=LEFT)
self.frames["rightItemFrame"].pack(side=RIGHT)
with open(os.path.join("resources","app","gui","randomize","item","widgets.json")) as widgetDefns:
myDict = json.load(widgetDefns)
for framename,theseWidgets in myDict.items():
dictWidgets = widgets.make_widgets_from_dict(self, theseWidgets, self.frames[framename])
for key in dictWidgets:
self.widgets[key] = dictWidgets[key]
packAttrs = {"anchor":E}
if self.widgets[key].type == "checkbox":
packAttrs["anchor"] = W
self.widgets[key].pack(packAttrs)
return self

View File

@@ -0,0 +1,38 @@
from tkinter import ttk, StringVar, Entry, Frame, Label, Spinbox, N, E, W, X, LEFT, RIGHT
import source.gui.widgets as widgets
import json
import os
def multiworld_page(parent,settings):
# Multiworld
self = ttk.Frame(parent)
# Multiworld options
self.widgets = {}
# Multiworld option sections
self.frames = {}
self.frames["widgets"] = Frame(self)
self.frames["widgets"].pack(anchor=W, fill=X)
with open(os.path.join("resources","app","gui","randomize","multiworld","widgets.json")) as multiworldItems:
myDict = json.load(multiworldItems)
dictWidgets = widgets.make_widgets_from_dict(self, myDict, self.frames["widgets"])
for key in dictWidgets:
self.widgets[key] = dictWidgets[key]
self.widgets[key].pack(side=LEFT, anchor=N)
## List of Player Names
key = "names"
self.widgets[key] = Frame(self.frames["widgets"])
self.widgets[key].label = Label(self.widgets[key], text='Player names')
self.widgets[key].storageVar = StringVar(value=settings["names"])
def saveMultiNames(caller,_,mode):
settings["names"] = self.widgets[key].storageVar.get()
self.widgets[key].storageVar.trace_add("write",saveMultiNames)
self.widgets[key].textbox = Entry(self.widgets[key], textvariable=self.widgets[key].storageVar)
self.widgets[key].label.pack(side=LEFT)
self.widgets[key].textbox.pack(side=LEFT, fill=X, expand=True)
self.widgets[key].pack(anchor=N, fill=X, expand=True)
return self,settings

View File

@@ -0,0 +1 @@
# do nothing, just exist to make "source.gui.startinventory" package

View File

@@ -0,0 +1,63 @@
from tkinter import ttk, StringVar, Entry, Frame, Label, N, E, W, LEFT, RIGHT, X, VERTICAL, Y
import source.gui.widgets as widgets
import json
import os
import source.classes.constants as CONST
def startinventory_page(top,parent):
# Starting Inventory
self = ttk.Frame(parent)
def create_list_frame(parent, framename):
parent.frames[framename] = Frame(parent)
parent.frames[framename].pack(side=LEFT, padx=(0,0), anchor=N)
parent.frames[framename].thisRow = 0
parent.frames[framename].thisCol = 0
def create_vertical_rule(num=1):
for i in range(0,num):
ttk.Separator(self, orient=VERTICAL).pack(side=LEFT, anchor=N, fill=Y)
def validation(P):
if str.isdigit(P) or P == "":
return True
else:
return False
vcmd=(self.register(validation), '%P')
# Starting Inventory options
self.startingWidgets = {}
# Starting Inventory option sections
self.frames = {}
create_list_frame(self,"itemList1")
create_vertical_rule(2)
create_list_frame(self,"itemList2")
create_vertical_rule(2)
create_list_frame(self,"itemList3")
create_vertical_rule(2)
create_list_frame(self,"itemList4")
create_vertical_rule(2)
create_list_frame(self,"itemList5")
with open(os.path.join("resources","app","gui","custom","overview","widgets.json")) as widgetDefns:
myDict = json.load(widgetDefns)
for key in CONST.CANTSTARTWITH:
for num in range(1, 5 + 1):
thisList = "itemList" + str(num)
if key in myDict[thisList]:
del myDict[thisList][key]
for framename,theseWidgets in myDict.items():
dictWidgets = widgets.make_widgets_from_dict(self, theseWidgets, self.frames[framename])
for key in dictWidgets:
self.startingWidgets[key] = dictWidgets[key]
for key in CONST.CUSTOMITEMS:
if key not in CONST.CANTSTARTWITH:
val = 0
if key in top.settings["startinventoryarray"]:
val = top.settings["startinventoryarray"][key]
self.startingWidgets[key].storageVar.set(val)
return self

145
source/gui/widgets.py Normal file
View File

@@ -0,0 +1,145 @@
from tkinter import Checkbutton, Entry, Frame, IntVar, Label, OptionMenu, Spinbox, StringVar, RIGHT, X
class Empty():
pass
class mySpinbox(Spinbox):
def __init__(self, *args, **kwargs):
Spinbox.__init__(self, *args, **kwargs)
self.bind('<MouseWheel>', self.mouseWheel)
self.bind('<Button-4>', self.mouseWheel)
self.bind('<Button-5>', self.mouseWheel)
def mouseWheel(self, event):
if event.num == 5 or event.delta == -120:
self.invoke('buttondown')
elif event.num == 4 or event.delta == 120:
self.invoke('buttonup')
def make_checkbox(self, parent, label, storageVar, manager, managerAttrs):
self = Frame(parent, name="checkframe-" + label.lower())
self.storageVar = storageVar
self.checkbox = Checkbutton(self, text=label, variable=self.storageVar, name="checkbox-" + label.lower())
if managerAttrs is not None:
self.checkbox.pack(managerAttrs)
else:
self.checkbox.pack()
return self
def make_selectbox(self, parent, label, options, storageVar, manager, managerAttrs):
def change_storage(*args):
self.storageVar.set(options[self.labelVar.get()])
def change_selected(*args):
keys = options.keys()
vals = options.values()
keysList = list(keys)
valsList = list(vals)
self.labelVar.set(keysList[valsList.index(str(self.storageVar.get()))])
self = Frame(parent, name="selectframe-" + label.lower())
self.storageVar = storageVar
self.storageVar.trace_add("write",change_selected)
self.labelVar = StringVar()
self.labelVar.trace_add("write",change_storage)
self.label = Label(self, text=label)
if managerAttrs is not None and "label" in managerAttrs:
self.label.pack(managerAttrs["label"])
else:
self.label.pack()
self.selectbox = OptionMenu(self, self.labelVar, *options.keys())
self.selectbox.config(width=20)
self.labelVar.set(managerAttrs["default"] if "default" in managerAttrs else list(options.keys())[0])
if managerAttrs is not None and "selectbox" in managerAttrs:
self.selectbox.pack(managerAttrs["selectbox"])
else:
self.selectbox.pack()
return self
def make_spinbox(self, parent, label, storageVar, manager, managerAttrs):
self = Frame(parent, name="spinframe-" + label.lower())
self.storageVar = storageVar
self.label = Label(self, text=label)
if managerAttrs is not None and "label" in managerAttrs:
self.label.pack(managerAttrs["label"])
else:
self.label.pack()
fromNum = 1
toNum = 100
if "spinbox" in managerAttrs:
if "from" in managerAttrs:
fromNum = managerAttrs["spinbox"]["from"]
if "to" in managerAttrs:
toNum = managerAttrs["spinbox"]["to"]
self.spinbox = mySpinbox(self, from_=fromNum, to=toNum, width=5, textvariable=self.storageVar, name="spinbox-" + label.lower())
if managerAttrs is not None and "spinbox" in managerAttrs:
self.spinbox.pack(managerAttrs["spinbox"])
else:
self.spinbox.pack()
return self
def make_textbox(self, parent, label, storageVar, manager, managerAttrs):
widget = Empty()
widget.storageVar = storageVar
widget.label = Label(parent, text=label)
widget.textbox = Entry(parent, justify=RIGHT, textvariable=widget.storageVar, width=3)
if "default" in managerAttrs:
widget.storageVar.set(managerAttrs["default"])
# grid
if manager == "grid":
widget.label.grid(managerAttrs["label"] if managerAttrs is not None and "label" in managerAttrs else None, row=parent.thisRow, column=parent.thisCol)
parent.thisCol += 1
widget.textbox.grid(managerAttrs["textbox"] if managerAttrs is not None and "textbox" in managerAttrs else None, row=parent.thisRow, column=parent.thisCol)
parent.thisRow += 1
parent.thisCol = 0
# pack
elif manager == "pack":
widget.label.pack(managerAttrs["label"] if managerAttrs is not None and "label" in managerAttrs else None)
widget.textbox.pack(managerAttrs["textbox"] if managerAttrs is not None and "textbox" in managerAttrs else None)
return widget
def make_widget(self, type, parent, label, storageVar=None, manager=None, managerAttrs=dict(), options=None):
widget = None
if manager is None:
manager = "pack"
thisStorageVar = storageVar
if isinstance(storageVar,str):
if storageVar == "int" or storageVar == "integer":
thisStorageVar = IntVar()
elif storageVar == "str" or storageVar == "string":
thisStorageVar = StringVar()
if type == "checkbox":
if thisStorageVar is None:
thisStorageVar = IntVar()
widget = make_checkbox(self, parent, label, thisStorageVar, manager, managerAttrs)
elif type == "selectbox":
if thisStorageVar is None:
thisStorageVar = StringVar()
widget = make_selectbox(self, parent, label, options, thisStorageVar, manager, managerAttrs)
elif type == "spinbox":
if thisStorageVar is None:
thisStorageVar = StringVar()
widget = make_spinbox(self, parent, label, thisStorageVar, manager, managerAttrs)
elif type == "textbox":
if thisStorageVar is None:
thisStorageVar = StringVar()
widget = make_textbox(self, parent, label, thisStorageVar, manager, managerAttrs)
widget.type = type
return widget
def make_widget_from_dict(self, defn, parent):
type = defn["type"] if "type" in defn else None
label = defn["label"]["text"] if "label" in defn and "text" in defn["label"] else ""
manager = defn["manager"] if "manager" in defn else None
managerAttrs = defn["managerAttrs"] if "managerAttrs" in defn else None
options = defn["options"] if "options" in defn else None
widget = make_widget(self, type, parent, label, None, manager, managerAttrs, options)
return widget
def make_widgets_from_dict(self, defns, parent):
widgets = {}
for key,defn in defns.items():
widgets[key] = make_widget_from_dict(self, defn, parent)
return widgets