There’s editor-like GUI app: Frame->Panel->MultiSplitterWindow->Panel->STC.
I’m implementing Search function as LongRunning, simplified excerpt:
while self.SearchInTarget(stoken) > 0:
highlighter.FillRange((self.GetTargetStart(), self.GetTargetEnd()))
self.SetTargetStart(self.GetTargetEnd())
self.SetTargetEnd(endpos)
total_found += 1
start = time.time()
wx.YieldIfNeeded()
end = time.time()
res += end-start
print('Yield took avg time:', res/total_found)
In case the Doc contains large text data and many found tokens, the search runs much longer, but especially when the Doc is scrolled down even for few hundreds lines (of tens thousands).
By the measurement I observed that wx.Yield actually runs much longer.
Note, I collect search word characters in Queue, then using Thread to read each and send a message to MainThread by QueueEvent (CallAfter/Later were tried as well) with custom event to launch the search loop.
Could someone describe the reason of the scrolling impact and can I work it around?
Thanks.
well, your yielding & scrolling both run on one and the same queue and therefore the more you scroll the less yielding will be done in case the queue is full
the standard remedy is to assign those two tasks to different resources if there are any (best, of course is, to different cores)
Thanks for reply, however I’ve taken it to account. I have not start search before scrolling and even waited some time upon file loaded and scrolling (“goto line” as well) completed. Of course, many additional searches have been performed as well to empty the events queue, but it changed nothing. BTW, if I scroll up to top, it works fast like w/out scrolling, as there should be delay by your logic.
Use extreme caution when calling this function as, just as EvtLoopBase.Yield(), it can result in unexpected reentrances.
It’s difficult to say without having a runnable app, but perhaps the combination of YieldIfNeeded() and the scrolling is causing some code to be called recursively?
When the InspectionTool window appears, click on the Events button on the toolbar. An ‘Events Watcher’ window should then appear which will display the events occurring in the Frame (try moving the mouse over Frame or resizing it).
Thanks Richard. EventFilter did the job - I observed and counted EVT_PAINT that has sent during the search loop. The problem is in massive EVT_PAINT sending when the doc is scrolled down, then searched.
However, when the doc is not scrolled or hidden (Frame collapsed or Splitter sash moved to hide the view) no massive repainting occurs. As you can see in the code, each found token has been highlighted by indicator, that sends repainting (does not if no highlighting).
I cannot realize why the scrolling causes to the doc repainting, obviously from line 0 to the last visible line (last one if fully scrolled). Search runs through the whole doc anyway, as only a screen of ~40 lines is visible. It is fast when it is the first screen, but looks like wxPython repaints the whole doc since 0 till last visible line, which takes larger time for Yield to end.
Now, the question is how to config this repainting.
In the wxWidgets source code for src/stc/ScintillaWX.cpp the following methods might be relevant:
void ScintillaWX::DoPaint(wxDC* dc, wxRect rect) {
paintState = painting;
AutoSurface surfaceWindow(dc, this);
if (surfaceWindow) {
rcPaint = PRectangleFromwxRect(rect);
PRectangle rcClient = GetClientRectangle();
paintingAllText = rcPaint.Contains(rcClient);
ClipChildren(*dc, rcPaint);
Paint(surfaceWindow, rcPaint);
surfaceWindow->Release();
}
if (paintState == paintAbandoned) {
// Painting area was insufficient to cover new styling or brace
// highlight positions. So trigger a new paint event that will
// repaint the whole window.
stc->Refresh(false);
#if wxALWAYS_NATIVE_DOUBLE_BUFFER
// On systems using double buffering, we also need to finish the
// current paint to make sure that everything is on the screen that
// needs to be there between now and when the next paint event arrives.
FullPaintDC(dc);
#endif
}
paintState = notPainting;
}
// Force the whole window to be repainted
void ScintillaWX::FullPaint() {
stc->Refresh(false);
stc->Update();
}
void ScintillaWX::FullPaintDC(wxDC* dc) {
paintState = painting;
rcPaint = GetClientRectangle();
paintingAllText = true;
AutoSurface surfaceWindow(dc, this);
if (surfaceWindow) {
Paint(surfaceWindow, rcPaint);
surfaceWindow->Release();
}
paintState = notPainting;
}
Sorry, I do not know what to do with that knowledge. Mass repainting occurs due to indicating of found tokens and, if STC is not scrolled, the screen is repainted, but if STC scrolled WHOLE doc area has been repainted. I’m not a “software crafter” to fix that bug or work it around by overriding, cause I don’t know which one and what’s the problem.
Meanwhile, I freezed STC window while searching loop running to prevent mass EVT_PAINT to be piled for Yield.
Thanks.
well, what you are doing there are infant steps of separating the resources of your machine many, many years ago
Scintilla is a powerful Text Editing control and if that runs in Python in a dedicated process satisfactorily that’s great luck
everything else must be offloaded to different processes and the results communicated to that main process for display etc
I think a fitting concept in this situation is a Process Pool Executor
I’ll tell you more, when STC works with small files it is a real mustang, just like that. Well, separating my beast to work in few processes instead of break a large file for smaller parts is much more mature for sure. Actually I just wanted to realize is there some problem with WX, as I’ve specified previously in the thread.