唯一合理的方法是使用您的选项2:创建自定义委托。这几乎就是委托用于的情况:使用
createEditor
创建自定义编辑器(例如旋转框或富文本编辑器等),并实现一个
paint
方法,让您完全控制输入数据后的外观。虽然可能有其他方法可以完成此操作,但它们几乎肯定比使用委托更糟糕。
因此,为了使其工作,您需要重新实现
paint
以及
createEditor
用于
QStyledItemDelegate
。
不幸的是,对于实现
createEditor
,Qt没有提供本地的富文本行编辑器(也就是说,没有像富文本那样的
QLineEdit
)。幸运的是,Mark Summerfield在他关于PyQt的书的第13章中编写了这样的函数,因此我已将其采用到下面的完全工作示例中,其中包括一个树视图在主窗口中,具有使用工具栏或上下文(右键单击)菜单或编辑器打开时的键盘快捷键切换文本属性的功能。
相关文章
以下线程中,我直接得到了许多有关实现这些功能的帮助:
图标
这是工具栏中使用的图片:
代码
这是代码。很抱歉它有点大,但它包含了许多对于学习代理(正如原帖显然是)可能有用的东西,因此我决定不进行编辑:
import sys
from xml.sax.saxutils import escape as escape
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.createActions()
self.createToolbar()
self.tree = tree
self.setGeometry(500,150,400,300)
def createStatusBar(self):
self.status = self.statusBar()
self.status.setSizeGripEnabled(False)
self.status.showMessage("Ready")
def createActions(self):
'''Create all actions to be used in toolbars/menus: calls createAction()'''
self.boldTextAction = self.createAction("&Bold",
shortcut = QtGui.QKeySequence.Bold, iconName = "boldText", tip = "Embolden",
status = "Toggle bold", disabled = True)
self.italicTextAction = self.createAction("&Italic",
shortcut = QtGui.QKeySequence.Italic, iconName = "italicText", tip = "Italicize",
status = "Toggle italics", disabled = True)
self.underlineTextAction = self.createAction("&Underline",
shortcut = QtGui.QKeySequence.Underline, iconName = "underlineText", tip = "Underline",
status = "Toggle underline", disabled = True)
self.strikeoutTextAction = self.createAction("Stri&keout",
shortcut = QtGui.QKeySequence("Ctrl+K"), iconName = "strikeoutText", tip = "Strikeout",
status = "Toggle strikeout", disabled = True)
def createAction(self, text, slot = None, shortcut = None, iconName = None,
tip = None, status = None, disabled = False):
'''Creates each individual action'''
action = QtGui.QAction(text, self)
if iconName is not None:
action.setIcon(QtGui.QIcon("{0}.png".format(iconName)))
if shortcut is not None:
action.setShortcut(shortcut)
if tip is not None:
action.setToolTip(tip)
if status is not None:
action.setStatusTip(status)
if slot is not None:
action.triggered.connect(slot)
if disabled:
action.setDisabled(True)
return action
def createToolbar(self):
self.textToolbar = self.addToolBar("Text actions")
self.textToolbar.addAction(self.boldTextAction)
self.textToolbar.addAction(self.underlineTextAction)
self.textToolbar.addAction(self.italicTextAction)
self.textToolbar.addAction(self.strikeoutTextAction)
class HtmlTree(QtGui.QTreeView):
def __init__(self, parent = None):
QtGui.QTreeView.__init__(self)
model = QtGui.QStandardItemModel()
model.setHorizontalHeaderLabels(['Task', 'Description'])
self.rootItem = model.invisibleRootItem()
item0 = [QtGui.QStandardItem('Sneeze'), QtGui.QStandardItem('You have been blocked up')]
item00 = [QtGui.QStandardItem('Tickle nose'), QtGui.QStandardItem('Key first step')]
item1 = [QtGui.QStandardItem('Get a job'), QtGui.QStandardItem('Do not blow it')]
item01 = [QtGui.QStandardItem('Call temp agency'), QtGui.QStandardItem('Maybe they will be kind')]
self.rootItem.appendRow(item0)
item0[0].appendRow(item00)
self.rootItem.appendRow(item1)
item1[0].appendRow(item01)
self.setModel(model)
self.expandAll()
self.setItemDelegate(HtmlPainter(self))
self.resizeColumnToContents(0)
self.resizeColumnToContents(1)
class HtmlPainter(QtGui.QStyledItemDelegate):
def __init__(self, parent=None):
print "delegate parent: ", parent, parent.metaObject().className()
QtGui.QStyledItemDelegate.__init__(self, parent)
def paint(self, painter, option, index):
if index.column() == 1 or index.column() == 0:
text = index.model().data(index)
palette = QtGui.QApplication.palette()
document = QtGui.QTextDocument()
document.setDefaultFont(option.font)
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)
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):
rowHeight = 18
text = index.model().data(index)
document = QtGui.QTextDocument()
document.setDefaultFont(option.font)
document.setHtml(text)
return QtCore.QSize(document.idealWidth() + 5, rowHeight)
def createEditor(self, parent, option, index):
if index.column() == 1:
editor = RichTextLineEdit(option, parent)
editor.returnPressed.connect(self.commitAndCloseEditor)
editor.mainWindow = parent.window()
self.setConnections(editor.mainWindow, editor)
self.enableActions(editor.mainWindow)
return editor
else:
return QtGui.QStyledItemDelegate.createEditor(self, parent, option,
index)
def setConnections(self, mainWindow, editor):
'''Create connections for font toggle actions when editor is created'''
mainWindow.boldTextAction.triggered.connect(editor.toggleBold)
mainWindow.underlineTextAction.triggered.connect(editor.toggleUnderline)
mainWindow.italicTextAction.triggered.connect(editor.toggleItalic)
mainWindow.strikeoutTextAction.triggered.connect(editor.toggleStrikeout)
def enableActions(self, mainWindow):
mainWindow.boldTextAction.setEnabled(True)
mainWindow.underlineTextAction.setEnabled(True)
mainWindow.italicTextAction.setEnabled(True)
mainWindow.strikeoutTextAction.setEnabled(True)
def disableActions(self, mainWindow):
mainWindow.boldTextAction.setDisabled(True)
mainWindow.underlineTextAction.setDisabled(True)
mainWindow.italicTextAction.setDisabled(True)
mainWindow.strikeoutTextAction.setDisabled(True)
def commitAndCloseEditor(self):
editor = self.sender()
if isinstance(editor, (QtGui.QTextEdit, QtGui.QLineEdit)):
self.commitData.emit(editor)
self.closeEditor.emit(editor, QtGui.QAbstractItemDelegate.NoHint)
def setModelData(self, editor, model, index):
if index.column() == 1:
self.disableActions(editor.mainWindow)
model.setData(index, editor.toSimpleHtml())
else:
QtGui.QStyledItemDelegate.setModelData(self, editor, model, index)
class RichTextLineEdit(QtGui.QTextEdit):
'''Single line editor invoked by delegate'''
(Bold, Italic, Underline, StrikeOut) = range(4)
returnPressed = QtCore.Signal()
def __init__(self, option, 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())
margin = 2
self.document().setDocumentMargin(margin)
height = fontMetrics.height() + (margin + self.frameWidth()) * 2
self.setFixedHeight(height)
self.setToolTip("Right click for text effect menu.")
def toggleBold(self):
self.setFontWeight(QtGui.QFont.Normal
if self.fontWeight() > QtGui.QFont.Normal else QtGui.QFont.Bold)
def toggleItalic(self):
self.setFontItalic(not self.fontItalic())
def toggleUnderline(self):
self.setFontUnderline(not self.fontUnderline())
def toggleStrikeout(self):
textFormat = self.currentCharFormat()
textFormat.setFontStrikeOut(not textFormat.fontStrikeOut())
self.setCurrentCharFormat(textFormat)
def contextMenuEvent(self, event):
'''
Context menu for controlling text
'''
textFormat = self.currentCharFormat()
menu = QtGui.QMenu("Text Effects")
for text, shortcut, data, checked in (
("&Bold", "Ctrl+B", RichTextLineEdit.Bold,
self.fontWeight() > QtGui.QFont.Normal),
("&Italic", "Ctrl+I", RichTextLineEdit.Italic,
self.fontItalic()),
("Stri&keout", "Ctrl+K", RichTextLineEdit.StrikeOut,
textFormat.fontStrikeOut()),
("&Underline", "Ctrl+U", RichTextLineEdit.Underline,
self.fontUnderline())):
action = menu.addAction(text, self.setTextEffect)
if shortcut is not None:
action.setShortcut(QtGui.QKeySequence(shortcut))
action.setData(data)
action.setCheckable(True)
action.setChecked(checked)
self.ensureCursorVisible()
menu.exec_(self.viewport().mapToGlobal(
self.cursorRect().center()))
def setTextEffect(self):
'''Called by context menu'''
action = self.sender()
if action is not None and isinstance(action, QtGui.QAction):
what = int(action.data())
if what == RichTextLineEdit.Bold:
self.toggleBold()
return
if what == RichTextLineEdit.Italic:
self.toggleItalic()
return
if what == RichTextLineEdit.Underline:
self.toggleUnderline()
return
format = self.currentCharFormat()
if what == RichTextLineEdit.StrikeOut:
format.setFontStrikeOut(not format.fontStrikeOut())
self.mergeCurrentCharFormat(format)
def keyPressEvent(self, event):
'''
Handles all keyboard shortcuts, and stops retun from returning newline
'''
if event.modifiers() & QtCore.Qt.ControlModifier:
handled = False
if event.key() == QtCore.Qt.Key_B:
self.toggleBold()
handled = True
elif event.key() == QtCore.Qt.Key_I:
self.toggleItalic()
handled = True
elif event.key() == QtCore.Qt.Key_U:
self.toggleUnderline()
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 toSimpleHtml(self):
html = ""
block = self.document().begin()
while block.isValid():
iterator = block.begin()
while iterator != block.end():
fragment = iterator.fragment()
if fragment.isValid():
format = fragment.charFormat()
text = escape(fragment.text())
if format.fontUnderline():
text = "<u>{}</u>".format(text)
if format.fontItalic():
text = "<i>{}</i>".format(text)
if format.fontWeight() > QtGui.QFont.Normal:
text = "<b>{}</b>".format(text)
if format.fontStrikeOut():
text = "<s>{}</s>".format(text)
html += text
iterator += 1
block = block.next()
return html
def main():
app = QtGui.QApplication(sys.argv)
myTree = HtmlTree()
myMainTree = MainTree(myTree)
myMainTree.show()
sys.exit(app.exec_())
if __name__ == "__main__":
main()
QtGui.QStyle.State_Selected
位中,您应该能够玩弄QRect
的填充/轮廓。如果您想查看它们在默认视图中如何执行,请参见以下帖子以了解如何开始探索(请注意,探索默认委托的机制仅适用于勇敢的人,它几乎让我崩溃 :)):https://dev59.com/uZHea4cB1Zd3GeqPq5CN - ericQtGui.QApplication.style().drawControl(QtGui.QStyle.CE_ItemViewItem, option, painter)
。你的很多帖子都很有用。是的,我发现浏览pyqt sourceforge信息比原始代码更有用。一本关于在pyqt/pyside中使用样式的全面书籍肯定会对初学者有所帮助,但我还没有找到这样的书。但是《Python和Qt快速GUI编程》也很有帮助。干杯。 - RXander