Chacun de nous a beaucoup de photos qui gardent le souvenir de nos proches, amis, parents, événements mémorables et beaux endroits que nous avons visités. La technologie moderne vous permet de prendre rapidement et facilement des photos de haute qualité avec une haute résolution. De plus, l’appareil photo est désormais toujours à portée de main – tous les téléphones mobiles modernes ont la capacité de prendre des photos. Et ce serait formidable d’avoir un système simple et fonctionnel pour stocker et visualiser ces photos. L’environnement de développement visuel My Visual Database sera utilisé comme outil de création.

Conception

Description générale

Le programme créé permettra non seulement de visualiser vos photos, mais également de leur fournir une description, ainsi que des balises – clés, en combinant lesquelles vous pourrez trouver rapidement l’image souhaitée.

La deuxième fonctionnalité intéressante est la compilation d’albums, qui sont une présentation : chaque écran peut inclure plusieurs photos, images décoratives et descriptions textuelles qui seront stockées dans la base de données. Lorsque vous démarrez une présentation, les écrans changent à un intervalle spécifié à l’aide de transitions visuelles. Vous pouvez également ajuster la bande son, et en cliquant sur la photo, vous pouvez l’agrandir en plein écran.

Modèle d’affaires Ajout et stockage d’images : photographies (fichiers au format jpg), éléments de décoration (fichiers au format png, jpg).

Les images sont fournies avec des fonctionnalités : titre, description textuelle, date de création, description sonore.

Vous pouvez attacher une ou plusieurs balises aux images.

La balise a les propriétés suivantes : nom, catégorie. Options de catégories de balises : lieux, heures, personnes, émotions, décor, etc.

Lorsque vous ajoutez une photo, elle est copiée dans le stockage (un dossier spécial sur le disque) et
renommée. Le nom dans le référentiel correspond à l’ID d’enregistrement dans la base de données.

Une vignette est créée pour l’image : une version plus petite du fichier, qui est utilisée dans le mode d’affichage de la liste d’images. La vignette est stockée dans le référentiel, a un nom qui correspond au nom de l’image principale, mais avec le préfixe “m”.

Les images peuvent être organisées en albums. L’album a un titre et une description. L’album est composé de pages.

La page contient un ou plusieurs éléments : une image de la base de données ou un texte quelconque.
L’emplacement des éléments est défini de manière statique lors de la création de la page.

La vignette de couverture est créée pour l’album – l’image de la première page de l’album.
Pour chaque page de l’album, une vignette est créée, qui est utilisée lors de l’édition de l’album.

L’album peut être vu comme une présentation : pour chaque page, on peut régler le temps d’affichage, on peut régler la bande son de l’album, la page change par un effet visuel donné.

Pour le résultat de la recherche d’images, vous pouvez créer un album dynamique – une présentation basée sur un modèle fixe.

Pour les images, vous pouvez définir une zone de conseil – une zone rectangulaire, lorsque vous la survolez, un cadre et une inscription apparaissent. Par exemple, cela peut être utilisé pour désigner qui est sur la photo. Les conseils sont affichés en vue plein écran d’une seule image. La zone d’info-bulle est relative à la taille de l’image, elle s’adapte donc facilement à l’image.

Modèle de données

Lors de la conception, la création d’un modèle de données est l’étape la plus importante. Pour sa mise en
œuvre, des outils de conception spéciaux sont parfois utilisés, mais dans la plupart des cas, une description textuelle des entités, ainsi que de leurs propriétés, suffira. Une telle description peut être facilement compilée dans n’importe quel éditeur de texte ou esquissée au crayon sur une feuille de papier.

Image

  • Nom
  • La description
  • Date de création

Étiquette

  • Catégorie de balise
  • Nom

Catégorie de balise

  • Nom

Balises d’images

  • Image
  • Étiquette

Album

  • Nom
  • La description
  • Bande son (liste de lecture)
  • Durée d’affichage de la page par défaut

Page de l’album

  • Album
  • Numéro de série
  • Temps de rendu de la page
  • Effet de transition visuelle

Effet de transition visuelle

  • Nom

Élément d’album

  • Image
  • Poste X
  • Position U
  • Largeur
  • Hauteur
  • Texte
  • Nom de la police
  • Taille de police
  • Couleur de la police

Conseils d’image

  • Image
  • Poste X
  • Poste Y
  • Largeur
  • Hauteur
  • Texte

Représentation des données

La form principale est la form d’albums.

Sur la fiche des albums on trouve une zone de visualisation des vignettes des pochettes d’albums et un
panneau de recherche, une barre d’outils avec des boutons de contrôle : ajout, édition, suppression, passageen mode édition de la base d’images, lancement d’un album en mode présentation.

Sur le formulaire de base d’images, il y a une zone pour visualiser les vignettes des images et un panneau de recherche, une barre d’outils avec des boutons de contrôle : ajouter, éditer, supprimer, passer en mode de travail avec des albums.

La mise en œuvre du projet

My Visual Database (MVDB) dispose d’un système visuel pratique pour créer une base de données (DB). Il ne nécessite aucune connaissance de SQL pour l’utiliser.

Lors de la création d’une structure, un modèle de données créé précédemment est utile, dans lequel chaque entité correspond à une table de base de données et chaque propriété d’entité correspond à un champ de la table.

Avec une certaine expérience, vous pouvez utiliser MVDB pour la conception, mais, malheureusement, il lui manque la capacité de définir des noms alternatifs pour les tables et les champs, ainsi que la capacité de générer une représentation textuelle de la structure de la base de données. Cela peut être utile à la fois lors de la création de documentation et lors de la génération de formulaires de présentation de données.

Les bases de données

Schéma de données

Cette représentation est pratique pour une perception holistique de la structure. Vous pouvez l’obtenir en un clic :

Résultat intermédiaire

MVDB vous permet d’obtenir rapidement une application prête à l’emploi grâce à l’utilisation d’outils de
développement visuels : un ensemble de composants visuels, une représentation visuelle de formulaire
modifiable et un éditeur de propriétés.

