1. Home
  2. 프로그래밍/Python
  3. [PyQT] pyinstaller로 윈도우 아이콘, UI, 프로그램 아이콘 전부 exe에 포함시키기

[PyQT] pyinstaller로 윈도우 아이콘, UI, 프로그램 아이콘 전부 exe에 포함시키기

PyQT와 Qt Designer로 아이콘 및 UI를 다루는건 디버깅을 할땐 아무 문제가 되지 않습니다만 pyinstaller로 exe로 패키징 하는 과정에선 문제가 발생할 수 있습니다.

UI와 아이콘 파일의 위치를 못찾는것이죠.

 

pyinstaller에 icon 또는 i 옵션에 아이콘 파일을 제공하면 됩니다만 위 사진처럼 내부 Window에는 적용되지 않음을 알 수 있습니다.

 

setWindowIcon() 이용해 설정하는게 저 위 아이콘인데 pyinstaller 로 패키징을 끝내면 파일을 못찾는 문제가 발생하는 것이죠. 동일하게 ui 파일도 패키징 이후에 exe에 포함되지 않아서 ui를 못찾는다는 것은 곧 창을 못연다는 뜻이고 아예 프로그램 구동이 안되게 됩니다.

 

아이콘의 경우 저렇게 깨진상태라도 실행이 되지만요.

 

UI와 아이콘이 표시 되지 않는 문제를 한꺼번에 해결 하시려면 pyinstaller로 exe를 만들때 필요한 파일을 전부 포함시켜주시면 됩니다.

 

인터넷에 나와있는 일반적인 해법으로는 pyinstaller로 -w -F 옵션을 주고 일단은 exe를 만든다음에 나온 spec 파일을 열어서 파일을 포함시키는 구문을 적고 spec로 한번더 패키징 하는것인데

 

이러면 패키징을 2번 하게 되서 번거롭고 시간도 많이 걸립니다.

제가 알기론 파이썬 자체가 인터프리터 언어라 컴파일이 안되는데 저렇게 exe로 만들 수 있는것은 pyinstaller로 만들때 인터프리터랑 관련 파이썬 파일을 전부 때려박고 인터프리터를 실행시켜서 만드는걸로 알고 있습니다.

 

그래서 pyinstaller로 exe를 만들면 죄다 크고 무거운 것이죠. 특히 포함시켜야 할 라이브러리가 기본적으로 많을때 exe 용량이 기하급수적으로 증가하게 됩니다. 예를 들어서 기본적인 패키지가 대부분 존재하는 anaconda로 pyinstaller로 만들었을때 exe 용량이 가볍게 몇백MB를 상회하는 문제가 발생하게 됩니다.

 

우선 각설하고 해당 문제를 해결하는 Solution을 알려드리겠습니다.

 

def resource_path(relative_path):
    """ Get absolute path to resource, works for dev and for PyInstaller """
    base_path = getattr(sys, '_MEIPASS', os.path.dirname(os.path.abspath(__file__)))
    return os.path.join(base_path, relative_path)

우선 UI파일, 아이콘 파일의 상대경로를 pyinstaller로 포함시켜도 찾아낼 수 있게 변환시켜주는 함수입니다.

소스코드 맨위에 전역으로 아무데나 추가해주세요.

호출만 할 수 있으면 됩니다.

 

from PyQt5 import uic

main_ui = resource_path('main.ui')
Ui_MainWindow = uic.loadUiType(main_ui)[0]  # ui 가져오기

그리고 방금 만든 ui를 추가한 함수를 활용해서 추가해줍니다.

 

지금 현재 실행하는 main.py(메인 실행 소스), main.ui (ui 파일), main.ico(프로그램 아이콘) 은 전부 동일 경로에 있다고 상정하겠습니다.

 

class Form(QMainWindow, Ui_MainWindow):
    def __init__(self):
        super().__init__()
        self.setupUi(self)

        window_ico = resource_path('main.ico')
        self.setWindowIcon(QIcon(window_ico)) #아이콘 설정

그리고 하드코딩을 통해 다시 윈도우 아이콘도 설정해줍니다.

qtDesigner 로 추가하지마시고 추가했더라도 다음 소스코드를 통해 아이콘을 수정해줍니다.

 

# 폼스킨 적용 & 폼오픈
qtmodern.styles.dark(app)
mw = qtmodern.windows.ModernWindow(mainForm)

window_ico = resource_path('main.ico')
mw.setWindowIcon(QIcon(window_ico))  # 아이콘 설정

mw.show()

*추가(2022-02-11)

가끔 별도의 폼스킨을 사용하면 저렇게 MainWindow 클래스에서 아이콘을 적용해도 안될때가 있습니다.

그럴땐 폼스킨 적용 이후의 QApplication 에 아이콘을 적용해주시면 됩니다.

 

이렇게 하면 경로 작업은 완료되었습니다.

이제 pyinstaller로 가서 main.py가 있는곳에 cd를 통해 이동하신 후,

 pyinstaller -w -F -i="main.ico" --add-data="main.ui;./" --add-data="main.ico;./" main.py

다음 명령어를 통해서 exe로 변환시켜줍니다.

-w 는 콘솔없이 윈도우로만 실행, -F는 단일 exe로 만들고, --add-data로 데이터를 추가해주시면 됩니다.

--add-data 의 경우 한번만 가능한줄 알았는데 pyinstaller 레퍼런스에 보시면 여러번 호출해서 여러개 추가할 수 있다고 나와있습니다.

 

(이걸 찾느라 좀 애먹었습니다.)

 

* 파이참으로 프로젝트 폴더에서 가상 환경에 있는 venv 폴더의 pyinstaller 를 이용하려는 경우엔 아래와 같이 경로를 지정합니다.

.\venv\Scripts\pyinstaller.exe -w -F -i="main.ico" --add-data="main.ui;./" --add-data="main.ico;./" main.py

 

 

 

위처럼 하시면 main.spec 을 바꾸고 2번 작업하실필요 없이 add-file에 의해 자동으로 spec에 파일 추가가 되게 됩니다.

 

그렇게 해서 만들어진 프로그램입니다. 보시면 아이콘도 잘 나오고 관련 ui 파일이나 아이콘 파일이 없는데도 아이콘이 전부 잘 나오게 되었습니다.

 

추가로 속도 향상을 원하시면 -F 옵션을 제거하고 파일을 만드시면 됩니다.

 

확인해보니 OneFile로 만드는 기능은 통째로 패킹을 시켜서 만드는거 같습니다. 실행할때 CPU가 압축을 풀어서 실행해야 하므로 압축을 푸는 시간이 걸리는것 같습니다.

 

파일 한개로 만들어야 하는걸 포기해야하지만 뭐.. exe만 딸랑 배포하진 않고 거의 대부분이 압축을 해서 배포할꺼니깐 이렇게 하셔도 상관 없을거 같습니다.

 

저 위 프로그램이 궁금하신 분은 여기로!

https://pgh268400.tistory.com/380?category=1072301 

 

SNS 공유하기
네이버밴드
카카오톡
페이스북
X(트위터)

최근글
인기글
이모티콘창 닫기
울음
안녕
감사
당황
피폐