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:
- 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()
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.
SetCurrentPos(self.GetTextLength())