Fermer

mars 10, 2020

Créer une application de recherche GIF de bureau natif à l'aide de NodeGui –


NodeGui est une bibliothèque open source pour la création d'applications de bureau natives multiplateformes avec Node.js. Les applications NodeGui peuvent s'exécuter sur macOS, Windows et Linux. Les applications construites avec NodeGui sont écrites à l'aide de JavaScript, stylisées en CSS et rendues en tant que widgets de bureau natifs en utilisant le framework Qt .

Certaines des fonctionnalités de NodeGui sont:

  • widgets natifs avec intégré prise en charge du mode sombre
  • style et empreinte mémoire faibles
  • style CSS avec prise en charge complète de la mise en page Flexbox
  • prise en charge complète de l'API Node.js et accès à tous les modules npm compatibles Node.js
  • excellente prise en charge du débogage en utilisant DevTools de Chrome
  • prise en charge TypeScript de première classe

NodeGui est alimenté par le cadre Qt, ce qui le rend efficace en termes de CPU et de mémoire par rapport à d'autres solutions basées sur Chromium telles qu'Electron. Cela signifie que les applications écrites à l'aide de NodeGui n'ouvrent pas une instance de navigateur et n'y rendent pas l'interface utilisateur. Au lieu de cela, tous les widgets sont rendus en mode natif.

Ce didacticiel montrera comment installer NodeGui et l'utiliser pour créer un moteur de recherche de mèmes qui réside dans la barre d'état système et communique avec l'API GIPHY .

Le code source complet de ce didacticiel est disponible sur GitHub .

Installation et configuration de base

Pour ce didacticiel, il est supposé que vous avez Node.js v12 ou une version ultérieure installée. Vous pouvez confirmer que Node et npm sont disponibles en exécutant:

 # Cette commande doit imprimer la version de Node.js
noeud -v

# Cette commande devrait imprimer la version de npm
npm -v

Si vous avez besoin d'aide pour cette étape, consultez notre didacticiel sur l'installation de Node .

NodeGui nécessite des outils de compilation CMake et C ++ pour créer la couche C ++ native du projet. Assurez-vous d'installer CMake> = 3.1 avec un compilateur C ++ qui prend en charge C ++ 11 et versions ultérieures. Les instructions détaillées varient un peu selon votre système d'exploitation.

macOS

Il est recommandé d'installer CMake à l'aide de Homebrew. Exécutez les commandes suivantes dans un terminal après avoir installé Homebrew:

 brew install cmake
brasser installer faire

Vous pouvez confirmer l'installation en exécutant:

 # Cette commande doit imprimer la version de CMake qui doit être supérieure à 3.1
cmake --version

faire --version

Enfin, vous avez besoin de GCC / Clang pour compiler du code C ++. Vérifiez que GCC est installé à l'aide de cette commande:

 gcc --version

Si vous n'avez pas installé GCC, assurez-vous d'installer Outils de ligne de commande pour Xcode ou Outils de développement XCode à partir de Page de développeur d'Apple . [196590255] ] Windows

Vous pouvez installer CMake sur Windows en téléchargeant la dernière version de la page de téléchargement de CMake .

Il est fortement recommandé d'utiliser Powershell comme terminal préféré dans Windows.

Vous pouvez confirmer l'installation de CMake en exécutant:

 # Cette commande devrait imprimer la version de CMake qui devrait être supérieure à 3.1
cmake --version

Enfin, vous avez besoin d'un compilateur C ++. Une possibilité serait d'installer Visual Studio 2017 ou une version supérieure . Il est recommandé de choisir la charge de travail Développement de bureau avec C ++ pendant le processus d'installation.

Linux

Nous allons nous concentrer sur Ubuntu 18.04 pour les besoins de ce didacticiel. Il est recommandé d'installer CMake à l'aide du gestionnaire de packages. Exécutez les commandes suivantes dans un terminal:

 sudo apt-get install pkg-config build-essential
sudo apt-get install cmake make

Vous pouvez confirmer l'installation en exécutant:

 # Cette commande doit imprimer la version de CMake qui doit être supérieure à 3.1
cmake --version

faire --version

Enfin, vous avez besoin de GCC pour compiler du code C ++. Vérifiez que GCC est installé à l'aide de la commande:

 # la version gcc doit être> = v7
gcc --version

Hello World

Afin de commencer avec notre application meme NodeGui, nous allons cloner le projet de démarrage.

Remarque: L'exécution de cette opération nécessite Git et npm.

Ouvrez un terminal et exécuter:

 clone git https://github.com/nodegui/nodegui-starter memeapp
cd memeapp
installation de npm
npm start

Si tout se passe bien, vous devriez voir une application NodeGui Hello World fonctionnelle à l'écran.

 Exemple Hello World NodeGui

