파이썬 + UI 파일 연동

 

 

앞으로 실습을 할 때, 파이썬 파일을 생성하고 아래의 코드를 복사하여 넣고 시작을 하면 GUI 프로그래밍을 쉽게 할 수 있다.

 

프로젝트에 맞게 UI 파일 이름과 경로, Window Title, Window Icon의 경로만 변경해주면 기본적인 GUI 프로그램이 완성된다.

 

import sys

import os

from PySide2 import QtUiTools, QtGui

from PySide2.QtWidgets import QApplication, QMainWindow

 

class MainView(QMainWindow):    

    def __init__(self):

        super().__init__()

        self.setupUI()

 

    def setupUI(self):

        global UI_set

 

        UI_set = QtUiTools.QUiLoader().load(resource_path("main.ui"))

        self.setCentralWidget(UI_set)

        self.setWindowTitle("UI TEST")

        self.setWindowIcon(QtGui.QPixmap(resource_path("./images/jbmpa.png")))

        self.resize(500,270)

        self.show()

 

#파일 경로

#pyinstaller로 원파일로 압축할때 경로 필요함

def resource_path(relative_path):

    if hasattr(sys, '_MEIPASS'):

        return os.path.join(sys._MEIPASS, relative_path)

    return os.path.join(os.path.abspath("."), relative_path)

 

if __name__ == '__main__':

    app = QApplication(sys.argv)

    main = MainView()

    #main.show()

    sys.exit(app.exec_())

 

 

 

코드 설명

 

1. import

 

import는 기존에 만들어진 모듈 또는 패키지 파일들을 불러오는 코드이다. Python을 설치하면 기본적으로 내장되어 있는 많은 모듈들이 있지만(이를 라이브러리라고 한다), 필요하면 다른 사람들이 만든 모듈을 불러 올 수 있고, 본인이 직접 만든 모듈들을 불러올 수 있다.

프로그래밍을 할 때, 모든 코드를 한 명이 작성하지는 않는다. 다른 사람이 만들어 놓은 좋은 코드가 있으면 그 코드를 복사해서 사용한다던가, 다른 사람이 만든 파일의 일정부분만을 골라서 사용할 수 있다. 이런 이유로 남이 만들어 놓은 코드, 즉 남이 만들어 놓은 파일(모듈)을 내가 사용할 수 있도록 불러오는 것. 그것이 import이다.

또한 프로그램을 하다보면 코드가 길어진다. 하나의 파일에서 모든 코드들을 관리하기가 힘이 들거나, 반복해서 이용하는 코드, 계산이 복잡한 코드들은 따로 다른 파일(모듈)로 만들어서 필요할 때 마다 호출하여 사용할 수 있다.

모듈들을 불러올 때는 아래와 같이 여러 모듈들을 쉼표로 구분하여 불러올 수 있다.

 

import sys, os

 

하지만 PEP(파이썬 문법 또는 개발 가이드라인)에서 모든 모듈들을 따로 import하라고 장려하니 모든 모듈들은 각 줄에 따로 import한다.

 

import sys

import os

 

 

 

2. from

 

하나의 프레임워크에는 수많은 패키지와 모듈이 존재하고, 각 모듈안에는 여러개의 클래스와 함수들이 존재한다. 패키지에서 특정 모듈만을 불러올 때 from과 import를 이용할 수 있다. 

 

from PySide2 import QtUiTools, QtGui

from PySide2.QtWidgets import QApplication, QMainWindow

 

위의 코드는 PySide2라는 패키지에서 QtUiTools, QtGui 모듈을 불러온다고 생각하면 된다.

 

 

 

3. 초기 함수 호출 

 

if __name__ == '__main__':

    app = QApplication(sys.argv)

    main = MainView()

    #main.show()

    sys.exit(app.exec_())

 

 

1) if __name__ == '__main__':

 

 

파이썬 파일이 실행되면, 파이썬 내장 변수인 “__name__”이라는 변수에 “__main__”이라는 값이 들어간다.

 

>> python uitest.py

 

위처럼 shell(command prompt)에서 입력을 하면 uitest.py 모듈이 실행되면서 __name__ 이라는 변수에 '__main__' 이라는 값이 자동으로 들어간다고 생각하면 된다. 따라서 if 구문이 참이 되기 때문에 그 다음 구문들이 실행이 된다.

반대로 uitest.py라는 모듈이 자체적으로 실행되지 않고 다른 모듈에 의해 호출된다면 __name__ = ‘__main__’ 이라는 조건이 거짓이 되기 때문에 이 구문은 실행되지 않는다. 아래의 예제를 통해 확인한다.

 

file A.py

def func():

    print("function A.py")

 

print("top-level A.py")

 

if __name__ == "__main__":

    print("A.py 직접 실행")

else:

    print("A.py가 임포트되어 사용됨")

 

 

file B.py

import A as one

 

print("top-level in B.py")

one.func()

 

if __name__ == "__main__":

    print("B.py가 직접 실행")

else:

    print("B.py가 임포트되어 사용됨")

 

 

실행 1

>> python A.py

top-level in A.py

A.py가 직접 실행

 

실행 2

>>python B.py

top-level in A.py

A.py가 임포트되어 사용됨

top-level in B.py

function A.py

B.py가 직접 실행

 

 

2) app = QApplication(sys.argv)

 

QApplication()이라는 모듈은 PySide2 내장 모듈이다. 이 모듈에서 외부 인수들을 받아들인다. 저장하는 매개변수(parameter)는 sys.argv이다. 

 

 

예를 들어 

 

>> python uitest.py a b c d 

 

라고 입력하면, sys.argv는 아래의 값들을 배열에 저장하게 된다.

 

[‘uitest.py’, ‘a’,‘b’,‘c’,‘d’]

 

따라서 app = QApplication(sys.argv) QApplication이라는 모듈이 외부에서 입력된 인수들을 받아서 app 이라는 변수에 저장된다는 의미이다. PySide2를 통해 프로그램을 실행시키기 위한 필수 코드라 생각하면 된다.

 

3) MainView()

 

main = MainView()

#main.show()

 

MainView() 클래스를 생성하고 main 변수에 저장한다. 물론 MainView()만 실행해도 되지만, MainView()를 사용자에게 보여주는 코드가 필요하다. 이를 위해서 main 변수에 저장을 하고 main.show()를 통해 UI를 사용자에게 보여주게 된다.

하지만 우리는 MainView() 클래스 코드 내부에 show() 함수를 삽입할 것이므로 main.show() 코드는 주석처리 하였다.

 

 

 

4. 파일 경로 찾기

 

def resource_path(relative_path):

    if hasattr(sys, '_MEIPASS'):

        return os.path.join(sys._MEIPASS, relative_path)

    return os.path.join(os.path.abspath("."), relative_path)

 

위의 함수 코드는 파이썬 코드에서 외부 폴더의 특정 파일을 불러들일 때, 필수적으로 사용한다고 생각하면 된다.

아래의 코드처럼 상대 경로를 지정할 때 사용한다.

 

QtGui.QPixmap(resource_path("./images/jbmpa.png")   

 

* 주의 사항

상대경로를 지정하여 파일을 선택할때, 아래의 코드와 같이 사용하여도 된다.

 

QtGui.QPixmap("./images/jbmpa.png") 

 

이와 같이 경로를 지정하는 건, 코드를 개발할 때 상대 경로의 리소스 파일들을 불러들일 때는 아무 문제가 없다.

 

하지만 개발된 코드를 타인에게 배포할 경우에는 개발된 코드 뿐만 아니라 사용된 리소스 파일(이미지,  UI 파일, DB 파일등)들을 모두 제공해야 한다. 

 

알다시피 파이썬은 인터프리터 언어이므로, 개발된 코드는 그대로 노출이 된다. 

(C나 JAVA 같은 경우는 컴파일 과정을 거치기 때문에 소스코드는 노출이 되지 않는다.)

 

이때, 모든 코드들을 공개하기가 꺼려질 때는 Cython과 같은 모듈로 python 코드를 C 코드로 변환 시킬 수도 있고, pyinstaller 같은 모듈로 하나의 파일로 만들어서 배포할 수 있다. pyinstaller를 이용하면 모든 리소스 파일들을 하나로 묶어서 실행파일로 만들어준다.

 

배포된 실행파일을 실행하면 묶여진 파일들이 압축 해제되면서 리소스 파일들이 운영체제가 지정한 특정한 곳에 위치하게 된다.

이럴 경우 참조한 리소스 파일들, 즉 프로젝트 루트 폴더가 아닌 하위 폴더의 이미지나 UI 파일들은 압축이 해제되면서 운영체제의 특정한 곳에 위치하게 된다.

 

따라서 파이썬 코드에서 입력한 상대 경로에는 그 파일들이 존재하지 않으므로 에러를 발생시킨다.

 

resource_path() 는 코드에서 적힌 파일 경로를 운영체제에서 지정한 경로로 변경하여 처리한다고 생각하면 된다. 따라서 pyinstaller 같은 모듈로 압축하여 배포한 프로그램에서도 정상적으로 파일 경로를 찾아서 작동하게 된다.

 

따라서 파일 경로 찾기 코드는 어떤 프로젝트에서건 함수(메서드)로 만들어놓고, 파일을 접근하는 경로를 사용하는 곳에 모두 사용하면 된다.

 

 

 

5. MainView class

 

class MainView(QMainWindow):

    def __init__(self):

        super().__init__()

        self.setupUI()

 

    def setupUI(self):

        global UI_set

 

        UI_set = QtUiTools.QUiLoader().load(resource_path("main.ui"))

        self.setCentralWidget(UI_set)

        self.setWindowTitle("UI TEST")

        self.setWindowIcon(QtGui.QPixmap(resource_path("./images/jbmpa.png")))

        self.resize(500,270)

        self.show()

 

MainView 클래스는 QMainWindow 클래스를 상속받아 생성한다. QMainWindow 클래스는 centralwidget, menubar, statusbar 클래스를 포함하는 프로그램의 Main UI 역할을 한다.

 

MainView 클래스는 “3. 초기 함수 호출” 코드에서 생성 및 호출이 되었었다.

클래스가 실행되는 순서를 보면, 최초 클래스가 생성(호출)이 되면, __init__ 함수(클래스 내부 함수는 메서드라고 부른다)가 실행된다. 초기화 함수라고 생각하면 된다. 다른 프로그램에서는 생성자라고 표현된다.

클래스를 만들때  변수에 초기 값을 할당하고, 특정 함수들을 실행시키려면, __init__ 메서드를 만들어주고 이곳에 실행할 함수들을 순서대로 입력하면 된다.

 

용어 정의

 

프로그래밍 언어마다 기능들을 부르는 용어들이 조금씩 다를 수 있다. 또한 영어를 한글로 해석해서 부르다보니 영어의 정확한 늬앙스를 놓치기도 쉽다.

 

일반적으로 함수라 하면 독립적으로 실행되는 함수(function)를 말한다.

그리고 메서드(method)는 클래스 내부에 작성된 함수이다. 

 

아래의 코드를 보자.

 

IamFunc()는 함수이다.

IamMethod()IamClass내부에서 선언된 함수, 즉 메서드이다.

 

함수를 호출할때는 함수를 바로 호출하면 되고, 메서드를 호출할때는 클래스이름.메서드이름 형태로 호출한다.

 

메서드도 함수이므로 함수라 불러도 되지만, 메서드라고 호칭되면 클래스 내부에서 선언되었다는 것을 좀 더 명확하게 알 수 있다.

 

앞으로는 수(function, define)메서드(method)를 구분해서 사용한다.

 

 

1) def __init__(self):

 