Des détails sur les composants et les principes de leur utilisation peuvent être trouvés dans mon livre “Créer des applications dans ma base de données visuelle. Niveau débutant », qui se trouve dans la bibliothèque en ligne « My Visual Database » (https://mvdlibrary.blogspot.com).

Bien que j’aie un certain nombre de développements d’interface utilisateur, détaillés dans le livre
“Programmation visuelle”, j’ai décidé de me diversifier en utilisant la capacité intégrée de définir des styles d’application. Pour ce faire, le projet utilisera des composants standard et de nouvelles approches, cohérents avec le concept de styles, dont la force est la diversité et la facilité d’utilisation – il suffit de sélectionner le style souhaité dans le menu principal de l’environnement de développement.

Créer une structure de données et des formulaires dans My Visual Database est sans aucun doute un processus rapide et passionnant, mais dans notre cas, il est nécessaire de mettre en œuvre des fonctionnalités assez complexes, à savoir :

  • création de vignettes pour les images
  • création d’une routine d’affichage de vignettes
  • éditeur d’albums
  • éditeur d’indices d’image

Il est possible d’afficher des vignettes en utilisant des éléments d’interface standard (tableau), mais je souhaite faire un affichage adaptatif : pour que le nombre d’éléments horizontalement et verticalement change en fonction de la taille du formulaire. Le niveau de technologie augmente rapidement et la résolution FullHD n’est pas la limite, l’utilisateur peut avoir un écran 2K ou même 4K. Et vice versa – quelqu’un n’a qu’un ordinateur portable qui sort à peine de la HD.

L’éditeur d’albums est d’une complexité similaire au programme MS PowerPoint, ce qui peut également
retarder quelque peu le processus de développement, de sorte que la première version du programme sera probablement publiée sans albums, mais avec un stockage d’images complet et un système de recherche avancé.

Galerie d’images

Visualisez les images sous forme de galerie de mini-images. Une telle fonctionnalité sera utile non seulement dans l’album de famille, mais également dans d’autres programmes axés sur le stockage d’images.

Miniatures

Le principe de la galerie repose sur le fait que pour chaque image une vignette est créée – une copie réduite de l’image avec une taille donnée. Le programme standard “Explorer” fait de même : pour chaque image du dossier, il crée automatiquement une vignette qui s’affiche dans la partie principale de l’écran si le mode d’affichage “Grandes icônes” est sélectionné. Et à droite, la zone de visualisation de l’image sélectionnée peut être réfléchie, dont la taille peut être modifiée.

Malheureusement, il n’existe aucun moyen de créer automatiquement des vignettes dans My Visual Database en raison de la capacité limitée à travailler avec des composants d’image. Par conséquent, l’utilisateur créera des vignettes dans “l’album de famille” en définissant manuellement la zone qui sera affichée sur l’image de la vignette. Cependant, c’est encore mieux, car la galerie doit être composée d’images carrées et les images originales peuvent avoir n’importe quel rapport d’aspect. Par conséquent, seul l’utilisateur peut décider exactement ce qui doit être affiché sur la vignette.

Stockage de données

Le deuxième point important est la façon dont les images sont stockées. Bien que les bases de données SQLite et MySQL vous permettent de stocker des images directement dans la base de données, cette méthode n’est pas adaptée pour stocker de grandes quantités de données, car elle ralentira sensiblement le système. Par conséquent, les images originales, ainsi que les vignettes, seront stockées dans le stockage de fichiers – un dossier sur le disque à côté de la base de données.

Pour simplifier le système de recherche, le nom du fichier image d’origine sera composé de l’ID d’entrée et de l’extension, et pour la vignette, le type de vignette est ajouté à l’ID.

Le type de miniature est la possibilité d’étendre la fonctionnalité du système de stockage à l’avenir afin qu’il puisse être utilisé dans d’autres projets. Par exemple, un service des ressources humaines peut souhaiter une vue miniature supplémentaire d’une photo de profil 3×4, tandis qu’une arborescence de structure de service peut nécessiter des vignettes circulaires. L’extension de fichier de vignettes (format de stockage) sera corrigée – JPG est bien adapté à de telles fins. Mais nous écrirons l’extension du fichier d’origine dans la base de données – il sera nécessaire de charger l’image dans les composants visuels.

L’album de famille utilisera trois dossiers pour stocker les images, nommés d’après les tables de base de
données correspondantes:

  • album – vignettes de la couverture de l’album
  • image – images originales et vignettes de photographies et d’images décoratives
  • page – vignettes de la page de l’album

Il n’y aura qu’un seul type de vignette : une image carrée à afficher dans une galerie de 150 x 150 pixels. Le type est codé avec le chiffre 0, qui est séparé du nom par un caractère de soulignement. Ainsi, le dossier des fichiers image ressemble à ceci :

Formulaire d’affichage

Le formulaire comporte un panneau d’affichage de miniatures (1) et un panneau de prévisualisation (2). En plus des boutons d’édition de base de données habituels, la barre d’outils comporte une case à cocher pour contrôler la visibilité du panneau de prévisualisation (3) et un bouton btnSearch invisible, dont le gestionnaire de clics construit une galerie d’images. Sur le panneau de prévisualisation, en plus de l’image elle-même, il y a quelques étiquettes pour afficher le titre (5) et la description (6).

La case à cocher a été conçue comme un bouton utilisant un panneau. Cette décision est due au fait
malheureux que les boutons standard (TdbButton) n’ont pas la capacité de définir la propriété Down. Peut-être qu’à l’avenir, nous implémenterons la barre d’outils à l’aide du composant TToolbar, mais pour l’instant, afin de ne pas trop compliquer le code du programme, nous nous limiterons à une case à cocher sur le panneau. L’utilisation d’un bouton appelé btnSeach est un clin d’œil à une norme dont nous pourrions également avoir besoin à l’avenir.

Pour que le formulaire vierge acquière les conditions nécessaires (ajouter un séparateur pour contrôler la taille du panneau de prévisualisation, rendre l’étiquette responsable de la description de la photo adaptative en taille), vous devez le “terminer” à l’aide d’un script.

procedure InitForm;
var 
  tmpSplitter:TSplitter;
begin
  frmImage.panToolbar.Align := alTop;
  frmImage.panImagePreview.Align := alRight;
  frmImage.panPreview.Align := alClient;
  tmpSplitter := TSplitter.Create(frmImage); // ajoute un séparateur de zone d'aperçu
  tmpSplitter.Name := 'splMain';
  tmpSplitter.Parent := frmImage;
  tmpSplitter.Left := frmImage.panImagePreview.Left - 1;
  tmpSplitter.Align := alRight;
  tmpSplitter.Width := 7;
  // rend la taille de l'inscription avec la description de l'image adaptative
  frmImage.labName.align := alTop;
  frmImage.labDescription.align := alBottom;
  frmImage.imgPreview.align := alClient;
  // essayer de cacher le panneau
  frmImage_chbShowPreview_OnClick( frmImage.chbShowPreview );
end;
Langage du code : Delphi (delphi)

Création de la galerie

Pour construire des données, une simple requête de base de données est utilisée. À l’avenir, cette requête
deviendra plus complexe pour filtrer les données selon des critères spécifiés.

procedure frmImage_btnSearch_OnClick (Sender: TObject; var Cancel: boolean);
var 
  tmpSQL: string;
  tmpDataSet: TDataSet;
  tmpTop: integer;
  tmpLeft: integer;
  tmpImage:TdbImage;
  tmpScrollBox: TScrollBox;
begin
  Control_ClearChild( frmImage.panPreview );
  tmpScrollBox := TScrollBox.Create(frmImage); // pour le défilement vertical, ajoutez une zone de défilement :)
  tmpScrollBox.Parent := frmImage.panPreview;
  tmpScrollBox.Align := alClient;
  tmpScrollBox.Name := 'scbPreview';
  //
  frmImage.panPreview.Tag := 0; // ID de l'élément sélectionné
  tmpTop := 0;
  tmpLeft := 0;
  tmpSQL := 'SELECT id, name, description FROM image'; // création d'aperçus sur le panneau
  SQLQuery(tmpSQL,tmpDataSet);
  try
    while not tmpDataSet.EOF do
    begin
      tmpImage := TdbImage.Create(frmImage); // ajouter une image
      tmpImage.Parent := tmpScrollBox;
      tmpImage.Top := tmpTop + 2;
      tmpImage.Left := tmpLeft + 2;
      tmpImage.Width := 150;
      tmpImage.Height := 150;
      tmpImage.Proportional := False;
      tmpImage.Stretch := False;
      AssignEvents(tmpImage); // assigne des gestionnaires aux boutons
      tmpImage.dbOnClick := 'PreviewImage_OnClick';
      tmpImage.dbOnDoubleClick := 'PreviewImage_OnDoubleClick';
      // utilise certaines propriétés d'image pour stocker des données
      tmpImage.Tag := tmpDataSet.FieldByName('id').asInteger;
      tmpImage.TagString := tmpDataSet.FieldByName('name').asString;
      tmpImage.Hint := tmpDataSet.FieldByName('description').asString;
      // charge l'image depuis le stockage de fichiers
      ImageEdit_LoadImageFromBase( tmpImage, tmpDataSet.FieldByName('id').asInteger, PI_SMALL,TM_IMAGE);
      tmpDataSet.Next;
      // calcule la position suivante de l'image
      tmpLeft := tmpLeft + 154; // pas de grille
      // si l'image dépasse la largeur du panneau + la largeur du scroll, alors
      if frmImage.panPreview.Width < tmpLeft + 154 + 20 then
      begin // passe à une nouvelle ligne
        tmpLeft := 0;
        tmpTop := tmpTop + 154;
      end;
    end;
  except
    tmpDataSet.Free;
  end;
end;Langage du code : Delphi (delphi)

Cette procédure ajoute des gestionnaires pour cliquer et double-cliquer sur l’image. Veuillez noter que pour définir le gestionnaire, au lieu de la propriété onDoubleClick (qui, pour une raison quelconque, n’est pas disponible dans les scripts), la propriété similaire dbonDoubleClick est utilisée.

Lors de l'affectation de gestionnaires via les propriétés de la vue db*, vous devez utiliser la procédure
AssignEvents().

Un clic normal est utilisé pour sélectionner une image (affichage d’un cadre et chargement des données d’image dans le panneau de prévisualisation), tandis qu’un double-clic est utilisé pour éditer l’image.

Pour charger une image à partir du disque, la procédure ImageEdit_LoadImageFromBase() est appelée, qui passe en paramètre le composant à charger, l’identifiant de l’enregistrement et des paramètres supplémentaires, dont nous parlerons plus tard. Cela est dû au fait qu’il est nécessaire de considérer un
ensemble de scripts qui assurent la création de vignettes.

Taille réactive

Lorsque la largeur du panneau est modifiée, les vignettes sont déplacées de sorte qu’elles occupent toute la largeur de la zone de défilement. Si les éléments ne tiennent pas dans la fenêtre, une barre de défilement verticale apparaît automatiquement.

procedure frmImage_panPreview_OnResize (Sender: TObject);
// redimensionner
var
  i: integer;
  tmpImage:TdbImage;
  tmpFrame:TShape;
  C: TControl;
  tmpPanel: TdbPanel;
  tmpTop: integer;
  tmpLeft: integer;
  tmpForm:TAForm;
  tmpScrollBox: TScrollBox;
begin
  CForm(Sender,tmpForm);
  tmpPanel := TdbPanel(Sender);
  tmpTop := 0;
  tmpLeft := 0;
  FindC(tmpForm,'scbPreview',tmpScrollBox,False);
  //  le traitement n'est nécessaire que pour le panneau rempli
  if tmpScrollBox <> nil then
  begin
    // déplacer les images
    for i := tmpScrollBox.ControlCount - 1 downto 0 do
    begin
      C := tmpScrollBox.Controls[i];
      if C is TdbImage then
      begin
        c.Top := tmpTop + 2;
        c.Left := tmpLeft + 2;
        tmpLeft := tmpLeft + 154;
        if tmpPanel.Width < tmpLeft + 154 + 20 then
        begin
          tmpLeft := 0;
          tmpTop := tmpTop + 154;
        end;
      end;
    end;
    // déplace le cadre, s'il y en a un.
    FindC(tmpForm,'shpSelector',tmpFrame,False);
    if tmpFrame <> nil then
    begin
      tmpImage := TdbImage(frmImage.panPreview.Tag);
      tmpFrame.Top := tmpImage.Top - 2;
      tmpFrame.Left := tmpImage.Left - 2;
    end;
  end;
end;Langage du code : Delphi (delphi)

Éditeur de vignettes

L’image miniature de l’original peut être utilisée à diverses fins. Par exemple, pour afficher dans une galerie d’albums photos ou comme avatar d’un employé dans un programme de comptabilité du personnel, pour afficher dans un arbre généalogique, etc.

Principales propriétés du composant TdbImage

Le composant TdbImage est hérité du composant standard TImage, qui est capable de “redimensionner” l’image qui y est chargée, en la réduisant ou en l’étirant à la taille du canevas. Dans le même temps, des algorithmes d’anticrénelage sont utilisés pour donner à l’image un aspect naturel. Quelles propriétés gouvernent ce processus ?

Stretch (Etirer)

La propriété stretch vous permet d’étirer une image, en la rendant plus grande ou plus petite que l’original.

  • Faux – l’image est affichée sans mise à l’échelle
  • True – l’image est étirée sur toute la taille du composant ; cependant, il peut être augmenté ou diminué.

Veuillez noter que l’image a été déformée lors de la mise à l’échelle, car lors du redimensionnement, la
proportion est violée : le rapport entre la largeur et la hauteur de l’image. Dans certains cas, ce n’est pas
critique (par exemple, lors de l’étirement d’une image monochrome ou d’un dégradé), mais pour les photos et les dessins, vous devez conserver les proportions. La propriété Proportionnelle en est responsable, qui complète la propriété Stretch.

Proportionnel

Faux – les proportions ne sont pas conservées, l’image est étirée pour s’adapter au composant
Vrai – les proportions sont conservées

Centre

Cette propriété permet de centrer l’image
False – l’image est rendue à partir du coin supérieur gauche du composant
Vrai – l’image est affichée au centre du composant

Algorithme

Pour obtenir la vignette, nous avons besoin de deux composants TdbImage. Le premier composant contiendra l’image d’origine. Le deuxième composant est pour la vignette. Les deux composants auront leurs propriétés définies pour mettre à l’échelle l’image tout en conservant le rapport d’aspect : Stretch = Vrai, Proportional = Vrai et Centre = Vrai.

Pour obtenir une copie de l’image, utilisez la méthode CopyRect() du canevas. Il copie sur le canevas une image qui se trouve sur un autre canevas. Pour que le focus fonctionne, les composants TdbImage doivent être placés sur des panneaux et le canevas du panneau parent doit être utilisé comme source de copie. L’explication est simple : pour que l’image chargée dans le composant TdbImage devienne visible, elle est dessinée sur le canevas du composant parent, dans notre cas, le panneau. Et il est dessiné en tenant compte des propriétés d’échelle décrites ci-dessus. Et le canevas TdbImage contient l’image d’origine.

Il est également nécessaire de définir en quelque sorte la zone de copie, il est souhaitable que le mécanisme soit intuitif. Par exemple, assombrissez l’image et rendez la zone de copie transparente en lui fournissant des “aides” – de petits carrés à faire glisser avec la souris, avec lesquels vous pouvez modifier la taille de la “fenêtre” transparente.

Pour l’ombrage, vous devrez utiliser 4 images dans lesquelles une image png translucide est chargée, et pour les aides – quatre marques noires opaques. Nous aurons également besoin d’une autre “image sans image” – une image complètement transparente, que nous ferons glisser sur l’écran pour sélectionner la zone de copie, et tous les autres éléments (rideaux translucides, aides) se déplaceront après, créant l’illusion d’un “trou” transparent.

Pour qu’un outil soit universel, tous ses réglages doivent être décrits par des constantes. De plus, il doit être capable de créer plusieurs vignettes de tailles différentes à la fois à partir d’une image donnée.

Classes virtuelles

Étant donné que My Visual Database ne vous permet pas de créer vos propres classes, vous devrez créer une “classe virtuelle” – une bibliothèque de scripts qui fournit les fonctionnalités requises pour une instance d’un composant d’une classe accessible.

Dans ce livre, cela n’a aucun sens de commenter toutes les méthodes des classes virtuelles, je ne parlerai donc que des principales.

La forme

Nous aurons besoin d’un formulaire sur lequel nous placerons les éléments clés : le panneau de l’image source, le panneau des vignettes et les boutons auxiliaires pour contrôler le processus. Tous les autres éléments seront créés par le programme, dont nous placerons les scripts dans un module séparé. Ceci est fait pour que l’éditeur de vignettes puisse être facilement intégré dans n’importe quel projet créé sur la plateforme My Visual Database.

TImageEdit

Strictement parlant, l’éditeur d’images dans lequel l’image sera chargée. Des méthodes de sauvegarde de
l’image, des méthodes de manipulation des obturateurs et des aides pour contrôler la zone de copie seront également mises en œuvre.

Méthodes de classe virtuelle:

  • ImageEdit_GetFileExt() – extension du fichier en cours d’édition
  • ImageEdit_GetImageDir() – récupère le chemin de l’image par type –
  • ImageEdit_AjustHelpers() – définit les assistants par image
  • ImageEdit_Create() – création d’un éditeur d’images
  • ImageEdit_CreatePreviews() – mettre à jour les vignettes
  • ImageEdit_Frame_OnMouseDown() – début du déplacement
  • ImageEdit_Frame_OnMouseMove() – déplacer le cadre du sélecteur
  • ImageEdit_Frame_OnMouseUp() – achèvement du déplacement
  • ImageEdit_GetPhoto() – prend une image du canevas aux coordonnées spécifiées par le cadre
  • ImageEdit_Helper_OnMouseDown() – commencer à déplacer l’assistant
  • ImageEdit_Helper_OnMouseMove() – déplacement des aides
  • ImageEdit_Helper_OnMouseUp() – fin du déplacement de l’assistant
  • ImageEdit_Init() – définir le mode d’édition
  • ImageEdit_LoadImage() – chargement d’une image pour l’édition
  • ImageEdit_LoadImageFromBase() – charge l’image depuis le serveur
  • ImageEdit_PhotoPanel_OnResize() – redimensionnement du panneau – ajustement du cadre
  • ImageEdit_Reset() – réinitialiser à l’état d’origine
  • ImageEdit_RestoreImages() – chargement des photos depuis le serveur de fichiers
  • ImageEdit_SaveImages() – enregistrement de photos sur un serveur de fichiers
  • ImageEdit_SetFrame() – définir les assistants et définir l’écran pour l’édition de photos
  • ImageEdit_SetFrameDef() – réinitialiser à l’état par défaut
  • ImageEdit_SetTargetImage() – définition de l’image cible

Les images sont stockées dans des dossiers dont les noms sont les mêmes que les noms de table, et le nom de fichier se compose de l’ID d’enregistrement et de l’extension. Pour les fichiers originaux, l’extension d’origine est conservée, pour les fichiers vignettes, l’extension jpg.

procedure ImageEdit_LoadImageFromBase(AImage: TdbImage; AID: integer; APhotoType: integer; AImageType: integer);
// télécharger l'image depuis le serveur
// si le fichier existe, chargez-le ; sinon, insérez l'image par défaut
// AImage - où télécharger
// AIDE - ID d'image
// APhotoType - taille du texte
// AImageType - type d'imag
var
  tmpFileName: string;
  tmpFolder: string;
  tmpFileExt: string;
begin
  tmpFolder := ImageEdit_GetImageDir(AImageType);
  // l'extension du fichier d'origine est inconnue, il doit donc être extrait par la recherche
  if APhotoType = IE_FT_ORIGINAL then
  begin
    // ici, vous devez faire une vérification de type, mais dans notre projet, les images originales ne sont stockées
que dans les photos
    tmpFileExt := SQLExecute('SELECT FileExt FROM image WHERE id ='+IntToStr(AID));
    tmpFileName := tmpFolder + IntToStr(AID) + tmpFileExt;
  end
  else // pour le reste, la taille est incluse dans le nom, le format de fichier est jpg
    tmpFileName := tmpFolder + IntToStr(AID) + '_' + IntToStr(APhotoType) + '.jpg';
  if FileExists(tmpFileName) then // si le fichier existe, chargez-le
    AImage.Picture.LoadFromFile(tmpFileName)
  else 
    SetImage(AImage, IE_IMG_DEFAULT); // sinon, insère l'image par défaut
end;

procedure ImageEdit_SaveImages(AImageEdit: TdbImage;); // enregistre les photos sur le serveur de fichiers
var
  tmpTIPanel: TdbPanel;
  tmpForm: TAForm;
  tmpName: string;
  tmpFileName: string;
  tmpID: Integer;
begin
  tmpID := AImageEdit.Tag;
  CForm(AImageEdit, tmpForm);
  tmpName := DeleteClassName(AImageEdit.Name);
  FindC(tmpForm, T_TARGET_IMAGE_PANEL + tmpName, tmpTIPanel);
  tmpFileName := tmpTIPanel.TagString + inttostr(tmpID) + AImageEdit.TagString;
  SaveImageToFile(AImageEdit, tmpFileName, True);
  // si l'image est vide, alors le fichier n'est pas enregistré, sinon on enregistre le reste
  if FileExists(tmpFileName) then
  begin
    TargetImagePanel_SaveImages(tmpTIPanel, tmpID);
  end;
end;Langage du code : Delphi (delphi)

TTargetImage

Module miniature. Accepte les images de TImageEdit, fonctionne avec le stockage de fichiers (enregistre et charge les vignettes au format jpg).

Méthodes de classe virtuelle :

  • TargetImage_Checker_OnClick() – cliquez sur le vérificateur d’en-tête
  • TargetImage_Create() – création d’un panneau pour afficher/enregistrer une image de la taille souhaitée
  • TargetImage_Reset() – réinitialiser l’image par défaut
  • TargetImage_RestoreImage() – charge une image
  • TargetImage_SaveImage() – enregistrer l’image

L’image est enregistrée en deux étapes. Tout d’abord, nous préparons l’image : nous formons le nom du fichier et supprimons le cadre, s’il y en avait un.

procedure TargetImage_SaveImage(APanel: TdbPanel; AID: integer); // enregistrer l'image
var tmpForm: TAForm;
tmpName: string;
 tmpImage: TdbImage;
tmpFrame: TdbImage;
tmpTIPanel: TdbPanel;
tmpFileName: string;
begin
 tmpTIPanel := TdbPanel(APanel.Parent);
 tmpName := DeleteClassName(DeleteSuffix(APanel.Name));
 CForm(APanel, tmpForm);
 FindC(tmpForm, T_IMAGE + tmpName, tmpImage);
 FindC(tmpForm, T_IMAGE + tmpName + SX_FRAME, tmpFrame, False);
// collecte le nom du fichier : dossier + ID + extension
 tmpFileName := tmpTIPanel.TagString + inttostr(AID) + APanel.TagString;
 if tmpFrame <> nil then // s'il y a un cadre, alors supprimer
 begin
 tmpFrame.Visible := False;
 Application.ProcessMessages;
 end;
 SaveImageToFile(tmpImage, tmpFileName);
 if tmpFrame <> nil then // puis retour
 tmpFrame.Visible := True;
end;Langage du code : Delphi (delphi)

Ensuite, nous écrivons l’image dans un fichier :

procedure SaveImageToFile(AImage: TdbImage; AFileName: string; AOriginalSize: boolean = False);
// enregistrement de l'image de l'image dans un fichier, au format JPG
// l'image doit avoir la même taille sur le panneau, puisque l'image est tirée du canevas du panneau.
// cela permet d'économiser
var 
  tmpBuffer: TBitMap;
  tmpJPG: TJPEGImage;
  tmpPan: TdbPanel;
begin
  if AOriginalSize then // pas de mise à l'échelle, résolution d'origine
    AImage.Picture.SaveToFile(AFileName)
  else
  begin
    tmpBuffer := TBitMap.Create;
    tmpJPG := TJPEGImage.Create;
    tmpPan := TdbPanel(AImage.Parent); // s'assurer que le panneau existe
    tmpBuffer.Width := AImage.Width;
    tmpBuffer.Height := AImage.Height;
    tmpBuffer.Canvas.CopyRect(0, 0, tmpBuffer.Width, tmpBuffer.Height, tmpPan.Canvas, 0, 0, mpBuffer.Width,
    tmpBuffer.Height);
    tmpJPG.Assign(tmpBuffer);
    tmpJPG.SaveToFile(AFileName);
    tmpJPG.Free;
    tmpBuffer.Free;
  end;
end;
Langage du code : Delphi (delphi)

TTargetImagePanel

Panneau de placement de modules pour l’obtention de vignettes. Adaptateur – conteneur. Lien entre
TImageEdit et TTatgetImage set.

Méthodes de classe :

  • TargetImagePanel_Clear() – efface le panneau des vignettes
  • TargetImagePanel_Reset() – réinitialise toutes les images du panneau à leur état d’origine
  • TargetImagePanel_RestoreImages() – restaurer les images de la base de données
  • TargetImagePanel_SaveImages() – enregistrement des images dans la base de données
  • TargetImagePanel_SetMode() – définit le mode de fonctionnement – ajoute les composants nécessaires au
  • panneau d’image cible

Toutes les méthodes, à l’exception de la dernière, opèrent sur la liste des éléments TTargetImage, en lui
transmettant la commande appropriée. Et la méthode TargetImagePanel_SetMode() gère la liste elle-même : elle supprime et crée les éléments nécessaires sur le panneau.

procedure TargetImagePanel_SetMode(APanel: TdbPanel; AMode: integer);
// définir le mode de fonctionnement - ajouter les composants nécessaires au panel d'images cibles
var
  tmpImageEditor: TdbImage;
  tmpForm: TAForm;
  tmpName: string;
  tmpTIList: array of string;
  i: integer;
  tmpTIType: integer;
  tmpMaxWidth: integer;
  tmpParent: TWinControl;
  tmpPanel: TdbPanel;
begin
  tmpMaxWidth := 0;
  tmpName := DeleteClassName(DeleteSuffix(APanel.Name));
  CForm(APanel, tmpForm);
  FindC(tmpForm, T_IMAGE_EDIT + tmpName, tmpImageEditor);
  // si le mode est le même que celui en cours, on ne change pas la composition des vignettes, on réinitialise uniquement les images à celles d'origine
  if APanel.Tag <> AMode then
  begin
    if AMode > (Length(ie_List) - 1) then
    begin
      RaiseException('TargetImagePanel_SetMode - режим не поддерживается: '+IntToStr(AMode) );
      Exit;
    end;
    TargetImagePanel_Clear(APanel); // supprimer toutes les vignettes
    APanel.TagString := ImageEdit_GetImageDir(AMode); // création d'un dossier
    // ajouter des vignettes
    // récupère la liste des formats
    tmpTIList := SplitString( ie_List[ AMode ] ,',' );
    for i:=0 to Length(tmpTIList) - 1 do
    begin
      tmpTIType := StrToInt( tmpTIList[i] );
      // ajoute la vignette cible au panneau
      TargetImage_Create('TI'+IntToStr( tmpTIType ), ie_TICaption[tmpTIType], tmpImageEditor, APanel,
ie_TIWidth[tmpTIType], ie_TIHeight[tmpTIType] );
      if ie_TIWidth[tmpTIType] > tmpMaxWidth then
        tmpMaxWidth := ie_TIWidth[tmpTIType];
    end;
    APanel.Tag := AMode; // stocker le mode actuel ici
  end;
  // définit la largeur de la barre de vignettes
  // convention : l'éditeur et le panneau de vignettes doivent être sur le même composant parent
  tmpParent := TWinControl( APanel.Parent );
  APanel.Width := tmpMaxWidth + IE_TI_MARGIN * 2;
  APanel.Left := tmpParent.Width - APanel.Width;
  tmpPanel := TdbPanel( tmpImageEditor.Parent );
  tmpPanel.Width := tmpParent.Width - APanel.Width;
  TargetImagePanel_Reset(APanel);
end;
Langage du code : Delphi (delphi)

Étant donné que les données d’initialisation sont stockées dans des tableaux, la procédure TargetImagePanel_SetMode n’a pas besoin d’être réécrite chaque fois que le jeu de vignettes ou les modes d’édition changent.

Options d’initialisation

Toutes les constantes et variables de l’éditeur sont placées dans un module IEConstVar.pas séparé :

// Constantes et variables
const
  // Options de l'éditeur d'images
  IE_HELPER_SIZE = 10; // taille de l'assistant
  IE_HELPER_COLOR = clBlack; // couleur de l'assistant
  IE_IMG_TRANSLUCENT = 'Black50'; // nom du composant avec image translucide, 50% noir
  IE_IMG_DEFAULT = 'DefImage'; // image par défaut
  // Miniature
  IE_TI_CHECKBOX_SIZE = 24; // taille du vérificateur de sélection de vignettes actives
  IE_TI_MARGIN = 16; // retrait vertical
  IE_TI_CHECKER_NAME = 'PhotoEditMode_'; // nom de l'élément avec checker
  //
  // pour initialiser les tableaux
  IE_TI_COUNT = 2; // nombre de formats de vignettes
  IE_COUNT = 3; // nombre de formats d'édition
  //
  // pour communiquer avec l'application principale
  //
  // types d'images (formats d'édition)
  IE_IT_UNKNOW = -1; // Etat initial
  IE_IT_IMAGE = 0; // images/photos
  IE_IT_ALBUM = 1; // album d'art
  IE_IT_PAGE = 2; // prévisualiser les pages de l'album
  // quei charger - formats de vignettes
  IE_FT_ORIGINAL = -1; // image originale

  IE_FT_SMALL = 0; // petite image
  IE_FT_MIDDLE = 1; // une plus grande image

var
  ie_MovedObject: TObject; // objet mobile
  ie_pX: integer;
  ie_pY: integer;
  // formats des vignettes
  ie_TIWidth: array of integer; // largeur de la vignette
  ie_TIHeight: array of integer; // hauteur de la vignette
  ie_TICaption: array of string; // nom de la vignette
  // édite les formats
  ie_Tables: array of string; // table de stockage
  ie_List: array of string; // liste des vignettes, nombres séparés par des virgules

begin
  // initialisation
  //
  // formats des vignettes
  SetLength(ie_TIWidth, IE_TI_COUNT);
  SetLength(ie_TIHeight, IE_TI_COUNT);
  SetLength(ie_TICaption, IE_TI_COUNT);
  //
  ie_TICaption[0] := 'Vignette du tableau';
  ie_TIWidth[0] := 150;
  ie_TIHeight[0] := 150;
  //
  ie_TICaption[1] := 'Page d'album';
  ie_TIWidth[1] := 250;
  ie_TIHeight[1] := 250;
  //
  // édite les formats
  SetLength(ie_Tables, IE_COUNT);
  SetLength(ie_List, IE_COUNT);
  //
  ie_Tables[0] := 'Image';
  ie_List[0] := '0';
  //
  ie_Tables[1] := 'Album';
  ie_List[1] := '1';
  //
  ie_Tables[2] := 'Page';
  ie_List[2] := '1';
end.Langage du code : Delphi (delphi)

Recherche par tags

L’efficacité de la recherche dépend des algorithmes appliqués. Chaque image a un titre, une description et une liste de balises dans la base de données. La recherche par titre de texte et description est la plus simple à mettre en œuvre. Mais c’est aussi le plus lent, car la condition LIKE est généralement utilisée pour rechercher, à l’aide de laquelle un fragment de texte est trouvé. Une option de recherche alternative, qui est utilisée avec succès dans de nombreux réseaux sociaux, est une recherche par balises – mots-clés associés aux données souhaitées.

Structure de données

Pour organiser la recherche par balises, la structure de table nécessaire a été créée :

Les images (Image) sont associées à des balises (tag) par une relation plusieurs-à-plusieurs (imageTag). Le champ tag.Checked est utilisé pour filtrer par balises, ce qui fonctionne bien pour une application monoutilisateur. Un répertoire supplémentaire de catégories de balises vous permettra de mettre les choses en ordre s’il y a beaucoup de balises.

Modification des balises d’image

Selon les canons de My Visual Database, pour éditer la table imageTag, il faudrait ajouter une table enfant au formulaire d’édition d’image, dans laquelle les balises seraient affichées. Mais ce n’est pas très pratique. Premièrement, je ne suis pas fan des tables enfants, et deuxièmement, il y aura des difficultés à organiser le processus d’édition d’une telle table, car l’édition de données dans la table elle-même avec un grand nombre de balises sera gênante. Et troisièmement, lors de l’ajout d’une image, il devient souvent nécessaire d’ajouter de nouvelles balises. Par conséquent, nous utiliserons un éditeur de texte et un script spécial pour modifier les balises d’image.

Deux composants conviennent à un éditeur de texte : TdbMemo et TdbRichEdit. Cependant, j’ai opté pour ce dernier, car c’est lui qui vous permet de mettre en évidence des mots individuels du texte tapé avec des couleurs différentes.

Comme prévu, l’utilisateur tape des balises dans l’éditeur, et après avoir terminé l’édition, et ce moment est déterminé par une pause dans la frappe, le programme les vérifie automatiquement. Et si une telle balise n’est pas encore dans la base de données, elle est alors surlignée dans une couleur différente afin que l’utilisateur comprenne immédiatement : soit il s’agit d’une nouvelle balise, soit il a fait une erreur d’écriture. Et lors de l’enregistrement des données pour chaque nouvelle balise, un formulaire d’ajout s’ouvre pour sélectionner la catégorie de balise.

La première difficulté est venue du comportement de TdbRichEdit lors de l’utilisation d’un style, à savoir que la couleur de fond du document n’est pas définie conformément au style sélectionné. De plus, ce composant n’a pas de propriété pour contrôler la couleur de fond de l’éditeur. La solution a été trouvée : lorsque l’application démarre, un document HTML est chargé dans le composant, dans lequel la propriété de couleur d’arrière-plan est définie pour le corps (mais les choses sont pires avec le composant TdbDateTimePicker – il brille avec une tache blanche brillante sur le noir thème “TableDark” et vous devrez très probablement le remplacer par TdbEdit et quelques scripts…).

La deuxième difficulté, ou plutôt une nuance de comportement, était que l’événement OnChange se produisait avec tout changement dans l’éditeur (à titre de comparaison, dans TdbMemo OnChange se produit uniquement lorsque l’utilisateur modifie manuellement le texte), j’ai donc dû utiliser le gestionnaire d’événement OnKeyUp , qui démarre un minuteur de 2 secondes .

procedure frmImageEdit_redTags_OnKeyUp (Sender: TObject; var Key: Word; Shift, Alt, Ctrl: boolean);
// libère la clé dans l'éditeur de balises
begin
  if TagTimer = nil then // s'il n'y a pas encore de minuterie, alors créez-la
  begin
    TagTimer := TTimer.Create(frmImageEdit);
    TagTimer.OnTimer := @TagTimer_OnTimer;
  end;
  TagTimer.Enabled := False; // redémarre le minuteur pendant 2 secondes
  TagTimer.Interval := 2000;
  TagTimer.Enabled := True;
  // définit le signe que les données ont été modifiées par l'utilisateur
  frmImageEdit.redTags.Tag := 1;
end;
Langage du code : Delphi (delphi)

Et après la fin de l’édition des balises, un gestionnaire est lancé qui vérifie les balises et les met en surbrillance si nécessaire.

procedure TagTimer_OnTimer (Sender: TObject); // traitement de la liste des balises dans l'éditeur de balises
var
  i: integer;
  Tags: array of string;
  s: string;
  tmpTag: string;
  tmpETag: string;
begin
  TagTimer.Enabled := False; // arrête le chronomètre
  s := frmImageEdit.redTags.Text; // récupère les données de la balise
  frmImageEdit.redTags.Clear; // efface l'éditeur
  // remplace les virgules par des espaces, supprime les retours à la ligne et les doubles espaces
  s := ReplaceStr(s,',',' ');
  s := ReplaceStr(s,chr(13),' ');
  s := ReplaceStr(s,' ',' ');
  Tags := SplitString( s,' '); // récupère une liste de balises sous forme de tableau de chaînes
  for i := 0 to length(Tags) - 1 do
  begin
    tmpTag := Trim(Tags[i]); // на всякий случай, чтобы остался только текст
    if (tmpTag <> '') then
    begin
      // recherche d'un tag dans la base de données
      tmpETag := VarToStr( SQLExecute('SELECT name FROM tag WHERE upper( name ) = upper("'+tmpTag+'")') );
      if tmpETag = '' then // si non trouvé, alors
      begin
        // insère la balise dans l'orthographe de l'utilisateur, mais avec une première lettre en majuscule, en jaune
        tmpTag := UpperCase(copy(tmpTag,1,1)) + copy(tmpTag,2,length(tmpTag)-1);
        frmImageEdit.redTags.InsertTextEx(tmpTag,$00FFFF,11,0,'Segoe UI')
      end // si trouvé, alors insérer par écrit à partir de la base, en blanc
      else
        frmImageEdit.redTags.InsertTextEx(tmpETag,$FFFFFF,11,0,'Segoe UI');
      frmImageEdit.redTags.InsertTextEx(' ',$FFFFFF,11,0,'Segoe UI'); // ajouter de l'espace
    end;
  end;
end;
Langage du code : Delphi (delphi)

Vous aurez également besoin de quelques scripts : pour enregistrer les balises dans la base de données

procedure frmImageEdit_btnSave_OnAfterClick (Sender: TObject); // après une sauvegarde réussie
var 
  tmpSQL: string;
  s: string;
  tmpIDTag: string;
  tmpIDImage: string;
  Tags: array of string;
  i: integer;
begin
  if frmImageEdit.redTags.Tag = 1 then // fonctionne avec des balises
  begin
    tmpIDImage := IntToStr(frmImageEdit.btnSave.dbGeneralTableId); // ID d'image
    tmpSQL := 'DELETE FROM imageTag WHERE id_image = '+tmpIDImage; // supprimer l'ancien
    SQLExecute(tmpSQL);
    s := frmImageEdit.redTags.Text; // enregistrer le nouveau
    s := ReplaceStr(s,',',' ');
    s := ReplaceStr(s,chr(13),' ');
    s := ReplaceStr(s,' ',' ');
    Tags := SplitString(s,' ');
    for i := 0 to length(Tags) - 1 do
    begin
      s := Trim(Tags[i]);
      if (s <> '') then
      begin
        // utilise une recherche insensible à la casse
        tmpSQL := 'SELECT id FROM tag WHERE upper( name ) = upper("'+s+'")';
        tmpIDTag := VarToStr( SQLExecute(tmpSQL) ); // ID de balise
        // s'il n'y a pas de balise de ce type dans la base de données, nous suggérons à l'utilisateur de l'ajouter
        if tmpIDTag = '' then
        begin
          // TODO : faire une option d'ajout automatique, sans intervention de l'utilisateur
          frmTagEdit.TagString := s; // ajouter une nouvelle balise
          frmTagEdit.NewRecord('tag');
          tmpIDTag := VarToStr( SQLExecute('SELECT id FROM tag WHERE upper( name ) = upper("'+s+'")') );
        end;
        if tmpIDTag <> '' then
        begin
          tmpSQL := 'INSERT INTO imageTag (id_image,id_tag) VALUES ( '+tmpIDImage+', '+tmpIDTag+') ';
          SQLExecute(tmpSQL);
        end;
      end;
    end;
  end;
end;
Langage du code : Delphi (delphi)

…et de restaurer l’apparence des balises de la base de données lors de l’ouverture du formulaire d’édition: après tout, la base de données de chaque image ne stocke pas les textes des balises, mais des liens vers cellesci, ce qui vous permet d’effectuer des sélections avec de grands tableaux de données assez rapidement, ainsi que d’utiliser diverses opérations logiques et une recherche pertinente.

procedure frmImageEdit_OnShow (Sender: TObject; Action: string); // affichage du formulaire
var
  s: string;
  tmpSQL: string;
begin
  // charge les images de la base de données
  ImageEdit_LoadImageFromBase( frmImageEdit.imgImage, frmImageEdit.btnSave.dbGeneralTableId,
IE_FT_ORIGINAL, IE_IT_IMAGE);
  ImageEdit_LoadImageFromBase( frmImageEdit.imgPreview, frmImageEdit.btnSave.dbGeneralTableId,
IE_FT_SMALL, IE_IT_IMAGE);
  if Action = 'NewRecord' then
  begin
    frmImageEdit.edtName.Text := 'Nouvelle image';
    frmImageEdit.redTags.InsertTextEx(' ',$FFFFFF,11,0,'Segoe UI');
  end
  else
  begin
    // génère une liste de balises
    tmpSQL := ' SELECT GROUP_CONCAT (tag.name,'' '') FROM imageTag LEFT JOIN tag ON tag.id =
    imageTag.id_tag WHERE imageTag.id_image = '+IntToStr(frmImageEdit.btnSave.dbGeneralTableId);
     s := VarToStr( SQLExecute(tmpSQL) );
     frmImageEdit.redTags.InsertTextEx(s,$FFFFFF,11,0,'Segoe UI');
  end;
  frmImageEdit.redTags.Tag := 0;
end;Langage du code : Delphi (delphi)

Systeme de recherche

La barre de recherche est située sur la barre d’outils. Il comprend un champ de saisie de texte (1), un checker pour activer le mode de recherche par tags (2), un checker pour activer le “OU” logique lors de la recherche par tags (3), un bouton pour sélectionner des tags dans le répertoire (4 ) et, en fait, un bouton de recherche (5).

procedure frmImage_btnSearch_OnClick (Sender: TObject; var Cancel: boolean); // création de la galerie
var
  tmpSQL: string;
  tmpDataSet: TDataSet;
  tmpTop: integer;
  tmpLeft: integer;
  tmpImage:TdbImage;
  tmpScrollBox: TScrollBox;
begin
  Control_ClearChild( frmImage.panPreview );
  // pour le défilement vertical, ajoutez une zone de défilement :)
  tmpScrollBox := TScrollBox.Create(frmImage);
  tmpScrollBox.Parent := frmImage.panPreview;
  tmpScrollBox.Align := alClient;
  tmpScrollBox.Name := 'scbPreview';
  //
  frmImage.panPreview.Tag := 0; // ID de l'élément sélectionné
  tmpTop := 0;
  tmpLeft := 0;
  if frmImage.edtFilter.Text = '' then // création d'aperçus sur le panneau
    tmpSQL := 'SELECT id, name, description FROM image LIMIT 30' // la totalité
  else
  if not frmImage.chbTag.Checked then
    // par nom ou description par entrée
    tmpSQL := 'SELECT id, name, description FROM image WHERE (name LIKE "%'+frmImage.edtFilter.Text+'%")
OR (description LIKE "%'+frmImage.edtFilter.Text+'%") LIMIT 30'
  else
    if frmImage.chbOR.Checked then // "OU", trié par pertinence

      tmpSQL := ' SELECT id, name, description FROM ( SELECT count(image.id) as c, image.id, image.name,
  image.description FROM tag LEFT JOIN imageTag ON imageTag.id_tag = tag.id LEFT JOIN image ON image.id =
  imageTag.id_image WHERE tag.checked = 1 AND image.id IS NOT NULL GROUP BY image.id ORDER BY 1 DESC
LIMIT 30 ) '
  else
    // correspondance stricte "ET"
    tmpSQL := 'SELECT id, name, description FROM ( SELECT count(image.id) as c, image.id, image.name,
