[TUTO] Une classe pour gérer une fenêtre

Informations pour bien débuter en PureBasic
Avatar de l’utilisateur
microdevweb
Messages : 1798
Inscription : mer. 29/juin/2011 14:11
Localisation : Belgique

[TUTO] Une classe pour gérer une fenêtre

Message par microdevweb »

Préface:
Je développe actuellement une pseudo classe pour la gestion d'une fenêtre dialogue, djes ma suggéré de faire un tuto. Donc le voici.

Pourquoi en POO ?
Tout ce qui va être fait dans ce tuto pourrait être réalisé en procédural, voici pour comparaison une des fonctions dans chacun des deux systèmes.

Code : Tout sélectionner

; Procédural
getGadgetValue(IdWindow,IdGadget)
; POO
monObjet\getValue()
Vous utilisez sans vraiment vous en rendre compte ce principe, lorsque vous utilisez une liste de structure.

Code : Tout sélectionner

maListe()\x
Les avantages de la POO
  • Le code de chaque objet est encapsulé et ainsi il n'y a pas de risque de conflit.
  • En plus de l'exemple donné si dessus avec la liste de structure, nous aurons accès aux méthodes (procédures) directement depuis l'objet.
  • Bien que je ne m'attarde pas sur ce sujet, il est possible de créer une classe qui héritera des même caractéristiques que la classe mère.
Pour terminer dans le cas de codes réutilisables, la POO peut vraiment être efficace, mais cela demande souvent plus de travail et de rigueur. Donc personnellement je n'utilise ce concept que dans un cadre comme celui-ci.

A qui s'adresse ce Tuto:
Vous devez avoir de bonne bases en Purebasic et comprendre les pointeurs, je ne m'attarderai pas sur les fonctions basiques de PureBasic.

Que fera notre travail final
Nous utiliserons dans le cadre de notre travail 2 classes, une pour la gestion des fenêtres et une autre pour la gestion des gadgets.
  • Ouverture/fermeture d'une fenêtre
  • Ajout de gadget
  • Validation de gadget
  • Appel automatique de procédure événementielle
Table des matières:
Dernière modification par microdevweb le jeu. 03/août/2017 15:33, modifié 13 fois.
Windows 10 64 bits PB: 5.70 ; 5.72 LST
Work at Centre Spatial de Liège
Avatar de l’utilisateur
microdevweb
Messages : 1798
Inscription : mer. 29/juin/2011 14:11
Localisation : Belgique

Re: [TUTO] une class pour gèrer une fenêtre

Message par microdevweb »

Mise en place d'une classe CLASS

Pour encapsuler notre classe nous utiliserons un module, les membres (variables) de notre classe seront placés dans une structure. Les méthodes (procédures) dans une interface, et l'adresse des procédures liées aux méthodes dans une datasection.

REMARQUE: dans le cas de notre classe, j'ai placé la structure dans le module, si je désire que cette classe puise être héritée je placerai la structure dans le DeclareModule

Code : Tout sélectionner

; ************************************************
; Nom de la class : FORM
; Autheur         : MicrodevWeb
; Version         : 1.0
; Requis          : Pb 5.60
; ************************************************
DeclareModule FORM
  ; =======================================================
  ;-* les méthodes publiques
  ; =======================================================
  Interface _FORM
    
  EndInterface
  ;} ------------------------------------------------------
EndDeclareModule
Module FORM
  ; =======================================================
  ;-* les membres privés
  ; =======================================================
  Structure sFORM
    *pFORM ; IMPORTANT ceci doit absolument être placé en premier et va contenir l'adresse des procédures
  
  EndStructure
  ;} ------------------------------------------------------
  ; =======================================================
  ;-* les functions privées
  ; =======================================================

  ;} ------------------------------------------------------
  ; =======================================================
  ;-* les méthodes publiques
  ; =======================================================

  ;} ------------------------------------------------------
  DataSection
    PROC: ; les adresses des procédures IMPORTANT il faut respecter le même ordre que l'interface
  EndDataSection
EndModule


Windows 10 64 bits PB: 5.70 ; 5.72 LST
Work at Centre Spatial de Liège
Avatar de l’utilisateur
microdevweb
Messages : 1798
Inscription : mer. 29/juin/2011 14:11
Localisation : Belgique

Re: [TUTO] une class pour gèrer une fenêtre

Message par microdevweb »

Le constructeur
J'ai ajouté quelques membres à ma classe "FORM"

Code : Tout sélectionner

Structure sPOS
    x.l
    y.l
    w.l
    h.l
  EndStructure
  Structure sFORM
    *pFORM ; IMPORTANT ceci doit absolument être placé en premier et va contenir l'adresse des procédures
    maPosition.sPOS
    titre.s
    flags.l
    FenetreMere.l
  EndStructure
Nous allons maintenant créer le constructeur

Code : Tout sélectionner

Procedure New(x,y,w,h,titre.s,flags.l=0)
    Protected *objet.sFORM = AllocateStructure(sFORM)
    With *objet
      \pFORM=?PROC
      \maPosition\x=x
      \maPosition\y=y
      \maPosition\w=w
      \maPosition\h=h
      \titre=titre
      \flags=flags
      \FenetreMere=#PB_Ignore ; Pas de fenêtre mère
      ProcedureReturn *objet
    EndWith
  EndProcedure
Et voici maintenant un code exemple pour instancier un objet

Code : Tout sélectionner

EnableExplicit
; Déclaration de l'objet
Global maFenetre.FORM::_FORM
; instanciation de l'objet
maFenetre=FORM::New(0,0,800,600)
Comme vous pouvez le constater, ceci n'est pas sorcier :wink:

Tout le code de la classe

Code : Tout sélectionner

