Forcing the position of scroll bars on a wxTextCtrl

  • Thread starter Clans Of Intrigue
  • Start date

C

Clans Of Intrigue

Hello, this is my first post here so apologies if it's in the wrong
place, inappropriate or embarrassingly stupid - please let me know :)

My problem seems quite simple - I've redirected stdout to a wxTextCtrl,
so that any trace messages appear in a log window at the bottom of my
app. The problem is that whenever I update the text, the scrollbar
resets to the top - i.e. to the earliest log message. What I really
want it to do is reset to the bottom, so that the most recent log
messages are on screen.

Calling SetScrollPos( GetScrollRange() ) sets the scrollbar slider to
the correct position, but *doesn't update the text in the window* :(

Is there a call to tell it to update the visible text based on the new
slider position? Or is there a better way to update the slider
position, e.g. by sending the control an event?

I'm new to wxWindows and new to Python, so any help (and as much
detail!) as possible would be appreciated! Here's the class I'm using
to capture stdout:

class LogControl:
""" Simple helper to redirect stdout to a panel in the GUI """
def __init__( self, textCtrl ):
self._ctrl = textCtrl
self._log = ""
self.write( "Application Started...\n" )

def write( self, Message ):
self._log = self._log + Message
self._ctrl.SetValue( self._log )
# Force scroll bars to end of window - does not update text in
control!
self._ctrl.SetScrollPos( wx.VERTICAL,
self._ctrl.GetScrollRange( wx.VERTICAL) )

Thanks!
 
Ad

Advertisements

M

Magnus Lycka

Someone said:
Hello, this is my first post here so apologies if it's in the wrong
place, inappropriate or embarrassingly stupid - please let me know :)

No, that's ok. The wxpython mailing list might give better answers though.
My problem seems quite simple - I've redirected stdout to a wxTextCtrl,
so that any trace messages appear in a log window at the bottom of my
app. The problem is that whenever I update the text, the scrollbar
resets to the top - i.e. to the earliest log message. What I really
want it to do is reset to the bottom, so that the most recent log
messages are on screen.

It's a few years since I coded wx, but I guess you should do
something along the lines of:

self._ctrl.ShowPosition(self._ctrl.GetLastPosition())

Don't mess manually with the scroll bars (I think).

(I know I've gone through this once...)
 
C

Clans Of Intrigue

Thanks, that did the trick perfectly :)

also got rid of the self._log member so the class is now just:

class LogControl:
""" Simple helper to redirect stdout to a panel in the GUI """
def __init__( self, textCtrl ):
self._ctrl = textCtrl
self.write( "Application Started..." )

def write( self, Message ):
# Add message to log and force scroll bars to end of window
self._ctrl.SetValue( self._ctrl.GetValue() + Message )
self._ctrl.ShowPosition(self._ctrl.GetLastPosition())

Lovely :)
 
M

Magnus Lycka

Clans said:
Thanks, that did the trick perfectly :)

also got rid of the self._log member so the class is now just:

class LogControl:
""" Simple helper to redirect stdout to a panel in the GUI """
def __init__( self, textCtrl ):
self._ctrl = textCtrl
self.write( "Application Started..." )

def write( self, Message ):
# Add message to log and force scroll bars to end of window
self._ctrl.SetValue( self._ctrl.GetValue() + Message )
self._ctrl.ShowPosition(self._ctrl.GetLastPosition())

But if you have 100kB text in the control, and add another 1kB log
message, it certainly seems suboptimal to use:
self._ctrl.SetValue( self._ctrl.GetValue() + Message )
Here, you first copy all the text in the control to a Python
string, build a new string with old+new text, and replace the old
text with the context of this bigger string. You should simply use
self._ctrl.AppendText( Message ). I'm not sure exactly what happens
under he hood, but I suspect that's more efficient, besides being
prettier in the source...

Thou shalt study thy libraries and strive not to reinvent them
without cause, that thy code may be short and readable and thy
days pleasant and productive.

E.g. http://www.wxpython.org/docs/api/wx.TextCtrl-class.html
 
Ad

Advertisements

A

Andrea Gavana

Hello Clans,

as a first suggestion, it is usually recommended that you post a small
*working* sample, in order to help others in understanding the problem and
also to give others the possibility to test your code. Noting that I am not
able to run your code as it is, I can just speculate one suggestion:
Calling SetScrollPos( GetScrollRange() ) sets the scrollbar slider to
the correct position, but *doesn't update the text in the window* :(

This is because SetScrollPos only affects scrollbars, not the wx.TextCtrl
itself. What happens if you add a self._ctrl.Refresh() after the
SetScrollPos() call? Something like:

class LogControl:
""" Simple helper to redirect stdout to a panel in the GUI """
def __init__( self, textCtrl ):
self._ctrl = textCtrl
self._log = ""
self.write( "Application Started...\n" )

def write( self, Message ):
self._log = self._log + Message
self._ctrl.SetValue( self._log )
# Force scroll bars to end of window - does not update text in
control!
self._ctrl.SetScrollPos(wx.VERTICAL, self._ctrl.GetScrollRange(
wx.VERTICAL))
self._ctrl.Refresh()


I also suggest that you use AppendText() instead of the bunch of calls to
write(). Something along the lines (untested code!!!):

class LogControl:
""" Simple helper to redirect stdout to a panel in the GUI """
def __init__( self, textCtrl ):
self._ctrl = textCtrl
self._log = ""
self.write( "Application Started...\n" )

def write( self, Message ):
self._log = self._log + Message
self._ctrl.AppendText( self._log )
# Force scroll bars to end of window - does not update text in control!
# self._ctrl.SetScrollPos(wx.VERTICAL, self._ctrl.GetScrollRange(
wx.VERTICAL))
# self._ctrl.Refresh()


HTH.

Andrea.

"Imagination Is The Only Weapon In The War Against Reality."
http://xoomer.virgilio.it/infinity77
 

Top