I am trying to build the basic function in a text editor where the font-selector box is automatically updated when you click on a new chunk of text, so that it reflects the current font of that text. (Minimal code posted below.)
I've succeeded in getting the desired behavior. However, for an unknown reason, the key line of code to update the QFontComboBox is suppressing the ability of that combobox to actually update the font of text when it is changed.
In other words, it seems like I can only have it one way - either the QFontComboBox can update the font in the QTextBrowser, or the text where the cursor is in the QTextBrowser can update the QFontComboBox, but not both. I can recover the ability to set the font by commenting out a single line: self.fontBox.setCurrentFont(cursor.charFormat().font())
Some idiosyncrasies of my project that may be involved in the bug:
- I am using a QTextBrowser instead of a QTextEdit because I want the ability to insert clickable links and it is easier to implement in the QTextBrowser.
- I have separated the menu bars into a separate widget called "TextBars", because I want the menu bar interface to be able to exist separately from the editor that these menus are controlling. (An example application is if I put a proxy of a text editor into a QGraphicsItem within a QGraphicsViewer, and want to control this editor from a widget outside the viewer.)
- I have configured the text editor to enter/exit "edit mode" when double-clicked. When in edit mode, the editor accepts keyboard input and the menu bars become visible and linked to the text editor.
Here's the whole minimal example code. Thank you to anyone who can help!
import sys
from PyQt5 import QtGui, QtWidgets
TEST_TEXT = '<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd">\
<html><head><meta name="qrichtext" content="1" />\
<style type="text/css">\
p, li { white-space: pre-wrap; }\
</style>\
</head>\
<body style=" font-family:\'MS Shell Dlg 2\'; font-size:7.875pt; font-weight:400; font-style:normal;">\
<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">\
<span style=" font-family:\'MS Shell Dlg 2\'; font-size:6pt;">XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX</span>\
</p>\
</body>\
</html>'
class TextBrowserCustom(QtWidgets.QTextBrowser):
def __init__(self, parent=None):
super(TextBrowserCustom, self).__init__()
self.is_active = False
self.text_menu = None
self.setHtml(TEST_TEXT)
self.setReadOnly(True)
def set_text_menu(self, text_menu):
self.text_menu = text_menu
def mouseDoubleClickEvent(self, event):
if not self.is_active:
self.text_menu.set_text_edit(self)
self.setReadOnly(False)
else:
self.text_menu.set_text_edit(None)
self.setReadOnly(True)
self.is_active = not self.is_active
class TextBars(QtWidgets.QMainWindow):
def __init__(self, parent=None):
super(TextBars, self).__init__()
self.text_edit = None
self.formatbar = self.addToolBar("Format")
self.allow_font_box_changes = True
self.allow_set_editor_font_size = True
def set_text_edit(self, text_edit):
self.text_edit = text_edit
if self.text_edit:
self.init_UI()
else:
self.clear_UI()
def init_UI(self):
self.fontBox = QtWidgets.QFontComboBox(self)
self.fontBox.setMinimumContentsLength(8)
self.fontBox.setCurrentFont(QtGui.QFont('Arial', pointSize=10, weight=-1, italic=False))
self.fontSize = QtWidgets.QSpinBox(self)
self.fontSize.setValue(10)
self.fontSize.setSuffix(" pt")
self.formatbar.addWidget(self.fontBox)
self.formatbar.addWidget(self.fontSize)
self.updateFontSelector()
self.updateFontSizeSelector()
self.fontBox.currentFontChanged.connect(self.setEditorFont)
self.fontSize.valueChanged.connect(self.setEditorFontSize)
self.text_edit.cursorPositionChanged.connect(self.updateFontSelector)
self.text_edit.cursorPositionChanged.connect(self.updateFontSizeSelector)
self.formatbar.show()
self.setMaximumSize(10000, self.sizeHint().height())
def updateFontSelector(self):
cursor = self.text_edit.textCursor()
if not (cursor.charFormat().font() == self.fontBox.currentFont()):
self.allow_set_editor_font = False
self.fontBox.setCurrentFont(cursor.charFormat().font())
self.allow_set_editor_font = True
def updateFontSizeSelector(self):
cursor = self.text_edit.textCursor()
if not (cursor.charFormat().fontPointSize() == self.fontSize.value()):
self.allow_set_editor_font_size = False
self.fontSize.setValue(cursor.charFormat().fontPointSize())
self.allow_set_editor_font_size = True
def setEditorFont(self, font):
if self.allow_set_editor_font:
self.text_edit.blockSignals(True)
self.text_edit.setCurrentFont(font)
self.text_edit.blockSignals(False)
def setEditorFontSize(self, size):
if self.allow_set_editor_font_size:
self.text_edit.setFontPointSize(size)
def clear_UI(self):
self.formatbar.clear()
self.formatbar.hide()
self.setMaximumSize(10000, self.sizeHint().height())
if __name__ == "__main__":
app = QtWidgets.QApplication(sys.argv)
window = QtWidgets.QMainWindow()
layout = QtWidgets.QVBoxLayout()
text_browser_custom = TextBrowserCustom()
text_bars = TextBars()
text_bars.set_text_edit(None)
text_browser_custom.set_text_menu(text_bars)
layout.addWidget( text_bars )
layout.addWidget( text_browser_custom )
widget = QtWidgets.QWidget()
widget.setLayout(layout)
window.setCentralWidget(widget)
window.show()
sys.exit(app.exec_())