codekingpro/portable-devtools
115k
1# intpyapp.py - Interactive Python application class2#3import os4import sys5import traceback6 7import __main__8import commctrl9import win32api10import win32con11import win32ui12from pywin.mfc import afxres, dialog13 14from . import app, dbgcommands15 16lastLocateFileName = ".py" # used in the "File/Locate" dialog...17 18 19# todo - _SetupSharedMenu should be moved to a framework class.20def _SetupSharedMenu_(self):21 sharedMenu = self.GetSharedMenu()22 from pywin.framework import toolmenu23 24 toolmenu.SetToolsMenu(sharedMenu)25 from pywin.framework import help26 27 help.SetHelpMenuOtherHelp(sharedMenu)28 29 30from pywin.mfc import docview31 32docview.DocTemplate._SetupSharedMenu_ = _SetupSharedMenu_33 34 35class MainFrame(app.MainFrame):36 def OnCreate(self, createStruct):37 self.closing = 038 if app.MainFrame.OnCreate(self, createStruct) == -1:39 return -140 style = (41 win32con.WS_CHILD42 | afxres.CBRS_SIZE_DYNAMIC43 | afxres.CBRS_TOP44 | afxres.CBRS_TOOLTIPS45 | afxres.CBRS_FLYBY46 )47 48 self.EnableDocking(afxres.CBRS_ALIGN_ANY)49 50 tb = win32ui.CreateToolBar(self, style | win32con.WS_VISIBLE)51 tb.ModifyStyle(0, commctrl.TBSTYLE_FLAT)52 tb.LoadToolBar(win32ui.IDR_MAINFRAME)53 tb.EnableDocking(afxres.CBRS_ALIGN_ANY)54 tb.SetWindowText("Standard")55 self.DockControlBar(tb)56 # Any other packages which use toolbars57 from pywin.debugger.debugger import PrepareControlBars58 59 PrepareControlBars(self)60 # Note "interact" also uses dockable windows, but they already happen61 62 # And a "Tools" menu on the main frame.63 menu = self.GetMenu()64 from . import toolmenu65 66 toolmenu.SetToolsMenu(menu, 2)67 # And fix the "Help" menu on the main frame68 from pywin.framework import help69 70 help.SetHelpMenuOtherHelp(menu)71 72 def OnClose(self):73 try:74 import pywin.debugger75 76 if (77 pywin.debugger.currentDebugger is not None78 and pywin.debugger.currentDebugger.pumping79 ):80 try:81 pywin.debugger.currentDebugger.close(1)82 except:83 traceback.print_exc()84 return85 except win32ui.error:86 pass87 self.closing = 188 self.SaveBarState("ToolbarDefault")89 self.SetActiveView(None) # Otherwise MFC's OnClose may _not_ prompt for save.90 91 from pywin.framework import help92 93 help.FinalizeHelp()94 95 self.DestroyControlBar(afxres.AFX_IDW_TOOLBAR)96 self.DestroyControlBar(win32ui.ID_VIEW_TOOLBAR_DBG)97 98 return self._obj_.OnClose()99 100 def DestroyControlBar(self, id):101 try:102 bar = self.GetControlBar(id)103 except win32ui.error:104 return105 bar.DestroyWindow()106 107 def OnCommand(self, wparam, lparam):108 # By default, the current MDI child frame will process WM_COMMAND109 # messages before any docked control bars - even if the control bar110 # has focus. This is a problem for the interactive window when docked.111 # Therefore, we detect the situation of a view having the main frame112 # as its parent, and assume it must be a docked view (which it will in an MDI app)113 try:114 v = (115 self.GetActiveView()116 ) # Raise an exception if none - good - then we want default handling117 # Main frame _does_ have a current view (ie, a docking view) - see if it wants it.118 if v.OnCommand(wparam, lparam):119 return 1120 except (win32ui.error, AttributeError):121 pass122 return self._obj_.OnCommand(wparam, lparam)123 124 125class InteractivePythonApp(app.CApp):126 # This works if necessary - just we dont need to override the Run method.127 # def Run(self):128 # return self._obj_.Run()129 130 def HookCommands(self):131 app.CApp.HookCommands(self)132 dbgcommands.DebuggerCommandHandler().HookCommands()133 self.HookCommand(self.OnViewBrowse, win32ui.ID_VIEW_BROWSE)134 self.HookCommand(self.OnFileImport, win32ui.ID_FILE_IMPORT)135 self.HookCommand(self.OnFileCheck, win32ui.ID_FILE_CHECK)136 self.HookCommandUpdate(self.OnUpdateFileCheck, win32ui.ID_FILE_CHECK)137 self.HookCommand(self.OnFileRun, win32ui.ID_FILE_RUN)138 self.HookCommand(self.OnFileLocate, win32ui.ID_FILE_LOCATE)139 self.HookCommand(self.OnInteractiveWindow, win32ui.ID_VIEW_INTERACTIVE)140 self.HookCommandUpdate(141 self.OnUpdateInteractiveWindow, win32ui.ID_VIEW_INTERACTIVE142 )143 self.HookCommand(self.OnViewOptions, win32ui.ID_VIEW_OPTIONS)144 self.HookCommand(self.OnHelpIndex, afxres.ID_HELP_INDEX)145 self.HookCommand(self.OnFileSaveAll, win32ui.ID_FILE_SAVE_ALL)146 self.HookCommand(self.OnViewToolbarDbg, win32ui.ID_VIEW_TOOLBAR_DBG)147 self.HookCommandUpdate(self.OnUpdateViewToolbarDbg, win32ui.ID_VIEW_TOOLBAR_DBG)148 149 def CreateMainFrame(self):150 return MainFrame()151 152 def MakeExistingDDEConnection(self):153 # Use DDE to connect to an existing instance154 # Return None if no existing instance155 try:156 from . import intpydde157 except ImportError:158 # No dde support!159 return None160 conv = intpydde.CreateConversation(self.ddeServer)161 try:162 conv.ConnectTo("Pythonwin", "System")163 return conv164 except intpydde.error:165 return None166 167 def InitDDE(self):168 # Do all the magic DDE handling.169 # Returns TRUE if we have pumped the arguments to our170 # remote DDE app, and we should terminate.171 try:172 from . import intpydde173 except ImportError:174 self.ddeServer = None175 intpydde = None176 if intpydde is not None:177 self.ddeServer = intpydde.DDEServer(self)178 self.ddeServer.Create("Pythonwin", intpydde.CBF_FAIL_SELFCONNECTIONS)179 try:180 # If there is an existing instance, pump the arguments to it.181 connection = self.MakeExistingDDEConnection()182 if connection is not None:183 connection.Exec("self.Activate()")184 if self.ProcessArgs(sys.argv, connection) is None:185 return 1186 except:187 # It is too early to 'print' an exception - we188 # don't have stdout setup yet!189 win32ui.DisplayTraceback(190 sys.exc_info(), " - error in DDE conversation with Pythonwin"191 )192 return 1193 194 def InitInstance(self):195 # Allow "/nodde" and "/new" to optimize this!196 if (197 "/nodde" not in sys.argv198 and "/new" not in sys.argv199 and "-nodde" not in sys.argv200 and "-new" not in sys.argv201 ):202 if self.InitDDE():203 return 1 # A remote DDE client is doing it for us!204 else:205 self.ddeServer = None206 207 win32ui.SetRegistryKey(208 "Python %s" % (sys.winver,)209 ) # MFC automatically puts the main frame caption on!210 app.CApp.InitInstance(self)211 212 # Create the taskbar icon213 win32ui.CreateDebuggerThread()214 215 # Allow Pythonwin to host OCX controls.216 win32ui.EnableControlContainer()217 218 # Display the interactive window if the user wants it.219 from . import interact220 221 interact.CreateInteractiveWindowUserPreference()222 223 # Load the modules we use internally.224 self.LoadSystemModules()225 226 # Load additional module the user may want.227 self.LoadUserModules()228 229 # Load the ToolBar state near the end of the init process, as230 # there may be Toolbar IDs created by the user or other modules.231 # By now all these modules should be loaded, so all the toolbar IDs loaded.232 try:233 self.frame.LoadBarState("ToolbarDefault")234 except win32ui.error:235 # MFC sucks. It does essentially "GetDlgItem(x)->Something", so if the236 # toolbar with ID x does not exist, MFC crashes! Pythonwin has a trap for this237 # but I need to investigate more how to prevent it (AFAIK, ensuring all the238 # toolbars are created by now _should_ stop it!)239 pass240 241 # Finally process the command line arguments.242 try:243 self.ProcessArgs(sys.argv)244 except:245 # too early for printing anything.246 win32ui.DisplayTraceback(247 sys.exc_info(), " - error processing command line args"248 )249 250 def ExitInstance(self):251 win32ui.DestroyDebuggerThread()252 try:253 from . import interact254 255 interact.DestroyInteractiveWindow()256 except:257 pass258 if self.ddeServer is not None:259 self.ddeServer.Shutdown()260 self.ddeServer = None261 return app.CApp.ExitInstance(self)262 263 def Activate(self):264 # Bring to the foreground. Mainly used when another app starts up, it asks265 # this one to activate itself, then it terminates.266 frame = win32ui.GetMainFrame()267 frame.SetForegroundWindow()268 if frame.GetWindowPlacement()[1] == win32con.SW_SHOWMINIMIZED:269 frame.ShowWindow(win32con.SW_RESTORE)270 271 def ProcessArgs(self, args, dde=None):272 # If we are going to talk to a remote app via DDE, then273 # activate it!274 if (275 len(args) < 1 or not args[0]276 ): # argv[0]=='' when started without args, just like Python.exe!277 return278 279 i = 0280 while i < len(args):281 argType = args[i]282 i += 1283 if argType.startswith("-"):284 # Support dash options. Slash options are misinterpreted by python init285 # as path and not finding usually 'C:\\' ends up in sys.path[0]286 argType = "/" + argType[1:]287 if not argType.startswith("/"):288 argType = win32ui.GetProfileVal(289 "Python", "Default Arg Type", "/edit"290 ).lower()291 i -= 1 # arg is /edit's parameter292 par = i < len(args) and args[i] or "MISSING"293 if argType in ("/nodde", "/new", "-nodde", "-new"):294 # Already handled295 pass296 elif argType.startswith("/goto:"):297 gotoline = int(argType[len("/goto:") :])298 if dde:299 dde.Exec(300 "from pywin.framework import scriptutils\n"301 "ed = scriptutils.GetActiveEditControl()\n"302 "if ed: ed.SetSel(ed.LineIndex(%s - 1))" % gotoline303 )304 else:305 from . import scriptutils306 307 ed = scriptutils.GetActiveEditControl()308 if ed:309 ed.SetSel(ed.LineIndex(gotoline - 1))310 elif argType == "/edit":311 # Load up the default application.312 i += 1313 fname = win32api.GetFullPathName(par)314 if not os.path.isfile(fname):315 # if we don't catch this, OpenDocumentFile() (actually316 # PyCDocument.SetPathName() in317 # pywin.scintilla.document.CScintillaDocument.OnOpenDocument)318 # segfaults Pythonwin on recent PY3 builds (b228)319 win32ui.MessageBox(320 "No such file: %s\n\nCommand Line: %s"321 % (fname, win32api.GetCommandLine()),322 "Open file for edit",323 win32con.MB_ICONERROR,324 )325 continue326 if dde:327 dde.Exec("win32ui.GetApp().OpenDocumentFile(%s)" % (repr(fname)))328 else:329 win32ui.GetApp().OpenDocumentFile(par)330 elif argType == "/rundlg":331 if dde:332 dde.Exec(333 "from pywin.framework import scriptutils;scriptutils.RunScript(%r, %r, 1)"334 % (par, " ".join(args[i + 1 :]))335 )336 else:337 from . import scriptutils338 339 scriptutils.RunScript(par, " ".join(args[i + 1 :]))340 return341 elif argType == "/run":342 if dde:343 dde.Exec(344 "from pywin.framework import scriptutils;scriptutils.RunScript(%r, %r, 0)"345 % (par, " ".join(args[i + 1 :]))346 )347 else:348 from . import scriptutils349 350 scriptutils.RunScript(par, " ".join(args[i + 1 :]), 0)351 return352 elif argType == "/app":353 raise RuntimeError(354 "/app only supported for new instances of Pythonwin.exe"355 )356 elif argType == "/dde": # Send arbitary command357 if dde is not None:358 dde.Exec(par)359 else:360 win32ui.MessageBox(361 "The /dde command can only be used\r\nwhen Pythonwin is already running"362 )363 i += 1364 else:365 raise ValueError("Command line argument not recognised: %s" % argType)366 367 def LoadSystemModules(self):368 self.DoLoadModules("pywin.framework.editor,pywin.framework.stdin")369 370 def LoadUserModules(self, moduleNames=None):371 # Load the users modules.372 if moduleNames is None:373 default = "pywin.framework.sgrepmdi,pywin.framework.mdi_pychecker"374 moduleNames = win32ui.GetProfileVal("Python", "Startup Modules", default)375 self.DoLoadModules(moduleNames)376 377 def DoLoadModules(self, moduleNames): # ", sep string of module names.378 if not moduleNames:379 return380 modules = moduleNames.split(",")381 for module in modules:382 try:383 __import__(module)384 except: # Catch em all, else the app itself dies! 'ImportError:385 traceback.print_exc()386 msg = 'Startup import of user module "%s" failed' % module387 print(msg)388 win32ui.MessageBox(msg)389 390 #391 # DDE Callback392 #393 def OnDDECommand(self, command):394 try:395 exec(command + "\n")396 except:397 print("ERROR executing DDE command: ", command)398 traceback.print_exc()399 raise400 401 #402 # General handlers403 #404 def OnViewBrowse(self, id, code):405 "Called when ViewBrowse message is received"406 from pywin.tools import browser407 408 obName = dialog.GetSimpleInput("Object", "__builtins__", "Browse Python Object")409 if obName is None:410 return411 try:412 browser.Browse(eval(obName, __main__.__dict__, __main__.__dict__))413 except NameError:414 win32ui.MessageBox("This is no object with this name")415 except AttributeError:416 win32ui.MessageBox("The object has no attribute of that name")417 except:418 traceback.print_exc()419 win32ui.MessageBox("This object can not be browsed")420 421 def OnFileImport(self, id, code):422 "Called when a FileImport message is received. Import the current or specified file"423 from . import scriptutils424 425 scriptutils.ImportFile()426 427 def OnFileCheck(self, id, code):428 "Called when a FileCheck message is received. Check the current file."429 from . import scriptutils430 431 scriptutils.CheckFile()432 433 def OnUpdateFileCheck(self, cmdui):434 from . import scriptutils435 436 cmdui.Enable(scriptutils.GetActiveFileName(0) is not None)437 438 def OnFileRun(self, id, code):439 "Called when a FileRun message is received."440 from . import scriptutils441 442 showDlg = win32api.GetKeyState(win32con.VK_SHIFT) >= 0443 scriptutils.RunScript(None, None, showDlg)444 445 def OnFileLocate(self, id, code):446 from . import scriptutils447 448 global lastLocateFileName # save the new version away for next time...449 450 name = dialog.GetSimpleInput(451 "File name", lastLocateFileName, "Locate Python File"452 )453 if name is None: # Cancelled.454 return455 lastLocateFileName = name456 # if ".py" supplied, rip it off!457 # should also check for .pys and .pyw458 if lastLocateFileName[-3:].lower() == ".py":459 lastLocateFileName = lastLocateFileName[:-3]460 lastLocateFileName = lastLocateFileName.replace(".", "\\")461 newName = scriptutils.LocatePythonFile(lastLocateFileName)462 if newName is None:463 win32ui.MessageBox("The file '%s' can not be located" % lastLocateFileName)464 else:465 win32ui.GetApp().OpenDocumentFile(newName)466 467 # Display all the "options" proprety pages we can find468 def OnViewOptions(self, id, code):469 win32ui.InitRichEdit()470 sheet = dialog.PropertySheet("Pythonwin Options")471 # Add property pages we know about that need manual work.472 from pywin.dialogs import ideoptions473 474 sheet.AddPage(ideoptions.OptionsPropPage())475 476 from . import toolmenu477 478 sheet.AddPage(toolmenu.ToolMenuPropPage())479 480 # Get other dynamic pages from templates.481 pages = []482 for template in self.GetDocTemplateList():483 try:484 # Dont actually call the function with the exception handler.485 getter = template.GetPythonPropertyPages486 except AttributeError:487 # Template does not provide property pages!488 continue489 pages = pages + getter()490 491 # Debugger template goes at the end492 try:493 from pywin.debugger import configui494 except ImportError:495 configui = None496 if configui is not None:497 pages.append(configui.DebuggerOptionsPropPage())498 # Now simply add the pages, and display the dialog.499 for page in pages:500 sheet.AddPage(page)501 502 if sheet.DoModal() == win32con.IDOK:503 win32ui.SetStatusText("Applying configuration changes...", 1)504 win32ui.DoWaitCursor(1)505 # Tell every Window in our app that win.ini has changed!506 win32ui.GetMainFrame().SendMessageToDescendants(507 win32con.WM_WININICHANGE, 0, 0508 )509 win32ui.DoWaitCursor(0)510 511 def OnInteractiveWindow(self, id, code):512 # toggle the existing state.513 from . import interact514 515 interact.ToggleInteractiveWindow()516 517 def OnUpdateInteractiveWindow(self, cmdui):518 try:519 interact = sys.modules["pywin.framework.interact"]520 state = interact.IsInteractiveWindowVisible()521 except KeyError: # Interactive module hasnt ever been imported.522 state = 0523 cmdui.Enable()524 cmdui.SetCheck(state)525 526 def OnFileSaveAll(self, id, code):527 # Only attempt to save editor documents.528 from pywin.framework.editor import editorTemplate529 530 num = 0531 for doc in editorTemplate.GetDocumentList():532 if doc.IsModified() and doc.GetPathName():533 num = num = 1534 doc.OnSaveDocument(doc.GetPathName())535 win32ui.SetStatusText("%d documents saved" % num, 1)536 537 def OnViewToolbarDbg(self, id, code):538 if code == 0:539 return not win32ui.GetMainFrame().OnBarCheck(id)540 541 def OnUpdateViewToolbarDbg(self, cmdui):542 win32ui.GetMainFrame().OnUpdateControlBarMenu(cmdui)543 cmdui.Enable(1)544 545 def OnHelpIndex(self, id, code):546 from . import help547 548 help.SelectAndRunHelpFile()549 550 551# As per the comments in app.py, this use is depreciated.552# app.AppBuilder = InteractivePythonApp553 554# Now all we do is create the application555thisApp = InteractivePythonApp()556 