최근에 PyQT로 만든 프로젝트 코드를 리팩토링 하고 있습니다. PyQT 프레임워크를 이용하면 파이썬에서 GUI 프레임워크인 Qt를 쉽게 다루어 GUI 를 쉽게 구현할 수 있도록 해줍니다.
개인적으로 파이썬에서 GUI를 구현할 수 있는 최선의 방법이자 거의 유일한 선택지라고 생각합니다. tkinter 와 같은 파이썬 기본 라이브러리도 있으나 마개조를 거치지 않는 이상 C#의 윈폼보다도 훨씬 구린 디자인이 나옵니다. 반면에 Qt 로 개발하면 무려 CSS를 적용할 수 있는 장점이 있구요 (물론 모든 CSS가 잘 적용되는건 아닙니다만..)
잡담은 여기까지 하고, PyQT로 개발을 하면서 불편한 점은 UI를 불러오고, 그 UI 요소들에 접근할 때 사용할 수 있는 메소드나 데이터들이 IDE에서 자동완성되지 않는점이였습니다.
# Main UI Load
main_ui = resource_path('main.ui')
Ui_MainWindow = uic.loadUiType(main_ui)[0] # UI 가져오기
class Main(QMainWindow, Ui_MainWindow):
# 여기서 상속된 UI 요소들에 접근
# 기능을 추가한다..
일반적으로 PyQT로 UI 작업을 할 땐 Qt Designer 등으로 눈으로 보이는 껍데기 파일(*.UI 파일) 만 먼저 만듭니다. 파이썬 코드로 만든 UI 파일을 동적 로딩하고 class 안에서 QMainWindow 와 로딩한 UI를 상속해서, 그 class 안에서 UI 요소들에 접근해 실제로 기능을 넣는 작업을 하게 됩니다.
웹으로 비유하자면 HTML 로 뼈대만 먼저 만들고, 나중에 JS를 이용해서 기능을 넣는 행위와 비슷하다고도 볼 수 있겠네요.
하지만 이렇게 UI 파일을 로딩해서 UI 요소들에 접근하면 한 가지 문제가 있습니다.
지금 UI 파일 안에 articleView 라는 이름의 QTableWidget 이 있습니다.
self.articleView.setEditTriggers(
QAbstractItemView.NoEditTriggers) # TableWidget 읽기 전용 설정
self.articleView.setColumnWidth(0, 60) # Column 폭 조정
그리고 articleView 에 접근해서 다음과 같은 코드를 작성해서 작업을 하고 싶다고 가정해봅니다.
코드를 작성하면서 자동 완성 기능이 전혀 동작하지 않습니다.
보시면 articleView 뒷부분에 함수들의 코드 하이라이팅이 전혀 보이지 않습니다.
(만약에 함수로 제대로 인식했다면 Vscode에서 주황색으로 색을 자동으로 변경해줬을 겁니다.)
이런 문제가 일어난 이유는 UI 파일을 동적으로 로딩하기 때문에 VSCODE IDE가 UI 요소들의 타입이나 목록을 추론할 수 없는 것 입니다. self를 이용해 UI 요소에 접근하려고 해도 아무것도 표시되지 않습니다.
사실 제목에나 본문에나 VSCode를 사용하는 것으로 못박아 뒀는데, Pycharm 같은 IDE에서도 문제는 동일할 것입니다.
이 문제를 어떻게 해결해야 할까요?
우선 2가지 해결 방법이 있습니다.
1. *.UI 파일을 *.PY 파일로 변환한다. (Best Solution, 추천하는 방법)
미리 UI 파일을 파이썬이 이해할 수 있는 PY로 바꿔서 로딩함으로써 IDE가 미리 자동 완성 기능을 지원할 수 있게 하는 방법입니다. 사실 어떻게 보면 가장 좋은 방법이기도 합니다.
왜냐면 이렇게 하면 미리 파일에서 다 읽어 와서 UI 요소 목록도 가져오고 그에 대응해 타입도 추론할 것이기 때문입니다.
이 방법의 단점은 귀찮고 번거롭다는 것입니다. UI 파일을 사용하는 장점이 UI 파일만 바꿔주면 화면을 쉽게 변경할 수 있다는 건데 PY 파일로 바꾸는 번거로운 과정이 추가됩니다.
그래서 저는 2번 방법을 사용하고 있습니다.
물론 UI 를 PY로 자동으로 바꾸는 편리한 방법이 있으면 이 방법을 이용하는 걸 추천드립니다.
2022-09-18 추가
Stackover Flow에서 UI 파일을 자동으로 PY로 컴파일 해주는 좋은 "파이썬 코드"를 찾아서 조금 수정해서 정리합니다.
원래는 2번 방법을 사용하고 있었으나 이제 UI를 PY로 컴파일 해주는 작업을 자동화 함으로써 본 방법이 제일 좋은 해결방법인 거 같습니다.
웬만해선 이 방법을 사용해주시면 됩니다.
# ui_loader.py
# UI 파일을 PY로 자동 변환한후 읽어온다
# PY로 변환작업을 거치지 않으면 IDE의 자동 완성 기능이 활성화 되지 않는다
# EX) uic.loadUiType(your_ui)[0] => 자동 완성이 제대로 작동하지 않음!!
# 출처 : https://stackoverflow.com/questions/58770646/autocomplete-from-ui
from distutils.dep_util import newer
import os
from PyQt5 import uic
def ui_auto_complete(ui_dir, ui_to_py_dir):
encoding = 'utf-8'
# UI 파일이 존재하지 않으면 아무 작업도 수행하지 않는다.
if not os.path.isfile(ui_dir):
print("The required file does not exist.")
return
# UI 파일이 업데이트 됬는지 확인하고, 업데이트 되었으면 *.py로 변환한다
if not newer(ui_dir, ui_to_py_dir):
print("UI has not changed!")
else:
print("UI changed detected, compiling...")
# ui 파일이 업데이트 되었다, py파일을 연다.
fp = open(ui_to_py_dir, "w", encoding=encoding)
# ui 파일을 py파일로 컴파일한다.
uic.compileUi(ui_dir, fp,
execute=True, indent=4, from_imports=True)
fp.close()
우선 ui_loader.py 에 다음 코드를 작성합니다. 저희가 호출해서 사용할 함수는 ui_auto_complete() 함수입니다. 저는 모듈화를 위해서 코드 파일로 따로 뺐으나 그냥 작성중이신 코드에 저 함수와 import 구분을 복붙 하셔도 됩니다.
뭐가 됐던 ui_auto_complete() 함수만 호출해서 사용하실 수 있으면 됩니다.
ui_auto_complete(ui_dir, ui_to_py_dir) 함수는 간단하게 컴파일할 대상 ui 파일의 경로, ui를 py로 바꿔서 저장할 경로를 제공해주면 ui 파일을 py 파일로 컴파일 해주는 함수입니다.
# Main UI Load
Ui_MainWindow = uic.loadUiType("main_ui_path_here")[0] # 이 부분을 주석처리
class Main(QMainWindow, Ui_MainWindow):
# 여기서 상속된 UI 요소들에 접근
# 기능을 추가한다..
그리고 기존에 이렇게 uic.loadUiType() 를 이용해 UI를 동적로딩하던 것을 주석 처리해주시고
# import 위치를 수동으로 정하기 위해 잠시 포맷팅을 꺼준다.
# fmt: off
ui_auto_complete("main.ui", "main.py") # ui 파일 컴파일
# ui 컴파일 이후 UI를 가져온다
from main import Ui_MainWindow
# fmt: on
다음과 같이 코드를 수정해줍니다. ui_auto_complete() 함수가 실행된 이후 ui 파일을 main.py로 바꿔서 컴파일 할것인데, 이후 파이썬의 from ... import ... 를 이용해 ui 데이터를 가져오시면 끝입니다.
ui_auto_complete() 이후에 *.py 파일이 생성되므로 import 구문은 반드시 그 이후에 작성해주셔야 합니다. 위 아래 # fmt : off, # fmt : on 이라는 주석이 보이는데 포맷터가 자꾸 자동으로 from ... import ... 구문을 최상단에 올려서 자동 포맷팅을 저 코드 부분만 무시하도록 주석을 따로 달아두었습니다.
이제 UI 파일을 PY로 컴파일 해서 import 된 상태기 때문에 ui 파일의 요소를 파이썬 코드로써 이해할 수 있으며, from import 로 추가했기 때문에 자동적으로 IDE가 (Vscode던 Pycharm이던) UI의 모든 요소를 알게되어 자동 완성이 이루어집니다.
또한 UI 파일을 파이썬 파일로 변환시켰기에, 이제 따로 ui 파일을 프로그램에 포함시킬 필요도 없어집니다 ^^.
2. 타입 힌트(타입 어노테이션) 를 이용해서 그 변수의 타입이 뭔지 코드에 알려준다
파이썬 3.5에서 추가된 타입 힌트를 사용해서 그 변수가 어떤 타입인지 알려주는 방법입니다.
이 방법의 장점은 문제를 아주 쉽게 해결 할 수 있다는 점입니다.
쉽게 해결할 수 있으나 그만한 단점이 있어서 저는 1번 방법을 추천하고 있습니다.
class Main(QMainWindow, Ui_MainWindow):
# 타입 힌트 (IDE 자동 완성 지원을 위해)
articleView: QTableWidget
txt_repeat: QLineEdit
txt_id: QLineEdit
txt_keyword: QLineEdit
btn_search: QPushButton
comboBox: QComboBox
txt_status: QLabel
다음과 같이 Class 맨 윗부분에 각 요소들이 어떤 Qt 요소인지 콜론으로 알려주시면 됩니다.
웹 개발을 좀 해보신 분들은 아마 타입스크립트가 떠오르실 겁니다 ㅎㅎ
저 QTableWidget이나, QLineEdit 부분은 QtDesigner 부분을 보면서 똑같이 맞춰주시면 됩니다.
이렇게 Class 최상단에 타입을 명시해주면 타입을 알게되어 Vscode의 자동 완성 기능이 활성화되게 됩니다. 이제 기존에 보이지 않았던 QTableWidget 에서 사용할 수 있는 함수나 데이터들이 보이게 됩니다.
물론 이 방법의 경우 UI를 동적 로딩해서 가져오는건 여전하고, 저희가 하드코딩 해서 타입을 직접 적어준 것이기 때문에 UI 에서 요소 이름이 바뀌면 코드에 가서도 수정을 해줘야 합니다. 이게 단점이 되겠네요.
* 이런 문제를 근본적으로 해결하고 싶으시면 그냥 아래 제가 위에서 설명한 1번 방법대로 하시면 됩니다.
def __init__(self):
super().__init__()
self.setupUi(self)
self.MainTable: QTableWidget = self.articleView
이렇게 Class 생성자로 가서 self. 를 통해 다른 이름으로 지정해주는 것도 가능합니다.
어쨌던 둘 중에 괜찮은 방법을 양자택일 하시면 되겠습니다.
파이썬 타입 힌트에 대한 오해?
파이썬에서 타입을 적어주는 걸 보고 아 파이썬은 정적 타입을 지향하는 구나! 하고 오해하시면 안됩니다. 파이썬은 타입스크립트 처럼 정적 타이핑을 지향하는게 아닌 여전히 동적 타입을 지향합니다.
a라는 변수에 int로 타입 힌트를 주고 문자열을 넣어도 오류가 발생하지 않습니다. 말 그대로 "힌트" 일 뿐이죠.
https://security-nanglam.tistory.com/417
참고
https://stackoverflow.com/questions/58770646/autocomplete-from-ui
https://security-nanglam.tistory.com/417
'프로그래밍 > Python' 카테고리의 다른 글
[Python] 구글 코랩(Google Colaboratory)에서 Selenium 사용하기 (18) | 2023.01.22 |
---|---|
[Python] 멀티 쓰레딩 vs 멀티 프로세싱 비교 (0) | 2022.10.23 |
디시인사이드 간편 글 검색기(탐색기) v0.15 (26) | 2022.08.31 |
많은 파일을 한꺼번에 폴더로 정리하자! Directory Packager (0) | 2022.04.29 |
[PyQT5] SFTP를 이용한 만능 자동 업데이트 런처 구현 (13) | 2022.04.22 |