Par défaut, le projet nodegui-starter est un projet TypeScript. Cependant, dans ce didacticiel, nous allons écrire notre application en JavaScript. Pour convertir notre démarreur en projet JS, nous apporterons les modifications mineures suivantes:

  1. Supprimez le fichier index.ts dans le dossier src .

  2. Créez un nouveau fichier index.js dans le répertoire src avec le contenu suivant:

    src / index.js

     const {QMainWindow, QLabel } = require ('@ nodegui / nodegui');
    
    const win = new QMainWindow ();
    win.setWindowTitle ('Recherche Meme');
    
    const label = new QLabel ();
    label.setText ('Bonjour tout le monde');
    
    win.setCentralWidget (étiquette);
    win.show ();
    
    global.win = gagner;
    

En ce qui concerne le développement, une application NodeGui est essentiellement une application Node.js. Toutes les API et fonctionnalités présentes dans NodeGui sont accessibles via le module @ nodegui / nodegui qui peut être requis comme tout autre module Node.js. De plus, vous avez accès à toutes les API Node.js et modules Node. NodeGui utilise des composants natifs au lieu de composants Web comme blocs de construction.

Dans l'exemple ci-dessus, nous avons importé QMainWindow et QLabel pour créer une fenêtre native qui affiche le texte "Bonjour tout le monde".

Réexécutez maintenant l'application:

 npm start

 Hello World version JavaScript

Maintenant que notre configuration de base est prête, commençons à construire notre moteur de recherche de mèmes ?.

Remarque: Si quelque chose ne fonctionne pas en suivant ceci tutoriel, vérifiez votre fichier package.json pour vous assurer que le projet de démarrage a extrait la version la plus récente de NodeGui.

Affichage d'un GIF animé

Les mèmes étant généralement des GIF animés , nous allons commencer par créer une fenêtre de base qui affiche une image GIF à partir d'une URL.

Pour ce faire, nous utiliserons QMovie avec QLabel. QMovie n'est pas un widget mais un conteneur qui peut jouer des animations simples. Nous l’utiliserons en combinaison avec QLabel.

Un exemple d’utilisation de QMovie ressemble à ceci:

 const movie = new QMovie ();
movie.setFileName ('/ absolu / chemin / vers / animated.gif');
movie.start ();

const animatedLabel = new QLabel ();
animatedLabel.setMovie (film);

Étant donné que nous souhaitons charger une image à partir d'une URL, nous ne pouvons pas utiliser la méthode setFileName de QMovie qui est réservée uniquement aux fichiers locaux. Au lieu de cela, nous allons télécharger l'image GIF en utilisant axios comme tampon et utiliser à la place la méthode QMovie loadFromData .

Commençons donc par l'installation d'axios:

 npm i axios

Créons maintenant une fonction qui prendra une URL comme paramètre et renverra une instance de QMovie configurée pour le GIF:

 fonction async getMovie (url) {
  const {data} = attendre axios.get (url, {responseType: 'arraybuffer'});
  const movie = new QMovie ();
  movie.loadFromData (données);
  movie.start ();
  film de retour;
}

La fonction getMovie prend une URL, indique à axios de télécharger le GIF en tant que tampon, puis utilise ce tampon pour créer une instance QMovie .

Vous pouvez penser de QMovie en tant que classe qui gère la logique interne de lecture de l'animation GIF image par image. QMovie n'est pas un widget, il ne peut donc pas être affiché à l'écran tel quel. Au lieu de cela, nous utiliserons une instance régulière de QLabel et lui définirons QMovie .

Puisque getMovie renvoie une promesse, nous devons apporter quelques modifications à le code. Après quelques modifications mineures, nous nous retrouvons avec ce qui suit:

src / index.js

 const {QMainWindow, QMovie, QLabel} = require ('@ nodegui / nodegui');
const axios = require ('axios'). default;

fonction asynchrone getMovie (url) {
  const {data} = attendre axios.get (url, {responseType: 'arraybuffer'});
  const movie = new QMovie ();
  movie.loadFromData (données);
  movie.start ();
  film de retour;
}

const main = async () => {
  const win = new QMainWindow ();
  win.setWindowTitle ('Recherche Meme');

  const label = new QLabel ();
  const gifMovie = attendre getMovie (
    «https://upload.wikimedia.org/wikipedia/commons/e/e3/Animhorse.gif»
  );
  label.setMovie (gifMovie);

  win.setCentralWidget (étiquette);
  win.show ();
  global.win = gagner;
};

main (). catch (console.error);

La fonction principale est notre point d'entrée. Ici, nous créons une fenêtre et une étiquette. Nous instancions ensuite une instance de QMovie à l'aide de notre fonction getMovie puis définissons le QMovie sur un QLabel .

