From 6bb38ab67e4ec09f29ced5dc7166cf91b7f26085 Mon Sep 17 00:00:00 2001 From: Robin Dunn Date: Mon, 2 Apr 2001 03:41:54 +0000 Subject: [PATCH] Added wxPython/lib/infoframe.py from Chris Fama. It contains a class that can be used in place of wxPyOnDemandOutputWindow. git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/trunk@9631 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775 --- wxPython/CHANGES.txt | 7 + wxPython/wxPython/lib/infoframe.py | 397 +++++++++++++++++++++++++++++ 2 files changed, 404 insertions(+) create mode 100644 wxPython/wxPython/lib/infoframe.py diff --git a/wxPython/CHANGES.txt b/wxPython/CHANGES.txt index ec6dc14ac8..b617147bcf 100644 --- a/wxPython/CHANGES.txt +++ b/wxPython/CHANGES.txt @@ -24,6 +24,13 @@ access from non-GUI threads. wxPyOnDemandOutputWindow is now thread safe if non-GUI threads use print, sys.stdout.write, etc. +Added CreateTextSizer and CreateButtonSizer to wxDialog + + +Added wxPython/lib/infoframe.py from Chris Fama. It contains a class +that can be used in place of wxPyOnDemandOutputWindow. + + diff --git a/wxPython/wxPython/lib/infoframe.py b/wxPython/wxPython/lib/infoframe.py new file mode 100644 index 0000000000..4da7c8368e --- /dev/null +++ b/wxPython/wxPython/lib/infoframe.py @@ -0,0 +1,397 @@ +""" +infoframe.py +Released under wxWindows license etc. + +This is a fairly rudimentary, but slightly fancier tha +wxPyOnDemandOutputWindow (on which it's based; thanks Robin), version +of the same sort of thing: a file-like class called +InformationalMessagesFrame.. This window also has a status bar with a +couple of buttons for controlling the echoing of all output to a file +with a randomly-chosen filename... [[A LITTLE MORE COULD BE SAID +HERE]] + +Typical usage: + from wxPython.lib.infoframe import * + ... # ... modify your wxApp as follows: + class myApp[wxApp): + outputWindowClass = wxInformationalMessagesFrame + ... +If you're running on Linux, you'll also have to supply an argument 1 to your +constructor of myApp to redirect stdout/stderr to this window (it's done +automatically for you on Windows). + +If you don't want to redirect stdout/stderr, but use the class directly: do +it this way: + +InformationalMessagesFrame = wxInformationalMessagesFrame\ + ([options from progname (default ""), + txt (default "informational + messages"]) +#^^^^ early in the program +... +InformationalMessagesFrame([comma-separated list of items to + display. Note that these will never + be separated by spaces as they may + be when used in the Python 'print' + command]) + +The latter statement, of course, may be repeated arbitrarily often. +The window will not appear until it is written to, and it may be +manually closed by the user, after which it will not appear again +until written to... Also note that all output is echoed to a file with +a randomly-generated name [see the mktemp module in the standard +library], in the directory given as the 'dir' keyword argument to the +InformationalMessagesFrame constructor [which has a default value of +'.'), or set via the method SetOutputDirectory... + +Please also note the methods EnableOutput and DisableOutput, and the +possible arguments for the constructor in the code below... (* TO DO: +explain this here...*) The former, EnableOutput, displays the frame +with an introductory message, opens a random file to which future +displayed output also goes, and sets the __debug__ variable of each +module whose name begins with a capital letter {this happens to be the +author's personal practice; all my python module start with capital +letters} to 1. This is so that you can say + + if __debug__: + InformationalMessagesFrame("... with lots of % constructs" + % TUPLE) + +without worrying about a huge about of overhead in the case where +debugging is not turned on. "Debug mode" can also be turned on by +selecting the item-"Enable debugging output" from the "Debug" menu of +a frame which has been either passed appropriately to the constructor +of the wxInformationalMessagesFrame (see the code), or set via the +SetOtherMenuBar method thereof. (I have found this to be an extremely +useful tool, in lieu of a full wxPython debugger...) This menu item +is also disabled, and an item "Disable debugging output" (which calls +the method described in the next paragraph) is enabled. Note that +these things need not be done: e.g., you don't need to have a "Debug" +menu with appropriate items; in this case simply do not call the +SetOtherMenuBar method or use the othermenubar keyword argument of the +class Instance constructor. + +The DisableOutput method does the reverse of this; it closes the +window (and associated file), and sets the __debug__ variable of each +module whose name begins with a capital letter {this happens to be the +author's personal practice; all my python module start with capital +letters} to 0. It also enables/disabled the appropriate menu items, +if this was done previously (or SetOtherMenuBar has been called...). + +Finally, note that the file-like method close() destroys the window +(and any associated file) and there is a file-like method write() +which displays it's argument [actually, it's very similar to +DisableOutput). Also, class instances are callable as noted above, +displaying successive arguments if this is done. + +""" + +from wxPython.wx import * +import string, sys, types, tempfile, os + +class _MyStatusBar(wxStatusBar): + def __init__(self, parent,callbacks=None): + wxStatusBar.__init__(self, parent, -1, style=wxTAB_TRAVERSAL) + self.SetFieldsCount(3) + + self.SetStatusText("",0) + + ID = NewId() + self.button1 = wxButton(self,ID,"Dismiss", + style=wxTAB_TRAVERSAL) + EVT_BUTTON(self,ID,callbacks[0]) + + ID = NewId() + self.button2 = wxButton(self,ID,"Close File", + style=wxTAB_TRAVERSAL) + EVT_BUTTON(self,ID,self.OnButton2) + self.usealternate = 0 + self.callbacks = [callbacks[1],callbacks[2]] + + # figure out how tall to make the status bar + dc = wxClientDC(self) + dc.SetFont(self.GetFont()) + (w,h) = dc.GetTextExtent('X') + h = int(h * 1.8) + self.SetSize(wxSize(100, h)) + self.OnSize("dummy") + EVT_SIZE(self,self.OnSize) + + # reposition things... + def OnSize(self, event): + self.CalculateSizes() + rect = self.GetFieldRect(1) + self.button1.SetPosition(wxPoint(rect.x+5, rect.y+2)) + self.button1.SetSize(wxSize(rect.width-10, rect.height-4)) + rect = self.GetFieldRect(2) + self.button2.SetPosition(wxPoint(rect.x+5, rect.y+2)) + self.button2.SetSize(wxSize(rect.width-10, rect.height-4)) + + # widths........ + def CalculateSizes(self): + dc = wxClientDC(self.button1) + dc.SetFont(self.button1.GetFont()) + (w1,h) = dc.GetTextExtent(self.button1.GetLabel()) + + dc = wxClientDC(self.button2) + dc.SetFont(self.button2.GetFont()) + (w2,h) = dc.GetTextExtent(self.button2.GetLabel()) + + self.SetStatusWidths([-1,w1+15,w2+15]) + + def OnButton2(self,event): + if self.usealternate: + if self.callbacks[1] (): + self.button2.SetLabel ("Close File") + self.usealternate = 1 - self.usealternate + else: + if self.callbacks[0] (): + self.button2.SetLabel ("Open New File") + self.usealternate = 1 - self.usealternate + self.OnSize("") + self.button2.Refresh(TRUE) + self.Refresh() + +class wxInformationalMessagesFrame:#wxPyOnDemandOutputWindow): + parent = None + + def SetParent(self, parent): + self.parent = parent + + def SetOtherMenuBar(self,othermenu): + self.othermenu = othermenu + + def __init__(self,progname="",text="informational messages",dir=',', + othermenubar=None): + self.othermenu = othermenubar + self.frame = None + self.title = "%s %s" % (progname,text) + self.softspace = 1 # of rather limited use + if dir: + self.SetOutputDirectory(dir) + if __debug__: + self.EnableOutput() + #wxPyOnDemandOutputWindow.__init__(self,self.title) + for m in sys.modules.values(): + if m is not None:# and m.__dict__.has_key("__debug__"): + m.__dict__["__debug__"] = self.Enabled + + f = None + + def write(self,string): + if self.Enabled: + if self.f: + self.f.write (string) + self.f.flush () + move = 1 + if hasattr(self,"text")\ + and self.text is not None\ + and self.text.GetInsertionPoint()\ + <> self.text.GetLastPosition(): + move = 0 + if not self.frame: + self.frame = wxFrame(self.parent, -1, self.title) + self.text = wxTextCtrl(self.frame, -1, "", + style = wxTE_MULTILINE|wxTE_READONLY + |wxTE_RICH)# appears to cause problem? + self.frame.sb = _MyStatusBar(self.frame, + callbacks=[self.DisableOutput, + self.CloseFile, + self.OpenNewFile]) + self.frame.SetStatusBar(self.frame.sb) + self.frame.SetSize(wxSize(450, 300)) + self.frame.Show(true) + EVT_CLOSE(self.frame, self.OnCloseWindow) + self.text.AppendText(string) +## if __debug__ and type(sys.__stderr__) == types.FileType\ +## and sys.__stderr__.isatty(): +## sys.__stderr__.write( +## "%s.write(): self.text.GetInsertionPoint() = %s, "\ +## "self.text.GetLastPosition() = %s, "\ +## "move = %d\n" % (self, +## self.text.GetInsertionPoint(), +## self.text.GetLastPosition(), +## move)) + if move: + self.text.ShowPosition(self.text.GetLastPosition()) + + Enabled = __debug__ + + def OnCloseWindow(self,event,exiting=0): + if self.f: + self.f.close() + self.f = None + if hasattr(self,"othermenu") and self.othermenu is not None\ + and self.frame is not None\ + and not exiting: + i = self.othermenu.FindMenuItem('Debug','Disable debugging output') + self.othermenu.Enable(i,0) + i = self.othermenu.FindMenuItem('Debug','Enable debugging output') + self.othermenu.Enable(i,1) + for m in sys.modules.values(): + if m is not None:# and m.__dict__.has_key("__debug__"): + m.__dict__["__debug__"] = 0 + if self.frame is not None: # should be true, but, e.g., allows + # DisableOutput method (which calls this + # one) to be called when the frame is not + # actually open, so that it is always safe + # to call this method... + frame = self.frame + self.frame = self.text = None + frame.Destroy() + self.Enabled = 0 + + def EnableOutput(self,othermenubar=None): + if othermenubar is not None: + self.othermenu = othermenubar + self.Enabled = 1 + for m in sys.modules.values(): + if m is not None:# and m.__dict__.has_key("__debug__"): + m.__dict__["__debug__"] = 1 + if hasattr(self,"othermenu") and self.othermenu is not None: + i = self.othermenu.FindMenuItem('Debug','Disable debugging output') + self.othermenu.Enable(i,1) + i = self.othermenu.FindMenuItem('Debug','Enable debugging output') + self.othermenu.Enable(i,0) + if not self.f: + try: + filename = tempfile.mktemp () + self.write("Please close this window (or select the " + "'Dismiss' button below) when desired. By " + "default all messages written to this window " + "will also be written to the file '%s'--you " + "may close this file by selecting 'Close " + "File' below, whereupon this button will be " + "replaced with one allowing you to select a " + "new file...\n\n" % os.path.abspath(filename)) + self.f = open (filename,'w') + self.frame.sb.SetStatusText("File '%s' opened..." + % os.path.abspath(self.f.name), + 0) + except EnvironmentError: + self.frame.sb.SetStatusText("File creation failed (filename " + "'%s')..." + % os.path.abspath(filename), + 0) + + def CloseFile(self): + if self.f: + if self.frame: + self.frame.sb.SetStatusText("File '%s' closed..." + % os.path.abspath(self.f.name), + 0) + self.f.close () + self.f = None + else: + if self.frame: + self.frame.sb.SetStatusText("") + if self.frame: + self.frame.sb.Refresh() + return 1 + + def OpenNewFile(self): + self.CloseFile() + dlg = wxFileDialog(self.frame, + "Choose a new log file", self.dir,"","*", + wxSAVE | wxHIDE_READONLY | wxOVERWRITE_PROMPT) + if dlg.ShowModal() == wxID_CANCEL: + dlg.Destroy() + return 0 + else: + try: + self.f = open(os.path.abspath(dlg.GetPath()),'w') + except EnvironmentError: + dlg.Destroy() + return 0 + dlg.Destroy() + if self.frame: + self.frame.sb.SetStatusText("File '%s' opened..." + % os.path.abspath(self.f.name), + 0) + return 1 + + def DisableOutput(self,exiting=0): + self.write(".DisableOutput()\n") + self.CloseFile() + self.Enabled = 0 + if hasattr(self,"othermenu") and self.othermenu is not None: + i = self.othermenu.FindMenuItem('Debug','Disable debugging output') + self.othermenu.Enable(i,0) + i = self.othermenu.FindMenuItem('Debug','Enable debugging output') + self.othermenu.Enable(i,1) + if hasattr(self,"frame") \ + and self.frame is not None: + self.OnCloseWindow("Dummy",exiting=exiting) + + def close(self): + self.DisableOutput() + + def flush(self): + if self.text: + self.text.SetInsertionPointEnd() + wxYield() + + def __call__(self,* args): + for s in args: + self.write (str (s)) + + def SetOutputDirectory(self,dir): + self.dir = tempfile.tempdir = dir + +class DummyFile: + def __init__(self,progname=""): + self.softspace = 1 + def __call__(self,*args): + pass + def write(self,s): + pass + def flush(self): + pass + def close(self): + pass + def EnableOutput(self): + pass + def __call__(self,* args): + pass + def DisableOutput(self,exiting=0): + pass + def SetParent(self,wX): + pass + +if __name__ == "__main__": + __debug__ = 1 + + ImportErrors = 0 + try: + import Errors + importErrors = 1 + except ImportError: + pass + + class MyFrame(wxFrame): + def __init__(self): + wxFrame.__init__(self,None,-1,"Close me...",size=(300,10)) + EVT_CLOSE(self,self.OnClose) + + def OnClose(self,event): + if isinstance(sys.stdout,wxInformationalMessagesFrame): + sys.stdout.close()# shouldn't be necessary? + self.Destroy() + + class MyApp(wxApp): + outputWindowClass = wxInformationalMessagesFrame + def OnInit(self): + if ImportErrors: + sys.stderr = Errors.NonWindowingErrorWindow( + file=self.stdioWin) + print "Starting.\n", + frame = MyFrame() + frame.Show(TRUE) + self.SetTopWindow(frame) + if isinstance(sys.stdout,wxInformationalMessagesFrame): + sys.stdout.SetParent(frame)# Shouldn't this mean the + #wxInternationalMessagesFrame is Destroy()'d when MFrame is? + return true + + app = MyApp() + app.MainLoop() -- 2.49.0