# 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 win32con import win32api import win32ui import sys import string import os from pywin.mfc import window, dialog, afxres from pywin.mfc.thread import WinApp import traceback import regutil 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 distutils.sysconfig site_packages = distutils.sysconfig.get_python_lib(plat_specific=1) 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