Exécutez l'application avec npm start et vous devriez voir quelque chose comme ceci:

 Exemple d'animation de base montrant un cheval au galop

Récupération de GIF à partir de l'API GIPHY

Giphy.com a un API publique que tout le monde peut utiliser pour créer de superbes applications qui utilisent des GIF animés. Pour utiliser l'API GIPHY, vous devez vous enregistrer sur developers.giphy.com et obtenir une clé API. Vous pouvez trouver des instructions supplémentaires ici .

Nous utiliserons la fonction de point final de recherche pour implémenter notre recherche de mèmes.

Commençons par écrire un searchGifs fonction qui prendra un paramètre searchTerms comme entrée et demande des GIF en utilisant le point de terminaison ci-dessus:

 const GIPHY_API_KEY = 'Votre clé API ici';

fonction asynchrone searchGifs (searchTerm) {
  const url = 'https://api.giphy.com/v1/gifs/search';
  const res = attendre axios.get (url, {
    params: {
      api_key: GIPHY_API_KEY,
      limite: 25,
      q: searchTerm,
      lang: 'en',
      décalage: 0,
      évaluation: 'pg-13'
    }
  });
  return res.data.data;
}

Le résultat de la fonction après exécution ressemblera à ceci:

 [
  {
    "type": "gif",
    "id": "dzaUX7CAG0Ihi",
    "url": "https://giphy.com/gifs/hello-hi-dzaUX7CAG0Ihi",
    "images": {
      "fixed_width_small": {
        "height": "54",
        "size": "53544",
        "url": "https://media3.giphy.com/media/dzaUX7CAG0Ihi/100w.gif?cid=725ec7e0c00032f700929ce9f09f3f5fe5356af8c874ab12&rid=100w.gif",
        "width": "100"
      },
      "downsized_large": {
        "height": "220",
        "size": "807719",
        "url": "https://media3.giphy.com/media/dzaUX7CAG0Ihi/giphy.gif?cid=725ec7e0c00032f700929ce9f09f3f5fe5356af8c874ab12&rid=giphy.gif",
        "width": "410"
      },
      ...
    },
    "slug": "hello-hi-dzaUX7CAG0Ihi",
    ...
    "import_datetime": "2016-01-07 15:40:35",
    "trending_datetime": "1970-01-01 00:00:00"
  },
  {
    type: "gif",
    ...
  },
  ...
]

Le résultat est essentiellement un tableau d'objets qui contiennent des informations sur chaque GIF. Nous sommes particulièrement intéressés par returnValue [i] .images.fixed_width_small.url pour chaque image, qui contient l'URL du GIF.

Affichage d'une liste de GIF à l'aide de la réponse de l'API

Dans afin d'afficher une liste de GIF, nous allons créer une fonction getGifViews qui:

  1. créera un conteneur QWidget
  2. créera un widget QMovie pour chaque GIF
  3. créez un QLabel à partir de chaque QMovie instance
  4. attachez chaque QLabel en tant qu'enfant du conteneur QWidget
  5. renvoie le conteneur QWidget

