Robin Dunn,
A few days ago I promissed some report about the problem
using
the stc with the unicode version of wxPython.
Instead of reporting, I propose a test application, which
shows the problem a user (probably a non US-user) may meet.
You
can just toy with it. This is a ready to use application.
Unless I am doing something wrong, it shows, it is not
obvious
to have the stc methods working properly. Adding or getting
stc texts are not a problem (I also tested the *Raw And
*UTF8 methods).
Working with some other methonds, which depends on the
position or on
the "unicode char size" is more critical.
As far I understand, the stc is not a real unicode editor,
more a "std"
editor with some handy unicode functions. A full unicode
working
stc requires probably a lot of work.
Regards.
Jean-Michel Fauth, Switzerland.
# -*- coding: iso-8859-1 -*-
#-----------------------------------------------------------
---------
# Name: elephant.py
# Purpose: Some tests with stc (unicode - ansi)
# Author: Jean-Michel Fauth, Switzerland
# Copyright: (c) 2006 Jean-Michel Fauth
# Licence: ----
#-----------------------------------------------------------
---------
# os dev: w2k sp4
# py dev: Python 2.4.3
# wx dev: wxPython 2.6.3.2
# Revision: 17 July 2006
#-----------------------------------------------------------
---------
# By varying the AppMode, one can see the behaviour of the
stc
(ascii/unicode-utf-8).
# AppMode 1, 2 or 3
# 1 : 2.6-ansi, std
# 2 : 2.6-unicode, std
# 3 : 2.6-unicode, with some modified stc methods
# If AppMode == 1, everything is perfect.
# If AppMode == 2, does not work properly, the main reason
is due to the fact,
the stc
# return sizes, lengthes in bytes unit and not in a
"number of chars unit". This
is ok
# for English text ("ascii", code points in
range 0..127, 1 byte), does not work
very
# well in the Latin-1 world, code point in range 128..255
are represented by 2
bytes.
# If AppMode == 3, it is possible to get around with *some*
stc methods, however not
# really optimal.
import os
import time
import re
AppMode = 3
if AppMode == 1:
WXVER = '2.6-ansi'
elif AppMode in [2, 3]:
WXVER = '2.6-unicode'
else:
pass
import wxversion
wxversion.select(WXVER, True)
import wx
import wx.stc
#~ print wx.VERSION_STRING, wx.PlatformInfo[2]
#-----------------------------------------------------------
--------
def jmtime():
return time.strftime('[%X]')
def printu(*arg):
for e in arg:
if isinstance(e, unicode):
print e.encode('iso-8859-1', 'replace'),
else:
print e,
print
#-----------------------------------------------------------
--------
# a single instance of stc, but different behaviour based on
AppMode
class MySTC(wx.stc.StyledTextCtrl):
def __init__(self, parent, id, pos, size, style):
wx.stc.StyledTextCtrl.__init__(self, parent, id,
pos, size, style)
self.parent = parent
self.SetMarginLeft(2)
self.SetViewWhiteSpace(True)
self.SetTabWidth(4)
self.SetUseTabs(False)
self.SetViewEOL(True)
self.StyleDefault = 0
self.StyleOne = 1
self.StyleDefaultSpec = "face:Courier
New,size:10,fore:#000000,back:#FFFFFF"
self.StyleOneSpec = "face:Courier
New,size:10,fore:#000000,back:#00FFFF"
self.StyleLineNumberSpec = "face:Courier
New,size:10,fore:#000000,back:#F0F0F0"
#default style and clear
self.StyleSetSpec(wx.stc.STC_STYLE_DEFAULT,
self.StyleDefaultSpec)
self.StyleClearAll()
self.StyleSetSpec(self.StyleOne, self.StyleOneSpec)
#builtin styles
self.StyleSetSpec(wx.stc.STC_STYLE_LINENUMBER,
self.StyleLineNumberSpec)
#the 3 margins
self.SetMarginWidth(0, 40)
self.SetMarginWidth(1, 18)
self.SetMarginWidth(2, 0)
self.SetMarginType(0, wx.stc.STC_MARGIN_NUMBER)
self.SetMarginType(1, wx.stc.STC_MARGIN_SYMBOL)
self.WriteLog(wx.VERSION_STRING + ' ' +
wx.PlatformInfo[2] + ' AppMode:
' + str(AppMode))
#add some text
if AppMode == 1: #str
s = ''
s += 'éléphant' + os.linesep
s += 'A Ctrl+j selects the french word
éléphant in the lines 5..7.'
+ os.linesep
s += 'A Ctrl+k removes the selection.' +
os.linesep
s += 'A red éléphant and a white duck.' +
os.linesep
s += 'A blue éléphant and a green cow.' +
os.linesep
s += 'A pink éléphant and a black dog.' +
os.linesep
s += 'A red éléphant and a yellow bird.' +
os.linesep
s += 'A white éléphant and a brown cat.' +
os.linesep
self.AddText(s + os.linesep)
elif AppMode in [2, 3]: #unicode
u = ''
u += u'éléphant' + os.linesep
u += u'A Ctrl+j selects the french word
éléphant in the lines
5..7.' + os.linesep
u += u'A Ctrl+k removes the selection.' +
os.linesep
u += u'A red éléphant and a white duck.' +
os.linesep
u += u'A blue éléphant and a green cow.' +
os.linesep
u += u'A pink éléphant and a black dog.' +
os.linesep
u += u'A red éléphant and a yellow bird.' +
os.linesep
u += u'A white éléphant and a brown cat.' +
os.linesep
self.AddText(u + os.linesep)
else:
pass
self.Bind(wx.EVT_KEY_DOWN, self.OnKeyDown)
self.Bind(wx.stc.EVT_STC_UPDATEUI, self.OnUpdateUI)
# I do not pay attention to the encoding here
def WriteLog(self, txt):
self.parent.mytxt.AppendText(os.linesep + txt)
# length u in bytes
def lenu(self, u):
return len(u.encode('utf-8'))
def OnUpdateUI(self, event):
if AppMode == 1:
p = self.GetCurrentPos()
l = self.GetLength()
cint = self.GetCharAt(p)
s = '%s OnUpdateUI: pos in chars: %i, length
in chars: %i, char at
caret: %i %s' \
% (jmtime(), p, l, cint, chr(cint))
self.WriteLog(s)
elif AppMode == 2:
# Getting the char at the caret position is
practically impossible.
# The stc does not know the length in bytes of
the char following
the caret.
# I do not know how to solve this.
# Strangely, the stc get only the first byte of
a multibytes code
point, here en é.
# For exemple, in the word éléphant, if the
carret is just before
the first é,
# the returned value is 195, an
"Ä", moving the carret one step to
the right returns
# a correct "p".
# Looks like an an é read in a web browser with
a wrong encoded
page display setting "Ä ", (very common)
p = self.GetCurrentPos()
l = self.GetLength()
cint = self.GetCharAt(p)
s = '%s OnUpdateUI: pos in bytes: %i, length
in bytes: %i, char at
caret: %i %s' \
% (jmtime(), p, l, cint, chr(cint))
self.WriteLog(s)
elif AppMode == 3:
# It works, I do not thing it's optimal
p = self.GetCurrentPos() #pos in bytes
l = self.GetLength() # in bytes
u1 = self.GetTextRange(0, p)
u2 = self.GetTextRange(0, l)
pu = len(u1) #in chars
lu = len(u2) #in chars
s = '%s OnUpdateUI: pos in bytes: %i, length
in bytes: %i, pos in
chars: %i, length in chars: %s' \
% (jmtime(), p, l, pu, lu)
self.WriteLog(s)
else:
pass
event.Skip()
def OnKeyDown(self, event):
#~ print jmtime(), 'OnKeyDown:', AppMode
if AppMode == 1:
kc = event.KeyCode()
ControlDown = event.ControlDown()
AltDown = event.AltDown()
ShiftDown = event.ShiftDown()
if kc in (ord('J'), ord('j')) and
ControlDown and not AltDown and
not ShiftDown:
#remove all styling
p1, p2 = 0, self.GetLength()
self.StartStyling(p1, 0xff)
self.SetStyling(p2 - p1, self.StyleDefault)
#pos at start of the lines
p1 = self.PositionFromLine(3)
p2 = self.PositionFromLine(6)
#get text from p1 to p2, s is a str
s = self.GetTextRange(p1, p2).rstrip()
#with re, find all occurences of the word
éléphant and apply
some styling
#the tricky step of using an extracted
string is very common
some of my apps.
pat = 'éléphant'
cpat = re.compile(pat)
start = 0 #this is the start of s!
mo = cpat.search(s, start)
while mo != None:
#~ print 'mo:', mo.start(), mo.end()
self.StartStyling(p1 + mo.start(),
0xff)
self.SetStyling(mo.end() - mo.start(),
self.StyleOne)
start = mo.end()
mo = cpat.search(s, start)
elif kc in (ord('K'), ord('k')) and
ControlDown and not AltDown and
not ShiftDown:
#remove all styling
p1, p2 = 0, self.GetLength()
self.StartStyling(p1, 0xff)
self.SetStyling(p2 - p1, self.StyleDefault)
else:
pass
elif AppMode == 2:
print 'appmode: 2'
kc = event.KeyCode()
ControlDown = event.ControlDown()
AltDown = event.AltDown()
ShiftDown = event.ShiftDown()
if kc in (ord('J'), ord('j')) and
ControlDown and not AltDown and
not ShiftDown:
#remove all styling
p1, p2 = 0, self.GetLength()
self.StartStyling(p1, 0xff)
self.SetStyling(p2 - p1, self.StyleDefault)
#pos at start of the lines
p1 = self.PositionFromLine(4)
p2 = self.PositionFromLine(7)
#get text from p1 to p2, u is an unicode
u = self.GetTextRange(p1, p2).rstrip()
#~ printu(u)
#with re, find all occurences of the word
éléphant and apply
some styling
pat = 'éléphant'
cpat = re.compile(pat, re.U)
start = 0 #this is the start of u (Python
unicode)
mo = cpat.search(u, start)
while mo != None:
self.StartStyling(p1 + mo.start(),
0xff)
self.SetStyling(mo.end() - mo.start(),
self.StyleOne)
start = mo.end()
mo = cpat.search(u, start)
elif kc in (ord('K'), ord('k')) and
ControlDown and not AltDown and
not ShiftDown:
#remove all styling
p1, p2 = 0, self.GetLength()
self.StartStyling(p1, 0xff)
self.SetStyling(p2 - p1, self.StyleDefault)
else:
pass
elif AppMode == 3:
kc = event.KeyCode()
ControlDown = event.ControlDown()
AltDown = event.AltDown()
ShiftDown = event.ShiftDown()
if kc in (ord('J'), ord('j')) and
ControlDown and not AltDown and
not ShiftDown:
#remove all styling
p1, p2 = 0, self.GetLength()
self.StartStyling(p1, 0xff)
self.SetStyling(p2 - p1, self.StyleDefault)
#pos at start of the lines
p1 = self.PositionFromLine(4)
p2 = self.PositionFromLine(7)
#get text from p1 to p2, u is an unicode
u = self.GetTextRange(p1, p2).rstrip()
#~ printu( 'p1:', p1, 'p2:', p2)
#~ printu('type(u) and u:', type(u))
#~ printu(u)
#with re, find all occurences of the word
éléphant and apply
some styling
pat = u'éléphant'
cpat = re.compile(pat)
start = 0 #this is the start of u!
mo = cpat.search(u, start)
while mo != None:
#length of the unicode in bytes, used
for the stc, styling
start
tmpu = u[:mo.start()]
t = self.lenu(tmpu)
#length of the unicode in bytes, used
for the stc, styling
length
tmpu = u[mo.start():mo.end()]
tt = self.lenu(tmpu)
#apply styling in bytes units
self.StartStyling(p1 + t, 0xff)
self.SetStyling(tt, self.StyleOne)
start = mo.end()
mo = cpat.search(u, start)
elif kc in (ord('K'), ord('k')) and
ControlDown and not AltDown and
not ShiftDown:
#remove all styling
p1, p2 = 0, self.GetLength()
self.StartStyling(p1, 0xff)
self.SetStyling(p2 - p1, self.StyleDefault)
else:
pass
else:
pass
event.Skip()
#-----------------------------------------------------------
--------
class MyPanel(wx.Panel):
def __init__(self, parent, id):
wx.Panel.__init__(self, parent, id,
wx.DefaultPosition, wx.DefaultSize)
self.parent = parent
#log window
sty = wx.TE_MULTILINE
self.mytxt = wx.TextCtrl(self, wx.NewId(), '',
style=sty)
self.mytxt.SetFont(wx.Font(10, wx.MODERN,
wx.NORMAL, wx.NORMAL, False))
self.mystc = MySTC(self, wx.NewId(),
wx.DefaultPosition,
wx.DefaultSize, wx.SUNKEN_BORDER)
self.mystc.SetFocus()
b = 4
vsizer1 = wx.BoxSizer(wx.VERTICAL)
vsizer1.Add(self.mystc, 1, wx.EXPAND | wx.ALL, b)
vsizer1.Add(self.mytxt, 1, wx.EXPAND | wx.ALL, b)
self.SetSizer(vsizer1)
#-----------------------------------------------------------
--------
class MyFrame(wx.Frame):
def __init__(self, parent, id):
s = __file__
wx.Frame.__init__(self, parent, id, s, (0, 0),
(1000, 600))
panel = MyPanel(self, wx.NewId())
self.Bind(wx.EVT_CLOSE, self.OnCloseWindow)
def OnCloseWindow(self, event):
self.Destroy()
#-----------------------------------------------------------
--------
class MyApp(wx.App):
def OnInit(self):
frame = MyFrame(None, -1)
frame.Show(True)
self.SetTopWindow(frame)
return True
#-----------------------------------------------------------
--------
def main():
app = MyApp(0)
app.MainLoop()
#-----------------------------------------------------------
--------
if __name__ == "__main__" :
main()
#eof--------------------------------------------------------
-----------
------------------------------------------------------------
---------
To unsubscribe, e-mail: wxPython-dev-unsubscribe lists.wxwidgets.org
For additional commands, e-mail: wxPython-dev-help lists.wxwidgets.org
|