py packaging tool

Posted by spitfire_esquive on Tue, 04 Jan 2022 00:21:43 +0100

Library address:

auto-py-to-exe

https://pypi.org/project/auto-py-to-exe/

Gooey

https://pypi.org/project/Gooey/

Why introduce these two libraries?

  1. Directly throw the code to others: various configuration environments can only be executed with
  2. Command line execution: ugly
  3. Packaging complexity
  4. Visual interface programming is complex

auto-py-to-exe

Auto py to exe is a graphical tool for packaging Python programs into executable files. In the past, the pyinstaller library was used for packaging. It is difficult to remember the functions of various parameters. Auto py to exe is based on pyinstaller. Compared with pyinstaller, auto py to exe has more GUI interface and is more simple and convenient to use.

install

# py version 3.6-3.10
pip install auto-py-to-exe

start-up

auto-py-to-exe

Introduction to common options

  • Single file

    • Single file: there is only one packaged executable file, which packages all dependencies into one file

    • Single directory: the packaged executable file is in the output directory, including dependent files and libraries

  • console window

    • Console: all interactions are displayed on the console
    • Window: this option can only be used when the form interface is written. The console is hidden and only the written form is displayed
  • Icon

    The icon of the executable file needs to be in ico format

  • Additional documents

    • Add file: the script depends on an external file, that is, add a single file as a dependency
    • Add Directory: the script depends on external libraries (or multiple files), and the corresponding directory is added as the dependency
  • Advanced options

    • general option
      • Name: the name of the execution file
      • clean: whether to clear the cache before packaging
    • Bundle options
      • Add binary: if you need an executable file, you can add it
      • paths: search the directory of the dependent library * (generally not used)*
      • key: whether the compiled temporary file needs to be encrypted. If it is core code, encryption is recommended.
    • windows specific options
      • Version file: version file, generally not used
  • set up

    • Output path: used to generate the storage place of packaged files
  • current command

    Commands used to show pyinstaller packaging

  • output

    Show the packaging process

practice

function

Write a simple interactive script to input a number and output the value of this number plus 3.

Script

  • Script structure

  • File content

# Add3.py

def add3(x):
    return x + 3
# run_it.py

import time
from Fun.Add3 import add3

if __name__ == '__main__':
    n = input('Please enter a number:')
    print(add3(int(n)))
    time.sleep(10)  # Avoid exiting directly after execution. You can't see anything

Take a look at the normal console interaction effect:

collect files

Generate file

If you don't want to generate a directory, you can select to generate a file. Select single file in single file.

Gooey

A tool library for quickly building visual pages, with encapsulated components, can generate a tool with interface with one line of command.

install

# py 2.7 3.x
pip install Gooey

Rewrite script

# run_it.py

from Fun.Add3 import add3
from gooey import Gooey, GooeyParser


@Gooey(program_name="The name of the tool")
def main():
    parser = GooeyParser(description="First example!")
    parser.add_argument(
        "x",
        metavar=u'Number of entered:',
        help="Please enter an integer"
    )
    args = parser.parse_args()
    try:
        res = add3(int(args.x))
        print(res)
    except TypeError as e:
        print('Oh, wrong report')


if __name__ == '__main__':
    main()

function

Component introduction

assembly– a –
FileChooserFile selector
MultiFileChooserFile selector
DirChooserDirectory selector
MultiDirChooserDirectory multiplexer
DateChooserDate Pickers
TextFieldText input box
DropdownRadio
RadioGroupcheck box

Global configuration

parameterintroduce
advancedToggles whether to display all settings or just a simplified version
show_configSkip all configurations and run the program now
languageSpecifies which language pack to read from the gooey/languages directory
program_nameThe program name displayed in the GUI window. Sys. Is displayed by default argv[0].
program_descriptionDescriptive text displayed in the top bar of the Settings window. The default value is obtained from ArgumentParser.
default_sizeDefault window size, (600400)
required_colsSet the number of required parameter lines.
optional_colsSets the number of optional parameter lines.
dump_build_configSave the settings in JSON format on the hard disk for editing / reuse.
richtext_controlsTurn on / off console support for terminal control sequence (limited support for font thickness and color)