Le code ressemble à ceci:

 fonction asynchrone getGifViews (listOfGifs) {
  const container = new QWidget ();
  container.setLayout (new FlexLayout ());

  const promises = listOfGifs.map (async gif => {
    const {url, width} = gif.images.fixed_width_small;
    const movie = attendre getMovie (url);
    const gifView = new QLabel ();
    gifView.setMovie (film);
    gifView.setInlineStyle (`largeur: $ {largeur}`);
    container.layout.addWidget (gifView);
  });

  attendre Promise.all (promesses);

  container.setInlineStyle (`
      flex-direction: 'rangée';
      flex-wrap: «wrap»;
      justifier-contenu: «espace autour»;
      largeur: 330 px;
      hauteur: 300px;
  ');

  conteneur de retour;
}

Décomposons cela un peu.

Tout d'abord, nous créons notre widget conteneur. Les QWidget sont essentiellement des widgets vides qui agissent comme des conteneurs. Ils sont similaires aux éléments

du monde Web.

Ensuite, pour attribuer des widgets enfants au QWidget nous devons lui donner une disposition. Une disposition dicte comment les widgets enfants doivent être disposés à l'intérieur d'un parent. Ici, nous choisissons FlexLayout .

Ensuite, nous utilisons notre fonction getMovie pour créer une instance QMovie pour chaque URL GIF. Nous attribuons l'instance QMovie à une QLabel (nommée gifView ) et lui donnons un style de base à l'aide de la méthode setInlineStyle . Enfin, nous ajoutons le widget QLabel à la disposition du conteneur à l'aide de la méthode layout.addWidget .

Comme tout se passe de manière asynchrone, nous attendons que tout se résout à l'aide de Promise.all avant de définir certains styles de conteneur et de renvoyer le widget conteneur.

Modifions maintenant notre fonction principale afin de voir la liste des widgets que nous avons préparés.

src / index.js

 const {FlexLayout, QLabel, QMainWindow, QMovie, QWidget} = require ('@ nodegui / nodegui');
const axios = require ('axios'). default;
const GIPHY_API_KEY = 'Votre clé API ici';

fonction asynchrone getMovie (url) {...}
fonction asynchrone searchGifs (searchTerm) {...}
fonction asynchrone getGifViews (listOfGifs) {...}

const main = async () => {
  const win = new QMainWindow ();
  win.setWindowTitle ('Recherche Meme');

  const center = new QWidget ();
  center.setLayout (nouveau FlexLayout ());

  // Nous obtenons la liste des gifs ici
  const listOfGifs = attendre searchGifs ('bonjour');

  // Nous créons le conteneur avec des widgets de vue GIF
  const container = wait getGifViews (listOfGifs);

  // On attache enfin le conteneur au widget
  center.layout.addWidget (conteneur);

  win.setCentralWidget (centre);
  win.show ();

  global.win = gagner;
};

main (). catch (console.error);

Si vous exécutez le projet après avoir apporté ces modifications, vous devriez voir:

 Liste des GIF "bonjour" extraits de l'API GIPHY

Génial! Maintenant, ajoutons un champ de saisie de recherche avec un bouton, afin que les utilisateurs puissent rechercher autre chose que des "bonjour" GIF.

Ajout d'une entrée de recherche et d'un bouton

Commençons par créer un createSearchContainer qui acceptera une fonction de rappel comme paramètre. Ceci sera appelé lorsque le bouton de recherche sera cliqué.

Voici ce que la fonction devrait faire:

  1. créer un QWidget conteneur, auquel nous ajouterons un champ de saisie de recherche et un bouton en tant qu'enfants [19659004] créer une disposition et l'attacher au conteneur
  2. créer une entrée et un bouton de recherche, puis les attacher au FlexLayout
  3. attacher un écouteur d'événements au bouton qui, lorsque vous cliquez dessus, appellera la fonction de rappel onSearch en lui passant le texte présent dans le champ de saisie de texte
  4. renvoie le conteneur QWidget

Le code ressemble à ceci:

 fonction createSearchContainer (onSearch ) {
  const searchContainer = new QWidget ();
  searchContainer.setObjectName ('searchContainer');
  searchContainer.setLayout (nouveau FlexLayout ());

  const searchInput = new QLineEdit ();
  searchInput.setObjectName ('searchInput');

  const searchButton = new QPushButton ();
  searchButton.setObjectName ('searchButton');
  searchButton.setText ('?');

  searchButton.addEventListener ('cliqué', () => {
    onSearch (searchInput.text ());
  });

  searchContainer.layout.addWidget (searchInput);
  searchContainer.layout.addWidget (searchButton);

  searchContainer.setStyleSheet (`
    #searchContainer {
      flex-direction: 'rangée';
      rembourrage: 10px;
      align-items: 'center';
    }
    #searchInput {
      flex: 1;
      hauteur: 40px;
    }
    #searchButton {
      marge gauche: 5px;
      largeur: 50 px;
      hauteur: 35px;
    }
  ');
  return searchContainer;
}

J'espère que vous avez une bonne idée de ce qui se passe ici, mais une nouvelle chose à noter est la méthode setStyleSheet . Vous pouvez penser à cela comme un moyen d'appliquer du CSS de niveau bloc en une seule fois. C'est très similaire aux feuilles de style globales sur le Web, mais avec la différence que dans NodeGui / Qt une feuille de style peut être attachée à n'importe quel bloc et pas seulement globalement.

Afin de styliser un widget en utilisant une feuille de style, nous devons ajouter un objectName à un widget, que nous utiliserons pour le référencer dans la feuille de style. Ceci est à peu près identique à un id dans le monde du Web. Pour définir un objectName nous utiliserons la méthode setObjectName .

Ajoutons maintenant searchContainer à la fenêtre principale.

src / index.js

 const {
  FlexLayout,
  QLabel,
  QLineEdit,
  QMainWindow,
  QMovie,
  QPushButton,
  QWidget,
} = require ('@ nodegui / nodegui');

const axios = require ('axios'). default;
const GIPHY_API_KEY = 'Votre clé API ici';

fonction asynchrone getMovie (url) {...}
fonction asynchrone searchGifs (searchTerm) {...}
fonction asynchrone getGifViews (listOfGifs) {...}
fonction createSearchContainer (onSearch) {...}

