Pythonのお勉強:PythonのGUIプログラムを “cx_Freeze” を使用してWindowsアプリに変換する

cx_Freeze

cx_Freeze」を使用するとPythonのGUIプログラムをWindowsのexeファイルへ変換して、スタンドアロンのWindowsアプリとして使用することが出来るようになります。前の記事「Pythonのお勉強:PySimpleGUIを使ってPythonのGUIプログラムを作ってみた」で作成したGUIサンプルプログラム「PSG_Pdf_DirList.py」をSakura.Oh!Happy.JPの記事「VsCodeでPython 3.11の開発環境をつくる(Windows10)」で作製した環境で、cx_Freezeeを使用してスタンドアロンのWindowsアプリにしてみました。

cx_Freezeeのインストール

cx_Freezeは、Windows10のPowerShellを使用して、以下のpipコマンドで簡単にインストールできました。

> pip install --upgrade cx_Freeze
:
Successfully installed cx-Logging-3.1.0 cx_Freeze-6.14.0 lief-0.12.3

セットアップファイルの作成

Windows10のPowerShellで「cxfreeze-quickstart」コマンドを実行すると、セットアップファイル作成用のスクリプトが起動します。スクリプトの質問に答えていくとカレントフォルダに、セットアップファイル「setup.py」が作成されます。以下に「PSG_Pdf_DirList.py」用のセットアップファイルを作成した際のログを記載します。「Project name:」「Python file to make executable from:」「(C)onsole application, (G)UI application, or (S)ervice [C]:」は入力回答し、他の項目はリターンキーのみ(デフォルト値)で回答しました。

PS > cxfreeze-quickstart
Project name: PSG_Pdf_DirList <- (入力)
Version [1.0]:
Description:
Python file to make executable from: PSG_Pdf_DirList.py <- (入力)
Executable file name [PSG_Pdf_DirList]:
(C)onsole application, (G)UI application, or (S)ervice [C]: G <- (入力)
Save setup script to [setup.py]:

Setup script written to setup.py; run it as:
    python setup.py build
Run this now [n]?

「cxfreeze-quickstart」で作成された「setup.py」

from cx_Freeze import setup, Executable

# Dependencies are automatically detected, but it might need
# fine tuning.
build_options = {'packages': [], 'excludes': []}

import sys
base = 'Win32GUI' if sys.platform=='win32' else None

executables = [
    Executable('PSG_Pdf_DirList.py', base=base)
]

setup(name='PSG_Pdf_DirList',
      version = '1.0',
      description = '',
      options = {'build_exe': build_options},
      executables = executables)

Windowsアプリの作成と動作確認

以下の手順で「PSG_Pdf_DirList.py」のWindowsアプリを作成しました。

  • 「PSG_Pdf_DirList.py」と「setup.py」を1つのフォルダ(任意)に入れました。
  • PowerShellで上記のフォルダに移動して、以下のコマンドでWindowsアプリを作成しました。
    > python setup.py build
    >
    
  • フォルダ(任意)の下に/build/というフォルダが作成され、その直下の/exe.win-amd64-3.11/フォルダ内に「PSG_Pdf_DirList.exe」というEXEファイルが出来ていました。
    /build/
    └/exe.win-amd64-3.11/
     ├ frozen_application_license.txt
     ├ PSG_Pdf_DirList.exe
     ├ python3.dll
     ├ python311.dll
     └─/lib/
    
  • /exe.win-amd64-3.11/フォルダ以下のファイル(/lib/フォルダを含む)を全てUSBにコピーし、USBの「PSG_Pdf_DirList.exe」を起動して動作確認を行ったところ、問題無くWindowsアプリとして使用することが出来ました。「PSG_Pdf_DirList.exe」のディスク上のサイズは「32KB」でしたが、アプリ全体のファイルサイズは「141M」、/lib/フォルダ全体のディスク上のサイズは「130M」でした。

Windowsアプリに変換したPythonのGUIプログラム(「PSG_Pdf_DirList.py」)

