노트와 노트

Python PyQT5 프로그램 실행 파일 변환 후 배포하기 본문

Programming/Python

Python PyQT5 프로그램 실행 파일 변환 후 배포하기

gellygelly 2021. 11. 29. 22:11

PyQT5로 간단한 GUI 프로그램을 제작한 후, 다른 사람에게 테스트를 부탁하기 위해 실행 파일로 변환 후 배포하려고 했는데.. 이럴수가ㅠ 이틀동안 오류 해결하느라 머리 터지는 줄 알았다...... 하나 해결하면 하나가 새로 나타나고 또 하나 해결하면 다른 오류가 ㅠㅠ 

 

한참 헤매다 드디어 해결했고 다시는 이런 괴로운 일 겪지 않도록 기록해두려 한다.

 

  • PyQT5  5.15.2
  • pyinstaller 4.7
  • python 3.8
  • PyCharm Community Edition 2021.2.2

※해당 라이브러리들이 설치되어 있지 않을 경우, 반드시 설치 필요 / 다른 버전의 경우, 테스트해보지 않았기 때문에 정상 작동하는지 모름

 

본 포스팅에서 다룰 파이썬 프로젝트 구성은 아래와 같다.  PythonProject라는 큰 폴더 안에 Image 폴더, UI 폴더가 있고 UI 폴더엔 pyqt5의 qt designer를 통해 제작한 .ui 파일과 2개의 .py 파일, 그리고 search.jpg 파일이 존재한다. 

PythonProject
  ├─ Image
  └─ UI
     ├─BrandSelect.ui
     ├─connectDB.py
     ├─Main.ui
     ├─test.py
     └─search.jpg

 

Pycharm에서는 test.py을 실행하면 해당 파일이 .ui 파일들과 다른 파일들을 전부 불러오며 프로그램이 실행되는 형태다. 

 

1. 실행 파일 생성

(base) C:\Users\>cd C:\Users\PycharmProjects\PythonProject\UI

(base) C:\Users\PycharmProjects\PythonProject\UI>pyinstaller -F test.py # -F option: one file

test.py 파일이 있는 경로로 이동 후, pyinstaller -F test.py 명령어를 입력한다. -F 옵션은 하나의 파일만 만드는 명령어로, 자잘한 라이브러리 등의 파일들이 하나의 파일로 들어가게 되어 깔끔하다. (-F 대신 --onefile이라고 해도 무관, pyinstaller --onefile test.py)

 

참고로, --windowed 옵션을 사용하면 콘솔창을 안 띄울 수 있다. 

 

  • -F, --onefile : 하나의 파일만 생성하는 옵션 

 

묶어야 할 실행 파일은 test.py와 connectDB.py 두갠데, 하나로만 지정했다. 왜냐하면 어차피 이러나 저러나 test.spec 파일을 싹 수정해야 하기 때문이다. pyinstaller 파일은 .ui 파일까지 묶어서 실행 파일로 만들어주지 않는다. 물론, 폴더 내에 있는 이미지 파일(.jpg)들도 묶어주지 않는다! 따라서, .spec 파일을 직접 수정해서 .ui 파일도 넣어주고, Image 폴더의 이미지들과 UI 폴더 내에 있는 search.jpg 파일까지 .spec 파일에서 싹 경로를 넣어준 후 실행파일로 만들어줘야 한다. 

2. test.spec 파일 수정

 

위 명령어 수행 직후 UI 폴더에 생성된 test.spec 파일을 열어보면 다음과 같다. 

 

# -*- mode: python ; coding: utf-8 -*-

block_cipher = None


a = Analysis(['test.py'],
             pathex=['C:\\Users\\PythonProjects\\UI'],
             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,
          a.binaries,
          a.zipfiles,
          a.datas,
          [],
          name='test',
          debug=False,
          bootloader_ignore_signals=False,
          strip=False,
          upx=True,
          upx_exclude=[],
          runtime_tmpdir=None,
          console=True )