const main = async () => {
  const win = new QMainWindow ();
  win.setWindowTitle ('Recherche Meme');

  const center = new QWidget ();
  center.setLayout (nouveau FlexLayout ());

  // Ici, nous créons le conteneur de recherche
  const searchContainer = createSearchContainer (searchText => {
    console.log ('searchText:', searchText);
  });

  // Ici, nous l'ajoutons au widget central avant d'ajouter la liste des GIF.
  center.layout.addWidget (searchContainer);

  const listOfGifs = attendre searchGifs ('bonjour');
  const container = wait getGifViews (listOfGifs);

  center.layout.addWidget (conteneur);

  win.setCentralWidget (centre);
  win.show ();

  global.win = gagner;
};

main (). catch (console.error);

Maintenant, lorsque vous lancez l'application et entrez quelque chose dans le champ de recherche, vous devriez voir tout ce que vous avez recherché connecté à votre terminal.

 Liste des GIF avec entrée de recherche

Connexion de la recherche à la Vue GIF

Pour charger de nouveaux GIF en réponse à la recherche d'un utilisateur, nous devons procéder comme suit:

  1. Dans le rappel qui est déclenché lorsque le bouton de recherche est cliqué, saisissez le texte de la recherche et utilisez searchGifs pour obtenir une nouvelle liste de GIF.
  2. Créez un nouveau conteneur pour ces GIF à l'aide de la fonction getGifViews .
  3. Supprimez le conteneur existant de la fenêtre.
  4. Ajoutez le nouveau

Si nous mélangeons un peu les choses, nous obtenons:

 const main = async () => {
  const win = new QMainWindow ();
  win.setWindowTitle ('Recherche Meme');

  const center = new QWidget ();
  center.setLayout (nouveau FlexLayout ());

  laissez container = new QWidget ();
  const searchContainer = createSearchContainer (async searchText => {
    essayez {
      // Créer un nouveau conteneur GIF avec de nouveaux GIF
      const listOfGifs = attendre searchGifs (searchText);
      const newGifContainer = attendre getGifViews (listOfGifs);

      // Supprimer le conteneur existant de la fenêtre
      center.layout.removeWidget (conteneur);
      container.close ();

      // Ajout du nouveau conteneur GIF à la fenêtre
      center.layout.addWidget (newGifContainer);
      container = newGifContainer;
    } catch (err) {
      console.error ('Quelque chose s'est passé!', err);
    }
  });
  center.layout.addWidget (searchContainer);

  win.setCentralWidget (centre);
  win.show ();

  global.win = gagner;
};

Exécutons-le à nouveau et voyons la magie ?‍♂️.

 Widget de recherche GIF connecté

Comme vous pouvez le voir, lorsque vous tapez quelque chose dans la zone de recherche et appuyez sur le bouton de recherche, notre widget récupérera une liste de GIF correspondant au terme de recherche à partir de l'API GIPHY.

Alors que tout va dans la bonne direction, vous avez probablement remarqué que la liste des GIF est coupée en bas et il n'y a aucun moyen de faites-les défiler. En effet, nous utilisons un conteneur QWidget pour les afficher. Pour faire défiler le conteneur, nous devons remplacer le QWidget par un QScrollArea . Cela donne une vue défilante sur un autre widget.

Nous allons commencer par supprimer la propriété height dans la fonction getGifViews :

 fonction async getGifViews (listOfGifs) {
  ...

  container.setInlineStyle (`
      flex-direction: 'rangée';
      flex-wrap: «wrap»;
      justifier-contenu: «espace autour»;
      largeur: 330 px;
- hauteur: 300px;
  ');

  conteneur de retour;
}

Ensuite, nous devons changer src / index.js pour qu'il ressemble à ceci:

 const {
  FlexLayout,
  QLabel,
  QLineEdit,
  QMainWindow,
  QMovie,
  QPushButton,
  QScrollArea,
  QWidget,
} = require ('@ nodegui / nodegui');

const axios = require ('axios'). default;
const GIPHY_API_KEY = 'Votre clé API ici';

fonction asynchrone getMovie (url) {...}
fonction asynchrone searchGifs (searchTerm) {...}
fonction asynchrone getGifViews (listOfGifs) {...}
fonction createSearchContainer (onSearch) {...}

const main = async () => {
  const win = new QMainWindow ();
  win.setWindowTitle ('Recherche Meme');

  const center = new QWidget ();
  center.setLayout (nouveau FlexLayout ());

  const scrollArea = new QScrollArea ();
  scrollArea.setWidgetResizable (false);
  scrollArea.setInlineStyle ('flex: 1; largeur: 350px; hauteur: 400px;');

  const searchContainer = createSearchContainer (async searchText => {
    essayez {
      const listOfGifs = attendre searchGifs (searchText);
      const newGifContainer = attendre getGifViews (listOfGifs);

      // Supprimer le conteneur existant de scrollArea
      const oldContainer = scrollArea.takeWidget ();
      if (oldContainer) oldContainer.close ();

      // Ajout du nouveau conteneur GIF à scrollArea
      scrollArea.setWidget (newGifContainer);
    } catch (err) {
      console.error ('Quelque chose s'est passé!', err);
    }
  });

  center.layout.addWidget (searchContainer);
  center.layout.addWidget (scrollArea);

  win.setCentralWidget (centre);
  win.show ();

  global.win = gagner;
};

main (). catch (console.error);

Il n'y a rien de trop excitant ici. Nous créons un nouveau QScrollArea que nous ajoutons à la mise en page sous le champ de recherche. Nous utilisons également la méthode takeWidget de QScrollArea pour supprimer tout conteneur existant de la zone de défilement, avant d'ajouter les nouveaux résultats de recherche.

Si vous lancez le moteur de recherche de mèmes, vous devriez maintenant avoir des GIF défilants:

 Recherche défilante

Ajouter des écouteurs Cliquez pour copier les URL GIF pour le partage

Maintenant que nous pouvons voir tous les GIF, nous voulons pouvoir les partager. Une façon rapide de le faire est de copier l'URL dans le presse-papiers global chaque fois qu'un utilisateur clique sur le GIF de son choix.

Ensuite, l'utilisateur peut simplement naviguer jusqu'à l'endroit où il souhaite utiliser le GIF et l'insérer avec Ctrl / Cmd + V .

Pour ce faire, nous devons:

  1. attacher un écouteur d'événements souris vers le bas à chaque GIF
  2. à l'intérieur du rappel de l'écouteur d'événements, utilisez la classe QClipboard pour copier l'URL dans le presse-papiers global
  3. affiche un modal à l'utilisateur disant que l'URL a été copiée

L'écouteur d'événements peut être attaché à l'intérieur la fonction getGifViews :

 fonction asynchrone getGifViews (listOfGifs) {
  ...

  const promises = listOfGifs.map (async gif => {
    ...

    gifView.addEventListener (WidgetEventTypes.MouseButtonRelease, () => {
      const clipboard = QApplication.clipboard ();
      clipboard.setText (url, QClipboardMode.Clipboard);

      showModal (
        'Copié dans le presse-papier!',
        `Vous pouvez appuyer sur Cmd / Ctrl + V pour coller l'URL GIF: $ {url}`
      );

    });

    container.layout.addWidget (gifView);
  });

  ...

  conteneur de retour;
}

