Logotipo de Zephyrnet

Cree una aplicación nativa de búsqueda de GIF de escritorio con NodeGui

Fecha:

NodoGui es una biblioteca de código abierto para crear aplicaciones de escritorio nativas multiplataforma con Node.js. Las aplicaciones NodeGui pueden ejecutarse en macOS, Windows y Linux. Las aplicaciones creadas con NodeGui están escritas usando JavaScript, diseñadas con CSS y renderizadas como widgets de escritorio nativos usando el Marco de Qt.

Algunas de las características de NodeGui son:

  • widgets nativos con soporte incorporado para modo oscuro
  • CPU baja y huella de memoria
  • estilo con CSS que incluye soporte completo para el diseño de Flexbox
  • soporte completo de API Node.js y acceso a todos los módulos npm compatibles con Node.js
  • excelente soporte de depuración utilizando DevTools de Chrome
  • soporte de TypeScript de primera clase

NodeGui funciona con el marco Qt, que lo hace eficiente en CPU y memoria en comparación con otras soluciones basadas en Chromium como Electron. Esto significa que las aplicaciones escritas usando NodeGui no es necesario abra una instancia del navegador y renderice la IU en ella. En cambio, todos los widgets se representan de forma nativa.

Este tutorial demostrará cómo instalar NodeGui y usarlo para crear un buscador de memes que viva en la bandeja del sistema y se comunique con el API GIPHY.

El código fuente completo para este tutorial es disponible en GitHub.

Instalación y configuración básica

Para este tutorial se supone que tienes Nodo.js v12 o mayor instalado. Puede confirmar que tanto Node como npm están disponibles ejecutando:

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

Si necesita ayuda con este paso, consulte nuestro tutorial sobre la instalación de Node.

NodeGui requiere herramientas de compilación CMake y C ++ para construir la capa nativa C ++ del proyecto. Asegúrese de instalar CMake> = 3.1 junto con un compilador de C ++ que admita C ++ 11 y superior. Las instrucciones detalladas son un poco diferentes según su sistema operativo.

macOS

Se recomienda instalar CMake usando Homebrew. Ejecute los siguientes comandos en un terminal después de instalar Homebrew:

brew install cmake
brew install make

Puede confirmar la instalación ejecutando:

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

Por último, necesita GCC / Clang para compilar código C ++. Verifique que tiene GCC instalado usando este comando:

gcc --version

Si no tiene GCC instalado, asegúrese de instalar Herramientas de línea de comandos para Xcode or Herramientas para desarrolladores de XCode Desde Página de desarrollador de Apple.

Windows

Puede instalar CMake en Windows descargando la última versión de Página de descarga de CMake.

Se recomienda encarecidamente que utilice Powershell como terminal preferido en Windows.

Puede confirmar la instalación de CMake ejecutando:

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

Por último, necesitas un compilador de C ++. Una posibilidad sería instalar Visual Studio 2017 o superior. Se recomienda que elija el Desarrollo de escritorio con C ++ carga de trabajo durante el proceso de instalación.

Linux

Nos centraremos en Ubuntu 18.04 para los fines de este tutorial. Se recomienda instalar CMake usando el administrador de paquetes. Ejecute los siguientes comandos en una terminal:

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

Puede confirmar la instalación ejecutando:

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

Por último, necesita GCC para compilar código C ++. Verifique que tiene GCC instalado usando el comando:

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

Hola Mundo

Para comenzar con nuestra aplicación de meme NodeGui, clonaremos el proyecto inicial.

Nota: Ejecutar esto requiere Git y npm.

Abra una terminal y ejecute:

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

Si todo va bien, debería ver una aplicación NodeGui de hello world funcionando en la pantalla.

Hola ejemplo de World NodeGui

Por defecto, el proyecto nodegui-starter es un proyecto TypeScript Sin embargo, en este tutorial escribiremos nuestra aplicación en JavaScript. Para convertir nuestro iniciador en un proyecto JS, haremos los siguientes cambios menores:

  1. Eliminar el index.ts presentar en el src carpeta.

  2. Crear un nuevo archivo index.js existentes src directorio con los siguientes contenidos:

    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 lo que respecta al desarrollo, una aplicación NodeGui es esencialmente una aplicación Node.js. Se puede acceder a todas las API y características que se encuentran en NodeGui a través de @nodegui/nodegui módulo, que puede ser requerido como cualquier otro módulo Node.js. Además, tiene acceso a todas las API de Node.js y los módulos de nodo. NodeGui utiliza componentes nativos en lugar de componentes basados ​​en la web como bloques de construcción.

