様々なピットをスキップしてpythonプログラムをpyinstallerでexeにパッケージ化するにはどうすればいいですか?

7611 ワード

前に文章を書いたことがありますが、seleniumで文ファンのページの箴言を登る方法と、これらの文を画像に書く方法を議論して、画像をデスクトップの背景に設定して、タイミングを決めて更新して、興味があれば見てください.
この文章では、以上のpythonプログラムをpyinstallerでexeにパッケージ化し、他人に共有しやすくし、嫉妬や憎しみをうらやましがらせる方法について議論します.
まずpythonプログラムをexeに変換し、よく使われるのはpyinstaller、py 2 exe、nuitkaです.最後のnuitkaはpythonコードをc++コードに変換してからexeファイルをコンパイルして生成し、実行効率が大幅に向上します.py 2 exeはプラットフォーム間で悪く、単一のexeファイルにパッケージできないと言われています.だからネット上の世論に基づいてpyinstallerを重点的に研究して、本文の重点と難点はパッケージタイミング実行モジュールapschedulerの時に多くの問題に出会って、同時にどのように使った画像、フォント、テキストなどのデータファイルをexeにパッケージします.
ソースプログラムは前のリンクを見てください.パッケージ化の過程で各モジュールのインポートの問題に遭遇しました.ここにはテクニックがあります.生成後のexeファイルが直接ダブルクリックして実行されると、エラーが発生した後、フラッシュして、エラー情報を見極めることができません.ソースコードにinput()を入れる人がいますが、実際にエラーが発生したときはinputを実行していないので、フラッシュしました.私は最初とても愚かで、windowsのスクリーンショットでその驚きの一瞥を残そうとしたが、何度も試しても成功しなかった.結局、これは目が速くて、自動スクリーンショットのスクリプトを書きたいほどだった.幸いなことに、後で知恵を絞って、もとはexeもcmdの中で直接ファイル名をたたいて運行することができて、このように間違いがあっても、雁が音を残して、跡があります.
さて、モジュールエラーの問題について、最初はapschedulerのhookファイルが少なくなったと思っていたので、ネット上でapschedulerのhookファイル(ページが長いので、自習して探しています)を見つけました.pyinstaller-F--additional-hooks-dir add_でhook set_wall_paper.pyはパッケージ化されていますが、生成されたexeファイルは依然としてエラーを報告しています.No trigger by the name「interval」was foundは、ネット上でsetuptoolsをアップグレードすると言っていますが、アップグレード後は役に立たないです.そこで探し続けると、すぐにIntervalTriggerというモジュールを導入し、プログラムヘッダに参加する必要があるという説があります.
from apscheduler.triggers import interval

実行後にエラーが発生しました:No module named'apscheduler.triggers.interval'は、グーグルを続け、stactoverflow上の解決方法を発見した.
解決策に従ってやっと変更され、手動で作成する必要があります.
trigger_interval = IntervalTrigger(seconds=60)
scheduler.add_job(set_wallpaper, args=(pic_files, poet_files, fonts_dir), 
                  trigger=trigger_interval)

以上、やっと様々なグーグルでapschedulerモジュールの問題を解決しました.次に、フォント、ピクチャ、jsonデータをexeファイルにパッケージしたいと思っています.公式サイトの紹介に従って、コマンドラインにパラメータを追加したり、specファイルを修正したりすればいいです.specファイルを生成するにはpyi-makespec , pyinstaller 。 exe pyinstaller -F set_wall_paper.spec 。でspecファイルを修正するのがより直感的で、簡単で、メモ帳で開きます.以下は修正後のspecファイルです.
# -*- mode: python -*-

block_cipher = None


a = Analysis(['set_poet_wallpaper.py'],
             pathex=['D:\\       \\PoetWallPaper'],
             binaries=[],
             datas=[('bgpics/*.jpg', 'bgpics'),
                    ('mottos.json', '.'),
                    ('fonts/*.ttf', 'fonts')],
             hiddenimports=[],
             hookspath=[],
             runtime_hooks=[],
             excludes=[],
             win_no_prefer_redirects=False,
             win_private_assemblies=False,
             cipher=block_cipher)
pyz = PYZ(a.pure, a.zipped_data,
             cipher=block_cipher)
exe = EXE(pyz,
          a.scripts,
          a.binaries,
          a.zipfiles,
          a.datas,
          name='set_poet_wallpaper',
          debug=False,
          strip=False,
          upx=True,
          runtime_tmpdir=None,
          console=True )

