Team Ai
Datasetpublic

codekingpro/portable-devtools

sourceHugging Faceupdated 5mo agoView on Hugging Face
1likes15kdownloads
winout.py595 linesDownload Raw Back to framework
1# winout.py2#3# generic "output window"4#5# This Window will detect itself closing, and recreate next time output is6# written to it.7 8# This has the option of writing output at idle time (by hooking the9# idle message, and queueing output) or writing as each10# write is executed.11# Updating the window directly gives a jerky appearance as many writes12# take place between commands, and the windows scrolls, and updates etc13# Updating at idle-time may defer all output of a long process, giving the14# appearence nothing is happening.15# There is a compromise "line" mode, which will output whenever16# a complete line is available.17 18# behaviour depends on self.writeQueueing19 20# This module is thread safe - output can originate from any thread.  If any thread21# other than the main thread attempts to print, it is always queued until next idle time22 23import queue24import re25 26import win32api27import win32con28import win32ui29from pywin.framework import app, window30from pywin.mfc import docview31 32debug = lambda msg: None33 34##debug=win32ui.OutputDebugString35##import win32trace;win32trace.InitWrite() # for debugging - delete me!36##debug = win32trace.write37 38 39class flags:40    # queueing of output.41    WQ_NONE = 042    WQ_LINE = 143    WQ_IDLE = 244 45 46# WindowOutputDocumentParent=docview.RichEditDoc47# WindowOutputDocumentParent=docview.Document48import pywin.scintilla.document49from pywin import default_scintilla_encoding50from pywin.scintilla import scintillacon51 52WindowOutputDocumentParent = pywin.scintilla.document.CScintillaDocument53 54 55class WindowOutputDocument(WindowOutputDocumentParent):56    def SaveModified(self):57        return 1  # say it is OK to destroy my document58 59    def OnSaveDocument(self, fileName):60        win32ui.SetStatusText("Saving file...", 1)61        try:62            self.SaveFile(fileName)63        except IOError as details:64            win32ui.MessageBox("Error - could not save file\r\n\r\n%s" % details)65            return 066        win32ui.SetStatusText("Ready")67        return 168 69 70class WindowOutputFrame(window.MDIChildWnd):71    def __init__(self, wnd=None):72        window.MDIChildWnd.__init__(self, wnd)73        self.HookMessage(self.OnSizeMove, win32con.WM_SIZE)74        self.HookMessage(self.OnSizeMove, win32con.WM_MOVE)75 76    def LoadFrame(self, idResource, style, wndParent, context):77        self.template = context.template78        return self._obj_.LoadFrame(idResource, style, wndParent, context)79 80    def PreCreateWindow(self, cc):81        cc = self._obj_.PreCreateWindow(cc)82        if (83            self.template.defSize84            and self.template.defSize[0] != self.template.defSize[1]85        ):86            rect = app.RectToCreateStructRect(self.template.defSize)87            cc = cc[0], cc[1], cc[2], cc[3], rect, cc[5], cc[6], cc[7], cc[8]88        return cc89 90    def OnSizeMove(self, msg):91        # so recreate maintains position.92        # Need to map coordinates from the93        # frame windows first child.94        mdiClient = self.GetParent()95        self.template.defSize = mdiClient.ScreenToClient(self.GetWindowRect())96 97    def OnDestroy(self, message):98        self.template.OnFrameDestroy(self)99        return 1100 101 102class WindowOutputViewImpl:103    def __init__(self):104        self.patErrorMessage = re.compile('\W*File "(.*)", line ([0-9]+)')105        self.template = self.GetDocument().GetDocTemplate()106 107    def HookHandlers(self):108        # Hook for the right-click menu.109        self.HookMessage(self.OnRClick, win32con.WM_RBUTTONDOWN)110 111    def OnDestroy(self, msg):112        self.template.OnViewDestroy(self)113 114    def OnInitialUpdate(self):115        self.RestoreKillBuffer()116        self.SetSel(-2)  # end of buffer117 118    def GetRightMenuItems(self):119        ret = []120        flags = win32con.MF_STRING | win32con.MF_ENABLED121        ret.append((flags, win32ui.ID_EDIT_COPY, "&Copy"))122        ret.append((flags, win32ui.ID_EDIT_SELECT_ALL, "&Select all"))123        return ret124 125    #126    # Windows command handlers, virtuals, etc.127    #128    def OnRClick(self, params):129        paramsList = self.GetRightMenuItems()130        menu = win32ui.CreatePopupMenu()131        for appendParams in paramsList:132            if type(appendParams) != type(()):133                appendParams = (appendParams,)134            menu.AppendMenu(*appendParams)135        menu.TrackPopupMenu(params[5])  # track at mouse position.136        return 0137 138    # as this is often used as an output window, exeptions will often139    # be printed.  Therefore, we support this functionality at this level.140    # Returns TRUE if the current line is an error message line, and will141    # jump to it.  FALSE if no error (and no action taken)142    def HandleSpecialLine(self):143        from . import scriptutils144 145        line = self.GetLine()146        if line[:11] == "com_error: ":147            # An OLE Exception - pull apart the exception148            # and try and locate a help file.149            try:150                import win32api151                import win32con152 153                det = eval(line[line.find(":") + 1 :].strip())154                win32ui.SetStatusText("Opening help file on OLE error...")155                from . import help156 157                help.OpenHelpFile(det[2][3], win32con.HELP_CONTEXT, det[2][4])158                return 1159            except win32api.error as details:160                win32ui.SetStatusText(161                    "The help file could not be opened - %s" % details.strerror162                )163                return 1164            except:165                win32ui.SetStatusText(166                    "Line is a COM error, but no WinHelp details can be parsed"167                )168        # Look for a Python traceback.169        matchResult = self.patErrorMessage.match(line)170        if matchResult is None:171            # No match - try the previous line172            lineNo = self.LineFromChar()173            if lineNo > 0:174                line = self.GetLine(lineNo - 1)175                matchResult = self.patErrorMessage.match(line)176        if matchResult is not None:177            # we have an error line.178            fileName = matchResult.group(1)179            if fileName[0] == "<":180                win32ui.SetStatusText("Can not load this file")181                return 1  # still was an error message.182            else:183                lineNoString = matchResult.group(2)184                # Attempt to locate the file (in case it is a relative spec)185                fileNameSpec = fileName186                fileName = scriptutils.LocatePythonFile(fileName)187                if fileName is None:188                    # Dont force update, so it replaces the idle prompt.189                    win32ui.SetStatusText(190                        "Cant locate the file '%s'" % (fileNameSpec), 0191                    )192                    return 1193 194                win32ui.SetStatusText(195                    "Jumping to line " + lineNoString + " of file " + fileName, 1196                )197                if not scriptutils.JumpToDocument(fileName, int(lineNoString)):198                    win32ui.SetStatusText("Could not open %s" % fileName)199                    return 1  # still was an error message.200                return 1201        return 0  # not an error line202 203    def write(self, msg):204        return self.template.write(msg)205 206    def writelines(self, lines):207        for line in lines:208            self.write(line)209 210    def flush(self):211        self.template.flush()212 213 214class WindowOutputViewRTF(docview.RichEditView, WindowOutputViewImpl):215    def __init__(self, doc):216        docview.RichEditView.__init__(self, doc)217        WindowOutputViewImpl.__init__(self)218 219    def OnInitialUpdate(self):220        WindowOutputViewImpl.OnInitialUpdate(self)221        return docview.RichEditView.OnInitialUpdate(self)222 223    def OnDestroy(self, msg):224        WindowOutputViewImpl.OnDestroy(self, msg)225        docview.RichEditView.OnDestroy(self, msg)226 227    def HookHandlers(self):228        WindowOutputViewImpl.HookHandlers(self)229        # Hook for finding and locating error messages230        self.HookMessage(self.OnLDoubleClick, win32con.WM_LBUTTONDBLCLK)231 232    # 		docview.RichEditView.HookHandlers(self)233 234    def OnLDoubleClick(self, params):235        if self.HandleSpecialLine():236            return 0  # dont pass on237        return 1  # pass it on by default.238 239    def RestoreKillBuffer(self):240        if len(self.template.killBuffer):241            self.StreamIn(win32con.SF_RTF, self._StreamRTFIn)242            self.template.killBuffer = []243 244    def SaveKillBuffer(self):245        self.StreamOut(win32con.SF_RTFNOOBJS, self._StreamRTFOut)246 247    def _StreamRTFOut(self, data):248        self.template.killBuffer.append(data)249        return 1  # keep em coming!250 251    def _StreamRTFIn(self, bytes):252        try:253            item = self.template.killBuffer[0]254            self.template.killBuffer.remove(item)255            if bytes < len(item):256                print("Warning - output buffer not big enough!")257            return item258        except IndexError:259            return None260 261    def dowrite(self, str):262        self.SetSel(-2)263        self.ReplaceSel(str)264 265 266import pywin.scintilla.view267 268 269class WindowOutputViewScintilla(270    pywin.scintilla.view.CScintillaView, WindowOutputViewImpl271):272    def __init__(self, doc):273        pywin.scintilla.view.CScintillaView.__init__(self, doc)274        WindowOutputViewImpl.__init__(self)275 276    def OnInitialUpdate(self):277        pywin.scintilla.view.CScintillaView.OnInitialUpdate(self)278        self.SCISetMarginWidth(3)279        WindowOutputViewImpl.OnInitialUpdate(self)280 281    def OnDestroy(self, msg):282        WindowOutputViewImpl.OnDestroy(self, msg)283        pywin.scintilla.view.CScintillaView.OnDestroy(self, msg)284 285    def HookHandlers(self):286        WindowOutputViewImpl.HookHandlers(self)287        pywin.scintilla.view.CScintillaView.HookHandlers(self)288        self.GetParent().HookNotify(289            self.OnScintillaDoubleClick, scintillacon.SCN_DOUBLECLICK290        )291 292    ##		self.HookMessage(self.OnLDoubleClick,win32con.WM_LBUTTONDBLCLK)293 294    def OnScintillaDoubleClick(self, std, extra):295        self.HandleSpecialLine()296 297    ##	def OnLDoubleClick(self,params):298    ##			return 0	# never dont pass on299 300    def RestoreKillBuffer(self):301        assert len(self.template.killBuffer) in (0, 1), "Unexpected killbuffer contents"302        if self.template.killBuffer:303            self.SCIAddText(self.template.killBuffer[0])304        self.template.killBuffer = []305 306    def SaveKillBuffer(self):307        self.template.killBuffer = [self.GetTextRange(0, -1)]308 309    def dowrite(self, str):310        end = self.GetTextLength()311        atEnd = end == self.GetSel()[0]312        self.SCIInsertText(str, end)313        if atEnd:314            self.SetSel(self.GetTextLength())315 316    def SetWordWrap(self, bWrapOn=1):317        if bWrapOn:318            wrap_mode = scintillacon.SC_WRAP_WORD319        else:320            wrap_mode = scintillacon.SC_WRAP_NONE321        self.SCISetWrapMode(wrap_mode)322 323    def _MakeColorizer(self):324        return None  # No colorizer for me!325 326 327WindowOutputView = WindowOutputViewScintilla328 329 330# The WindowOutput class is actually an MFC template.  This is a conventient way of331# making sure that my state can exist beyond the life of the windows themselves.332# This is primarily to support the functionality of a WindowOutput window automatically333# being recreated if necessary when written to.334class WindowOutput(docview.DocTemplate):335    """Looks like a general Output Window - text can be written by the 'write' method.336    Will auto-create itself on first write, and also on next write after being closed"""337 338    softspace = 1339 340    def __init__(341        self,342        title=None,343        defSize=None,344        queueing=flags.WQ_LINE,345        bAutoRestore=1,346        style=None,347        makeDoc=None,348        makeFrame=None,349        makeView=None,350    ):351        """init the output window -352        Params353        title=None -- What is the title of the window354        defSize=None -- What is the default size for the window - if this355                        is a string, the size will be loaded from the ini file.356        queueing = flags.WQ_LINE -- When should output be written357        bAutoRestore=1 -- Should a minimized window be restored.358        style -- Style for Window, or None for default.359        makeDoc, makeFrame, makeView -- Classes for frame, view and window respectively.360        """361        if makeDoc is None:362            makeDoc = WindowOutputDocument363        if makeFrame is None:364            makeFrame = WindowOutputFrame365        if makeView is None:366            makeView = WindowOutputViewScintilla367        docview.DocTemplate.__init__(368            self, win32ui.IDR_PYTHONTYPE, makeDoc, makeFrame, makeView369        )370        self.SetDocStrings("\nOutput\n\nText Documents (*.txt)\n.txt\n\n\n")371        win32ui.GetApp().AddDocTemplate(self)372        self.writeQueueing = queueing373        self.errorCantRecreate = 0374        self.killBuffer = []375        self.style = style376        self.bAutoRestore = bAutoRestore377        self.title = title378        self.bCreating = 0379        self.interruptCount = 0380        if type(defSize) == type(""):  # is a string - maintain size pos from ini file.381            self.iniSizeSection = defSize382            self.defSize = app.LoadWindowSize(defSize)383            self.loadedSize = self.defSize384        else:385            self.iniSizeSection = None386            self.defSize = defSize387        self.currentView = None388        self.outputQueue = queue.Queue(-1)389        self.mainThreadId = win32api.GetCurrentThreadId()390        self.idleHandlerSet = 0391        self.SetIdleHandler()392 393    def __del__(self):394        self.Close()395 396    def Create(self, title=None, style=None):397        self.bCreating = 1398        if title:399            self.title = title400        if style:401            self.style = style402        doc = self.OpenDocumentFile()403        if doc is None:404            return405        self.currentView = doc.GetFirstView()406        self.bCreating = 0407        if self.title:408            doc.SetTitle(self.title)409 410    def Close(self):411        self.RemoveIdleHandler()412        try:413            parent = self.currentView.GetParent()414        except (AttributeError, win32ui.error):  # Already closed415            return416        parent.DestroyWindow()417 418    def SetTitle(self, title):419        self.title = title420        if self.currentView:421            self.currentView.GetDocument().SetTitle(self.title)422 423    def OnViewDestroy(self, view):424        self.currentView.SaveKillBuffer()425        self.currentView = None426 427    def OnFrameDestroy(self, frame):428        if self.iniSizeSection:429            # use GetWindowPlacement(), as it works even when min'd or max'd430            newSize = frame.GetWindowPlacement()[4]431            if self.loadedSize != newSize:432                app.SaveWindowSize(self.iniSizeSection, newSize)433 434    def SetIdleHandler(self):435        if not self.idleHandlerSet:436            debug("Idle handler set\n")437            win32ui.GetApp().AddIdleHandler(self.QueueIdleHandler)438            self.idleHandlerSet = 1439 440    def RemoveIdleHandler(self):441        if self.idleHandlerSet:442            debug("Idle handler reset\n")443            if win32ui.GetApp().DeleteIdleHandler(self.QueueIdleHandler) == 0:444                debug("Error deleting idle handler\n")445            self.idleHandlerSet = 0446 447    def RecreateWindow(self):448        if self.errorCantRecreate:449            debug("Error = not trying again")450            return 0451        try:452            # This will fail if app shutting down453            win32ui.GetMainFrame().GetSafeHwnd()454            self.Create()455            return 1456        except (win32ui.error, AttributeError):457            self.errorCantRecreate = 1458            debug("Winout can not recreate the Window!\n")459            return 0460 461    # this handles the idle message, and does the printing.462    def QueueIdleHandler(self, handler, count):463        try:464            bEmpty = self.QueueFlush(20)465            # If the queue is empty, then we are back to idle and restart interrupt logic.466            if bEmpty:467                self.interruptCount = 0468        except KeyboardInterrupt:469            # First interrupt since idle we just pass on.470            # later ones we dump the queue and give up.471            self.interruptCount = self.interruptCount + 1472            if self.interruptCount > 1:473                # Drop the queue quickly as the user is already annoyed :-)474                self.outputQueue = queue.Queue(-1)475                print("Interrupted.")476                bEmpty = 1477            else:478                raise  # re-raise the error so the users exception filters up.479        return not bEmpty  # More to do if not empty.480 481    # Returns true if the Window needs to be recreated.482    def NeedRecreateWindow(self):483        try:484            if self.currentView is not None and self.currentView.IsWindow():485                return 0486        except (487            win32ui.error,488            AttributeError,489        ):  # Attribute error if the win32ui object has died.490            pass491        return 1492 493    # Returns true if the Window is OK (either cos it was, or because it was recreated494    def CheckRecreateWindow(self):495        if self.bCreating:496            return 1497        if not self.NeedRecreateWindow():498            return 1499        if self.bAutoRestore:500            if self.RecreateWindow():501                return 1502        return 0503 504    def QueueFlush(self, max=None):505        # Returns true if the queue is empty after the flush506        # 		debug("Queueflush - %d, %d\n" % (max, self.outputQueue.qsize()))507        if self.bCreating:508            return 1509        items = []510        rc = 0511        while max is None or max > 0:512            try:513                item = self.outputQueue.get_nowait()514                items.append(item)515            except queue.Empty:516                rc = 1517                break518            if max is not None:519                max = max - 1520        if len(items) != 0:521            if not self.CheckRecreateWindow():522                debug(":Recreate failed!\n")523                return 1  # In trouble - so say we have nothing to do.524            win32ui.PumpWaitingMessages()  # Pump paint messages525            self.currentView.dowrite("".join(items))526        return rc527 528    def HandleOutput(self, message):529        # 		debug("QueueOutput on thread %d, flags %d with '%s'...\n" % (win32api.GetCurrentThreadId(), self.writeQueueing, message ))530        self.outputQueue.put(message)531        if win32api.GetCurrentThreadId() != self.mainThreadId:532            pass533        # 			debug("not my thread - ignoring queue options!\n")534        elif self.writeQueueing == flags.WQ_LINE:535            pos = message.rfind("\n")536            if pos >= 0:537                # 				debug("Line queueing - forcing flush\n")538                self.QueueFlush()539                return540        elif self.writeQueueing == flags.WQ_NONE:541            # 			debug("WQ_NONE - flushing!\n")542            self.QueueFlush()543            return544        # Let our idle handler get it - wake it up545        try:546            win32ui.GetMainFrame().PostMessage(547                win32con.WM_USER548            )  # Kick main thread off.549        except win32ui.error:550            # This can happen as the app is shutting down, so we send it to the C++ debugger551            win32api.OutputDebugString(message)552 553    # delegate certain fns to my view.554    def writelines(self, lines):555        for line in lines:556            self.write(line)557 558    def write(self, message):559        self.HandleOutput(message)560 561    def flush(self):562        self.QueueFlush()563 564    def HandleSpecialLine(self):565        self.currentView.HandleSpecialLine()566 567 568def RTFWindowOutput(*args, **kw):569    kw["makeView"] = WindowOutputViewRTF570    return WindowOutput(*args, **kw)571 572 573def thread_test(o):574    for i in range(5):575        o.write("Hi from thread %d\n" % (win32api.GetCurrentThreadId()))576        win32api.Sleep(100)577 578 579def test():580    w = WindowOutput(queueing=flags.WQ_IDLE)581    w.write("First bit of text\n")582    import _thread583 584    for i in range(5):585        w.write("Hello from the main thread\n")586        _thread.start_new(thread_test, (w,))587    for i in range(2):588        w.write("Hello from the main thread\n")589        win32api.Sleep(50)590    return w591 592 593if __name__ == "__main__":594    test()595 
codekingpro/portable-devtools · Team Ai