aboutsummaryrefslogtreecommitdiff
path: root/srb_tk.py
diff options
context:
space:
mode:
Diffstat (limited to 'srb_tk.py')
-rwxr-xr-xsrb_tk.py130
1 files changed, 90 insertions, 40 deletions
diff --git a/srb_tk.py b/srb_tk.py
index 0f74e8d..fc29a28 100755
--- a/srb_tk.py
+++ b/srb_tk.py
@@ -21,8 +21,11 @@
# possible way to have a widget store its variable: https://stackoverflow.com/questions/63871376/tkinter-widget-cgetvariable
# count number of 1s in a binary value https://stackoverflow.com/questions/8871204/count-number-of-1s-in-binary-representation/71307775#71307775
# Improve:
-# Add levelset, level info.
# Enable the checkbox for profile-in-use
+# Add reset-level-breakables
+# disable/gray out levels labels not reachable because of levelset_available_levels
+# setting that sets the background colors of the regions
+# add images to option menus if possible: https://stackoverflow.com/questions/67334913/can-i-possibly-put-an-image-label-into-an-option-box-in-tkinter
# Dependencies:
# dep-devuan: python3-tkinter, python3-pil.imagetk
# rec-devuan: python3-cairosvg
@@ -30,8 +33,16 @@
# spinbox appears to way too wide by default, even without a .grid(sticky="EW"). Try defining on the spinbox object at instantiation width=3
import tkinter as tk
from pathlib import Path
-import tkinter.filedialog, srb_lib, os, time
+import tkinter.filedialog, srb_lib, os, time, sys
import stackrpms_tk_lib as stk
+from PIL import Image, ImageTk
+
+srb_tk_dir = os.path.split(os.path.abspath(__file__))[0]
+image_dir = os.path.join(srb_tk_dir,"images")
+
+# make this configurable?
+IMAGES = True
+IMAGE_FAILURES = 0
ABOUT_TEXT = """
srb_tk \"Savegame Editor for Snoopy vs. the Red Baron\"
@@ -40,6 +51,20 @@ SPDX-License-Identifier: GPL-3.0-only
Icon courtesy of https://icons8.com/icon/9336/aircraft
"""
+def get_srb_image(filename, sizex, sizey, resample=Image.Resampling.LANCZOS):
+ """ helper function that returns an ImageTk from the named file from the configured images directory, resized for you. """
+ global IMAGE_FAILURES
+ if IMAGES and IMAGE_FAILURES < 5:
+ try:
+ img = Image.open(os.path.join(image_dir,filename)).resize((sizex,sizey),resample=resample)
+ return ImageTk.PhotoImage(img)
+ except:
+ IMAGE_FAILURES += 1
+ print(f"Failed to load image {filename}, only {5-IMAGE_FAILURES} failures left before giving up on images entirely.",file=sys.stderr)
+ return stk.empty_photoimage(sizex)
+ else:
+ return stk.empty_photoimage(sizex)
+
class App(tk.Frame):
def __init__(self, master):
super().__init__(master)
@@ -62,7 +87,7 @@ class App(tk.Frame):
# app icon
#imgicon = stk.get_scaled_icon("hexedit",24,"Numix-Circle", "","apps")
#imgicon = stk.get_scaled_icon(os.path.join(os.getenv("HOME"),"Downloads/snoopy.png"),24,"Numix-Circle", "","apps")
- imgicon = stk.get_scaled_icon("./srb.png",24,"default", "","apps")
+ imgicon = stk.get_scaled_icon(os.path.join(image_dir,"srb.png"),24,"default", "","apps")
self.master.tk.call("wm","iconphoto",self.master._w,imgicon)
# app variables
@@ -112,11 +137,11 @@ class App(tk.Frame):
self.pur_plane_str_baron = tk.StringVar(value="baron")
self.pur_plane_bool_baron = tk.BooleanVar()
# levelsets and levels
- #self.levelset_name_strs = []
self.levelset_status_ints = []
+ self.levelset_balloons_ints = []
for i in range(0,len(srb_lib.LEVELSETS)):
- #self.levelset_name_strs.append(tk.StringVar(value=srb_lib.get_levelset_info(i)[0]["name"]))
self.levelset_status_ints.append(tk.StringVar()) # will hold the status, as in completed mission mask
+ self.levelset_balloons_ints.append(tk.IntVar()) # will hold the number of balloons for the levelset
self.level_strs = []
self.level_statuses_hex = []
self.level_statuses = []
@@ -146,7 +171,7 @@ class App(tk.Frame):
self.ent_checksum.grid(row=2,column=0)
self.chk_silent = tk.Checkbutton(self.master,text="silent",variable=self.silent)
self.chk_silent.grid(row=2,column=1)
- self.frm_profiles = tk.Frame(self.master,background="orange",borderwidth=1)
+ self.frm_profiles = tk.Frame(self.master) #,background="orange",borderwidth=1)
self.frm_profiles.grid(row=3,column=0,columnspan=2)
# investigate radio button key-bind, up-down, to increment/decrement choice?
self.rad_prof1 = stk.Radiobutton(self.frm_profiles,value=1,text="Profile 1",variable=self.selected_profile,underline=8)
@@ -185,10 +210,11 @@ class App(tk.Frame):
#self.selected_profile.set(1) # purposefully do not set this value.
# one profile, so we will have to use the value of self.selected_profile
- self.frm_curprof = tk.Frame(self.master,background="red")
+ self.frm_curprof = tk.Frame(self.master) #,background="red")
self.frm_curprof.grid(row=4,column=0,rowspan=22,columnspan=2)
# most form fields are inside the current profile
- self.lbl_money = tk.Label(self.frm_curprof,text="Money")
+ self.img_money = get_srb_image("money.png",20,16)
+ self.lbl_money = tk.Label(self.frm_curprof,text="Money",image=self.img_money,compound="left")
self.lbl_money.grid(row=0,column=0)
self.ent_money = stk.Entry(self.frm_curprof,textvariable=self.money,func=self.load_form_into_data)
#self.ent_money.bind("<Key-Return>",self.load_form_into_data)
@@ -201,15 +227,18 @@ class App(tk.Frame):
# the indicatoron,padx,pady and grid sticky are incredibly useful and hard to find in the docs
self.opt_equ_weapon.config(indicatoron=False,padx=1,pady=1,takefocus=1)
self.opt_equ_weapon.grid(row=1,column=1,sticky="ew")
- self.lbl_health = tk.Label(self.frm_curprof,text="Health")
+ self.img_health = get_srb_image("health.png",16,16)
+ self.lbl_health = tk.Label(self.frm_curprof,text="Health",image=self.img_health,compound="left")
self.lbl_health.grid(row=2,column=0)
self.spn_health = stk.Spinbox(self.frm_curprof,from_=1,to=4,textvariable=self.health,width=2)
self.spn_health.grid(row=2,column=1)
- self.lbl_stunt = tk.Label(self.frm_curprof,text="Stunt")
+ self.img_stunt = get_srb_image("stunt.png",16,16)
+ self.lbl_stunt = tk.Label(self.frm_curprof,text="Stunt",image=self.img_stunt,compound="left")
self.lbl_stunt.grid(row=3,column=0)
self.spn_stunt = stk.Spinbox(self.frm_curprof,from_=1,to=4,textvariable=self.stunt,width=2)
self.spn_stunt.grid(row=3,column=1)
- self.lbl_gun = tk.Label(self.frm_curprof,text="Gun")
+ self.img_gun = get_srb_image("gun.png",19,16)
+ self.lbl_gun = tk.Label(self.frm_curprof,text="Gun",image=self.img_gun,compound="left")
self.lbl_gun.grid(row=4,column=0)
self.spn_gun = stk.Spinbox(self.frm_curprof,from_=1,to=5,textvariable=self.gun,width=2)
self.spn_gun.grid(row=4,column=1)
@@ -219,21 +248,29 @@ class App(tk.Frame):
#self.lbl_pur_weap.grid(row=5,column=0)
self.lbl_pur_weap = tk.Label(self.frm_curprof,text="Planes")
self.lbl_pur_weap.grid(row=5,column=1)
- self.chk_pur_weap_potato = stk.Checkbutton(self.frm_curprof,text="Potato Gun",variable=self.pur_weap_potato,func=self.load_form_into_data)
+ self.img_potato = get_srb_image("Potato Gun.png",16,16)
+ self.chk_pur_weap_potato = stk.Checkbutton(self.frm_curprof,text="Potato Gun",variable=self.pur_weap_potato,func=self.load_form_into_data,image=self.img_potato,compound="left")
self.chk_pur_weap_potato.grid(row=6,column=0,sticky="W")
- self.chk_pur_weap_stinger = stk.Checkbutton(self.frm_curprof,text="Stinger",variable=self.pur_weap_stinger,func=self.load_form_into_data)
+ self.img_stinger = get_srb_image("Stinger.png",16,16)
+ self.chk_pur_weap_stinger = stk.Checkbutton(self.frm_curprof,text="Stinger",variable=self.pur_weap_stinger,func=self.load_form_into_data,image=self.img_stinger,compound="left")
self.chk_pur_weap_stinger.grid(row=7,column=0,sticky="W")
- self.chk_pur_weap_watball = stk.Checkbutton(self.frm_curprof,text="Water Balloon Cannon",variable=self.pur_weap_watball,func=self.load_form_into_data)
+ self.img_watball = get_srb_image("Water Balloon Cannon.png",12,16)
+ self.chk_pur_weap_watball = stk.Checkbutton(self.frm_curprof,text="Water Balloon Cannon",variable=self.pur_weap_watball,func=self.load_form_into_data,image=self.img_watball,compound="left")
self.chk_pur_weap_watball.grid(row=8,column=0,sticky="W")
- self.chk_pur_weap_snow = stk.Checkbutton(self.frm_curprof,text="Snow Blower",variable=self.pur_weap_snow,func=self.load_form_into_data)
+ self.img_snow = get_srb_image("Snow Blower.png",16,16)
+ self.chk_pur_weap_snow = stk.Checkbutton(self.frm_curprof,text="Snow Blower",variable=self.pur_weap_snow,func=self.load_form_into_data,image=self.img_snow,compound="left")
self.chk_pur_weap_snow.grid(row=9,column=0,sticky="W")
- self.chk_pur_weap_fire = stk.Checkbutton(self.frm_curprof,text="Fire Boomerang",variable=self.pur_weap_fire,func=self.load_form_into_data)
+ self.img_fire = get_srb_image("Fire Boomerang.png",12,16)
+ self.chk_pur_weap_fire = stk.Checkbutton(self.frm_curprof,text="Fire Boomerang",variable=self.pur_weap_fire,func=self.load_form_into_data,image=self.img_fire,compound="left")
self.chk_pur_weap_fire.grid(row=10,column=0,sticky="W")
- self.chk_pur_weap_lightn = stk.Checkbutton(self.frm_curprof,text="Lightning Rod",variable=self.pur_weap_lightn,func=self.load_form_into_data)
+ self.img_lightn = get_srb_image("Lightning Rod.png",16,16)
+ self.chk_pur_weap_lightn = stk.Checkbutton(self.frm_curprof,text="Lightning Rod",variable=self.pur_weap_lightn,func=self.load_form_into_data,image=self.img_lightn,compound="left")
self.chk_pur_weap_lightn.grid(row=11,column=0,sticky="W")
- self.chk_pur_weap_romanc = stk.Checkbutton(self.frm_curprof,text="Roman Candles",variable=self.pur_weap_romanc,func=self.load_form_into_data)
+ self.img_romanc = get_srb_image("Roman Candles.png",16,16)
+ self.chk_pur_weap_romanc = stk.Checkbutton(self.frm_curprof,text="Roman Candles",variable=self.pur_weap_romanc,func=self.load_form_into_data,image=self.img_romanc,compound="left")
self.chk_pur_weap_romanc.grid(row=12,column=0,sticky="W")
- self.chk_pur_weap_pumpkin = stk.Checkbutton(self.frm_curprof,text="10 Gauge Pumpkin",variable=self.pur_weap_pumpkin,func=self.load_form_into_data)
+ self.img_pumpkin = get_srb_image("10 Gauge Pumpkin.png",16,16)
+ self.chk_pur_weap_pumpkin = stk.Checkbutton(self.frm_curprof,text="10 Gauge Pumpkin",variable=self.pur_weap_pumpkin,func=self.load_form_into_data,image=self.img_pumpkin,compound="left")
self.chk_pur_weap_pumpkin.grid(row=13,column=0,sticky="W")
self.chk_pur_plane_marcie = stk.Checkbutton(self.frm_curprof,textvariable=self.pur_plane_str_marcie,variable=self.pur_plane_bool_marcie,func=self.load_form_into_data)
self.chk_pur_plane_marcie.grid(row=6,column=1,sticky="W")
@@ -249,22 +286,23 @@ class App(tk.Frame):
self.chk_pur_plane_baron.grid(row=11,column=1,sticky="W")
# level and levelsets
- self.frm_levels = tk.Frame(self.master,borderwidth=1,background="green")
+ self.frm_levels = tk.Frame(self.master) #,borderwidth=1,background="green")
self.frm_levels.grid(row=0,column=3,rowspan=len(srb_lib.LEVELS)+4)
self.lbl_levelsets = []
self.spn_levelsets = []
+ self.ent_levelsets_balloons = []
self.lbl_levelsets_name = tk.Label(self.frm_levels,text="Billboard/Available")
- self.lbl_levelsets_name.grid(row=0,column=0,sticky="EW",columnspan=2)
+ self.lbl_levelsets_name.grid(row=0,column=0,sticky="EW",columnspan=4)
#self.lbl_levelsets_available_levels = tk.Label(self.frm_levels,text="Available")
#self.lbl_levelsets_available_levels.grid(row=0,column=1,columnspan=2)
self.lbl_levels_name = tk.Label(self.frm_levels,text="Level")
- self.lbl_levels_name.grid(row=0,column=3,sticky="EW")
+ self.lbl_levels_name.grid(row=0,column=5,sticky="EW")
self.lbl_levels_status = tk.Label(self.frm_levels,text="Rank")
- self.lbl_levels_status.grid(row=0,column=4,sticky="EW",columnspan=2)
+ self.lbl_levels_status.grid(row=0,column=6,sticky="EW",columnspan=2)
self.lbl_levels_balloons = tk.Label(self.frm_levels,text="Balloons")
- self.lbl_levels_balloons.grid(row=0,column=6,columnspan=4,sticky="EW")
+ self.lbl_levels_balloons.grid(row=0,column=8,columnspan=4,sticky="EW")
self.lbl_levels_letters = tk.Label(self.frm_levels,text="Letters")
- self.lbl_levels_letters.grid(row=0,column=10,columnspan=4,sticky="EW")
+ self.lbl_levels_letters.grid(row=0,column=12,columnspan=4,sticky="EW")
self.lbl_levels = []
self.opt_levels_hex = []
self.opt_levels = []
@@ -275,13 +313,27 @@ class App(tk.Frame):
self.btn_letters_none = []
self.btn_letters_all = []
x = 1
+ self.img_balloons = []
for i in range(0,len(srb_lib.LEVELSETS)):
tl = srb_lib.get_levelset_info(i)[0]
+ if "c" not in tl or tl["c"] == "none":
+ # this one will fail, but that is ok. there is no balloon color for the last levelset.
+ self.img_balloons.append(get_srb_image("balloon-none.png",16,20))
+ elif tl["c"] and tl["c"] != "none":
+ self.img_balloons.append(get_srb_image("balloon-" + tl["c"] + ".png",16,20))
+ #self.lbl_levelsets.append(tk.Label(self.frm_levels,text=tl["name"],image=self.img_balloons[-1],compound="right"))
self.lbl_levelsets.append(tk.Label(self.frm_levels,text=tl["name"]))
self.lbl_levelsets[i].grid(row=x,column=0,sticky="EW",columnspan=2)
+ if "c" in tl and tl["c"] != "none":
+ tk.Label(self.frm_levels,image=self.img_balloons[-1]).grid(row=x,column=2,columnspan=2)
self.spn_levelsets.append(stk.Spinbox(self.frm_levels,textvariable=self.levelset_status_ints[i],from_=0,to=tl["l"],width=2))
self.spn_levelsets[i].grid(row=x+1,column=0)
tk.Label(self.frm_levels,text="/ " + str(tl["l"])).grid(row=x+1,column=1,sticky="EW")
+ # balloons per levelset
+ if "c" in tl and tl["c"] != "none":
+ self.ent_levelsets_balloons.append(tk.Entry(self.frm_levels,textvariable=self.levelset_balloons_ints[i],state="readonly",width=3))
+ self.ent_levelsets_balloons[-1].grid(row=x+1,column=2)
+ tk.Label(self.frm_levels,text="/" + str(tl["b"])).grid(row=x+1,column=3,sticky="E")
#print(f"For levelset {tl},")
these_levels = [k for k in srb_lib.LEVELS if k["setid"] == tl["id"]]
sorted(these_levels,key=lambda i: i["id"])
@@ -289,29 +341,29 @@ class App(tk.Frame):
for j in these_levels:
#print(f"Process level {j}")
self.lbl_levels.append(tk.Label(self.frm_levels,text=j["name"]))
- self.lbl_levels[-1].grid(row=x,column=3)
+ self.lbl_levels[-1].grid(row=x,column=5)
self.opt_levels_hex.append(tk.Entry(self.frm_levels,state="readonly",textvariable=self.level_statuses_hex[j["id"]],width=2))
- self.opt_levels_hex[-1].grid(row=x,column=4)
+ self.opt_levels_hex[-1].grid(row=x,column=6)
self.opt_levels.append(tk.OptionMenu(self.frm_levels,self.level_statuses[j["id"]],*statuses))
self.opt_levels[-1].config(indicatoron=False,padx=0,pady=0,takefocus=1,width=8)
- self.opt_levels[-1].grid(row=x,column=5,sticky="EW")
+ self.opt_levels[-1].grid(row=x,column=7,sticky="EW")
# only some levels have balloons
if j["b"]:
self.ent_levels_balloons.append(tk.Entry(self.frm_levels,state="readonly",textvariable=self.level_balloons[j["id"]],width=2))
- self.ent_levels_balloons[-1].grid(row=x,column=6)
- tk.Label(self.frm_levels,text="/ " + str(j["b"])).grid(row=x,column=7)
+ self.ent_levels_balloons[-1].grid(row=x,column=8)
+ tk.Label(self.frm_levels,text="/ " + str(j["b"])).grid(row=x,column=9)
self.btn_balloons_none.append(tk.Button(self.frm_levels,text="none",padx=0,pady=0,command=lambda tl=j,action="none",ty="balloons": self.button_action_level(tl,action,ty)))
- self.btn_balloons_none[-1].grid(row=x,column=8)
+ self.btn_balloons_none[-1].grid(row=x,column=10)
self.btn_balloons_all.append(tk.Button(self.frm_levels,text="all",padx=0,pady=0,command=lambda tl=j,action="all",ty="balloons": self.button_action_level(tl,action,ty)))
- self.btn_balloons_all[-1].grid(row=x,column=9)
+ self.btn_balloons_all[-1].grid(row=x,column=11)
# every level has letters though
self.ent_levels_letters.append(tk.Entry(self.frm_levels,state="readonly",textvariable=self.level_letters[j["id"]],width=2))
- self.ent_levels_letters[-1].grid(row=x,column=10)
- tk.Label(self.frm_levels,text="/ " + str(len(str(j["l"])))).grid(row=x,column=11)
+ self.ent_levels_letters[-1].grid(row=x,column=12)
+ tk.Label(self.frm_levels,text="/ " + str(len(str(j["l"])))).grid(row=x,column=13)
self.btn_letters_none.append(tk.Button(self.frm_levels,text="none",padx=0,pady=0,command=lambda tl=j,action="none",ty="letters": self.button_action_level(tl,action,ty)))
- self.btn_letters_none[-1].grid(row=x,column=12)
+ self.btn_letters_none[-1].grid(row=x,column=14)
self.btn_letters_all.append(tk.Button(self.frm_levels,text="all",padx=0,pady=0,command=lambda tl=j,action="all",ty="letters": self.button_action_level(tl,action,ty)))
- self.btn_letters_all[-1].grid(row=x,column=13)
+ self.btn_letters_all[-1].grid(row=x,column=15)
x += 1
x += 1
# end for-i-in-levelsets
@@ -603,7 +655,7 @@ class App(tk.Frame):
self.pur_plane_bool_baron.set(True if "baron" in planes_list else False)
x = 0
for i in srb_lib.LEVELSETS:
- mission_mask, letters = srb_lib.get_levelset_status(self.bdata,profile_id,x,silent=self.silent.get())
+ mission_mask, letters, balloon_count = srb_lib.get_levelset_status(self.bdata,profile_id,x,silent=self.silent.get())
if 0 == x:
self.pur_plane_str_marcie.set(letters)
elif 1 == x:
@@ -620,10 +672,8 @@ class App(tk.Frame):
mission_mask = bin(int(mission_mask[1:],2)).count('1')
#print(f"for levelset {i}, need to set max {i['l']}, current {mission_mask}")
self.levelset_status_ints[x].set(mission_mask)
+ self.levelset_balloons_ints[x].set(balloon_count)
x += 1
- # levelset available levels
- for i in range(0,len(srb_lib.LEVELSETS)):
- this_mission_mask, collected_letters = srb_lib.get_levelset_status(self.bdata,profile_id,i,silent=True)
# levels
for i in range(0,len(srb_lib.LEVELS)):
tl_status, tl_balloons, tl_letters = srb_lib.get_level_status(self.bdata,profile_id,i,silent=True)
bgstack15