Acción de conexión en QMainWindow con el delegado del método de visualización (PySide / Qt / PyQt)

Tengo un QTreeView muestra datos de un QStandardItemModel . Una de las columnas del árbol se muestra con un delegado que le permite al usuario editar y mostrar texto enriquecido. A continuación se muestra un SSCCE que limita la edición a negrita (con el método abreviado de teclado).

Cuando el usuario está editando uno de los elementos, ¿cómo puedo configurarlo para que, además de alternar la audacia con el método abreviado del teclado (CTRL-B), el usuario también pueda alternarlo utilizando el icono de la barra de herramientas?

introduzca la descripción de la imagen aquí

Hasta ahora, el método abreviado de teclado funciona muy bien (puede hacer doble clic, editar texto y CTRL-B cambiará a negrita). Sin embargo, no he descubierto cómo conectar el botón en negrita en la barra de herramientas a la ranura apropiada:

  self.boldTextAction.triggered.connect(self.emboldenText) 

donde tengo esto solo sentado allí sin hacer nada:

 def emboldenText(self): print "Make selected text bold...How do I do this?" 

Las cosas serían fáciles si el widget central de la ventana principal fuera el editor de texto: podría invocar directamente el método en negrita del editor de texto. Desafortunadamente, el editor de texto solo lo genera de forma transitoria el delegado de la vista de árbol cuando el usuario hace doble clic para comenzar a editar el árbol.

Es decir, tenemos esta complicada relación:

QMainWindow -> QTreeView -> Delegate.CreateEditor -> QTextEdit.toggleBold ()

¿Cómo accedo a toggleBold () desde dentro de la ventana principal para que lo utilice la acción de la barra de herramientas, especialmente dado que el editor solo se crea temporalmente cuando lo abre el usuario?

Me doy cuenta de que esto puede no ser tanto una pregunta de PySide / Qt como una pregunta de Python / OOP, así que he incluido tags potencialmente relevantes adicionales. Cualquier ayuda para mejorar mi elección de palabras / jerga también sería apreciada.

