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

Online Coding Courses for Kids

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 en utilisant JavaScript, stylisées avec CSS et rendues en tant que widgets de bureau natifs en utilisant le Cadre Qt.

Certaines des fonctionnalités de NodeGui sont:

  • widgets natifs avec prise en charge intégrée du mode sombre
  • faible encombrement CPU et mémoire
  • style avec CSS, y compris un support complet pour 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 à l’aide des DevTools de Chrome
  • Prise en charge de 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 ne pas ouvrez une instance de navigateur et affichez-y l’interface utilisateur. Au lieu de cela, tous les widgets sont rendus nativement.

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

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

Installation et configuration de base

Pour ce didacticiel, nous supposons que vous avez Node.js v12 ou supérieur installé. Vous pouvez confirmer que Node et npm sont disponibles en exécutant:

# This command should print the version of Node.js
node -v

# This command should print the version of npm
npm -v

Si vous avez besoin d’aide pour cette étape, consultez notre tutoriel 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 sont un peu différentes 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
brew install make

Vous pouvez confirmer l’installation en exécutant:

# This command should print the version of CMake which should be higher than 3.1
cmake --version

make --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 de Page développeur d’Apple.

les fenêtres

Vous pouvez installer CMake sur Windows en téléchargeant la dernière version du 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:

# This command should print the version of CMake which should be higher than 3.1
cmake --version

Enfin, vous avez besoin d’un compilateur C ++. Une possibilité serait de installer Visual Studio 2017 ou supérieur. Il est recommandé de choisir le Développement de bureau avec C ++ charge de travail 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:

# This command should print the version of CMake which should be higher than 3.1
cmake --version

make --version

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

# gcc version should be >= v7
gcc --version

Bonjour le monde

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

Remarque: L’exécution de cela nécessite Git et npm.

Ouvrez un terminal et exécutez:

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

Si tout se passe bien, vous devriez voir une application NodeGui de bonjour à l’écran.

Exemple de 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. Afin de convertir notre démarreur en projet JS, nous apporterons les modifications mineures suivantes:

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

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

    src / index.js

    const { QMainWindow, QLabel } = require('@nodegui/nodegui');
    
    const win = new QMainWindow();
    win.setWindowTitle('Meme Search');
    
    const label = new QLabel();
    label.setText('Hello World');
    
    win.setCentralWidget(label);
    win.show();
    
    global.win = win;
    

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 @nodegui/nodegui module, 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 «Hello World».

Maintenant, exécutez à nouveau l’application:

npm start

Version JavaScript de Hello World

Maintenant que notre configuration de base est prête, commençons à construire notre moteur de recherche de memes 🥳.

Remarque: si quelque chose ne fonctionne pas en suivant ce didacticiel, vérifiez votre package.json pour vous assurer que le projet de démarrage a intégré la version la plus récente de NodeGui.

Affichage d’un GIF animé

Étant donné que les mèmes sont 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('/absolute/path/to/animated.gif');
movie.start();

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

Étant donné que nous voulons charger une image à partir d’une URL, nous ne pouvons pas utiliser QMovie«S setFileName , 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 méthode QMovie loadFromData au lieu.

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

npm i axios

Créons maintenant une fonction qui prendra une URL comme paramètre et retournera une configuration QMovie exemple pour le GIF:

async function getMovie(url) {
  const { data } = await axios.get(url, { responseType: 'arraybuffer' });
  const movie = new QMovie();
  movie.loadFromData(data);
  movie.start();
  return movie;
}

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

Vous pouvez penser QMovie comme une 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 un QLabel instance et ensemble QMovie à elle.

Puisque getMovie renvoie une promesse, nous devons apporter quelques modifications au code. Après quelques remaniements mineurs, nous nous retrouvons avec ce qui suit.

src / index.js

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

async function getMovie(url) {
  const { data } = await axios.get(url, { responseType: 'arraybuffer' });
  const movie = new QMovie();
  movie.loadFromData(data);
  movie.start();
  return movie;
}

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

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

  win.setCentralWidget(label);
  win.show();
  global.win = win;
};

