728x90

1. 목표

더보기
  1. 메뉴 Format 의 Bold, Italic, Underline 액션을 이용해서
    선택한 문자 또는 커서 위치 이후에 입력되는 문자에
    굵게, 기울임, 밑줄 스타일을 적용하는 기능을 구현한다.

  2. QTextEdit 전체에 폰트를 바꾸는 방식이 아니라
    QTextCharFormat 과 QFont 를 사용하여
    선택된 문자 영역에만 서식을 적용하는 방법을 이해한다.

  3. 현재 상태를 토글 형태로 구현해서
    Bold 버튼을 한 번 누르면 굵게, 한 번 더 누르면 원래 두께로 돌아가는
    사용성 좋은 편집 기능을 구현한다.

  4. Qt Designer 에서 만든 QAction(actionBold, actionItalic, actionUnderline)을
    메인 윈도우 슬롯 메서드와 연결하는 패턴을 익힌다.

  5. 문서 내용은 QTextEdit 내부의 QTextDocument 안에서만 다룹니다

 

2. 전체 로직

더보기
# mainwindow.py

from PySide6.QtGui import QFont, QTextCharFormat

. . .

class MainWindow(QMainWindow):
    def __init__(self, parent=None):

        . . .
        
        self.ui.actionBold.triggered.connect(self.text_bold)
        self.ui.actionItalic.triggered.connect(self.text_italic)
        self.ui.actionUnderline.triggered.connect(self.text_underline)
 
        . . .

    def _merge_format_on_selection(self, fmt: QTextCharFormat):
        cursor = self.editor.textCursor()
        if cursor.hasSelection():
            cursor.mergeCharFormat(fmt)
        else:
            self.editor.mergeCurrentCharFormat(fmt)

    def text_bold(self):
        cursor = self.editor.textCursor()
        fmt = cursor.charFormat()
        is_bold = fmt.fontWeight() > QFont.Normal
        fmt.setFontWeight(QFont.Normal if is_bold else QFont.Bold)
        self._merge_format_on_selection(fmt)

    def text_italic(self):
        cursor = self.editor.textCursor()
        fmt = cursor.charFormat()
        current_italic = fmt.fontItalic()
        fmt.setFontItalic(not current_italic)
        self._merge_format_on_selection(fmt)

    def text_underline(self):
        cursor = self.editor.textCursor()
        fmt = cursor.charFormat()
        current_underline = fmt.fontUnderline()
        fmt.setFontUnderline(not current_underline)
        self._merge_format_on_selection(fmt)
# main.py

import sys
from PySide6.QtWidgets import QApplication

from mainwindow import MainWindow

if __name__ == "__main__":
    app = QApplication(sys.argv)
    w = MainWindow()
    w.show()
    sys.exit(app.exec())

 

3. 필요한 import + 주석 설명

더보기
from PySide6.QtGui import QFont, QTextCharFormat
# QFont
#   글꼴 이름, 크기, 굵기, 기울임 등 폰트 정보를 표현하는 클래스
#   여기서는 굵게 여부를 확인하고, 굵게 또는 일반 두께로 설정할 때 사용한다
#
# QTextCharFormat
#   개별 문자에 대한 서식 정보를 표현하는 클래스
#   굵게, 기울임, 밑줄, 글자색 등 스타일을 담고
#   선택된 텍스트에 병합 적용할 수 있다


4. 메뉴 액션과 슬롯 연결하기

더보기
class MainWindow(QMainWindow):
    def __init__(self, parent=None):
        super().__init__(parent)

        self.ui = Ui_MainWindow()
        self.ui.setupUi(self)

        # 중앙 편집기 QTextEdit 에 자주 접근하므로 별칭을 만들어 둔다
        self.editor = self.ui.textEdit

        # 9.10 문자 스타일 편집 기능 구현
        # Format 메뉴의 Bold, Italic, Underline 액션을
        # MainWindow 의 슬롯 메서드에 연결한다

        # Bold 메뉴와 툴바 아이콘을 text_bold 함수에 연결
        self.ui.actionBold.triggered.connect(self.text_bold)

        # Italic 메뉴와 툴바 아이콘을 text_italic 함수에 연결
        self.ui.actionItalic.triggered.connect(self.text_italic)

        # Underline 메뉴와 툴바 아이콘을 text_underline 함수에 연결
        self.ui.actionUnderline.triggered.connect(self.text_underline)

 

  • 메뉴 Format 에서 Bold, Italic, Underline 을 클릭했을 때
  • 툴바의 굵게/기울임/밑줄 버튼을 눌렀을 때
  • 단축키 Ctrl+B, Ctrl+I, Ctrl+E 를 눌렀을 때

 

5. 문자 포멧 기능 구현 함수

더보기

5-1. 선택 영역에 서식 병합을 도와주는 헬퍼 메서드

    def _merge_format_on_selection(self, fmt: QTextCharFormat):
        # 현재 QTextEdit 의 텍스트 커서를 가져온다
        cursor = self.editor.textCursor()

        # 선택된 텍스트가 있을 경우
        if cursor.hasSelection():
            # 선택된 문자들에 문자 서식을 병합 적용한다
            # 이미 존재하는 서식 위에 변경된 부분만 덮어쓴다
            cursor.mergeCharFormat(fmt)
        else:
            # 선택 영역이 없으면
            # 이후에 입력될 텍스트의 기본 문자 서식을 변경한다
            self.editor.mergeCurrentCharFormat(fmt)