上記の手順で”cx_Freeze” を使用してWindowsアプリに変換したPythonのGUIプログラムを以下に記載します。

  • 「PSG_Pdf_DirList.py」
    import PySimpleGUI as sg
    import sys
    import os
    import os.path
    import fnmatch
    import openpyxl
    from openpyxl.styles.alignment import Alignment
    from openpyxl.styles import PatternFill
    from openpyxl.styles import Font
    
    def event_Submit(root_dir):
        # 変数定義
        search_dir = root_dir
        dirname_lst = list()    # ディレクトリ名の格納用リスト
        pdfname_lst = list()    # PDFファイル名の格納用リスト
        lnkname_lst = list()    # HYPERLINK用ファイル名の格納用リスト
        maxlen_dirname = 0      # ディレクトリ名の最大バイト数格納用変数
        maxlen_pdfname = 0      # PDFファイル名の最大バイト数格納用変数
        maxlen_lnkname = 0      # HYPERLINK用ファイル名の最大バイト数格納用変数
    
        # 新規ワークブックの作成
        wb = openpyxl.Workbook()
        ws = wb.active
        ws.title = "pdf_list"
    
        # PDFファイル名とHYPERLINK用ファイル名をリストに格納
        for dirpath, dirs, files in os.walk(search_dir):
            for fname in files:
                if fnmatch.fnmatch(fname, '*.pdf'):
    
                    # ディレクトリ名をリストに格納
                    # print(dirpath)
                    dirname = dirpath
                    dirname_lst.append(dirname)
                    if len(dirname.encode()) &gt; maxlen_dirname:
                        maxlen_dirname = len(dirname.encode())
    
                    # PDFファイル名をリストに格納
                    # print(fname)
                    pdfname = fname
                    pdfname_lst.append(pdfname)
                    if len(pdfname.encode()) &gt; maxlen_pdfname:
                        maxlen_pdfname = len(pdfname.encode())
    
                    # HYPERLINK用ファイル名をリストに格納
                    # print(os.path.join(dirpath, fname))
                    lnkname = os.path.join(dirpath, fname)
                    lnkname_lst.append(lnkname)
                    if len(lnkname.encode()) &gt; maxlen_lnkname:
                        maxlen_lnkname = len(lnkname.encode())
    
        start_row = 1 # EXCEL表示開始列
        start_col = 1 # EXCEL表示開始行
    
        # タイトルセルの名前を設定
        ws.cell(start_col,start_row).value='ディレクトリ名'
        ws.cell(start_col,start_row+1).value='PDFファイル名'
    
        # タイトルセルの表示幅を変更
        ws.column_dimensions['A'].width = maxlen_dirname
        ws.column_dimensions['B'].width = maxlen_pdfname
    
        # タイトルセル('A1':'B1')の背景色を変更 「fil_obj(水色 塗り潰し)」
        fil_obj = PatternFill(fgColor='00ffff' , patternType='solid')
        for rows in ws['A1':'B1']:
            for cell in rows:
                cell.fill = fil_obj
    
        # セルにファイル名を書き込む
    
        for i in range(0,len(pdfname_lst)):
            # 現在の行(cur_col)と列(cur_row)を変数に格納
            cur_col = i+start_col+1
            cur_row = start_row
            # シート'A'列にディレクトリ名を書き込む
            hdir_name = dirname_lst[i]
            hdir_str =  hdir_name
            ws.cell(cur_col,cur_row).value = hdir_name
            ws.cell(cur_col,cur_row).hyperlink = hdir_str
    
            # シート'B'列にPDFファイル名とHYPERLINKを書き込む
            hpdf_name = pdfname_lst[i]
            hlnk_name = lnkname_lst[i]
            hlnk_str = hlnk_name
            ws.cell(cur_col,cur_row+1).value = hpdf_name
            ws.cell(cur_col,cur_row+1).hyperlink = hlnk_str
    
            # ハイパーリンクの書式設定 fnt_obj(色:青 シングルアンダーライン)
            fnt_obj = Font(color='0000FF', u='single')
            ws.cell(cur_col,cur_row).font = fnt_obj
            ws.cell(cur_col,cur_row+1).font = fnt_obj
    
            # シートセル全体の水平方向を左寄せ、高さ方向を中央揃え
            for col in ws:
                for cell in col:
                    cell.alignment = Alignment(horizontal='left',vertical='center')
    
            # シートセルの行高さ変更
            for i in range(len(dirname_lst)+2):
                ws.row_dimensions[i].height = 21
    
        # 作成したファイルをに名前をつけて保存
        save_filename = 'Pdf_DirList.xlsx'
        while True:
            try:
                wb.save(filename = save_filename)
            except PermissionError:
                sg.popup_ok('Excelファイルが開いていますので閉じてください。',title='エラーメッセージ')
            else:
                break;
        wb.close()
    
        yes_no_ans = sg.popup_yes_no('ルートフォルダ \n[ ' + root_dir + ' ]'+'\n にあるPDFファイルのリスト(EXCEL)を作成しました。\n\nプログラムを終了しますか?\n',title='&lt;YES/NO&gt;確認ダイアログ')
        if yes_no_ans == "Yes":
            sys.exit()
    
    # メイン
    def main():
    
        layout = [
          [sg.Text('[1] PDFのルートフォルダを選択', size=(40, 1))],
          [sg.Input(default_text='', size=(50,2)),sg.FolderBrowse()],
          [sg.Text('[2] PDFリストのEXCELファイルを作成', size=(44, 2)),sg.Submit(key="btn_Submit")],
          [sg.Text('[3] プログラムの終了', size=(44, 2)),sg.Cancel(key="btn_Cancel")]]
    
        window = sg.Window('PDFリストのEXCELファイル作成', layout=layout)
    
        while True:
            event, value = window.read()
    
            if event is None:
                break
    
            # keyの文字列を判定してイベント処理を分岐
            elif event == "btn_Submit":
                if not value[0]:
                    sg.popup('最初にPDFのルートフォルダを選択してください.')
                else:
                    root_dir=value[0]
                    event_Submit(root_dir)
    
            elif event == "btn_Cancel":
                break
    
            else:
                continue
    
        window.close()
    
    if __name__ == "__main__":
        main()
    

管理人がPythonの勉強のために購入した本

コメント