StyledTextCtrl Log Window Demo

I often here people bemoan the lack of a 'simple' demo for the StyledTextCtrl, so I present here the simplest possible useful application of the control that I can think of.

This demo shows how to use a StyledTextCtrl as a log window with the ability to display messages in colour and that allows users to zoom the text with the keyboard(Ctrl + or Ctrl -) and the mouse ( Ctrl Scroll Wheel.)

I hope to show in this demo that although the StyledTextCtrl is a complicated beast and takes a lot of effort to master, it is possible to achieve very worthwhile results with very little effort.

When I say that this is the simplest useful application of a StyledTextCtrl that I can think of, thats not quite true. This application would still be worthwhile even if the colour facility was left out. Whenever you use the control in wxPython you get the ability to zoom text for free, it comes as standard, you don't need to do anything to enable it.

I get very annoyed with programs that display log messages, and scrolled message dialogs, in tiny fonts that I can barely read. ( Linux programs seem to be particularly susceptible to this crime.) Be kind to yourself and your users, use a StyledTextCtrl for log and message output.

Platform

This demo has been tested by the author on:

and by others on:

   1 ##  StyledTextCtrl Log Window Demo
   2 #
   3 # Last modified: 23 July 2006
   4 #
   5 # Tested On:
   6 #       Window XP with Python 2.4 and wxPython 2.6.3 (Unicode)
   7 #       Debian GNU/Linux 3.1 (Sarge) with Python 2.3 and wxPython 2.6.3 (Unicode)
   8 #
   9 # The purpose of this program is to illustrate a very simple but useful
  10 # application of a StyledTextCtrl.
  11 #
  12 # The StyledTextCtrl is complicated and some people find it hard to get
  13 # started with it.  This demo, shows that programers can start to reap the
  14 # benefits of using a StyledTextCtrl with very little effort.
  15 #
  16 # Normally a wx.Text control is used for log windows, however, using a
  17 # StyledTextCtrl has the advantage of allowing the user to zoom the text
  18 # in the control using CTRL-+ and CTRL--.  This facility is availiable by
  19 # default, you get if for free!, and you have the option of using coloured
  20 # messages too.
  21 #
  22 
  23 import wx
  24 import wx.stc as stc
  25 
  26 class Log(stc.StyledTextCtrl):
  27     """
  28     Subclass the StyledTextCtrl to provide  additions
  29     and initializations to make it useful as a log window.
  30 
  31     """
  32     def __init__(self, parent, style=wx.SIMPLE_BORDER):
  33         """
  34         Constructor
  35         
  36         """
  37         stc.StyledTextCtrl.__init__(self, parent, style=style)
  38         self._styles = [None]*32
  39         self._free = 1
  40         
  41     def getStyle(self, c='black'):
  42         """
  43         Returns a style for a given colour if one exists.  If no style
  44         exists for the colour, make a new style.
  45         
  46         If we run out of styles, (only 32 allowed here) we go to the top
  47         of the list and reuse previous styles.
  48 
  49         """
  50         free = self._free
  51         if c and isinstance(c, (str, unicode)):
  52             c = c.lower()
  53         else:
  54             c = 'black'
  55         
  56         try:
  57             style = self._styles.index(c)
  58             return style
  59             
  60         except ValueError:
  61             style = free
  62             self._styles[style] = c
  63             self.StyleSetForeground(style, wx.NamedColour(c))
  64 
  65             free += 1
  66             if free >31:
  67                 free = 0
  68             self._free = free
  69             return style
  70 
  71     def write(self, text, c=None):
  72         """
  73         Add the text to the end of the control using colour c which
  74         should be suitable for feeding directly to wx.NamedColour.
  75         
  76         'text' should be a unicode string or contain only ascii data.
  77         """
  78         style = self.getStyle(c)
  79         lenText = len(text.encode('utf8'))
  80         end = self.GetLength()
  81         self.AddText(text)
  82         self.StartStyling(end, 31)
  83         self.SetStyling(lenText, style)
  84         self.EnsureCaretVisible()
  85         
  86 
  87     __call__ = write
  88 
  89 
  90 class TestPanel(wx.Panel):
  91     def __init__(self, parent, log):
  92         self.log = log
  93         self.colour = 'black'
  94         log('Welcom to wxPython %s' % wx.VERSION_STRING, 'blue')
  95         log('\n\n[')
  96         log(u'Unicode Test 1: \u041f\u0438\u0442\u043e\u043d \u0435 \u043d\u0430\u0439-\u0434\u043e\u0431\u0440\u0438\u044f \u043f\u0440\u043e\u0433\u0440\u0430\u043c\u0435\u043d \u0435\u0437\u0438\u043a!', 'red')
  97         log(']')
  98         log('\n\n[')
  99         log(u'Unicode Test 2: \u041f\u0438\u0442\u043e\u043d - \u043b\u0443\u0447\u0448\u0438\u0439 \u044f\u0437\u044b\u043a \n\u043f\u0440\u043e\u0433\u0440\u0430\u043c\u043c\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f!', 'blue')
 100         log(']')
 101 
 102         log(u'\n\n[Unicode Test 3: x')
 103         log(u'\u00a3', 'red')
 104         log(u'x]')
 105 
 106         log('\n\nUse your mouse buttons to click on the')
 107         log(' coloured ', 'cyan')
 108         log('panel and ')
 109         log('buttons ', 'red')
 110         log( 'above.')
 111         log('\n\nUse ')
 112         log('Ctrl-+ and Ctrl-- or Ctrl-ScrollWheel', 'blue')
 113         log(' to zoom text in this window.\n')
 114         wx.Panel.__init__(self, parent, -1)
 115 
 116         self.SetBackgroundColour('cyan')        
 117         
 118         sizer = wx.BoxSizer(wx.HORIZONTAL)
 119 
 120         self.Bind(wx.EVT_LEFT_DOWN, lambda e:self.OnMouse(e, 'Left'))
 121         self.Bind(wx.EVT_RIGHT_DOWN, lambda e:self.OnMouse(e, 'Right'))
 122         self.Bind(wx.EVT_MIDDLE_DOWN, lambda e:self.OnMouse(e, 'Middle'))
 123 
 124         for i in ('red', 'green', 'blue', 'cyan', 'magenta'):
 125             btn = wx.Button(self, -1, i)
 126             sizer.Add(btn, 1, wx.TOP|wx.BOTTOM, 15)
 127             btn.Bind(wx.EVT_BUTTON, self.OnButton)
 128         
 129         self.SetSizer(sizer)
 130         
 131     def OnMouse(self, event, type):
 132         self.log('\n\n%s Mouse Button Clicked'%type, self.colour)
 133         event.Skip()
 134         
 135     def OnButton(self, event):
 136         btn = event.GetEventObject()
 137         label = btn.GetLabel()
 138         self.colour = label
 139         self.log('\n\n%s button clicked'%label, label)
 140         event.Skip()
 141 
 142 
 143 class TestFrame(wx.Frame):
 144 
 145     def __init__(self):
 146         wx.Frame.__init__(self, None, -1, 'StyledTextCtrl Log Panel Demo')
 147 
 148         log = Log(self)
 149         tp = TestPanel(self, log)
 150         sizer = wx.BoxSizer(wx.VERTICAL)
 151         sizer.Add(tp, 0, wx.EXPAND)
 152         sizer.Add(log, 1, wx.EXPAND)
 153         self.SetSizer(sizer)
 154         self.SetSize((400, 300))
 155 
 156         
 157 if __name__=="__main__":
 158     app = wx.PySimpleApp()
 159     win = TestFrame()
 160     win.Show(True)
 161     app.MainLoop()