Ici, QApplication.clipboard renvoie un objet pour interagir avec le presse-papiers. Nous pouvons utiliser la méthode setText de cet objet pour modifier le contenu réel du presse-papiers.

Nous utilisons également une fonction showModal . Définissons cela ensuite:

 fonction showModal (titre, détails) {
  const modal = new QMessageBox ();
  modal.setText (titre);
  modal.setDetailedText (détails);
  const okButton = new QPushButton ();
  okButton.setText ('OK');
  modal.addButton (okButton, ButtonRole.AcceptRole);
  modal.exec ();
}

Le widget QMessageBox est similaire à une boîte d'alerte dans un navigateur Web. Il peut être utilisé pour arrêter l'interaction de l'utilisateur et afficher un message.

Enfin, nous devons importer tous ces nouveaux widgets en haut de src / index.js :

 const {
  ButtonRole,
  FlexLayout,
  QApplication,
  QClipboardMode,
  QLabel,
  QLineEdit,
  QMainWindow,
  QMessageBox,
  QMovie,
  QPushButton,
  QScrollArea,
  QWidget,
  WidgetEventTypes,
} = require ('@ nodegui / nodegui');
const axios = require ('axios'). default;
const GIPHY_API_KEY = 'Votre clé API ici';

fonction asynchrone searchGifs (searchTerm) {...};
fonction asynchrone getGifViews (listOfGifs) {...};
fonction asynchrone getMovie (url) {...};
function createSearchContainer (onSearch) {...};
fonction showModal (titre, détails) {...};

const main = async () => {...};

main (). catch (console.error);

Si vous lancez le moteur de recherche de meme, vous devriez maintenant avoir la possibilité de copier / coller des URL GIF:

 Copier l'URL GIF dans le presse-papiers GIF

Ajouter une icône de barre d'état système

Nous voulons notre application à cacher dans la barre d'état système lorsqu'elle n'est pas utilisée. Pour cela, nous allons créer une icône de barre d'état système qui aura un élément de menu qui, au clic, basculera la visibilité du widget en cours d'exécution.