main().catch(console.error);

le main la fonction est notre point d’entrée. Ici, nous créons une fenêtre et une étiquette. On instancie ensuite un QMovie par exemple avec l’aide de notre getMovie fonction, et enfin définir la QMovie à un QLabel.

Exécutez l’application avec npm start et vous devriez voir quelque chose comme ça:

Exemple d'animation de base montrant un cheval au galop

Récupérer des GIF à partir de l’API GIPHY

Giphy.com possède une 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 inscrire sur developers.giphy.com et obtenir une clé API. Tu peux trouver d’autres instructions ici.

Nous utiliserons le point de terminaison de recherche fonctionnalité pour implémenter notre recherche de meme.

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

const GIPHY_API_KEY = 'Your API key here';

async function searchGifs(searchTerm) {
  const url = 'https://api.giphy.com/v1/gifs/search';
  const res = await axios.get(url, {
    params: {
      api_key: GIPHY_API_KEY,
      limit: 25,
      q: searchTerm,
      lang: 'en',
      offset: 0,
      rating: 'pg-13'
    }
  });
  return res.data.data;
}

Le résultat de la fonction après l’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

Afin d’afficher une liste de GIF, nous allons créer un getGifViews fonction qui:

  1. créer un QWidget récipient
  2. créer un QMovie widget pour chaque GIF
  3. créer un QLabel de chaque QMovie exemple
  4. attacher chacun QLabel enfant de la QWidget récipient
  5. retourner le QWidget récipient

Le code ressemble à ceci:

async function 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 = await getMovie(url);
    const gifView = new QLabel();
    gifView.setMovie(movie);
    gifView.setInlineStyle(`width: ${width}`);
    container.layout.addWidget(gifView);
  });

  await Promise.all(promises);

  container.setInlineStyle(`
      flex-direction: 'row';
      flex-wrap: 'wrap';
      justify-content: 'space-around';
      width: 330px;
      height: 300px;
  `);

  return container;
}

Décomposons cela un peu.

Tout d’abord, nous créons notre widget conteneur. QWidgets sont essentiellement des widgets vides qui agissent comme des conteneurs. Ils sont similaires à

éléments du monde Web.

Ensuite, afin d’assigner des widgets enfants au QWidget, nous devons lui donner une mise en page. 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 getMovie fonction pour créer un QMovie instance pour chaque URL GIF. Nous attribuons le QMovie par exemple à un QLabel (nommé gifView) et lui donner un style de base en utilisant le setInlineStyle méthode. Enfin, nous ajoutons le QLabel widget à la disposition du conteneur en utilisant le layout.addWidget méthode.

Étant donné que tout cela se produit de manière asynchrone, nous attendons que tout soit résolu Promise.all, avant de définir certains styles de conteneur et de renvoyer le widget de conteneur.

Modifions maintenant notre main 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 = 'Your API key here';

async function getMovie(url) { ... }
async function searchGifs(searchTerm) { ... }
async function getGifViews(listOfGifs) { ... }

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

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

  // We get the list of gifs here
  const listOfGifs = await searchGifs('hello');

  // We create the container with GIF view widgets
  const container = await getGifViews(listOfGifs);

  // We finally attach the container to the widget
  center.layout.addWidget(container);

  win.setCentralWidget(center);
  win.show();

  global.win = win;
};

main().catch(console.error);

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

Liste des GIF

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 et d’un bouton de recherche

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

Voici ce que la fonction doit faire:

  1. créer un QWidget conteneur, auquel nous ajouterons un champ et un bouton de recherche en tant qu’enfants
  2. créer une mise en page et l’attacher au conteneur
  3. créer une entrée et un bouton de recherche, puis les attacher au FlexLayout
  4. associez un écouteur d’événements au bouton qui, lorsque vous cliquez dessus, appelle onSearch fonction de rappel en lui faisant passer le texte présent dans le champ de saisie de texte
  5. retourner le QWidget récipient

