0.発端
子供(小学2年生)の冬休みの宿題として
「掛け算九九の各段について、ランダムに並び替えた数字の順で間違えないように暗唱する」
というものがあり、その「ランダムな数字の順」を考えるのが面倒なので、Pythonにやらせてみようと思ったのが事の発端です。
(Qiita様と投稿者の皆々様のエントリには常日頃お世話になっており、何かひとつ投稿してみたいと考え、当エントリを作成しました)
1.整数リストをランダムに入れ替えるスクリプト
最初に作ったのは「1から9までのリスト」をランダムに並び替える関数でした。
作っているうちに欲が出て「リストを違うパターンで並び替えて出力する」仕様も追加しました。
import sys
import random
import copy
def random_list(repeat=1, num=10):
# 1からnumまでのリスト
numbers = list(range(1,num))
# 繰り返し
numbers_list = []
i = 0
while i < repeat:
# ランダマイズ
random.shuffle(numbers)
numbers_list.append(copy.deepcopy(numbers))
# インクリメント
i+=1
return numbers_list
# コマンドライン呼び出し
if __name__ == "__main__":
args = sys.argv # パラメータ引数
repeat = 1 # 繰り返し出力数
num = 10 # 配列の要素数
if(len(args) > 1) :
repeat = int(args[1])
if(len(args) > 2) :
num = int(args[2])
numbers_list = random_list(repeat, num)
for numbers in numbers_list:
print(numbers)
全行バラバラになるはずなのに
行ごとに並びが変わる事を期待し、以下のフロー(抜粋)で実装しました。
numbers = list(range(1,num)) # 1~9までの整数リスト
while i < repeat:
random.shuffle(numbers) # 整数リストをランダムに並び替える
numbers_list.append(numbers) # リストのリストに追加
が、予想に反し、すべての行が同じ並びで出力される事に。
原因は最後の1行で、numbers_list.append(numbers)
でリストを直接appendしても、IDが参照で渡されているだけなので、追加後にnumbers
の順番が再抽選されるとnumbers_list
のすべての行が同様に並び替わった状態になっていました。
解決策としてnumbers
を参照渡しではなくcopy.deepcopy(numbers)
で複製してappendすることで期待通りの結果が得られました。
> Python3 .\RandomList.py 10
[8, 2, 5, 6, 9, 7, 3, 4, 1]
[4, 3, 9, 6, 2, 7, 1, 5, 8]
[8, 1, 2, 4, 9, 5, 3, 6, 7]
[5, 3, 6, 8, 2, 9, 4, 7, 1]
[2, 3, 4, 5, 8, 1, 9, 7, 6]
[1, 8, 4, 3, 6, 7, 9, 2, 5]
[1, 4, 5, 7, 9, 6, 2, 3, 8]
[9, 7, 1, 6, 5, 8, 3, 4, 2]
[7, 4, 6, 1, 5, 3, 8, 9, 2]
[4, 6, 7, 5, 2, 9, 8, 3, 1]
当問題の解決に際し、こちらのエントリを参考にさせていただきました。
2.PySimpleGUIでアプリケーション化
コマンドラインで直接呼び出して数値が出るのも、子供ウケとしては悪くはありませんが、どうせならGUIアプリケーション化するほうが子供も使いやすいかもしれない。
これは学習の好機と考え、「PySimpleGUI」を用いたGUIアプリケーション化を行いました。
import PySimpleGUI as sg
import RandomList as rl
# GUIウインドウテーマ
sg.theme('Python')
# 入力フレーム
frame1 = sg.Frame('入力',
[
[
sg.Text('繰り返し回数'),
sg.InputText(key='-INPUTTEXT-', default_text='10', size='3'),
sg.Text('回')
],
[
sg.Submit(button_text='生成', key='button_create')
]
]
)
# 出力フレーム
frame2 = sg.Frame('出力',
[
[
sg.MLine(size=(18,20), key='-OUTPUT-', disabled=True)
]
]
)
# レイアウト
layout = [
[
frame1
],
[
frame2
]
]
# Window生成
window = sg.Window('ランダム数字リスト生成アプリ', layout, resizable=True)
# GUI表示実行部分
while True:
# ウインドウ表示
event, values = window.read()
# クローズボタン
if event is None:
print('exit')
break
# ボタンが押された場合
if event == 'button_create':
repeat = int(values['-INPUTTEXT-'])
numbers_list = rl.random_list(repeat)
output = ''
if len(numbers_list) > 0:
for numbers in numbers_list:
for number in numbers:
output += str(number) + ' '
output += '\n'
window['-OUTPUT-'].Update(output)
window.close()
概要としては
sg.InputText
、sg.Submit
、sg.MLine
それぞれにkey
をつけて参照可能にした上で、
InputText の値の数だけリストを繰り返し生成しランダムに並び替え、
output
に文字列として整形した上で MLine に出力するという処理を行っています。
PySimpleGUIの使用に際し、以下のエントリを参考にさせて頂きました。
3.PyInstallerでスクリプトのexe化
GUIアプリケーションとしての体裁が整ったところで、今度は単独で動かせるようにexe化を試みました。
PyInstallerを用いることでexe化が可能だと知り、導入しようとしたのですが…
PyInstallerが実行できない?
Windows10のPower Shell上でpipを用いてPyInstallerをインストール。
pip install PyInstaller
他のエントリでも書かれているように、以下のコマンドを実行してもpyinstallerが見つからない。
pyinstaller GuiRandomList.py # pyinstaller.exe が Not Found
PATHを通せばよいのかもとも思ったのですが、ネットの海を漁ってみると
「PyInstallerは仮想環境で実行してexe化する」
という情報にたどり着きました。
仮想環境の構築とモジュールのインストール
pythonのvenvコマンドで、PyInstaller用の仮想環境GuiRandomListEnv
を作成。
python -m venv GuiRandomListEnv
作成された仮想環境を起動。
.\GuiRandomListEnv\Scripts\activate
pipを最新化し、今回使用しているモジュールPySimpleGUI
とPyInstaller
をインストール。
pip install -U pip # pipの最新化
pip install PySimpleGUI
pip install PyInstaller
PyInstaller実行によるexe化
仮にスクリプトGuiRandomList.py
がc:\example\scripts\
に配置されているとして、以下の手順でexe化します。
cd c:\example\scripts\
pyinstaller GuiRandomList.py
上記コマンド実行後、GuiRandomList.py
と同じフォルダ内に、以下のフォルダ・ファイルが生成されます。
build\ # ビルド時の一時ファイルの格納フォルダ
dist\ # 生成された実行ファイルの格納フォルダ
GuiRandomList.spec # ビルド中間ファイル
オプションを設定しなければ、dist\
配下にGuiRandomList.exe
と共に、実行に必要なファイルが吐き出されます。
下記オプションで出力内容を変える事も可能です。
--onefile (-Fでも可) # 出力ファイルを1つにまとめる
--onedir (-Dでも可) # 出力フォルダを1つにまとめる
--noconsole (-wでも可) # 実行時にコンソール画面を表示しない
個人的には「exeファイルひとつ」が「コンソールが開くことなく」実行できて欲しいので、下記オプションで出力しました。短縮版はハイフンの後に複数続けて書く事も可能です。
pyinstaller -wF GuiRandomList.py
実行結果として、dist\
フォルダ下に、以下の実行ファイルが生成されました。
無事起動を確認し「整数リストを並び替えて出力するGUIアプリケーションのexe」が完成しました。
3'. PyInstallerによるMac向けアプリ化
Pythonの仮想環境上でPySimpleGUIとPyInstallerをinstallしてpyinstallerすればいいだけなら、Macでも同様の手順でアプリケーションが作れるのでは?
と思い、GitHub経由でリソースをMacに持ってきて、アプリケーション化を試みました。
仮想環境の構築手順は変わらず。
python -m venv GuiRandomListEnv
仮想環境に入る際のコマンドやフォルダ構成がWindowsのPower Shellとは違いました。
source GuiRandomListEnv¥bin¥activate
入ってしまえばあとは変わらず。
pip -U install pip
pip install PySimpleGUI
pip install PyInstaller
pyinstaller -wF GuiRandomList.py
これで、Windows同様dist¥
フォルダ下にアプリケーションGuiRandomList
が生成されました。
ダブルクリックで起動も確認出来ました。