En el ejemplo anterior, hemos importado QPrincipalVentana y Etiqueta Q para crear una ventana nativa que muestre el texto "Hola mundo".

Ahora ejecuta la aplicación nuevamente:

npm start

Hello World versión JavaScript

Ahora que tenemos nuestra configuración básica lista, comencemos a construir nuestro buscador de memes 🥳.

Nota: Si algo no funciona mientras sigue este tutorial, verifique su package.json para asegurarse de que el proyecto de inicio ha obtenido la versión más actualizada de NodeGui.

Mostrar un GIF animado

Como los memes son generalmente GIF animados, comenzaremos creando una ventana básica que muestre una imagen GIF desde una URL.

Para hacer esto, haremos uso de QPelícula junto con QLabel. QMovie no es un widget sino un contenedor que puede reproducir animaciones simples. Lo usaremos en combinación con QLabel.

Un ejemplo de uso de QMovie se ve así:

const movie = new QMovie();
movie.setFileName('/absolute/path/to/animated.gif');
movie.start(); const animatedLabel = new QLabel();
animatedLabel.setMovie(movie);

Dado que queremos cargar una imagen desde una URL, no podemos usar QMoviees establecer nombre de archivo método, que está reservado solo para archivos locales. En su lugar, descargaremos la imagen GIF usando axios como un búfer y usar el método QMovie cargarDeDatos preferiblemente.

Entonces, comencemos con la instalación de axios:

npm i axios

Ahora creemos una función que tomará una URL como parámetro y devolverá una configuración QMovie instancia para el GIF:

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

La getMovie la función toma una URL, le dice a axios que descargue el GIF como un búfer, y luego usa ese búfer para crear un QMovie ejemplo.

Tu puedes pensar en QMovie como una clase que maneja la lógica interna de reproducir la animación GIF cuadro por cuadro. QMovie no es un widget, por lo que no se puede mostrar en la pantalla tal como está. En cambio, usaremos un regular QLabel instancia y conjunto QMovie a la misma.

Como getMovie devuelve una promesa, tenemos que hacer algunos cambios en el código. Después de algunas refactorizaciones menores, terminamos con lo siguiente.

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);

La main La función es nuestro punto de entrada. Aquí creamos una ventana y una etiqueta. Luego instanciamos un QMovie instancia con la ayuda de nuestro getMovie función, y finalmente establecer el QMovie a una QLabel.

Ejecute la aplicación con npm start y deberías ver algo como esto:

Ejemplo básico de animación que muestra un caballo al galope

Obteniendo GIFs de la API GIPHY

Giphy.com tiene una API pública que cualquiera puede usar para crear excelentes aplicaciones que usan GIF animados. Para utilizar la API GIPHY, debe registrarse en desarrolladores.giphy.com y obtener una clave API. Puedes encontrar más instrucciones aquí.

Estaremos usando el punto final de búsqueda característica para implementar nuestra búsqueda de memes.

Comencemos escribiendo un searchGifs función que tomará un searchTerms parámetro como entrada y solicitar GIF utilizando el punto final anterior:

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;
}

El resultado de la función después de la ejecución se verá así:

[ { "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", ... }, ...
]

El resultado es esencialmente una matriz de objetos que contienen información sobre cada GIF. Estamos particularmente interesados ​​en returnValue[i].images.fixed_width_small.url para cada imagen, que contiene la URL del GIF.

Mostrar una lista de GIF utilizando la respuesta de la API

Para mostrar una lista de GIF, crearemos un getGifViews función que:

  1. crear un QWidget envase
  2. crear un QMovie widget para cada GIF
  3. crear un QLabel de cada QMovie ejemplo
  4. adjuntar cada QLabel como hijo de la QWidget envase
  5. devuelve el QWidget envase

El código se ve así:

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;
}

Analicemos esto un poco.

Primero, creamos nuestro widget contenedor. QWidgetLos s son esencialmente widgets vacíos que actúan como contenedores. Son similares a <div> elementos en el mundo web.