image.description FROM tag LEFT JOIN imageTag ON imageTag.id_tag = tag.id LEFT JOIN image ON image.id =
imageTag.id_image WHERE tag.checked = 1 AND image.id IS NOT NULL GROUP BY image.id ) WHERE c =
(SELECT count(*) FROM tag WHERE tag.checked = 1) LIMIT 30';
  SQLQuery(tmpSQL,tmpDataSet);
  try
    while not tmpDataSet.EOF do
    begin
      // ajouter une image
      tmpImage := TdbImage.Create(frmImage);
      tmpImage.Parent := tmpScrollBox;
      tmpImage.Top := tmpTop + 2;
      tmpImage.Left := tmpLeft + 2;
      tmpImage.Width := 150;
      tmpImage.Height := 150;
      tmpImage.Proportional := False;
      tmpImage.Stretch := False;
      // assigne des gestionnaires aux boutons
      AssignEvents(tmpImage);
      tmpImage.dbOnClick := 'frmImage_PreviewImage_OnClick';
      tmpImage.dbOnDoubleClick := 'frmImage_PreviewImage_OnDoubleClick';
      // utilise certaines propriétés d'image pour stocker des données
      tmpImage.Tag := tmpDataSet.FieldByName('id').asInteger;
      tmpImage.TagString := tmpDataSet.FieldByName('name').asString;
      tmpImage.Hint := tmpDataSet.FieldByName('description').asString;
      // charge l'image depuis le stockage de fichiers
      ImageEdit_LoadImageFromBase( tmpImage, tmpDataSet.FieldByName('id').asInteger, IE_FT_SMALL,
IE_IT_IMAGE);
      tmpDataSet.Next;
      // calcule la position suivante de l'image
      tmpLeft := tmpLeft + 154; // шаг сетки
      // si l'image dépasse la largeur du panneau + la largeur du scroll, alors
      if frmImage.panPreview.Width < tmpLeft + 154 + 20 then
      begin // passe à une nouvelle ligne
        tmpLeft := 0;
        tmpTop := tmpTop + 154;
      end;
    end;
  finally
    tmpDataSet.Free;
  end;
