📄 mvctree.py
字号:
self.editcomp.Bind(wx.EVT_KEY_UP, self._key)
self.editcomp.Bind(wx.EVT_LEFT_DOWN, self._mdown)
self.editcomp.CaptureMouse()
def CanEdit(self, node):
return isinstance(node, FileWrapper)
def EndEdit(self, commit):
if not self.tree._EditEnding(self.treenode.data):
return
if commit:
node = self.treenode.data
try:
os.rename(node.path + os.sep + node.fileName, node.path + os.sep + self.editcomp.GetValue())
node.fileName = self.editcomp.GetValue()
except:
traceback.print_exc()
self.editcomp.ReleaseMouse()
self.editcomp.Destroy()
del self.editcomp
self.tree.Refresh()
def _key(self, evt):
if evt.KeyCode() == wx.WXK_RETURN:
self.EndEdit(True)
elif evt.KeyCode() == wx.WXK_ESCAPE:
self.EndEdit(False)
else:
evt.Skip()
def _mdown(self, evt):
if evt.IsButton():
x, y = evt.GetPosition()
w, h = self.editcomp.GetSize()
if x < 0 or y < 0 or x > w or y > h:
self.EndEdit(False)
class FileWrapper:
"""
Node class for FSTreeModel.
"""
def __init__(self, path, fileName):
self.path = path
self.fileName = fileName
def __str__(self):
return self.fileName
class FSTreeModel(BasicTreeModel):
"""
This treemodel models the filesystem starting from a given path.
"""
def __init__(self, path):
BasicTreeModel.__init__(self)
fw = FileWrapper(path, path.split(os.sep)[-1])
self._Build(path, fw)
self.SetRoot(fw)
self._editable = True
def _Build(self, path, fileWrapper):
for name in os.listdir(path):
fw = FileWrapper(path, name)
self.AddChild(fileWrapper, fw)
childName = path + os.sep + name
if os.path.isdir(childName):
self._Build(childName, fw)
def IsEditable(self, node):
return self._editable
def SetEditable(self, node, bool):
self._editable = bool
class LateFSTreeModel(FSTreeModel):
"""
This treemodel models the filesystem starting from a given path.
It retrieves the directory list as requested.
"""
def __init__(self, path):
BasicTreeModel.__init__(self)
name = path.split(os.sep)[-1]
pathpart = path[:-len(name)]
fw = FileWrapper(pathpart, name)
self._Build(path, fw)
self.SetRoot(fw)
self._editable = True
self.children = {}
self.parents = {}
def _Build(self, path, parent):
ppath = parent.path + os.sep + parent.fileName
if not os.path.isdir(ppath):
return
for name in os.listdir(ppath):
fw = FileWrapper(ppath, name)
self.AddChild(parent, fw)
def GetChildCount(self, node):
if self.children.has_key(node):
return FSTreeModel.GetChildCount(self, node)
else:
self._Build(node.path, node)
return FSTreeModel.GetChildCount(self, node)
def IsLeaf(self, node):
return not os.path.isdir(node.path + os.sep + node.fileName)
class StrTextConverter(TextConverter):
def Convert(self, node):
return str(node.data)
class NullTransform(Transform):
def GetSize(self):
return tuple(self.size)
def Transform(self, node, offset, rotation):
self.size = [0,0]
list = self.tree.GetLayoutEngine().GetNodeList()
for node in list:
node.projx = node.x + offset[0]
node.projy = node.y + offset[1]
if node.projx > self.size[0]:
self.size[0] = node.projx
if node.projy > self.size[1]:
self.size[1] = node.projy
class Rect:
def __init__(self, x, y, width, height):
self.x = x
self.y = y
self.width = width
self.height = height
def __getitem__(self, index):
return (self.x, self.y, self.width, self.height)[index]
def __setitem__(self, index, value):
name = ['x', 'y', 'width', 'height'][index]
setattr(self, name, value)
def Contains(self, other):
if type(other) == type(()):
other = Rect(other[0], other[1], 0, 0)
if other.x >= self.x:
if other.y >= self.y:
if other.width + other.x <= self.width + self.x:
if other.height + other.y <= self.height + self.y:
return True
return False
def __str__(self):
return "Rect: " + str([self.x, self.y, self.width, self.height])
class TreeLayout(LayoutEngine):
def SetHeight(self, num):
self.NODE_HEIGHT = num
def __init__(self, tree):
LayoutEngine.__init__(self, tree)
self.NODE_STEP = 20
self.NODE_HEIGHT = 20
self.nodelist = []
def Layout(self, node):
self.nodelist = []
self.NODE_HEIGHT = self.tree.GetFont().GetPointSize() * 2
self.layoutwalk(node)
def GetNodeList(self):
return self.nodelist
def layoutwalk(self, node):
if node == self.tree.currentRoot:
node.level = 1
self.lastY = (-self.NODE_HEIGHT)
node.x = self.NODE_STEP * node.level
node.y = self.lastY + self.NODE_HEIGHT
self.lastY = node.y
self.nodelist.append(node)
if node.expanded:
for kid in node.kids:
kid.level = node.level + 1
self.layoutwalk(kid)
class TreePainter(Painter):
"""
The default painter class. Uses double-buffering, delegates the painting of nodes and
lines to helper classes deriving from NodePainter and LinePainter.
"""
def __init__(self, tree, nodePainter = None, linePainter = None, textConverter = None):
Painter.__init__(self, tree)
if not nodePainter:
nodePainter = TreeNodePainter(self)
self.nodePainter = nodePainter
if not linePainter:
linePainter = TreeLinePainter(self)
self.linePainter = linePainter
if not textConverter:
textConverter = StrTextConverter(self)
self.textConverter = textConverter
self.charWidths = []
def Paint(self, dc, node, doubleBuffered=1, paintBackground=1):
if not self.charWidths:
self.charWidths = []
for i in range(25):
self.charWidths.append(dc.GetTextExtent("D")[0] * i)
self.charHeight = dc.GetTextExtent("D")[1]
self.textpen = wx.Pen(self.GetTextColour(), 1, wx.SOLID)
self.fgpen = wx.Pen(self.GetForegroundColour(), 1, wx.SOLID)
self.bgpen = wx.Pen(self.GetBackgroundColour(), 1, wx.SOLID)
self.linepen = wx.Pen(self.GetLineColour(), 1, wx.SOLID)
self.dashpen = wx.Pen(self.GetLineColour(), 1, wx.DOT)
self.textbrush = wx.Brush(self.GetTextColour(), wx.SOLID)
self.fgbrush = wx.Brush(self.GetForegroundColour(), wx.SOLID)
self.bgbrush = wx.Brush(self.GetBackgroundColour(), wx.SOLID)
self.linebrush = wx.Pen(self.GetLineColour(), 1, wx.SOLID)
treesize = self.tree.GetSize()
size = self.tree.transform.GetSize()
size = (max(treesize.width, size[0]+50), max(treesize.height, size[1]+50))
dc.BeginDrawing()
if doubleBuffered:
mem_dc = wx.MemoryDC()
if not self.GetBuffer():
self.knobs = []
self.rectangles = []
self.bmp = wx.EmptyBitmap(size[0], size[1])
mem_dc.SelectObject(self.GetBuffer())
mem_dc.SetPen(self.GetBackgroundPen())
mem_dc.SetBrush(self.GetBackgroundBrush())
mem_dc.DrawRectangle(0, 0, size[0], size[1])
mem_dc.SetFont(self.tree.GetFont())
self.paintWalk(node, mem_dc)
else:
mem_dc.SelectObject(self.GetBuffer())
xstart, ystart = self.tree.CalcUnscrolledPosition(0,0)
size = self.tree.GetClientSizeTuple()
dc.Blit(xstart, ystart, size[0], size[1], mem_dc, xstart, ystart)
else:
if node == self.tree.currentRoot:
self.knobs = []
self.rectangles = []
dc.SetPen(self.GetBackgroundPen())
dc.SetBrush(self.GetBackgroundBrush())
dc.SetFont(self.tree.GetFont())
if paintBackground:
dc.DrawRectangle(0, 0, size[0], size[1])
if node:
#Call with not paintBackground because if we are told not to paint the
#whole background, we have to paint in parts to undo selection coloring.
pb = paintBackground
self.paintWalk(node, dc, not pb)
dc.EndDrawing()
def GetDashPen(self):
return self.dashpen
def SetLinePen(self, pen):
Painter.SetLinePen(self, pen)
self.dashpen = wx.Pen(pen.GetColour(), 1, wx.DOT)
def paintWalk(self, node, dc, paintRects=0):
self.linePainter.Paint(node.parent, node, dc)
self.nodePainter.Paint(node, dc, drawRects = paintRects)
if node.expanded:
for kid in node.kids:
if not self.paintWalk(kid, dc, paintRects):
return False
for kid in node.kids:
px = (kid.projx - self.tree.layout.NODE_STEP) + 5
py = kid.projy + kid.height/2
if (not self.tree.model.IsLeaf(kid.data)) or ((kid.expanded or self.tree._assumeChildren) and len(kid.kids)):
dc.SetPen(self.linepen)
dc.SetBrush(self.bgbrush)
dc.DrawRectangle(px -4, py-4, 9, 9)
self.knobs.append( (kid, Rect(px -4, py -4, 9, 9)) )
dc.SetPen(self.textpen)
if not kid.expanded:
dc.DrawLine(px, py -2, px, py + 3)
dc.DrawLine(px -2, py, px + 3, py)
if node == self.tree.currentRoot:
px = (node.projx - self.tree.layout.NODE_STEP) + 5
py = node.projy + node.height/2
dc.SetPen(self.linepen)
dc.SetBrush(self.bgbrush)
dc.DrawRectangle(px -4, py-4, 9, 9)
self.knobs.append( (node, Rect(px -4, py -4, 9, 9)) )
dc.SetPen(self.textpen)
if not node.expanded:
dc.DrawLine(px, py -2, px, py + 3)
dc.DrawLine(px -2, py, px + 3, py)
return True
def OnMouse(self, evt):
Painter.OnMouse(self, evt)
class TreeNodePainter(NodePainter):
def Paint(self, node, dc, location = None, drawRects = 0):
text = self.painter.textConverter.Convert(node)
extent = dc.GetTextExtent(text)
node.width = extent[0]
node.height = extent[1]
if node.selected:
dc.SetPen(self.painter.GetLinePen())
dc.SetBrush(self.painter.GetForegroundBrush())
dc.SetTextForeground(wx.NamedColour("WHITE"))
dc.DrawRectangle(node.projx -1, node.projy -1, node.width + 3, node.height + 3)
else:
if drawRects:
dc.SetBrush(self.painter.GetBackgroundBrush())
dc.SetPen(self.painter.GetBackgroundPen())
dc.DrawRectangle(node.projx -1, node.projy -1, node.width + 3, node.height + 3)
dc.SetTextForeground(self.painter.GetTextColour())
dc.DrawText(text, node.projx, node.projy)
self.painter.rectangles.append((node, Rect(node.projx, node.projy, node.width, node.height)))
class TreeLinePainter(LinePainter):
def Paint(self, parent, child, dc):
dc.SetPen(self.painter.GetDashPen())
px = py = cx = cy = 0
if parent is None or child == self.painter.tree.currentRoot:
px = (child.projx - self.painter.tree.layout.NODE_STEP) + 5
py = child.projy + self.painter.tree.layout.NODE_HEIGHT/2 -2
cx = child.projx
cy = py
dc.DrawLine(px, py, cx, cy)
else:
px = parent.projx + 5
py = parent.projy + parent.height
cx = child.projx -5
cy = child.projy + self.painter.tree.layout.NODE_HEIGHT/2 -3
dc.DrawLine(px, py, px, cy)
dc.DrawLine(px, cy, cx, cy)
#>> Event defs
wxEVT_MVCTREE_BEGIN_EDIT = wx.NewEventType() #Start editing. Vetoable.
wxEVT_MVCTREE_END_EDIT = wx.NewEventType() #Stop editing. Vetoable.
wxEVT_MVCTREE_DELETE_ITEM = wx.NewEventType() #Item removed from model.
wxEVT_MVCTREE_ITEM_EXPANDED = wx.NewEventType()
wxEVT_MVCTREE_ITEM_EXPANDING = wx.NewEventType()
wxEVT_MVCTREE_ITEM_COLLAPSED = wx.NewEventType()
wxEVT_MVCTREE_ITEM_COLLAPSING = wx.NewEventType()
wxEVT_MVCTREE_SEL_CHANGED = wx.NewEventType()
wxEVT_MVCTREE_SEL_CHANGING = wx.NewEventType() #Vetoable.
wxEVT_MVCTREE_KEY_DOWN = wx.NewEventType()
wxEVT_MVCTREE_ADD_ITEM = wx.NewEventType() #Item added to model.
EVT_MVCTREE_SEL_CHANGED = wx.PyEventBinder(wxEVT_MVCTREE_SEL_CHANGED, 1)
EVT_MVCTREE_SEL_CHANGING = wx.PyEventBinder(wxEVT_MVCTREE_SEL_CHANGING, 1)
EVT_MVCTREE_ITEM_EXPANDED = wx.PyEventBinder(wxEVT_MVCTREE_ITEM_EXPANDED, 1)
EVT_MVCTREE_ITEM_EXPANDING = wx.PyEventBinder(wxEVT_MVCTREE_ITEM_EXPANDING, 1)
EVT_MVCTREE_ITEM_COLLAPSED = wx.PyEventBinder(wxEVT_MVCTREE_ITEM_COLLAPSED, 1)
EVT_MVCTREE_ITEM_COLLAPSING = wx.PyEventBinder(wxEVT_MVCTREE_ITEM_COLLAPSING, 1)
EVT_MVCTREE_ADD_ITEM = wx.PyEventBinder(wxEVT_MVCTREE_ADD_ITEM, 1)
EVT_MVCTREE_DELETE_ITEM = wx.PyEventBinder(wxEVT_MVCTREE_DELETE_ITEM, 1)
EVT_MVCTREE_KEY_DOWN = wx.PyEventBinder(wxEVT_MVCTREE_KEY_DOWN, 1)
class MVCTreeEvent(wx.PyCommandEvent):
def __init__(self, type, id, node = None, nodes = None, keyEvent = None, **kwargs):
apply(wx.PyCommandEvent.__init__, (self, type, id), kwargs)
self.node = node
self.nodes = nodes
self.keyEvent = keyEvent
def GetNode(self):
return self.node
def GetNodes(self):
return self.nodes
def getKeyEvent(self):
return self.keyEvent
class MVCTreeNotifyEvent(MVCTreeEvent):
def __init__(self, type, id, node = None, nodes = None, **kwargs):
apply(MVCTreeEvent.__init__, (self, type, id, node, nodes), kwargs)
self.notify = wx.NotifyEvent(type, id)
def getNotifyEvent(self):
return self.notify
class MVCTree(wx.ScrolledWindow):
"""
The main mvc tree class.
"""
def __init__(self, parent, id, model = None, layout = None, transform = None,
painter = None, *args, **kwargs):
apply(wx.ScrolledWindow.__init__, (self, parent, id), kwargs)
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -