IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

Lazarus Pascal Discussion :

Persistance d'images dans des cellules de StringGrid à partir d'un champ PostgreSQL.ByteA ou TEXT [Lazarus]


Sujet :

Lazarus Pascal

  1. #1
    Invité
    Invité(e)
    Par défaut Persistance d'images dans des cellules de StringGrid à partir d'un champ PostgreSQL.ByteA ou TEXT
    Bonjour,

    Après une petite "cessation d'activité" en Lazarus, je m'y recolle.

    J'ai un problème toujours non résolu qui m'agace vraiment. Je stocke des images [png 24*24] dans ma base de données PostgreSQL distante. Le serveur est hébergé, ne dispose pas de ftp et évidemment aucun protocole SMB/CIFS n'est envisageable.

    Les images de la table sont affichées dans des StringGrids. J'utilise les connecteurs natifs de Lazarus mais le problème serait identique en Zeos ou autre. L'insertion est classique :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
      Strm := TMemoryStream.Create;
      imgSrc.Picture.SaveToStream(Strm);
      with pgQuery do try
      [...]
         with SQL, Params do begin
           Clear;
           SQL.Text := 'INSERT INTO test2013 '+
                       '(teid, tenom, teimage) '+
                       'VALUES ' +
                       '(:paID, :paNOM, :paIMAGE);';
           ParamByName('paID').AsString  := AleaString();
           ParamByName('paNOM').AsString := 'TEST Numéro 1';
           ParamByName('paIMAGE').LoadFromStream(Strm, ftBlob);
         end;
         ExecSQL; 
      [...]
    La lecture l'est tout autant. Elle utilise également un Stream.

    Pour le StringGrid, j'ai une méthode trop compliquée et visiblement mal adaptée.
    J'aurais voulu dans un premier temps stocker le Stream dans une Cell[aImg, aRow] lors du chargement de la table. Mais c'est impossible car les Cells ne sont pas des strings mais des strings C (autrement dit le caractère Null équivaut à une fin de chaîne et le stream d'une image ne contient pas qu'un caractère Null...). J'ai pensé un moment utiliser un ImageList mais mes id ne sont pas numériques (mais alphanumériques) et donc les correspondances peu aisées... Les refresh des TStstingGrids sont assez "périlleux".

    Bref, quelqu'un a-t-il une solution correcte à ce problème ?

    Merci. Cordialement. Gilles
    Dernière modification par Invité ; 13/04/2013 à 19h00. Motif: Titre plus explicite

  2. #2
    Expert confirmé
    Avatar de Ph. B.
    Homme Profil pro
    Freelance
    Inscrit en
    Avril 2002
    Messages
    1 784
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 57
    Localisation : France, Haute Garonne (Midi Pyrénées)

    Informations professionnelles :
    Activité : Freelance
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Avril 2002
    Messages : 1 784
    Points : 5 915
    Points
    5 915
    Par défaut
    Bonjour,

    TDrawGrid permet de représenter des informations diverses, non ?
    En implémentant OnDrawCell, cela devrait le faire...
    Philippe.

  3. #3
    Invité
    Invité(e)
    Par défaut
    Bonjour,

    TDrawgrid, je n'ai jamais utilisé. Je vais regarder éventuellement de ce côté mais simplement comme "support" des TStringGrids.

    Je m'y prends certainement mal. Mes TStringGrids me servent de dbGrid en réalité [Mais ceci n'est pas négociable ... à moins que quelqu'un(e) m'explique comment gérer le crash qui résulte d'une déconnexion ponctuelle et limitée -disons une minute- d'Internet... Mes bases sont à l'autre bout... Or si on coupe la connexion ne serait-ce que 10", la dbgrid "s'effondre si on y touche"].

    Pour en revenir à mon problème, périodiquement, par thread, la TStringGrid est réactualisée. Chaque ligne de la Grid contient un DateTimeStamp initial. Si il diffère de la dernière lecture de la base, la ligne est remise à jour. Le OnDrawCell est alors très difficile à gérer pour les lignes dont le Stamp n'a pas varié. Les autres sont rechargées par le Stream issu de la liaison. Or dans onDrawcell, le fait de ne pas redessiner une ancienne image la supprime*. Donc je redessine l'ancienne image (celle dont le Stamp n'a pas varié) mais à partir de quoi ?... S'il faut relire toutes les images de la base, même si c'est threadé, c'est absurde. Donc dans quoi stocker mes "anciens" Stream ?

    Actuellement je joue dans la TstringGrid avec
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    TClassAddOn = class(TObject)
       private
          Selected : Boolean;
       public
          ID       : String[20];
          StrBlob  : String; //<--------------Ici stockage dans une VRAIE string 
       constructor Create(AOwner: TComponent); virtual;
      end;
    Cela fonctionne mais c'est lourd (la conversion)... et lent... sauf à utiliser les TStringStream (plus "légers à programmer") mais encore plus lents !

    Donc je remets en cause totalement mon approche... et je me renseigne... avant d'essayer de la modifier si "rien n'apparaît". La première amélioration dans ce cas sera d'essayer de modifier "StrBlob : String;" par "strBlob : TStream" diminuant ainsi par 2 le nombre des conversions nécessaires à l'affichage. J'avais essayé mais en vain. Mais à mon avis, il doit avoir mieux mais comme l'usage que je pratique de la StringGrid ne semble pas usuel, je trouve peu de commentaires sur la "chose".

    Cordialement. Gilles

    * "Or dans onDrawcell, le fait de ne pas redessiner une ancienne image la supprime." Pour s'en convaincre, il suffit d'essayer ce code. Un clic sur le bouton 7 efface toutes les images de la colonne et dessine celle de aRow = 3
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    type
      TFormMain = class(TForm)    
      [...]
    public
        { public declarations }
         bRepaint : boolean;
    end; 
     
    procedure TFormMain.FormCreate(Sender: TObject);
    begin
       bRepaint := False; 
    end;
     
    procedure TFormMain.Button7Click(Sender: TObject);
    begin
       bRepaint := True;
       StringGrid1.Refresh;
    end; 
     
    procedure TFormMain.StringGrid1DrawCell(Sender: TObject; aCol, aRow: Integer;
      aRect: TRect; aState: TGridDrawState);
    begin
      if not bRepaint then begin
         if (aCol = 1) and (aRow > 0) then
            StringGrid1.Canvas.Draw(aRect.Left +1, aRect.Top +1, imgSrc.Picture.Graphic);
      end else begin
         if (aCol = 1) and (aRow = 3) then
            StringGrid1.Canvas.Draw(aRect.Left +1, aRect.Top +1, imgSrc.Picture.Graphic);
      end;
    end;
    Dernière modification par Invité ; 12/04/2013 à 17h33. Motif: Relecture - Précisions

  4. #4
    Modérateur
    Avatar de tourlourou
    Homme Profil pro
    Biologiste ; Progr(amateur)
    Inscrit en
    Mars 2005
    Messages
    3 858
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 61
    Localisation : France, Yvelines (Île de France)

    Informations professionnelles :
    Activité : Biologiste ; Progr(amateur)

    Informations forums :
    Inscription : Mars 2005
    Messages : 3 858
    Points : 11 301
    Points
    11 301
    Billets dans le blog
    6
    Par défaut
    Donc dans quoi stocker mes "anciens" Stream ?
    Dans un tableau de Streams ?
    Ou dans une TStringGrid après transformation en Base64, style PJ de mail ?
    Ou juste les BitMaps correspondant, à leur emplacement dans un grand BMP grille et récupération du rectangle voulu ?
    Delphi 5 Pro - Delphi 11.3 Alexandria Community Edition - CodeTyphon 6.90 sous Windows 10 ; CT 6.40 sous Ubuntu 18.04 (VM)
    . Ignorer la FAQ Delphi et les Cours et Tutoriels Delphi nuit gravement à notre code !

  5. #5
    Invité
    Invité(e)
    Par défaut
    Dans un tableau de Streams ?
    • Incorporé dans la TstringGrid (dans son TObject lié)... J'ai rencontré non pas des problèmes de "déclaration" mais des problèmes de création et de libération de mémoire (suppression de lignes)... En plus, en supposant que j'utilise un strBLOB pour la première colonne d'image, il faut rentrer dans le code de mon composant s'il y en a 2 : strBlob1, stBlob2...... C'est extrêmement lourd.
    • A l'extérieur de la TStringGrid : gestion des tris, ajout, suppression extrêmement difficiles et lourds.


    Ou juste les BitMaps correspondant, à leur emplacement dans un grand BMP grille et récupération du rectangle voulu ?
    Je n'imagine même pas le lien entre les lignes, les actions sur la StringGrid et les Bitmaps.

    Ou dans une TStringGrid après transformation en Base64, style PJ de mail ?
    Oui je peux regarder de ce côté-là... Cela réglerait le problème du caratère null des string C autant que je m'en souvienne. Mais il va falloir utiliser les TStringStream + TBase64EncodingStream. Je ne sais pas pour le second... mais alors pour le premier, il ne faut pas être pressé... Déjà le nombre de balayages onDrawcell est souvent "important" pour une seule action de l'utilisateur... Mais je vais tenter cette approche.

    Elle a un avantage certain : on peut augmenter le nombre de colonnes graphiques à volonté quoique je ne dépasse rarement 2... Mais à la réflexion dans ce cas-là, je pourrais stocker mes images non pas dans dans ByteA mais dans des champs "base64" de PostgreSQL sachant que ce SGDB possède des fonctions d'encodage base64
    pgQuery.SQL.Text := INSERT into test2013 (teimage) VALUES (encode(:paImage, 'base64'))'; devrait passer.
    Et au lieu de remplir la StringGrid par un pgQuery.SQL.Text := 'SELECT decode(teimage, 'base64') FROM test2013;' on charge la base64 string DIRECTEMENT dans une Cell que l'on transforme en stream dans le OnDrawcell...

    Je teste. CR à l'issue.
    Merci pour votre aide à tous deux. Cordialement. Gilles
    Dernière modification par Invité ; 12/04/2013 à 18h22.

  6. #6
    Invité
    Invité(e)
    Par défaut
    Petit CR avant d'arrêter.
    Dans PostgreSQL 9.1, je crée le champ teblob [TEXT].

    Dans Lazarus
    Pour enregistrer une Timage dans la table :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
       Strm:=TMemoryStream.Create;
      imgSrc.Picture.SaveToStream(Strm);
      with pgQueryUID do try             
    [...]
           with SQL, Params do begin
           Clear;
           SQL.Text := 'INSERT INTO test2012 '+
                       '(teid, tenom, teblob) '+
                       'VALUES ' +
                       '(:paID, :paNOM, encode(:paImage, ' + QuotedStr('base64')+'));';
           ParamByName('paID').AsString  := AleaString();
           ParamByName('paNOM').AsString := 'TEST numéro 3';
           ParamByName('paIMAGE').LoadFromStream(Strm, ftBlob);
         end;
         ExecSQL;
    [...]
    Pour charger la StringGrid
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
     with pgQuery do try
        [...]
        with SQL, Params do begin
           Clear;
           SQL.Text := 'SELECT teid, tenom, teblob FROM test2012 WHERE tenom ='+quotedstr('TEST numéro 3')+';';
           Open;
           With StringGrid1 do begin
              cells[2,1] := Fields[0].AsString;
              cells[3,1] := Fields[1].AsString;
              cells[4,1] := Fields[2].AsString;
           end;
          [...]
     StringGrid1.Refresh;
    end;
    Dans Ondrawcell de la StringGrid
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    var
       Strm : TStream;
       S  : AnsiString;
       imTmp : TCustomImage;
    begin
      if (ACol=1) and (aRow =1 ) then
         if StringGrid1.Cells[4,1] <> '' then begin
            imTmp := TCustomImage.Create(nil);
            S := DecodeStringBase64(StringGrid1.Cells[4,1]);
            Strm := TStringStream.Create(S);  
            StringToStream(S, Strm);
            imTmp.Picture.LoadFromStream(sTrm);
            Strm.Free;    
            StringGrid1.Canvas.Draw(aRect.Left+1, aRect.Top+1,imTmp.picture.Graphic);
            imTMP.Free;
          end;          
    end;
    L'image apparaît bien dans Cells[1,1]. Donc, jouable sur le principe. Reste à mesurer la performance. Je testerai demain sur une grande table.

    Bonsoir. Gilles

  7. #7
    Expert confirmé
    Avatar de Ph. B.
    Homme Profil pro
    Freelance
    Inscrit en
    Avril 2002
    Messages
    1 784
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 57
    Localisation : France, Haute Garonne (Midi Pyrénées)

    Informations professionnelles :
    Activité : Freelance
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Avril 2002
    Messages : 1 784
    Points : 5 915
    Points
    5 915
    Par défaut
    Citation Envoyé par selzig Voir le message
    Je m'y prends certainement mal. Mes TStringGrids me servent de dbGrid en réalité [Mais ceci n'est pas négociable ... à moins que quelqu'un(e) m'explique comment gérer le crash qui résulte d'une déconnexion ponctuelle et limitée -disons une minute- d'Internet... Mes bases sont à l'autre bout... Or si on coupe la connexion ne serait-ce que 10", la dbgrid "s'effondre si on y touche"].
    Et en utilisant un TBufDataSet ?
    Cela permettrait d'embarquer en local les données nécessaires, les mettre à jour par le thread et resynchroniser le tout.
    Philippe.

  8. #8
    Invité
    Invité(e)
    Par défaut
    Citation Envoyé par Ph. B. Voir le message
    Et en utilisant un TBufDataSet ?
    Cela permettrait d'embarquer en local les données nécessaires, les mettre à jour par le thread et resynchroniser le tout.
    Bonjour,

    je ne maîtrise absolument pas cette technique et la documentation relative à ce sujet semble pour le moins "limitée".

    La mise à jour par thread m'a posé un problème inattendu quand j'ai évalué les diverses approches possibles dont ne faisait pas partie TBufDataSet. Aucun problème jusqu'au dataset (SQLQuery) mais impossible de franchir l'écueil datasource en thread... Je m'y suis peut-être mal pris mais à l'époque en analysant le code du DataSource, il présentait de manière évidente une incompatibilité "au Thread". C'était en 0.9.28. Cela a peut-être changé. Mais je reste "attentif" notamment si la chaîne incorporant le TBufDataSet comprend également un TDataSource.

    Donc par élimination le choix s'est imposé : Connecteur -> DataSet -> StringGrid... Et comme à l'époque, la TStringGrid de Lazarus était buguée (incapable de gérer les colonnes invisibles), j'ai failli laisser tomber Lazarus/FPC.

    Je ne regrette évidemment pas d'avoir persisté. Mettre les mains dans les composants a été et reste un énorme plaisir... alors qu'en Delphi 7, je "fabriquais" des bibliothèques externes. Mais je n'avais pas accès aux sources. Pour en revenir à mon choix, il a fallu tout réincorporer dans ma dbStringGrid : les tris (sur plusieurs colonnes [Col1 ASC + Col2 DESC...] avec des types différents [alpha, numériques...]) et les recherches (incorporés tous 2 dans l'entête), les mémos (multiligne des cellules avec hauteur auto ou max et dans ce cas end-ellipsis et le "Hint" de la cellule en survol -même si c'est encore du bricolage sous Nux pour cette dernière approche), les images, le multiselect non-contigu... et un peu d'embellissement . Je crois même que j'ai abusé du forum à ce sujet : 50% de tous mes messages sont probablement relatifs à ce composant. Reste un problème, celui de la fluidité... A force de surcharger les méthodes d'affichage, le composant peut en manquer. Aussi je cherche à optimiser ces méthodes et le cas de l'affichage des images provenant du SGDB traînait depuis trop longtemps.

    Je reporte un peu l'étude du TBufDataSet. Je préfère dans l'immédiat (ie pendant quelques jours) me concentrer sur le problème des images. Mais ne pas être curieux serait une erreur : il me faudrait une démo d'utilisation de TBufDataSet en situation réelle... avec un code source pour évaluer sa complexité. On trouve cela sur le Web ? J'ai passé un bout de la matinée à chercher sans rien trouver de vraiment concluant. Donc si vous possédez une adresse (ou plusieurs), je suis preneur.

    Bonne fin de WE.
    Merci pour l'information.

    Cordialement. Gilles
    Dernière modification par Invité ; 13/04/2013 à 17h40. Motif: Correction syntaxe - langage approximatif

  9. #9
    Invité
    Invité(e)
    Par défaut
    Bonjour,

    Testé sur une base importante, cela fonctionne correctement. On peut même améliorer la "chose" en utilisant les "lo_xxx" (Large Object) de PostgreSQL.

    Merci pour votre aide. Je considère le sujet comme résolu. J'ouvrirai un nouveau post pour le TBufdataset.

    Bonne journée ensoleillée puisqu'il paraît qu'ajourd'hui arrive le printemps
    Cordialement, Gilles

+ Répondre à la discussion
Cette discussion est résolue.

Discussions similaires

  1. Réponses: 0
    Dernier message: 13/01/2015, 08h27
  2. Ranger des images dans des cellules définies
    Par bobafric dans le forum Macros et VBA Excel
    Réponses: 4
    Dernier message: 05/07/2013, 09h34
  3. ajouter des combobox dans les cellules de stringgrid
    Par sky88 dans le forum Débuter
    Réponses: 5
    Dernier message: 22/01/2009, 18h35
  4. pbl image dans une cellule
    Par UNi[FR] dans le forum Balisage (X)HTML et validation W3C
    Réponses: 7
    Dernier message: 04/08/2006, 12h54

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo