I'm writing a custom word processor as a hobby project. I'm using Python and PyQt6.
I wrote the following. The intent is that if I select some text and apply Bold formatting (for example, by hitting "ctrl-b"), it will toggle the formatting. Specifically, it should remove the bold formatting if all of the selected text is bold. Otherwise, it will apply the bold formatting.
class OvidFont:
def __init__(self, ovid) -> None:
self.textEditor = ovid.textEditor
def setBoldText(self) -> None:
fmt = QTextCharFormat()
if self.textEditor.currentCharFormat().fontWeight() != QFont.Weight.Bold:
print(" setting bold") # for debugging
fmt.setFontWeight(QFont.Weight.Bold)
else:
print(" setting normal") # for debugging
fmt.setFontWeight(QFont.Weight.Normal)
self.textEditor.textCursor().mergeCharFormat(fmt)
However, it won't remove the bold formatting.
For example, in the sentence "this is a test", if I select "is a" and apply the bold formatting, I get "this is a test", with the "is a" properly bold. However, with the selection in place, it still remains bold if I hit "ctrl-b". If I deselect either the first or last character, the toggling of bold works as expected. (I've tried reversing the if/else logic, but that fails, too).
What am I missing?
Update: I've added a working, minimal test case at https://gist.github.com/Ovid/65936985c6838c0220620cf40ba935fa
The issue with setBoldText
function was that it checked the bold status using self.textEditor.currentCharFormat().fontWeight()
, which only reflects the formatting of the character at the current cursor position, not the entire selected text. If your cursor was at the beginning or end of the selection, it might not accurately represent the formatting of the whole selected range.
So I used the existing cursor and adjust it if necessary to check the formatting and directly apply the new font weight using setFontWeight()
on the QTextEdit
.
Now it looks like that:
Updated code:
import sys
from PyQt6.QtWidgets import QTextEdit, QToolButton, QApplication, QMainWindow, QToolBar
from PyQt6.QtGui import QFont, QShortcut, QKeySequence, QTextCharFormat, QTextCursor
class OvidFont:
def __init__(self, ovid) -> None:
self.textEditor = ovid.textEditor
def setBoldText(self):
cursor = self.textEditor.textCursor()
# If there's a selection, and the cursor is not at the block start and at the beginning of the selection,
# move the cursor to the end of the selection
if cursor.hasSelection() and not cursor.atBlockStart() and cursor.position() == cursor.selectionStart():
cursor.setPosition(cursor.selectionEnd())
# Check if the text (either selected or where the cursor is) is bold
is_bold = cursor.charFormat().fontWeight() == QFont.Weight.Bold
# Apply the new weight based on the current state
new_weight = QFont.Weight.Normal if is_bold else QFont.Weight.Bold
self.textEditor.setFontWeight(new_weight)
print(f"Bold set to: {'Normal' if is_bold else 'Bold'}")
class Ovid(QMainWindow):
def __init__(self):
super().__init__()
self.initUI()
def initUI(self):
self.setWindowTitle("Ovid")
self.setGeometry(100, 100, 200, 200)
self.textEditor = QTextEdit()
self.setCentralWidget(self.textEditor)
self.fonts = OvidFont(self)
self.toolbar = QToolBar("Main Toolbar")
self.addToolBar(self.toolbar)
bold_button = QToolButton()
bold_button.setText("B")
bold_button.setFont(QFont("Arial", 16, QFont.Weight.Bold))
bold_button.setToolTip("Bold")
bold_button.clicked.connect(self.fonts.setBoldText)
self.toolbar.addWidget(bold_button)
QShortcut(QKeySequence("Ctrl+B"), self, self.fonts.setBoldText)
def main():
app = QApplication(sys.argv)
ex = Ovid()
ex.show()
sys.exit(app.exec())
if __name__ == "__main__":
main()