# App.py # Application stuff. # The application is responsible for managing the main frame window. # # We also grab the FileOpen command, to invoke our Python editor " The PythonWin application code. Manages most aspects of MDI, etc " import os import sys import traceback import regutil import win32api import win32con import win32ui from pywin.mfc import afxres, dialog, window from pywin.mfc.thread import WinApp from . import scriptutils ## NOTE: App and AppBuild should NOT be used - instead, you should contruct your ## APP class manually whenever you like (just ensure you leave these 2 params None!) ## Whoever wants the generic "Application" should get it via win32iu.GetApp() # These are "legacy" AppBuilder = None App = None # default - if used, must end up a CApp derived class. # Helpers that should one day be removed! def AddIdleHandler(handler): print( "app.AddIdleHandler is deprecated - please use win32ui.GetApp().AddIdleHandler() instead." ) return win32ui.GetApp().AddIdleHandler(handler) def DeleteIdleHandler(handler): print( "app.DeleteIdleHandler is deprecated - please use win32ui.GetApp().DeleteIdleHandler() instead." ) return win32ui.GetApp().DeleteIdleHandler(handler) # Helper for writing a Window position by name, and later loading it. def SaveWindowSize(section, rect, state=""): """Writes a rectangle to an INI file Args: section = section name in the applications INI file rect = a rectangle in a (cy, cx, y, x) tuple (same format as CREATESTRUCT position tuples).""" left, top, right, bottom = rect if state: state = state + " " win32ui.WriteProfileVal(section, state + "left", left) win32ui.WriteProfileVal(section, state + "top", top) win32ui.WriteProfileVal(section, state + "right", right) win32ui.WriteProfileVal(section, state + "bottom", bottom) def LoadWindowSize(section, state=""): """Loads a section from an INI file, and returns a rect in a tuple (see SaveWindowSize)""" if state: state = state + " " left = win32ui.GetProfileVal(section, state + "left", 0) top = win32ui.GetProfileVal(section, state + "top", 0) right = win32ui.GetProfileVal(section, state + "right", 0) bottom = win32ui.GetProfileVal(section, state + "bottom", 0) return (left, top, right, bottom) def RectToCreateStructRect(rect): return (rect[3] - rect[1], rect[2] - rect[0], rect[1], rect[0]) # Define FrameWindow and Application objects # # The Main Frame of the application. class MainFrame(window.MDIFrameWnd): sectionPos = "Main Window" statusBarIndicators = ( afxres.ID_SEPARATOR, # // status line indicator afxres.ID_INDICATOR_CAPS, afxres.ID_INDICATOR_NUM, afxres.ID_INDICATOR_SCRL, win32ui.ID_INDICATOR_LINENUM, win32ui.ID_INDICATOR_COLNUM, ) def OnCreate(self, cs): self._CreateStatusBar() return 0 def _CreateStatusBar(self): self.statusBar = win32ui.CreateStatusBar(self) self.statusBar.SetIndicators(self.statusBarIndicators) self.HookCommandUpdate(self.OnUpdatePosIndicator, win32ui.ID_INDICATOR_LINENUM) self.HookCommandUpdate(self.OnUpdatePosIndicator, win32ui.ID_INDICATOR_COLNUM) def OnUpdatePosIndicator(self, cmdui): editControl = scriptutils.GetActiveEditControl() value = " " * 5 if editControl is not None: try: startChar, endChar = editControl.GetSel() lineNo = editControl.LineFromChar(startChar) colNo = endChar - editControl.LineIndex(lineNo) if cmdui.m_nID == win32ui.ID_INDICATOR_LINENUM: value = "%0*d" % (5, lineNo + 1) else: value = "%0*d" % (3, colNo + 1) except win32ui.error: pass cmdui.SetText(value) cmdui.Enable() def PreCreateWindow(self, cc): cc = self._obj_.PreCreateWindow(cc) pos = LoadWindowSize(self.sectionPos) self.startRect = pos if pos[2] - pos[0]: rect = RectToCreateStructRect(pos) cc = cc[0], cc[1], cc[2], cc[3], rect, cc[5], cc[6], cc[7], cc[8] return cc def OnDestroy(self, msg): # use GetWindowPlacement(), as it works even when min'd or max'd rectNow = self.GetWindowPlacement()[4] if rectNow != self.startRect: SaveWindowSize(self.sectionPos, rectNow) return 0 class CApp(WinApp): "A class for the application" def __init__(self): self.oldCallbackCaller = None WinApp.__init__(self, win32ui.GetApp()) self.idleHandlers = [] def InitInstance(self): "Called to crank up the app" HookInput() numMRU = win32ui.GetProfileVal("Settings", "Recent File List Size", 10) win32ui.LoadStdProfileSettings(numMRU) # self._obj_.InitMDIInstance() if win32api.GetVersionEx()[0] < 4: win32ui.SetDialogBkColor() win32ui.Enable3dControls() # install a "callback caller" - a manager for the callbacks # self.oldCallbackCaller = win32ui.InstallCallbackCaller(self.CallbackManager) self.LoadMainFrame() self.SetApplicationPaths() def ExitInstance(self): "Called as the app dies - too late to prevent it here!" win32ui.OutputDebug("Application shutdown\n") # Restore the callback manager, if any. try: win32ui.InstallCallbackCaller(self.oldCallbackCaller) except AttributeError: pass if self.oldCallbackCaller: del self.oldCallbackCaller self.frame = None # clean Python references to the now destroyed window object. self.idleHandlers = [] # Attempt cleanup if not already done! if self._obj_: self._obj_.AttachObject(None) self._obj_ = None global App global AppBuilder App = None AppBuilder = None return 0 def HaveIdleHandler(self, handler): return handler in self.idleHandlers def AddIdleHandler(self, handler): self.idleHandlers.append(handler) def DeleteIdleHandler(self, handler): self.idleHandlers.remove(handler) def OnIdle(self, count): try: ret = 0 handlers = self.idleHandlers[:] # copy list, as may be modified during loop for handler in handlers: try: thisRet = handler(handler, count) except: print("Idle handler %s failed" % (repr(handler))) traceback.print_exc() print("Idle handler removed from list") try: self.DeleteIdleHandler(handler) except ValueError: # Item not in list. pass thisRet = 0 ret = ret or thisRet return ret except KeyboardInterrupt: pass def CreateMainFrame(self): return MainFrame() def LoadMainFrame(self): "Create the main applications frame" self.frame = self.CreateMainFrame() self.SetMainFrame(self.frame) self.frame.LoadFrame(win32ui.IDR_MAINFRAME, win32con.WS_OVERLAPPEDWINDOW) self.frame.DragAcceptFiles() # we can accept these. self.frame.ShowWindow(win32ui.GetInitialStateRequest()) self.frame.UpdateWindow() self.HookCommands() def OnHelp(self, id, code): try: if id == win32ui.ID_HELP_GUI_REF: helpFile = regutil.GetRegisteredHelpFile("Pythonwin Reference") helpCmd = win32con.HELP_CONTENTS else: helpFile = regutil.GetRegisteredHelpFile("Main Python Documentation") helpCmd = win32con.HELP_FINDER if helpFile is None: win32ui.MessageBox("The help file is not registered!") else: from . import help help.OpenHelpFile(helpFile, helpCmd) except: t, v, tb = sys.exc_info() win32ui.MessageBox( "Internal error in help file processing\r\n%s: %s" % (t, v) ) tb = None # Prevent a cycle def DoLoadModules(self, modules): # XXX - this should go, but the debugger uses it :-( # dont do much checking! for module in modules: __import__(module) def HookCommands(self): self.frame.HookMessage(self.OnDropFiles, win32con.WM_DROPFILES) self.HookCommand(self.HandleOnFileOpen, win32ui.ID_FILE_OPEN) self.HookCommand(self.HandleOnFileNew, win32ui.ID_FILE_NEW) self.HookCommand(self.OnFileMRU, win32ui.ID_FILE_MRU_FILE1) self.HookCommand(self.OnHelpAbout, win32ui.ID_APP_ABOUT) self.HookCommand(self.OnHelp, win32ui.ID_HELP_PYTHON) self.HookCommand(self.OnHelp, win32ui.ID_HELP_GUI_REF) # Hook for the right-click menu. self.frame.GetWindow(win32con.GW_CHILD).HookMessage( self.OnRClick, win32con.WM_RBUTTONDOWN ) def SetApplicationPaths(self): # Load the users/application paths new_path = [] apppath = win32ui.GetProfileVal("Python", "Application Path", "").split(";") for path in apppath: if len(path) > 0: new_path.append(win32ui.FullPath(path)) for extra_num in range(1, 11): apppath = win32ui.GetProfileVal( "Python", "Application Path %d" % extra_num, "" ).split(";") if len(apppath) == 0: break for path in apppath: if len(path) > 0: new_path.append(win32ui.FullPath(path)) sys.path = new_path + sys.path def OnRClick(self, params): "Handle right click message" # put up the entire FILE menu! menu = win32ui.LoadMenu(win32ui.IDR_TEXTTYPE).GetSubMenu(0) menu.TrackPopupMenu(params[5]) # track at mouse position. return 0 def OnDropFiles(self, msg): "Handle a file being dropped from file manager" hDropInfo = msg[2] self.frame.SetActiveWindow() # active us nFiles = win32api.DragQueryFile(hDropInfo) try: for iFile in range(0, nFiles): fileName = win32api.DragQueryFile(hDropInfo, iFile) win32ui.GetApp().OpenDocumentFile(fileName) finally: win32api.DragFinish(hDropInfo) return 0 # No longer used by Pythonwin, as the C++ code has this same basic functionality # but handles errors slightly better. # It all still works, tho, so if you need similar functionality, you can use it. # Therefore I havent deleted this code completely! # def CallbackManager( self, ob, args = () ): # """Manage win32 callbacks. Trap exceptions, report on them, then return 'All OK' # to the frame-work. """ # import traceback # try: # ret = apply(ob, args) # return ret # except: # # take copies of the exception values, else other (handled) exceptions may get # # copied over by the other fns called. # win32ui.SetStatusText('An exception occured in a windows command handler.') # t, v, tb = sys.exc_info() # traceback.print_exception(t, v, tb.tb_next) # try: # sys.stdout.flush() # except (NameError, AttributeError): # pass # Command handlers. def OnFileMRU(self, id, code): "Called when a File 1-n message is recieved" fileName = win32ui.GetRecentFileList()[id - win32ui.ID_FILE_MRU_FILE1] win32ui.GetApp().OpenDocumentFile(fileName) def HandleOnFileOpen(self, id, code): "Called when FileOpen message is received" win32ui.GetApp().OnFileOpen() def HandleOnFileNew(self, id, code): "Called when FileNew message is received" win32ui.GetApp().OnFileNew() def OnHelpAbout(self, id, code): "Called when HelpAbout message is received. Displays the About dialog." win32ui.InitRichEdit() dlg = AboutBox() dlg.DoModal() def _GetRegistryValue(key, val, default=None): # val is registry value - None for default val. try: hkey = win32api.RegOpenKey(win32con.HKEY_CURRENT_USER, key) return win32api.RegQueryValueEx(hkey, val)[0] except win32api.error: try: hkey = win32api.RegOpenKey(win32con.HKEY_LOCAL_MACHINE, key) return win32api.RegQueryValueEx(hkey, val)[0] except win32api.error: return default scintilla = "Scintilla is Copyright 1998-2008 Neil Hodgson (http://www.scintilla.org)" idle = "This program uses IDLE extensions by Guido van Rossum, Tim Peters and others." contributors = "Thanks to the following people for making significant contributions: Roger Upole, Sidnei da Silva, Sam Rushing, Curt Hagenlocher, Dave Brennan, Roger Burnham, Gordon McMillan, Neil Hodgson, Laramie Leavitt. (let me know if I have forgotten you!)" # The About Box class AboutBox(dialog.Dialog): def __init__(self, idd=win32ui.IDD_ABOUTBOX): dialog.Dialog.__init__(self, idd) def OnInitDialog(self): text = ( "Pythonwin - Python IDE and GUI Framework for Windows.\n\n%s\n\nPython is %s\n\n%s\n\n%s\n\n%s" % (win32ui.copyright, sys.copyright, scintilla, idle, contributors) ) self.SetDlgItemText(win32ui.IDC_EDIT1, text) # Get the build number - written by installers. # For distutils build, read pywin32.version.txt import sysconfig site_packages = sysconfig.get_paths()["platlib"] try: build_no = ( open(os.path.join(site_packages, "pywin32.version.txt")).read().strip() ) ver = "pywin32 build %s" % build_no except EnvironmentError: ver = None if ver is None: # See if we are Part of Active Python ver = _GetRegistryValue( "SOFTWARE\\ActiveState\\ActivePython", "CurrentVersion" ) if ver is not None: ver = "ActivePython build %s" % (ver,) if ver is None: ver = "" self.SetDlgItemText(win32ui.IDC_ABOUT_VERSION, ver) self.HookCommand(self.OnButHomePage, win32ui.IDC_BUTTON1) def OnButHomePage(self, id, code): if code == win32con.BN_CLICKED: win32api.ShellExecute( 0, "open", "https://github.com/mhammond/pywin32", None, "", 1 ) def Win32RawInput(prompt=None): "Provide raw_input() for gui apps" # flush stderr/out first. try: sys.stdout.flush() sys.stderr.flush() except: pass if prompt is None: prompt = "" ret = dialog.GetSimpleInput(prompt) if ret == None: raise KeyboardInterrupt("operation cancelled") return ret def Win32Input(prompt=None): "Provide input() for gui apps" return eval(input(prompt)) def HookInput(): try: raw_input # must be py2x... sys.modules["__builtin__"].raw_input = Win32RawInput sys.modules["__builtin__"].input = Win32Input except NameError: # must be py3k import code sys.modules["builtins"].input = Win32RawInput def HaveGoodGUI(): """Returns true if we currently have a good gui available.""" return "pywin.framework.startup" in sys.modules def CreateDefaultGUI(appClass=None): """Creates a default GUI environment""" if appClass is None: from . import intpyapp # Bring in the default app - could be param'd later. appClass = intpyapp.InteractivePythonApp # Create and init the app. appClass().InitInstance() def CheckCreateDefaultGUI(): """Checks and creates if necessary a default GUI environment.""" rc = HaveGoodGUI() if not rc: CreateDefaultGUI() return rc