Page 2 of 2

Re: [Android]TIdHTTP and TThread

PostPosted: Fri Jan 06, 2017 1:24 am
by Lena
You MUST synchronize...
Sync with the main UI thread when loading the Bitmap


Can you show С++ example of the combined use TThread::CreateAnonymousThread and synchronize loading the Bitmap for my case?

TBitmap class is not thread-safe


Some people say that it is better to use TBitmapSurface for loading the bitmap thread-safe:
http://docwiki.embarcadero.com/Librarie ... mapSurface
However, there is no example for C++ Builder.

Re: [Android]TIdHTTP and TThread

PostPosted: Fri Jan 27, 2017 3:56 pm
by rlebeau
Lena wrote:
You MUST synchronize...
Can you show С++ example of the combined use TThread::CreateAnonymousThread and synchronize loading the Bitmap for my case?


Something like this:

Code: Select all
//Try in WIN64 for test
/*
my images here:
http://welcome.um.la/myimg/1.png
http://welcome.um.la/myimg/2.png
***
*/
#include <fmx.h>
#pragma hdrstop

#include <memory>
#include "UnitHTTP.h"
//---------------------------------------------------------------------------
#pragma package(smart_init)
#pragma resource "*.fmx"
TForm1 *Form1;

int ThreadsRunning = 0;
//---------------------------------------------------------------------------
__fastcall TForm1::TForm1(TComponent* Owner)
   : TForm(Owner)
{
}
//---------------------------------------------------------------------------
void __fastcall TForm1::Button1Click(TObject *Sender)
{
   TListBoxItem *ListBoxItem;
   TListBoxGroupHeader *ListBoxGroupHeader;

   AniIndicator1->Enabled = true;

   ListBox1->BeginUpdate();
   try
   {
      //test for 5 images:
      for (int i = 1; i <= 5; i++)
      {
         ListBoxGroupHeader = new TListBoxGroupHeader(ListBox1);
         ListBoxGroupHeader->Text = L"Custom header";
         ListBox1->AddObject(ListBoxGroupHeader);

         ListBoxItem = new TListBoxItem(ListBox1);
         ListBoxItem->StyleLookup = L"listboxitemleftdetail";
         ListBoxItem->Height = 44; //image heigth
         ListBoxItem->TextSettings->Trimming = TTextTrimming::None;
         ListBoxItem->TextSettings->WordWrap = true;
         ListBoxItem->Text = L"Custom text";
         ListBox1->AddObject(ListBoxItem);
         ListBoxItem->ApplyStyleLookup();

         String URLLink = L"http://welcome.um.la/myimg/" + IntToStr(i) + ".png";
         TThread *thread = TThread::CreateAnonymousThread(
           [URLLink, ListBoxItem, this]()
           {
              std::unique_ptr<TMemoryStream> WelcomeINI(new TMemoryStream);
              std::unique_ptr<TIdHTTP> HTTP(new TIdHTTP);

              try
              {
                 HTTP->Get(URLLink, WelcomeINI.get());
              }
              catch(...)
              {
                 WelcomeINI->LoadFromFile("44x44.png"); //image by default
              }

              WelcomeINI->Position = 0;

               TThread::Synchronize(NULL,
                 [ListBoxItem, WelcomeINI&]()
                 {
                    //ListBoxItem->ItemData->Bitmap->LoadFromStream(WelcomeINI.get());
                    TValue value = ListBoxItem->StylesData["ItemData.Bitmap"];
                    if (!value.IsEmpty)
                    {
                       TBitmap *bitmap = value.AsType<TBitmap*>();
                       bitmap->LoadFromStream(WelcomeINI.get());
                    }
                 }
               );
           }
         );
         thread->OnTerminate = &ThreadTerminated;
         thread->Start();

         ++ThreadsRunning;
      }
   }
    __finally
   {
      ListBox1->EndUpdate();
   }

}
//---------------------------------------------------------------------------
void __fastcall TForm1::ThreadTerminated(TObject *Sender)
{
    if (--ThreadsRunning <= 0)
      AniIndicator1->Enabled = false;
}

Re: [Android]TIdHTTP and TThread

