import glob import os import pyclbr import afxres import commctrl import pywin.framework.scriptutils import regutil import win32api import win32con import win32ui from pywin.mfc import dialog from . import hierlist class HLIErrorItem(hierlist.HierListItem): def __init__(self, text): self.text = text hierlist.HierListItem.__init__(self) def GetText(self): return self.text class HLICLBRItem(hierlist.HierListItem): def __init__(self, name, file, lineno, suffix=""): # If the 'name' object itself has a .name, use it. Not sure # how this happens, but seems pyclbr related. # See PyWin32 bug 817035 self.name = getattr(name, "name", name) self.file = file self.lineno = lineno self.suffix = suffix def __lt__(self, other): return self.name < other.name def __eq__(self, other): return self.name == other.name def GetText(self): return self.name + self.suffix def TakeDefaultAction(self): if self.file: pywin.framework.scriptutils.JumpToDocument( self.file, self.lineno, bScrollToTop=1 ) else: win32ui.SetStatusText("The source of this object is unknown") def PerformItemSelected(self): if self.file is None: msg = "%s - source can not be located." % (self.name,) else: msg = "%s defined at line %d of %s" % (self.name, self.lineno, self.file) win32ui.SetStatusText(msg) class HLICLBRClass(HLICLBRItem): def __init__(self, clbrclass, suffix=""): try: name = clbrclass.name file = clbrclass.file lineno = clbrclass.lineno self.super = clbrclass.super self.methods = clbrclass.methods except AttributeError: name = clbrclass file = lineno = None self.super = [] self.methods = {} HLICLBRItem.__init__(self, name, file, lineno, suffix) def GetSubList(self): ret = [] for c in self.super: ret.append(HLICLBRClass(c, " (Parent class)")) for meth, lineno in self.methods.items(): ret.append(HLICLBRMethod(meth, self.file, lineno, " (method)")) return ret def IsExpandable(self): return len(self.methods) + len(self.super) def GetBitmapColumn(self): return 21 class HLICLBRFunction(HLICLBRClass): def GetBitmapColumn(self): return 22 class HLICLBRMethod(HLICLBRItem): def GetBitmapColumn(self): return 22 class HLIModuleItem(hierlist.HierListItem): def __init__(self, path): hierlist.HierListItem.__init__(self) self.path = path def GetText(self): return os.path.split(self.path)[1] + " (module)" def IsExpandable(self): return 1 def TakeDefaultAction(self): win32ui.GetApp().OpenDocumentFile(self.path) def GetBitmapColumn(self): col = 4 # Default try: if win32api.GetFileAttributes(self.path) & win32con.FILE_ATTRIBUTE_READONLY: col = 5 except win32api.error: pass return col def GetSubList(self): mod, path = pywin.framework.scriptutils.GetPackageModuleName(self.path) win32ui.SetStatusText("Building class list - please wait...", 1) win32ui.DoWaitCursor(1) try: try: reader = pyclbr.readmodule_ex # Post 1.5.2 interface. extra_msg = " or functions" except AttributeError: reader = pyclbr.readmodule extra_msg = "" data = reader(mod, [path]) if data: ret = [] for item in data.values(): if ( item.__class__ != pyclbr.Class ): # ie, it is a pyclbr Function instance (only introduced post 1.5.2) ret.append(HLICLBRFunction(item, " (function)")) else: ret.append(HLICLBRClass(item, " (class)")) ret.sort() return ret else: return [HLIErrorItem("No Python classes%s in module." % (extra_msg,))] finally: win32ui.DoWaitCursor(0) win32ui.SetStatusText(win32ui.LoadString(afxres.AFX_IDS_IDLEMESSAGE)) def MakePathSubList(path): ret = [] for filename in glob.glob(os.path.join(path, "*")): if os.path.isdir(filename) and os.path.isfile( os.path.join(filename, "__init__.py") ): ret.append(HLIDirectoryItem(filename, os.path.split(filename)[1])) else: if os.path.splitext(filename)[1].lower() in [".py", ".pyw"]: ret.append(HLIModuleItem(filename)) return ret class HLIDirectoryItem(hierlist.HierListItem): def __init__(self, path, displayName=None, bSubDirs=0): hierlist.HierListItem.__init__(self) self.path = path self.bSubDirs = bSubDirs if displayName: self.displayName = displayName else: self.displayName = path def IsExpandable(self): return 1 def GetText(self): return self.displayName def GetSubList(self): ret = MakePathSubList(self.path) if ( os.path.split(self.path)[1] == "win32com" ): # Complete and utter hack for win32com. try: path = win32api.GetFullPathName( os.path.join(self.path, "..\\win32comext") ) ret = ret + MakePathSubList(path) except win32ui.error: pass return ret class HLIProjectRoot(hierlist.HierListItem): def __init__(self, projectName, displayName=None): hierlist.HierListItem.__init__(self) self.projectName = projectName self.displayName = displayName or projectName def GetText(self): return self.displayName def IsExpandable(self): return 1 def GetSubList(self): paths = regutil.GetRegisteredNamedPath(self.projectName) pathList = paths.split(";") if len(pathList) == 1: # Single dir - dont bother putting the dir in ret = MakePathSubList(pathList[0]) else: ret = list(map(HLIDirectoryItem, pathList)) return ret class HLIRoot(hierlist.HierListItem): def __init__(self): hierlist.HierListItem.__init__(self) def IsExpandable(self): return 1 def GetSubList(self): keyStr = regutil.BuildDefaultPythonKey() + "\\PythonPath" hKey = win32api.RegOpenKey(regutil.GetRootKey(), keyStr) try: ret = [] ret.append(HLIProjectRoot("", "Standard Python Library")) # The core path. index = 0 while 1: try: ret.append(HLIProjectRoot(win32api.RegEnumKey(hKey, index))) index = index + 1 except win32api.error: break return ret finally: win32api.RegCloseKey(hKey) class dynamic_browser(dialog.Dialog): style = win32con.WS_OVERLAPPEDWINDOW | win32con.WS_VISIBLE cs = ( win32con.WS_CHILD | win32con.WS_VISIBLE | commctrl.TVS_HASLINES | commctrl.TVS_LINESATROOT | commctrl.TVS_HASBUTTONS ) dt = [ ["Python Projects", (0, 0, 200, 200), style, None, (8, "MS Sans Serif")], ["SysTreeView32", None, win32ui.IDC_LIST1, (0, 0, 200, 200), cs], ] def __init__(self, hli_root): dialog.Dialog.__init__(self, self.dt) self.hier_list = hierlist.HierListWithItems(hli_root, win32ui.IDB_BROWSER_HIER) self.HookMessage(self.on_size, win32con.WM_SIZE) def OnInitDialog(self): self.hier_list.HierInit(self) return dialog.Dialog.OnInitDialog(self) def on_size(self, params): lparam = params[3] w = win32api.LOWORD(lparam) h = win32api.HIWORD(lparam) self.GetDlgItem(win32ui.IDC_LIST1).MoveWindow((0, 0, w, h)) def BrowseDialog(): root = HLIRoot() if not root.IsExpandable(): raise TypeError( "Browse() argument must have __dict__ attribute, or be a Browser supported type" ) dlg = dynamic_browser(root) dlg.CreateWindow() def DockableBrowserCreator(parent): root = HLIRoot() hl = hierlist.HierListWithItems(root, win32ui.IDB_BROWSER_HIER) style = ( win32con.WS_CHILD | win32con.WS_VISIBLE | win32con.WS_BORDER | commctrl.TVS_HASLINES | commctrl.TVS_LINESATROOT | commctrl.TVS_HASBUTTONS ) control = win32ui.CreateTreeCtrl() control.CreateWindow(style, (0, 0, 150, 300), parent, win32ui.IDC_LIST1) list = hl.HierInit(parent, control) return control def DockablePathBrowser(): import pywin.docking.DockingBar bar = pywin.docking.DockingBar.DockingBar() bar.CreateWindow( win32ui.GetMainFrame(), DockableBrowserCreator, "Path Browser", 0x8E0A ) bar.SetBarStyle( bar.GetBarStyle() | afxres.CBRS_TOOLTIPS | afxres.CBRS_FLYBY | afxres.CBRS_SIZE_DYNAMIC ) bar.EnableDocking(afxres.CBRS_ALIGN_ANY) win32ui.GetMainFrame().DockControlBar(bar) # The "default" entry point Browse = DockablePathBrowser