Spaces:
Sleeping
Sleeping
| # intpyapp.py - Interactive Python application class | |
| # | |
| import os | |
| import sys | |
| import traceback | |
| import __main__ | |
| import commctrl | |
| import win32api | |
| import win32con | |
| import win32ui | |
| from pywin.mfc import afxres, dialog | |
| from . import app, dbgcommands | |
| lastLocateFileName = ".py" # used in the "File/Locate" dialog... | |
| # todo - _SetupSharedMenu should be moved to a framework class. | |
| def _SetupSharedMenu_(self): | |
| sharedMenu = self.GetSharedMenu() | |
| from pywin.framework import toolmenu | |
| toolmenu.SetToolsMenu(sharedMenu) | |
| from pywin.framework import help | |
| help.SetHelpMenuOtherHelp(sharedMenu) | |
| from pywin.mfc import docview | |
| docview.DocTemplate._SetupSharedMenu_ = _SetupSharedMenu_ | |
| class MainFrame(app.MainFrame): | |
| def OnCreate(self, createStruct): | |
| self.closing = 0 | |
| if app.MainFrame.OnCreate(self, createStruct) == -1: | |
| return -1 | |
| style = ( | |
| win32con.WS_CHILD | |
| | afxres.CBRS_SIZE_DYNAMIC | |
| | afxres.CBRS_TOP | |
| | afxres.CBRS_TOOLTIPS | |
| | afxres.CBRS_FLYBY | |
| ) | |
| self.EnableDocking(afxres.CBRS_ALIGN_ANY) | |
| tb = win32ui.CreateToolBar(self, style | win32con.WS_VISIBLE) | |
| tb.ModifyStyle(0, commctrl.TBSTYLE_FLAT) | |
| tb.LoadToolBar(win32ui.IDR_MAINFRAME) | |
| tb.EnableDocking(afxres.CBRS_ALIGN_ANY) | |
| tb.SetWindowText("Standard") | |
| self.DockControlBar(tb) | |
| # Any other packages which use toolbars | |
| from pywin.debugger.debugger import PrepareControlBars | |
| PrepareControlBars(self) | |
| # Note "interact" also uses dockable windows, but they already happen | |
| # And a "Tools" menu on the main frame. | |
| menu = self.GetMenu() | |
| from . import toolmenu | |
| toolmenu.SetToolsMenu(menu, 2) | |
| # And fix the "Help" menu on the main frame | |
| from pywin.framework import help | |
| help.SetHelpMenuOtherHelp(menu) | |
| def OnClose(self): | |
| try: | |
| import pywin.debugger | |
| if ( | |
| pywin.debugger.currentDebugger is not None | |
| and pywin.debugger.currentDebugger.pumping | |
| ): | |
| try: | |
| pywin.debugger.currentDebugger.close(1) | |
| except: | |
| traceback.print_exc() | |
| return | |
| except win32ui.error: | |
| pass | |
| self.closing = 1 | |
| self.SaveBarState("ToolbarDefault") | |
| self.SetActiveView(None) # Otherwise MFC's OnClose may _not_ prompt for save. | |
| from pywin.framework import help | |
| help.FinalizeHelp() | |
| self.DestroyControlBar(afxres.AFX_IDW_TOOLBAR) | |
| self.DestroyControlBar(win32ui.ID_VIEW_TOOLBAR_DBG) | |
| return self._obj_.OnClose() | |
| def DestroyControlBar(self, id): | |
| try: | |
| bar = self.GetControlBar(id) | |
| except win32ui.error: | |
| return | |
| bar.DestroyWindow() | |
| def OnCommand(self, wparam, lparam): | |
| # By default, the current MDI child frame will process WM_COMMAND | |
| # messages before any docked control bars - even if the control bar | |
| # has focus. This is a problem for the interactive window when docked. | |
| # Therefore, we detect the situation of a view having the main frame | |
| # as its parent, and assume it must be a docked view (which it will in an MDI app) | |
| try: | |
| v = ( | |
| self.GetActiveView() | |
| ) # Raise an exception if none - good - then we want default handling | |
| # Main frame _does_ have a current view (ie, a docking view) - see if it wants it. | |
| if v.OnCommand(wparam, lparam): | |
| return 1 | |
| except (win32ui.error, AttributeError): | |
| pass | |
| return self._obj_.OnCommand(wparam, lparam) | |
| class InteractivePythonApp(app.CApp): | |
| # This works if necessary - just we dont need to override the Run method. | |
| # def Run(self): | |
| # return self._obj_.Run() | |
| def HookCommands(self): | |
| app.CApp.HookCommands(self) | |
| dbgcommands.DebuggerCommandHandler().HookCommands() | |
| self.HookCommand(self.OnViewBrowse, win32ui.ID_VIEW_BROWSE) | |
| self.HookCommand(self.OnFileImport, win32ui.ID_FILE_IMPORT) | |
| self.HookCommand(self.OnFileCheck, win32ui.ID_FILE_CHECK) | |
| self.HookCommandUpdate(self.OnUpdateFileCheck, win32ui.ID_FILE_CHECK) | |
| self.HookCommand(self.OnFileRun, win32ui.ID_FILE_RUN) | |
| self.HookCommand(self.OnFileLocate, win32ui.ID_FILE_LOCATE) | |
| self.HookCommand(self.OnInteractiveWindow, win32ui.ID_VIEW_INTERACTIVE) | |
| self.HookCommandUpdate( | |
| self.OnUpdateInteractiveWindow, win32ui.ID_VIEW_INTERACTIVE | |
| ) | |
| self.HookCommand(self.OnViewOptions, win32ui.ID_VIEW_OPTIONS) | |
| self.HookCommand(self.OnHelpIndex, afxres.ID_HELP_INDEX) | |
| self.HookCommand(self.OnFileSaveAll, win32ui.ID_FILE_SAVE_ALL) | |
| self.HookCommand(self.OnViewToolbarDbg, win32ui.ID_VIEW_TOOLBAR_DBG) | |
| self.HookCommandUpdate(self.OnUpdateViewToolbarDbg, win32ui.ID_VIEW_TOOLBAR_DBG) | |
| def CreateMainFrame(self): | |
| return MainFrame() | |
| def MakeExistingDDEConnection(self): | |
| # Use DDE to connect to an existing instance | |
| # Return None if no existing instance | |
| try: | |
| from . import intpydde | |
| except ImportError: | |
| # No dde support! | |
| return None | |
| conv = intpydde.CreateConversation(self.ddeServer) | |
| try: | |
| conv.ConnectTo("Pythonwin", "System") | |
| return conv | |
| except intpydde.error: | |
| return None | |
| def InitDDE(self): | |
| # Do all the magic DDE handling. | |
| # Returns TRUE if we have pumped the arguments to our | |
| # remote DDE app, and we should terminate. | |
| try: | |
| from . import intpydde | |
| except ImportError: | |
| self.ddeServer = None | |
| intpydde = None | |
| if intpydde is not None: | |
| self.ddeServer = intpydde.DDEServer(self) | |
| self.ddeServer.Create("Pythonwin", intpydde.CBF_FAIL_SELFCONNECTIONS) | |
| try: | |
| # If there is an existing instance, pump the arguments to it. | |
| connection = self.MakeExistingDDEConnection() | |
| if connection is not None: | |
| connection.Exec("self.Activate()") | |
| if self.ProcessArgs(sys.argv, connection) is None: | |
| return 1 | |
| except: | |
| # It is too early to 'print' an exception - we | |
| # don't have stdout setup yet! | |
| win32ui.DisplayTraceback( | |
| sys.exc_info(), " - error in DDE conversation with Pythonwin" | |
| ) | |
| return 1 | |
| def InitInstance(self): | |
| # Allow "/nodde" and "/new" to optimize this! | |
| if ( | |
| "/nodde" not in sys.argv | |
| and "/new" not in sys.argv | |
| and "-nodde" not in sys.argv | |
| and "-new" not in sys.argv | |
| ): | |
| if self.InitDDE(): | |
| return 1 # A remote DDE client is doing it for us! | |
| else: | |
| self.ddeServer = None | |
| win32ui.SetRegistryKey( | |
| "Python %s" % (sys.winver,) | |
| ) # MFC automatically puts the main frame caption on! | |
| app.CApp.InitInstance(self) | |
| # Create the taskbar icon | |
| win32ui.CreateDebuggerThread() | |
| # Allow Pythonwin to host OCX controls. | |
| win32ui.EnableControlContainer() | |
| # Display the interactive window if the user wants it. | |
| from . import interact | |
| interact.CreateInteractiveWindowUserPreference() | |
| # Load the modules we use internally. | |
| self.LoadSystemModules() | |
| # Load additional module the user may want. | |
| self.LoadUserModules() | |
| # Load the ToolBar state near the end of the init process, as | |
| # there may be Toolbar IDs created by the user or other modules. | |
| # By now all these modules should be loaded, so all the toolbar IDs loaded. | |
| try: | |
| self.frame.LoadBarState("ToolbarDefault") | |
| except win32ui.error: | |
| # MFC sucks. It does essentially "GetDlgItem(x)->Something", so if the | |
| # toolbar with ID x does not exist, MFC crashes! Pythonwin has a trap for this | |
| # but I need to investigate more how to prevent it (AFAIK, ensuring all the | |
| # toolbars are created by now _should_ stop it!) | |
| pass | |
| # Finally process the command line arguments. | |
| try: | |
| self.ProcessArgs(sys.argv) | |
| except: | |
| # too early for printing anything. | |
| win32ui.DisplayTraceback( | |
| sys.exc_info(), " - error processing command line args" | |
| ) | |
| def ExitInstance(self): | |
| win32ui.DestroyDebuggerThread() | |
| try: | |
| from . import interact | |
| interact.DestroyInteractiveWindow() | |
| except: | |
| pass | |
| if self.ddeServer is not None: | |
| self.ddeServer.Shutdown() | |
| self.ddeServer = None | |
| return app.CApp.ExitInstance(self) | |
| def Activate(self): | |
| # Bring to the foreground. Mainly used when another app starts up, it asks | |
| # this one to activate itself, then it terminates. | |
| frame = win32ui.GetMainFrame() | |
| frame.SetForegroundWindow() | |
| if frame.GetWindowPlacement()[1] == win32con.SW_SHOWMINIMIZED: | |
| frame.ShowWindow(win32con.SW_RESTORE) | |
| def ProcessArgs(self, args, dde=None): | |
| # If we are going to talk to a remote app via DDE, then | |
| # activate it! | |
| if ( | |
| len(args) < 1 or not args[0] | |
| ): # argv[0]=='' when started without args, just like Python.exe! | |
| return | |
| i = 0 | |
| while i < len(args): | |
| argType = args[i] | |
| i += 1 | |
| if argType.startswith("-"): | |
| # Support dash options. Slash options are misinterpreted by python init | |
| # as path and not finding usually 'C:\\' ends up in sys.path[0] | |
| argType = "/" + argType[1:] | |
| if not argType.startswith("/"): | |
| argType = win32ui.GetProfileVal( | |
| "Python", "Default Arg Type", "/edit" | |
| ).lower() | |
| i -= 1 # arg is /edit's parameter | |
| par = i < len(args) and args[i] or "MISSING" | |
| if argType in ("/nodde", "/new", "-nodde", "-new"): | |
| # Already handled | |
| pass | |
| elif argType.startswith("/goto:"): | |
| gotoline = int(argType[len("/goto:") :]) | |
| if dde: | |
| dde.Exec( | |
| "from pywin.framework import scriptutils\n" | |
| "ed = scriptutils.GetActiveEditControl()\n" | |
| "if ed: ed.SetSel(ed.LineIndex(%s - 1))" % gotoline | |
| ) | |
| else: | |
| from . import scriptutils | |
| ed = scriptutils.GetActiveEditControl() | |
| if ed: | |
| ed.SetSel(ed.LineIndex(gotoline - 1)) | |
| elif argType == "/edit": | |
| # Load up the default application. | |
| i += 1 | |
| fname = win32api.GetFullPathName(par) | |
| if not os.path.isfile(fname): | |
| # if we don't catch this, OpenDocumentFile() (actually | |
| # PyCDocument.SetPathName() in | |
| # pywin.scintilla.document.CScintillaDocument.OnOpenDocument) | |
| # segfaults Pythonwin on recent PY3 builds (b228) | |
| win32ui.MessageBox( | |
| "No such file: %s\n\nCommand Line: %s" | |
| % (fname, win32api.GetCommandLine()), | |
| "Open file for edit", | |
| win32con.MB_ICONERROR, | |
| ) | |
| continue | |
| if dde: | |
| dde.Exec("win32ui.GetApp().OpenDocumentFile(%s)" % (repr(fname))) | |
| else: | |
| win32ui.GetApp().OpenDocumentFile(par) | |
| elif argType == "/rundlg": | |
| if dde: | |
| dde.Exec( | |
| "from pywin.framework import scriptutils;scriptutils.RunScript(%r, %r, 1)" | |
| % (par, " ".join(args[i + 1 :])) | |
| ) | |
| else: | |
| from . import scriptutils | |
| scriptutils.RunScript(par, " ".join(args[i + 1 :])) | |
| return | |
| elif argType == "/run": | |
| if dde: | |
| dde.Exec( | |
| "from pywin.framework import scriptutils;scriptutils.RunScript(%r, %r, 0)" | |
| % (par, " ".join(args[i + 1 :])) | |
| ) | |
| else: | |
| from . import scriptutils | |
| scriptutils.RunScript(par, " ".join(args[i + 1 :]), 0) | |
| return | |
| elif argType == "/app": | |
| raise RuntimeError( | |
| "/app only supported for new instances of Pythonwin.exe" | |
| ) | |
| elif argType == "/dde": # Send arbitary command | |
| if dde is not None: | |
| dde.Exec(par) | |
| else: | |
| win32ui.MessageBox( | |
| "The /dde command can only be used\r\nwhen Pythonwin is already running" | |
| ) | |
| i += 1 | |
| else: | |
| raise ValueError("Command line argument not recognised: %s" % argType) | |
| def LoadSystemModules(self): | |
| self.DoLoadModules("pywin.framework.editor,pywin.framework.stdin") | |
| def LoadUserModules(self, moduleNames=None): | |
| # Load the users modules. | |
| if moduleNames is None: | |
| default = "pywin.framework.sgrepmdi,pywin.framework.mdi_pychecker" | |
| moduleNames = win32ui.GetProfileVal("Python", "Startup Modules", default) | |
| self.DoLoadModules(moduleNames) | |
| def DoLoadModules(self, moduleNames): # ", sep string of module names. | |
| if not moduleNames: | |
| return | |
| modules = moduleNames.split(",") | |
| for module in modules: | |
| try: | |
| __import__(module) | |
| except: # Catch em all, else the app itself dies! 'ImportError: | |
| traceback.print_exc() | |
| msg = 'Startup import of user module "%s" failed' % module | |
| print(msg) | |
| win32ui.MessageBox(msg) | |
| # | |
| # DDE Callback | |
| # | |
| def OnDDECommand(self, command): | |
| try: | |
| exec(command + "\n") | |
| except: | |
| print("ERROR executing DDE command: ", command) | |
| traceback.print_exc() | |
| raise | |
| # | |
| # General handlers | |
| # | |
| def OnViewBrowse(self, id, code): | |
| "Called when ViewBrowse message is received" | |
| from pywin.tools import browser | |
| obName = dialog.GetSimpleInput("Object", "__builtins__", "Browse Python Object") | |
| if obName is None: | |
| return | |
| try: | |
| browser.Browse(eval(obName, __main__.__dict__, __main__.__dict__)) | |
| except NameError: | |
| win32ui.MessageBox("This is no object with this name") | |
| except AttributeError: | |
| win32ui.MessageBox("The object has no attribute of that name") | |
| except: | |
| traceback.print_exc() | |
| win32ui.MessageBox("This object can not be browsed") | |
| def OnFileImport(self, id, code): | |
| "Called when a FileImport message is received. Import the current or specified file" | |
| from . import scriptutils | |
| scriptutils.ImportFile() | |
| def OnFileCheck(self, id, code): | |
| "Called when a FileCheck message is received. Check the current file." | |
| from . import scriptutils | |
| scriptutils.CheckFile() | |
| def OnUpdateFileCheck(self, cmdui): | |
| from . import scriptutils | |
| cmdui.Enable(scriptutils.GetActiveFileName(0) is not None) | |
| def OnFileRun(self, id, code): | |
| "Called when a FileRun message is received." | |
| from . import scriptutils | |
| showDlg = win32api.GetKeyState(win32con.VK_SHIFT) >= 0 | |
| scriptutils.RunScript(None, None, showDlg) | |
| def OnFileLocate(self, id, code): | |
| from . import scriptutils | |
| global lastLocateFileName # save the new version away for next time... | |
| name = dialog.GetSimpleInput( | |
| "File name", lastLocateFileName, "Locate Python File" | |
| ) | |
| if name is None: # Cancelled. | |
| return | |
| lastLocateFileName = name | |
| # if ".py" supplied, rip it off! | |
| # should also check for .pys and .pyw | |
| if lastLocateFileName[-3:].lower() == ".py": | |
| lastLocateFileName = lastLocateFileName[:-3] | |
| lastLocateFileName = lastLocateFileName.replace(".", "\\") | |
| newName = scriptutils.LocatePythonFile(lastLocateFileName) | |
| if newName is None: | |
| win32ui.MessageBox("The file '%s' can not be located" % lastLocateFileName) | |
| else: | |
| win32ui.GetApp().OpenDocumentFile(newName) | |
| # Display all the "options" proprety pages we can find | |
| def OnViewOptions(self, id, code): | |
| win32ui.InitRichEdit() | |
| sheet = dialog.PropertySheet("Pythonwin Options") | |
| # Add property pages we know about that need manual work. | |
| from pywin.dialogs import ideoptions | |
| sheet.AddPage(ideoptions.OptionsPropPage()) | |
| from . import toolmenu | |
| sheet.AddPage(toolmenu.ToolMenuPropPage()) | |
| # Get other dynamic pages from templates. | |
| pages = [] | |
| for template in self.GetDocTemplateList(): | |
| try: | |
| # Dont actually call the function with the exception handler. | |
| getter = template.GetPythonPropertyPages | |
| except AttributeError: | |
| # Template does not provide property pages! | |
| continue | |
| pages = pages + getter() | |
| # Debugger template goes at the end | |
| try: | |
| from pywin.debugger import configui | |
| except ImportError: | |
| configui = None | |
| if configui is not None: | |
| pages.append(configui.DebuggerOptionsPropPage()) | |
| # Now simply add the pages, and display the dialog. | |
| for page in pages: | |
| sheet.AddPage(page) | |
| if sheet.DoModal() == win32con.IDOK: | |
| win32ui.SetStatusText("Applying configuration changes...", 1) | |
| win32ui.DoWaitCursor(1) | |
| # Tell every Window in our app that win.ini has changed! | |
| win32ui.GetMainFrame().SendMessageToDescendants( | |
| win32con.WM_WININICHANGE, 0, 0 | |
| ) | |
| win32ui.DoWaitCursor(0) | |
| def OnInteractiveWindow(self, id, code): | |
| # toggle the existing state. | |
| from . import interact | |
| interact.ToggleInteractiveWindow() | |
| def OnUpdateInteractiveWindow(self, cmdui): | |
| try: | |
| interact = sys.modules["pywin.framework.interact"] | |
| state = interact.IsInteractiveWindowVisible() | |
| except KeyError: # Interactive module hasnt ever been imported. | |
| state = 0 | |
| cmdui.Enable() | |
| cmdui.SetCheck(state) | |
| def OnFileSaveAll(self, id, code): | |
| # Only attempt to save editor documents. | |
| from pywin.framework.editor import editorTemplate | |
| num = 0 | |
| for doc in editorTemplate.GetDocumentList(): | |
| if doc.IsModified() and doc.GetPathName(): | |
| num = num = 1 | |
| doc.OnSaveDocument(doc.GetPathName()) | |
| win32ui.SetStatusText("%d documents saved" % num, 1) | |
| def OnViewToolbarDbg(self, id, code): | |
| if code == 0: | |
| return not win32ui.GetMainFrame().OnBarCheck(id) | |
| def OnUpdateViewToolbarDbg(self, cmdui): | |
| win32ui.GetMainFrame().OnUpdateControlBarMenu(cmdui) | |
| cmdui.Enable(1) | |
| def OnHelpIndex(self, id, code): | |
| from . import help | |
| help.SelectAndRunHelpFile() | |
| # As per the comments in app.py, this use is depreciated. | |
| # app.AppBuilder = InteractivePythonApp | |
| # Now all we do is create the application | |
| thisApp = InteractivePythonApp() | |