; ************************************************
; Nom de la class : FORM
; Autheur         : MicrodevWeb
; Version         : 1.0
; Requis          : Pb 5.60
; ************************************************
DeclareModule FORM
  ; =======================================================
  ;-* les méthodes publiques
  ; =======================================================
  Interface _FORM
    
  EndInterface
  ;} ------------------------------------------------------
  ; =======================================================
  ;-* les prototypes public
  ; =======================================================
  Declare New(x,y,w,h,titre.s,flags.l=0)
  ;} ------------------------------------------------------
EndDeclareModule
Module FORM
  EnableExplicit
  ; =======================================================
  ;-* les membres privés
  ; =======================================================
  Structure sPOS
    x.l
    y.l
    w.l
    h.l
  EndStructure
  Structure sFORM
    *pFORM ; IMPORTANT ceci doit absolument être placé en premier et va contenir l'adresse des procédures
    maPosition.sPOS
    titre.s
    flags.l
    FenetreMere.l
  EndStructure
  ;} ------------------------------------------------------
  ; =======================================================
  ;-* les functions privées
  ; =======================================================

  ;} ------------------------------------------------------
  ; =======================================================
  ;-* Le constructeur
  ; =======================================================
  Procedure New(x,y,w,h,titre.s,flags.l=0)
    Protected *objet.sFORM = AllocateStructure(sFORM)
    With *objet
      \pFORM=?PROC
      \maPosition\x=x
      \maPosition\y=y
      \maPosition\w=w
      \maPosition\h=h
      \titre=titre
      \flags=flags
      \FenetreMere=#PB_Ignore ; Pas de fenêtre mère
      ProcedureReturn *objet
    EndWith
  EndProcedure
  ;} ------------------------------------------------------
  ; =======================================================
  ;-* les méthodes publiques
  ; =======================================================

  ;} ------------------------------------------------------
  DataSection
    PROC: ; les adresses des procédures IMPORTANT il faut respecté le même ordre que l'interface
  EndDataSection
EndModule

Windows 10 64 bits PB: 5.70 ; 5.72 LST
Work at Centre Spatial de Liège
Avatar de l’utilisateur
microdevweb
Messages : 1798
Inscription : mer. 29/juin/2011 14:11
Localisation : Belgique

Re: [TUTO] une class pour gèrer une fenêtre

Message par microdevweb »

Notre première méthode:

Code : Tout sélectionner