A continuación, para asignar widgets secundarios a QWidget, tenemos que darle un diseño. UNA diseño dicta cómo deben organizarse los widgets secundarios dentro de un padre. Aquí elegimos diseño flexible.

Entonces, usamos nuestro getMovie función para crear un QMovie instancia para cada URL GIF. Asignamos el QMovie instancia a un QLabel (nombrada gifView) y darle un estilo básico usando el setInlineStyle método. Finalmente, agregamos el QLabel widget al diseño del contenedor utilizando el layout.addWidget método.

Como todo esto sucede de forma asincrónica, esperamos que todo se resuelva usando Promise.all, antes de configurar algunos estilos de contenedor y devolver el widget de contenedor.

Ahora modifiquemos nuestro main funcionar para ver la lista de widgets que preparamos.

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 ejecuta el proyecto después de realizar estos cambios, debería ver:

Lista de GIF "hola" extraídos de la API GIPHY

¡Excelente! Ahora, agreguemos un campo de entrada de búsqueda junto con un botón, para que los usuarios puedan buscar algo más que GIFs "hola".

Agregar una entrada de búsqueda y un botón

Comencemos creando un createSearchContainer function, que aceptará una función de devolución de llamada como parámetro. Esto se llamará cuando se haga clic en el botón de búsqueda.

Esto es lo que debe hacer la función:

  1. crear un QWidget contenedor, al que agregaremos un campo de entrada de búsqueda y un botón como elementos secundarios
  2. crear un diseño y adjuntarlo al contenedor
  3. cree una entrada de búsqueda y un botón, luego adjúntelos al FlexLayout
  4. adjunte un detector de eventos al botón que, al hacer clic, llamará al onSearch función de devolución de llamada que pasa cualquier texto presente en el campo de entrada de texto
  5. devuelve el QWidget envase

El código se ve así:

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;
}

Espero que tengas una idea justa de lo que está sucediendo aquí, pero una cosa nueva para notar es la método setStyleSheet. Puede pensar en esto como una forma de aplicar CSS a nivel de bloque de una vez. Es muy similar a las hojas de estilo globales en la Web, pero con la diferencia de que en NodeGui / Qt se puede adjuntar una hoja de estilos a cualquier bloque y no solo globalmente.

Para diseñar un widget usando una hoja de estilo, necesitamos agregar un objectName a un widget, que usaremos para referenciarlo en la hoja de estilo. Esto es casi idéntico a un id en el mundo web Para establecer un objectName, usaremos el setObjectName método.

Ahora agreguemos esto searchContainer a la ventana principal

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);

Ahora, cuando inicie la aplicación e ingrese algo en el campo de búsqueda, debería ver lo que haya buscado registrado en su terminal.

Lista de GIF con entrada de búsqueda

Conectando la búsqueda a la vista GIF

Para cargar nuevos GIF en respuesta a la búsqueda de un usuario, debemos hacer lo siguiente:

  1. Dentro de la devolución de llamada que se activa cuando se hace clic en el botón de búsqueda, tome el texto de búsqueda y use searchGifs función para obtener una nueva lista de GIF.
  2. Cree un nuevo contenedor para estos GIF utilizando el getGifViews función.
  3. Retire el contenedor existente de la ventana.
  4. Agregue el nuevo contenedor a la ventana.

Si barajamos un poco las cosas, obtenemos:

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;
};

Vamos a ejecutarlo de nuevo y ver la magia 🧙‍♂️.

Widget de búsqueda de GIF conectado

Como puede ver, cuando escribe algo en el cuadro de búsqueda y presiona el botón de búsqueda, nuestro widget obtendrá una lista de GIF que coincidan con el término de búsqueda de la API GIPHY.

Si bien todo esto se mueve en la dirección correcta, probablemente haya notado que la lista de GIF está cortada en la parte inferior y no hay forma de desplazarlos. Esto es porque estamos usando un QWidget contenedor para mostrarlos. Para hacer que el contenedor sea desplazable, necesitamos intercambiar el QWidget para agendar una QScrollArea. Esto proporciona una vista de desplazamiento a otro widget.

Comenzaremos eliminando el height propiedad en el getGifViews función:

async function getGifViews(listOfGifs) { ... container.setInlineStyle(` flex-direction: 'row'; flex-wrap: 'wrap'; justify-content: 'space-around'; width: 330px;
- height: 300px; `); return container;
}

