wx.stc.StyledTextCtrl: any performance tips?

I'm in love with the StyledTextCtrl, however performance really
sucks on anything more than 20 lines or so of text when the
lexer is set.

Commenting the following line makes performance acceptable even
on lots of lines (>1000):

self.SetLexer(stc.STC_LEX_PYTHON)

IOW, when the lexer is set performance crawls. Otherwise, it is
acceptable, but still way slower than using Kate to edit the
same files.

Does anyone know any tricks to get better performance out of
STC?

I'm on GTK2 / wxPython 2.5.2.7

Thanks!

···

--
Paul McNett
Independent Software Consultant
http://www.paulmcnett.com

Paul McNett writes:

I'm in love with the StyledTextCtrl, however performance
really sucks on anything more than 20 lines or so of text
when the lexer is set.

Update: even with the lexer set, if I zoom all the way to the
max, performance is very very good, excellent in fact. At a
zoom factor of 0, performance is poor. As I go to -20,
performance gets worse and worse. This is on Linux wxPython
2.5.2.7 Unicode GTK2.

I tested on Windows, and performance is excellent at all zoom
levels. However, random keys get eaten.

I tested on OSX, and performance is better than Linux but not
nearly as good as Windows, and there are some strange display
anomolies as keys get pressed but other than that it seems to
work well.

Just some observations. I'm going to check to see if changing to
a large default font on Linux affects the performance, or if it
is zoom level.

···

--
Paul McNett
Independent Software Consultant
http://www.paulmcnett.com

Paul McNett wrote:

Paul McNett writes:

I'm in love with the StyledTextCtrl, however performance
really sucks on anything more than 20 lines or so of text
when the lexer is set.

Update: even with the lexer set, if I zoom all the way to the max, performance is very very good, excellent in fact. At a zoom factor of 0, performance is poor. As I go to -20, performance gets worse and worse. This is on Linux wxPython 2.5.2.7 Unicode GTK2.

I tested on Windows, and performance is excellent at all zoom levels. However, random keys get eaten.

I tested on OSX, and performance is better than Linux but not nearly as good as Windows, and there are some strange display anomolies as keys get pressed but other than that it seems to work well.

Just some observations. I'm going to check to see if changing to a large default font on Linux affects the performance, or if it is zoom level.

It's probably the fact that more text is displayed with the smaller fonts, and so more font measuring is being done. For every style run Scintilla measures the position from the start of the run to the begining of each character in the run (so it knows not only the full size of the run but also where to place the caret, etc.)

It used to be a lot worse than it is now, but I added wxDC::GetPartialTextExtents to wxWidgets to do this kind of measuring using native APIs where available. Some work better than others. In fact, last I checked the natvie API for GTK2 (I don't remember right now what it was called) performed worse than doing it in a brute force manner so wxGTK2 is still doing it the old way. If you're interested in improving this then finding a new native implementation for wxGTK2's DoGetPartialTextExtents would be the first step.

···

--
Robin Dunn
Software Craftsman
http://wxPython.org Java give you jitters? Relax with wxPython!

Robin Dunn writes:

It's probably the fact that more text is displayed with the
smaller fonts, and so more font measuring is being done.

Right. I also discovered that if I make the window only show a
couple rows and 10 or so columns, the performance is good.

For
every style run Scintilla measures the position from the
start of the run to the begining of each character in the run
(so it knows not only the full size of the run but also where
to place the caret, etc.)

So it needs to do a complete style run after every key pressed?
Do you have any hints on how to set up a timer or something to
only let the style runs happen say once per second? I'll
actually play around with that idea and get back to this thread
if it seems to be panning out.

It used to be a lot worse than it is now, but I added
wxDC::GetPartialTextExtents to wxWidgets to do this kind of
measuring using native APIs where available. Some work
better than others. In fact, last I checked the natvie API
for GTK2 (I don't remember right now what it was called)
performed worse than doing it in a brute force manner so
wxGTK2 is still doing it the old way. If you're interested
in improving this then finding a new native implementation
for wxGTK2's DoGetPartialTextExtents would be the first step.

I'm afraid that is beyond my ability at the moment, but if the
itch really starts to hurt I may just have to scratch it.
Thanks for the pointers Robin!

···

--
Paul McNett
Independent Software Consultant
http://www.paulmcnett.com

Paul McNett wrote:

Robin Dunn writes:

For
every style run Scintilla measures the position from the
start of the run to the begining of each character in the run
(so it knows not only the full size of the run but also where
to place the caret, etc.)

So it needs to do a complete style run after every key pressed?

By style run I mean each run of a single style before the style changes. So if a line has 5 different sets of font/fg/bg on it then it has 5 style runs. I think that only the current line is remeasured on each keypress, and when an EVT_PAINT happens then all lines in the update region will be remeasured to do the paint...

Do you have any hints on how to set up a timer or something to only let the style runs happen say once per second?

You may be able to control the lexing with EVT_STYLE_NEEDED (although it may not be sent if you are usign a stock lexer...) but once the styles are set in the buffer then it has to remeasure every time it paints in order to know where to place each piece of text.

···

--
Robin Dunn
Software Craftsman
http://wxPython.org Java give you jitters? Relax with wxPython!

Robin Dunn writes:

> Do you have any hints on how to set up a timer or something
> to only let the style runs happen say once per second?

You may be able to control the lexing with EVT_STYLE_NEEDED
(although it may not be sent if you are usign a stock
lexer...) but once the styles are set in the buffer then it
has to remeasure every time it paints in order to know where
to place each piece of text.

I've been successful in improving performance significantly, at
the cost of delaying the styling. Basically (pseudocode: the
Dabo code wouldn't be helpful to the discussion) here's what I
do:

class MySTC(stc.StyledTextCtrl):
  def __init__(self, ...):
    # EVT_STYLE_NEEDED only sent if LEX_CONTAINER
    self.SetLexer(stc.STC_LEX_CONTAINER)
    self.Bind(stc.STC_EVT_STYLE_NEEDED, self.onStyleNeeded)

  def onStyleNeeded(self, evt):
    # Here, I create a timer with a .5 second interval that sets
    # the lexer to stc.STC_LEX_PYTHON, and then creates a
    # second timer which then sets the lexer back to
    # stc.STC_LEX_CONTAINER.

I found I needed the two timers in order for it to work
correctly. Also I should mention that it appears that some of
my EVT_TIMER's are being eaten by someone else as at certain
intervals my timer callback isn't being called at all.

Now, as I type it is fast because no styling is happening, and
when I stop typing the styling will happen after a .5 second
wait. The feel is much better.

···

--
Paul McNett
Independent Software Consultant
http://www.paulmcnett.com