[python] 실행파일 자동 실행
서론
요는 pyqt에서는 코드 내에서 'designer'를 이용해 만든 .ui파일을 업데이트 하는 라이브러리가 있는 반면, pyside2에서는 해당 자료를 찾을 수가 없어 직접 만들어보았다.
목표
$ pyside2-uic monitor.ui > ui_monitor.py
(> : pyside2-uic monitor.ui의 결과를 ui_monitor.py 파일에 저장하라는 명령이다.)
를 python 내에서 바로 실행이 될 수 있게 만들어보자.
코드
import os, sys
from subprocess import Popen, PIPE
import PySide2 as ref_mod
from PySide2.QtWidgets import *
def main():
#파일 이름
file_name = "monitor.py"
dir = os.path.dirname(os.path.realpath(__file__)) #root 폴더 경로 가져오기
ui_path = os.path.join(dir,"ui" ,file_name.replace(".py", ".ui")) #*.ui 경로 가져오기
ui_py_path = os.path.join(dir,"ui_py" ,"ui_" + file_name) #ui_*.py 경로 가져오기
exe = os.path.join(os.path.dirname(ref_mod.__file__), "uic.exe") #실행 명령어 입력하기
cmd = [exe] + ['-g', 'python'] + [ui_path]
proc = Popen(cmd, stdout=PIPE, encoding='utf8') #명령어 실행
out = open(ui_py_path, 'w') #저장할 파일 열기
print(proc.stdout.read(), file=out) #파일 저장
out.close() #저장 파일 닫기
설명
main.py에서 실행이 되는 코드이고, ui/monitor.ui를 ui_py/ui_monitor.py로 변경해주는 기능을 구현한 것이다.
일단 pyside2-uic의 uic파일의 위치부터 확인해보았다.(우연찮게..ㅋㅋ)
$pyside2-uic -h
를 치면 다양한 옵션이 나오는데 여러가지 시도해보던 중 에러 코드의 실행 위치를 통해 uic.exe 파일의 위치를 확인할 수 있었다.
C:\Users\PC\anaconda3\envs\plcmonitoring\Lib\site-packages\PySide2
plcmonitoring이 프로젝트 명이고 그 뒤로부터는 똑같을 것이다..ㅎㅎ 여기 uic.exe가 있다.
그리고 이를 실행시켜주는 코드는
C:\Users\PC\anaconda3\envs\plcmonitoring\Lib\site-packages\PySide2\scripts\pyside_tool.py
pyside_tool.py 안에 코드가 구현이 되어있다.
해당 코드의 부분을 잠깐 살펴보면
from __future__ import print_function
import sys
import os
import subprocess
from subprocess import Popen, PIPE
import PySide2 as ref_mod
def main():
# This will take care of "pyside2-lupdate" listed as an entrypoint
# in setup.py are copied to 'scripts/..'
cmd = os.path.join("..", os.path.basename(sys.argv[0]))
command = [os.path.join(os.path.dirname(os.path.realpath(__file__)), cmd)]
command.extend(sys.argv[1:])
sys.exit(subprocess.call(command))
def qt_tool_wrapper(qt_tool, args):
# Taking care of pyside2-uic, pyside2-rcc, and pyside2-designer
# listed as an entrypoint in setup.py
pyside_dir = os.path.dirname(ref_mod.__file__)
exe = os.path.join(pyside_dir, qt_tool)
cmd = [exe] + args
proc = Popen(cmd, stderr=PIPE)
out, err = proc.communicate()
if err:
msg = err.decode("utf-8")
print(msg, file=sys.stderr)
sys.exit(proc.returncode)
def uic():
qt_tool_wrapper("uic", ['-g', 'python'] + sys.argv[1:])
이런 식으로 구현이 되어있는데, 여기서 모티브를 얻었다.
먼저 os.path부터 보자.
os.path.dirname() : 파일이 있는 디렉토리 path를 반환한다.
os.path.basename() : 현재 파일의 이름을 반환한다.
os.path.join() : path끼리 합쳐준다.(인자 여러개 가능)
이 외에도 다양하게 많은데, 몇개만 알고 있으면 파일의 위치를 찾아내 절대 경로를 설정하는 것은 전혀 어렵지 않을 듯 하다.
살짝 헤맸던 부분은 subprocess의 Popen()이다.
class subprocess.Popen(args, bufsize=-1, excutable=None,
stdin=None, stdout=None, stderr=None,
preexec_fn=None, close_fds=None, shell=False,
cwd=None, env=None, universal_newlines=False,
startupinfo=None, creationflags=0, restoreflags=0,
restore_signals=True, start_new_session=False, pass_fds=()
)
Popen은 여러가지 옵션들이 있는데 내가 설정한 것은 크게 2가지이다. stdout과 encoding.
stdout = PIPE로 설정을 했는데 이는 Popen()의 결과가 현재 프로세스로 출력되는 것이 아닌 PIPE를 통해 자식 프로세스에서 출력이 되도록(현재의 출력창에는 보이지 않도록) 하기 위함이고,
이 놈은 출력을 binary 형식으로 하는데 우리는 utf-8이 제일 익숙하므로 해당 타입으로 출력하라는 의미이다.
출력 결과는 proc.stdout.read()를 통해 읽을 수 있고,
open() 명령어를 통해 file descriptor를 생성해주고 print()의 결과를 file descriptor에 써준 뒤 다시 파일을 닫음으로써 새로운 파일을 생성시킨다.