Procedure Open(*This.sFORM,FenetreMere=#PB_Ignore)
    With *This
      \FenetreMere=FenetreMere
      ; si aucun flag n'est renseigné je crée ceux de base
      If \flags=0
        \flags=#PB_Window_SystemMenu
        ; je centre également la fenêtre en fonction d'une fenêtre mère ou pas
        If \FenetreMere=#PB_Ignore
          \flags|#PB_Window_ScreenCentered
        Else
          \flags|#PB_Window_WindowCentered
        EndIf
      EndIf
      If \FenetreMere=#PB_Ignore ; pas de fenêtre mère
        \IdForm=OpenWindow(#PB_Any,
                           \maPosition\x,
                           \maPosition\y,
                           \maPosition\w,
                           \maPosition\h,
                           \titre,
                           \flags)
      Else
        ; je désactive également la fenêtre mère
        DisableWindow(\FenetreMere,#True)
        \IdForm=OpenWindow(#PB_Any,
                           \maPosition\x,
                           \maPosition\y,
                           \maPosition\w,
                           \maPosition\h,
                           \titre,
                           \flags,
                           WindowID(\FenetreMere))
      EndIf
      ; important je vais sauver dans ma fenêtre l'adresse de mon objet
      SetWindowData(\IdForm,*This)
      ; Je vais placer maintenant un callback si l'utilisateur clique sur la croix
      BindEvent(#PB_Event_CloseWindow,@Exit(),\IdForm)
    EndWith
  EndProcedure
Comme vous le constaterez il n'y a pas beaucoup de différence avec une procédure classique. Si ce n'est le

Code : Tout sélectionner

*This.sFORM
Ceci est simplement l'adresse de l'objet qui sera passé par l'interface

Pour que notre méthode puisse être appelée, il faut l'ajouter à l'interface ainsi qu'au DataSection

Code : Tout sélectionner

Interface _FORM
    Open(FenetreMere=#PB_Ignore)
EndInterface
DataSection
    PROC: ; les adresses des procédures IMPORTANT il faut respecter le même ordre que l'interface
    Data.i @Open()
  EndDataSection
Remarquez que le *This n'est pas présent dans l'interface

Le code exemple

Code : Tout sélectionner

EnableExplicit
XIncludeFile "FORM.pbi"
; Déclaration de l'objet
Global maFenetre.FORM::_FORM
; instanciation de l'objet
maFenetre=FORM::New(0,0,800,600,"Teste")
Procedure EXit()
  CloseWindow(0)
  End
EndProcedure
OpenWindow(0,0,0,800,600,"Main",#PB_Window_Maximize|#PB_Window_SystemMenu)
BindEvent(#PB_Event_CloseWindow,@EXit(),0)
; Ouverture de la fenêtre
maFenetre\Open(0)

Repeat 
  WaitWindowEvent()
ForEver 
Tout le code de la classe

Code : Tout sélectionner

; ************************************************
; Nom de la class : FORM
; Autheur         : MicrodevWeb
; Version         : 1.0
; Requis          : Pb 5.60
; ************************************************
DeclareModule FORM
  ; =======================================================
  ;-* les méthodes publiques
  ; =======================================================
  Interface _FORM
    Open(FenetreMere=#PB_Ignore)
  EndInterface
  ;} ------------------------------------------------------
  ; =======================================================
  ;-* les prototypes public
  ; =======================================================
  Declare New(x,y,w,h,titre.s,flags.l=0)
  ;} ------------------------------------------------------
EndDeclareModule
Module FORM
  EnableExplicit
  ; =======================================================
  ;-* les membres privés
  ; =======================================================
  Structure sPOS
    x.l
    y.l
    w.l
    h.l
  EndStructure
  Structure sFORM
    *pFORM ; IMPORTANT ceci doit absolument être placé en premier et va contenir l'adresse des procédures
    maPosition.sPOS
    titre.s
    flags.l
    FenetreMere.l
    IdForm.l
  EndStructure
  ;} ------------------------------------------------------
  ; =======================================================
  ;-* les functions privées
  ; =======================================================
  Procedure Exit()
    ; IMPORTANT: je récupère l'adresse de l'objet
    Protected *This.sFORM=GetWindowData(EventWindow())
    With *This
      CloseWindow(\IdForm)
      ; Si une fenêtre mère
      If \FenetreMere<>#PB_Ignore
        DisableWindow(\FenetreMere,#False)
        SetActiveWindow(\FenetreMere)
      EndIf
    EndWith
  EndProcedure
  ;} ------------------------------------------------------
  ; =======================================================
  ;-* Le constructeur
  ; =======================================================
  Procedure New(x,y,w,h,titre.s,flags.l=0)
    Protected *objet.sFORM = AllocateStructure(sFORM)
    With *objet
      \pFORM=?PROC
      \maPosition\x=x
      \maPosition\y=y
      \maPosition\w=w
      \maPosition\h=h
      \titre=titre
      \flags=flags
      \FenetreMere=#PB_Ignore ; Pas de fenêtre mère
      ProcedureReturn *objet
    EndWith
  EndProcedure
  ;} ------------------------------------------------------
  ; =======================================================
  ;-* les méthodes publiques
  ; =======================================================
  Procedure Open(*This.sFORM,FenetreMere=#PB_Ignore)
    With *This
      \FenetreMere=FenetreMere
      ; si aucun flag n'est renseigné je crée ceux de base
      If \flags=0
        \flags=#PB_Window_SystemMenu
        ; je centre également la fenêtre en fonction d'une fenêtre mère ou pas
        If \FenetreMere=#PB_Ignore
          \flags|#PB_Window_ScreenCentered
        Else
          \flags|#PB_Window_WindowCentered
        EndIf
      EndIf
      If \FenetreMere=#PB_Ignore ; pas de fenêtre mère
        \IdForm=OpenWindow(#PB_Any,
                           \maPosition\x,
                           \maPosition\y,
                           \maPosition\w,
                           \maPosition\h,
                           \titre,
                           \flags)
      Else
        ; je désactive également la fenêtre mère
        DisableWindow(\FenetreMere,#True)
        \IdForm=OpenWindow(#PB_Any,
                           \maPosition\x,
                           \maPosition\y,
                           \maPosition\w,
                           \maPosition\h,
                           \titre,
                           \flags,
                           WindowID(\FenetreMere))
      EndIf
      ; important je vais sauver dans ma fenêtre l'adresse de mon objet
      SetWindowData(\IdForm,*This)
      ; Je vais placer maintenant un callback si l'utilisateur clique sur la croix
      BindEvent(#PB_Event_CloseWindow,@Exit(),\IdForm)
    EndWith
  EndProcedure
  ;} ------------------------------------------------------
  DataSection
    PROC: ; les adresses des procédures IMPORTANT il faut respecter le même ordre que l'interface
    Data.i @Open()
  EndDataSection
EndModule
Windows 10 64 bits PB: 5.70 ; 5.72 LST
Work at Centre Spatial de Liège
Avatar de l’utilisateur
djes
Messages : 4252
Inscription : ven. 11/févr./2005 17:34
Localisation : Arras, France

Re: [TUTO] Une classe pour gérer une fenêtre

Message par djes »

C'est chouette, merci de ce travail ! :)
Avatar de l’utilisateur
microdevweb
Messages : 1798
Inscription : mer. 29/juin/2011 14:11
Localisation : Belgique

Re: [TUTO] Une classe pour gérer une fenêtre

Message par microdevweb »

Le destructeur:

Lorsque dans notre classe on ferme la fenêtre l'objet reste présent. Cela peut être pratique puisque toutes les méthodes restent accessibles. Cependant, il se peut que l'on veuille détruire l'objet, et ainsi libérer la mémoire. Pour ce faire nous allons créer une méthode "Free". Nous procéderons comme la première méthode, sans oublier de l'ajouter dans l'interface et le "Datasection", et ceci évidement dans le même ordre.

Code : Tout sélectionner

Procedure Free(*This.sFORM)
    FreeStructure(*This)
EndProcedure
Maintenant il faut savoir si la fenêtre est effectivement fermée dans notre code principal. Nous allons donc créer une méthode qui va ajouter un callback, ce dernier sera appelé pour tous les événements gérés dans la classe "FORM".

Voici la procédure qui sera appelée

Code : Tout sélectionner

Procedure FenetreFermee(*form.FORM::_FORM,monEvenement.l)
  If monEvenement=#PB_Event_CloseWindow
    Debug "La fenêtre a été fermée je libère l'objet"
    *form\Free()
  EndIf
EndProcedure
Le code de la méthode

Code : Tout sélectionner

Procedure AddCallback(*This.sFORM,*Callback)
    With *This
      \Callback=*Callback
    EndWith
EndProcedure
Tout le code exemple

Code : Tout sélectionner

EnableExplicit
XIncludeFile "FORM.pbi"
; Déclaration de l'objet
Global maFenetre.FORM::_FORM
; instanciation de l'objet
maFenetre=FORM::New(0,0,800,600,"Teste")
Procedure EXit()
  CloseWindow(0)
  End
EndProcedure
Procedure FenetreFermee(*form.FORM::_FORM,monEvenement.l)
  If monEvenement=#PB_Event_CloseWindow
    Debug "La fenêtre a été fermée je libère l'objet"
    *form\Free()
  EndIf
EndProcedure
; J'ajoute un callback qui est appelé quand la fenêtre est fermée
maFenetre\AddCallback(@FenetreFermee(),#PB_Event_CloseWindow)
OpenWindow(0,0,0,800,600,"Main",#PB_Window_Maximize|#PB_Window_SystemMenu)
BindEvent(#PB_Event_CloseWindow,@EXit(),0)
; Ouverture de la fenêtre
maFenetre\Open(0)

Repeat 
  WaitWindowEvent()
ForEver 
Tout le code de la classe

Code : Tout sélectionner

; ************************************************
; Nom de la class : FORM
; Autheur         : MicrodevWeb
; Version         : 1.0
; Requis          : Pb 5.60
; ************************************************
DeclareModule FORM
  ; =======================================================
  ;-* les méthodes publiques
  ; =======================================================
  Interface _FORM
    Open(FenetreMere=#PB_Ignore)
    Free()
    AddCallback(*Callback,Declencheur.l=#PB_Ignore)
  EndInterface
  ;} ------------------------------------------------------
  ; =======================================================
  ;-* les prototypes public
  ; =======================================================
  Declare New(x,y,w,h,titre.s,flags.l=0)
  ;} ------------------------------------------------------
EndDeclareModule
Module FORM
  EnableExplicit
  Prototype monCallback(*objet,MonEvenement.l)
  ; =======================================================
  ;-* les membres privés
  ; =======================================================
  Structure sPOS
    x.l
    y.l
    w.l
    h.l
  EndStructure
  Structure sFORM
    *pFORM ; IMPORTANT ceci doit absolument être placé en premier et va contenir l'adresse des procédures
    maPosition.sPOS
    titre.s
    flags.l
    FenetreMere.l
    IdForm.l
    *Callback
  EndStructure
  ;} ------------------------------------------------------
  ; =======================================================
  ;-* les functions privées
  ; =======================================================
  Procedure AppelCallback(*This.sFORM)
    Protected maProcedure.monCallback
    With *This
      If \Callback
        maProcedure=\Callback
        maProcedure(*This,Event())
      EndIf
    EndWith
  EndProcedure
  Procedure Exit()
    ; IMPORTANT: je récupère l'adresse de l'objet
    Protected *This.sFORM=GetWindowData(EventWindow())
    With *This
      CloseWindow(\IdForm)
      ; Si une fenêtre mère
      If \FenetreMere<>#PB_Ignore
        DisableWindow(\FenetreMere,#False)
        SetActiveWindow(\FenetreMere)
      EndIf
      ; REMARQUE chaque procédure callback appellera cette procédure
      AppelCallback(*This)
    EndWith
  EndProcedure
 
  ;} ------------------------------------------------------
  ; =======================================================
  ;-* Le constructeur
  ; =======================================================
  Procedure New(x,y,w,h,titre.s,flags.l=0)
    Protected *objet.sFORM = AllocateStructure(sFORM)
    With *objet
      \pFORM=?PROC
      \maPosition\x=x
      \maPosition\y=y
      \maPosition\w=w
      \maPosition\h=h
      \titre=titre
      \flags=flags
      \FenetreMere=#PB_Ignore ; Pas de fenêtre mère
      ProcedureReturn *objet
    EndWith
  EndProcedure
  ;} ------------------------------------------------------
  ; =======================================================
  ;-* les méthodes publiques
  ; =======================================================
  Procedure Open(*This.sFORM,FenetreMere=#PB_Ignore)
    With *This
      \FenetreMere=FenetreMere
      ; si aucun flag n'est renseigné je crée ceux de base
      If \flags=0
        \flags=#PB_Window_SystemMenu
        ; je centre également la fenêtre en fonction d'une fenêtre mère ou pas
        If \FenetreMere=#PB_Ignore
          \flags|#PB_Window_ScreenCentered
        Else
          \flags|#PB_Window_WindowCentered
        EndIf
      EndIf
      If \FenetreMere=#PB_Ignore ; pas de fenêtre mère
        \IdForm=OpenWindow(#PB_Any,
                           \maPosition\x,
                           \maPosition\y,
                           \maPosition\w,
                           \maPosition\h,
                           \titre,
                           \flags)
      Else
        ; je désactive également la fenêtre mère
        DisableWindow(\FenetreMere,#True)
        \IdForm=OpenWindow(#PB_Any,
                           \maPosition\x,
                           \maPosition\y,
                           \maPosition\w,
                           \maPosition\h,
                           \titre,
                           \flags,
                           WindowID(\FenetreMere))
      EndIf
      ; important je vais sauver dans ma fenêtre l'adresse de mon objet
      SetWindowData(\IdForm,*This)
      ; Je vais placer maintenant un callback si l'utilisateur clique sur la croix
      BindEvent(#PB_Event_CloseWindow,@Exit(),\IdForm)
    EndWith
  EndProcedure
  Procedure Free(*This.sFORM)
    FreeStructure(*This)
  EndProcedure
  Procedure AddCallback(*This.sFORM,*Callback)
    With *This
      \Callback=*Callback
    EndWith
  EndProcedure
  ;} ------------------------------------------------------
  DataSection
    PROC: ; les adresses des procédures IMPORTANT il faut respecter le même ordre que l'interface
    Data.i @Open()
    Data.i @Free()
    Data.i @AddCallback()
  EndDataSection
EndModule
Windows 10 64 bits PB: 5.70 ; 5.72 LST
Work at Centre Spatial de Liège
Avatar de l’utilisateur
microdevweb
Messages : 1798
Inscription : mer. 29/juin/2011 14:11
Localisation : Belgique

Re: [TUTO] Une classe pour gérer une fenêtre

Message par microdevweb »

Une nouvelle classe Gadget

Vous remarquerez que cette classe est similaire à la classe "FORM". Cependant quelques différences subsistent. J'ai créer ici un bloque de code "les méthodes privées" et coupé en 2 également le "DataSection".

Nous allons voir dans le chapitre suivant, que nous n'utiliserons pas directement le constructeur pour les gadgets mais nous ferons appel à une méthode de la classz "FORM".

Nous verrons également comment rendre certaines méthodes de la classe "GADGET" privées et seulement accessible dans la classe "FORM"

Code : Tout sélectionner

; ************************************************
; Nom de la class : GADGET
; Autheur         : MicrodevWeb
; Version         : 1.0
; Requis          : Pb 5.60 / FORM.pbi
; ************************************************
DeclareModule GADGET
  ; =======================================================
  ;-* les méthodes public
  ; =======================================================
  Interface _GADGET
    GetTitle.s()
    GetValue.s()
    SetTitle(Valeur.s)
    SetValue(Valeur.s)
    AddCallback(*Callback)
  EndInterface
  ;} ------------------------------------------------------
  ; =======================================================
  ;-* les prototypes public
  ; =======================================================
  Declare New(x,y,w,h,titre.s,Type=#PB_GadgetType3D_String,Value.s="",flags.l=0)
  ;} ------------------------------------------------------
EndDeclareModule
Module GADGET
  EnableExplicit
  Prototype monCallback(*objet,MonEvenement.l)
  ; =======================================================
  ;-* les membres privés
  ; =======================================================
  Structure sPOS
    x.l
    y.l
    w.l
    h.l
  EndStructure
  Structure sGADGET
    *pGADGET ; IMPORTANT ceci doit absolument être placé en premier et va contenir l'adresse des procédures
    titre.s
    valeur.s
    flags.l
    TypeDeGadget.l
    IdGadget.l
    IdFrame.l
    *Callback
    maPosition.sPOS
  EndStructure
  ;} ------------------------------------------------------
  ; =======================================================
  ;-* les functions privées
  ; =======================================================
  Procedure MiseAJour(*This.sGADGET)
    With *This
      If IsGadget(\IdGadget)
        Select GadgetType(\IdGadget)
          Case #PB_GadgetType_String
            SetGadgetText(\IdFrame,\titre)
            SetGadgetText(\IdGadget,\valeur)
          Case #PB_GadgetType_Button
            SetGadgetText(\IdGadget,\titre)
        EndSelect
      EndIf
    EndWith
  EndProcedure
  Procedure monEvenement()
    Protected *This.sGADGET=GetGadgetData(EventGadget())
    Protected maProcedure.monCallback
    With *This  
      Select GadgetType(EventGadget())
        Case #PB_GadgetType_String
          \valeur=GetGadgetText(\IdGadget)
      EndSelect
      If \Callback
        maProcedure=\Callback
        maProcedure(*This,EventType())
      EndIf
    EndWith
  EndProcedure
  ;} ------------------------------------------------------
  ; =======================================================
  ;-* Le constructeur
  ; =======================================================
  Procedure New(x,y,w,h,titre.s,Type=#PB_GadgetType3D_String,Value.s="",flags.l=0)
    Protected *objet.sGADGET = AllocateStructure(sGADGET)
    With *objet
      \pGADGET=?PROC
      \TypeDeGadget=Type
      \flags=flags
      \titre=titre
      \valeur=Value
      \maPosition\x=x
      \maPosition\y=y
      \maPosition\w=w
      \maPosition\h=h
      ProcedureReturn *objet
    EndWith
  EndProcedure
  ;} ------------------------------------------------------
  
  ; =======================================================
  ;-* les méthodes publiques
  ; =======================================================
  Procedure.s GetTitle(*This.sGADGET)
    With *This
      ProcedureReturn \titre
    EndWith
  EndProcedure
  Procedure.s GetValue(*This.sGADGET)
    With *This
      ProcedureReturn \valeur
    EndWith
  EndProcedure
  Procedure SetTitle(*This.sGADGET,Valeur.s)
    With *This
      \titre=Valeur
    EndWith
  EndProcedure
  Procedure SetValue(*This.sGADGET,Valeur.s)
    With *This
      \valeur=Valeur
    EndWith
  EndProcedure
  Procedure AddCallback(*This.sGADGET,*callback)
    With *This
      \Callback=*callback
    EndWith
  EndProcedure
  ;} ------------------------------------------------------
  ; =======================================================
  ;-* les méthodes privées
  ; =======================================================
  Procedure Create(*This.sGADGET)
    With *This
      Select \TypeDeGadget
        Case #PB_GadgetType_String
          \IdFrame=TextGadget(#PB_Any,
                              \maPosition\x,
                              \maPosition\y,
                              \maPosition\w,
                              \maPosition\h/2,
                              \titre)
          \IdGadget=StringGadget(#PB_Any,
                                 \maPosition\x,
                                 \maPosition\y+\maPosition\h/2,
                                 \maPosition\w,
                                 \maPosition\h/2,
                                 \valeur,
                                 \flags)
          
        Case #PB_GadgetType_Button
          \IdGadget=ButtonGadget(#PB_Any,
                                 \maPosition\x,
                                 \maPosition\y,
                                 \maPosition\w,
                                 \maPosition\h,
                                 \titre)
      EndSelect
      ; Je sauve l'adresse de l'objet
      SetGadgetData(\IdGadget,*This)
      ; je place le callback
      BindGadgetEvent(\IdGadget,@monEvenement())
    EndWith
  EndProcedure
  Procedure Free(*This.sGADGET)
    FreeStructure(*This)
  EndProcedure
  ;} ------------------------------------------------------
  DataSection
    PROC: ; les adresse des procédures IMPORTANT il faut respecté le même ordre que l'interface
          ; Les méthodes PUBLIQUES
    Data.i @GetTitle()
    Data.i @GetValue()
    Data.i @SetTitle()
    Data.i @SetValue()
    Data.i @AddCallback()
    ; les méthodes PIVEES 
    Data.i @Free()
    Data.i @Create()
  EndDataSection
EndModule

Windows 10 64 bits PB: 5.70 ; 5.72 LST
Work at Centre Spatial de Liège
Avatar de l’utilisateur
microdevweb
Messages : 1798
Inscription : mer. 29/juin/2011 14:11
Localisation : Belgique

Re: [TUTO] Une classe pour gérer une fenêtre

Message par microdevweb »

Une classe qui contient des objets d'une classe étrangère

Nous n'utiliserons pas le constructeur pour ajouter les gadgets car c'est l'objet FORM qui contiendra tous les gadget.

Code : Tout sélectionner

; =======================================================
  ;-* les méthodes de gadget privées
  ; =======================================================
  Interface _PRIVATE_GADGET Extends GADGET::_GADGET
    Free()
    Create()    
  EndInterface
  ;} ------------------------------------------------------
  ; =======================================================
  ;-* les membres privés
  ; =======================================================
  Structure sPOS
    x.l
    y.l
    w.l
    h.l
  EndStructure
  Structure sFORM
    *pFORM ; IMPORTANT ceci doit absolument être placé en premier et va contenir l'adresse des procédures
    maPosition.sPOS
    titre.s
    flags.l
    FenetreMere.l
    IdForm.l
    *Callback
    List *myGADGET._PRIVATE_GADGET()
  EndStructure
On ne peux communiquer avec les objets "GADGET" qu'à travers des méthodes.
J'ai donc créer une nouvelle interface qui hérite de l'interface "GADGET" ceci va me permettre d’utiliser des méthodes privées qui ne seront accessible que dans la classe "FORM".

C'est ce qui justifie le fait d'avoir séparé le "Datasection" de la classe "GADGET" en deux.

Code : Tout sélectionner

DataSection
    PROC: ; les adresse des procédures IMPORTANT il faut respecté le même ordre que l'interface
          ; Les méthodes PUBLIQUES
    Data.i @GetTitle()
    Data.i @GetValue()
    Data.i @SetTitle()
    Data.i @SetValue()
    Data.i @AddCallback()
    ; les méthodes PIVEES 
    Data.i @Free()
    Data.i @Create()
  EndDataSection
Enfin la méthode "AddGadget" renvoie l'adresse de l'objet afin de permettre à l'utilisateur de pouvoir utiliser les méthodes publiques de classe "GADGET"

Code : Tout sélectionner

nom=maFenetreFille\AddGadget(10,10,400,40,"Nom")
Windows 10 64 bits PB: 5.70 ; 5.72 LST
Work at Centre Spatial de Liège
Avatar de l’utilisateur
microdevweb
Messages : 1798
Inscription : mer. 29/juin/2011 14:11
Localisation : Belgique

Re: [TUTO] Une classe pour gérer une fenêtre

Message par microdevweb »

Conclusion et code au complet

Voila évidement il est possible d'ajouter toutes les méthodes désirées, le but de ce tutoriel n'étant que de donné les bases.

Mais voici encore une autre facette de PureBasic intéressante, j'espère que ce tutoriel vous aura été utile et vous remercie de l'avoir suivi.

MicrodevWeb

code FORM.pbi

Code : Tout sélectionner

; ************************************************
; Nom de la class : FORM
; Autheur         : MicrodevWeb
; Version         : 1.0
; Requis          : Pb 5.60
; ************************************************
XIncludeFile "GADGET.pbi"
DeclareModule FORM
  ; =======================================================
  ;-* les méthodes public
  ; =======================================================
  Interface _FORM
    Open(FenetreMere=#PB_Ignore)
    Free()
    AddCallback(*Callback)
    GetId()
    AddGadget(x,y,w,h,titre.s,Type=#PB_GadgetType3D_String,Value.s="",flags.l=0)
    Close()
  EndInterface
  ;} ------------------------------------------------------
  ; =======================================================
  ;-* les prototypes public
  ; =======================================================
  Declare New(x,y,w,h,titre.s,flags.l=0)
  ;} ------------------------------------------------------
EndDeclareModule
Module FORM
  EnableExplicit
  Prototype monCallback(*objet,MonEvenement.l)
  ; =======================================================
  ;-* les méthodes de gadget privées
  ; =======================================================
  Interface _PRIVATE_GADGET Extends GADGET::_GADGET
    Free()
    Create()    
  EndInterface
  ;} ------------------------------------------------------
  ; =======================================================
  ;-* les membres privés
  ; =======================================================
  Structure sPOS
    x.l
    y.l
    w.l
    h.l
  EndStructure
  Structure sFORM
    *pFORM ; IMPORTANT ceci doit absolument être placé en premier et va contenir l'adresse des procédures
    maPosition.sPOS
    titre.s
    flags.l
    FenetreMere.l
    IdForm.l
    *Callback
    List *myGADGET._PRIVATE_GADGET()
  EndStructure
  ;} ------------------------------------------------------
  ; =======================================================
  ;-* les functions privées
  ; =======================================================
  Procedure AppelCallback(*This.sFORM)
    Protected maProcedure.monCallback
    With *This
      If \Callback
        maProcedure=\Callback
        maProcedure(*This,Event())
      EndIf
    EndWith
  EndProcedure
  Procedure Exit()
    ; IMPORTANT: je récupère l'adresse de l'objet
    Protected *This.sFORM=GetWindowData(EventWindow())
    With *This
      CloseWindow(\IdForm)
      ; Si une fenêtre mère
      If \FenetreMere<>#PB_Ignore
        DisableWindow(\FenetreMere,#False)
        SetActiveWindow(\FenetreMere)
      EndIf
      ; REMARQUE chaque procédure callback appellera cette procédure
      AppelCallback(*This)
    EndWith
  EndProcedure
  
  ;} ------------------------------------------------------
  ; =======================================================
  ;-* Le constructeur
  ; =======================================================
  Procedure New(x,y,w,h,titre.s,flags.l=0)
    Protected *objet.sFORM = AllocateStructure(sFORM)
    With *objet
      \pFORM=?PROC
      \maPosition\x=x
      \maPosition\y=y
      \maPosition\w=w
      \maPosition\h=h
      \titre=titre
      \flags=flags
      \FenetreMere=#PB_Ignore ; Pas de fenêtre mère
      ProcedureReturn *objet
    EndWith
  EndProcedure
  ;} ------------------------------------------------------
  ; =======================================================
  ;-* les méthodes publiques
  ; =======================================================
  Procedure Open(*This.sFORM,FenetreMere=#PB_Ignore)
    With *This
      \FenetreMere=FenetreMere
      ; si aucun flags n'est renseigné je crée ceux de bases
      If \flags=0
        \flags=#PB_Window_SystemMenu
        ; je centre également la fenêtre en fonction d'une fenêtre mère ou pas
        If \FenetreMere=#PB_Ignore
          \flags|#PB_Window_ScreenCentered
        Else
          \flags|#PB_Window_WindowCentered
        EndIf
      EndIf
      If \FenetreMere=#PB_Ignore ; pas de fenêtre mère
        \IdForm=OpenWindow(#PB_Any,
                           \maPosition\x,
                           \maPosition\y,
                           \maPosition\w,
                           \maPosition\h,
                           \titre,
                           \flags)
      Else
        ; je désactive également la fenêtre mère
        DisableWindow(\FenetreMere,#True)
        \IdForm=OpenWindow(#PB_Any,
                           \maPosition\x,
                           \maPosition\y,
                           \maPosition\w,
                           \maPosition\h,
                           \titre,
                           \flags,
                           WindowID(\FenetreMere))
      EndIf
      ; Je crée ici tous les gadgets
      ForEach \myGADGET()
        \myGADGET()\Create()
      Next
      ; important je vais sauver dans ma fenêtre l'adresse de mon objet
      SetWindowData(\IdForm,*This)
      ; Je vais placer maintenant un callback si l'utilisateur clique sur la croix
      BindEvent(#PB_Event_CloseWindow,@Exit(),\IdForm)
    EndWith
  EndProcedure
  Procedure Free(*This.sFORM)
    With *This
      ; je libère également tous les gadget
      ForEach \myGADGET()
        \myGADGET()\Free()
      Next
      FreeStructure(*This)
    EndWith
  EndProcedure
  Procedure AddCallback(*This.sFORM,*Callback)
    With *This
      \Callback=*Callback
    EndWith
  EndProcedure
  Procedure GetId(*This.sFORM)
    With *This
      If IsWindow(\IdForm)
        ProcedureReturn \IdForm
      EndIf
      ProcedureReturn -1
    EndWith
  EndProcedure
  Procedure AddGadget(*This.sFORM,x,y,w,h,titre.s,Type=#PB_GadgetType3D_String,Value.s="",flags.l=0)
    With *This
      ; j'ajoute mon objet
      AddElement(\myGADGET())
      ; j'instancie mon objet
      \myGADGET()=GADGET::New(x,y,w,h,titre,Type,Value,flags)
      ; je retourne mon instance
      ProcedureReturn \myGADGET()
    EndWith
  EndProcedure
  Procedure Close(*This.sFORM)
    With *This
      CloseWindow(\IdForm)
    EndWith
  EndProcedure
  ;} ------------------------------------------------------
  DataSection
    PROC: ; les adresse des procédures IMPORTANT il faut respecté le même ordre que l'interface
    Data.i @Open()
    Data.i @Free()
    Data.i @AddCallback()
    Data.i @GetId()
    Data.i @AddGadget()
    Data.i @Close()
  EndDataSection
EndModule
code GADGET.pbi

Code : Tout sélectionner

; ************************************************
; Nom de la class : GADGET
; Autheur         : MicrodevWeb
; Version         : 1.0
; Requis          : Pb 5.60 / FORM.pbi
; ************************************************
DeclareModule GADGET
  ; =======================================================
  ;-* les méthodes public
  ; =======================================================
  Interface _GADGET
    GetTitle.s()
    GetValue.s()
    SetTitle(Valeur.s)
    SetValue(Valeur.s)
    AddCallback(*Callback)
  EndInterface
  ;} ------------------------------------------------------
  ; =======================================================
  ;-* les prototypes public
  ; =======================================================
  Declare New(x,y,w,h,titre.s,Type=#PB_GadgetType3D_String,Value.s="",flags.l=0)
  ;} ------------------------------------------------------
EndDeclareModule
Module GADGET
  EnableExplicit
  Prototype monCallback(*objet,MonEvenement.l)
  ; =======================================================
  ;-* les membres privés
  ; =======================================================
  Structure sPOS
    x.l
    y.l
    w.l
    h.l
  EndStructure
  Structure sGADGET
    *pGADGET ; IMPORTANT ceci doit absolument être placé en premier et va contenir l'adresse des procédures
    titre.s
    valeur.s
    flags.l
    TypeDeGadget.l
    IdGadget.l
    IdFrame.l
    *Callback
    maPosition.sPOS
  EndStructure
  ;} ------------------------------------------------------
  ; =======================================================
  ;-* les functions privées
  ; =======================================================
  Procedure MiseAJour(*This.sGADGET)
    With *This
      If IsGadget(\IdGadget)
        Select GadgetType(\IdGadget)
          Case #PB_GadgetType_String
            SetGadgetText(\IdFrame,\titre)
            SetGadgetText(\IdGadget,\valeur)
          Case #PB_GadgetType_Button
            SetGadgetText(\IdGadget,\titre)
        EndSelect
      EndIf
    EndWith
  EndProcedure
  Procedure monEvenement()
    Protected *This.sGADGET=GetGadgetData(EventGadget())
    Protected maProcedure.monCallback
    With *This  
      Select GadgetType(EventGadget())
        Case #PB_GadgetType_String
          \valeur=GetGadgetText(\IdGadget)
      EndSelect
      If \Callback
        maProcedure=\Callback
        maProcedure(*This,EventType())
      EndIf
    EndWith
  EndProcedure
  ;} ------------------------------------------------------
  ; =======================================================
  ;-* Le constructeur
  ; =======================================================
  Procedure New(x,y,w,h,titre.s,Type=#PB_GadgetType3D_String,Value.s="",flags.l=0)
    Protected *objet.sGADGET = AllocateStructure(sGADGET)
    With *objet
      \pGADGET=?PROC
      \TypeDeGadget=Type
      \flags=flags
      \titre=titre
      \valeur=Value
      \maPosition\x=x
      \maPosition\y=y
      \maPosition\w=w
      \maPosition\h=h
      ProcedureReturn *objet
    EndWith
  EndProcedure
  ;} ------------------------------------------------------
  
  ; =======================================================
  ;-* les méthodes publiques
  ; =======================================================
  Procedure.s GetTitle(*This.sGADGET)
    With *This
      ProcedureReturn \titre
    EndWith
  EndProcedure
  Procedure.s GetValue(*This.sGADGET)
    With *This
      ProcedureReturn \valeur
    EndWith
  EndProcedure
  Procedure SetTitle(*This.sGADGET,Valeur.s)
    With *This
      \titre=Valeur
    EndWith
  EndProcedure
  Procedure SetValue(*This.sGADGET,Valeur.s)
    With *This
      \valeur=Valeur
    EndWith
  EndProcedure
  Procedure AddCallback(*This.sGADGET,*callback)
    With *This
      \Callback=*callback
    EndWith
  EndProcedure
  ;} ------------------------------------------------------
  ; =======================================================
  ;-* les méthodes privées
  ; =======================================================
  Procedure Create(*This.sGADGET)
    With *This
      Select \TypeDeGadget
        Case #PB_GadgetType_String
          \IdFrame=TextGadget(#PB_Any,
                              \maPosition\x,
                              \maPosition\y,
                              \maPosition\w,
                              \maPosition\h/2,
                              \titre)
          \IdGadget=StringGadget(#PB_Any,
                                 \maPosition\x,
                                 \maPosition\y+\maPosition\h/2,
                                 \maPosition\w,
                                 \maPosition\h/2,
                                 \valeur,
                                 \flags)
          
        Case #PB_GadgetType_Button
          \IdGadget=ButtonGadget(#PB_Any,
                                 \maPosition\x,
                                 \maPosition\y,
                                 \maPosition\w,
                                 \maPosition\h,
                                 \titre)
      EndSelect
      ; Je sauve l'adresse de l'objet
      SetGadgetData(\IdGadget,*This)
      ; je place le callback
      BindGadgetEvent(\IdGadget,@monEvenement())
    EndWith
  EndProcedure
  Procedure Free(*This.sGADGET)
    FreeStructure(*This)
  EndProcedure
  ;} ------------------------------------------------------
  DataSection
    PROC: ; les adresse des procédures IMPORTANT il faut respecté le même ordre que l'interface
          ; Les méthodes PUBLIQUES
    Data.i @GetTitle()
    Data.i @GetValue()
    Data.i @SetTitle()
    Data.i @SetValue()
    Data.i @AddCallback()
    ; les méthodes PIVEES 
    Data.i @Free()
    Data.i @Create()
  EndDataSection
