본문 바로가기
GUI 튜토리얼/PySide6 # Qt의 원조가 돌아왔다!

PySide6 메모장 만들기: 열기, 저장 및 끝내기 구현

by 일코 2021. 4. 9.

지난 시간에는

지난 시간에는 QtDesigner 내에서 ①파이썬 코드를 생성한 후 복사해서 파이참에 붙여넣는 방식으로 ui변환을 생략하고 파일을 직접 업데이트하는 방법을 보여드렸습니다. ②또한 "새 창(W)" 메뉴를 클릭했을 때 윈도우 인스턴스가 하나 더 생성되도록 시그널(triggered)과 슬롯(add_window메서드)을 연결하는 작업도 해보았고, 가장 중요하다고 할 수 있는 ③레이아웃과 플레인텍스트 위젯도 배치해보았습니다. 해당내용이 궁금하신 분은 아래 링크를 참고해주시기 바랍니다.

 

1. PySide6 메모장 만들기 - 위젯 추가하기

우린 지금 어디쯤? 지금 우리는 윈도우10의 기본프로그램 중 하나인 메모장을 PySide6로 클론코딩하는 작업을 진행하고 있습니다. 지난 포스팅에서는 메모장의 틀을 만들고, 메뉴바를 완성했습니

www.martinii.fun

그리고 이번 시간에는

그리고 이번 시간에는, 지난 시간에 이어 메뉴바의 기능을 몇 가지씩 보완해 가려고 합니다. 오늘은 차례대로 열기, 저장 및 끝내기를 구현해보려고 하는데, 끝내기는 좀 더 자세히 다뤄드려야 할 부분이 있어서 다음 포스팅까지 이어서 설명을 드려야 할 것 같습니다.

위의 열기(O), 저장(S) 및 끝내기(X)를 구현하겠습니다.

지난 시간까지 작성했던 최종 코드는 아래와 같습니다.

가장 중요한 코드는  

self.action_W.triggered.connect(self.add_window) = "메인윈도우의 새 창(W) 메뉴를 클릭하면 새 창이 생성"

from PySide6.QtWidgets import QApplication, QMainWindow
from notepad_mainwindow import Ui_MainWindow


class MainWindow(QMainWindow, Ui_MainWindow):
    def __init__(self):
        super(MainWindow, self).__init__()
        self.setupUi(self)
        self.windows = []
        self.action_W.triggered.connect(self.add_window)
        
    def add_window(self):
        add_window = MainWindow()
        self.windows.append(add_window)
        add_window.show()


app = QApplication()
window = MainWindow()
window.show()
app.exec_()

(지난 포스팅에서 ui파일의 "새 파일" 오브젝트이름을 "action_W"에서 "new_window"로 고쳤는데, 다시 "action_W"로 되돌려놓았습니다. 오브젝트이름과 액션, 그리고 메서드이름이 모호해지는 것 같아 기본설정-기존대로 다시 수정하였습니다. 유의하여 주시기 바랍니다.)

 

위 코드처럼, 슬롯이란 게 저렇게 간단한 코드로도 구현이 되는구나 하는 느낌을 받으셨기를 바랍니다. 사실 이번에 구현해볼 "열기"와 "저장"은 훨씬 쉽거든요. 다만 파이썬과 Qt의 기본 명령어를 몇 가지 사용해야 합니다. 가급적이면 암기해 두시면 좋겠는데, 암기하지 않으셔도 앞으로 종종 사용하시게 될 코드들이라면 암기가 자동으로 될 거니까 걱정하지 않으셔도 됩니다.

 

미리 알아두셔야 할 게 크게 세 가지입니다.

 

1. 파이썬의 open 함수

2. Qt의 QFileDialog (getOpenFileName 및 getSaveFileName)

3. Qt의 plainTextEdit (setPlainText 및 toPlainText)

하나씩 간단히 설명을 드리겠습니다.

 

1. 파이썬의 open 함수

파이썬에서 일반텍스트 파일을 열거나 저장할 때 사용하는 함수가 바로 open입니다.

일반적인 사용방법은 아래와 같습니다.

with open("main.py") as f:
    print(f.read())  
    

파이참에서 콘솔을 열어서(Ctrl-Tab-C) 위 두 줄의 코드를 입력하고 엔터를 치면, 현재 main.py 파일의 코드가 출력됩니다. 반대로 특정 파일을 생성해서 내용을 채워넣고 싶을 때에는 open 함수를 아래처럼 사용하면 됩니다.

with open("main1.py", "w") as f:
    f.write("print('hello world!')")

