파이썬

[python] 실행파일 자동 실행

mistive 2020. 3. 31. 14:04

서론

요는 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에 써준 뒤 다시 파일을 닫음으로써 새로운 파일을 생성시킨다.