Page 1 of 2

Thread and gadget creation

Posted: Fri Jan 19, 2024 6:18 am
by boddhi
Hi,

I've driving my brain crazy for the last 3-4 days trying to understand why some of my code isn't working.

Here's a purified functional adaptation, except that in the original code the more complex UI is created with the DialogXML lib and the procedure is called inside a thread. This procedure is exactly the same as the original. That's why I haven't translated the variable names, to avoid making mistakes.

Code: Select all

DebugLevel 5
;UsePNGImageEncoder()

Enumeration Fenetres
  #CGP_FENPRINCIPALE
EndEnumeration
Enumeration Gadgets
  #GAD_FP_ZD_GALERIE
  #GAD_FP_IMG_0
EndEnumeration
Enumeration Polices
  #POLICE
EndEnumeration
Enumeration Images
  #IMG_TEXTEEXEMPLE
EndEnumeration

Global.u HauteurPoliceSysteme

NomPolice.s=""
FichierPolice.s=""
#TEXTETEST="ABCDEFGH abcdefgh ÀCÉÏÔ àçéïô 012345 ?!€$+={([A"
#COUL_TEXTETITRE=#White
#COUL_FONDTEXTETITRE=#Red
#COUL_TEXTEEXEMPLE=#Black
#COUL_FONDTEXTEEXEMPLE=#White

CompteurGadget.u=0:HauteurImage.u=0:PositionY.u=4:HauteurTexte.u=0:LargeurTexte.u=0
Global FacteurEchelleX.f=DesktopScaledX(100)/100
Global FacteurEchelleY.f=DesktopScaledY(100)/100
;

#GAD_FP_IMG_POLICE=50