둘 다 기본적으로 open 함수를 통해 파일객체를 생성합니다. (관례상 이 파일객체를 f로 정의합니다.) 중요한 건 f는 "텍스트"가 아니라 "객체"이며, read나 write 등의 메서드를 지니고 있다는 점입니다. 이에 유의하셔야 합니다. 

with문을 open 함수와 함께 사용하는 가장 중요한 이유는, open으로 파일을 열었으면 닫아줘야 하는데, 위처럼 with문을 사용하면 필요한 시간만큼만 열고, 동작이 종료되면 with문법 덕분에 해당 텍스트 파일을 자동으로 닫아주기 때문입니다. 정리를 굉장히 잘 해 준 포스팅이 있어 참고하시라고 아래 첨부해놓습니다. 설명은 이만 줄입니다.

 

Python - Text 파일 읽고 쓰는 방법 (read, write, append) (codechacha.com)

 

Python - Text 파일 읽고 쓰는 방법 (read, write, append)

Text file을 읽고 쓰는 방법을 소개합니다. C, Java에서 파일을 읽고 쓰는 방법과 유사합니다. Access mode가 있어서 읽기 전용으로 파일을 사용할 것인지, 쓰기 전용으로 파일을 사용할 것인지 정할 수

codechacha.com

이지스퍼블리싱 Do it! 점프 투 파이썬 (개정판)

 

이지스퍼블리싱 Do it! 점프 투 파이썬 (개정판)

COUPANG

www.coupang.com


2. PySide6의 QFileDialog

아래에 Qt for Python 공식 홈페이지의 QFileDialog에 대한 링크를 붙여놓습니다.

 

QFileDialog — Qt for Python

QFileDialog The QFileDialog class provides a dialog that allow users to select files or directories. More… Synopsis Static functions def getExistingDirectory ([parent=None[, caption=””[, dir=””[, options=QFileDialog.ShowDirsOnly]]]]) def getExist

doc.qt.io

페이지 상단에 보시면 QFileDialog가 상속받은 클래스들이 나와 있습니다. 

QFileDialog처럼 QDialog에서 상속받은 위젯은 show() 메서드를 사용하지 않아도 바로 대화창이 뜬다는 특징이 있습니다. 어떤 모양인지 한 번 실행해봅시다. QFileDialog만 실행해볼 예정이므로 파이참에서 임시로 스크래치 파일 하나만 만들어보겠습니다.

 

파이참에서 Ctrl-Shift-Alt-Insert 키 네 개를 같이 누른 후 팝업에서 Python을 선택하고 엔터를 누릅니다.

스크래치파일을 열었습니다. 짧은 코드를 테스트할 때 콘솔에 직접 치지 말고 스크래치를 사용하면 효율적입니다.

스크래치파일에 아래 코드를 입력하고 바로 실행해보겠습니다. 가장 짧게 파일선택창을 실행할 수 있는 코드입니다.

from PySide6.QtWidgets import QApplication, QFileDialog

app = QApplication()
window = QFileDialog()
filename = window.getOpenFileName()
print(filename)
app.exec_()

파이참의 강점인 오토임포트(Alt-Enter)를 사용하여 코드를 완성해보았습니다.

출력결과를 보시면 상당히 괜찮은(그리고 자연스러운) 비주얼의 파일선택창이 하나 뜨는 걸 보실 수 있습니다. 그리고 출력결과를 보시면, getOpenFileName은 파일 선택시 원소가 두 개인 튜플을 반환하고, 선택한 파일명은 0번인덱스에 있음을 확인하였습니다. 향후 우리 메모장의 "열기" 및 "저장"시에 0번인덱스의 파일명을 활용할 예정입니다.

 

3. Qt의 plainTextEdit (setPlainText 및 toPlainText)

"plainTextEdit"가 도대체 어디서 튀어나온 변수인가 생각하실 수 있지만, 우리가 QtDesigner에서 plain text widget을 메인윈도우에 넣었던 것 기억나시죠? 그 위젯의 이름을 별도로 지정하지 않으면 이 객체 이름이 plainTextEdit로 설정됩니다. 이 위젯에 유용한 메서드가 여러 가지 있는데, (메서드만 거의 백 개쯤 됩니다ㄷㄷㄷ;;;) 그 중에 두 가지만 이번 튜토리얼에 사용해보려고 합니다. 바로 setPlainText(string)과 toPlainText()입니다. setPlainText는 텍스트위젯의 문자열을 string 변수의 문자열 값으로 교체하는 메서드이고, setPlainText는 해당 위젯 내의 텍스트를 일반 문자열 자료형으로 변환하여 반환하는 메서드입니다. 각각 "열기"와 "저장"에 사용됩니다.

 

