서론

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

와.... 진짜 이거 때문에 3시간 날린 듯...ㅋㅋㅋ

 

문제의 시작은 이랬다....

 

0~9까지의 버튼을 만드는데

 

self.button0.clicked.connect(lambda idx: self.setNumber(0))
self.button1.clicked.connect(lambda idx: self.setNumber(1))
self.button2.clicked.connect(lambda idx: self.setNumber(2))
self.button3.clicked.connect(lambda idx: self.setNumber(3))
self.button4.clicked.connect(lambda idx: self.setNumber(4))
...

def setNumber(self, num):
	print(num)

이런식으로 만들려고 했다.

 

하지만 아무리봐도 저렇게 무식하게 다 쓰는건 폼이 안난다.

 

그래서 좀 더 뒤져보니

for문과 list를 이용해 좀 더 깔쌈하게 짜는 법이 있어서 그 방법으로 해보기로 했다.

 

  self.button_list = [self.button0, self.button1, self.button2, self.button3, self.button4, self.button5, self.button6, self.button7, self.button8, self.button9]

  for i, btn in enumerate(self.button_list):
      btn.clicked.connect(lambda stat, idx=i: self.setCarNumber(stat, idx))

def setCarNumber(self, stat, num):
	print(num)

button 리스트를 만들어주고 clicked가 지 상태를 반환한다고 해서 쓰지도 않을 stat 인자와 실제 필요한 idx인자를 이용해서 깔쌈하게 짜보기로...

 

TypeError: () missing 1 required positional argument: 'stat' ...?

 

에러가 나네.... stat인자가 없다고..?

lambda [매개변수] : [식] 이런 식으로 구현하는거 맞지 않니..?

 

그래서 정말 여러가지 시도를 해봤다... 나는 람다 함수를 잘 모르니깐!!! ㅋㅋㅋ

 

하지만 문제를 해결하고 나니 원인을 알 수 있겠더라..ㅋㅋ


해결

일단 PySide2의 clicked() 함수는 2종류다. 바로 clicked()와 clicked(bool)

여담 : Signal : 신호.. 말 그대로 어떠한 이벤트가 발생했을 때 실행되는 함수라고 보면 되고... Slot : 신호가 발생했을 때 실행되는 함수라고 보면 된다. 같은 소리같이 들리겠지만 실제 구현은 Slot에서 하면 된단 소리다...ㅋㅋ

그래서

btn.clicked.connect(lambda idx=i: self.setCarNumber(idx))

이런식으로 선언을 하면 clicked(bool)에 의해서 idx가 False값이 들어가버린다..ㅋㅋㅋ

 

즉, clicked를 이용해서 내가 원하는 구현을 하고 싶으면 일단 stat를 놓고, idx를 추가하면 된다.

btn.clicked.connect(lambda stat=False, idx=i: self.setCarNumber(idx))

이런식으로 말이다..ㅋ

개 웃긴게 매개변수 2개를 반환하는 clicked는 없어서 stat만 쓰면 인자가 없다고 쌩난리를 피는 것...

 


아 그리고 이런 문제도 경험했다.

#안되는 것
btn.clicked.connect(lambda idx: self.setCarNumber(i))

이건 안되고(어떤 버튼을 누르던 출력이 9가 나온다 ㅋㅋ)

#되는 것
self.button0.clicked.connect(lambda idx: self.setNumber(0))

이건 되고..ㅋㅋㅋ

 

진심 개빡쳤었다..ㅋㅋ

 

지금 돌이켜보면 내가 람다 함수를 잘 몰랐던 것과... 저 connect가 한 번 연결시켜놓으면 계속 그 상태를 불러오는게 아니라 lambda idx: self.setNumber(i)를 불러오는거라서 계속 i의 최종 값인 9가 출력되었던 것....

 

 

출처 : https://www.jetbrains.com/help/pycharm/installation-guide.html

 

Install PyCharm - Help | PyCharm

To create a desktop entry, do one of the following: On the Welcome screen, click Configure | Create Desktop EntryFrom the main menu, click Tools | Create Desktop Entry Install using snap packages For Ubuntu 16.04 and later, you can use snap packages to ins

www.jetbrains.com

블로그의 글보다는 만든 회사에서 제공하는 설치 방법이 최고겠쥬?

 

01. 설치

$ sudo snap install pycharm-community --classic

설치 완료 후 재시작을 하면 "pycharm-community" 명령어로 실행 가능

$ sudo add-apt-repository ppa:mystic-mirage/pycharm
$ sudo apt-get update
$ sudo apt-get install pycharm
너무 느림

 

02. 실행

$ pycharm-community

 

03. 삭제

$ sudo apt-get remove pycharm-community
$ sudo apt-get remove pycharm
$ sudo add-apt-repository --remove ppa:mystic-mirage/pycharm
이하 동문

+ Recent posts