Support multiple structural layouts

practice

Picture angle correction visualizer

directory structure

file

# main.py
import os
from fun import *

from gooey import Gooey, GooeyParser


@Gooey(
    richtext_controls=True,  # Turn on terminal color support
    program_name="Aibiao xx tool",  # Program name
    encoding="utf-8",  # Set the encoding format and encounter problems when packaging
    progress_regex=r"^progress: (\d+)%$",  # Regular, used to pattern runtime progress information
    menu=[{
        'name': 'file',
        'items': [{
            'type': 'AboutDialog',
            'menuTitle': 'about',
            'name': 'Image rotation angle processing tool',
            'description': 'The picture used to process the marked page looks normal, but it is found that the position of the picture and the picture frame is inconsistent after cutting the picture, such as inversion, rotation, etc',
            'developer': 'wjlv4@iflytek.com',
        }, {
            'type': 'Link',
            'menuTitle': 'Visit the home page',
            'url': 'https://ainxx.iflyxxx.com/'
        }]
    }, {
        'name': 'help',
        'items': [{
            'type': 'AboutDialog',
            'menuTitle': 'Help documentation',
            'name': 'Image rotation angle processing help',
            'description': 'Self service processing steps for problems such as inversion and rotation in the preview picture after marking the page frame:\n1. Open the browser debugging window(F12)\n2. click Network(network)\n3. Click in the label page box√number\n4. Found in network in the debug window ocr?url=After the request, click the request with the mouse\n5. Copy from the address on the right/test/xxxx/xxxx.jpg To the tool input field',
        }]
    }]
)
def main():
    parser = GooeyParser(description="Platform auxiliary tools: picture rotation angle correction, warehousing test question visualization ...")

    subs = parser.add_subparsers(help='commands', dest='command')

    draw_pic = subs.add_parser('Warehousing picture visualization')
    pic_fix = subs.add_parser('Picture rotation angle correction')
    pic_fix.add_argument('source_page_url',
                         metavar='Picture path',
                         help='Please enter ocr The path of the picture is shown in/test start.jpg ending',
                         widget='TextField')
    pic_fix.add_argument(
        "rotate",
        metavar=u'Rotation angle:',
        help="The angle at which the picture needs to be rotated(anti-clockwise)",
        # option
        choices=['90', '180', '270', '0'],
        # Default value
        default='180',
        # drop-down menu 
        widget='Dropdown',
        # data verification 
        gooey_options={
            'validator': {
                'test': "user_input in ['0','90', '180', '270']",
                'message': "Only 90, 180, 270, 0 can be entered]"
            }}
    )


    draw_pic.add_argument(
        "page_path",
        metavar=u'Please enter the directory to be processed',
        help="page.json and topic.json Directory where",
        # drop-down menu 
        widget='DirChooser'
    )

    args = parser.parse_args()
    if getattr(args, 'page_path', None):
        run(os.path.join(args.page_path, 'page.json'))
    elif getattr(args, 'source_page_url', None):
        deal_pic(args.source_page_url, int(args.rotate))


# Process the parameters collected by the interface
# ......

if __name__ == '__main__':
    main()

# zzj_rotate_pic.py

# coding:utf8
import sys

import requests
from PIL import Image
from fun.Log import *


def resource_path(relative_path):
    """ Get absolute path to resource, works for dev and for PyInstaller """
    try:
        # PyInstaller creates a temp folder and stores path in _MEIPASS
        base_path = sys._MEIPASS
    except Exception:
        base_path = os.path.abspath(".")

    return os.path.join(base_path, relative_path)