클래스안에서 self는 클래스 자기 자신을 의미한다. 

클래스 내부의 메서드는 무조건 self를 첫번째 매개변수로 지정해야 한다.

super().__init__()는 부모 클래스를 초기화한다. 즉, 예제 코드에서는 상속받은 QMainWindow를 초기화한다고 보면 된다.

 

2) self.setupUI()   

 

이곳에서 self는 MainView 클래스 자신을 의미한다. self.setupUI()는 MainView 클래스 내부의 setupUI() 메서드를 실행시킨다. 이는  UI를 세팅하기 위한 코드를 실행하는 메서드이다.

클래스 내부에서 클래스 내부의 메서드를 호출할 때는 무조건 self.메서드이름과 같은 방법으로 호출한다.

 

3) def setupUI(self):

 

함수 즉, def는 클래스 내부 또는 외부에 존재할 수 있다. 여기서 주의할 것은 클래스 내부에 존재하는 메서드는 항상 클래스 자체를 인자로써 주고 받는다.

따라서 클래스 내부에서 def를 정의할 때는 항상 self를 첫번째 매개변수로 입력하고, 다음에 다른 매개변수들을 나열한다.

 

클래스 내부 메서드 예 :

 

def Plus(self, a, b, c)  #메서드 정의

    print(a + b + c)

 

self.Plus(1, 2, 3)  #메서드 호출

 

아래 코드는 UI 파일을 연결하는 코드이다. PySide2의 QtUiTools.QUiLoader() 클래스를 통해 UI 파일을 호출하면, QT Designer로 생성된 xml로 이루어진 UI 파일을 직접 호출 할 수 있다. 호출된 UI 파일을 UI_set 변수에 저장한다. UI_set 변수는 다른 메서드나 다른 함수에서 사용할 수 있으므로 global 변수로 설정한다.

 

global UI_set

 

UI_set = QtUiTools.QUiLoader().load(resource_path("main.ui"))

 

### UI 파일을 메인 위젯으로 설정

self.setCentralWidget(UI_set)

### 프로그램 창의 왼쪽 상단 제목 설정

self.setWindowTitle("UI TEST")

### 프로그램 창의 왼쪽 상단 아이콘

self.setWindowIcon(QtGui.QPixmap(resource_path("./images/jbmpa.png")))

### 프로그램의 실행 크기

self.resize(500,270)

### 이때까지 설정한 값들을 가지고 실제 프로그램을 보여줌.

self.show()

 

 

 

이로써 프로그램을 실행할 수 있는 기본 틀을 모두 작성하였다. 프로그램을 실행하기 전에 설정한 경로에 파일들이 존재해야한다.

Icon을 위한 이미지 파일을 ./images/ 폴더를 만들어 안에 넣어두면 되고, 실제 UI 파일들을 만들어 보도록 한다.

+ Recent posts