ここでdatas=[....]の内容は追加のデータファイルで、tupleの前項はソースファイルのパスで、後項はファイルがexe bundleの中の相対的なパスで、うん、今は簡単で、問題はプログラムの中でどのようにbundleの中のファイルを使って、どのようにソースプログラムを変更しないで、それではプログラムが読むたびにやはりソースファイルで、exeファイルとそのフォルダを一緒に置かなければならないことです.これは私の目的ではありません.私はexeがどこへ行っても一人で実行できるようにしたいと思っています.他の人に頼らないで、私と同じです.
どうしよう?公式サイトからヒントが出てきて、何度も繰り返し見てやっと分かりました.Googleがありますが、基本的には公式サイトの言葉ですが、この例はいいですね.公式サイトには、ファイルがfrozenとfrozenでないときにいくつかのパラメータがそれぞれどのようなパスであるかを示す例があります.自分でテイクアウトしてみたら、納得.exeが実行するときにtempフォルダが作成され、パッケージされたデータの親ディレクトリがこのtempフォルダであり、公式サイトの例によって親ディレクトリのpathが得られ、os.path.joinは同じでいいので、外部からexeにパッケージされたデータファイルはすべてjoinしなければならないことに注意してください.最終的なコードは以下の通りで、興味のある学生は前のものと比較することができます.
# -*- coding: utf-8 -*-
"""
Created on Wed Aug 15 11:30:56 2018

@author: xiaozhen
"""

import os, json, sys
from PIL import Image, ImageFont, ImageDraw
import win32api
import win32con
import win32gui
import random
from apscheduler.schedulers.blocking import BlockingScheduler
from apscheduler.triggers.interval import IntervalTrigger


def reformat(string):
    string_lst = string.splitlines()
    format_string = []
    for line in string_lst:
        for i in range(len(line)//25+1):
            format_string.append(line[i*25:25*(i+1)])
    return '
'.join(format_string) def set_wallpaper_from_bmp(bmp_path): # reg_key = win32api.RegOpenKeyEx( win32con.HKEY_CURRENT_USER, "Control Panel\\Desktop", 0, win32con.KEY_SET_VALUE) # :2 ,0 ,6 ,10 ,0 win32api.RegSetValueEx(reg_key, "WallpaperStyle", 0, win32con.REG_SZ, "2") # :1 , 0 win32api.RegSetValueEx(reg_key, "TileWallpaper", 0, win32con.REG_SZ, "0") # win32gui.SystemParametersInfo( win32con.SPI_SETDESKWALLPAPER, bmp_path, win32con.SPIF_SENDWININICHANGE) def random_poems(poet_files): poet_file = random.choice(poet_files) with open(poet_file, 'r', encoding='utf-8') as json_file: poems = json.load(json_file) # preferred_poets = (' ', ' ', ' ') # for i in range(1000): # poem = random.choice(poems) # if poem['author'] in preferred_poets: # break poem = random.choice(poems) poem_content = poem['paragraphs'][0] poem['paragraphs'] = [reformat(poem_content)] poem_string = '
'.join([poem['title'], poem['author']] + poem['paragraphs']) return poem_string def set_wallpaper(img_files, poem_files, fonts_dir): # bmp , img_path = random.choice(img_files) img_dir = os.path.dirname(img_path) bmpImage = Image.open(img_path) bmpImage = bmpImage.resize((1920, 1080), Image.ANTIALIAS) draw = ImageDraw.Draw(bmpImage) font = random.choice([os.path.join(fonts_dir, file) for file in os.listdir(fonts_dir)]) print(font) fnt = ImageFont.truetype(font, 40) poem_str = random_poems(poem_files) print(poem_str) width, height = bmpImage.size draw.multiline_text((width/4, height/5), poem_str, fill='#000000', font=fnt, anchor='center', spacing=10, align="center") new_bmp_path = os.path.join(img_dir, 'wallpaper.bmp') bmpImage.save(new_bmp_path, "BMP") set_wallpaper_from_bmp(os.path.abspath(new_bmp_path)) if __name__ == '__main__': if getattr(sys, 'frozen', False): # we are running in a bundle bundle_dir = sys._MEIPASS else: # we are running in a normal Python environment bundle_dir = os.path.dirname(os.path.abspath(__file__)) pic_path = os.path.join(bundle_dir, 'bgpics') pic_files = [os.path.join(pic_path, file) for file in os.listdir(pic_path) if 'wallpaper' not in file] poet_files = [os.path.join(bundle_dir, 'mottos.json')] fonts_dir = os.path.join(bundle_dir, 'fonts') set_wallpaper(pic_files, poet_files, fonts_dir) scheduler = BlockingScheduler() trigger_interval = IntervalTrigger(seconds=60) scheduler.add_job(set_wallpaper, args=(pic_files, poet_files, fonts_dir), trigger=trigger_interval) # # scheduler.add_job(set_wallpaper, args=(pic_files, poet_files,), # trigger='interval', seconds=60) # scheduler.start()

最後にパッケージされたexeファイル33 M程度は、20 M以上のフォント、ピクチャファイルが含まれているので、総じて納得できます.もちろんtkinterでインタフェースを作ることもできますが、間隔を設定したり、背景画像やフォントを増やしたりして、暇があれば話しましょう.