@@ -160,6 +160,14 @@ class WME(tk.Tk):
160
160
'assets/images/icon_256x256.ico' ,
161
161
]
162
162
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
+ }
163
171
164
172
def __init__ (self , parent ):
165
173
tk .Tk .__init__ (self ,parent )
@@ -168,7 +176,7 @@ def __init__(self, parent):
168
176
if getattr (sys , 'frozen' , False ) and hasattr (sys , '_MEIPASS' ):
169
177
self .WME_assets = sys ._MEIPASS
170
178
else :
171
- self .WME_assets = '.'
179
+ self .WME_assets = os . path . dirname ( __file__ )
172
180
173
181
self .findIcons ()
174
182
# if len(self.windowIcons) > 0:
@@ -228,7 +236,7 @@ def __init__(self, parent):
228
236
229
237
try :
230
238
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' ),
232
240
}
233
241
234
242
self .panedGrip ['horizontal' ] = ImageTk .PhotoImage (self .panedGrip ['image' ])
@@ -291,20 +299,53 @@ def __init__(self, parent):
291
299
292
300
self .protocol ("WM_DELETE_WINDOW" , self .close )
293
301
294
- def getAsset (self , path : str ):
302
+ def getAssetPath (self , path : str ):
295
303
return os .path .join (self .WME_assets , path )
296
304
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
+
297
338
def findIcons (self ):
298
339
self .windowIcons : dict [str , ImageTk .PhotoImage ] = {}
299
340
300
341
for icon in self .APP_ICONS :
301
342
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 ))
304
345
)
305
346
306
347
except :
307
- pass
348
+ logging . exception ( 'cannot load icon' )
308
349
309
350
return self .windowIcons
310
351
@@ -1264,7 +1305,8 @@ def pasteObject(
1264
1305
)
1265
1306
1266
1307
def addObject (
1267
- self , obj : wmwpy .classes .Object | str ,
1308
+ self ,
1309
+ obj : wmwpy .classes .Object | str ,
1268
1310
properties : dict = {},
1269
1311
pos : tuple [float , float ] = (0 , 0 ),
1270
1312
name : str = 'Obj' ,
@@ -1283,6 +1325,59 @@ def addObject(
1283
1325
self .updateObjectSelector ()
1284
1326
1285
1327
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
1286
1381
1287
1382
def addObjectSelector (self , pos : tuple = (0 ,0 )):
1288
1383
filename = filedialog .askopenfilename (
@@ -1336,12 +1431,15 @@ def addProperty(
1336
1431
row = 0 ,
1337
1432
label_prefix : str = '' ,
1338
1433
label_editable : bool = True ,
1434
+ update_on_entry_edit : bool = True ,
1339
1435
entry_callback : typing .Callable [[str ], typing .Any ] = None ,
1340
1436
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 ,
1342
1438
button_text : str = '-' ,
1343
1439
options : list [str ] = None ,
1344
1440
label_color : str | tuple = None ,
1441
+ button_image : tk .PhotoImage = None ,
1442
+ button_bitmap : tk .BitmapImage = None ,
1345
1443
** kwargs ,
1346
1444
) -> dict [typing .Literal [
1347
1445
'label' ,
@@ -1418,13 +1516,14 @@ def inputType(type, value):
1418
1516
t = t .lower ()
1419
1517
input , var = inputType (t , value [column ])
1420
1518
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 :
1422
1520
var .trace_add ('write' , lambda * args , value = var .get , col = column : entry_callback (value (), col ))
1423
1521
1424
1522
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 ))
1425
1525
1426
1526
# 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))
1428
1527
1429
1528
column += 1
1430
1529
@@ -1440,12 +1539,14 @@ def inputType(type, value):
1440
1539
input , var = inputType (type , value )
1441
1540
input .grid (column = 0 , row = row , sticky = 'ew' , columnspan = 2 , padx = 2 )
1442
1541
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 ()))
1445
1544
# 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 ()))
1447
1546
1448
1547
input .bind ('<Return>' , lambda e : self .focus ())
1548
+ if not update_on_entry_edit :
1549
+ input .bind ('<FocusOut>' , lambda e : entry_callback (input .get ()))
1449
1550
1450
1551
if input .winfo_reqheight () > row_size :
1451
1552
row_size = input .winfo_reqheight ()
@@ -1456,11 +1557,31 @@ def inputType(type, value):
1456
1557
button = None
1457
1558
1458
1559
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
+
1459
1578
button = crossplatform .Button (
1460
1579
self .properties ['right' ],
1461
1580
text = button_text ,
1462
1581
width = 2 ,
1463
- command = button_callback ,
1582
+ command = callback ,
1583
+ bitmap = button_bitmap ,
1584
+ image = button_image ,
1464
1585
)
1465
1586
button .grid (column = 2 , row = row )
1466
1587
@@ -1591,10 +1712,24 @@ def updateObjectName(name):
1591
1712
)
1592
1713
sizes .append (self .objectProperties ['angle' ]['size' ])
1593
1714
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
1595
1730
1596
1731
for property in obj .properties :
1597
- if property not in ['Angle' ]:
1732
+ if property not in ['Angle' , 'Filename' ]:
1598
1733
row += 1
1599
1734
logging .debug (f'{ property = } ' )
1600
1735
prefix = ''
@@ -2163,7 +2298,7 @@ def showAbout(self):
2163
2298
version = f'{ __version__ } \n wmwpy-{ wmwpy .__version__ } ' ,
2164
2299
description = """Where's My Editor? is a program to create and modify levels in the Where's My Water? game series.""" ,
2165
2300
credits = __credits__ ,
2166
- logo = Image .open (self .getAsset (self .LOGO )),
2301
+ logo = Image .open (self .getAssetPath (self .LOGO )),
2167
2302
)
2168
2303
2169
2304
def showSettings (self ):
0 commit comments