EndModule
code main.pbi

Code : Tout sélectionner

EnableExplicit
XIncludeFile "FORM.pbi"
; Déclaration des l'objets
; --> Objets fenêtre
Global.FORM::_FORM maFenetreFille,maFenetreMere
; --> Objet gadget
Global.GADGET::_GADGET nom,prenom,bt_valide,bt_exit
Procedure EvenementSurFenetre(*form.FORM::_FORM,monEvenement.l)
  If monEvenement=#PB_Event_CloseWindow And *form=maFenetreFille
    Debug "La fenêtre fille à été fermée je libère l'objet"
    *form\Free()
  EndIf
  If monEvenement=#PB_Event_CloseWindow And *form=maFenetreMere
    Debug "La fenêtre mère à été fermée je libère l'objet et je quitte le program"
    *form\Free()
    End
  EndIf
EndProcedure
Procedure EvenementSurGadget(*gadget.GADGET::_GADGET,monEvenement.l)
  If *gadget=bt_exit
    maFenetreFille\Close()
  Else
    If *gadget=bt_valide
      Debug "Vous avez cliqué sur le bouton valider"
    Else
      Debug "La valeur de "+*gadget\GetTitle()+" est "+*gadget\GetValue()
    EndIf
  EndIf
EndProcedure
; instanciation des l'objet FORM
maFenetreMere=FORM::New(0,0,800,600,"Main",#PB_Window_SystemMenu|#PB_Window_Maximize)
maFenetreFille=FORM::New(0,0,420,160,"Teste")
; j'ajoute les callback
maFenetreMere\AddCallback(@EvenementSurFenetre())
maFenetreFille\AddCallback(@EvenementSurFenetre())
; Je crée les gadget
nom=maFenetreFille\AddGadget(10,10,400,40,"Nom")
nom\AddCallback(@EvenementSurGadget())
prenom=maFenetreFille\AddGadget(10,60,400,40,"Prénom")
prenom\AddCallback(@EvenementSurGadget())
bt_valide=maFenetreFille\AddGadget(10,110,100,30,"Valider",#PB_GadgetType_Button)
bt_valide\AddCallback(@EvenementSurGadget())
bt_exit=maFenetreFille\AddGadget(120,110,100,30,"Quitter",#PB_GadgetType_Button)
bt_exit\AddCallback(@EvenementSurGadget())
; j'ouvre la fenetre mère
maFenetreMere\Open()
; j'ouvre la fenêtre fille
maFenetreFille\Open(maFenetreMere\GetId())
Repeat 
  WaitWindowEvent()
ForEver 
Windows 10 64 bits PB: 5.70 ; 5.72 LST
Work at Centre Spatial de Liège
Répondre