Le code ressemble à ceci:

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

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

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

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

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

  searchContainer.setStyleSheet(`
    #searchContainer {
      flex-direction: 'row';
      padding: 10px;
      align-items: 'center';
    }
    #searchInput {
      flex: 1;
      height: 40px;
    }
    #searchButton {
      margin-left: 5px;
      width: 50px;
      height: 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 à l’aide d’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. Afin de définir un objectName, nous utiliserons le setObjectName méthode.

Ajoutons maintenant ceci 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 = 'Your API key here';

async function getMovie(url) { ... }
async function searchGifs(searchTerm) { ... }
async function getGifViews(listOfGifs) { ... }
function createSearchContainer(onSearch) { ... }

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

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

  // Here we create the search container
  const searchContainer = createSearchContainer(searchText => {
    console.log('searchText: ', searchText);
  });

  // Here we add it to the center widget before we add the list of GIFs.
  center.layout.addWidget(searchContainer);

  const listOfGifs = await searchGifs('hello');
  const container = await getGifViews(listOfGifs);

  center.layout.addWidget(container);

  win.setCentralWidget(center);
  win.show();

  global.win = win;
};

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 vous cliquez sur le bouton de recherche, saisissez le texte de recherche et utilisez searchGifs pour obtenir une nouvelle liste de GIF.
  2. Créez un nouveau conteneur pour ces GIF à l’aide du getGifViews une fonction.
  3. Retirez le conteneur existant de la fenêtre.
  4. Ajoutez le nouveau conteneur à la fenêtre.

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

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

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

  let container = new QWidget();
  const searchContainer = createSearchContainer(async searchText => {
    try {
      // Create a new GIF container with new GIFs
      const listOfGifs = await searchGifs(searchText);
      const newGifContainer = await getGifViews(listOfGifs);

      // Remove existing container from the window
      center.layout.removeWidget(container);
      container.close();

      // Add the new GIF container to the window
      center.layout.addWidget(newGifContainer);
      container = newGifContainer;
    } catch (err) {
      console.error('Something happened!', err);
    }
  });
  center.layout.addWidget(searchContainer);

  win.setCentralWidget(center);
  win.show();

  global.win = win;
};

Lançons-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ère une liste de GIF correspondant au terme de recherche à partir de l’API GIPHY.

Bien que tout cela bouge dans la bonne direction, vous avez probablement remarqué que la liste des GIF est coupée en bas et qu’il n’y a aucun moyen de les faire défiler. En effet, nous utilisons un QWidget conteneur pour les afficher. Pour faire défiler le conteneur, nous devons échanger le QWidget pour un QScrollArea. Cela fournit une vue défilante sur un autre widget.

Commençons par supprimer le height propriété dans le getGifViews une fonction:

async function getGifViews(listOfGifs) {
  ...

  container.setInlineStyle(`
      flex-direction: 'row';
      flex-wrap: 'wrap';
      justify-content: 'space-around';
      width: 330px;
-     height: 300px;
  `);

  return container;
}

Ensuite, nous devons changer src/index.js ressembler à ceci:

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

const axios = require('axios').default;
const GIPHY_API_KEY = 'Your API key here';

async function getMovie(url) { ... }
async function searchGifs(searchTerm) { ... }
async function getGifViews(listOfGifs) { ... }
function createSearchContainer(onSearch) { ... }

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

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

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

  const searchContainer = createSearchContainer(async searchText => {
    try {
      const listOfGifs = await searchGifs(searchText);
      const newGifContainer = await getGifViews(listOfGifs);

      // Remove existing container from the scrollArea
      const oldContainer = scrollArea.takeWidget();
      if (oldContainer) oldContainer.close();

      // Add the new GIF container to the scrollArea
      scrollArea.setWidget(newGifContainer);
    } catch (err) {
      console.error('Something happened!', err);
    }
  });

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

  win.setCentralWidget(center);
  win.show();

  global.win = win;
};

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 le QScrollArea«S méthode takeWidget 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 meme, vous devriez maintenant avoir des GIF déroulants:

Recherche par défilement

Ajouter des écouteurs de clic pour copier les URL GIF à partager

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énement de souris vers le bas à chaque GIF
  2. à l’intérieur du rappel de l’écouteur d’événements, utilisez le QClipboard class pour copier l’URL dans le presse-papiers global
  3. montrer un modal à l’utilisateur disant que l’URL a été copiée

L’auditeur d’événements peut être attaché à l’intérieur du getGifViews une fonction:

async function getGifViews(listOfGifs) {
  ...

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

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

      showModal(
        'Copied to clipboard!',
        `You can press Cmd/Ctrl + V to paste the GIF url: ${url}`
      );

    });

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

  ...

  return container;
}

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

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

function showModal(title, details) {
  const modal = new QMessageBox();
  modal.setText(title);
  modal.setDetailedText(details);
  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 =  'Your API key here';

async function searchGifs(searchTerm) { ... };
async function getGifViews(listOfGifs) { ... };
async function getMovie(url) { ... };
function createSearchContainer(onSearch) { ... };
function showModal(title, details) { ... };

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

main().catch(console.error);

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

Copier l'URL GIF dans le presse-papiers GIF

Ajout d’une icône de barre d’état système

Nous voulons que notre application soit cachée dans la barre d’état système lorsqu’elle n’est pas utilisée. Pour cela, nous allons créer une icône dans la 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éer 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éer des éléments de menu à l’aide Widgets QAction et configurer des auditeurs d’événements pour écouter leur trigger événements.
  4. Au déclenchement, masquez ou affichez la fenêtre.

Commençons par exiger les modules nécessaires, puis apportons une petite modification à la main fonction 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 =  'Your API key here';

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

  win.show();
  systemTrayIcon(win);

  global.win = win;
};

Comme vous pouvez le voir, nous avons besoin d’une icône de la assets dossier. Si vous suivez, vous pouvez téléchargez le fichier icône 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(icon);
  tray.show();

  // Menu that should pop up when clicking on systray icon.
  const menu = new QMenu();
  tray.setContextMenu(menu);

  //Each item in the menu is called an action
  const visibleAction = new QAction();
  menu.addAction(visibleAction);
  visibleAction.setText('Show/Hide');
  visibleAction.addEventListener('triggered', () => {
    if (win.isVisible()) {
      win.hide();
    } else {
      win.show();
    }
  });

  global.tray = tray;
}

Ici, nous créons l’icône à l’aide de NodeGui Classe QIcon. Ensuite, nous utilisons le 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 = {
  ...
  node: {
-    __dirname: true,
-    __filename: true
+    __dirname: false,
+    __filename: false
  },
  ...
}

Le résultat final:

Le widget de recherche finale

Quelques ajustements finaux

La gestion des erreurs

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

const main = async () => {
  ...
  const searchContainer = createSearchContainer(async searchText => {
    try {
      ...
    } catch (err) {
      ...
      showModal('Something went wrong!', 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 essayant 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 de saisir sa clé API. Cela signifie qu’il n’a pas besoin d’être codé en dur dans le programme:

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

async function searchGifs(searchTerm) { ... }
async function getGifViews(listOfGifs) { ... }
async function getMovie(url) { ... }
function createSearchContainer(onSearch) { ... }
function showModal(title, details) { ... }
function systemTrayIcon(win) { ... }

function showAPIKeyDialog() {
  const dialog = new QDialog();
  dialog.setLayout(new FlexLayout());
  const label = new QLabel();
  label.setText('Enter your Giphy API Key');
  const input = new QLineEdit();
  const okButton = new QPushButton();
  okButton.setText('OK');
  okButton.addEventListener('clicked', () => {
    GIPHY_API_KEY = input.text();
    dialog.close();
  });
  dialog.layout.addWidget(label);
  dialog.layout.addWidget(input);
  dialog.layout.addWidget(okButton);
  dialog.setInlineStyle(`
    padding: 10;
    height: 150px;
    flex-direction: 'column';
    align-items:'center';
    justify-content: 'space-around';
  `);
  dialog.exec();
}

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

main().catch(console.error);

Comme vous pouvez le voir, nous utilisons un QDialog widget pour inviter l’utilisateur à entrer, puis à stocker tout ce qu’il fournit dans le GIPHY_API_KEY variable. 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.

Empaqueter 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 produits distribuables 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.

Usage

Tout d’abord, installez packer en tant que dépendance de développement:

npm install --save-dev @nodegui/packer

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

npx nodegui-packer --init MemeApp

Le modèle est essentiellement un projet spécifique au système d’exploitation qui contient le code pour empaqueter avec succès l’ensemble du code, des actifs et des dépendances de l’application NodeGui. Notez que vous devez l’exécuter séparément sous Windows, macOS et Linux pour créer trois modèles différents. Ce modèle vous permet d’affiner les paramètres de déploiement finaux spécifiques à chaque système d’exploitation. Vous pouvez ajuster des éléments tels que les informations sur la société, les icônes et autres métadonnées en fonction de vos besoins.

Pour Linux, le modèle ressemble à ceci:

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

Notez que vous avez seulement besoin d’exécuter la commande init une fois que. Ensuite, vous apportez des modifications au modèle et le validez dans le référentiel de projet.

L’étape suivante consiste à réellement construire et empaqueter le projet dans un distribuable.

Supprimer le build répertoire s’il existe:

rm -rf ./deploy/build

Créez ensuite l’application à l’aide de webpack:

npm run build

Enfin, exécutez la commande packer’s pack, en lui passant dist dossier comme argument:

npx nodegui-packer --pack ./dist

Cela se traduira par les éléments suivants:

  • Sous macOS, le packer affichera un dmg fichier.
  • Sous Linux, le packer affichera un AppImage, qui est quelque chose de similaire à un .app fichier sous macOS.
  • Sous Windows, le packer génère un dossier contenant l’exécutable et toutes les DLL.

Une fois la commande réussie, elle devrait imprimer le répertoire de sortie, qui se trouve généralement à l’intérieur du deploy//build annuaire. Assurez-vous ne fais pas valider ce répertoire:

.
└── 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

Le Linux distribuable est deploy/linux/build/MemeApp/Application-aed23d8-x86_64.AppImage 🚀📦.

Conclusion

Dans ce didacticiel, nous avons réussi à créer une application de recherche de mèmes dans le monde réel à l’aide de NodeGui dans environ 200 lignes de code. Nous avons appris certains des concepts et capacités de base de la bibliothèque. Nous avons également pu empaqueter l’application finie dans un fichier distribuable qui peut être partagé avec les utilisateurs finaux.

Je crois que NodeGui ouvre la porte à la création de nombreuses applications natives vraiment efficaces avec Node.js.

NodeGui prend également en charge les bibliothèques et les frameworks tels que React (officiel), Angulaire (communauté) etc Vue.js (communauté). Veuillez les consulter et leur donner une étoile sur GitHub s’ils sont le genre de chose qui vous intéresse.

NodeGui est une bibliothèque open source qui bénéficier grandement des contributions de code. Il a une base de code relativement facile à comprendre et une communauté très accueillante. J’encourage tout le monde à aider.

Enfin, grâce à leurs nombreux widgets intégrés et à leur style via CSS, je pense que les applications NodeGui sont aussi faciles à développer que les applications Web ou Electron. Je vous encourage à créer quelque chose de cool et à partager avec nous.

Ce didacticiel est un extrait de la bibliothèque SitePoint Premium, où vous pouvez créer un ensemble de compétences Node.js prêt à l’emploi.

Close Menu