위 test.spec 파일을 아래와 같이 수정하면 .ui 파일들과, connectDB.py 파일, 각종 이미지 파일들을 싹 묶어 전달해줄 수 있다. '##'으로 표시된 주석 부분을 보고 해당 부분만 각자 프로젝트에 맞게 수정하면 된다.) 

 

add_files에서 ('BrandSelect.ui', '.')의 의미는 (src, dst)이다. 

 

# -*- mode: python ; coding: utf-8 -*-

block_cipher = None

## <중요!!> ui 파일과 image 파일 전부 다 전달해주기
add_files=[ ('BrandSelect.ui', '.'), ('Main.ui', '.'), ('search.jpg', '.'), ('../new_product_image/*', '../new_product_image')]

a = Analysis(['test.py', 'connectDB.py'], ## !!! 2개 .py 파일 모두 넣어주기 !!! 
             pathex=['C:\\Users\\PythonProjects\\UI'],
             binaries=[],
             datas=add_files, ## 여기 중요!! add_files 지정
             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,
          a.binaries,
          a.zipfiles,
          a.datas,
          [],
          name='test',
          debug=False,
          bootloader_ignore_signals=False,
          strip=False,
          upx=True,
          upx_exclude=[],
          runtime_tmpdir=None,
          console=True )

 

그 외에도 아래 EXE에서 각종 옵션을 선택할 수 있다. 오류가 발생하면 debug를 true로, 콘솔창을 보고싶지 않으면 console=False로 지정하면 된다. 

 

3. 수정한 test.spec 파일을 이용해 test.exe (실행 파일) 생성

 

pyinstaller -F test.spec

 

해당 명령어 입력 후, dist 폴더에 들어가보면 test.exe가 생성되었다. 

 

4. 생성한 실행 파일을 이용해 사용자가 사용할 수 있는 설치 파일 생성

 

이 부분은 아래 포스팅을 보고 그대로 진행하였다. 

 

https://ecogis.tistory.com/50

 

PyQT5로 제작된 파이썬 프로그램 pyinstaller로 배포하기

1. 아나콘다 가상환경에서 진행 주의사항: 반드시 아나콘다 환경에서 실행해야 함. 2. 필수 패키지  - pyqt5 : 버전 5.15.2 -> 설치: pip install pyqt5==5.15.2  - pyinstaller: 버전 4.2 -> 설치: pip instal..

ecogis.tistory.com

 

 


1. 위 과정대로 했는데, 타 컴퓨터에서 실행 시 warning: file already exists but should not ~ 오류가 발생하고 실행이 안 되는 경우

 

...열심히 찾아서 실행하고 설치파일까지 만들었는데 또 오류가 발생해서 슬펐다. 위 오류의 해결 방안은 아래 깃헙 링크에서 찾았다. 

 

https://github.com/pyinstaller/pyinstaller/issues/4561

 

WARNING: file already exists but should not: C:\Users\320068~1\AppData\Local\Temp\_MEI1103602\torch\_C.cp37-win_amd64.pyd · Iss

