123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477 |
- # intpyapp.py - Interactive Python application class
- #
- import win32con
- import win32api
- import win32ui
- import __main__
- import sys
- import os
- from . import app
- import traceback
- from pywin.mfc import afxres, dialog
- import commctrl
- from . import 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()
|