使用视图委托的方法连接 QMainWindow 中的操作 (PySide/Qt/PyQt)
Connect action in QMainWindow with method of view's delegate (PySide/Qt/PyQt)
我有一个 QTreeView
显示来自 QStandardItemModel
的数据。树的其中一列显示了一个委托,允许用户编辑和显示富文本。下面是一个将编辑限制为粗体的 SSCCE(使用键盘快捷键)。
当用户编辑其中一项时,我如何设置它以便除了使用键盘快捷键 (CTRL-B) 切换粗体度外,用户还可以使用工具栏图标切换它?
到目前为止,键盘快捷键效果很好(您可以双击、编辑文本,CTRL-B 将切换为粗体)。但是,我还没有弄清楚如何将工具栏中的粗体按钮连接到适当的插槽:
self.boldTextAction.triggered.connect(self.emboldenText)
我只是坐在那里什么都不做:
def emboldenText(self):
print "Make selected text bold...How do I do this?"
如果主要 window 的中央小部件是文本编辑器,事情会很容易:我可以直接调用文本编辑器的切换粗体方法。不幸的是,当用户双击开始编辑树时,文本编辑器仅由树视图的委托暂时生成。
也就是说,我们有这样复杂的关系:
QMainWindow -> QTreeView -> Delegate.CreateEditor ->
QTextEdit.toggleBold()
如何从主要 window 中访问 toggleBold() 以供工具栏操作使用,特别是考虑到编辑器仅在用户打开时临时创建?
我意识到这可能不是 PySide/Qt 问题,而是 Python/OOP 问题,所以我添加了其他可能相关的标签。如果您能帮助我改进 choice/jargon,我们将不胜感激。
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('<b>High</b>')]
item01 = [QtGui.QStandardItem('Call temp agency'), QtGui.QStandardItem('<b>Extremely</b> <i>high</i>')]
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 = "<font color={0}>{1}</font>".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.restore()
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 <b>Ctrl+b</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()
对于那些想要全树体验的人请注意,使用工具栏中的按钮,在这里你可以将它放在与脚本相同的文件夹中(将名称更改为 boldText.png
:
我认为从设计的角度来看,顶部 window 是一种全局。您已经描述了一种以这种方式对待它的行为,并且(正如 ekhumoro 所说)这非常需要您向编辑器提供对该顶部 window 的访问权限。
一个非常简单的方法是在 createEditor
方法中调用 parent.window()
。可能是这样的:
parent.window().boldTextAction.triggered.connect(editor.toggleBold)
这似乎对我有用。
我有一个 QTreeView
显示来自 QStandardItemModel
的数据。树的其中一列显示了一个委托,允许用户编辑和显示富文本。下面是一个将编辑限制为粗体的 SSCCE(使用键盘快捷键)。
当用户编辑其中一项时,我如何设置它以便除了使用键盘快捷键 (CTRL-B) 切换粗体度外,用户还可以使用工具栏图标切换它?
到目前为止,键盘快捷键效果很好(您可以双击、编辑文本,CTRL-B 将切换为粗体)。但是,我还没有弄清楚如何将工具栏中的粗体按钮连接到适当的插槽:
self.boldTextAction.triggered.connect(self.emboldenText)
我只是坐在那里什么都不做:
def emboldenText(self):
print "Make selected text bold...How do I do this?"
如果主要 window 的中央小部件是文本编辑器,事情会很容易:我可以直接调用文本编辑器的切换粗体方法。不幸的是,当用户双击开始编辑树时,文本编辑器仅由树视图的委托暂时生成。
也就是说,我们有这样复杂的关系:
QMainWindow -> QTreeView -> Delegate.CreateEditor -> QTextEdit.toggleBold()
如何从主要 window 中访问 toggleBold() 以供工具栏操作使用,特别是考虑到编辑器仅在用户打开时临时创建?
我意识到这可能不是 PySide/Qt 问题,而是 Python/OOP 问题,所以我添加了其他可能相关的标签。如果您能帮助我改进 choice/jargon,我们将不胜感激。
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('<b>High</b>')]
item01 = [QtGui.QStandardItem('Call temp agency'), QtGui.QStandardItem('<b>Extremely</b> <i>high</i>')]
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 = "<font color={0}>{1}</font>".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.restore()
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 <b>Ctrl+b</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()
对于那些想要全树体验的人请注意,使用工具栏中的按钮,在这里你可以将它放在与脚本相同的文件夹中(将名称更改为 boldText.png
:
我认为从设计的角度来看,顶部 window 是一种全局。您已经描述了一种以这种方式对待它的行为,并且(正如 ekhumoro 所说)这非常需要您向编辑器提供对该顶部 window 的访问权限。
一个非常简单的方法是在 createEditor
方法中调用 parent.window()
。可能是这样的:
parent.window().boldTextAction.triggered.connect(editor.toggleBold)
这似乎对我有用。