0

I have a simple 'brut' big text file (20MB). I would like to show it in a TRichEdit. The problem is that it takes 6 seconds for the showing. I would like to put a progess bar in the bottom of the application to avoid this bad UX design.

My question is how to get the progress of the TRichEdit showing ? With the TRichEdit::LoadFromStream method, it goes from 0 to 100% fast (less than 1 second) but after the application wait 6 secondes during the first show.

I created this class FileStreamProgress with TFileStream inherit. I overide the TFileStream::Read()

    int __fastcall FileStreamProgress::Read(void *Buffer, int Count)
    {
        __int64 previousPosition = this->Position;
        int ret = TFileStream::Read(Buffer, Count);
        if (this->Position == 0 || this->Position == this->Size || (previousPosition/128000) != (this->Position/128000)) {
            ProgressCallBack(ProgressCallBackParam1, this->Position, this->Size);
        }
        return ret;
    }
    static void FileStreamProgress::ProgressCallBack(void*thiz, int i, int max)
    {
        TProgressBar* ProgressBar = (TProgressBar*)thiz;
        if (ProgressBar)
        {
            if (max > 0)
            {
                ProgressBar->Position = int(i * 100 / max);
            }

            if (Application)
            {
                Sleep(1);
                Application->ProcessMessages();
            }
        }
    }        

This is how I test it :

void MyApp::CreatePage(AnsiString filename)
{
    ProgressBar->Visible = true;
    FileStreamProgress::ProgressCallBackParam1 = (void*)this->ProgressBar;
    TFileStream * stream = new FileStreamProgress(filename.c_str(), fmOpenRead);
    TPageMemo* Page = new TPageMemo(this);

    Page->Parent = PControl;
    Page->PageControl = PControl;

    MessageDlg("111",mtError,TMsgDlgButtons()<<mbOK,0);
    Page->Texte->Lines->LoadFromStream(stream);
    MessageDlg("222",mtError,TMsgDlgButtons()<<mbOK,0);

    PControl->ActivePage = Page;
}

There are 7 secondes between the 2 message dialogs "111" and "222". And my progress bar wait 6 secondes at 100% (during the showing)

Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770
John Smith
  • 45
  • 3
  • the 6 seconds is probably the reformatting of the raw txt into rich edit format, I do not think you can tap into it to dig out the progress state directly without changing the `TRitchEdit` component source code (or created your own component class derived from it) ... how long it takes to copy already loaded `TrichEdit` text into onother `TRitchEdit` component ? If its fast you can load the file by chunks into invisible componont and appending it to the visible one counting progress ... I can not test this as I am bound to old BDS2006 without `TRitchEdit` component I got only `TMemo`... – Spektre Feb 05 '20 at 14:05
  • 1
    @Spektre `TRichEdit` was introduced in Delphi/C++Builder 2, so it certainly exists in BDS 2006. – Remy Lebeau Feb 05 '20 at 18:28
  • @JohnSmith I would suggest you get rid of the `Sleep()` and `Application->ProcessMessages()` calls, they are just wasted overhead and slow down your code. Call `ProgressBar->Update()` or `MyApp->Update()` instead to update your UI without processing other pending messages. And I would change `(previousPosition/128000) != (this->Position/128000)` to simply `previousPosition != this->Position` and let the callback handle throttling UI updates as needed. Also, you should change `int i, int max` to use `__int64` instead of `int` since `Position` and `Size` are `__int64`. – Remy Lebeau Feb 05 '20 at 18:37
  • @RemyLebeau will search it then :) do you know at which page of components it is on? – Spektre Feb 05 '20 at 22:14
  • 1
    @Spektre same page it has always been on - Win32 – Remy Lebeau Feb 05 '20 at 23:06
  • @RemyLebeau heh yep I see it now :) looks like I has overlooked that one for ages. Always taught it was 3th party component and or added in newer versions ... – Spektre Feb 06 '20 at 01:19
  • @Spektre this is why, in modern IDE versions, you should use the Tool Palette instead of the Component Palette. The Tool Palette is context-sensitive (ie, when the Form is focused, the Tool Palette will display components that can be placed on the Form) and has a search filter that works with substrings. You could just do a search for `rich` and `TRichEdit` should appear – Remy Lebeau Feb 06 '20 at 01:24
  • @RemyLebeau heh I gave it a shot and looks like it works can you **please** check If I did not miss something obvious in the `TRichEdit` usage? – Spektre Feb 06 '20 at 15:50