colourlog.py


Requests

If you would like to see a particular feature of the StyledTextCtrl demonstrated, state your request below in 20 words or less. If you are specific and concise, there is a small but finite probability that your request will be considered.

Q) Request: When using a XML lexer, I would like to see an example how to enable (un)folding of XML sections.

A) Replacing YourStc with the instance of your StyledTextCtrl and making these two calls should do

YourStc.SetProperty("fold", "1")
YourStc.SetProperty("fold.html", "1")

Q) Using XML, is there an option to hand over a DTD or XSD to the wxStyledTextCtrl for the AutoCompletion feature ?


Comments

Any comments and suggestions will be welcome and you can be as verbose as you like in this section. Bug reports most welcome.

Don't forget to set cursor position

Don't forget to issue the following statement before calling AddText(). This statement will restore the cursor position to the end of text, before the insertion of the new text. This will give you the "append" behaviour you want.

Otherwise, in case the user plays with the log window (which in the example is NOT read only), or places the cursor somewhere in the log window, the text will not be inserted in the correct place. Also your nice formatting might be messed. So to avoid this, use the below statement.

StyledTextCtrl Log Window Demo (last edited 2008-03-11 10:50:25 by localhost)

NOTE: To edit pages in this wiki you must be a member of the TrustedEditorsGroup.