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 ๋งŒ ๋‹ค๋ฃจ๋Š” ๊ตฌ์กฐ๊ฐ€ ์„ค๊ณ„ ์ธก๋ฉด์—์„œ ๊น”๋”ํ•˜๋‹ค.

 

๋‹จ๊ณ„๋ณ„ ์™„์„ฑ ํŒŒ์ผ