• R/O
  • HTTP
  • SSH
  • HTTPS

Commit

Tags
No Tags

Frequently used words (click to add to your profile)

javac++androidlinuxc#windowsobjective-ccocoa誰得qtpythonphprubygameguibathyscaphec計画中(planning stage)翻訳omegatframeworktwitterdomtestvb.netdirectxゲームエンジンbtronarduinopreviewer

wxPythonで作った簡易XMLエディタ


Commit MetaInfo

Revisão6028aaf1a49bfa6287ca63f9a9c22ece68fba207 (tree)
Hora2014-04-07 09:26:16
Autortamanegi <tamanegi@user...>
Commitertamanegi

Mensagem de Log

fix undo

Mudança Sumário

Diff

--- a/README
+++ b/README
@@ -6,6 +6,7 @@ TODO
66 * implement multiple delete, copy, cut
77
88 CHANGELOG
9+2014/04/07: fix and improve undo functions (paste, del, add)
910 2014/03/14: read file from command line argument
1011 fix bug on removing attributes
1112 add multiple delete (ad hoc); multiple copy/cut is not supported yet
--- a/wmutils.py
+++ b/wmutils.py
@@ -226,9 +226,6 @@ class Action:
226226 def undo( self, treectrl ):
227227 return True
228228
229- def gatherItemIdsForUndo( self ):
230- return []
231-
232229 # edit action; reversed action is also edit
233230 class ActionEdit( Action ):
234231 def __init__( self, itemid = None, myxml = None ):
@@ -259,9 +256,6 @@ class ActionEdit( Action ):
259256 def reverse( self ):
260257 return ActionEdit( self.itemid, self.myxml )
261258
262- def gatherItemIdsForUndo( self ):
263- return [ self.itemid ]
264-
265259 # edit multipe elements at once
266260 class ActionEdits( Action ):
267261 def __init__( self, itemids = None, myxmls = None ):
@@ -292,72 +286,74 @@ class ActionEdits( Action ):
292286 def reverse( self ):
293287 return( ActionEdits( self.itemids, self.myxmls ) )
294288
295- def gatherItemIdsForUndo( self ):
296- return self.itemids
297-
298289 # add action; reversed action is delete
299290 class ActionAdd( Action ):
300- def __init__( self, parent = None, pos = None, itemid = None, myxml = None ):
291+ def __init__( self, uniqids = None, pos = None, itemid = None, myxml = None ):
301292 Action.__init__( self )
302293 # itemid is an TreeItemId of added object
303294 # myxml is optional argument now
304295 if not itemid is None:
305- self.initialize( parent, pos, itemid, myxml )
296+ self.initialize( uniqids, pos, itemid, myxml )
306297
307- def initialize( self, parent, pos, itemid, myxml = None ):
308- self.parent = parent
298+ def initialize( self, uniqids, pos, itemid, myxml = None ):
299+ self.uniqids = copy.deepcopy( uniqids )
309300 self.pos = copy.deepcopy( pos )
310301 self.itemid = itemid
311302 self.myxml = copy.deepcopy( myxml )
312303
313304 # simply delete specified data
314305 def undo( self, treectrl, wmxmled ):
315- self.parent = treectrl.GetItemParent( self.itemid )
306+ self.itemid = wmxmled.findItemIdByUniqId( self.uniqids[1] )
316307 self.pos = wmxmled.calculateItemPosition( self.itemid )
317- self.myxml = wmxmled.createElementTreeFromTreeCtrl( self.itemid ).getroot()
308+ # do not update uniqids here
309+ self.myxml, uniqids = wmxmled.createElementTreeFromTreeCtrl( self.itemid )
310+ self.myxml = self.myxml.getroot()
318311 # ListCtrl is not updated here
319312 return wmxmled.deleteTreeCtrlItem( self.itemid )
320313
321314 def reverse( self ):
322- return ActionDel( self.parent, self.pos, self.itemid, self.myxml )
323-
324- def gatherItemIdsForUndo( self ):
325- return [ self.parent ]
315+ return ActionDel( self.uniqids, self.pos, self.itemid, self.myxml )
326316
327317 # delete action; reversed action is add
328318 class ActionDel( Action ):
329- def __init__( self, parent = None, pos = None, itemid = None, myxml = None ):
319+ def __init__( self, uniqids = None, pos = None, itemid = None, myxml = None ):
330320 Action.__init__( self )
331321 # note: myxml is not WMXmlElement, but is Element object of ET
332322 # itemid is optional, currently unused.
333323 if not myxml is None:
334- self.initialize( parent, pos, itemid, myxml )
324+ self.initialize( uniqids, pos, itemid, myxml )
335325
336- def initialize( self, parent, pos, itemid, myxml ):
337- self.parent = parent
326+ def initialize( self, uniqids, pos, itemid, myxml ):
327+ self.uniqids = copy.deepcopy( uniqids )
338328 self.pos = copy.deepcopy( pos )
339329 self.itemid = itemid
340330 self.myxml = copy.deepcopy( myxml )
341331
342332 # wmxmled is the main class; WMXmlEditor in wmxmled.py
343333 def undo( self, treectrl, wmxmled ):
344- if self.parent is None and treectrl.tc.IsEmpty(): # maybe root element
334+ if treectrl.IsEmpty(): # maybe root element
345335 wmxmled.createXmlTree( self.myxml )
346336 else: # add as a child element
347337 tag = wmxmled.createTreeCtrlLabel( self.myxml )
338+ parentid = wmxmled.findItemIdByUniqId(self.uniqids[0])
339+ # find parent from saved uniqid
348340 if self.pos < 0:
349341 # if the original item is the last one
350342 # i.e. GetNextSibling(itemid) returns false
351- self.itemid = treectrl.AppendItem( self.parent, tag, wmxmled.imageFolder, wmxmled.imageFolderOpen )
343+ self.itemid = treectrl.AppendItem( parentid, tag, wmxmled.imageFolder, wmxmled.imageFolderOpen )
352344 else:
353- self.itemid = treectrl.InsertItemBefore( self.parent, self.pos, tag, wmxmled.imageFolder, wmxmled.imageFolderOpen )
354- treectrl.SetItemPyData( self.itemid, [ 0, WMXmlElement( self.myxml ) ] )
345+ self.itemid = treectrl.InsertItemBefore( parentid, self.pos, tag, wmxmled.imageFolder, wmxmled.imageFolderOpen )
346+ treectrl.SetItemPyData( self.itemid, [ 0, WMXmlElement( self.myxml ), self.uniqids[1] ] )
347+ # save current uniqid
348+ uniqid_tmp = wmxmled.uniqid
349+ # add to tree
355350 wmxmled.createXmlTreeRecursive( self.myxml, self.itemid )
351+ # reassign uniq ids
352+ wmxmled.reassignUniqIDsRecursive( self.itemid, self.uniqids )
353+ # reset uniqid
354+ wmxmled.uniqid = uniqid_tmp
356355 # ListCtrl is not updated here
357356 return True
358357
359358 def reverse( self ):
360- return ActionAdd( self.parent, self.pos, self.itemid, self.myxml )
361-
362- def gatherItemIdsForUndo( self ):
363- return [ self.parent ]
359+ return ActionAdd( self.uniqids, self.pos, self.itemid, self.myxml )
--- a/wmxmled.py
+++ b/wmxmled.py
@@ -143,6 +143,8 @@ class WMXmlEditor( wx.App ):
143143 self.initializeImagesForTreeCtrl()
144144 # undo and redo list
145145 self.clearUndoList()
146+ # unique ids for objects
147+ self.uniqid = 0
146148
147149 def clearUndoList( self ):
148150 self.undo = []
@@ -309,11 +311,13 @@ class WMXmlEditor( wx.App ):
309311 def createXmlTree( self, xmlroot ):
310312 # initialize tree control
311313 self.tc.DeleteAllItems()
314+ self.uniqid = 0
312315 showname = self.createTreeCtrlLabel( xmlroot )
313316 root_itemid = self.tc.AddRoot( showname, self.imageFolder, self.imageFolderOpen )
314317 num = self.tc.GetCount() - 1
315318 # first element of the data is tentative value
316- self.tc.SetItemPyData( root_itemid, [ num, WMXmlElement( xmlroot ) ] )
319+ self.tc.SetItemPyData( root_itemid, [ num, WMXmlElement( xmlroot ), self.uniqid ] )
320+ self.uniqid += 1
317321 self.createXmlTreeRecursive( xmlroot, root_itemid )
318322
319323 def createXmlTreeRecursive( self, element, itemid ):
@@ -321,7 +325,8 @@ class WMXmlEditor( wx.App ):
321325 showname = self.createTreeCtrlLabel( child )
322326 child_itemid = self.tc.AppendItem( itemid, showname, self.imageFolder, self.imageFolderOpen )
323327 num = self.tc.GetCount() - 1
324- self.tc.SetItemPyData( child_itemid, [ num, WMXmlElement( child ) ] )
328+ self.tc.SetItemPyData( child_itemid, [ num, WMXmlElement( child ), self.uniqid ] )
329+ self.uniqid += 1
325330 self.createXmlTreeRecursive( child, child_itemid )
326331
327332 def createTreeCtrlLabel( self, myxml ):
@@ -339,6 +344,14 @@ class WMXmlEditor( wx.App ):
339344 ret += attr + ":" + val
340345 return ret
341346
347+ def reassignUniqIDsRecursive( self, itemid, uniqids ):
348+ localcounter = 1
349+ for val in self.childItemIdGeneratorRecursive( itemid ):
350+ localcounter += 1
351+ # item order shall not be changed
352+ curdata = self.tc.GetItemPyData( val )
353+ self.tc.SetItemPyData( val, [ curdata[0], curdata[1], uniqids[localcounter] ] )
354+
342355 ### create ListCtrl from pre-built TreeCtrl
343356 def createListCtrlFromTreeData( self ):
344357 # initialize ListCtrl
@@ -666,38 +679,9 @@ class WMXmlEditor( wx.App ):
666679 if not itemid.IsOk():
667680 self.miscShowMessageBox( "unexisting item id is requested to delete?" )
668681 return False
669- # check undo queue (queue is expected to be small)
670- for i in range( 0, len( self.undo ) ):
671- ####queueitem = self.undo[i]
672- queueitemids = self.undo[i].gatherItemIdsForUndo()
673- # if the object trying to delete is found in undo queue, show dialog
674- if self.isDescendantItem( itemid, queueitemids ):
675- if not WMXmlEditorParams.UNDO_truncate_queue_wo_query:
676- message = "Your " + str(i) + "the action cannot be undone due to the technical problem.\n" + "Can I truncate undo queue?"
677- dialog = wx.MessageDialog( self.frameMain, message, style=wx.YES_NO|wx.NO_DEFAULT )
678- try:
679- if dialog.ShowModal() == wx.ID_YES:
680- del self.undo[i:]
681- break
682- else:
683- return False
684- finally:
685- dialog.Destroy()
686- else:
687- del self.undo[i:]
688- break
689682 self.tc.Delete( itemid )
690683 return True
691684
692- def isDescendantItem( self, parent, candidates ):
693- for candidate in candidates:
694- itemid = candidate
695- while itemid.IsOk():
696- if parent == itemid:
697- return True
698- itemid = self.tc.GetItemParent( itemid )
699- return False
700-
701685 ### "File" menu events and related
702686 def evMenuFileNew( self, event = None ):
703687 # NOTE: Close is usually more safer method.
@@ -770,7 +754,7 @@ class WMXmlEditor( wx.App ):
770754 fh.close()
771755
772756 def writeXml( self, fh ):
773- xmltree = self.createElementTreeFromTreeCtrl( self.tc.GetRootItem() )
757+ xmltree, uniqids = self.createElementTreeFromTreeCtrl( self.tc.GetRootItem() )
774758 if xmltree is None:
775759 self.miscShowMessageBox( "no data to write." )
776760 else:
@@ -808,19 +792,24 @@ class WMXmlEditor( wx.App ):
808792 def createElementTreeFromTreeCtrl( self, itemid ):
809793 # if data is empty do nothing and return None
810794 if not itemid.IsOk():
811- return None
812- element = self.createElementFromWMXmlElement( self.tc.GetItemPyData( itemid )[1] )
813- self.createElementFromTreeCtrlRecursive( itemid, element )
795+ return None, None
796+ element_data = self.tc.GetItemPyData( itemid )
797+ element = self.createElementFromWMXmlElement( element_data[1] )
798+ # first item of uniqids is itemid of its parent, and the second is itemid
799+ # of the selected element
800+ uniqids = [element_data[2]]
801+ self.createElementFromTreeCtrlRecursive( itemid, element, uniqids )
814802 ret = ET.ElementTree( element )
815- return ret
803+ return ret, uniqids
816804
817- def createElementFromTreeCtrlRecursive( self, itemid, element ):
805+ def createElementFromTreeCtrlRecursive( self, itemid, element, uniqids ):
818806 if self.tc.ItemHasChildren( itemid ):
819807 child_itemid, cookie = self.tc.GetFirstChild( itemid )
820808 while child_itemid.IsOk():
821- child_element = self.createElementFromWMXmlElement( self.tc.GetItemPyData( child_itemid )[1] )
822- element.append( child_element )
823- self.createElementFromTreeCtrlRecursive( child_itemid, element[-1] )
809+ element_data = self.tc.GetItemPyData( child_itemid )
810+ element.append( self.createElementFromWMXmlElement( element_data[1] ) )
811+ uniqids.append( element_data[2] )
812+ self.createElementFromTreeCtrlRecursive( child_itemid, element[-1], uniqids )
824813 child_itemid, cookie = self.tc.GetNextChild( itemid, cookie )
825814
826815 def createElementFromWMXmlElement( self, wmxml ):
@@ -1100,7 +1089,9 @@ class WMXmlEditor( wx.App ):
11001089 return
11011090 myindex = min( self.lc_editing_index, self.l2l_correspondence[self.lc_editing_index] )
11021091 treeitemid = self.l2t_correspondence[myindex]
1103- self.copybuffer = self.createElementTreeFromTreeCtrl( treeitemid ).getroot()
1092+ # uniqids will be discarded
1093+ self.copybuffer, uniqids = self.createElementTreeFromTreeCtrl( treeitemid )
1094+ self.copybuffer = self.copybuffer.getroot()
11041095
11051096 def evPopupMenuCut( self, event ):
11061097 if not self.popupIsItemAvailable( self.lc_editing_index ):
@@ -1155,9 +1146,15 @@ class WMXmlEditor( wx.App ):
11551146 return
11561147 else:
11571148 my_itemid = self.appendElement( showname, treeitemid )
1158- self.tc.SetItemPyData( my_itemid, [ self.tc.GetCount() - 1, WMXmlElement( self.copybuffer ) ] )
1149+ newxml = WMXmlElement( self.copybuffer )
1150+ # use new uniq id
1151+ self.tc.SetItemPyData( my_itemid, [ self.tc.GetCount() - 1, newxml, self.uniqid ] )
1152+ self.uniqid += 1
11591153 self.createXmlTreeRecursive( self.copybuffer, my_itemid )
11601154 self.createListCtrlFromTreeData()
1155+ # add to the undo queue
1156+ self.addActionToUndoQueue( ActionAdd( self.gatherUniqIds( my_itemid ), 0, my_itemid, newxml ) )
1157+ self.IsSaved = False
11611158
11621159 def evPopupMenuDelete( self, event ):
11631160 if not self.popupIsItemAvailable( self.lc_editing_index ):
@@ -1165,17 +1162,22 @@ class WMXmlEditor( wx.App ):
11651162 myindex = self.lc_editing_index
11661163 treeitemid = self.l2t_correspondence[myindex]
11671164 # save action
1168- parent = self.tc.GetItemParent( treeitemid )
11691165 pos = self.calculateItemPosition( treeitemid )
11701166 if not self.tc.GetNextSibling( treeitemid ):
11711167 pos = -1
11721168 ## we need Element, not WMXmlElement
1173- myxml = self.createElementTreeFromTreeCtrl( treeitemid ).getroot()
1169+ myxml, uniqids = self.createElementTreeFromTreeCtrl( treeitemid )
1170+ myxml = myxml.getroot()
1171+ parent = self.tc.GetItemParent(treeitemid)
1172+ if parent.IsOk():
1173+ uniqids.insert( 0, self.tc.GetItemPyData(parent)[2] )
1174+ else:
1175+ uniqids.insert( 0, None )
11741176 #myxml = self.tc.GetItemPyData( treeitemid )[1]
11751177 # remove item
11761178 if self.deleteTreeCtrlItem( treeitemid ):
11771179 # add to undo queue
1178- self.addActionToUndoQueue( ActionDel( parent, pos, None, myxml ) )
1180+ self.addActionToUndoQueue( ActionDel( uniqids, pos, None, myxml ) )
11791181 # store removed data for undo
11801182 # reset flag
11811183 self.IsSaved = False
@@ -1222,10 +1224,11 @@ class WMXmlEditor( wx.App ):
12221224 my_itemid = self.insertElement( showname, treeitemid )
12231225 else: # append
12241226 my_itemid = self.appendElement( showname, treeitemid )
1225- self.tc.SetItemPyData( my_itemid, [ self.tc.GetCount() - 1, newxml ] )
1227+ self.tc.SetItemPyData( my_itemid, [ self.tc.GetCount() - 1, newxml, self.uniqid ] )
1228+ self.uniqid += 1
12261229 self.createListCtrlFromTreeData()
12271230 # add to undo queue
1228- self.addActionToUndoQueue( ActionAdd( None, 0, my_itemid, newxml ) )
1231+ self.addActionToUndoQueue( ActionAdd( self.gatherUniqIds( my_itemid ), 0, my_itemid, newxml ) )
12291232 # work-up
12301233 self.IsSaved = False
12311234
@@ -1294,10 +1297,11 @@ class WMXmlEditor( wx.App ):
12941297 my_itemid = self.insertChildElement( showname, treeitemid )
12951298 else: # append
12961299 my_itemid = self.appendChildElement( showname, treeitemid )
1297- self.tc.SetItemPyData( my_itemid, [ self.tc.GetCount() - 1, newxml ] )
1300+ self.tc.SetItemPyData( my_itemid, [ self.tc.GetCount() - 1, newxml, self.uniqid ] )
1301+ self.uniqid += 1
12981302 self.createListCtrlFromTreeData()
12991303 # register undo
1300- self.addActionToUndoQueue( ActionAdd( None, 0, my_itemid, newxml ) )
1304+ self.addActionToUndoQueue( ActionAdd( self.gatherUniqIds( my_itemid ), 0, my_itemid, newxml ) )
13011305 # work up
13021306 self.IsSaved = False
13031307
@@ -1320,6 +1324,51 @@ class WMXmlEditor( wx.App ):
13201324 def miscShowMessageBox( self, msg, msgtype = 'Info' ):
13211325 wx.MessageBox( msg, msgtype )
13221326
1327+ def findItemIdByUniqId( self, uniqid ):
1328+ for myid in self.allItemIdGenerator():
1329+ if myid.IsOk():
1330+ if self.tc.GetItemPyData(myid)[2] == uniqid:
1331+ return myid
1332+ return None
1333+
1334+ def gatherUniqIds( self, itemid ):
1335+ if not itemid.IsOk():
1336+ return None
1337+ ret = [self.tc.GetItemPyData(itemid)[2]]
1338+ self.gatherUniqIdsRecursive( itemid, ret )
1339+ tmpid = self.tc.GetItemParent( itemid )
1340+ if tmpid.IsOk():
1341+ ret.insert( 0, self.tc.GetItemPyData( tmpid )[2] )
1342+ else:
1343+ ret.insert( 0, None )
1344+ return ret
1345+
1346+ def gatherUniqIdsRecursive( self, itemid, ret ):
1347+ if self.tc.ItemHasChildren( itemid ):
1348+ child_itemid, cookie = self.tc.GetFirstChild( itemid )
1349+ while child_itemid.IsOk():
1350+ ret.append( self.tc.GetItemPyData(child_itemid)[2] )
1351+ self.gatherUniqIdsRecursive( child_itemid, ret )
1352+ child_itemid, cookie = self.tc.GetNextChild( itemid, cookie )
1353+
1354+ # all itemid generator
1355+ def allItemIdGenerator( self ):
1356+ rootid = self.tc.GetRootItem()
1357+ if rootid.IsOk():
1358+ yield rootid
1359+ for val in self.childItemIdGeneratorRecursive( rootid ):
1360+ yield val
1361+
1362+ def childItemIdGeneratorRecursive( self, itemid ):
1363+ if self.tc.ItemHasChildren( itemid ):
1364+ citemid, cookie = self.tc.GetFirstChild( itemid )
1365+ while citemid.IsOk():
1366+ yield citemid
1367+ if self.tc.ItemHasChildren( citemid ):
1368+ for val in self.childItemIdGeneratorRecursive( citemid ):
1369+ yield val
1370+ citemid, cookie = self.tc.GetNextChild( itemid, cookie )
1371+
13231372 ### undo and redo queues related
13241373 def addActionToUndoQueue( self, action, remove = False ):
13251374 self.addActionToQueue( action, self.undo )
@@ -1382,24 +1431,6 @@ class WMXmlEditor( wx.App ):
13821431 for itemid in matched:
13831432 self.tc.SelectItem( itemid )
13841433
1385- # all itemid generator
1386- def allItemIdGenerator( self ):
1387- rootid = self.tc.GetRootItem()
1388- if rootid.IsOk():
1389- yield rootid
1390- for val in self.childItemIdGeneratorRecursive( rootid ):
1391- yield val
1392-
1393- def childItemIdGeneratorRecursive( self, itemid ):
1394- if self.tc.ItemHasChildren( itemid ):
1395- citemid, cookie = self.tc.GetFirstChild( itemid )
1396- while citemid.IsOk():
1397- yield citemid
1398- if self.tc.ItemHasChildren( citemid ):
1399- for val in self.childItemIdGeneratorRecursive( citemid ):
1400- yield val
1401- citemid, cookie = self.tc.GetNextChild( itemid, cookie )
1402-
14031434 def genXmlQuery( self ):
14041435 ret = WMXmlQuery()
14051436 ret.setElementQuery( xrc.XRCCTRL( self.dialogFind, "textCtrlElementFind" ).GetValue(),