PostPosted: Sun Jan 29, 2017 3:37 am
by Lena
TThread::Synchronize([NULL, ListBoxItem, WelcomeINI&]()


[bcc64 Error] UnitHTTP.cpp(63): expected variable name or 'this' in lambda capture list

I try
Code: Select all
TThread::Synchronize([NULL, ListBoxItem, WelcomeINI&, this]()


[bcc64 Error] UnitHTTP.cpp(63): expected variable name or 'this' in lambda capture list

:(
C++ Builder 10.2

Re: [Android]TIdHTTP and TThread

PostPosted: Sun Jan 29, 2017 11:26 am
by rlebeau
Lena wrote:
TThread::Synchronize([NULL, ListBoxItem, WelcomeINI&]()


[bcc64 Error] UnitHTTP.cpp(63): expected variable name or 'this' in lambda capture list


Look more closely at what I showed you in my last example. You put the opening '[' in the wrong place. It should be:

Code: Select all
TThread::Synchronize(NULL, [ListBoxItem, WelcomeINI&](){...});


Not:

Code: Select all
TThread::Synchronize([NULL, ListBoxItem, WelcomeINI&](){...});


Don't include NULL in the lambda's capture list. The NULL is the first parameter of Synchronize(), the lambda is the second parameter.

Re: [Android]TIdHTTP and TThread

PostPosted: Sun Jan 29, 2017 12:20 pm
by Lena
Hi.
You put the opening '[' in the wrong place


This was my first attempt but:
Code: Select all
TThread::Synchronize(NULL, [ListBoxItem, WelcomeINI&](){


[bcc64 Error] UnitHTTP.cpp(63): expected ',' or ']' in lambda capture list

Re: [Android]TIdHTTP and TThread

PostPosted: Sun Jan 29, 2017 12:45 pm
by rlebeau
Lena wrote:This was my first attempt but:
Code: Select all
TThread::Synchronize(NULL, [ListBoxItem, WelcomeINI&](){


[bcc64 Error] UnitHTTP.cpp(63): expected ',' or ']' in lambda capture list


Sorry, my bad, I had the '&' in the wrong place! In a lambda capture list, when declaring a variable to be captured by reference, the '&' needs to be in front of the variable name instead of behind it:

Code: Select all
TThread::Synchronize(NULL, [ListBoxItem, &WelcomeINI](){...});


See Lambda Expressions.

Re: [Android]TIdHTTP and TThread

PostPosted: Mon Jan 30, 2017 3:25 am
by Lena
Thank You very much!
No errors now.

I try in WIN64 and Android but no pictures in ListBox. This code is not executed:
Code: Select all
TBitmap *bitmap = value.AsType<TBitmap*>();
bitmap->LoadFromStream(WelcomeINI.get());

Will wait fix for properties ItemData.Bitmap from Embarcadero.

P.S.
In the future I want to use your code in WIN32. Can you show how it will look your code without lambda?
For WIN32 lambda is not supported. Thank You!

Re: [Android]TIdHTTP and TThread

PostPosted: Mon Jan 30, 2017 11:57 am
by rlebeau
Lena wrote:In the future I want to use your code in WIN32. Can you show how it will look your code without lambda?
For WIN32 lambda is not supported.


C++11, and thus lambdas, are supported in the CLang-based Win32 compiler.

For the classic Borland Win32 compiler, you have some alternative choices:

1. use a wrapper class for anonymous procedures (see How to Handle Delphi Anonymous Methods in C++):

Code: Select all
#include <fmx.h>
#pragma hdrstop

#include <memory>
#include "UnitHTTP.h"
//---------------------------------------------------------------------------
#pragma package(smart_init)
#pragma resource "*.fmx"
TForm1 *Form1;

int ThreadsRunning = 0;
//---------------------------------------------------------------------------
__fastcall TForm1::TForm1(TComponent* Owner)
   : TForm(Owner)
{
}
//---------------------------------------------------------------------------
class MyThreadFunctor
{
private:
   String URLLink;
   TListBoxItem *ListBoxItem;
   std::unique_ptr<TMemoryStream> WelcomeINI;

   void __fastcall Finished()
   {
      //ListBoxItem->ItemData->Bitmap->LoadFromStream(WelcomeINI.get());
      TValue value = ListBoxItem->StylesData[L"ItemData.Bitmap"];
      if (!value.IsEmpty)
      {
         TBitmap *bitmap = value.AsType<TBitmap*>();
         bitmap->LoadFromStream(WelcomeINI.get());
      }
   }

public:
   MyThreadFunctor(const String &URL, TListBoxItem *Item)
      : URLLink(URL), ListBoxItem(Item), WelcomeINI(new TMemoryStream)
   {
   }

   void operator()()
   {
      std::unique_ptr<TIdHTTP> HTTP(new TIdHTTP);

      try
      {
         HTTP->Get(URLLink, WelcomeINI.get());
      }
      catch(...)
      {
         WelcomeINI->LoadFromFile("44x44.png"); //image by default
      }

      WelcomeINI->Position = 0;

      TThread::Synchronize(NULL, &Finished);
   }
};

typedef TMethodRef<TThreadProcedure, MyThreadFunctor, void> MyThreadMethRef;

void __fastcall TForm1::Button1Click(TObject *Sender)
{
   TListBoxItem *ListBoxItem;
   TListBoxGroupHeader *ListBoxGroupHeader;

   AniIndicator1->Enabled = true;

   ListBox1->BeginUpdate();
   try
   {
      //test for 5 images:
      for (int i = 1; i <= 5; i++)
      {
         ListBoxGroupHeader = new TListBoxGroupHeader(ListBox1);
         ListBoxGroupHeader->Text = L"Custom header";
         ListBox1->AddObject(ListBoxGroupHeader);

         ListBoxItem = new TListBoxItem(ListBox1);
         ListBoxItem->StyleLookup = L"listboxitemleftdetail";
         ListBoxItem->Height = 44; //image heigth
         ListBoxItem->TextSettings->Trimming = TTextTrimming::None;
         ListBoxItem->TextSettings->WordWrap = true;
         ListBoxItem->Text = L"Custom text";
         ListBox1->AddObject(ListBoxItem);
         ListBoxItem->ApplyStyleLookup();

         TThread *thread = TThread::CreateAnonymousThread(
            _di_TThreadProcedure(
               new MyThreadMethRef(
                  L"http://welcome.um.la/myimg/" + IntToStr(i) + ".png", ListBoxItem
               )
            )
         );
         thread->OnTerminate = &ThreadTerminated;
         thread->Start();

         ++ThreadsRunning;
      }
   }
    __finally
   {
      ListBox1->EndUpdate();
   }

}
//---------------------------------------------------------------------------
void __fastcall TForm1::ThreadTerminated(TObject *Sender)
{
    if (--ThreadsRunning <= 0)
      AniIndicator1->Enabled = false;
}


2. avoid anonymous procedures and just define a custom TThread class instead:

Code: Select all
#include <fmx.h>
#pragma hdrstop

#include <memory>
#include "UnitHTTP.h"
//---------------------------------------------------------------------------
#pragma package(smart_init)
#pragma resource "*.fmx"
TForm1 *Form1;

int ThreadsRunning = 0;

class TMyThread : public TThread
{
private:
   String URLLink;
   TListBoxItem *ListBoxItem;
   std::auto_ptr<TMemoryStream> WelcomeINI;

   void __fastcall Finished()
   {
      //ListBoxItem->ItemData->Bitmap->LoadFromStream(WelcomeINI.get());
      TValue value = ListBoxItem->StylesData[L"ItemData.Bitmap"];
      if (!value.IsEmpty)
      {
         TBitmap *bitmap = value.AsType<TBitmap*>();
         bitmap->LoadFromStream(WelcomeINI.get());
      }
   }

protected:
   void __fastcall Execute()
   {
      std::auto_ptr<TIdHTTP> HTTP(new TIdHTTP);

      try
      {
         HTTP->Get(URLLink, WelcomeINI.get());
      }
      catch(...)
      {
         WelcomeINI->LoadFromFile("44x44.png"); //image by default
      }

      WelcomeINI->Position = 0;

      Synchronize(&Finished);
   }

public:
   TMyThread(const String &URL, TListBoxItem *Item)
      : URLLink(URL), ListBoxItem(Item), WelcomeINI(new TMemoryStream)
   {
   }
};

//---------------------------------------------------------------------------
__fastcall TForm1::TForm1(TComponent* Owner)
   : TForm(Owner)
{
}
//---------------------------------------------------------------------------
void __fastcall TForm1::Button1Click(TObject *Sender)
{
   TListBoxItem *ListBoxItem;
   TListBoxGroupHeader *ListBoxGroupHeader;

   AniIndicator1->Enabled = true;

   ListBox1->BeginUpdate();
   try
   {
      //test for 5 images:
      for (int i = 1; i <= 5; i++)
      {
         ListBoxGroupHeader = new TListBoxGroupHeader(ListBox1);
         ListBoxGroupHeader->Text = L"Custom header";
         ListBox1->AddObject(ListBoxGroupHeader);

         ListBoxItem = new TListBoxItem(ListBox1);
         ListBoxItem->StyleLookup = L"listboxitemleftdetail";
         ListBoxItem->Height = 44; //image heigth
         ListBoxItem->TextSettings->Trimming = TTextTrimming::None;
         ListBoxItem->TextSettings->WordWrap = true;
         ListBoxItem->Text = L"Custom text";
         ListBox1->AddObject(ListBoxItem);
         ListBoxItem->ApplyStyleLookup();

         TThread *thread = new TMyThread(L"http://welcome.um.la/myimg/" + IntToStr(i) + ".png", ListBoxItem);
         thread->OnTerminate = &ThreadTerminated;
         thread->Start();

         ++ThreadsRunning;
      }
   }
    __finally
   {
      ListBox1->EndUpdate();
   }

}
//---------------------------------------------------------------------------
void __fastcall TForm1::ThreadTerminated(TObject *Sender)
{
    if (--ThreadsRunning <= 0)
      AniIndicator1->Enabled = false;
}

Re: [Android]TIdHTTP and TThread

PostPosted: Tue Jan 31, 2017 10:42 am
by Lena
Thank You very much for help and support!