class Monitor: def __init__(self, callback=run_commands): self.__monitor = FSMonitorThread(callback) self.events = FSEvent.Create | FSEvent.Modify self.callback = callback def add_dir_watch(self, path, subfolders=False): def recursive_adder(path): self.__monitor.add_dir_watch(path, self.events) print "Adding subfolder: " + path for f in os.listdir(path): full_path = os.path.join(path, f) if os.path.isdir(full_path): recursive_adder(full_path) if not subfolders: self.__monitor.add_dir_watch(path, self.events) else: recursive_adder(path) def remove_watch(self, path): return self.__monitor.remove_watch(path) def remove_all_watches(self): return self.__monitor.remove_all_watches() def run(self): return self.__monitor.run() def stop(self): return self.__monitor.stop() def read_events(self): return self.__monitor.read_events()
class DirTreeCtrl(wx.TreeCtrl, wx.FileDropTarget): def __init__(self, parent, filter=None, show_root=False): style = wx.TR_DEFAULT_STYLE | wx.TR_EDIT_LABELS | wx.BORDER_NONE if not show_root: style |= wx.TR_HIDE_ROOT wx.TreeCtrl.__init__(self, parent, style=style) wx.FileDropTarget.__init__(self) self.SetDropTarget(self) self.SetDoubleBuffered(True) old_font = self.GetFont() fontsize = 10 if wx.Platform == "__WXMAC__" else 8 self.font = wx.Font(fontsize, old_font.Family, old_font.Style, old_font.Weight, faceName=old_font.FaceName) self.SetFont(self.font) self.filter = filter or DirTreeFilter() self.cq = CoroutineQueue() self.cm = CoroutineManager() self.select_later_parent = None self.select_later_name = None self.select_later_time = 0 self.expanding_all = False self.drop_item = None self.imglist = wx.ImageList(16, 16) self.imglist.Add(load_bitmap("icons/folder.png")) self.imglist.Add(load_bitmap("icons/folder_denied.png")) self.imglist.Add(load_bitmap("icons/file.png")) self.SetImageList(self.imglist) self.Bind(wx.EVT_TREE_ITEM_EXPANDING, self.OnItemExpanding) self.Bind(wx.EVT_TREE_ITEM_COLLAPSED, self.OnItemCollapsed) self.Bind(wx.EVT_TREE_ITEM_RIGHT_CLICK, self.OnItemRightClicked) self.Bind(wx.EVT_TREE_BEGIN_LABEL_EDIT, self.OnItemBeginLabelEdit) self.Bind(wx.EVT_TREE_END_LABEL_EDIT, self.OnItemEndLabelEdit) self.Bind(wx.EVT_TREE_BEGIN_DRAG, self.OnBeginDrag) self.Bind(wx.EVT_MENU, self.OnItemRename, id=ID_DIRTREE_RENAME) self.Bind(wx.EVT_MENU, self.OnItemDelete, id=ID_DIRTREE_DELETE) self.Bind(wx.EVT_MENU, self.OnNewFolder, id=ID_DIRTREE_NEW_FOLDER) self.Bind(wx.EVT_MENU, self.OnExpandAll, id=ID_DIRTREE_EXPAND_ALL) self.Bind(wx.EVT_MENU, self.OnCollapseAll, id=ID_DIRTREE_COLLAPSE_ALL) self.monitor = FSMonitorThread(callback=lambda event: wx.CallAfter(self.OnFSEvent, event)) def Destroy(self): self.monitor.remove_all_watches() self.monitor.read_events() self.cm.cancel() wx.TreeCtrl.Destroy(self) def SelectLater(self, parent, name, timeout=1): self.select_later_parent = parent self.select_later_name = name self.select_later_time = time.time() + timeout @managed("cm") @queued_coroutine("cq") def OnFSEvent(self, evt): if evt.action in (FSEvent.Create, FSEvent.MoveTo): item = (yield evt.user.add(evt.name, self, self.monitor, self.filter)) if item: if evt.name == self.select_later_name \ and self.GetItemParent(item) == self.select_later_parent: if time.time() < self.select_later_time: self.SelectItem(item) self.select_later_name = None self.select_later_parent = None self.select_later_time = 0 elif evt.action in (FSEvent.Delete, FSEvent.MoveFrom): evt.user.remove(evt.name, self, self.monitor) def OnItemExpanding(self, evt): node = self.GetEventNode(evt) if node.type == 'd' and node.state == NODE_UNPOPULATED: self.ExpandNode(node) def OnItemCollapsed(self, evt): node = self.GetEventNode(evt) if node.type == 'd': self.CollapseNode(node) def GetNodeMenu(self, node): return context_menu def OnItemRightClicked(self, evt): self.SelectItem(evt.GetItem()) node = self.GetEventNode(evt) menu = self.GetNodeMenu(node) if menu: self.PopupMenu(menu.Create()) def OnItemBeginLabelEdit(self, evt): node = self.GetEventNode(evt) if not (node and node.path): evt.Veto() def OnItemEndLabelEdit(self, evt): if not evt.IsEditCancelled(): evt.Veto() node = self.GetEventNode(evt) self.RenameNode(node, evt.GetLabel()) def OnBeginDrag(self, evt): node = self.GetEventNode(evt) if node: data = wx.FileDataObject() data.AddFile(node.path) dropsrc = wx.DropSource(self) dropsrc.SetData(data) dropsrc.DoDragDrop() def OnDragOver(self, x, y, default): self.OnLeave() item, flags = self.HitTest((x, y)) if item and (flags & wx.TREE_HITTEST_ONITEMLABEL): self.drop_item = item self.SetItemDropHighlight(item, True) return wx.DragCopy return wx.DragNone def OnLeave(self): if self.drop_item: self.SetItemDropHighlight(self.drop_item, False) self.drop_item = None def OnDropFiles(self, x, y, filenames): self.OnLeave() item, flags = self.HitTest((x, y)) if item and (flags & wx.TREE_HITTEST_ONITEMLABEL): while item: node = self.GetPyData(item) if node.type == 'd': for filename in filenames: fileutil.shell_move_or_copy(filename, node.path) break item = self.GetItemParent(item) def OnItemRename(self, evt): node = self.GetSelectedNode() if node and node.path: self.EditLabel(node.item) def OnItemDelete(self, evt): node = self.GetSelectedNode() if node: next_item = self.GetNextSibling(node.item) fileutil.shell_remove(node.path) def OnNewFolder(self, evt): node = self.GetSelectedNode() if node: if node.type != 'd': node = self.GetPyData(self.GetItemParent(node.item)) name = dialogs.get_text_input(self, "New Folder", "Please enter new folder name:") if name: self.NewFolder(node, name) @coroutine def OnExpandAll(self, evt): del evt if self.expanding_all: return self.expanding_all = True try: for item in iter_tree_breadth_first(self, self.GetRootItem()): if not self.expanding_all: return node = self.GetPyData(item) if node.type == 'd': if node.state == NODE_UNPOPULATED: try: yield self.ExpandNode(node) except Exception: pass else: self.Expand(item) finally: self.expanding_all = False def OnCollapseAll(self, evt): self.expanding_all = False self.CollapseAll() def SetItemNode(self, item, node): self.SetPyData(item, node) node.item = item return node def GetEventNode(self, evt): item = evt.GetItem() if item.IsOk(): return self.GetPyData(item) def GetSelectedNode(self): item = self.GetSelection() if item.IsOk(): return self.GetPyData(item) def GetSelectedPath(self): node = self.GetSelectedNode() return node.path if node else "" def IsExpanded(self, item): return self.GetRootItem() == item or wx.TreeCtrl.IsExpanded(self, item) def Expand(self, item): if self.GetRootItem() != item: wx.TreeCtrl.Expand(self, item) @managed("cm") @coroutine def PopulateNode(self, node): if node.state == NODE_UNPOPULATED: f = node.expand(self, self.monitor, self.filter) if isinstance(f, Future): yield f @managed("cm") @coroutine def ExpandNode(self, node): if node.state == NODE_UNPOPULATED: try: yield self.PopulateNode(node) except OSError: return if node.state != NODE_POPULATING: self.Expand(node.item) def CollapseNode(self, node): node.collapse(self, self.monitor) @managed("cm") @coroutine def RenameNode(self, node, name): newpath = os.path.join(os.path.dirname(node.path), name) if newpath != node.path: try: if (yield async_call(os.path.exists, newpath)): if not dialogs.ask_overwrite(self, newpath): return yield async_call(fileutil.rename, node.path, newpath) self.SelectLater(self.GetItemParent(node.item), name) except OSError, e: dialogs.error(self, str(e))