Skip to content

Commit f228148

Browse files
add ability to change object filename
1 parent 8c9140e commit f228148

File tree

2 files changed

+152
-17
lines changed

2 files changed

+152
-17
lines changed

src/assets/images/folder.png

625 Bytes
Loading

src/main.py

+152-17
Original file line numberDiff line numberDiff line change
@@ -160,6 +160,14 @@ class WME(tk.Tk):
160160
'assets/images/icon_256x256.ico',
161161
]
162162
LOGO = 'assets/images/WME_logo.png'
163+
ASSETS = {
164+
'folder_icon': {
165+
'path': 'assets/images/folder.png',
166+
'format': 'photo',
167+
'size': (16,16),
168+
'cache': None,
169+
}
170+
}
163171

164172
def __init__(self, parent):
165173
tk.Tk.__init__(self,parent)
@@ -168,7 +176,7 @@ def __init__(self, parent):
168176
if getattr(sys, 'frozen', False) and hasattr(sys, '_MEIPASS'):
169177
self.WME_assets = sys._MEIPASS
170178
else:
171-
self.WME_assets = '.'
179+
self.WME_assets = os.path.dirname(__file__)
172180

173181
self.findIcons()
174182
# if len(self.windowIcons) > 0:
@@ -228,7 +236,7 @@ def __init__(self, parent):
228236

229237
try:
230238
self.panedGrip : dict[typing.Literal['image', 'horizontal', 'vertical'], Image.Image | ImageTk.PhotoImage] = {
231-
'image' : Image.open(self.getAsset('assets/images/grip.gif')).convert('RGBA'),
239+
'image' : Image.open(self.getAssetPath('assets/images/grip.gif')).convert('RGBA'),
232240
}
233241

234242
self.panedGrip['horizontal'] = ImageTk.PhotoImage(self.panedGrip['image'])
@@ -291,20 +299,53 @@ def __init__(self, parent):
291299

292300
self.protocol("WM_DELETE_WINDOW", self.close)
293301

294-
def getAsset(self, path : str):
302+
def getAssetPath(self, path : str):
295303
return os.path.join(self.WME_assets, path)
296304

305+
def getAsset(self, name: str) -> typing.Any:
306+
info = self.ASSETS[name]
307+
if info.get('cache'):
308+
return info.get('cache')
309+
else:
310+
path = self.getAssetPath(info['path'])
311+
logging.debug(f'asset format: {info["format"]}')
312+
size = info.get('size')
313+
if info['format'] == 'image':
314+
info['cache'] = Image.open(path)
315+
if size:
316+
info['cache'] = info['cache'].resize(size)
317+
elif info['format'] == 'photo':
318+
image = Image.open(path)
319+
if size:
320+
image = image.resize(size)
321+
info['cache'] = ImageTk.PhotoImage(image)
322+
elif info['format'] == 'bitmap':
323+
image = Image.open(path)
324+
if size:
325+
image = image.resize(size)
326+
info['cache'] = ImageTk.BitmapImage(image.convert('1'), foreground = 'black')
327+
elif info['format'] == 'bytes':
328+
info['cache'] = None
329+
with open(path, 'rb') as file:
330+
info['cache'] = file.read()
331+
else:
332+
info['cache'] = None
333+
with open(path, 'r') as file:
334+
info['cache'] = file.read()
335+
336+
return info['cache']
337+
297338
def findIcons(self):
298339
self.windowIcons : dict[str, ImageTk.PhotoImage] = {}
299340