end;
Langage du code : Delphi (delphi)

Si vous tapez du texte dans la barre de recherche et cliquez sur le bouton “Rechercher”, alors la sélection des données s’effectuera via une requête SQL avec recherche de texte par occurrence dans les champs image.nom et image.description.

Lorsque le vérificateur “Tags” est activé, le texte saisi dans la barre de recherche est interprété comme un ensemble de balises séparées par un espace ou une virgule. Le résultat inclut les images qui ont toutes les
balises spécifiées.

Lorsque le vérificateur “Ou” est activé, le résultat inclura les images qui ont au moins une des balises
recherchées. Les images elles-mêmes seront triées par ordre de pertinence (la plupart des résultats en
premier).

Lorsque vous cliquez sur le bouton “Sélectionner des balises dans le répertoire”, le répertoire de balises
s’ouvre, qui est également un filtre personnalisé.

Pour sélectionner les balises, une colonne avec des contrôleurs (1) est utilisée, tandis que les balises peuvent être filtrées par catégorie (2). Pour réinitialiser tous les vérificateurs, le bouton “Effacer la sélection” (3) est fourni. Et pour confirmer le choix, le bouton “Sélectionner” (4), lorsqu’il est pressé, les balises sélectionnées sont transférées vers le filtre de recherche. La requête de sélection de données contient une limite fixe sur le nombre d’enregistrements, de sorte que la construction de la galerie prend un temps fini et relativement faible. À l’avenir, il sera nécessaire d’ajouter un bouton spécial “Afficher plus” pour charger le prochain lot d’images. La requête sera similaire, mais inclura la commande OFFSET – un décalage depuis le début de la sélection.

