mirror of
https://github.com/wxWidgets/Phoenix.git
synced 2026-01-04 11:00:07 +01:00
set svn:eol-style to native
git-svn-id: https://svn.wxwidgets.org/svn/wx/wxPython/Phoenix/trunk@71832 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775
This commit is contained in:
492
wx/lib/infoframe.py
Normal file
492
wx/lib/infoframe.py
Normal file
@@ -0,0 +1,492 @@
|
||||
# 12/20/2003 - Jeff Grimmett (grimmtooth@softhome.net)
|
||||
#
|
||||
# o wxPyInformationalMessagesFrame -> PyInformationalMessagesFrame
|
||||
# o dummy_wxPyInformationalMessagesFrame -> dummy_PyInformationalMessagesFrame
|
||||
#
|
||||
|
||||
"""
|
||||
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
|
||||
wxInformationalMessagesFrame. 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...
|
||||
|
||||
The class behaves similarly to wxPyOnDemandOutputWindow in that (at
|
||||
least by default) the frame does not appear until written to, but is
|
||||
somewhat different in that, either under programmatic (the
|
||||
DisableOutput method) or user (the frame's close button, it's status
|
||||
bar's "Dismiss" button, or a "Disable output" item of some menu,
|
||||
perhaps of some other frame), the frame will be destroyed, an
|
||||
associated file closed, and writing to it will then do nothing. This
|
||||
can be reversed: either under programmatic (the EnableOutput method)
|
||||
or user (an "Enable output" item of some menu), a new frame will be
|
||||
opened,And an associated file (with a "randomly"selected filename)
|
||||
opened for writing [to which all subsequent displayed messages will be
|
||||
echoed].
|
||||
|
||||
Please note that, like wxPyOnDemandOutputWindow, the instance is not
|
||||
itself a subclass of wxWindow: when the window is open (and ONLY
|
||||
then), it's "frame" attribute is the actual instance of wFrame...
|
||||
|
||||
Typical usage::
|
||||
|
||||
from wx.lib.infoframe import *
|
||||
... # ... modify your wxApp as follows:
|
||||
class myApp(wxApp):
|
||||
outputWindowClass = PyInformationalMessagesFrame
|
||||
# ...
|
||||
|
||||
|
||||
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 = PyInformationalMessagesFrame( \
|
||||
options_from_progname, # (default = "")
|
||||
txt), # (default = "informational messages")
|
||||
|
||||
#^^^^ early in the program
|
||||
# ...
|
||||
|
||||
InformationalMessagesFrame(list_of_items)
|
||||
|
||||
# where list_of_items:
|
||||
#
|
||||
# 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 reappear 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... This file will be
|
||||
closed with the window--a new one will be created [by default] upon
|
||||
each subsequent reopening.
|
||||
|
||||
Please also note the methods EnableOutput and DisableOutput, and the
|
||||
possible arguments for the constructor in the code below... (* TO DO:
|
||||
explain this here...*) Neither of these methods need be used at all,
|
||||
and in this case the frame will only be displayed once it has been
|
||||
written to, like wxPyOnDemandOutputWindow.
|
||||
|
||||
The former, EnableOutput, displays the frame with an introductory
|
||||
message, opens a random file to which future displayed output also
|
||||
goes (unless the nofile attribute is present), and sets the __debug__
|
||||
variable of each module to 1 (unless the no __debug__ attribute is
|
||||
present]. This is so that you can say, in any module whatsoever::
|
||||
|
||||
if __debug__:
|
||||
InformationalMessagesFrame("... with lots of %<Character> constructs"
|
||||
% TUPLE)
|
||||
|
||||
|
||||
without worrying about the overhead of evaluating the arguments, and
|
||||
calling the wxInformationalMessagesFrame instance, in the case where
|
||||
debugging is not turned on. (This won't happen if the instance has an
|
||||
attribute no__debug__; you can arrange this by sub-classing...)
|
||||
|
||||
"Debug mode" can also be turned on by selecting the item-"Enable
|
||||
output" from the "Debug" menu [the default--see the optional arguments
|
||||
to the SetOtherMenuBar method] 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. This
|
||||
also writes an empty string to the instance, meaning that the frame
|
||||
will open (unless DisablOutput has been called) with an appropriate
|
||||
introductory message (which will vary according to whether the
|
||||
instance/class has the "no __debug__" attribute)^ I have found this to
|
||||
be an extremely useful tool, in lieu of a full wxPython debugger...
|
||||
|
||||
Following this, the menu item is also disabled, and an item "Disable
|
||||
output" (again, by default) is enabled. Note that these need not be
|
||||
done: e.g., you don't NEED to have a 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...).
|
||||
Note too that after a call to DisableOutput, nothing further will be
|
||||
done upon subsequent write()'s, until the EnableOutput method is
|
||||
called, either explicitly or by the menu selection above...
|
||||
|
||||
Finally, note that the file-like method close() destroys the window
|
||||
(and closes any associated file) and there is a file-like method
|
||||
write() which displays it's argument.
|
||||
|
||||
All (well, most) of this is made clear by the example code at the end
|
||||
of this file, which is run if the file is run by itself; otherwise,
|
||||
see the appropriate "stub" file in the wxPython demo.
|
||||
|
||||
"""
|
||||
|
||||
import os
|
||||
import sys
|
||||
import tempfile
|
||||
|
||||
import wx
|
||||
|
||||
class _MyStatusBar(wx.StatusBar):
|
||||
def __init__(self, parent, callbacks=None, useopenbutton=0):
|
||||
wx.StatusBar.__init__(self, parent, -1, style=wx.TAB_TRAVERSAL)
|
||||
self.SetFieldsCount(3)
|
||||
|
||||
self.SetStatusText("",0)
|
||||
|
||||
self.button1 = wx.Button(self, -1, "Dismiss", style=wx.TAB_TRAVERSAL)
|
||||
self.Bind(wx.EVT_BUTTON, self.OnButton1, self.button1)
|
||||
|
||||
if not useopenbutton:
|
||||
self.button2 = wx.Button(self, -1, "Close File", style=wx.TAB_TRAVERSAL)
|
||||
else:
|
||||
self.button2 = wx.Button(self, -1, "Open New File", style=wx.TAB_TRAVERSAL)
|
||||
|
||||
self.Bind(wx.EVT_BUTTON, self.OnButton2, self.button2)
|
||||
self.useopenbutton = useopenbutton
|
||||
self.callbacks = callbacks
|
||||
|
||||
# figure out how tall to make the status bar
|
||||
dc = wx.ClientDC(self)
|
||||
dc.SetFont(self.GetFont())
|
||||
(w,h) = dc.GetTextExtent('X')
|
||||
h = int(h * 1.8)
|
||||
self.SetSize((100, h))
|
||||
self.OnSize("dummy")
|
||||
self.Bind(wx.EVT_SIZE, self.OnSize)
|
||||
|
||||
# reposition things...
|
||||
def OnSize(self, event):
|
||||
self.CalculateSizes()
|
||||
rect = self.GetFieldRect(1)
|
||||
self.button1.SetPosition((rect.x+5, rect.y+2))
|
||||
self.button1.SetSize((rect.width-10, rect.height-4))
|
||||
rect = self.GetFieldRect(2)
|
||||
self.button2.SetPosition((rect.x+5, rect.y+2))
|
||||
self.button2.SetSize((rect.width-10, rect.height-4))
|
||||
|
||||
# widths........
|
||||
def CalculateSizes(self):
|
||||
dc = wx.ClientDC(self.button1)
|
||||
dc.SetFont(self.button1.GetFont())
|
||||
(w1,h) = dc.GetTextExtent(self.button1.GetLabel())
|
||||
|
||||
dc = wx.ClientDC(self.button2)
|
||||
dc.SetFont(self.button2.GetFont())
|
||||
(w2,h) = dc.GetTextExtent(self.button2.GetLabel())
|
||||
|
||||
self.SetStatusWidths([-1,w1+15,w2+15])
|
||||
|
||||
def OnButton1(self,event):
|
||||
self.callbacks[0] ()
|
||||
|
||||
def OnButton2(self,event):
|
||||
if self.useopenbutton and self.callbacks[2] ():
|
||||
self.button2.SetLabel ("Close File")
|
||||
elif self.callbacks[1] ():
|
||||
self.button2.SetLabel ("Open New File")
|
||||
|
||||
self.useopenbutton = 1 - self.useopenbutton
|
||||
self.OnSize("")
|
||||
self.button2.Refresh(True)
|
||||
self.Refresh()
|
||||
|
||||
|
||||
|
||||
class PyInformationalMessagesFrame(object):
|
||||
def __init__(self,
|
||||
progname="",
|
||||
text="informational messages",
|
||||
dir='.',
|
||||
menuname="Debug",
|
||||
enableitem="Enable output",
|
||||
disableitem="Disable output",
|
||||
othermenubar=None):
|
||||
|
||||
self.SetOtherMenuBar(othermenubar,
|
||||
menuname=menuname,
|
||||
enableitem=enableitem,
|
||||
disableitem=disableitem)
|
||||
|
||||
if hasattr(self,"othermenu") and self.othermenu is not None:
|
||||
i = self.othermenu.FindMenuItem(self.menuname,self.disableitem)
|
||||
self.othermenu.Enable(i,0)
|
||||
i = self.othermenu.FindMenuItem(self.menuname,self.enableitem)
|
||||
self.othermenu.Enable(i,1)
|
||||
|
||||
self.frame = None
|
||||
self.title = "%s %s" % (progname,text)
|
||||
self.parent = None # use the SetParent method if desired...
|
||||
self.softspace = 1 # of rather limited use
|
||||
|
||||
if dir:
|
||||
self.SetOutputDirectory(dir)
|
||||
|
||||
|
||||
def SetParent(self, parent):
|
||||
self.parent = parent
|
||||
|
||||
|
||||
def SetOtherMenuBar(self,
|
||||
othermenu,
|
||||
menuname="Debug",
|
||||
enableitem="Enable output",
|
||||
disableitem="Disable output"):
|
||||
self.othermenu = othermenu
|
||||
self.menuname = menuname
|
||||
self.enableitem = enableitem
|
||||
self.disableitem = disableitem
|
||||
|
||||
|
||||
f = None
|
||||
|
||||
|
||||
def write(self, string):
|
||||
if not wx.Thread_IsMain():
|
||||
# Aquire the GUI mutex before making GUI calls. Mutex is released
|
||||
# when locker is deleted at the end of this function.
|
||||
#
|
||||
# TODO: This should be updated to use wx.CallAfter similarly to how
|
||||
# PyOnDemandOutputWindow.write was so it is not necessary
|
||||
# to get the gui mutex
|
||||
locker = wx.MutexGuiLocker()
|
||||
|
||||
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 = wx.Frame(self.parent, -1, self.title, size=(450, 300),
|
||||
style=wx.DEFAULT_FRAME_STYLE|wx.NO_FULL_REPAINT_ON_RESIZE)
|
||||
|
||||
self.text = wx.TextCtrl(self.frame, -1, "",
|
||||
style = wx.TE_MULTILINE|wx.TE_READONLY|wx.TE_RICH)
|
||||
|
||||
self.frame.sb = _MyStatusBar(self.frame,
|
||||
callbacks=[self.DisableOutput,
|
||||
self.CloseFile,
|
||||
self.OpenNewFile],
|
||||
useopenbutton=hasattr(self,
|
||||
"nofile"))
|
||||
self.frame.SetStatusBar(self.frame.sb)
|
||||
self.frame.Show(True)
|
||||
self.frame.Bind(wx.EVT_CLOSE, self.OnCloseWindow)
|
||||
|
||||
if hasattr(self,"nofile"):
|
||||
self.text.AppendText(
|
||||
"Please close this window (or select the "
|
||||
"'Dismiss' button below) when desired. By "
|
||||
"default all messages written to this window "
|
||||
"will NOT be written to a file--you "
|
||||
"may change this by selecting 'Open New File' "
|
||||
"below, allowing you to select a "
|
||||
"new file...\n\n")
|
||||
else:
|
||||
tempfile.tempdir = self.dir
|
||||
filename = os.path.abspath(tempfile.mktemp ())
|
||||
|
||||
self.text.AppendText(
|
||||
"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" % filename)
|
||||
try:
|
||||
self.f = open(filename, 'w')
|
||||
self.frame.sb.SetStatusText("File '%s' opened..."
|
||||
% filename,
|
||||
0)
|
||||
except EnvironmentError:
|
||||
self.frame.sb.SetStatusText("File creation failed "
|
||||
"(filename '%s')..."
|
||||
% filename,
|
||||
0)
|
||||
self.text.AppendText(string)
|
||||
|
||||
if move:
|
||||
self.text.ShowPosition(self.text.GetLastPosition())
|
||||
|
||||
if not hasattr(self,"no__debug__"):
|
||||
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(self.menuname,self.disableitem)
|
||||
self.othermenu.Enable(i,1)
|
||||
i = self.othermenu.FindMenuItem(self.menuname,self.enableitem)
|
||||
self.othermenu.Enable(i,0)
|
||||
|
||||
|
||||
Enabled = 1
|
||||
|
||||
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(self.menuname,self.disableitem)
|
||||
self.othermenu.Enable(i,0)
|
||||
i = self.othermenu.FindMenuItem(self.menuname,self.enableitem)
|
||||
self.othermenu.Enable(i,1)
|
||||
|
||||
if not hasattr(self,"no__debug__"):
|
||||
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: # typically 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 = 1
|
||||
|
||||
|
||||
def EnableOutput(self,
|
||||
event=None,# event must be the first optional argument...
|
||||
othermenubar=None,
|
||||
menuname="Debug",
|
||||
enableitem="Enable output",
|
||||
disableitem="Disable output"):
|
||||
|
||||
if othermenubar is not None:
|
||||
self.SetOtherMenuBar(othermenubar,
|
||||
menuname=menuname,
|
||||
enableitem=enableitem,
|
||||
disableitem=disableitem)
|
||||
self.Enabled = 1
|
||||
if self.f:
|
||||
self.f.close()
|
||||
self.f = None
|
||||
self.write("")
|
||||
|
||||
|
||||
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 = wx.FileDialog(self.frame,
|
||||
"Choose a new log file", self.dir,"","*",
|
||||
wx.SAVE | wx.OVERWRITE_PROMPT)
|
||||
if dlg.ShowModal() == wx.ID_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)
|
||||
if hasattr(self,"nofile"):
|
||||
self.frame.sb = _MyStatusBar(self.frame,
|
||||
callbacks=[self.DisableOutput,
|
||||
self.CloseFile,
|
||||
self.OpenNewFile])
|
||||
self.frame.SetStatusBar(self.frame.sb)
|
||||
if hasattr(self,"nofile"):
|
||||
delattr(self,"nofile")
|
||||
return 1
|
||||
|
||||
|
||||
def DisableOutput(self,
|
||||
event=None,# event must be the first optional argument...
|
||||
exiting=0):
|
||||
self.write("<InformationalMessagesFrame>.DisableOutput()\n")
|
||||
if hasattr(self,"frame") \
|
||||
and self.frame is not None:
|
||||
self.OnCloseWindow("Dummy",exiting=exiting)
|
||||
self.Enabled = 0
|
||||
|
||||
|
||||
def close(self):
|
||||
self.DisableOutput()
|
||||
|
||||
|
||||
def flush(self):
|
||||
if self.text:
|
||||
self.text.SetInsertionPointEnd()
|
||||
wx.Yield()
|
||||
|
||||
|
||||
def __call__(self,* args):
|
||||
for s in args:
|
||||
self.write (str (s))
|
||||
|
||||
|
||||
def SetOutputDirectory(self,dir):
|
||||
self.dir = os.path.abspath(dir)
|
||||
## sys.__stderr__.write("Directory: os.path.abspath(%s) = %s\n"
|
||||
## % (dir,self.dir))
|
||||
|
||||
|
||||
|
||||
class Dummy_PyInformationalMessagesFrame(object):
|
||||
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
|
||||
|
||||
Reference in New Issue
Block a user