[PYTHON] J'ai créé un outil CLI pour convertir les images de chaque répertoire en PDF

Github

:octocat: https://github.com/ikota3/image_utilities

Aperçu

Afin de cuisiner confortablement par moi-même, j'ai créé un outil pour convertir les images stockées dans chaque répertoire en PDF. En outre, les sous-répertoires sont également affichés et créés de manière récursive.

L'opération a été confirmée sous Windows 10.

Les choses nécessaires

Comment utiliser

$ git clone https://github.com/ikota3/image_utilities
$ cd image_utilities
$ pip install -r requirements.txt
$ python src/images_to_pdf.py convert -i "path/to/input" -o "path/to/output" -e "jpg,jpeg,png"

Détails d'implémentation

Cette fois, j'ai pensé qu'il serait difficile de tout faire à partir de zéro pour créer un outil CLI, j'ai donc utilisé une bibliothèque appelée fire dont on parlait auparavant. ..

En fait, c'était très facile à utiliser.

Être capable d'appuyer sur des commandes

Tout d'abord, créez un squelette afin de pouvoir appuyer sur la commande et recevoir la valeur d'entrée. Cette fois, créez une classe et appelez-la avec le feu. En plus des classes, fire peut appeler des fonctions, des modules, des objets et bien d'autres choses. Veuillez consulter le document officiel pour plus de détails. https://github.com/google/python-fire/blob/master/docs/guide.md

images_to_pdf.py


import fire

class PDFConverter(object):
    """Class for convert images to pdf."""

    def __init__(
            self,
            input_dir: str = "",
            output_dir: str = "",
            extensions: Union[str, Tuple[str]] = None,
            force_write: bool = False,
            yes: bool = False
    ):
        """Initialize

        Args:
            input_dir (str): Input directory. Defaults to "".
            output_dir (str): Output directory. Defaults to "".
            extensions (Union[str, Tuple[str]]): Extensions. Defaults to None.
            force_write (bool): Flag for overwrite the converted pdf. Defaults to False.
            yes (bool): Flag for asking to execute or not. Defaults to False.
        """
        self.input_dir: str = input_dir
        self.output_dir: str = output_dir
        if not extensions:
            extensions = ('jpg', 'png')
        self.extensions: Tuple[str] = extensions
        self.force_write: bool = force_write
        self.yes: bool = yes

	def convert(self):
		print("Hello World!")


if __name__ == '__main__':
    fire.Fire(PDFConverter)

Pour le moment, le squelette est terminé. Si vous tapez une commande dans cet état, vous devriez voir "Hello World!".

$ python src/images_to_pdf.py convert
Hello World!

De plus, d'autres paramètres tels que ʻinput_dir = "" `ont des valeurs par défaut, mais si vous ne passez pas les valeurs du côté commande sans les définir, une erreur du côté feu se produira.

Pour transmettre la valeur, ajoutez simplement un trait d'union avant le préfixe de l'argument défini dans __init__, puis écrivez la valeur que vous souhaitez transmettre.

La méthode de transmission des commandes suivantes est la même, bien que la méthode d'écriture soit différente.

$ # self.input_exemple de dir
$ python src/images_to_pdf.py convert -i "path/to/input"
$ python src/images_to_pdf.py convert -i="path/to/input"
$ python src/images_to_pdf.py convert --input_dir "path/to/input"
$ python src/images_to_pdf.py convert --input_dir="path/to/input"

De plus, j'étais confus lorsque j'ai essayé de faire passer la liste comme une pierre d'achoppement.

$ # self.Exemples d'extensions
$ python src/images_to_pdf.py convert -e jpg,png # OK
$ python src/images_to_pdf.py convert -e "jpg,png" # OK
$ python src/images_to_pdf.py convert -e "jpg, png" # OK
$ python src/images_to_pdf.py convert -e jpg, png # NG

Vérification de la valeur d'entrée

Avant d'effectuer le processus de conversion PDF, tapez check by ʻis instance () `et vérifiez si le chemin spécifié existe, etc. sont effectués.

images_to_pdf.py