Images sur les boutons

Une fois certaines des fonctionnalités principales implémentées, vous pouvez faire une pause et réfléchir à l’amélioration de l’apparence du programme. Par exemple, remplacez les images standard sur les boutons par des images plus modernes qui correspondent à la palette de couleurs sélectionnée.

La tâche est résolue en deux étapes : créer (rechercher) des images et les charger dans le programme. Je recommande de passer par la première étape en utilisant un outil pratique et gratuit : la bibliothèque d’images Pichon pour Windows, qui peut être trouvée sur https://icons8.com

Étant donné que les boutons du thème sélectionné sont affichés en deux couleurs contrastées, nous aurons besoin de deux ensembles d’images : les plus claires pour un fond sombre et les plus sombres pour un fond clair. Strictement parlant, le choix de la couleur des images est déterminé par le style : j’utiliserai la couleur du texte qui affiche les étiquettes sur les boutons.

Pour le style TabletDark, ce sont le blanc et le noir. À ces fins, les images sont bien adaptées pour lesquelles vous pouvez définir la couleur (Material), en particulier Material Two Tone – ces images, en plus du ton principal, contiennent des demi-teintes – des zones d’images translucides.

Les images elles-mêmes peuvent être stockées dans un fichier graphics.dll spécial, qui est généré
automatiquement et contient toutes les données graphiques utilisées en mode de conception de formulaire. Cette méthode est bien décrite dans le livre Modern UI. Mais cette fois, les images seront chargées au moment du lancement de l’application à partir d’un dossier spécial, ce qui est plus conforme aux principes de style du programme. Je note que le style de l’application finale dépend de la présence et du contenu du fichier style.vsf, c’est-à-dire qu’en modifiant ce fichier, vous pouvez changer le style de l’application sans la recompiler. La même chose peut être faite avec les images des boutons, simplement en remplaçant les fichiers du dossier par des images.