Les étapes impliquées sont:

  1. Créez un QSystemTrayIcon avec une icône.
  2. Créez un menu pour l'icône de la barre d'état système à l'aide de QMenu . Définissez l'instance de menu comme menu contextuel de la barre d'état système.
  3. Créez des éléments de menu à l'aide de Widgets QAction et configurez des écouteurs d'événements pour écouter leurs événements de déclenchement .
  4. Sur déclenchement, masquez ou afficher la fenêtre.

Commençons par exiger les modules nécessaires, puis modifions légèrement la fonction principale pour lui dire d'utiliser notre icône:

 const {
  ButtonRole,
  FlexLayout,
  QApplication,
  QClipboardMode,
  QIcon,
  QLabel,
  QLineEdit,
  QMainWindow,
  QMenu,
  QMessageBox,
  QMovie,
  QAction,
  QPushButton,
  QScrollArea,
  QSystemTrayIcon,
  QWidget,
  WidgetEventTypes,
} = require ('@ nodegui / nodegui');
const axios = require ('axios'). default;
const path = require ('path');
const iconImg = require ('../ assets / systray.png'). default;
const GIPHY_API_KEY = 'Votre clé API ici';

const main = async () => {
  ...

  win.show ();
  systemTrayIcon (win);

  global.win = gagner;
};

Comme vous pouvez le voir, nous avons besoin d'une icône dans le dossier actifs . Si vous suivez la suite, vous pouvez télécharger le fichier icône à partir d'ici .

Vient maintenant la fonction pour créer l'icône de la barre d'état système:

 function systemTrayIcon (win) {
  const icon = new QIcon (path.resolve (__ dirname, iconImg));
  const tray = new QSystemTrayIcon ();
  tray.setIcon (icône);
  tray.show ();

  // Menu qui devrait apparaître lorsque vous cliquez sur l'icône de la barre d'état système.
  menu const = nouveau QMenu ();
  tray.setContextMenu (menu);

  // Chaque élément du menu est appelé une action
  const visibleAction = new QAction ();
  menu.addAction (visibleAction);
  visibleAction.setText ('Afficher / Masquer');
  visibleAction.addEventListener ('triggered', () => {
    if (win.isVisible ()) {
      win.hide ();
    } autre {
      win.show ();
    }
  });

  global.tray = plateau;
}

Ici, nous créons l'icône en utilisant la classe QIcon de NodeGui . Ensuite, nous utilisons la classe QSystemTrayIcon pour créer une icône de barre d'état système pour notre application.

Enfin, nous devons modifier nos paramètres de webpack (dans webpack.config.js ) pour empêcher le webpack de polyfilling __ dirname :

 const path = require ('path');

module.exports = {
  ...
  nœud: {
- __dirname: true,
- __filename: true
+ __dirname: false,
+ __nom de fichier: faux
  },
  ...
}

Le résultat final:

 Le widget de recherche finale

Quelques ajustements finaux

Gestion des erreurs