기본적인 설명은 마쳤으니 코드를 보여드리겠습니다.

from PySide6.QtWidgets import QApplication, QMainWindow, QFileDialog
from notepad_mainwindow import Ui_MainWindow


class MainWindow(QMainWindow, Ui_MainWindow):
    def __init__(self):
        super(MainWindow, self).__init__()
        self.setupUi(self)
        self.windows = []

        self.action_W.triggered.connect(self.add_window)  # 새 창(W)
        self.action_O.triggered.connect(self.open_file)  # 열기(O)
        self.action_S.triggered.connect(self.save_file)  # 저장(S)

    def add_window(self):  # 새 창(W)
        add_window = MainWindow()
        self.windows.append(add_window)
        add_window.show()

    def open_file(self):  # 열기(O)
        file_name = QFileDialog.getOpenFileName(self)
        if file_name[0]:
            with open(file_name[0], encoding='UTF-8') as f:
                text = f.read()
            self.plainTextEdit.setPlainText(text)

    def save_file(self):  # 저장(S)
        file_name = QFileDialog.getSaveFileName(self)
        if file_name[0]:
            text = self.plainTextEdit.toPlainText()
            with open(file_name[0], 'w', encoding='UTF-8') as f:
                f.write(text)



app = QApplication()
window = MainWindow()
window.show()
app.exec_()

위 코드를 실행해보겠습니다.

저장 및 열기를 실행해보았습니다. 잘 되는 것 같네요.

마치기 전에 한 가지 기능을 더 추가해보겠습니다. 바로 "끝내기(A)"입니다. 끝내기 기능을 완벽히 구현하기 위해서는, 저장할지 말지 혹은 끝내기를 취소하는 다이얼로그를 추가해야 해서, 이 부분은 다음시간에 이어서 진행하겠습니다.

 

대신 이번 시간에는 파이썬 코드를 직접 추가하지 않고, QtDesigner 내에서 기본 시그널과 기본 슬롯으로 끝내기를 구현해보겠습니다.

액션 이름은 action_X네요.

action_X 의 triggered 시그널에 MainWindow의 close() 슬롯을 연결하겠습니다.

action_X 의 triggered 시그널에 MainWindow의 close() 슬롯을 연결하겠습니다. 우측 하단에 보시면 시그널/슬롯 편집기라는 창이 보입니다.

우측 하단의 시그널/슬롯 편집기를 사용해봅시다.

+키를 누르고,

 

송신자에 action_X,

시그널에 triggered,

수신자에 MainWindow

슬롯에 close()

 

를 각각 입력합니다.

마우스로만 입력을 완료하였습니다.

창이 잘 닫히는지 테스트해보겠습니다.

 

미리보기(Ctrl-R) 후 "끝내기"를 클릭하자 창이 잘 닫혔습니다.

기본적으로 제공하는 시그널과 슬롯의 종류가 위젯마다 굉장히 많기 때문에, 기본 기능을 충분히 활용하시면 좋겠습니다.

 

여기까지 저장한 main.py 파이썬 코드와 notepad_mainwindow.ui파일, 그리고 uic로 변환한 notepad_mainwindow.py 파일을 아래 첨부하오니 튜토리얼 진행하실 때 참고하시기 바랍니다.

 

main.py
0.00MB
notepad_mainwindow.ui
0.01MB
notepad_mainwindow.py
0.01MB

다음 포스팅에서는 더 알찬 내용으로 인사드리겠습니다.

감사합니다.

행복한 하루 되세요!!!

참고한 자료

 

QFileDialog — Qt for Python

QFileDialog The QFileDialog class provides a dialog that allow users to select files or directories. More… Synopsis Static functions def getExistingDirectory ([parent=None[, caption=””[, dir=””[, options=QFileDialog.ShowDirsOnly]]]]) def getExist

doc.qt.io

 

유튜브 "재즐보프" 채널의 "만들면서 배우는 파이큐티" 재생목록

 


국내 유일의 파이썬+한컴오피스 업무자동화 입문강의

 

움짤로 빠르게 배우는 파이썬-아래아한글 자동화 레시피 - 인프런 | 강의

파이썬으로 아래아한글을 다루는 짧은 예제코드들을 소개하고, 중간중간의 결과를 GIF로 보여드립니다. 동영상 강의가 아니지만 오히려 빠르게 배울 수 있고, 따라하기도 쉽습니다., - 강의 소개

www.inflearn.com

 

댓글