SSCCE

 #!/usr/bin/env python import platform import sys from PySide import QtGui, QtCore class MainTree(QtGui.QMainWindow): def __init__(self, tree, parent = None): QtGui.QMainWindow.__init__(self) self.setAttribute(QtCore.Qt.WA_DeleteOnClose) self.setCentralWidget(tree) self.createStatusBar() self.createBoldAction() self.createToolbar() self.tree = tree #self.htmlDelegate = self.tree.itemDelegateForColumn(1) def createStatusBar(self): self.status = self.statusBar() self.status.setSizeGripEnabled(False) self.status.showMessage("In editor, keyboard to toggle bold") def createToolbar(self): self.textToolbar = self.addToolBar("Text actions") self.textToolbar.addAction(self.boldTextAction) def createBoldAction(self): self.boldTextAction = QtGui.QAction("Bold", self) self.boldTextAction.setIcon(QtGui.QIcon("boldText.png")) self.boldTextAction.triggered.connect(self.emboldenText) self.boldTextAction.setStatusTip("Make selected text bold") def emboldenText(self): print "Make selected text bold...How do I do this? It's stuck in RichTextLineEdit" class HtmlTree(QtGui.QTreeView): def __init__(self, parent = None): QtGui.QTreeView.__init__(self) model = QtGui.QStandardItemModel() model.setHorizontalHeaderLabels(['Task', 'Priority']) rootItem = model.invisibleRootItem() item0 = [QtGui.QStandardItem('Sneeze'), QtGui.QStandardItem('Low')] item00 = [QtGui.QStandardItem('Tickle nose'), QtGui.QStandardItem('Low')] item1 = [QtGui.QStandardItem('Get a job'), QtGui.QStandardItem('High')] item01 = [QtGui.QStandardItem('Call temp agency'), QtGui.QStandardItem('Extremely high')] rootItem.appendRow(item0) item0[0].appendRow(item00) rootItem.appendRow(item1) item1[0].appendRow(item01) self.setModel(model) self.expandAll() self.resizeColumnToContents(0) self.setToolTip("Use keyboard to toggle bold") self.setItemDelegate(HtmlPainter(self)) class HtmlPainter(QtGui.QStyledItemDelegate): def __init__(self, parent=None): QtGui.QStyledItemDelegate.__init__(self, parent) def paint(self, painter, option, index): if index.column() == 1: text = index.model().data(index) #default role is display (for edit consider fixing Valign prob) palette = QtGui.QApplication.palette() document = QtGui.QTextDocument() document.setDefaultFont(option.font) #Set text (color depends on whether selected) if option.state & QtGui.QStyle.State_Selected: displayString = "{1}".format(palette.highlightedText().color().name(), text) document.setHtml(displayString) else: document.setHtml(text) #Set background color bgColor = palette.highlight().color() if (option.state & QtGui.QStyle.State_Selected)\ else palette.base().color() painter.save() painter.fillRect(option.rect, bgColor) document.setTextWidth(option.rect.width()) offset_y = (option.rect.height() - document.size().height())/2 painter.translate(option.rect.x(), option.rect.y() + offset_y) document.drawContents(painter) painter.restre() else: QtGui.QStyledItemDelegate.paint(self, painter, option, index) def sizeHint(self, option, index): fm = option.fontMetrics if index.column() == 1: text = index.model().data(index) document = QtGui.QTextDocument() document.setDefaultFont(option.font) document.setHtml(text) return QtCore.QSize(document.idealWidth() + 5, fm.height()) return QtGui.QStyledItemDelegate.sizeHint(self, option, index) def createEditor(self, parent, option, index): if index.column() == 1: editor = RichTextLineEdit(parent) editor.returnPressed.connect(self.commitAndCloseEditor) return editor else: return QtGui.QStyledItemDelegate.createEditor(self, parent, option, index) def commitAndCloseEditor(self): editor = self.sender() if isinstance(editor, (QtGui.QTextEdit, QtGui.QLineEdit)): self.commitData.emit(editor) self.closeEditor.emit(editor, QtGui.QAbstractItemDelegate.NoHint) class RichTextLineEdit(QtGui.QTextEdit): returnPressed = QtCore.Signal() def __init__(self, parent=None): QtGui.QTextEdit.__init__(self, parent) self.setLineWrapMode(QtGui.QTextEdit.NoWrap) self.setTabChangesFocus(True) self.setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff) self.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff) fontMetrics = QtGui.QFontMetrics(self.font()) h = int(fontMetrics.height() * (1.4 if platform.system() == "Windows" else 1.2)) self.setMinimumHeight(h) self.setMaximumHeight(int(h * 1.2)) self.setToolTip("Press Ctrl+b to toggle bold") def toggleBold(self): self.setFontWeight(QtGui.QFont.Normal if self.fontWeight() > QtGui.QFont.Normal else QtGui.QFont.Bold) def sizeHint(self): return QtCore.QSize(self.document().idealWidth() + 5, self.maximumHeight()) def minimumSizeHint(self): fm = QtGui.QFontMetrics(self.font()) return QtCore.QSize(fm.width("WWWW"), self.minimumHeight()) def keyPressEvent(self, event): '''This just handles all keyboard shortcuts, and stops retun from returning''' if event.modifiers() & QtCore.Qt.ControlModifier: handled = False if event.key() == QtCore.Qt.Key_B: self.toggleBold() handled = True if handled: event.accept() return if event.key() in (QtCore.Qt.Key_Enter, QtCore.Qt.Key_Return): self.returnPressed.emit() event.accept() else: QtGui.QTextEdit.keyPressEvent(self, event) def main(): app = QtGui.QApplication(sys.argv) myTree = HtmlTree() #myTree.show() myMainTree = MainTree(myTree) myMainTree.show() sys.exit(app.exec_()) if __name__ == "__main__": main() 

Tenga en cuenta para aquellos que desean la experiencia de árbol completo, con el botón en la barra de herramientas, aquí puede colocarlo en la misma carpeta que el script (cambie el nombre a boldText.png :

introduzca la descripción de la imagen aquí

Creo que desde el punto de vista del diseño, la ventana superior es una especie de global. Ya ha descrito un comportamiento que lo trata de esa manera y (como ha dicho ekhumoro) que prácticamente requiere que usted proporcione acceso a esa ventana superior al editor.

Una forma muy sencilla de hacerlo es llamar a parent.window() en el método createEditor . Quizás algo como:

 parent.window().boldTextAction.triggered.connect(editor.toggleBold) 

Eso parece funcionar para mí.