def _input_is_valid(self) -> bool:
    """Validator for input.

    Returns:
        bool: True if is valid, False otherwise.
    """
    is_valid = True

    # Check input_dir
    if not isinstance(self.input_dir, str) or \
            not os.path.isdir(self.input_dir):
        print('[ERROR] You must type a valid directory for input directory.')
        is_valid = False

    # Check output_dir
    if not isinstance(self.output_dir, str) or \
            not os.path.isdir(self.output_dir):
        print('[ERROR] You must type a valid directory for output directory.')
        is_valid = False

    # Check extensions
    if not isinstance(self.extensions, tuple) and \
            not isinstance(self.extensions, str):
        print('[ERROR] You must type at least one extension.')
        is_valid = False

    # Check force_write
    if not isinstance(self.force_write, bool):
        print('[ERROR] You must just type -f flag. No need to type a parameter.')
        is_valid = False

    # Check yes
    if not isinstance(self.yes, bool):
        print('[ERROR] You must just type -y flag. No need to type a parameter.')
        is_valid = False

    return is_valid

Scanner le répertoire, collecter des images et convertir en PDF

Nous utilisons quelque chose appelé ʻos.walk () pour scanner le répertoire à partir du chemin ʻinput_dir reçu. https://docs.python.org/ja/3/library/os.html?highlight=os walk#os.walk

Le répertoire est numérisé comme indiqué ci-dessous, les images sont collectées et converties au format PDF.

images_to_pdf.py


def convert(self):
    #Au préfixe de l'extension.Ajouter
    extensions: Union[str | Tuple[str]] = None
    if isinstance(self.extensions, tuple):
        extensions = []
        for extension in self.extensions:
            extensions.append(f'.{extension}')
        extensions = tuple(extensions)
    elif isinstance(self.extensions, str):
        extensions = tuple([f'.{self.extensions}'])

	#Scannez les répertoires et convertissez les images de chaque répertoire en PDF
    for current_dir, dirs, files in os.walk(self.input_dir):
        print(f'[INFO] Watching {current_dir}.')

        #Une liste qui stocke le chemin où se trouve l'image cible
        images = []

        #les fichiers sont à jour_Liste des fichiers dans le répertoire
        #Le tri sera dans le désordre si le nombre de chiffres est différent(https://github.com/ikota3/image_utilities#note)
        #Par conséquent, j'ai préparé une fonction pour la rendre comme prévu.(Voir ci-dessous)
        for filename in sorted(files, key=natural_keys):
            if filename.endswith(extensions):
                path = os.path.join(current_dir, filename)
                images.append(path)

		#Lorsqu'il n'y a pas d'image à la suite de la numérisation
        if not images:
            print(
                f'[INFO] There are no {", ".join(self.extensions).upper()} files at {current_dir}.'
            )
            continue

        pdf_filename = os.path.join(
            self.output_dir, f'{os.path.basename(current_dir)}.pdf'
        )

		# -S'il y a un paramètre f, écraser de force même s'il y a un fichier
        if self.force_write:
            with open(pdf_filename, 'wb') as f:
                f.write(img2pdf.convert(images))
            print(f'[INFO] Created {pdf_filename}!')
        else:
            if os.path.exists(pdf_filename):
                print(f'[ERROR] {pdf_filename} already exist!')
                continue

            with open(pdf_filename, 'wb') as f:
                f.write(img2pdf.convert(images))
            print(f'[INFO] Created {pdf_filename}!')

Lors de la collecte des images dans le répertoire, il y avait des moments où le tri était incorrect et l'ordre n'était pas ce à quoi je m'attendais, alors j'ai préparé une fonction. Aussi, je l'ai fait en me référant au lien suivant. https://stackoverflow.com/questions/5967500/how-to-correctly-sort-a-string-with-a-number-inside

sort_key.py


import re
from typing import Union, List


def atoi(text: str) -> Union[int, str]:
    """Convert ascii to integer.

    Args:
        text (str): string.

    Returns:
        Union[int, str]: integer if number, string otherwise.
    """
    return int(text) if text.isdigit() else text


def natural_keys(text: str) -> Union[List[int], List[str]]:
    """Key for natural sorting

    Args:
        text (str): string

    Returns:
        Union[List[int], List[str]]: A list of mixed integer and strings.
    """
    return [atoi(c) for c in re.split(r'(\d+)', text)]

la fin