Images de classe virtuelle

Pour implémenter toutes les fonctions de travail avec des images, nous allons créer une classe virtuelle et un ensemble de scripts. Le but de la classe est de travailler avec des ensembles d’images qui seront chargées dans le programme au moment du lancement de l’application.

La classe est implémentée via des méthodes abstraites (sans créer d’instance de la classe), il n’y en a que
quatre:

  • Images_ButtonsAssign() – Attribuer des images aux boutons sur les formulaires
  • Images_Init() – Initialisation des données
  • Images_Load() – Chargement des images
  • Images_Set – Affecter des index d’image au bouton par nom de fichier

Les données sont stockées dans des variables globales, dans des tableaux de différents types. Si MVDB prenait en charge la syntaxe à part entière du langage Pascal, alors au lieu de ce zoo, nous utiliserions des données structurées (Record), mais, comme on dit, ce qui est, est.

Const // les dossiers sont relatifs au dossier de l'application
  IMAGES_DIR = '\Images\'; // dossier où se trouvent les images
  IMAGES_SELECTED_SUFFIX = '_S'; // suffixe dans le nom de l'image du bouton sélectionné
  IMAGES_HOT_SUFFIX = '_H'; // suffixe dans le nom de l'image du bouton au survol
  IMAGES_DISABLE_SUFFIX = '_D'; // suffixe dans le nom de l'image du bouton désactivé
  IMAGES_PRESSED_SUFFIX = '_P'; // suffixe dans le nom de l'image du bouton pressé
  IMAGES_FORMAT_COUNT = 2; // combien de formats sont dans la bibliothèque
  // liste des formats
  IMAGES_FORMAT_BUTTON = 0; //
  IMAGES_FORMAT_BIGBUTTON = 1; //
  //
  // bouton image chargement modèles
  IMAGES_SIMPLE_MODEL = 1; // simplifié
  IMAGES_FULL_MODEL = 2; // Complet
