intpyapp.py 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447
  1. # intpyapp.py - Interactive Python application class
  2. #
  3. import win32con
  4. import win32api
  5. import win32ui
  6. import __main__
  7. import sys
  8. import string
  9. import app
  10. import traceback
  11. from pywin.mfc import window, afxres, dialog
  12. import commctrl
  13. import dbgcommands
  14. lastLocateFileName = ".py" # used in the "File/Locate" dialog...
  15. # todo - _SetupSharedMenu should be moved to a framework class.
  16. def _SetupSharedMenu_(self):
  17. sharedMenu = self.GetSharedMenu()
  18. from pywin.framework import toolmenu
  19. toolmenu.SetToolsMenu(sharedMenu)
  20. from pywin.framework import help
  21. help.SetHelpMenuOtherHelp(sharedMenu)
  22. from pywin.mfc import docview
  23. docview.DocTemplate._SetupSharedMenu_=_SetupSharedMenu_
  24. class MainFrame(app.MainFrame):
  25. def OnCreate(self, createStruct):
  26. self.closing = 0
  27. if app.MainFrame.OnCreate(self, createStruct)==-1:
  28. return -1
  29. style = win32con.WS_CHILD | afxres.CBRS_SIZE_DYNAMIC | afxres.CBRS_TOP | afxres.CBRS_TOOLTIPS | afxres.CBRS_FLYBY
  30. self.EnableDocking(afxres.CBRS_ALIGN_ANY)
  31. tb = win32ui.CreateToolBar (self, style | win32con.WS_VISIBLE)
  32. tb.ModifyStyle(0, commctrl.TBSTYLE_FLAT)
  33. tb.LoadToolBar(win32ui.IDR_MAINFRAME)
  34. tb.EnableDocking(afxres.CBRS_ALIGN_ANY)
  35. tb.SetWindowText("Standard")
  36. self.DockControlBar(tb)
  37. # Any other packages which use toolbars
  38. from pywin.debugger.debugger import PrepareControlBars
  39. PrepareControlBars(self)
  40. # Note "interact" also uses dockable windows, but they already happen
  41. # And a "Tools" menu on the main frame.
  42. menu = self.GetMenu()
  43. import toolmenu
  44. toolmenu.SetToolsMenu(menu, 2)
  45. # And fix the "Help" menu on the main frame
  46. from pywin.framework import help
  47. help.SetHelpMenuOtherHelp(menu)
  48. def OnClose(self):
  49. try:
  50. import pywin.debugger
  51. if pywin.debugger.currentDebugger is not None and pywin.debugger.currentDebugger.pumping:
  52. try:
  53. pywin.debugger.currentDebugger.close(1)
  54. except:
  55. traceback.print_exc()
  56. return
  57. except win32ui.error:
  58. pass
  59. self.closing = 1
  60. self.SaveBarState("ToolbarDefault")
  61. self.SetActiveView(None) # Otherwise MFC's OnClose may _not_ prompt for save.
  62. from pywin.framework import help
  63. help.FinalizeHelp()
  64. self.DestroyControlBar(afxres.AFX_IDW_TOOLBAR)
  65. self.DestroyControlBar(win32ui.ID_VIEW_TOOLBAR_DBG)
  66. return self._obj_.OnClose()
  67. def DestroyControlBar(self, id):
  68. try:
  69. bar = self.GetControlBar(id)
  70. except win32ui.error:
  71. return
  72. bar.DestroyWindow()
  73. def OnCommand(self, wparam, lparam):
  74. # By default, the current MDI child frame will process WM_COMMAND
  75. # messages before any docked control bars - even if the control bar
  76. # has focus. This is a problem for the interactive window when docked.
  77. # Therefore, we detect the situation of a view having the main frame
  78. # as its parent, and assume it must be a docked view (which it will in an MDI app)
  79. try:
  80. v = self.GetActiveView() # Raise an exception if none - good - then we want default handling
  81. # Main frame _does_ have a current view (ie, a docking view) - see if it wants it.
  82. if v.OnCommand(wparam, lparam):
  83. return 1
  84. except (win32ui.error, AttributeError):
  85. pass
  86. return self._obj_.OnCommand(wparam, lparam)
  87. class InteractivePythonApp(app.CApp):
  88. # This works if necessary - just we dont need to override the Run method.
  89. # def Run(self):
  90. # return self._obj_.Run()
  91. def HookCommands(self):
  92. app.CApp.HookCommands(self)
  93. dbgcommands.DebuggerCommandHandler().HookCommands()
  94. self.HookCommand(self.OnViewBrowse,win32ui.ID_VIEW_BROWSE)
  95. self.HookCommand(self.OnFileImport,win32ui.ID_FILE_IMPORT)
  96. self.HookCommand(self.OnFileCheck,win32ui.ID_FILE_CHECK)
  97. self.HookCommandUpdate(self.OnUpdateFileCheck, win32ui.ID_FILE_CHECK)
  98. self.HookCommand(self.OnFileRun,win32ui.ID_FILE_RUN)
  99. self.HookCommand(self.OnFileLocate,win32ui.ID_FILE_LOCATE)
  100. self.HookCommand(self.OnInteractiveWindow, win32ui.ID_VIEW_INTERACTIVE)
  101. self.HookCommandUpdate(self.OnUpdateInteractiveWindow, win32ui.ID_VIEW_INTERACTIVE)
  102. self.HookCommand(self.OnViewOptions, win32ui.ID_VIEW_OPTIONS)
  103. self.HookCommand(self.OnHelpIndex, afxres.ID_HELP_INDEX)
  104. self.HookCommand(self.OnFileSaveAll, win32ui.ID_FILE_SAVE_ALL)
  105. self.HookCommand(self.OnViewToolbarDbg, win32ui.ID_VIEW_TOOLBAR_DBG)
  106. self.HookCommandUpdate(self.OnUpdateViewToolbarDbg, win32ui.ID_VIEW_TOOLBAR_DBG)
  107. def CreateMainFrame(self):
  108. return MainFrame()
  109. def MakeExistingDDEConnection(self):
  110. # Use DDE to connect to an existing instance
  111. # Return None if no existing instance
  112. try:
  113. import intpydde
  114. except ImportError:
  115. # No dde support!
  116. return None
  117. conv = intpydde.CreateConversation(self.ddeServer)
  118. try:
  119. conv.ConnectTo("Pythonwin", "System")
  120. return conv
  121. except intpydde.error:
  122. return None
  123. def InitDDE(self):
  124. # Do all the magic DDE handling.
  125. # Returns TRUE if we have pumped the arguments to our
  126. # remote DDE app, and we should terminate.
  127. try:
  128. import intpydde
  129. except ImportError:
  130. self.ddeServer = None
  131. intpydde = None
  132. if intpydde is not None:
  133. self.ddeServer = intpydde.DDEServer(self)
  134. self.ddeServer.Create("Pythonwin", intpydde.CBF_FAIL_SELFCONNECTIONS )
  135. try:
  136. # If there is an existing instance, pump the arguments to it.
  137. connection = self.MakeExistingDDEConnection()
  138. if connection is not None:
  139. if self.ProcessArgs(sys.argv, connection) is None:
  140. return 1
  141. except:
  142. # It is too early to 'print' an exception - we
  143. # don't have stdout setup yet!
  144. win32ui.DisplayTraceback(sys.exc_info(), " - error in DDE conversation with Pythonwin")
  145. def InitInstance(self):
  146. # Allow "/nodde" and "/new" to optimize this!
  147. if "/nodde" not in sys.argv and "/new" not in sys.argv:
  148. if self.InitDDE():
  149. return 1 # A remote DDE client is doing it for us!
  150. else:
  151. self.ddeServer = None
  152. win32ui.SetRegistryKey("Python %s" % (sys.winver,)) # MFC automatically puts the main frame caption on!
  153. app.CApp.InitInstance(self)
  154. # Create the taskbar icon
  155. win32ui.CreateDebuggerThread()
  156. # Allow Pythonwin to host OCX controls.
  157. win32ui.EnableControlContainer()
  158. # Display the interactive window if the user wants it.
  159. import interact
  160. interact.CreateInteractiveWindowUserPreference()
  161. # Load the modules we use internally.
  162. self.LoadSystemModules()
  163. # Load additional module the user may want.
  164. self.LoadUserModules()
  165. # Load the ToolBar state near the end of the init process, as
  166. # there may be Toolbar IDs created by the user or other modules.
  167. # By now all these modules should be loaded, so all the toolbar IDs loaded.
  168. try:
  169. self.frame.LoadBarState("ToolbarDefault")
  170. except win32ui.error:
  171. # MFC sucks. It does essentially "GetDlgItem(x)->Something", so if the
  172. # toolbar with ID x does not exist, MFC crashes! Pythonwin has a trap for this
  173. # but I need to investigate more how to prevent it (AFAIK, ensuring all the
  174. # toolbars are created by now _should_ stop it!)
  175. pass
  176. # Finally process the command line arguments.
  177. self.ProcessArgs(sys.argv)
  178. def ExitInstance(self):
  179. win32ui.DestroyDebuggerThread()
  180. try:
  181. import interact
  182. interact.DestroyInteractiveWindow()
  183. except:
  184. pass
  185. if self.ddeServer is not None:
  186. self.ddeServer.Shutdown()
  187. self.ddeServer = None
  188. return app.CApp.ExitInstance(self)
  189. def Activate(self):
  190. # Bring to the foreground. Mainly used when another app starts up, it asks
  191. # this one to activate itself, then it terminates.
  192. frame = win32ui.GetMainFrame()
  193. frame.SetForegroundWindow()
  194. if frame.GetWindowPlacement()[1]==win32con.SW_SHOWMINIMIZED:
  195. frame.ShowWindow(win32con.SW_RESTORE)
  196. def ProcessArgs(self, args, dde = None):
  197. # If we are going to talk to a remote app via DDE, then
  198. # activate it!
  199. if dde is not None: dde.Exec("self.Activate()")
  200. if len(args) and args[0] in ['/nodde','/new']: del args[0] # already handled.
  201. if len(args)<1 or not args[0]: # argv[0]=='' when started without args, just like Python.exe!
  202. return
  203. try:
  204. if args[0] and args[0][0]!='/':
  205. argStart = 0
  206. argType = win32ui.GetProfileVal("Python","Default Arg Type","/edit").lower()
  207. else:
  208. argStart = 1
  209. argType = args[0]
  210. if argStart >= len(args):
  211. raise TypeError("The command line requires an additional arg.")
  212. if argType=="/edit":
  213. # Load up the default application.
  214. if dde:
  215. fname = win32api.GetFullPathName(args[argStart])
  216. dde.Exec("win32ui.GetApp().OpenDocumentFile(%s)" % (repr(fname)))
  217. else:
  218. win32ui.GetApp().OpenDocumentFile(args[argStart])
  219. elif argType=="/rundlg":
  220. if dde:
  221. dde.Exec("from pywin.framework import scriptutils;scriptutils.RunScript('%s', '%s', 1)" % (args[argStart], ' '.join(args[argStart+1:])))
  222. else:
  223. import scriptutils
  224. scriptutils.RunScript(args[argStart], ' '.join(args[argStart+1:]))
  225. elif argType=="/run":
  226. if dde:
  227. dde.Exec("from pywin.framework import scriptutils;scriptutils.RunScript('%s', '%s', 0)" % (args[argStart], ' '.join(args[argStart+1:])))
  228. else:
  229. import scriptutils
  230. scriptutils.RunScript(args[argStart], ' '.join(args[argStart+1:]), 0)
  231. elif argType=="/app":
  232. raise RuntimeError("/app only supported for new instances of Pythonwin.exe")
  233. elif argType=='/dde': # Send arbitary command
  234. if dde is not None:
  235. dde.Exec(args[argStart])
  236. else:
  237. win32ui.MessageBox("The /dde command can only be used\r\nwhen Pythonwin is already running")
  238. else:
  239. raise TypeError("Command line arguments not recognised")
  240. except:
  241. # too early for print anything.
  242. win32ui.DisplayTraceback(sys.exc_info(), " - error processing command line args")
  243. def LoadSystemModules(self):
  244. self.DoLoadModules("pywin.framework.editor,pywin.framework.stdin")
  245. def LoadUserModules(self, moduleNames = None):
  246. # Load the users modules.
  247. if moduleNames is None:
  248. default = "pywin.framework.sgrepmdi,pywin.framework.mdi_pychecker"
  249. moduleNames=win32ui.GetProfileVal('Python','Startup Modules',default)
  250. self.DoLoadModules(moduleNames)
  251. def DoLoadModules(self, moduleNames): # ", sep string of module names.
  252. if not moduleNames: return
  253. modules = moduleNames.split(",")
  254. for module in modules:
  255. try:
  256. __import__(module)
  257. except: # Catch em all, else the app itself dies! 'ImportError:
  258. traceback.print_exc()
  259. msg = 'Startup import of user module "%s" failed' % module
  260. print msg
  261. win32ui.MessageBox(msg)
  262. #
  263. # DDE Callback
  264. #
  265. def OnDDECommand(self, command):
  266. try:
  267. exec command + "\n"
  268. except:
  269. print "ERROR executing DDE command: ", command
  270. traceback.print_exc()
  271. raise
  272. #
  273. # General handlers
  274. #
  275. def OnViewBrowse( self, id, code ):
  276. " Called when ViewBrowse message is received "
  277. from pywin.mfc import dialog
  278. from pywin.tools import browser
  279. obName = dialog.GetSimpleInput('Object', '__builtins__', 'Browse Python Object')
  280. if obName is None:
  281. return
  282. try:
  283. browser.Browse(eval(obName, __main__.__dict__, __main__.__dict__))
  284. except NameError:
  285. win32ui.MessageBox('This is no object with this name')
  286. except AttributeError:
  287. win32ui.MessageBox('The object has no attribute of that name')
  288. except:
  289. traceback.print_exc()
  290. win32ui.MessageBox('This object can not be browsed')
  291. def OnFileImport( self, id, code ):
  292. " Called when a FileImport message is received. Import the current or specified file"
  293. import scriptutils
  294. scriptutils.ImportFile()
  295. def OnFileCheck( self, id, code ):
  296. " Called when a FileCheck message is received. Check the current file."
  297. import scriptutils
  298. scriptutils.CheckFile()
  299. def OnUpdateFileCheck(self, cmdui):
  300. import scriptutils
  301. cmdui.Enable( scriptutils.GetActiveFileName(0) is not None )
  302. def OnFileRun( self, id, code ):
  303. " Called when a FileRun message is received. "
  304. import scriptutils
  305. showDlg = win32api.GetKeyState(win32con.VK_SHIFT) >= 0
  306. scriptutils.RunScript(None, None, showDlg)
  307. def OnFileLocate( self, id, code ):
  308. from pywin.mfc import dialog
  309. import scriptutils
  310. import os
  311. global lastLocateFileName # save the new version away for next time...
  312. name = dialog.GetSimpleInput('File name', lastLocateFileName, 'Locate Python File')
  313. if name is None: # Cancelled.
  314. return
  315. lastLocateFileName = name
  316. # if ".py" supplied, rip it off!
  317. # should also check for .pys and .pyw
  318. if lastLocateFileName[-3:].lower()=='.py':
  319. lastLocateFileName = lastLocateFileName[:-3]
  320. lastLocateFileName = lastLocateFileName.replace(".","\\")
  321. newName = scriptutils.LocatePythonFile(lastLocateFileName)
  322. if newName is None:
  323. win32ui.MessageBox("The file '%s' can not be located" % lastLocateFileName)
  324. else:
  325. win32ui.GetApp().OpenDocumentFile(newName)
  326. # Display all the "options" proprety pages we can find
  327. def OnViewOptions(self, id, code):
  328. win32ui.InitRichEdit()
  329. sheet = dialog.PropertySheet("Pythonwin Options")
  330. # Add property pages we know about that need manual work.
  331. from pywin.dialogs import ideoptions
  332. sheet.AddPage( ideoptions.OptionsPropPage() )
  333. import toolmenu
  334. sheet.AddPage( toolmenu.ToolMenuPropPage() )
  335. # Get other dynamic pages from templates.
  336. pages = []
  337. for template in self.GetDocTemplateList():
  338. try:
  339. # Dont actually call the function with the exception handler.
  340. getter = template.GetPythonPropertyPages
  341. except AttributeError:
  342. # Template does not provide property pages!
  343. continue
  344. pages = pages + getter()
  345. # Debugger template goes at the end
  346. try:
  347. from pywin.debugger import configui
  348. except ImportError:
  349. configui = None
  350. if configui is not None: pages.append(configui.DebuggerOptionsPropPage())
  351. # Now simply add the pages, and display the dialog.
  352. for page in pages:
  353. sheet.AddPage(page)
  354. if sheet.DoModal()==win32con.IDOK:
  355. win32ui.SetStatusText("Applying configuration changes...", 1)
  356. win32ui.DoWaitCursor(1)
  357. # Tell every Window in our app that win.ini has changed!
  358. win32ui.GetMainFrame().SendMessageToDescendants(win32con.WM_WININICHANGE, 0, 0)
  359. win32ui.DoWaitCursor(0)
  360. def OnInteractiveWindow(self, id, code):
  361. # toggle the existing state.
  362. import interact
  363. interact.ToggleInteractiveWindow()
  364. def OnUpdateInteractiveWindow(self, cmdui):
  365. try:
  366. interact=sys.modules['pywin.framework.interact']
  367. state = interact.IsInteractiveWindowVisible()
  368. except KeyError: # Interactive module hasnt ever been imported.
  369. state = 0
  370. cmdui.Enable()
  371. cmdui.SetCheck(state)
  372. def OnFileSaveAll(self, id, code):
  373. # Only attempt to save editor documents.
  374. from pywin.framework.editor import editorTemplate
  375. num = 0
  376. for doc in editorTemplate.GetDocumentList():
  377. if doc.IsModified() and doc.GetPathName():
  378. num = num = 1
  379. doc.OnSaveDocument(doc.GetPathName())
  380. win32ui.SetStatusText("%d documents saved" % num, 1)
  381. def OnViewToolbarDbg(self, id, code):
  382. if code==0:
  383. return not win32ui.GetMainFrame().OnBarCheck(id)
  384. def OnUpdateViewToolbarDbg(self, cmdui):
  385. win32ui.GetMainFrame().OnUpdateControlBarMenu(cmdui)
  386. cmdui.Enable(1)
  387. def OnHelpIndex( self, id, code ):
  388. import help
  389. help.SelectAndRunHelpFile()
  390. # As per the comments in app.py, this use is depreciated.
  391. # app.AppBuilder = InteractivePythonApp
  392. # Now all we do is create the application
  393. thisApp = InteractivePythonApp()