Making RichTextControl scroll so typed text in visible.

The problem I'm having involves using RichTextControl and EVT_CHAR to
capture characters that user types, and then I write a different
character in response. That is, instead of using the default
character writing, I use WriteText with the character I choose. The
specific issue is that when the entries run beyond the vertical length
of the window and it starts to scroll, the entries are no longer
visible. I've tried using GetInsertionPoint and ShowPosition to make
this visible, but oddly, this only works for every other line of
entered text, where the alternate lines are invisible as they are
being typed.

Below is a small example that just writes random characters. Note
that if you enter characters beyond the length of the window, the
lines are only alternately visible, but if the Bind line is commented
out, the visibility is fine.

I would be very grateful if anyone has any ideas on this. The default
behavior is very crisp and nice looking, but I just can't seem to re-
create it with my own management.
Tom

import wx
import wx.richtext as rtcm
import random

class MyRTC(rtcm.RichTextCtrl):

    def __init__(self, parent):
        rtcm.RichTextCtrl.__init__(self, parent, -1,
                    style=wx.VSCROLL|wx.HSCROLL|wx.NO_BORDER|
wx.WANTS_CHARS)
        self.BeginFontSize(20)
        # comment out the line below to get correct behavior
        self.Bind(wx.EVT_CHAR, self.VisibleWrite)

    def VisibleWrite(self, evt):
        x = evt.GetKeyCode()
        if x>=33 and x<=126: # make a random char for each typed
char.
            x = random.randrange(33, 126+1)
        self.WriteText(unichr(x))
        # the lines below should make the typed text visible, but
don't
        ipos = self.GetInsertionPoint()
        self.ShowPosition(100000)

app = wx.PySimpleApp()
frame = wx.Frame(None, -1, "RTC demo", size=(100,100))
frame.Show(True)
tc = MyRTC(frame)
app.MainLoop()

The problem I'm having involves using RichTextControl and EVT_CHAR to
capture characters that user types, and then I write a different
character in response. That is, instead of using the default
character writing, I use WriteText with the character I choose. The
specific issue is that when the entries run beyond the vertical length
of the window and it starts to scroll, the entries are no longer
visible. I've tried using GetInsertionPoint and ShowPosition to make
this visible, but oddly, this only works for every other line of
entered text, where the alternate lines are invisible as they are
being typed.

There's probably a bug in ShowPosition where it is not scrolling enough to make the whole line visible, but just part of it. (I see about half the line when I run your sample.) It would be a good idea to create a bug report about this.

Below is a small example that just writes random characters. Note
that if you enter characters beyond the length of the window, the
lines are only alternately visible, but if the Bind line is commented
out, the visibility is fine.

The default char handler calls a method named ScrollIntoView after each char is inserted to help it ensure that the widget is scrolled to the right place. Unfortunately the method is private so it's not accessible from Python. You can see the source code here, at about line 1240, if you want to try and duplicate what it does in your own method:

http://trac.wxwidgets.org/browser/wxWidgets/branches/WX_2_8_BRANCH/src/richtext/richtextctrl.cpp

···

On 12/11/09 1:19 PM, Tom wrote:

--
Robin Dunn
Software Craftsman

Thanks for the advice Robin. Based on the link you mentioned, I wrote
my own Python method to control the scrolling so the cursor stays on
the screen. I've included it below (called _ShowPosition in the
code). It's not thoroughly tested, but is seems to work well enough
on my Mac running Snow Leopard and wxPython 2.8.10.1

I set this up to work a little differently than the default. Here, if
one is entering characters at the end of the document it automatically
scrolls so that the entered characters are on screen. Also, though,
if one is entering characters in the middle of a document, when the
cursor goes off screen, it scrolls so that the cursor moves to the
middle of the window.

Thanks again,
Tom

import wx
import wx.richtext as rtcm

class MyRTC(rtcm.RichTextCtrl):

    def __init__(self, parent):
        rtcm.RichTextCtrl.__init__(self, parent, -1, style=wx.VSCROLL|
wx.HSCROLL|wx.NO_BORDER|wx.WANTS_CHARS)
        self.BeginFontSize(20)
        self.Bind(wx.EVT_KEY_DOWN, self.VisibleWrite)
        self.center_caret = True

    def VisibleWrite(self, evt):
        x = evt.GetKeyCode()
        self.WriteText(unichr(x))
        ipos = self.GetInsertionPoint()
        self._ShowPosition(ipos)

    def _ShowPosition(self, ipos):
        line = self.GetVisibleLineForCaretPosition(ipos)
        ppuX, ppuY = self.GetScrollPixelsPerUnit() #unit = scroll
step
        startYUnits = self.GetViewStart()[1]
        sy = self.GetVirtualSize()[1]
        if ppuY==0:
            return False # since there's no scrolling, hence no
adjusting
        syUnits = sy/ppuY
        r = line.GetRect()
        ry = r.GetY()
        rh = r.GetHeight()
        csY = self.GetClientSize()[1]
        csY -= self.GetBuffer().GetBottomMargin()

        if self.center_caret:
            if ry >= startYUnits*ppuY + csY - rh/2:
                yUnits = startYUnits + csY/ppuY/2
                self.SetScrollbars(ppuX, ppuY, 0, syUnits, 0, yUnits)
                self.PositionCaret()
                return True
        return False

app = wx.PySimpleApp()
frame = wx.Frame(None, -1, "RTC demo", size=(100,100))
frame.Show(True)
tc = MyRTC(frame)
app.MainLoop()

···

On Dec 11, 6:12 pm, Robin Dunn <ro...@alldunn.com> wrote:

On 12/11/09 1:19 PM, Tom wrote:

> The problem I'm having involves using RichTextControl and EVT_CHAR to
> capture characters that user types, and then I write a different
> character in response. That is, instead of using the default
> character writing, I use WriteText with the character I choose. The
> specific issue is that when the entries run beyond the vertical length
> of the window and it starts to scroll, the entries are no longer
> visible. I've tried using GetInsertionPoint and ShowPosition to make
> this visible, but oddly, this only works for every other line of
> entered text, where the alternate lines are invisible as they are
> being typed.

There's probably a bug in ShowPosition where it is not scrolling enough
to make the whole line visible, but just part of it. (I see about half
the line when I run your sample.) It would be a good idea to create a
bug report about this.

> Below is a small example that just writes random characters. Note
> that if you enter characters beyond the length of the window, the
> lines are only alternately visible, but if the Bind line is commented
> out, the visibility is fine.

The default char handler calls a method named ScrollIntoView after each
char is inserted to help it ensure that the widget is scrolled to the
right place. Unfortunately the method is private so it's not accessible
from Python. You can see the source code here, at about line 1240, if
you want to try and duplicate what it does in your own method:

wxTrac has been migrated to GitHub Issues - wxWidgets

--
Robin Dunn
Software Craftsmanhttp://wxPython.org