xmlビューワー
見るだけだけどちょいと必要だったので作った。
とりあえず貼り。
#!/usr/bin/python # coding: utf-8 import wx from wx.lib.mixins import treemixin import xml.sax import xml.sax.handler import sys import os FS_ENCODING='cp932' class Node(object): __slots__=[ 'name', 'attrs', 'body', 'children', ] def __init__(self, name, attrs=[]): self.name=name self.attrs=attrs self.children=[] self.body='' class TreeModel(object): def __init__(self): self.root=Node('root') # @param indices[in] 目的のノードへのパスをtupleで受ける # rootノードは () # rootの一つ目の子ノードは (0, ) (1要素のタプルは不許可) # その2番目の子ノードは (0, 1) という具合 def GetNode(self, indices): node=self.root # パスを辿っていく for index in indices: node=node.children[index] return node def GetText(self, indices): return self.GetNode(indices).name def GetNamePath(self, indices): path=[''] self.__GetNamePath(self.root, indices, path) return path def __GetNamePath(self, node, indices, path): if len(indices)==0: return counter={} for i in xrange(indices[0]+1): child=node.children[i] if child.name in counter: counter[child.name]+=1 else: counter[child.name]=0 child=node.children[indices[0]] if counter[child.name]==0: path.append(child.name) else: path.append("%s[%d]" % (child.name, counter[child.name])) self.__GetNamePath(child, indices[1:], path) class VTree(treemixin.VirtualTree, wx.TreeCtrl): def __init__(self, parent): super(VTree, self).__init__(parent) self.model=TreeModel() self.SetRoot(Node('root')) def SetRoot(self, root): self.model.root=root self.RefreshItems() def OnGetItemText(self, indices): return self.model.GetText(indices) def OnGetChildrenCount(self, indices): return len(self.model.GetNode(indices).children) def SelectPath(self, indices): item=self.GetRootItem() while len(indices)>0: index=indices.pop(0) item, cookie=self.GetFirstChild(item) for i in xrange(index): item=self.GetNextSibling(item) self.SelectItem(item) self.EnsureVisible(item) class TreeBuildHandler(xml.sax.handler.ContentHandler): def __init__(self): self.root=Node('root', {}) self.stack=[self.root] def startElement(self, name, attrs): attrs_list=[] for attr in attrs.items(): attrs_list.append(attr) node=Node(name, attrs_list) self.stack[-1].children.append(node) self.stack.append(node) def endElement(self, name): self.stack[-1].body.strip() self.stack.pop() def characters(self, c): self.stack[-1].body+=c class AttrList(wx.ListCtrl): def __init__(self, parent): super(AttrList, self).__init__(parent, style=wx.LC_REPORT|wx.LC_VIRTUAL) self.InsertColumn(0, "key") self.SetColumnWidth(0, 64) self.InsertColumn(1, "value") self.SetColumnWidth(1, 200) self.SetAttrs([]) def SetAttrs(self, attrs): self.attrs=attrs self.attrs.sort() self.SetItemCount(len(self.attrs)) self.Refresh() def OnGetItemText(self, index, column): attr=self.attrs[index] if column==0: return attr[0] elif column==1: return attr[1] class RightView(wx.Panel): def __init__(self, parent): super(RightView, self).__init__(parent) box=wx.BoxSizer(wx.VERTICAL) self.SetSizer(box) self.body=wx.TextCtrl(self, style=wx.TE_MULTILINE) box.Add(self.body, 2, wx.EXPAND) self.attrs=AttrList(self) box.Add(self.attrs, 1, wx.EXPAND) def SetNode(self, node): self.attrs.SetAttrs(node.attrs) self.body.SetValue(node.body) def fileOpenDialog(parent, message): dialog = wx.FileDialog(parent, message, "", "", "", wx.OPEN) try: if dialog.ShowModal() == wx.ID_OK: return dialog.GetPath() finally: dialog.Destroy() class MyFrame(wx.Frame): def __init__(self, parent): super(MyFrame, self).__init__(parent) # menu bar menubar = wx.MenuBar() self.SetMenuBar(menubar) menubar.Append(*self.CreateFileMenu()) # status bar self.statusbar = self.CreateStatusBar() # view splitter=wx.SplitterWindow(self) self.tree=VTree(splitter) self.view=RightView(splitter) splitter.SplitVertically(self.tree, self.view) # bind event self.tree.Bind(wx.EVT_TREE_SEL_CHANGED, self.OnSelect) self.Bind(wx.EVT_MENU, self.OnOpen, id=wx.ID_OPEN) self.Bind(wx.EVT_MENU, lambda e: self.Close(), id=wx.ID_CLOSE) self.keyMap={ 27: lambda event: self.Close(), } self.Bind(wx.EVT_KEY_DOWN, self.OnKeyDown) self.SetFocus() def CreateFileMenu(self): menu = wx.Menu() menu.Append(wx.ID_OPEN, 'open', 'open a file') menu.AppendSeparator() menu.Append(wx.ID_CLOSE, 'quit', 'quit application') return menu, "&File" def OnOpen(self, event): path=fileOpenDialog(self, "open a file") if not path: return self.Load(path) def OnSelect(self, event): # wx.TreeItemIdからindex pathを得る # wx.treemixin.TreeHelper.GetIndexOfItem indices=self.tree.GetIndexOfItem(event.GetItem()) node=self.tree.model.GetNode(indices) self.view.SetNode(node) self.statusbar.PushStatusText( '/'.join(self.tree.model.GetNamePath(indices))) def OnKeyDown(self, event): keyCode=event.GetKeyCode() try: self.keyMap[keyCode](event) except KeyError: print "OnKeyDown", keyCode def Load(self, file): global FS_ENCODING if not os.path.exists(file): print 'file not found', file return handler=TreeBuildHandler() parser = xml.sax.make_parser() parser.setContentHandler(handler) isParsed=False for encoding in ['utf-8', 'euc-jp', 'cp932']: # デスクトップなど日本語パスを開くのに注意 io=xml.sax.xmlreader.InputSource(file.encode(FS_ENCODING)) io.setEncoding(encoding) try: parser.parse(io) isParsed=True break except (UnicodeEncodeError, UnicodeDecodeError, xml.sax._exceptions.SAXParseException), e: continue if not isParsed: print "fail to load", file return self.tree.SetRoot(handler.root) self.SetTitle("%s[%s]" % (file, encoding)) self.Refresh() def usage(): print "usage %s {xml}" % sys.argv[0] if __name__=='__main__': app=wx.App(0) frame=MyFrame(None) if len(sys.argv)>1: frame.Load(sys.argv[1]) frame.Show() app.MainLoop()