Python version: 3.7 pyinstaller version: 3.5 / develop (tried both) I trained a segmentation model with PyTorch and built a GUI with PyQt. When I run pyinstaller command in Anaconda Prompt(gui.py i...

github.com

 

.spec 파일(본 포스팅에서는 test.spec)에 아래 코드를 추가한다. 

#avoid warning
for d in a.datas:
    if '_C.cp37-win_amd64.pyd' in d[0]:
        a.datas.remove(d)
        break

 

2. ConnectionRefuseError: [WinError 10061] 대상 컴퓨터에서 연결을 거부했으므로 연결하지 못했습니다 오류가 발생하는 경우

 

로컬 컴퓨터에서는 문제없이 작동할 건데, 다른 컴퓨터에서 실행했을 때 나오는 오류이다. 로컬 PC의 DB가 외부 접속을 막고 있어서 발생하는 문제다. 이건 DB에 외부 접속 권한을 허용해주면 된다. 

 

https://gellygelly0808.tistory.com/12

 

[MySQL] 원격 접속(외부 접속) 방법 - 외부에서 로컬 PC의 데이터베이스에 접근하기

오늘은 외부에서 로컬 PC의 데이터베이스에 접근하는 방법에 대해서 정리할 것이다. 맨날 로컬 PC에서 해당 PC에 있는 DB에만 접속해보다가 외부에서 로컬 PC에 있는 DB에 접속할 일이 생겼는데 매

gellygelly0808.tistory.com

 

 

위 포스팅을 참고해서 새 계정을 생성 후, 해당 계정에 외부에서도 접속 가능하도록 권한을 부여한 뒤, PythonProject 내에 DB에 접속하는 부분의 코드를 수정해주면 된다. user, password 부분을 방금 권한을 허용해 준 계정으로 설정해주면 끝! 

 

3. "공유기" 사용중인 경우

 

......이것때문에 한참 해맸다ㅠㅠ 항상 로컬에서만 실행해봤어서..

 

공유기 사용중인 장비는 인터넷에 접근하기 위해 반드시 공유기를 거쳐야하고, 외부 PC에서 공유기를 사용중인 로컬 PC에 있는 DB에 연결하기 위해서는 반드시 이 공유기를 거쳐야한다. 

 

이때 공유기를 거쳐 공유기에 연결된 2번 PC에 연결하려고 한다고 치자.

IP를 공인 ip인 11.22.33.44로 입력하고, port를 mysql 기본 포트인 3306으로 지정해서 연결하려고 한다. 

11.22.33.44:3306

 

오! 그런데 이렇게 하면 1번 PC의 mysql에 접근하려는 건지, 2번 pc에 접근하려는 건지, 3번 pc에 접근하려는 건지 알 수가 없다. 그래서! 내가 접근하려는 IP 주소와 포트 번호를 결합해주는 포트 포워딩 과정이 필요하다. 

 

※참고: 아래 블로그에 포트 포워딩이 필요한 이유에 대해 상세하게 적혀 있다! 

https://lamanus.kr/59

 

포트 포워딩이란?

과거에는 집에 보통 컴퓨터 한 대만 사용하고 스마트폰이 없었기 때문에 인터넷을 설치한다고 하더라도 모뎀만 있으면 컴퓨터에 인터넷을 연결할 수 있었습니다. 최근 들어서는 가족 구성원 한

lamanus.kr

 

포트 포워딩 하는 법은 [공유기 제품] 포트포워딩 이라고 검색하면 각 제품에 맞는 포트포워딩 방법이 적혀 있다. 

 

  • 사설 ip: cmd 창에 ipconfig 해서 나오는 ip
  • 외부 포트: 외부에서 접속할 때 쓸 포트. 내부 포트랑 동일하게 지정해도 된다고 하는데, 나는 다르게 지정했다. 
  • 내부 포트: mysql 기본 포트인 3306(별도로 포트를 변경했을 경우 변경한 포트)

포트 포워딩 설정 시 세 개 적어주고 나머지 필요한 정보 잘 적어 준 다음! 외부 PC 접속용으로 python 코드에서 db 접속하는 부분의 host를 공인 IP, port를 외부 포트로 설정해주면 끝~!! 계정과 패스워드도 위에서 DB 접속 권한을 준 계정으로 입력해주면 된다. 

 

# 포트 포워딩 시 외부 포트를 3333으로 적은 경우

conn = pymysql.connect(host='공유기 공인 IP', database='mydb', port=3333, user='user', password='password', charset='utf8')

cursor = conn.cursor()

 

참고로, 공인 IP 확인하는 건 공유기 라우터 설정 페이지에서 확인해도 되고, 아래 사이트에서 확인 후 적어도 된다. 

 

https://ip.pe.kr/