aboutsummaryrefslogtreecommitdiffstats
path: root/examples/widgets/richtext/textedit/textedit.py
diff options
context:
space:
mode:
Diffstat (limited to 'examples/widgets/richtext/textedit/textedit.py')
-rw-r--r--examples/widgets/richtext/textedit/textedit.py721
1 files changed, 721 insertions, 0 deletions
diff --git a/examples/widgets/richtext/textedit/textedit.py b/examples/widgets/richtext/textedit/textedit.py
new file mode 100644
index 000000000..8019446f0
--- /dev/null
+++ b/examples/widgets/richtext/textedit/textedit.py
@@ -0,0 +1,721 @@
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+import sys
+from PySide6.QtCore import (QCoreApplication, QDir, QFile, QFileInfo,
+ QMimeDatabase, QUrl, Qt, Slot)
+from PySide6.QtGui import (QAction, QActionGroup, QColor, QGuiApplication,
+ QFont, QFontDatabase, QFontInfo, QIcon,
+ QKeySequence, QPalette, QPixmap, QTextBlockFormat,
+ QTextCharFormat, QTextCursor, QTextDocumentWriter,
+ QTextFormat, QTextListFormat)
+from PySide6.QtWidgets import (QApplication, QMainWindow, QColorDialog, QComboBox,
+ QDialog, QFileDialog, QFontComboBox,
+ QTextEdit, QMessageBox)
+from PySide6.QtPrintSupport import (QAbstractPrintDialog, QPrinter,
+ QPrintDialog, QPrintPreviewDialog)
+
+
+ABOUT = """This example demonstrates Qt's
+ rich text editing facilities in self.action, providing an example
+ document for you to experiment with."""
+
+
+MIME_TYPES = ["text/html", "text/markdown", "text/plain"]
+
+
+RSRC_PATH = ":/images/mac" if sys.platform == 'darwin' else ":/images/win"
+
+
+STYLES = ["Standard", "Bullet List (Disc)", "Bullet List (Circle)",
+ "Bullet List (Square)", "Task List (Unchecked)",
+ "Task List (Checked)", "Ordered List (Decimal)",
+ "Ordered List (Alpha lower)", "Ordered List (Alpha upper)",
+ "Ordered List (Roman lower)", "Ordered List (Roman upper)",
+ "Heading 1", "Heading 2", "Heading 3", "Heading 4", "Heading 5",
+ "Heading 6"]
+
+
+class TextEdit(QMainWindow):
+ def __init__(self, parent=None):
+ super().__init__(parent)
+ if sys.platform == 'darwin':
+ self.setUnifiedTitleAndToolBarOnMac(True)
+ self.setWindowTitle(QCoreApplication.applicationName())
+
+ self._text_edit = QTextEdit(self)
+ self._text_edit.currentCharFormatChanged.connect(self.current_char_format_changed)
+ self._text_edit.cursorPositionChanged.connect(self.cursor_position_changed)
+ self.setCentralWidget(self._text_edit)
+
+ self.setToolButtonStyle(Qt.ToolButtonFollowStyle)
+ self.setup_file_actions()
+ self.setup_edit_actions()
+ self.setup_text_actions()
+
+ help_menu = self.menuBar().addMenu("Help")
+ help_menu.addAction("About", self.about)
+ help_menu.addAction("About &Qt", qApp.aboutQt) # noqa: F821
+
+ text_font = QFont("Helvetica")
+ text_font.setStyleHint(QFont.SansSerif)
+ self._text_edit.setFont(text_font)
+ self.font_changed(self._text_edit.font())
+ self.color_changed(self._text_edit.textColor())
+ self.alignment_changed(self._text_edit.alignment())
+
+ document = self._text_edit.document()
+ document.modificationChanged.connect(self._action_save.setEnabled)
+ document.modificationChanged.connect(self.setWindowModified)
+ document.undoAvailable.connect(self._action_undo.setEnabled)
+ document.redoAvailable.connect(self._action_redo.setEnabled)
+ self.setWindowModified(document.isModified())
+ self._action_save.setEnabled(document.isModified())
+ self._action_undo.setEnabled(document.isUndoAvailable())
+ self._action_redo.setEnabled(document.isRedoAvailable())
+
+ self._action_cut.setEnabled(False)
+ self._text_edit.copyAvailable.connect(self._action_cut.setEnabled)
+ self._action_copy.setEnabled(False)
+ self._text_edit.copyAvailable.connect(self._action_copy.setEnabled)
+
+ QGuiApplication.clipboard().dataChanged.connect(self.clipboard_data_changed)
+
+ self._text_edit.setFocus()
+ self.set_current_file_name('')
+
+ # Use dark text on light background on macOS, also in dark mode.
+ if sys.platform == 'darwin':
+ pal = self._text_edit.palette()
+ pal.setColor(QPalette.Base, QColor(Qt.white))
+ pal.setColor(QPalette.Text, QColor(Qt.black))
+ self._text_edit.setPalette(pal)
+
+ def closeEvent(self, e):
+ if self.maybe_save():
+ e.accept()
+ else:
+ e.ignore()
+
+ def setup_file_actions(self):
+ tb = self.addToolBar("File self.actions")
+ menu = self.menuBar().addMenu("&File")
+
+ icon = QIcon.fromTheme(QIcon.ThemeIcon.DocumentNew,
+ QIcon(RSRC_PATH + "/filenew.png"))
+ a = menu.addAction(icon, "&New", self.file_new)
+ tb.addAction(a)
+ a.setPriority(QAction.LowPriority)
+ a.setShortcut(QKeySequence.New)
+
+ icon = QIcon.fromTheme(QIcon.ThemeIcon.DocumentOpen,
+ QIcon(RSRC_PATH + "/fileopen.png"))
+ a = menu.addAction(icon, "&Open...", self.file_open)
+ a.setShortcut(QKeySequence.Open)
+ tb.addAction(a)
+
+ menu.addSeparator()
+
+ icon = QIcon.fromTheme(QIcon.ThemeIcon.DocumentSave,
+ QIcon(RSRC_PATH + "/filesave.png"))
+ self._action_save = menu.addAction(icon, "&Save", self.file_save)
+ self._action_save.setShortcut(QKeySequence.Save)
+ self._action_save.setEnabled(False)
+ tb.addAction(self._action_save)
+
+ a = menu.addAction("Save &As...", self.file_save_as)
+ a.setPriority(QAction.LowPriority)
+ menu.addSeparator()
+
+ icon = QIcon.fromTheme(QIcon.ThemeIcon.DocumentPrint,
+ QIcon(RSRC_PATH + "/fileprint.png"))
+ a = menu.addAction(icon, "&Print...", self.file_print)
+ a.setPriority(QAction.LowPriority)
+ a.setShortcut(QKeySequence.Print)
+ tb.addAction(a)
+
+ icon = QIcon.fromTheme("fileprint", QIcon(RSRC_PATH + "/fileprint.png"))
+ menu.addAction(icon, "Print Preview...", self.file_print_preview)
+
+ icon = QIcon.fromTheme("exportpdf", QIcon(RSRC_PATH + "/exportpdf.png"))
+ a = menu.addAction(icon, "&Export PDF...", self.file_print_pdf)
+ a.setPriority(QAction.LowPriority)
+ a.setShortcut(Qt.CTRL | Qt.Key_D)
+ tb.addAction(a)
+
+ menu.addSeparator()
+
+ a = menu.addAction("&Quit", self.close)
+ a.setShortcut(Qt.CTRL | Qt.Key_Q)
+
+ def setup_edit_actions(self):
+ tb = self.addToolBar("Edit self.actions")
+ menu = self.menuBar().addMenu("&Edit")
+
+ icon = QIcon.fromTheme(QIcon.ThemeIcon.EditUndo,
+ QIcon(RSRC_PATH + "/editundo.png"))
+ self._action_undo = menu.addAction(icon, "&Undo", self._text_edit.undo)
+ self._action_undo.setShortcut(QKeySequence.Undo)
+ tb.addAction(self._action_undo)
+
+ icon = QIcon.fromTheme(QIcon.ThemeIcon.EditRedo,
+ QIcon(RSRC_PATH + "/editredo.png"))
+ self._action_redo = menu.addAction(icon, "&Redo", self._text_edit.redo)
+ self._action_redo.setPriority(QAction.LowPriority)
+ self._action_redo.setShortcut(QKeySequence.Redo)
+ tb.addAction(self._action_redo)
+ menu.addSeparator()
+
+ icon = QIcon.fromTheme(QIcon.ThemeIcon.EditCut,
+ QIcon(RSRC_PATH + "/editcut.png"))
+ self._action_cut = menu.addAction(icon, "Cu&t", self._text_edit.cut)
+ self._action_cut.setPriority(QAction.LowPriority)
+ self._action_cut.setShortcut(QKeySequence.Cut)
+ tb.addAction(self._action_cut)
+
+ icon = QIcon.fromTheme(QIcon.ThemeIcon.EditCopy,
+ QIcon(RSRC_PATH + "/editcopy.png"))
+ self._action_copy = menu.addAction(icon, "&Copy", self._text_edit.copy)
+ self._action_copy.setPriority(QAction.LowPriority)
+ self._action_copy.setShortcut(QKeySequence.Copy)
+ tb.addAction(self._action_copy)
+
+ icon = QIcon.fromTheme(QIcon.ThemeIcon.EditPaste,
+ QIcon(RSRC_PATH + "/editpaste.png"))
+ self._action_paste = menu.addAction(icon, "&Paste", self._text_edit.paste)
+ self._action_paste.setPriority(QAction.LowPriority)
+ self._action_paste.setShortcut(QKeySequence.Paste)
+ tb.addAction(self._action_paste)
+
+ md = QGuiApplication.clipboard().mimeData()
+ if md:
+ self._action_paste.setEnabled(md.hasText())
+
+ def setup_text_actions(self):
+ tb = self.addToolBar("Format self.actions")
+ menu = self.menuBar().addMenu("F&ormat")
+
+ icon = QIcon.fromTheme(QIcon.ThemeIcon.FormatTextBold,
+ QIcon(RSRC_PATH + "/textbold.png"))
+ self._action_text_bold = menu.addAction(icon, "&Bold", self.text_bold)
+ self._action_text_bold.setShortcut(Qt.CTRL | Qt.Key_B)
+ self._action_text_bold.setPriority(QAction.LowPriority)
+ bold = QFont()
+ bold.setBold(True)
+ self._action_text_bold.setFont(bold)
+ tb.addAction(self._action_text_bold)
+ self._action_text_bold.setCheckable(True)
+
+ icon = QIcon.fromTheme(QIcon.ThemeIcon.FormatTextItalic,
+ QIcon(RSRC_PATH + "/textitalic.png"))
+ self._action_text_italic = menu.addAction(icon, "&Italic", self.text_italic)
+ self._action_text_italic.setPriority(QAction.LowPriority)
+ self._action_text_italic.setShortcut(Qt.CTRL | Qt.Key_I)
+ italic = QFont()
+ italic.setItalic(True)
+ self._action_text_italic.setFont(italic)
+ tb.addAction(self._action_text_italic)
+ self._action_text_italic.setCheckable(True)
+
+ icon = QIcon.fromTheme(QIcon.ThemeIcon.FormatTextUnderline,
+ QIcon(RSRC_PATH + "/textunder.png"))
+ self._action_text_underline = menu.addAction(icon, "&Underline",
+ self.text_underline)
+ self._action_text_underline.setShortcut(Qt.CTRL | Qt.Key_U)
+ self._action_text_underline.setPriority(QAction.LowPriority)
+ underline = QFont()
+ underline.setUnderline(True)
+ self._action_text_underline.setFont(underline)
+ tb.addAction(self._action_text_underline)
+ self._action_text_underline.setCheckable(True)
+
+ menu.addSeparator()
+
+ icon = QIcon.fromTheme(QIcon.ThemeIcon.FormatJustifyLeft,
+ QIcon(RSRC_PATH + "/textleft.png"))
+ self._action_align_left = QAction(icon, "&Left", self)
+ self._action_align_left.setShortcut(Qt.CTRL | Qt.Key_L)
+ self._action_align_left.setCheckable(True)
+ self._action_align_left.setPriority(QAction.LowPriority)
+ icon = QIcon.fromTheme(QIcon.ThemeIcon.FormatJustifyCenter,
+ QIcon(RSRC_PATH + "/textcenter.png"))
+ self._action_align_center = QAction(icon, "C&enter", self)
+ self._action_align_center.setShortcut(Qt.CTRL | Qt.Key_E)
+ self._action_align_center.setCheckable(True)
+ self._action_align_center.setPriority(QAction.LowPriority)
+ icon = QIcon.fromTheme(QIcon.ThemeIcon.FormatJustifyRight,
+ QIcon(RSRC_PATH + "/textright.png"))
+ self._action_align_right = QAction(icon, "&Right", self)
+ self._action_align_right.setShortcut(Qt.CTRL | Qt.Key_R)
+ self._action_align_right.setCheckable(True)
+ self._action_align_right.setPriority(QAction.LowPriority)
+ icon = QIcon.fromTheme(QIcon.ThemeIcon.FormatJustifyFill,
+ QIcon(RSRC_PATH + "/textjustify.png"))
+ self._action_align_justify = QAction(icon, "&Justify", self)
+ self._action_align_justify.setShortcut(Qt.CTRL | Qt.Key_J)
+ self._action_align_justify.setCheckable(True)
+ self._action_align_justify.setPriority(QAction.LowPriority)
+ icon = QIcon.fromTheme(QIcon.ThemeIcon.FormatIndentMore,
+ QIcon(RSRC_PATH + "/format-indent-more.png"))
+ self._action_indent_more = menu.addAction(icon, "&Indent", self.indent)
+ self._action_indent_more.setShortcut(Qt.CTRL | Qt.Key_BracketRight)
+ self._action_indent_more.setPriority(QAction.LowPriority)
+ icon = QIcon.fromTheme(QIcon.ThemeIcon.FormatIndentLess,
+ QIcon(RSRC_PATH + "/format-indent-less.png"))
+ self._action_indent_less = menu.addAction(icon, "&Unindent",
+ self.unindent)
+ self._action_indent_less.setShortcut(Qt.CTRL | Qt.Key_BracketLeft)
+ self._action_indent_less.setPriority(QAction.LowPriority)
+
+ # Make sure the alignLeft is always left of the alignRight
+ align_group = QActionGroup(self)
+ align_group.triggered.connect(self.text_align)
+
+ if QGuiApplication.isLeftToRight():
+ align_group.addAction(self._action_align_left)
+ align_group.addAction(self._action_align_center)
+ align_group.addAction(self._action_align_right)
+ else:
+ align_group.addAction(self._action_align_right)
+ align_group.addAction(self._action_align_center)
+ align_group.addAction(self._action_align_left)
+ align_group.addAction(self._action_align_justify)
+
+ tb.addActions(align_group.actions())
+ menu.addActions(align_group.actions())
+ tb.addAction(self._action_indent_more)
+ tb.addAction(self._action_indent_less)
+ menu.addAction(self._action_indent_more)
+ menu.addAction(self._action_indent_less)
+
+ menu.addSeparator()
+
+ pix = QPixmap(16, 16)
+ pix.fill(Qt.black)
+ self._action_text_color = menu.addAction(pix, "&Color...", self.text_color)
+ tb.addAction(self._action_text_color)
+
+ icon = QIcon(RSRC_PATH + "/textundercolor.png")
+ self._action_underline_color = menu.addAction(icon, "Underline color...",
+ self.underline_color)
+ tb.addAction(self._action_underline_color)
+
+ menu.addSeparator()
+
+ icon = QIcon.fromTheme("status-checkbox-checked",
+ QIcon(RSRC_PATH + "/checkbox-checked.png"))
+ self._action_toggle_check_state = menu.addAction(icon, "Chec&ked")
+ self._action_toggle_check_state.toggled.connect(self.set_checked)
+ self._action_toggle_check_state.setShortcut(Qt.CTRL | Qt.Key_K)
+ self._action_toggle_check_state.setCheckable(True)
+ self._action_toggle_check_state.setPriority(QAction.LowPriority)
+ tb.addAction(self._action_toggle_check_state)
+
+ tb = self.addToolBar("Format self.actions")
+ tb.setAllowedAreas(Qt.TopToolBarArea | Qt.BottomToolBarArea)
+ self.addToolBarBreak(Qt.TopToolBarArea)
+ self.addToolBar(tb)
+
+ self._combo_style = QComboBox(tb)
+ tb.addWidget(self._combo_style)
+ self._combo_style.addItems(STYLES)
+
+ self._combo_style.activated.connect(self.text_style)
+
+ self._combo_font = QFontComboBox(tb)
+ tb.addWidget(self._combo_font)
+ self._combo_font.textActivated.connect(self.text_family)
+
+ self._combo_size = QComboBox(tb)
+ self._combo_size.setObjectName("comboSize")
+ tb.addWidget(self._combo_size)
+ self._combo_size.setEditable(True)
+
+ standard_sizes = QFontDatabase.standardSizes()
+ for size in standard_sizes:
+ self._combo_size.addItem(str(size))
+ index = standard_sizes.index(QApplication.font().pointSize())
+ self._combo_size.setCurrentIndex(index)
+
+ self._combo_size.textActivated.connect(self.text_size)
+
+ def load(self, f):
+ if not QFile.exists(f):
+ return False
+ file = QFile(f)
+ if not file.open(QFile.ReadOnly):
+ return False
+
+ data = file.readAll()
+ db = QMimeDatabase()
+ mime_type_name = db.mimeTypeForFileNameAndData(f, data).name()
+ text = data.data().decode('utf8')
+ if mime_type_name == "text/html":
+ file_url = QUrl(f) if f[0] == ':' else QUrl.fromLocalFile(f)
+ options = QUrl.FormattingOptions(QUrl.RemoveFilename)
+ self._text_edit.document().setBaseUrl(file_url.adjusted(options))
+ self._text_edit.setHtml(text)
+ elif mime_type_name == "text/markdown":
+ self._text_edit.setMarkdown(text)
+ else:
+ self._text_edit.setPlainText(text)
+
+ self.set_current_file_name(f)
+ return True
+
+ def maybe_save(self):
+ if not self._text_edit.document().isModified():
+ return True
+
+ ret = QMessageBox.warning(self, QCoreApplication.applicationName(),
+ "The document has been modified.\n"
+ "Do you want to save your changes?",
+ QMessageBox.Save | QMessageBox.Discard
+ | QMessageBox.Cancel)
+ if ret == QMessageBox.Save:
+ return self.file_save()
+ if ret == QMessageBox.Cancel:
+ return False
+ return True
+
+ def set_current_file_name(self, fileName):
+ self._file_name = fileName
+ self._text_edit.document().setModified(False)
+
+ shown_name = QFileInfo(fileName).fileName() if fileName else "untitled.txt"
+ app_name = QCoreApplication.applicationName()
+ self.setWindowTitle(f"{shown_name}[*] - {app_name}")
+ self.setWindowModified(False)
+
+ @Slot()
+ def file_new(self):
+ if self.maybe_save():
+ self._text_edit.clear()
+ self.set_current_file_name("")
+
+ @Slot()
+ def file_open(self):
+ file_dialog = QFileDialog(self, "Open File...")
+ file_dialog.setAcceptMode(QFileDialog.AcceptOpen)
+ file_dialog.setFileMode(QFileDialog.ExistingFile)
+ file_dialog.setMimeTypeFilters(MIME_TYPES)
+ if file_dialog.exec() != QDialog.Accepted:
+ return
+ fn = file_dialog.selectedFiles()[0]
+ native_fn = QDir.toNativeSeparators(fn)
+ if self.load(fn):
+ self.statusBar().showMessage(f'Opened "{native_fn}"')
+ else:
+ self.statusBar().showMessage(f'Could not open "{native_fn}"')
+
+ @Slot()
+ def file_save(self):
+ if not self._file_name or self._file_name.startswith(":/"):
+ return self.file_save_as()
+
+ writer = QTextDocumentWriter(self._file_name)
+ document = self._text_edit.document()
+ success = writer.write(document)
+ native_fn = QDir.toNativeSeparators(self._file_name)
+ if success:
+ document.setModified(False)
+ self.statusBar().showMessage(f'Wrote "{native_fn}"')
+ else:
+ self.statusBar().showMessage(f'Could not write to file "{native_fn}"')
+ return success
+
+ @Slot()
+ def file_save_as(self):
+ file_dialog = QFileDialog(self, "Save as...")
+ file_dialog.setAcceptMode(QFileDialog.AcceptSave)
+
+ mime_types = MIME_TYPES
+ mime_types.insert(1, "application/vnd.oasis.opendocument.text")
+ file_dialog.setMimeTypeFilters(mime_types)
+ file_dialog.setDefaultSuffix("odt")
+ if file_dialog.exec() != QDialog.Accepted:
+ return False
+ fn = file_dialog.selectedFiles()[0]
+ self.set_current_file_name(fn)
+ return self.file_save()
+
+ @Slot()
+ def file_print(self):
+ printer = QPrinter(QPrinter.HighResolution)
+ dlg = QPrintDialog(printer, self)
+ if self._text_edit.textCursor().hasSelection():
+ dlg.setOption(QAbstractPrintDialog.PrintSelection)
+ dlg.setWindowTitle("Print Document")
+ if dlg.exec() == QDialog.Accepted:
+ self._text_edit.print_(printer)
+
+ @Slot()
+ def file_print_preview(self):
+ printer = QPrinter(QPrinter.HighResolution)
+ preview = QPrintPreviewDialog(printer, self)
+ preview.paintRequested.connect(self._text_edit.print_)
+ preview.exec()
+
+ @Slot()
+ def file_print_pdf(self):
+ file_dialog = QFileDialog(self, "Export PDF")
+ file_dialog.setAcceptMode(QFileDialog.AcceptSave)
+ file_dialog.setMimeTypeFilters(["application/pdf"])
+ file_dialog.setDefaultSuffix("pdf")
+ if file_dialog.exec() != QDialog.Accepted:
+ return
+ pdf_file_name = file_dialog.selectedFiles()[0]
+ printer = QPrinter(QPrinter.HighResolution)
+ printer.setOutputFormat(QPrinter.PdfFormat)
+ printer.setOutputFileName(pdf_file_name)
+ self._text_edit.document().print_(printer)
+ native_fn = QDir.toNativeSeparators(pdf_file_name)
+ self.statusBar().showMessage(f'Exported "{native_fn}"')
+
+ @Slot()
+ def text_bold(self):
+ fmt = QTextCharFormat()
+ weight = QFont.Bold if self._action_text_bold.isChecked() else QFont.Normal
+ fmt.setFontWeight(weight)
+ self.merge_format_on_word_or_selection(fmt)
+
+ @Slot()
+ def text_underline(self):
+ fmt = QTextCharFormat()
+ fmt.setFontUnderline(self._action_text_underline.isChecked())
+ self.merge_format_on_word_or_selection(fmt)
+
+ @Slot()
+ def text_italic(self):
+ fmt = QTextCharFormat()
+ fmt.setFontItalic(self._action_text_italic.isChecked())
+ self.merge_format_on_word_or_selection(fmt)
+
+ @Slot(str)
+ def text_family(self, f):
+ fmt = QTextCharFormat()
+ fmt.setFontFamilies({f})
+ self.merge_format_on_word_or_selection(fmt)
+
+ @Slot(str)
+ def text_size(self, p):
+ point_size = float(p)
+ if point_size > 0:
+ fmt = QTextCharFormat()
+ fmt.setFontPointSize(point_size)
+ self.merge_format_on_word_or_selection(fmt)
+
+ @Slot(int)
+ def text_style(self, styleIndex):
+ cursor = self._text_edit.textCursor()
+ style = QTextListFormat.ListStyleUndefined
+ marker = QTextBlockFormat.MarkerType.NoMarker
+
+ if styleIndex == 1:
+ style = QTextListFormat.ListDisc
+ elif styleIndex == 2:
+ style = QTextListFormat.ListCircle
+ elif styleIndex == 3:
+ style = QTextListFormat.ListSquare
+ elif styleIndex == 4:
+ if cursor.currentList():
+ style = cursor.currentList().format().style()
+ else:
+ style = QTextListFormat.ListDisc
+ marker = QTextBlockFormat.MarkerType.Unchecked
+ elif styleIndex == 5:
+ if cursor.currentList():
+ style = cursor.currentList().format().style()
+ else:
+ style = QTextListFormat.ListDisc
+ marker = QTextBlockFormat.MarkerType.Checked
+ elif styleIndex == 6:
+ style = QTextListFormat.ListDecimal
+ elif styleIndex == 7:
+ style = QTextListFormat.ListLowerAlpha
+ elif styleIndex == 8:
+ style = QTextListFormat.ListUpperAlpha
+ elif styleIndex == 9:
+ style = QTextListFormat.ListLowerRoman
+ elif styleIndex == 10:
+ style = QTextListFormat.ListUpperRoman
+
+ cursor.beginEditBlock()
+
+ block_fmt = cursor.blockFormat()
+
+ if style == QTextListFormat.ListStyleUndefined:
+ block_fmt.setObjectIndex(-1)
+ # H1 to H6, or Standard
+ heading_level = styleIndex - 11 + 1 if styleIndex >= 11 else 0
+ block_fmt.setHeadingLevel(heading_level)
+ cursor.setBlockFormat(block_fmt)
+
+ # H1 to H6: +3 to -2
+ size_adjustment = 4 - heading_level if heading_level != 0 else 0
+ fmt = QTextCharFormat()
+ fmt.setFontWeight(QFont.Bold if heading_level else QFont.Normal)
+ fmt.setProperty(QTextFormat.FontSizeAdjustment, size_adjustment)
+ cursor.select(QTextCursor.LineUnderCursor)
+ cursor.mergeCharFormat(fmt)
+ self._text_edit.mergeCurrentCharFormat(fmt)
+ else:
+ block_fmt.setMarker(marker)
+ cursor.setBlockFormat(block_fmt)
+ list_fmt = QTextListFormat()
+ if cursor.currentList():
+ list_fmt = cursor.currentList().format()
+ else:
+ list_fmt.setIndent(block_fmt.indent() + 1)
+ block_fmt.setIndent(0)
+ cursor.setBlockFormat(block_fmt)
+ list_fmt.setStyle(style)
+ cursor.createList(list_fmt)
+ cursor.endEditBlock()
+
+ @Slot()
+ def text_color(self):
+ col = QColorDialog.getColor(self._text_edit.textColor(), self)
+ if not col.isValid():
+ return
+ fmt = QTextCharFormat()
+ fmt.setForeground(col)
+ self.merge_format_on_word_or_selection(fmt)
+ self.color_changed(col)
+
+ @Slot()
+ def underline_color(self):
+ col = QColorDialog.getColor(Qt.black, self)
+ if not col.isValid():
+ return
+ fmt = QTextCharFormat()
+ fmt.setUnderlineColor(col)
+ self.merge_format_on_word_or_selection(fmt)
+ self.color_changed(col)
+
+ @Slot(QAction)
+ def text_align(self, a):
+ if a == self._action_align_left:
+ self._text_edit.setAlignment(Qt.AlignLeft | Qt.AlignAbsolute)
+ elif a == self._action_align_center:
+ self._text_edit.setAlignment(Qt.AlignHCenter)
+ elif a == self._action_align_right:
+ self._text_edit.setAlignment(Qt.AlignRight | Qt.AlignAbsolute)
+ elif a == self._action_align_justify:
+ self._text_edit.setAlignment(Qt.AlignJustify)
+
+ @Slot(bool)
+ def set_checked(self, checked):
+ self.text_style(5 if checked else 4)
+
+ @Slot()
+ def indent(self):
+ self.modify_indentation(1)
+
+ @Slot()
+ def unindent(self):
+ self.modify_indentation(-1)
+
+ def modify_indentation(self, amount):
+ cursor = self._text_edit.textCursor()
+ cursor.beginEditBlock()
+ if cursor.currentList():
+ list_fmt = cursor.currentList().format()
+ # See whether the line above is the list we want to move self item
+ # into, or whether we need a new list.
+ above = QTextCursor(cursor)
+ above.movePosition(QTextCursor.Up)
+ if (above.currentList()
+ and list_fmt.indent() + amount == above.currentList().format().indent()):
+ above.currentList().add(cursor.block())
+ else:
+ list_fmt.setIndent(list_fmt.indent() + amount)
+ cursor.createList(list_fmt)
+ else:
+ block_fmt = cursor.blockFormat()
+ block_fmt.setIndent(block_fmt.indent() + amount)
+ cursor.setBlockFormat(block_fmt)
+ cursor.endEditBlock()
+
+ @Slot(QTextCharFormat)
+ def current_char_format_changed(self, format):
+ self.font_changed(format.font())
+ self.color_changed(format.foreground().color())
+
+ @Slot()
+ def cursor_position_changed(self):
+ self.alignment_changed(self._text_edit.alignment())
+ list = self._text_edit.textCursor().currentList()
+ if list:
+ style = list.format().style()
+ if style == QTextListFormat.ListDisc:
+ self._combo_style.setCurrentIndex(1)
+ elif style == QTextListFormat.ListCircle:
+ self._combo_style.setCurrentIndex(2)
+ elif style == QTextListFormat.ListSquare:
+ self._combo_style.setCurrentIndex(3)
+ elif style == QTextListFormat.ListDecimal:
+ self._combo_style.setCurrentIndex(6)
+ elif style == QTextListFormat.ListLowerAlpha:
+ self._combo_style.setCurrentIndex(7)
+ elif style == QTextListFormat.ListUpperAlpha:
+ self._combo_style.setCurrentIndex(8)
+ elif style == QTextListFormat.ListLowerRoman:
+ self._combo_style.setCurrentIndex(9)
+ elif style == QTextListFormat.ListUpperRoman:
+ self._combo_style.setCurrentIndex(10)
+ else:
+ self._combo_style.setCurrentIndex(-1)
+ marker = self._text_edit.textCursor().block().blockFormat().marker()
+ if marker == QTextBlockFormat.MarkerType.NoMarker:
+ self._action_toggle_check_state.setChecked(False)
+ elif marker == QTextBlockFormat.MarkerType.Unchecked:
+ self._combo_style.setCurrentIndex(4)
+ self._action_toggle_check_state.setChecked(False)
+ elif marker == QTextBlockFormat.MarkerType.Checked:
+ self._combo_style.setCurrentIndex(5)
+ self._action_toggle_check_state.setChecked(True)
+ else:
+ heading_level = self._text_edit.textCursor().blockFormat().headingLevel()
+ new_level = heading_level + 10 if heading_level != 0 else 0
+ self._combo_style.setCurrentIndex(new_level)
+
+ @Slot()
+ def clipboard_data_changed(self):
+ md = QGuiApplication.clipboard().mimeData()
+ self._action_paste.setEnabled(md and md.hasText())
+
+ @Slot()
+ def about(self):
+ QMessageBox.about(self, "About", ABOUT)
+
+ def merge_format_on_word_or_selection(self, format):
+ cursor = self._text_edit.textCursor()
+ if not cursor.hasSelection():
+ cursor.select(QTextCursor.WordUnderCursor)
+ cursor.mergeCharFormat(format)
+ self._text_edit.mergeCurrentCharFormat(format)
+
+ def font_changed(self, f):
+ index = self._combo_font.findText(QFontInfo(f).family())
+ self._combo_font.setCurrentIndex(index)
+ index = self._combo_size.findText(str(f.pointSize()))
+ self._combo_size.setCurrentIndex(index)
+ self._action_text_bold.setChecked(f.bold())
+ self._action_text_italic.setChecked(f.italic())
+ self._action_text_underline.setChecked(f.underline())
+
+ def color_changed(self, c):
+ pix = QPixmap(16, 16)
+ pix.fill(c)
+ self._action_text_color.setIcon(pix)
+
+ def alignment_changed(self, a):
+ if a & Qt.AlignLeft:
+ self._action_align_left.setChecked(True)
+ elif a & Qt.AlignHCenter:
+ self._action_align_center.setChecked(True)
+ elif a & Qt.AlignRight:
+ self._action_align_right.setChecked(True)
+ elif a & Qt.AlignJustify:
+ self._action_align_justify.setChecked(True)