300341
for icon in self.APP_ICONS:
301342
try:
302-
self.windowIcons[self.getAsset(icon)] = ImageTk.PhotoImage(
303-
Image.open(self.getAsset(icon))
343+
self.windowIcons[self.getAssetPath(icon)] = ImageTk.PhotoImage(
344+
Image.open(self.getAssetPath(icon))
304345
)
305346

306347
except:
307-
pass
348+
logging.exception('cannot load icon')
308349

309350
return self.windowIcons
310351

@@ -1264,7 +1305,8 @@ def pasteObject(
12641305
)
12651306

12661307
def addObject(
1267-
self, obj : wmwpy.classes.Object | str,
1308+
self,
1309+
obj : wmwpy.classes.Object | str,
12681310
properties: dict = {},
12691311
pos: tuple[float, float] = (0, 0),
12701312
name: str = 'Obj',
@@ -1283,6 +1325,59 @@ def addObject(
12831325
self.updateObjectSelector()
12841326

12851327
return obj
1328+
1329+
def changeObjectFilename(
1330+
self,
1331+
obj: wmwpy.classes.Object,
1332+
new_path: str | None,
1333+
):
1334+
if obj not in self.level.objects:
1335+
return
1336+
1337+
level_index = self.level.objects.index(obj)
1338+
1339+
if new_path == None:
1340+
new_path = filedialog.askopenfilename(
1341+
defaultextension = '.hs',
1342+
filetypes = (
1343+
('WMW Object', '*.hs'),
1344+
('Any', '*.*'),
1345+
),
1346+
initialdir = wmwpy.utils.path.joinPath(
1347+
self.game.gamepath,
1348+
self.game.assets,
1349+
self.game.baseassets,
1350+
'Objects'
1351+
),
1352+
)
1353+
1354+
if new_path == None:
1355+
return
1356+
1357+
if not isinstance(new_path, (wmwpy.filesystem.File)):
1358+
new_path = self.getFile(new_path)
1359+
1360+
if new_path == None:
1361+
return
1362+
1363+
self.level.objects.remove(obj)
1364+
1365+
new_obj = self.level.addObject(
1366+
new_path,
1367+
properties = deepcopy(obj.properties),
1368+
pos = copy(obj.pos),
1369+
name = obj.name,
1370+
)
1371+
1372+
self.level.objects.insert(level_index, self.level.objects.pop(self.level.objects.index(new_obj)))
1373+
1374+
1375+
self.updateObject(new_obj)
1376+
self.updateObjectSelector()
1377+
if self.selectedObject == obj:
1378+
self.selectObject(new_obj)
1379+
1380+
return new_obj
12861381

12871382
def addObjectSelector(self, pos : tuple = (0,0)):
12881383
filename = filedialog.askopenfilename(
@@ -1336,12 +1431,15 @@ def addProperty(
13361431
row = 0,
13371432
label_prefix: str = '',
13381433
label_editable: bool = True,
1434+
update_on_entry_edit: bool = True,
13391435
entry_callback: typing.Callable[[str], typing.Any] = None,
13401436
label_callback: typing.Callable[[str], bool] = None,
1341-
button_callback: typing.Callable = None,
1437+
button_callback: typing.Callable[[list[tk.StringVar]], str | list[str] | None] = None,
13421438
button_text: str = '-',
13431439
options: list[str] = None,
13441440
label_color: str | tuple = None,
1441+
button_image: tk.PhotoImage = None,
1442+
button_bitmap: tk.BitmapImage = None,
13451443
**kwargs,
13461444
) -> dict[typing.Literal[
13471445
'label',
@@ -1418,13 +1516,14 @@ def inputType(type, value):
14181516
t = t.lower()
14191517
input, var = inputType(t, value[column])
14201518
input.grid(column = column, row=row, sticky='ew', padx=2)
1421-
if callable(entry_callback):
1519+
if callable(entry_callback) and update_on_entry_edit:
14221520
var.trace_add('write', lambda *args, value = var.get, col = column: entry_callback(value(), col))
14231521

14241522
input.bind('<Return>', lambda e: self.focus())
1523+
if not update_on_entry_edit:
1524+
input.bind('<FocusOut>', lambda e, value = input.get, col = column: entry_callback(value(), col))
14251525

14261526
# input.bind('<Return>', lambda e, value = input.get, col = column: entry_callback(value(), col))
1427-
# input.bind('<FocusOut>', lambda e, value = input.get, col = column: entry_callback(value(), col))
14281527

14291528
column += 1
14301529

@@ -1440,12 +1539,14 @@ def inputType(type, value):
14401539
input, var = inputType(type, value)
14411540
input.grid(column = 0, row=row, sticky = 'ew', columnspan=2, padx=2)
14421541

1443-
if entry_callback:
1444-
var.trace('w', lambda *args: entry_callback(var.get()))
1542+
if entry_callback and update_on_entry_edit:
1543+
var.trace_add('write', lambda *args: entry_callback(var.get()))
14451544
# input.bind('<Return>', lambda e: entry_callback(input.get()))
1446-
# input.bind('<FocusOut>', lambda e: entry_callback(input.get()))
1545+
input.bind('<FocusOut>', lambda e: entry_callback(input.get()))
14471546

14481547
input.bind('<Return>', lambda e: self.focus())
1548+
if not update_on_entry_edit:
1549+
input.bind('<FocusOut>', lambda e: entry_callback(input.get()))
14491550

14501551
if input.winfo_reqheight() > row_size:
14511552
row_size = input.winfo_reqheight()
@@ -1456,11 +1557,31 @@ def inputType(type, value):
14561557
button = None
14571558

14581559
if show_button:
1560+
def callback(*args):
1561+
if callable(button_callback):
1562+
value = button_callback(vars, *args)
1563+
if value != None:
1564+
if len(vars) > 1 and isinstance(value, (list, tuple)):
1565+
for i, val in enumerate(value[0:len(vars)]):
1566+
vars[i].set(str(val))
1567+
else:
1568+
vars[0].set(str(value))
1569+
1570+
# if isinstance(button_image, Image.Image):
1571+
# logging.debug(f'button is PIL image: {button_image}')
1572+
# button_image = ImageTk.PhotoImage(button_image.resize((32,32)))
1573+
# button_text = None
1574+
# else:
1575+
# logging.debug(f'button not PIL image: {button_image}')
1576+
1577+
14591578
button = crossplatform.Button(
14601579
self.properties['right'],
14611580
text = button_text,
14621581
width = 2,
1463-
command = button_callback,
1582+
command = callback,
1583+
bitmap = button_bitmap,
1584+
image = button_image,
14641585
)
14651586
button.grid(column = 2, row = row)
14661587

@@ -1591,10 +1712,24 @@ def updateObjectName(name):
15911712
)
15921713
sizes.append(self.objectProperties['angle']['size'])
15931714

1594-
row = 3
1715+
self.objectProperties['Filename'] = addProperty(
1716+
'Filename',
1717+
obj.filename,
1718+
'text',
1719+
label_editable = False,
1720+
show_button = True,
1721+
row = 3,
1722+
update_on_entry_edit = False,
1723+
entry_callback = lambda name, object = obj: self.changeObjectFilename(object, f':game:{name}'),
1724+
button_callback = lambda vars, object = obj: self.changeObjectFilename(object, None),
1725+
button_image = self.getAsset('folder_icon'),
1726+
)
1727+
sizes.append(self.objectProperties['angle']['size'])
1728+
1729+
row = 4
15951730

15961731
for property in obj.properties:
1597-
if property not in ['Angle']:
1732+
if property not in ['Angle', 'Filename']:
15981733
row += 1
15991734
logging.debug(f'{property = }')
16001735
prefix = ''
@@ -2163,7 +2298,7 @@ def showAbout(self):
21632298
version = f'{__version__}\nwmwpy-{wmwpy.__version__}',
21642299
description = """Where's My Editor? is a program to create and modify levels in the Where's My Water? game series.""",
21652300
credits = __credits__,
2166-
logo = Image.open(self.getAsset(self.LOGO)),
2301+
logo = Image.open(self.getAssetPath(self.LOGO)),
21672302
)
21682303

21692304
def showSettings(self):

0 commit comments

Comments
 (0)