Entonces necesitamos cambiar src/index.js verse así:

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);

No hay nada demasiado emocionante pasando aquí. Estamos creando un nuevo QScrollArea, que agregamos al diseño debajo del campo de búsqueda. También estamos usando el QScrollAreaes Método takeWidget para eliminar cualquier contenedor existente del área de desplazamiento, antes de agregar los nuevos resultados de búsqueda.

Si inicia el buscador de memes, ahora debería tener GIF desplazables:

Búsqueda desplazable

Agregue oyentes de clics para copiar URL GIF para compartir

Ahora que podemos ver todos los GIF, queremos poder compartirlos. Una forma rápida de hacer esto es copiar la URL en el portapapeles global siempre que un usuario haga clic en el GIF de su elección.

Luego, el usuario puede simplemente navegar al lugar donde desea usar el GIF e insertarlo con Ctrl/Cmd + V.

Para hacer eso, debemos:

  1. adjunte un detector de eventos con mouse-down a cada GIF
  2. dentro de la devolución de llamada del oyente de eventos, use el Clase QClipboard copiar la URL al portapapeles global
  3. muestra un modal al usuario diciendo que la URL ha sido copiada

El oyente de eventos se puede adjuntar dentro del getGifViews función:

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;
}

Aquí, QApplication.portapapeles devuelve un objeto para interactuar con el portapapeles. Podemos usar este objeto setText método para alterar el contenido real del portapapeles.

También estamos haciendo uso de un showModal función. Vamos a definir eso a continuación:

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();
}

La Widget QMessageBox es similar a un cuadro de alerta en un navegador web. Se puede utilizar para detener la interacción del usuario y mostrar un mensaje.

Finalmente, necesitamos importar todos estos nuevos widgets en la parte superior 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 inicia el buscador de memes, ahora debería tener la capacidad de copiar / pegar URL GIF:

Copiar URL GIF en el portapapeles GIF

Agregar un icono de la bandeja del sistema

Queremos que nuestra aplicación se oculte en la bandeja del sistema cuando no esté en uso. Para esto, crearemos un icono de la bandeja del sistema que tendrá un elemento de menú que, al hacer clic, alternará la visibilidad del widget en ejecución.

Los pasos a seguir son:

  1. Créar un QSystemTrayIcono con un icono
  2. Cree un menú para el icono de la bandeja del sistema usando QMenú. Establezca la instancia del menú como menú contextual de la bandeja del sistema.
  3. Crear elementos de menú usando Widgets de QAction y configurar oyentes de eventos para escuchar sus trigger eventos.
  4. En el gatillo, oculta o muestra la ventana.

Comencemos por requerir los módulos necesarios, luego haga un pequeño cambio en el main función para decirle que use nuestro icono:

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;
};

Como puede ver, estamos requiriendo un icono de assets carpeta. Si estás siguiendo, puedes descargue el archivo de icono desde aquí.

Ahora viene la función de crear el icono de la bandeja del sistema:

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;
}

Aquí, estamos creando el ícono usando NodeGui Clase QIcon. Entonces estamos usando el QSystemTrayIcon clase para crear un icono de la bandeja del sistema para nuestra aplicación.

Finalmente, necesitamos ajustar la configuración de nuestro paquete web (en webpack.config.js) para evitar que el paquete web se llene __dirname:

const path = require('path'); module.exports = { ... node: {
- __dirname: true,
- __filename: true
+ __dirname: false,
+ __filename: false }, ...
}

El resultado final:

El widget de búsqueda final

Algunos ajustes finales

Manejo de errores

Antes de continuar con el empaque, hagamos uso de nuestro showModal función y agregue un diálogo de manejo de errores:

const main = async () => { ... const searchContainer = createSearchContainer(async searchText => { try { ... } catch (err) { ... showModal('Something went wrong!', JSON.stringify(err)); } }); ...
};

Esto alertará al usuario si, por ejemplo, algo sale mal con la solicitud de Ajax para obtener GIF de GIPHY. Puede probar esto alterando su clave API a algo no válido, luego iniciando la aplicación e intentando buscar un GIF.

Permitir al usuario ingresar una clave API

Mientras estamos en el tema de las claves API, agreguemos un diálogo para permitir que un usuario ingrese su clave API. Esto significa que no tiene que estar codificado en el programa:

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);

