List Info

Thread: The wxPython StyledTextCtrl, unicode-ansi tests.




The wxPython StyledTextCtrl, unicode-ansi tests.
user name
2006-07-17 20:30:58
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-unsubscribelists.wxwidgets.org
For additional commands, e-mail: wxPython-dev-helplists.wxwidgets.org

[1]

about | contact  Other archives ( Real Estate discussion Medical topics )