codekingpro/portable-devtools
114k
1# App.py2# Application stuff.3# The application is responsible for managing the main frame window.4#5# We also grab the FileOpen command, to invoke our Python editor6" The PythonWin application code. Manages most aspects of MDI, etc "7import os8import sys9import traceback10 11import regutil12import win32api13import win32con14import win32ui15from pywin.mfc import afxres, dialog, window16from pywin.mfc.thread import WinApp17 18from . import scriptutils19 20## NOTE: App and AppBuild should NOT be used - instead, you should contruct your21## APP class manually whenever you like (just ensure you leave these 2 params None!)22## Whoever wants the generic "Application" should get it via win32iu.GetApp()23 24# These are "legacy"25AppBuilder = None26App = None # default - if used, must end up a CApp derived class.27 28 29# Helpers that should one day be removed!30def AddIdleHandler(handler):31 print(32 "app.AddIdleHandler is deprecated - please use win32ui.GetApp().AddIdleHandler() instead."33 )34 return win32ui.GetApp().AddIdleHandler(handler)35 36 37def DeleteIdleHandler(handler):38 print(39 "app.DeleteIdleHandler is deprecated - please use win32ui.GetApp().DeleteIdleHandler() instead."40 )41 return win32ui.GetApp().DeleteIdleHandler(handler)42 43 44# Helper for writing a Window position by name, and later loading it.45def SaveWindowSize(section, rect, state=""):46 """Writes a rectangle to an INI file47 Args: section = section name in the applications INI file48 rect = a rectangle in a (cy, cx, y, x) tuple49 (same format as CREATESTRUCT position tuples)."""50 left, top, right, bottom = rect51 if state:52 state = state + " "53 win32ui.WriteProfileVal(section, state + "left", left)54 win32ui.WriteProfileVal(section, state + "top", top)55 win32ui.WriteProfileVal(section, state + "right", right)56 win32ui.WriteProfileVal(section, state + "bottom", bottom)57 58 59def LoadWindowSize(section, state=""):60 """Loads a section from an INI file, and returns a rect in a tuple (see SaveWindowSize)"""61 if state:62 state = state + " "63 left = win32ui.GetProfileVal(section, state + "left", 0)64 top = win32ui.GetProfileVal(section, state + "top", 0)65 right = win32ui.GetProfileVal(section, state + "right", 0)66 bottom = win32ui.GetProfileVal(section, state + "bottom", 0)67 return (left, top, right, bottom)68 69 70def RectToCreateStructRect(rect):71 return (rect[3] - rect[1], rect[2] - rect[0], rect[1], rect[0])72 73 74# Define FrameWindow and Application objects75#76# The Main Frame of the application.77class MainFrame(window.MDIFrameWnd):78 sectionPos = "Main Window"79 statusBarIndicators = (80 afxres.ID_SEPARATOR, # // status line indicator81 afxres.ID_INDICATOR_CAPS,82 afxres.ID_INDICATOR_NUM,83 afxres.ID_INDICATOR_SCRL,84 win32ui.ID_INDICATOR_LINENUM,85 win32ui.ID_INDICATOR_COLNUM,86 )87 88 def OnCreate(self, cs):89 self._CreateStatusBar()90 return 091 92 def _CreateStatusBar(self):93 self.statusBar = win32ui.CreateStatusBar(self)94 self.statusBar.SetIndicators(self.statusBarIndicators)95 self.HookCommandUpdate(self.OnUpdatePosIndicator, win32ui.ID_INDICATOR_LINENUM)96 self.HookCommandUpdate(self.OnUpdatePosIndicator, win32ui.ID_INDICATOR_COLNUM)97 98 def OnUpdatePosIndicator(self, cmdui):99 editControl = scriptutils.GetActiveEditControl()100 value = " " * 5101 if editControl is not None:102 try:103 startChar, endChar = editControl.GetSel()104 lineNo = editControl.LineFromChar(startChar)105 colNo = endChar - editControl.LineIndex(lineNo)106 107 if cmdui.m_nID == win32ui.ID_INDICATOR_LINENUM:108 value = "%0*d" % (5, lineNo + 1)109 else:110 value = "%0*d" % (3, colNo + 1)111 except win32ui.error:112 pass113 cmdui.SetText(value)114 cmdui.Enable()115 116 def PreCreateWindow(self, cc):117 cc = self._obj_.PreCreateWindow(cc)118 pos = LoadWindowSize(self.sectionPos)119 self.startRect = pos120 if pos[2] - pos[0]:121 rect = RectToCreateStructRect(pos)122 cc = cc[0], cc[1], cc[2], cc[3], rect, cc[5], cc[6], cc[7], cc[8]123 return cc124 125 def OnDestroy(self, msg):126 # use GetWindowPlacement(), as it works even when min'd or max'd127 rectNow = self.GetWindowPlacement()[4]128 if rectNow != self.startRect:129 SaveWindowSize(self.sectionPos, rectNow)130 return 0131 132 133class CApp(WinApp):134 "A class for the application"135 136 def __init__(self):137 self.oldCallbackCaller = None138 WinApp.__init__(self, win32ui.GetApp())139 self.idleHandlers = []140 141 def InitInstance(self):142 "Called to crank up the app"143 HookInput()144 numMRU = win32ui.GetProfileVal("Settings", "Recent File List Size", 10)145 win32ui.LoadStdProfileSettings(numMRU)146 # self._obj_.InitMDIInstance()147 if win32api.GetVersionEx()[0] < 4:148 win32ui.SetDialogBkColor()149 win32ui.Enable3dControls()150 151 # install a "callback caller" - a manager for the callbacks152 # self.oldCallbackCaller = win32ui.InstallCallbackCaller(self.CallbackManager)153 self.LoadMainFrame()154 self.SetApplicationPaths()155 156 def ExitInstance(self):157 "Called as the app dies - too late to prevent it here!"158 win32ui.OutputDebug("Application shutdown\n")159 # Restore the callback manager, if any.160 try:161 win32ui.InstallCallbackCaller(self.oldCallbackCaller)162 except AttributeError:163 pass164 if self.oldCallbackCaller:165 del self.oldCallbackCaller166 self.frame = None # clean Python references to the now destroyed window object.167 self.idleHandlers = []168 # Attempt cleanup if not already done!169 if self._obj_:170 self._obj_.AttachObject(None)171 self._obj_ = None172 global App173 global AppBuilder174 App = None175 AppBuilder = None176 return 0177 178 def HaveIdleHandler(self, handler):179 return handler in self.idleHandlers180 181 def AddIdleHandler(self, handler):182 self.idleHandlers.append(handler)183 184 def DeleteIdleHandler(self, handler):185 self.idleHandlers.remove(handler)186 187 def OnIdle(self, count):188 try:189 ret = 0190 handlers = self.idleHandlers[:] # copy list, as may be modified during loop191 for handler in handlers:192 try:193 thisRet = handler(handler, count)194 except:195 print("Idle handler %s failed" % (repr(handler)))196 traceback.print_exc()197 print("Idle handler removed from list")198 try:199 self.DeleteIdleHandler(handler)200 except ValueError: # Item not in list.201 pass202 thisRet = 0203 ret = ret or thisRet204 return ret205 except KeyboardInterrupt:206 pass207 208 def CreateMainFrame(self):209 return MainFrame()210 211 def LoadMainFrame(self):212 "Create the main applications frame"213 self.frame = self.CreateMainFrame()214 self.SetMainFrame(self.frame)215 self.frame.LoadFrame(win32ui.IDR_MAINFRAME, win32con.WS_OVERLAPPEDWINDOW)216 self.frame.DragAcceptFiles() # we can accept these.217 self.frame.ShowWindow(win32ui.GetInitialStateRequest())218 self.frame.UpdateWindow()219 self.HookCommands()220 221 def OnHelp(self, id, code):222 try:223 if id == win32ui.ID_HELP_GUI_REF:224 helpFile = regutil.GetRegisteredHelpFile("Pythonwin Reference")225 helpCmd = win32con.HELP_CONTENTS226 else:227 helpFile = regutil.GetRegisteredHelpFile("Main Python Documentation")228 helpCmd = win32con.HELP_FINDER229 if helpFile is None:230 win32ui.MessageBox("The help file is not registered!")231 else:232 from . import help233 234 help.OpenHelpFile(helpFile, helpCmd)235 except:236 t, v, tb = sys.exc_info()237 win32ui.MessageBox(238 "Internal error in help file processing\r\n%s: %s" % (t, v)239 )240 tb = None # Prevent a cycle241 242 def DoLoadModules(self, modules):243 # XXX - this should go, but the debugger uses it :-(244 # dont do much checking!245 for module in modules:246 __import__(module)247 248 def HookCommands(self):249 self.frame.HookMessage(self.OnDropFiles, win32con.WM_DROPFILES)250 self.HookCommand(self.HandleOnFileOpen, win32ui.ID_FILE_OPEN)251 self.HookCommand(self.HandleOnFileNew, win32ui.ID_FILE_NEW)252 self.HookCommand(self.OnFileMRU, win32ui.ID_FILE_MRU_FILE1)253 self.HookCommand(self.OnHelpAbout, win32ui.ID_APP_ABOUT)254 self.HookCommand(self.OnHelp, win32ui.ID_HELP_PYTHON)255 self.HookCommand(self.OnHelp, win32ui.ID_HELP_GUI_REF)256 # Hook for the right-click menu.257 self.frame.GetWindow(win32con.GW_CHILD).HookMessage(258 self.OnRClick, win32con.WM_RBUTTONDOWN259 )260 261 def SetApplicationPaths(self):262 # Load the users/application paths263 new_path = []264 apppath = win32ui.GetProfileVal("Python", "Application Path", "").split(";")265 for path in apppath:266 if len(path) > 0:267 new_path.append(win32ui.FullPath(path))268 for extra_num in range(1, 11):269 apppath = win32ui.GetProfileVal(270 "Python", "Application Path %d" % extra_num, ""271 ).split(";")272 if len(apppath) == 0:273 break274 for path in apppath:275 if len(path) > 0:276 new_path.append(win32ui.FullPath(path))277 sys.path = new_path + sys.path278 279 def OnRClick(self, params):280 "Handle right click message"281 # put up the entire FILE menu!282 menu = win32ui.LoadMenu(win32ui.IDR_TEXTTYPE).GetSubMenu(0)283 menu.TrackPopupMenu(params[5]) # track at mouse position.284 return 0285 286 def OnDropFiles(self, msg):287 "Handle a file being dropped from file manager"288 hDropInfo = msg[2]289 self.frame.SetActiveWindow() # active us290 nFiles = win32api.DragQueryFile(hDropInfo)291 try:292 for iFile in range(0, nFiles):293 fileName = win32api.DragQueryFile(hDropInfo, iFile)294 win32ui.GetApp().OpenDocumentFile(fileName)295 finally:296 win32api.DragFinish(hDropInfo)297 298 return 0299 300 # No longer used by Pythonwin, as the C++ code has this same basic functionality301 # but handles errors slightly better.302 # It all still works, tho, so if you need similar functionality, you can use it.303 # Therefore I havent deleted this code completely!304 # def CallbackManager( self, ob, args = () ):305 # """Manage win32 callbacks. Trap exceptions, report on them, then return 'All OK'306 # to the frame-work. """307 # import traceback308 # try:309 # ret = apply(ob, args)310 # return ret311 # except:312 # # take copies of the exception values, else other (handled) exceptions may get313 # # copied over by the other fns called.314 # win32ui.SetStatusText('An exception occured in a windows command handler.')315 # t, v, tb = sys.exc_info()316 # traceback.print_exception(t, v, tb.tb_next)317 # try:318 # sys.stdout.flush()319 # except (NameError, AttributeError):320 # pass321 322 # Command handlers.323 def OnFileMRU(self, id, code):324 "Called when a File 1-n message is recieved"325 fileName = win32ui.GetRecentFileList()[id - win32ui.ID_FILE_MRU_FILE1]326 win32ui.GetApp().OpenDocumentFile(fileName)327 328 def HandleOnFileOpen(self, id, code):329 "Called when FileOpen message is received"330 win32ui.GetApp().OnFileOpen()331 332 def HandleOnFileNew(self, id, code):333 "Called when FileNew message is received"334 win32ui.GetApp().OnFileNew()335 336 def OnHelpAbout(self, id, code):337 "Called when HelpAbout message is received. Displays the About dialog."338 win32ui.InitRichEdit()339 dlg = AboutBox()340 dlg.DoModal()341 342 343def _GetRegistryValue(key, val, default=None):344 # val is registry value - None for default val.345 try:346 hkey = win32api.RegOpenKey(win32con.HKEY_CURRENT_USER, key)347 return win32api.RegQueryValueEx(hkey, val)[0]348 except win32api.error:349 try:350 hkey = win32api.RegOpenKey(win32con.HKEY_LOCAL_MACHINE, key)351 return win32api.RegQueryValueEx(hkey, val)[0]352 except win32api.error:353 return default354 355 356scintilla = "Scintilla is Copyright 1998-2008 Neil Hodgson (http://www.scintilla.org)"357idle = "This program uses IDLE extensions by Guido van Rossum, Tim Peters and others."358contributors = "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!)"359 360 361# The About Box362class AboutBox(dialog.Dialog):363 def __init__(self, idd=win32ui.IDD_ABOUTBOX):364 dialog.Dialog.__init__(self, idd)365 366 def OnInitDialog(self):367 text = (368 "Pythonwin - Python IDE and GUI Framework for Windows.\n\n%s\n\nPython is %s\n\n%s\n\n%s\n\n%s"369 % (win32ui.copyright, sys.copyright, scintilla, idle, contributors)370 )371 self.SetDlgItemText(win32ui.IDC_EDIT1, text)372 # Get the build number - written by installers.373 # For distutils build, read pywin32.version.txt374 import sysconfig375 376 site_packages = sysconfig.get_paths()["platlib"]377 try:378 build_no = (379 open(os.path.join(site_packages, "pywin32.version.txt")).read().strip()380 )381 ver = "pywin32 build %s" % build_no382 except EnvironmentError:383 ver = None384 if ver is None:385 # See if we are Part of Active Python386 ver = _GetRegistryValue(387 "SOFTWARE\\ActiveState\\ActivePython", "CurrentVersion"388 )389 if ver is not None:390 ver = "ActivePython build %s" % (ver,)391 if ver is None:392 ver = ""393 self.SetDlgItemText(win32ui.IDC_ABOUT_VERSION, ver)394 self.HookCommand(self.OnButHomePage, win32ui.IDC_BUTTON1)395 396 def OnButHomePage(self, id, code):397 if code == win32con.BN_CLICKED:398 win32api.ShellExecute(399 0, "open", "https://github.com/mhammond/pywin32", None, "", 1400 )401 402 403def Win32RawInput(prompt=None):404 "Provide raw_input() for gui apps"405 # flush stderr/out first.406 try:407 sys.stdout.flush()408 sys.stderr.flush()409 except:410 pass411 if prompt is None:412 prompt = ""413 ret = dialog.GetSimpleInput(prompt)414 if ret == None:415 raise KeyboardInterrupt("operation cancelled")416 return ret417 418 419def Win32Input(prompt=None):420 "Provide input() for gui apps"421 return eval(input(prompt))422 423 424def HookInput():425 try:426 raw_input427 # must be py2x...428 sys.modules["__builtin__"].raw_input = Win32RawInput429 sys.modules["__builtin__"].input = Win32Input430 except NameError:431 # must be py3k432 import code433 434 sys.modules["builtins"].input = Win32RawInput435 436 437def HaveGoodGUI():438 """Returns true if we currently have a good gui available."""439 return "pywin.framework.startup" in sys.modules440 441 442def CreateDefaultGUI(appClass=None):443 """Creates a default GUI environment"""444 if appClass is None:445 from . import intpyapp # Bring in the default app - could be param'd later.446 447 appClass = intpyapp.InteractivePythonApp448 # Create and init the app.449 appClass().InitInstance()450 451 452def CheckCreateDefaultGUI():453 """Checks and creates if necessary a default GUI environment."""454 rc = HaveGoodGUI()455 if not rc:456 CreateDefaultGUI()457 return rc458 