var
  ImagesDir: array of string; // dossiers de stockage
  ImagesSize: array of integer; // tailles des images
  ImagesList: array of TImageList; // stockage d'images
  ImagesNames: array of TStringList; // noms des images chargéesLangage du code : Delphi (delphi)

L’initialisation des données est effectuée au démarrage de l’application. Il comprend la configuration de dossiers pour le téléchargement d’images et leur taille. La particularité de l’utilisation de la classe TImageList pour stocker des images est qu’elle peut stocker des images de même taille. Par conséquent, chaque taille nécessitera un stockage séparé.

procedure Images_Init; // initialisation des données
var
  i: integer;
begin
  // initialisation des tableaux de données
  SetLength(ImagesDir,IMAGES_FORMAT_COUNT);
  SetLength(ImagesSize,IMAGES_FORMAT_COUNT);
  SetLength(ImagesList,IMAGES_FORMAT_COUNT);
  SetLength(ImagesNames,IMAGES_FORMAT_COUNT);
  // paramètres effectués pour le projet
  ImagesDir[IMAGES_FORMAT_BUTTON] := IMAGES_DIR + 'Buttons\';
  ImagesSize[IMAGES_FORMAT_BUTTON] := 32;
  ImagesDir[IMAGES_FORMAT_BIGBUTTON] := IMAGES_DIR + 'BigButtons\';
  ImagesSize[IMAGES_FORMAT_BIGBUTTON] := 48;
  // charge les données
  for i := 0 to IMAGES_FORMAT_COUNT-1 do
  begin
    ImagesNames[i] := TStringList.Create;
    ImagesList[i] := TImageList.Create(MainForm);
    Images_Load(i); // Chargement des images
  end;
  Images_ButtonsAssign( IMAGES_FORMAT_BUTTON,'','frmShow' );
  Images_ButtonsAssign( IMAGES_FORMAT_BIGBUTTON,'frmShow','' );
end;
Langage du code : Delphi (delphi)

L’initialisation comprend non seulement la définition de tableaux pour un projet spécifique, mais également le chargement de données à partir du disque, ainsi que l’attribution automatique d’images aux boutons.

procedure Images_Load( AIndex:integer ); // chargement des images
// réalisé par une procédure paramétrée, puisque l'application peut avoir plusieurs tailles d'image,
// qui doit être stocké dans différentes listes ; png
Var
  tmpList:TStringList; // liste des fichiers du dossier avec les images
  tmpImageList:TImageList;
  tmpImageNames: TStringList;
  i:integer;
  tmpImageDir: string;
  s:string;
begin
  tmpImageDir := ExtractFilePath(Application.ExeName)+ImagesDir[AIndex];
  if DirectoryExists(tmpImageDir) then
  begin
    tmpImageList := ImagesList[AIndex];
    tmpImageNames := ImagesNames[AIndex];
    //
    tmpImageList.Masked:=false;
    tmpImageList.ColorDepth:=cd32bit;
    tmpImageList.Width := ImagesSize[AIndex]; // taille de l'image
    tmpImageList.Height := ImagesSize[AIndex];
    tmpList := TStringList.Create;
    try
      tmpList.Text := GetFilesList( tmpImageDir );
      for i := 0 to tmpList.Count - 1 do
      begin
        tmpImageList.AddPng( tmpList.strings[i] );
        s := UpperCase( ExtractFileName(tmpList.strings[i]) );
        s := copy(s,1,Length(s)-4);
        tmpImageNames.Add( s );
      end;
    finally
      tmpList.Free;
    end;
 end
 else
   RaiseException('Dossier introuvable '+tmpImageDir);
end;
Langage du code : Delphi (delphi)

Les images doivent être au format PNG. Leurs noms doivent suivre certaines règles : des suffixes dans les noms de fichiers sont utilisés pour désigner les images des différents états des boutons.

  • _S(elected) – Bouton sélectionné – Le bouton sélectionné est le bouton avec le focus, le focus est sur le bouton après avoir appuyé ou utilisé le bouton Tab
  • _H(ot) – Bouton survolé par le curseur – Au survol, si le bouton est disponible.
  • _D(isabled) – Bouton inaccessible
  • _P(ressed) – Bouton enfoncé – Lorsqu’il est enfoncé, après avoir relâché le bouton devient sélectionné

Le modèle complet utilise cinq images pour un bouton. Dans le modèle simplifié, il n’y en a que deux.

Les images sont attribuées selon certaines règles : le nom du bouton doit inclure un préfixe de trois lettres (nom de classe) et le nom du bouton. S’il existe une image portant le même nom, le tableau d’images est lié à ce bouton et les index correspondants sont attribués. L’affectation passe par tous les formulaires, mais vous pouvez définir une liste blanche et noire de formulaires. Ainsi, l’universalité maximale du code est atteinte.

procedure Images_ButtonsAssign( AIndex: integer; AWhiteList: string; ABlackList:string );
// assignation d'images aux boutons sur les formulaires
// AIndex - format d'image
// AWhiteList - liste blanche, ne modifie que les formulaires spécifiés
// ABlackList - liste noire, exclure les formulaires
var
  tmpForm: TAForm;
  tmpButton: TdbButton;
  tmpName: string;
  i: integer;
  j: integer;
  tmpByName: boolean;
  tmpFormList: string;