Como puede ver, estamos usando un QDialog Reproductor para pedirle al usuario que ingrese y luego almacenar lo que proporcione en el GIPHY_API_KEY variable. Si está buscando mejorar sus habilidades de NodeGui después de leer este tutorial, podría buscar mejorar esto, por ejemplo, al persistir la clave del sistema de archivos o validarla y proporcionar comentarios al usuario.

Nota: No lo olvide, el código fuente completo está disponible aquí: https://github.com/sitepoint-editors/memesearchapp-nodegui-tutorial.

Empaquetar la aplicación para la distribución multiplataforma

Una vez que hayamos creado con éxito la aplicación, necesitamos crear distribuibles para macOS, Windows y Linux que los usuarios finales puedan descargar y usar.

El proceso de creación de distribuibles suele ser diferente para cada sistema operativo, por lo que para aliviar el dolor usaremos la herramienta de empaquetado de NodeGui llamada @nodegui/packer.

Uso

Primero, instale Packer como una dependencia de desarrollo:

npm install --save-dev @nodegui/packer

A continuación, use el empaquetador para crear una plantilla de implementación:

npx nodegui-packer --init MemeApp

La plantilla es esencialmente un proyecto específico del sistema operativo que contiene el código para empaquetar con éxito todo el código, los activos y las dependencias de la aplicación NodeGui. Tenga en cuenta que debe ejecutar esto en Windows, macOS y Linux por separado para crear tres plantillas diferentes. Esta plantilla le permite ajustar la configuración de implementación final específica de cada sistema operativo. Puede ajustar cosas como información de la empresa, iconos y otros metadatos para satisfacer sus necesidades.

Para Linux, la plantilla se ve así:

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

Tenga en cuenta que solo necesita ejecutar el comando init una vez. Luego realiza cambios en la plantilla y la confirma en el repositorio del proyecto.

El siguiente paso es construir y empaquetar el proyecto en un distribuible.

Eliminar el build directorio si existe:

rm -rf ./deploy/build

Luego compila la aplicación usando webpack:

npm run build

Finalmente, ejecute el comando packer del packer, pasándole el dist carpeta como argumento:

npx nodegui-packer --pack ./dist

Esto resultará en lo siguiente:

  • En macOS, el empacador generará un dmg archivo.
  • En Linux, el empacador generará un AppImage, que es algo similar a un .app archivo en macOS.
  • En Windows, el empaquetador genera una carpeta que contiene el ejecutable y todos los archivos DLL.

Una vez que el comando tiene éxito, debe imprimir el directorio de salida, que normalmente está dentro del deploy/<os>/build directorio. Asegúrese no comprometer este directorio:

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

El distribuible de Linux es deploy/linux/build/MemeApp/Application-aed23d8-x86_64.AppImage 🚀📦.

Conclusión

En este tutorial, creamos con éxito una aplicación de búsqueda de memes del mundo real usando NodeGui en aproximadamente 200 líneas de código. Aprendimos algunos de los conceptos y capacidades básicas de la biblioteca. También pudimos empaquetar la aplicación terminada en una distribuible que se puede compartir con los usuarios finales.

Creo que NodeGui abre la puerta para crear muchas aplicaciones nativas verdaderamente eficientes con Node.js.

NodeGui también es compatible con bibliotecas y marcos como Reaccionar (oficial), Angular (comunidad) y pronto Vue.js (comunidad). Echa un vistazo a estos y dales una estrella en GitHub si son el tipo de cosas que te interesan.

NodeGui es una biblioteca de código abierto que se benefician enormemente de las contribuciones del código. Tiene una base de código relativamente fácil de entender y una comunidad muy acogedora. Animo a todos a ayudar.

Finalmente, gracias a sus muchos widgets incorporados y su estilo a través de CSS, creo que las aplicaciones NodeGui son tan fáciles de desarrollar como las aplicaciones web o Electron. Te animo a que construyas algo genial y lo compartas con nosotros.

Este tutorial es un extracto del Biblioteca SitePoint Premium, donde puede crear un conjunto de habilidades Node.js listo para el trabajo.

Fuente: https://www.sitepoint.com/build-native-desktop-gif-searcher-app-using-nodegui/?utm_source=rss

punto_img

Información más reciente

punto_img