이 함수 덕분에

  • 선택이 있을 때는 그 부분에만 스타일을 적용하고
  • 선택이 없을 때는 커서 이후에 입력되는 문자에 스타일을 적용

 

 

 

 

5-2. 굵게 토글 기능

    def text_bold(self):
        # 현재 텍스트 커서를 가져온다
        cursor = self.editor.textCursor()

        # 현재 커서 위치의 문자 서식을 가져온다
        fmt = cursor.charFormat()

        # 현재 글자가 굵은 상태인지 확인한다
        # QFont.Normal 보다 크면 굵게라고 판단할 수 있다
        is_bold = fmt.fontWeight() > QFont.Normal

        # 이미 굵게라면 일반 두께로,
        # 일반 두께라면 굵게로 토글한다
        fmt.setFontWeight(QFont.Normal if is_bold else QFont.Bold)

        # 선택된 영역 또는 이후 입력될 문자에 서식을 병합 적용한다
        self._merge_format_on_selection(fmt)
  • 버튼을 누를 때마다 현재 상태를 읽어서 반대로 바꾸는 토글 방식이다.
  • 이렇게 하면 사용자가 Bold 버튼을 한 번 더 눌러 굵게 상태를 해제할 수 있다.

 

 

 

 

5-3. 기울임 토글 기능

    def text_italic(self):
        # 현재 텍스트 커서를 가져온다
        cursor = self.editor.textCursor()

        # 현재 문자 서식을 가져온다
        fmt = cursor.charFormat()

        # 현재 기울임 상태를 읽어 온다
        current_italic = fmt.fontItalic()

        # True 는 False 로, False 는 True 로 반대로 토글한다
        fmt.setFontItalic(not current_italic)

        # 선택 영역 또는 이후 입력될 문자에 서식을 병합 적용한다
        self._merge_format_on_selection(fmt)
  • 문자 서식 객체에서 기울임 여부를 읽어오고
  • not 연산으로 반전시키는 전형적인 토글 패턴을 사용한다.

 

 

 

 

5-4. 밑줄 토글 기능

    def text_underline(self):
        # 현재 텍스트 커서를 가져온다
        cursor = self.editor.textCursor()

        # 현재 문자 서식을 가져온다
        fmt = cursor.charFormat()

        # 현재 밑줄 상태를 읽어 온다
        current_underline = fmt.fontUnderline()

        # 밑줄 상태를 반대로 토글한다
        fmt.setFontUnderline(not current_underline)

        # 선택 영역 또는 이후 입력될 문자에 서식을 병합 적용한다
        self._merge_format_on_selection(fmt)

 

  • 기울임과 동일한 패턴으로 밑줄 여부를 토글한다.
  • QTextCharFormat 이 밑줄, 취소선 등 다양한 효과를 모두 담당한다는 점을 보여준다.

 


 

6. 실행 테스트


7. 학습 주요 포인트

더보기

 

QTextEdit 전체 폰트 변경과 문자 서식 변경의 차이

  • setFont 를 사용하면 편집기 전체 기본 폰트가 변경된다.
  • QTextCharFormat 과 mergeCharFormat 을 사용하면 선택된 문자에만 서식을 적용할 수 있다.
  • 워드 프로세서와 같은 리치 텍스트 편집 기능을 구현할 때는 문자 단위 서식이 필수적이다.

 

토글 방식으로 상태를 제어하는 패턴

  • 현재 상태를 읽어서 반대로 설정하는 토글 패턴은
    Bold, Italic, Underline 이 모두 동일한 구조를 가진다.
  • 학생들에게 상태 기반 UI 구현의 전형적인 예제로 설명하기 좋다.

 

커서와 선택 영역의 역할

  • cursor.hasSelection 을 통해
    선택이 있는지 없는지에 따라 동작을 다르게 할 수 있다.
  • 선택이 있으면 그 영역에만 스타일을 적용하고
    없으면 이후 입력될 문자에 스타일을 적용하는 UX는 대부분의 텍스트 편집기와 동일한 동작이다.

 

QAction 과 슬롯 연결 구조

  • ui_form.py 에서 QAction 들이 이미 생성되어 있고
    메인 코드에서는 그 액션의 triggered 시그널만 슬롯 메서드에 연결하면 된다.
  • 이 덕분에 메뉴, 툴바, 단축키가 모두 같은 기능을 공유하게 된다.

 

파일 입출력과의 분리

  • 이 단계는 어디까지나 문서 내용의 스타일 편집만 다루고 디스크 파일 저장과는 분리되어 있다.
  • 파일 저장과 읽기는 다른 단계에서 QFile, QTextStream 을 사용하는 것으로 묶고
    스타일 편집은 메모리 상의 QTextDocument 만 다루는 구조가 설계 측면에서 깔끔하다.

 

단계별 완성 파일