프로젝트 생성
윈도우 10에서, python 3.6.8이 설치되어 있을때 아래와 같이 진행하였음
# 프로젝트 폴더 생성 및 이동
mkdir project
cd project# 가상환경 생성
python -m venv project_env# 가상환경 활성화
project_env\Scripts\activate.bat# 소스코드 폴더 생성
mkdir project_src
그러면 폴더구성은 아래와 같음
project
- project_env
- Include
- Lib
- pyenv.cfg
- Scripts
- project_src
라이브러리 설치
pip install PySide2 PyInstaller
pyqt5를 사용해도 상관은 없지만, 라이센스 문제에서 pyside2가 좀더 자유로움
소스코드
cd project_src
여기에서 main.py
파일을 생성
from PySide2 import QtWidgetsimport sysclass MainWindow(QtWidgets.QMainWindow): def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs) self.setWindowTitle("Hello World")
l = QtWidgets.QLabel("My simple app.")
l.setMargin(10)
self.setCentralWidget(l)
self.show()if __name__ == '__main__':
app = QtWidgets.QApplication(sys.argv)
w = MainWindow()
app.exec_()
폴더 구성은 아래와 같이 된다.
project
- project_env
- project_src
- main.py
아래처럼 실행해보면
python main.py
패키징
pyinstaller main.py
폴더 구성은 아래와 같이 된다.
project
- project_env
- project_src
- build
- dist
- main.py
- main.spec
build
, dist
, main.spec
이 추가된 것을 알 수 있다.
여기서 dist\main\main.exe
를 실행해보면
커맨드창과 앱이 같이 실행된다.
- build폴더는 디버그를 하는 목적이 아니라면 신경 쓸 필요가 없다
앱 수정
main.spec
파일을 수정하여 몇가지 수정해 줄 수 있다.
# -*- mode: python ; coding: utf-8 -*-block_cipher = Nonea = Analysis(['main.py'],
pathex=['C:\\Users\\Jay\\Desktop\\project\\project_src'],
binaries=[],
datas=[],
hiddenimports=[],
hookspath=[],
runtime_hooks=[],
excludes=[],
win_no_prefer_redirects=False,
win_private_assemblies=False,
cipher=block_cipher,
noarchive=False)
pyz = PYZ(a.pure, a.zipped_data,
cipher=block_cipher)
exe = EXE(pyz,
a.scripts,
[],
exclude_binaries=True,
name='main',
debug=False,
bootloader_ignore_signals=False,
strip=False,
upx=True,
console=True )
coll = COLLECT(exe,
a.binaries,
a.zipfiles,
a.datas,
strip=False,
upx=True,
upx_exclude=[],
name='main')
1. spec파일을 이용하여 앱 이름 바꾸기 및 콘솔창 없애기
위 main.spec
파일의 exe
와 coll
에서
name='main'
→ name='app'
console=True
→console=False
로 수정해주고
pyinstaller main.spec
아까 만든 앱이 지워진다는 경고가 뜨면 가볍게 무시하고 y
WARNING: The output directory "C:\Users\Jay\Desktop\project\project_src\dist\main" and ALL ITS CONTENTS will be REMOVED! Continue? (y/N)y
그러면 dist\main\app.exe
로 exe파일이름이 수정되고, 실행시 콘솔창이 안뜨는 것을 볼 수 있다.
2. spec파일 없이 이름 바꾸기 및 콘솔창 없애기
파이인스톨러에서 사용할 수 있는 플래그 리스트는 아래와 같은데
usage: pyinstaller [-h] [-v] [-D] [-F] [--specpath DIR] [-n NAME]
[--add-data <SRC;DEST or SRC:DEST>]
[--add-binary <SRC;DEST or SRC:DEST>] [-p DIR]
[--hidden-import MODULENAME]
[--additional-hooks-dir HOOKSPATH]
[--runtime-hook RUNTIME_HOOKS] [--exclude-module EXCLUDES]
[--key KEY] [-d {all,imports,bootloader,noarchive}] [-s]
[--noupx] [--upx-exclude FILE] [-c] [-w]
[-i <FILE.ico or FILE.exe,ID or FILE.icns>]
[--version-file FILE] [-m <FILE or XML>] [-r RESOURCE]
[--uac-admin] [--uac-uiaccess] [--win-private-assemblies]
[--win-no-prefer-redirects]
[--osx-bundle-identifier BUNDLE_IDENTIFIER]
[--runtime-tmpdir PATH] [--bootloader-ignore-signals]
[--distpath DIR] [--workpath WORKPATH] [-y]
[--upx-dir UPX_DIR] [-a] [--clean] [--log-level LEVEL]
scriptname [scriptname ...]
디테일을 확인하고 싶으면
pyinstaller -h
콘솔창에 아래처럼 플래그를 넣어서 수정할 수도 있다.
-n, --name
-w, --windowed, --noconsole
pyinstaller -n "app" -w main.py
단일 파일 패키징
-F, --onefile
pyinstaller -F -n "app" -w main.py
이렇게하면 단일 app.exe
파일이 생성되는 것을 볼 수 있다.
아이콘 추가하기
1. App desktop icon만들기
.ico
파일을 만들면 아이콘을 추가할 수 있다.
일단 예시로 위 링크에서 그림을 project_src
에 burning.ico
로 저장하고
-i, --icon
pyinstaller -i="burning.ico" --add-data="burning.ico;." -F -n="app" -w main.py
아이콘이 딸린 app.exe
가 생성된다.
하지만 앱을 실행한다고해서 앱 자체의 아이콘이 추가되는 것은 아니다.
2. App 아이콘 만들기
이를 위해 아래처럼
QtGui
를 import 해주고 app.setWindowIcon(QtGui.QIcon(‘burning.ico’))
를
추가한 뒤 다시 패키징을 해보자.
from PySide2 import QtWidgets, QtGui
import sysclass MainWindow(QtWidgets.QMainWindow): def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs) self.setWindowTitle("Hello World")
l = QtWidgets.QLabel("My simple app.")
l.setMargin(10)
self.setCentralWidget(l) self.show()if __name__ == '__main__':
app = QtWidgets.QApplication(sys.argv)
app.setWindowIcon(QtGui.QIcon('burning.ico'))
w = MainWindow()
app.exec_()
위 코드를 main.py에 저장 후 커맨드창에서 아래를 실행
pyinstaller -i="burning.ico" --add-data="burning.ico;." -n="app" -w main.py
이렇게 한뒤 dist\app.exe
를 실행하면
아이콘이 추가된 것을 볼수 있다
주의할 점은 -F
one file 플래그가 아닌경우에는 이것이 동작하지만
one file플래그를 반영하면 아이콘이 적용되지 않는다.
One file로 만들때 아이콘 반영하기
위 현상이 발생하는 이유는 one file로 패키징을 할 경우에는 이미지(아이콘) 파일이 지정해준 경로에 존재하지 않기 때문이다.
이러한 문제점을 해결하기 위한 방법은 두가지 방법이 있다.
1. resources.py 를 직접 만들기
방법은 간단한데, resources.py
를 아래와 같이 만들어주고
import os
root = os.path.dirname(__file__)
icon = os.path.join(root, 'burning.ico')
main.py
에 import resources
를 해준다
그리고 app.setWindowIcon(QtGui.QIcon(resources.icon))
로 수정해줌
from PySide2 import QtWidgets, QtGui
import sys
import resourcesclass MainWindow(QtWidgets.QMainWindow): def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs) self.setWindowTitle("Hello World")
l = QtWidgets.QLabel("My simple app.")
l.setMargin(10)
self.setCentralWidget(l) self.show()if __name__ == '__main__':
app = QtWidgets.QApplication(sys.argv)
app.setWindowIcon(QtGui.QIcon(resources.icon))
w = MainWindow()
app.exec_()
이렇게 해주면 -F, --one-file
플래그를 붙이건 안붙이건 icon이 잘 동작한다.
# without flag
pyinstaller -i="burning.ico" --add-data="burning.ico;." -n="app" -w main.py# with flag
pyinstaller -i="burning.ico" --add-data="burning.ico;." -n="app" -w -F main.py
2. resources.py 를 resource.qrc를 통해 만들기
이 기본적으로 위 방법과 비슷하긴 하지만 조금 더 복잡한데, qt designer와 호환되는 방법이므로 이 방법을 따르는 편을 권장한다.
qrc 파일은 xml 과 다르지 않은데,
<!DOCTYPE RCC>
<RCC version="1.0">
<qresource prefix="icons">
<file alias="burning.ico">burning.ico</file>
</qresource>
</RCC>
굵은 볼드체부분만 원하는대로 수정하여 resource.qrc
파일로 저장 후
pyside2-rcc resources.qrc -o resources.py
명령을 프롬프트에 입력하여 resource.py
를 생성한 후 main.py
에서 아래와 같이 import resources
로 수정해주고
app.setWindowIcon(QtGui.QIcon(‘:/icons/burning.ico’))
로 아이콘 위치를정해준다.
from PySide2 import QtWidgets, QtGui
import sys
import resourcesclass MainWindow(QtWidgets.QMainWindow):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs) self.setWindowTitle("Hello World")
l = QtWidgets.QLabel("My simple app.")
l.setMargin(10)
self.setCentralWidget(l) self.show()if __name__ == '__main__':
app = QtWidgets.QApplication(sys.argv)
app.setWindowIcon(QtGui.QIcon(':/icons/burning.ico'))
w = MainWindow()
app.exec_()
이 후 아래 명령어를 프롬프트에서 실행한다.
이렇게 하면--add-data
플래그 또는 .spec
에 이미지 파일을 추가하지 않아도 동작하게된다.
# without flag
pyinstaller -i="burning.ico" -n="app" -w main.py# with flag
pyinstaller -i="burning.ico" -n="app" -w -F main.py
정리하며
1. 환경설정
mkdir project
cd project
python -m venv project_env
project_env\Scripts\activate.bat
mkdir project_src
pip install PySide2 PyInstaller
cd project_src
2. 정말 가볍게 앱을 패키징해서 만들고싶다면 (icon같은것은 생략)
여기서 main.py
from PySide2 import QtWidgets
import sysclass MainWindow(QtWidgets.QMainWindow):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.setWindowTitle("Hello World")
l = QtWidgets.QLabel("My simple app.")
l.setMargin(10)
self.setCentralWidget(l)
self.show()if __name__ == '__main__':
app = QtWidgets.QApplication(sys.argv)
w = MainWindow()
app.exec_()
패키징-n=”app” -w -F
이 플래그는 각각 앱이름, 프롬프트창 없애기, 단일파일 플래그 옵션이므로 자유롭게 생략
pyinstaller -n="app" -w -F main.py
3. 체계적으로 앱을 구성하고 싶다면 (resources.py 생성)
ex) 아이콘추가
0. .ico
파일 준비 여기선 burning.ico
resources.qrc
생성
<!DOCTYPE RCC>
<RCC version="1.0">
<qresource prefix="icons">
<file alias="burning.ico">burning.ico</file>
</qresource>
</RCC>
2. resources.py
생성
pyside2-rcc resources.qrc -o resources.py
3. main.py
수정
from PySide2 import QtWidgets, QtGui
import sys
import resourcesclass MainWindow(QtWidgets.QMainWindow):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs) self.setWindowTitle("Hello World")
l = QtWidgets.QLabel("My simple app.")
l.setMargin(10)
self.setCentralWidget(l) self.show()if __name__ == '__main__':
app = QtWidgets.QApplication(sys.argv)
app.setWindowIcon(QtGui.QIcon(':/icons/burning.ico'))
w = MainWindow()
app.exec_()
4. 패키징
-i -n -w -F
는 각각 .exe 의 아이콘 파일 위치, -n 앱이름, -w 콘솔창 없애기, -F는 onefile 플래그.
# with flag
pyinstaller -i="burning.ico" -n="app" -w -F main.py