begin
  if AWhiteList <> '' then
  begin
    tmpByName := True;
    tmpFormList := ','+AWhiteList+',';
  end
  else
  begin
    tmpByName := False;
    tmpFormList := ','+ABlackList+',';
  end;
  for i := 0 to Screen.FormCount - 1 do
  begin
    tmpForm := TAForm(Screen.Forms[i]);
    if ( tmpByName and (pos( tmpForm.name, tmpFormList )>0) ) or ( not tmpByName and (pos( tmpForm.name,
tmpFormList )=0) ) then
      for j:=0 to tmpForm.ComponentCount - 1 do
      begin
        if tmpForm.Components[j] is TdbButton then
        begin
          tmpButton := TdbButton(tmpForm.Components[j]);
          tmpName := DeleteClassName(tmpButton.Name);
          Images_Set( tmpButton, tmpName, AIndex, IMAGES_SIMPLE_MODEL );
        end;
      end;
  end;
end;Langage du code : Delphi (delphi)

La dernière méthode consiste à attribuer une image par son nom. Cela peut être nécessaire non seulement lors de l’initialisation des boutons, mais également lors du fonctionnement de l’application afin de changer rapidement les images sur le bouton.

procedure Images_Set( AButton:TdbButton; AImageName:string; AIndexOfSize:integer; AModel: integer );
// attribue un index d'image par nom de fichier
// AButton:TdbButton ; - bouton
// AImageName:string; - nom de l'image
// AIndexOfSize:integer ; - indice de taille
// AModèle : entier - modèle
var
  tmpImageIndex: integer;
  tmpImageSelectedIndex: integer;
  tmpImageHotIndex: integer;
  tmpImageDisableIndex: integer;
  tmpImagePressedIndex: integer;
begin
  AImageName := UpperCase(AImageName);
  if AImageName <> '' then
  begin // image principale
    tmpImageIndex := ImagesNames[AIndexOfSize].IndexOf(AImageName);
    tmpImageSelectedIndex := ImagesNames[AIndexOfSize].IndexOf(AImageName+IMAGES_SELECTED_SUFFIX);
    tmpImageHotIndex := ImagesNames[AIndexOfSize].IndexOf(AImageName+IMAGES_HOT_SUFFIX);
    tmpImageDisableIndex := ImagesNames[AIndexOfSize].IndexOf(AImageName+IMAGES_DISABLE_SUFFIX);
    tmpImagePressedIndex := ImagesNames[AIndexOfSize].IndexOf(AImageName+IMAGES_PRESSED_SUFFIX);
    if tmpImageIndex >= 0 then
    begin
      AButton.Images := ImagesList[AIndexOfSize];
      AButton.ImageIndex := tmpImageIndex;
      case AModel of
      IMAGES_SIMPLE_MODEL: begin
        AButton.SelectedImageIndex := tmpImageSelectedIndex;
        AButton.HotImageIndex := tmpImageSelectedIndex;
        AButton.DisabledImageIndex := tmpImageIndex;
        AButton.PressedImageIndex := tmpImageSelectedIndex;
      end;
      IMAGES_FULL_MODEL: begin
        AButton.SelectedImageIndex := tmpImageSelectedIndex;
        AButton.HotImageIndex := tmpImageHotIndex;
        AButton.DisabledImageIndex := tmpImageDisableIndex;
        AButton.PressedImageIndex := tmpImagePressedIndex;
      end;
      end;
    end;
  end;
end;Langage du code : Delphi (delphi)

En conséquence, l’application a des images élégantes sur les boutons:

Mode de présentation

Une fois les images souhaitées trouvées, elles peuvent être visualisées dans la zone d’aperçu en changeant d’image avec la souris.

Mais il est beaucoup plus pratique d’utiliser un formulaire séparé pour cela, qui ne contiendra que l’image
visualisée, le titre, la description et les boutons de contrôle de visualisation.

Et le plus important – il y aura un bouton pour la lecture continue, lorsqu’il est pressé, le programme affichera automatiquement l’image suivante de la liste.

Pour implémenter le mode de présentation, nous avons besoin d’une minuterie qui sera créée lors du premier clic sur le bouton de lecture :

procedure frmShow_btnPlay_OnClick (Sender: TObject; var Cancel: boolean);
// en appuyant sur le bouton de lecture Play
begin
  if ShowTimer = nil then
  begin
    ShowTimer := TTimer.Create(frmMain); // premier démarrage
    ShowTimer.Interval := 3000; // intervalle pour changer l'image
    ShowTimer.OnTimer := @frmShow_ShowTimer_OnTimer;
    ShowTimer.Enabled := False;
  end;
  if ShowTimer.Enabled then
  begin
    ShowTimer.Enabled := False;
    Images_Set(frmShow.btnPlay, 'play', IMAGES_FORMAT_BIGBUTTON, IMAGES_SIMPLE_MODEL);
  end
  else
  begin
    ShowTimer.Enabled := True;
    Images_Set(frmShow.btnPlay, 'pause', IMAGES_FORMAT_BIGBUTTON, IMAGES_SIMPLE_MODEL);
  end;
end;
Langage du code : Delphi (delphi)

C’est là que la procédure Images_Set() est utile, à l’aide de laquelle l’apparence du bouton de lecture changera lorsqu’il sera enfoncé.

Pour naviguer dans les images, nous avons besoin d’un tableau invisible. Cela ne fonctionnera pas d’utiliser TDataSet pour cela, car il est unidirectionnel et vous ne pouvez vous déplacer que dans les enregistrements du début à la fin.

Mais vous pouvez utiliser la même requête que lors de la création d’une galerie. Pour la navigation, la propriété SelectedRow est utilisée:

procedure frmShow_btnNext_OnClick (Sender: TObject; var Cancel: boolean);
// en appuyant sur le bouton Suivant
begin
  if frmShow.tgrImageList.SelectedRow < (frmShow.tgrImageList.RowCount - 1) then
    frmShow.tgrImageList.SelectedRow := frmShow.tgrImageList.SelectedRow + 1;
  frmShow_LoadData;
end;
Langage du code : Delphi (delphi)

Et pour charger une image, on utilise la méthode de la classe virtuelle ImageEdit:

procedure frmShow_LoadData; // charge les données
var
  tmpRow: integer;
begin
  tmpRow := frmShow.tgrImageList.SelectedRow;
  if tmpRow <> -1 then
  begin
    frmShow.labName.Caption := frmShow.tgrImageList.cells[1,tmpRow];
    frmShow.labDescription.Caption := frmShow.tgrImageList.cells[2,tmpRow];
    ImageEdit_LoadImageFromBase( frmShow.imgPreview, StrToInt(frmShow.tgrImageList.cells[0,tmpRow]),
IE_FT_ORIGINAL, IE_IT_IMAGE);
  end;
end;
Langage du code : Delphi (delphi)

Essai

À la suite des tests, un grave problème a été révélé – une faible productivité. Lors du chargement d’une grande image à partir du disque, il y a des retards de plusieurs secondes (!). Et cela met une grosse croix grasse de la part du programme qui se charge de créer les albums et les présentations : le temps de chargement de la page de l’album sera inacceptable. Et il n’y a rien à dire sur les effets : un simple changement de la propriété Visible d’un composant avec une image haute résolution chargée peut prendre une seconde ou plus. Les astuces avec la double mise en mémoire tampon de l’affichage du formulaire n’ont pas aidé – lors du changement de la visibilité du composant, l’image “clignote”. Il s’ensuit que.

Les composants utilisés dans My Visual Database ne sont pas conçus pour fonctionner efficacement avec du contenu graphique et ne peuvent être utilisés que comme éléments d'interface auxiliaires avec un certain nombre de restrictions.

Changement de plan

Bien sûr, il est embarrassant d’admettre que les plans pour un PowerPoint alternatif ont été contrecarrés, mais de tels drames se produisent périodiquement dans le monde de la programmation. Malgré le fait qu’il n’a pas été possible d’atteindre le résultat escompté, le programme dispose de plusieurs solutions universelles pouvant être utilisées dans d’autres applications : classes virtuelles ImageEdit et Images, recherche de données par balises.

Le projet restera à jamais dans la catégorie éducative. Pour terminer le travail, il est nécessaire d’en supprimer tous les tableaux et formulaires inutilisés.

Et je laisse la mise en place de l’affichage plein écran et la définition des paramètres de présentation aux
lecteurs de ce livre 😉

Version finale

La structure des données a été grandement simplifiée :

Après avoir supprimé le formulaire d’échec pour afficher la liste des albums, la liste des formulaires a
également diminué.

Le fichier Script.pas a également pris une forme concise :

uses
  'ConstVar.pas',
  'Forms.pas',
  'system\Images.pas',
  'utils.pas';
begin
end.
Langage du code : Delphi (delphi)

Et tous les scripts sont idéalement situés dans des fichiers séparés, pour l’édition que j’utilise l’éditeur universel du programmeur Notepad ++.

J’ai fait de l’affichage de la galerie d’images le formulaire principal :

Liens

Traduction : Yann Yvnec

Laisser un commentaire

Votre adresse e-mail ne sera pas publiée. Les champs obligatoires sont indiqués avec *