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.
This demo has been tested by the author on:
- Window XP with Python 2.4 and wxPython 2.6.3
- Debian GNU/Linux 3.1(Sarge) with Python 2.3 and wxPython 2.6.3
and by others on:
- (add your platform here if different from above)
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()
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.
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 ?
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.