J'avais l'habitude de créer des outils CLI sans utiliser de bibliothèque, mais c'était plus facile à implémenter que le feu!

Souhaitez-vous créer un outil CLI?

Recommended Posts

J'ai créé un outil CLI pour convertir les images de chaque répertoire en PDF
Je souhaite convertir une table convertie en PDF en Python en CSV
J'ai créé un outil pour compiler nativement Hy
J'ai créé un réseau pour convertir des images noir et blanc en images couleur (pix2pix)
J'ai créé un script en python pour convertir des fichiers .md au format Scrapbox
Conversion par lots de fichiers PSD du répertoire en PDF
J'ai créé un outil pour convertir Jupyter py en ipynb avec VS Code
J'ai créé un outil pour obtenir de nouveaux articles
J'ai fait un module en langage C qui filtre les images chargées par Python
J'ai fait un script pour mettre un extrait dans README.md
J'ai créé un code pour convertir illustration2vec en modèle Keras
J'ai écrit un outil CLI en langue Go pour afficher le flux de balises de Qiita dans CLI
J'ai créé un outil pour créer un nuage de mots à partir de wikipedia
[Titan Craft] J'ai créé un outil pour invoquer un géant sur Minecraft
J'ai créé un script en Python pour convertir un fichier texte pour JSON (pour l'extrait d'utilisateur vscode)
Convertir Markdown en PDF en Python
Convertir un PDF A4 en A3 toutes les 2 pages
Outil pour convertir la configuration Juniper
J'ai fait une commande pour afficher un calendrier coloré dans le terminal
Je veux imprimer dans la notation d'inclusion
J'ai fait un programme de gestion de la paie en Python!
Comment convertir csv en tsv dans la CLI
J'ai fait un script pour afficher des pictogrammes
J'ai créé un outil d'estampage automatique du navigateur.
J'ai créé un outil de mot de passe en Python.
J'ai créé un plugin pour générer une table Markdown à partir de csv avec Vim
J'ai écrit un code pour convertir quaternion en angle de graissage de type z-y-x avec Python
J'ai créé un outil pour parcourir automatiquement plusieurs sites avec Selenium (Python)
J'ai fait une sorte d'outil de traitement d'image simple en langage Go.
J'ai créé un outil qui facilite un peu la décompression avec CLI (Python3)
J'ai fait un programme pour vérifier la taille d'un fichier avec Python
Notes J'ai recherché la création d'outils de ligne de commande en Python
J'ai fait un outil pour estimer le temps d'exécution de cron (+ débuts de PyPI)
J'ai créé un outil utile pour Digital Ocean
Convertissez des PDF en images en masse avec Python
Je veux créer une fenêtre avec Python
J'ai créé un outil pour informer Slack des événements Connpass et en ai fait Terraform
Je viens de créer un outil pour afficher facilement les données sous forme de graphique par opération GUI
J'ai créé une commande appdo pour exécuter des commandes dans le contexte de l'application
J'ai créé un outil de collecte de configuration de routeur Config Collecor
J'ai créé un outil pour générer du Markdown à partir du fichier JSON Scrapbox exporté
J'ai créé un outil pour sauvegarder automatiquement les métadonnées de l'organisation Salesforce
J'ai essayé d'implémenter ce qui semble être un outil de snipper Windows avec Python
Comment lire des fichiers dans différents répertoires
J'ai créé un programme cryptographique César en Python.
J'ai créé un outil en Python qui clique avec le bouton droit sur un fichier Excel et le divise en fichiers pour chaque feuille.
Je ne peux pas dormir tant que je n'ai pas construit un serveur !! (Introduction au serveur Python faite en un jour)
Je veux facilement implémenter le délai d'expiration en python
Création de l'outil de gestion des utilisateurs Let's Chat
Je veux faire la transition avec un bouton sur le ballon
J'ai fait une bibliothèque pour bien séparer les phrases japonaises
Je veux écrire en Python! (2) Écrivons un test
J'ai créé un outil de nettoyage pour Google Container Registry
J'ai essayé d'implémenter un pseudo pachislot en Python
J'ai créé un module Python pour traduire les commentaires
Convertissez l'image au format .zip en PDF avec Python
Je veux échantillonner au hasard un fichier avec Python