Avant de passer à l'emballage, utilisons notre showModal et ajoutez une boîte de dialogue de gestion des erreurs:

 const main = async () => {
  ...
  const searchContainer = createSearchContainer (async searchText => {
    essayez {
      ...
    } catch (err) {
      ...
      showModal ('Quelque chose s'est mal passé!', JSON.stringify (err));
    }
  });
  ...
};

Cela alertera l'utilisateur si, par exemple, quelque chose ne va pas avec la demande Ajax pour récupérer les GIF de GIPHY. Vous pouvez essayer cela en modifiant votre clé API en quelque chose de non valide, puis en lançant l'application et en tentant de rechercher un GIF.

Autoriser l'utilisateur à saisir une clé API

Pendant que nous parlons de clés API, ajoutons une boîte de dialogue pour permettre à un utilisateur d'entrer sa clé API. Cela signifie qu'il n'a pas besoin d'être codé en dur dans le programme:

 const {
  ...
  QDialog,
  ...
} = require ('@ nodegui / nodegui');
...
laissez GIPHY_API_KEY = '';

fonction asynchrone searchGifs (searchTerm) {...}
fonction asynchrone getGifViews (listOfGifs) {...}
fonction asynchrone getMovie (url) {...}
fonction createSearchContainer (onSearch) {...}
fonction showModal (titre, détails) {...}
fonction systemTrayIcon (win) {...}

fonction showAPIKeyDialog () {
  const dialog = new QDialog ();
  dialog.setLayout (new FlexLayout ());
  const label = new QLabel ();
  label.setText ('Entrez votre clé API Giphy');
  const input = new QLineEdit ();
  const okButton = new QPushButton ();
  okButton.setText ('OK');
  okButton.addEventListener ('clicked', () => {
    GIPHY_API_KEY = input.text ();
    dialog.close ();
  });
  dialog.layout.addWidget (étiquette);
  dialog.layout.addWidget (entrée);
  dialog.layout.addWidget (okButton);
  dialog.setInlineStyle (`
    rembourrage: 10;
    hauteur: 150px;
    flex-direction: «colonne»;
    align-items: 'center';
    justifier-contenu: «espace autour»;
  ');
  dialog.exec ();
}

const main = async () => {
  ...
  showAPIKeyDialog ();
  global.win = gagner;
};

main (). catch (console.error);

Comme vous pouvez le voir, nous utilisons un widget QDialog pour inviter l'utilisateur à entrer des données, puis stockant tout ce qu'il fournit dans la variable GIPHY_API_KEY . Si vous cherchez à améliorer vos compétences NodeGui après avoir lu ce didacticiel, vous pouvez envisager d'améliorer cela - par exemple, en conservant la clé du système de fichiers, ou en la validant et en fournissant des commentaires à l'utilisateur.

Remarque: N'oubliez pas, le code source complet est disponible ici: https://github.com/sitepoint-editors/memesearchapp-nodegui-tutorial .

Conditionnement de l'application pour la distribution multiplateforme

Après avoir créé l'application avec succès, nous devons créer des distributables pour macOS, Windows et Linux que les utilisateurs finaux peuvent télécharger et utiliser.

Le processus de création de distributables est généralement différent pour chaque système d'exploitation, donc pour soulager la douleur, nous utiliserons l'outil de packaging de NodeGui appelé @ nodegui / packer .

Utilisation

Premièrement, installez packer comme dépendance de dev:

 npm install --save-dev @nodegui /emballeur

Ensuite, utilisez le packer pour créer un modèle de déploiement:

 npx nodegui-packer --init MemeApp

The template is essentially an OS-specific project that contains the code to successfully package all of NodeGui app’s code, assets and dependencies. Note that you need to run this in Windows, macOS and Linux separately to create three different templates. This template allows you to fine tune the final deployment settings specific to each OS. You can adjust things like company info, icons and other metadata to suit your needs.

For Linux, the template looks like this:

.
└── deploy
    ├── config.json
    └── linux
        └── MemeApp
            ├── default.desktop
            ├── default.png
            └── qode.json

Note that you only need to run the init command once. Then you make changes to the template and commit it in the project repo.

The next step is to actually build and package the project into a distributable.

Delete the build directory if it exists:

rm -rf ./deploy/build

Then build the app using webpack:

npm run build

Finally, run the packer’s pack command, passing it the dist folder as an argument:

npx nodegui-packer --pack ./dist

This will result in the following:

  • On macOS, packer will output a dmg file.
  • On Linux, packer will output an AppImagewhich is something similar to an .app file in macOS.
  • On Windows, packer outputs a folder containing the executable and all the dlls.

Once the command is successful, it should print the output directory, which is typically inside the deploy//build directory. Make sure you don’t commit this directory:

.
└── deploy
    ├── config.json
    └── linux
        ├── build
        │   └── MemeApp
        │       ├── Application-aed23d8-x86_64.AppImage
        │       ├── AppRun -> qode
        │       ├── default.desktop
        │       ├── default.png
        │       ├── dist
        │       │   ├── f59514675cec2e70ce8598286c94dc22.png
        │       │   ├── index.js
        │       │   └── nodegui_core-7b3e73f5fef149ae765d5ea5d13d5bb0.node
        │       ├── doc
        │       │   └── ...
        │       ├── lib
        │       │   └── ...
        │       ├── plugins
        │       │   └── ...
        │       ├── qode
        │       ├── qode.json
        │       └── qt.conf
        └── MemeApp
            ├── default.desktop
            ├── default.png
            └── qode.json

The Linux distributable is deploy/linux/build/MemeApp/Application-aed23d8-x86_64.AppImage ??.

Conclusion

In this tutorial, we successfully built a real-world meme search app using NodeGui in roughly 200 lines of code. We learned some of the library’s basic concepts and capabilities. We also were able to package the finished app into a distributable that can be shared with end users.

I believe NodeGui opens the door to create a lot of truly efficient native apps with Node.js.

NodeGui also supports libraries and frameworks such as React (official)Angular (community) and soon Vue.js (community). Please check these out and give them a star on GitHub if they’re the sort of thing you’re interested in.

NodeGui is an open-source library that will greatly benefit from code contributions. It has a relatively easy codebase to understand and a very welcoming community. I encourage everyone to help out.

Finally, thanks to their many inbuilt widgets and styling through CSS, I believe NodeGui apps are as easy to develop as web or Electron apps. I encourage you to build something cool of your own and share it with us.

This tutorial is an excerpt from the SitePoint Premium librarywhere you can build a job-ready Node.js skillset.




Source link