Procedure.u Fc_Affichage_ImagePolice(ArgFichier.s,ArgNomPolice.s,ArgStyle.u,ArgTexteComplement.s,ArgCompteur.u,ArgPositionY.l)
  Debug #PB_Compiler_Procedure+" (Fichier="+ArgFichier+",NomPolice="+ArgNomPolice+",TexteComplement="+ArgTexteComplement+",Compteur="+ArgCompteur+",PositionY="+ArgPositionY+")",5
  Protected.u PositionY,HauteurImage,LargeurTexte,HauteurTexte,NoGadget=#GAD_FP_IMG_POLICE+ArgCompteur,Style
  
  If RegisterFontFile(ArgFichier)
    If ArgStyle&1:Style=#PB_Font_Italic:EndIf        ; Italique
    If ArgStyle>>1&1:Style|#PB_Font_Underline:EndIf  ; Souligné
    If ArgStyle>>4&1:Style|#PB_Font_StrikeOut:EndIf  ; Barré
    If ArgStyle>>5&1:Style|#PB_Font_Bold:EndIf       ; Gras
    LoadFont(#POLICE,ArgNomPolice,24,Style)
    StartDrawing(WindowOutput(0))
    DrawingFont(FontID(#POLICE))
    LargeurTexte=TextWidth(#TEXTETEST):HauteurTexte=TextHeight(#TEXTETEST)
    StopDrawing()
    PositionY=HauteurTexte+8:HauteurImage=HauteurPoliceSysteme+8+PositionY
    If CreateImage(#IMG_TEXTEEXEMPLE,792*FacteurEchelleX,HauteurImage,32,#COUL_FONDTEXTEEXEMPLE)
      StartDrawing(ImageOutput(#IMG_TEXTEEXEMPLE))
      DrawingMode(#PB_2DDrawing_Transparent)
      Box(0,0,792*FacteurEchelleX,HauteurPoliceSysteme+8,#COUL_FONDTEXTETITRE)
      DrawText(10,4,ArgNomPolice+" "+ArgTexteComplement,#COUL_TEXTETITRE)
      DrawingFont(FontID(#POLICE))
      DrawText(8,HauteurPoliceSysteme+12,#TEXTETEST,#COUL_TEXTEEXEMPLE)
      StopDrawing()
      OpenGadgetList(#GAD_FP_ZD_GALERIE)
      ImageGadget(NoGadget,4,ArgPositionY/FacteurEchelleY,792,HauteurImage,ImageID(#IMG_TEXTEEXEMPLE))
      GadgetToolTip(NoGadget,Str(ArgCompteur)+" • "+ArgFichier)
      SetGadgetData(NoGadget,ArgCompteur)
      CloseGadgetList()
      ;SaveImage(#IMG_TEXTEEXEMPLE,"C:\Temp\Temp"+Str(ArgCompteur)+".png",#PB_ImagePlugin_PNG)
      FreeImage(#IMG_TEXTEEXEMPLE)
    EndIf
    FreeFont(#POLICE)
    ProcedureReturn HauteurImage
  EndIf
EndProcedure


If OpenWindow(#CGP_FENPRINCIPALE, 0, 0, 800, 200, "Exemple...", #PB_Window_SystemMenu | #PB_Window_ScreenCentered)
  ScrollAreaGadget(#GAD_FP_ZD_GALERIE,4,4,792,192,792,192)
  ; 
  StartDrawing(WindowOutput(#CGP_FENPRINCIPALE))
  HauteurPoliceSysteme=TextHeight("Ly")
  StopDrawing()
  For Compteur.a=0 To 5
    Select Compteur
      Case 0:NomPolice.s="Arial":FichierPolice.s="C:\WINDOWS\Fonts\ARIAL.TTF":Style.u=64
      Case 1:NomPolice.s="Arial":FichierPolice.s="C:\WINDOWS\Fonts\ARIALBD.TTF":Style=32
      Case 2:NomPolice.s="Arial":FichierPolice.s="C:\WINDOWS\Fonts\ARIALBI.TTF":Style=33
      Case 3:NomPolice.s="Arial":FichierPolice.s="C:\WINDOWS\Fonts\ARIALI.TTF":Style=1
      Case 4:NomPolice.s="Arial Black":FichierPolice.s="C:\WINDOWS\Fonts\ARIBLK.TTF":Style=64
      Case 5:NomPolice.s="Arial Narrow":FichierPolice.s="C:\WINDOWS\Fonts\ARIALNBI.TTF":Style=33
    EndSelect
    HauteurImage=Fc_Affichage_ImagePolice(FichierPolice,NomPolice,Style,"",CompteurGadget,PositionY)
    If HauteurImage
      PositionY+HauteurImage
      CompteurGadget+1
    EndIf
  Next
  SetGadgetAttribute(#GAD_FP_ZD_GALERIE,#PB_ScrollArea_InnerHeight,PositionY/FacteurEchelleY)
  
  Repeat
    Event = WaitWindowEvent()
    Select Event
      Case #PB_Event_Gadget
        Select EventGadget()
        EndSelect
    EndSelect
  Until Event = #PB_Event_CloseWindow
EndIf

Procedure.u Fc_Affichage_ImagePolice2(ArgFichier.s,ArgNomPolice.s,ArgStyle.u,ArgTexteComplement.s,ArgCompteur.u,ArgPositionY.l)
  Debug #PB_Compiler_Procedure+" (Fichier="+ArgFichier+",NomPolice="+ArgNomPolice+",TexteComplement="+ArgTexteComplement+",Compteur="+ArgCompteur+",PositionY="+ArgPositionY+")",5
  Protected.u PositionY,HauteurImage,LargeurTexte,HauteurTexte,Style
  
  If RegisterFontFile(ArgFichier)
    If ArgStyle&1:Style=#PB_Font_Italic:EndIf        ; Italique
    If ArgStyle>>1&1:Style|#PB_Font_Underline:EndIf  ; Souligné
    If ArgStyle>>4&1:Style|#PB_Font_StrikeOut:EndIf  ; Barré
    If ArgStyle>>5&1:Style|#PB_Font_Bold:EndIf       ; Gras
    LoadFont(#POLICE,ArgNomPolice,24,Style)
    StartDrawing(WindowOutput(0))
    DrawingFont(FontID(#POLICE))
    LargeurTexte=TextWidth(#TEXTETEST):HauteurTexte=TextHeight(#TEXTETEST)
    StopDrawing()
    PositionY=HauteurTexte+8:HauteurImage=HauteurPoliceSysteme+8+PositionY
    If CreateImage(#IMG_TEXTEEXEMPLE,792*FacteurEchelleX,HauteurImage,32,#COUL_FONDTEXTEEXEMPLE)
      StartDrawing(ImageOutput(#IMG_TEXTEEXEMPLE))
      DrawingMode(#PB_2DDrawing_Transparent)
      Box(0,0,792*FacteurEchelleX,HauteurPoliceSysteme+8,#COUL_FONDTEXTETITRE)
      DrawText(10,4,ArgNomPolice+" "+ArgTexteComplement,#COUL_TEXTETITRE)
      DrawingFont(FontID(#POLICE))
      DrawText(8,HauteurPoliceSysteme+12,#TEXTETEST,#COUL_TEXTEEXEMPLE)
      StopDrawing()
      UsePNGImageEncoder()
      ImageGadget(#GAD_FP_IMG_0+ArgCompteur,4,ArgPositionY/FacteurEchelleY,792,HauteurImage,ImageID(#IMG_TEXTEEXEMPLE))
      GadgetToolTip(#GAD_FP_IMG_0+ArgCompteur,ArgFichier)
    EndIf
    FreeFont(#POLICE)
    FreeImage(#IMG_TEXTEEXEMPLE)
    ProcedureReturn HauteurImage
  EndIf
EndProcedure

The problem in my complete code is that, while the images are created correctly, the ImageGadgets don't appear, even though an ID is returned with a Debug (not 0).
I've tried all kinds of manipulations, with no results.

I'm beginning to wonder whether this might be due to the fact that the gadget creation procedure is launched inside the thread (as many calls as there are gadgets to create).
Is this the case? If so, how can gadgets be created from within a thread?

Re: Thread and gadget creation

Posted: Fri Jan 19, 2024 8:18 am
by infratec
Never do GUI stuff in a thread.

Use PostEvent() and do this in the main thread.

Re: Thread and gadget creation

Posted: Fri Jan 19, 2024 8:21 am
by jacdelad
https://www.purebasic.fr/english/viewtopic.php?t=66180
This doesn't include creation, but handling. If said gadgets aren't dynamic, you can create them beforehand and hide/unhide them from the thread.

Re: Thread and gadget creation

Posted: Fri Jan 19, 2024 9:34 am
by boddhi
Thank you Infratec for your reply.
infratec wrote: Never do GUI stuff in a thread. Use PostEvent() and do this in the main thread.
Ok, I notice (which reminded me of certain discussions on the subject that I hadn't fully considered). :wink:

One question though, does PostEvent() block thread execution? Because each gadget created requires the previous gadget to have been completely created to be positioned correctly, and therefore the thread execution must be blocked for the time of creation. Or PauseThred() is needed ?

jacdelad wrote: This doesn't include creation, but handling. If said gadgets aren't dynamic, you can create them beforehand and hide/unhide them from the thread.
I know that for sure ! :) :D
I use and abuse it to hide/unhide containers, progress bars, etc.

Re: Thread and gadget creation

Posted: Fri Jan 19, 2024 9:54 am
by jacdelad
Use own events to communicate when a gadget is ready.

Re: Thread and gadget creation

Posted: Fri Jan 19, 2024 10:02 am
by boddhi
jacdelad wrote: Fri Jan 19, 2024 9:54 am Use own events to communicate when a gadget is ready.
But it doesn't reply to my question : PostEvent() block thread execution until it has finished? or PauseThred() is needed?

Re: Thread and gadget creation

Posted: Fri Jan 19, 2024 10:10 am
by Fred
PostEvent() doesn't block.

Re: Thread and gadget creation

Posted: Fri Jan 19, 2024 10:27 am
by boddhi
Fred wrote: Fri Jan 19, 2024 10:10 am PostEvent() doesn't block.
Thanks Fred for your reply.
I've just done some tests and I can confirm :( :D

Re: Thread and gadget creation

Posted: Fri Jan 19, 2024 1:32 pm
by boddhi
I've found a solution :

Code: Select all

; Main procedure (Thread)
Debug "PositionY before : "+DonneesGadget\PositionY
PostEvent(#EVNMT_CREATIONGADGETIMAGE,#CGP_FENPRINCIPALE,0,0,@DonneesGadget)
Delay(50) ; => My solution
Debug "PositionY after : "+DonneesGadget\PositionY ; To control if my variable has been correctly modified by the procedure executed through PostEvent()
ImageHeight=DonneesGadget\PositionY
If ImageHeight
  PositionY+ImageHeight
  GadgetCount+1
EndIf
But (because there is always a but):
1) Is Delay() THE RIGHT solution to give the procedure time to be called and executed?
2) If yes, how can I be sure that this delay, which is sufficient for my PC, will be sufficient for another?

Re: Thread and gadget creation

Posted: Fri Jan 19, 2024 1:34 pm
by jacdelad
That's what I meant with my last post: let the thread fire a custom event which tells the main program, that it can continue (when handling the event, set a variable or something).

Re: Thread and gadget creation

Posted: Fri Jan 19, 2024 1:49 pm
by boddhi
jacdelad wrote: Fri Jan 19, 2024 1:34 pm That's what I meant with my last post: let the thread fire a custom event which tells the main program, that it can continue (when handling the event, set a variable or something).
You know, I understand very quickly, but you have to explain it to me for a long long time. :mrgreen: :wink:

So, if I've understood what you're saying, it would be to create, after PostEvent(), a loop that would check the content of a variable and until the content of this variable is not confirmed, continue to loop? that's right? and Delay(X) needed or not needed in this case ?

Re: Thread and gadget creation

Posted: Fri Jan 19, 2024 5:46 pm
by jacdelad
Nah...
...while running your thread your program remains in the main loop (and you know, you should only run one). The thread sends a message via Post message, which is processed normally (Event(), EventType()...). Now, after sending the message, the threads waits and regularly checks a variable for a change. The main thread sets the variable after doing everything that has to be done. The variable changes, the thread is happy.
Something like this...

Re: Thread and gadget creation

Posted: Fri Jan 19, 2024 6:51 pm
by mk-soft
You can use 'SendEvent' from my Module ThreadToGUI

Re: Thread and gadget creation

Posted: Fri Jan 19, 2024 7:06 pm
by infratec
Instead of a Delay() use WaitSemaphore() and SignalSemaphore()

Re: Thread and gadget creation

Posted: Fri Jan 19, 2024 7:37 pm
by boddhi
jacdelad wrote: Fri Jan 19, 2024 5:46 pm ...while running your thread [...] Something like this...
Sincerly ? I didn't understood everything :oops: :mrgreen:
A very little example? :mrgreen:
mk-soft wrote: You can use 'SendEvent' from my Module ThreadToGUI
Thank you for your suggestion :wink:
infratec wrote: Instead of a Delay() use WaitSemaphore() and SignalSemaphore()
@mk-soft & infratec
It's already foggy in my skull. Add to that the semaphores, which I don't understand aanything about...hummmm 😭😭😭 :oops: :mrgreen:

For the moment, I do it like this 🤔 (Edit : I wrote "I'll do it" : Sorry, translation error :oops: )

Code: Select all

Procedure CalledProcedure(*buffer.structureX)
  [...]
  *buffer\JobDone=#True
EndProcedure

Procedure TreadedProcedure()
  Protected.structureX Variable
  [...]
  For
    [...]
    Variable\xxx=xxx
    Variable\yyy=yyy
    Variable\JobDone=#False
    PostEvent(#EVNMT,#WINDOW,0,0,@Variable)
    Delay(5)
    While Not Variable\JobDone:Wend
EndProcedure

Do
  Select WaitWindowEvent()
    [...]
    Case #EVNMT:CalledProcedure(EventData())
    [...]
  EndSelect
Forever