2 Answers2

0

I tried to go deeper with the win32 API's SendMessage and Handle without the expected result.

At the end, I used a TMemo yesterday cuz it's a brut text. It's very fast (instant open) but some functions are missing like FindTextW(). I rewrote it. Thanks

http://docwiki.embarcadero.com/RADStudio/Rio/en/Memo_and_Rich_Edit_Controls

Community
  • 1
  • 1
John Smith
  • 45
  • 3
0

was curious so I tested the TRichEdit and came up with this:

//---------------------------------------------------------------------------
void load_text(TRichEdit *re,AnsiString filename,TProgressBar *pb)
    {
    // variables
    int hnd,adr,siz,len,i;
    const int _buf=128*1024;                    // buffer size
    AnsiString s,s0;
    char buf[_buf+1];
    // open file and detect size
    hnd=FileOpen(filename,fmOpenRead); if (hnd<0) return;
    siz=FileSeek(hnd,0,2);
        FileSeek(hnd,0,0);
    // prepare progress bar
    pb->Position=0;
    pb->Max=siz;
    pb->Visible=true;
    // process txt file by chunks
    for (s0="",adr=0;adr<siz;)
        {
        // update progress bar and GUI
        pb->Position=adr;
        Application->ProcessMessages();
        // load chunk
        len=FileRead(hnd,buf,_buf);
        adr+=len; buf[len]=0;
        // handle cutted lines by chunk size
        s=s0; s0="";
        // ignore last 2 lines for chunks (except last chunk)
        if (len==_buf)
            {
            // skip last line
            for (i=len-1;i>=0;i--)
             if ((buf[i]==13)||(buf[i]==10))
                {
                i--;
                if (i>=0)
                 if (buf[i]!=buf[i+1])              // different eol code to ignore empty line
                  if ((buf[i]==13)||(buf[i]==10))   // eol code
                   i--;
                break;
                }
            // skip another line to avoid inserting empty line if eol is cutted
            for (;i>=0;i--)
             if ((buf[i]==13)||(buf[i]==10))
                {
                s0+=buf+i+1;                        // copy last 2 lines into s0
                i--;
                if (i>=0)
                 if (buf[i]!=buf[i+1])              // different eol code to ignore empty line
                  if ((buf[i]==13)||(buf[i]==10))   // eol code
                   i--;
                i++; if (i<0) i=0; buf[i]=0;        // end of string
                break;
                }
            }
        // last chunk ignore last eol
        else{
            // skip last line
            i=len-1;
            if ((buf[i]==13)||(buf[i]==10))         // eol code
                {
                i--;
                if (buf[i]!=buf[i+1])               // different eol code to ignore empty line
                 if ((buf[i]==13)||(buf[i]==10))    // eol code
                  i--;
                i++; if (i<0) i=0; buf[i]=0;        // end of string
                }
            }
        // add actual chunk
        s+=buf;
        re->Lines->Add(s);
        }
    // tidy up
    pb->Visible=false;
    FileClose(hnd); hnd=-1;
    }
//---------------------------------------------------------------------------

Looks like it works without the ending pause you describe however that might be related to version of IDE/VCL/compiler I am using BDS2006 Turbo C++. When Tested on ~23 MByte STL file the load time is ~10sec (TMemo takes twice of that god know why)...

The saved file (while PlainText=true) is identical to the loaded one so the code should be correct.

Here animated GIF of preview:

preview

while used like this:

void __fastcall TForm1::FormActivate(TObject *Sender)
    {
    //tbeg();
    load_text(re_txt,"in.txt",pb_progress);
    //tend();
    //Caption=tstr();
    re_txt->Lines->SaveToFile("out.txt");
    }

where pb_progress is the TProgressBar and and re_txt is the TRichEdit.

As you can see no callback is needed ...

PS. If you want to measure the time like I did (the commented lines) the implementations of tbeg/tend/tstr functions are here:

Spektre
  • 49,595
  • 11
  • 110
  • 380