def download_src(source_page_url, out_file_path='before.jpg'):
    """
    Download original
    :param out_file_path:Save path
    :param source_page_url: Original drawing url
    :return:
    """

    logger.info(f' download {source_page_url}')
    print('progress: 15%')
    if not source_page_url.startswith('http'):
        url = r"http://aixxxe-mxxxxe.ifxxdxxxb.com/api/ds/api/daxxxet/file"
        source_page_url = url + '?path=' + source_page_url
    # Download and store the original drawing
    res = requests.get(source_page_url, stream=True)
    with open(out_file_path, 'wb') as f_w:
        f_w.write(res.content)
    print('progress: 30%')


def rotate_pic(rotate, pic_path='before.jpg'):
    logger.info('rotate pic ...')
    print('progress: 45%')
    img = Image.open(pic_path)
    img1 = img.rotate(rotate, expand=True)
    logger.info('save rotated pic ...')
    img1.save('after.jpg')
    print('progress: 60%')


def upload_file(file_path, local_file='after.jpg'):
    url = r"http://axxte-mxxxxe.iflxxxxub.com/api/ds/api/daxxxt/file"
    if not file_path.startswith('http'):
        file_path = url + '?path=' + file_path
    file = open(local_file, 'rb')
    logger.info('upload file ...')
    print('progress: 80%')
    querystring = {"path": file_path.split('=')[-1]}
    response = requests.post(url=url, data=file.read(), headers={'Content-Type': 'text/xml'}, params=querystring)
    if response.status_code == 200 and response.json().get('retcode', None) == '000000':
        logger.info('upload success!')
        print('progress: 100%')
    else:
        logger.error(f'upload failed!')
        print('progress: 0%')


def deal_pic(source_page_url, rotate, save_tmp_file=False):
    """
    Handle pictures with abnormal angle
    :param source_page_url: Original address
    :param rotate: Rotation angle, counterclockwise rotation
    :param save_tmp_file: Save temporary file
    :return:
    """
    try:
        download_src(source_page_url)
        rotate_pic(rotate)
        upload_file(source_page_url)
        if not save_tmp_file:
            os.remove('before.jpg')
            os.remove('after.jpg')
    except Exception as e:
        logger.error(e)




if __name__ == '__main__':
    deal_pic(r'/test/5db9bca31775476cbxxxxx11f018ea9/cf0907bf6e9adxxxxx90f35a278c9e41.jpg',180)

function

Package into executable

Package file too large?

  • Reason: the interpreter used for packaging is the conda environment, which contains third-party libraries not used in the current script

  • Solution: use the new environment to package files. The new environment only installs the libraries required by the script

    • Create a new environment

      I use the virtualenv management environment here to directly create a new environment

      D:\ProgramData\virtualenvs>virtualenv -p "C:\Users\wjlv4\AppData\Local\Programs\Python\Python39-32\python.exe" tmp_env
      created virtual environment CPython3.9.5.final.0-32 in 2517ms
        creator CPython3Windows(dest=D:\ProgramData\virtualenvs\tmp_env, clear=False, no_vcs_ignore=False, global=False)
        seeder FromAppData(download=False, pip=bundle, setuptools=bundle, wheel=bundle, via=copy, app_data_dir=C:\Users\wjlv4\AppData\Local\pypa\virtualenv)
          added seed packages: pip==21.3.1, setuptools==59.4.0, wheel==0.37.0
        activators BashActivator,BatchActivator,FishActivator,NushellActivator,PowerShellActivator,PythonActivator
      
      D:\ProgramData\virtualenvs>
      
      

      Switch the interpreter of the project to the new virtual environment, and you can see that the new virtual environment does not have any external libraries.

    • Execute the script locally, check which dependent packages are missing, and install them separately

      pip install Pillow requests pandas loguru gooey
      
    • In the current virtual environment, use pyinstaller to package * (the command can directly use the command just packaged)*

      After the dependency installation is completed, install pyinstaller in the virtual environment and directly execute the copy command:

      pyinstaller --noconfirm --onedir --windowed --icon "D:/abk.ico" --name "Title of the script" --clean --key "1231a" --add-data "D:/t1/fun;fun/"  "D:/t1/main.py"
      
    • View packaged files

    Significantly reduced.

Topics: Python pyinstaller