代码之家  ›  专栏  ›  技术社区  ›  Nemo XXX

QStyledItemDelegate水平截断文本,不添加水平滚动条

  •  1
  • Nemo XXX  · 技术社区  · 6 年前

    how to use HTML formatted text in a QListWidget .

    main.ui

    <?xml version="1.0" encoding="UTF-8"?>
    <ui version="4.0">
     <class>Dialog</class>
     <widget class="QDialog" name="Dialog">
      <property name="geometry">
       <rect>
        <x>0</x>
        <y>0</y>
        <width>341</width>
        <height>161</height>
       </rect>
      </property>
      <property name="windowTitle">
       <string>Dialog</string>
      </property>
      <widget class="QWidget" name="verticalLayoutWidget">
       <property name="geometry">
        <rect>
         <x>10</x>
         <y>10</y>
         <width>321</width>
         <height>141</height>
        </rect>
       </property>
       <layout class="QVBoxLayout" name="verticalLayout">
        <item>
         <widget class="QListWidget" name="lwOptions"/>
        </item>
       </layout>
      </widget>
     </widget>
     <resources/>
     <connections/>
    </ui>
    

    import os
    import html
    from PyQt5 import uic
    from PyQt5.Qt import (QApplication, QDialog, QListWidget, 
        QListWidgetItem, QStyleOptionViewItem, 
        QStyledItemDelegate, QStyle)
    from PyQt5.QtGui import QPalette, QAbstractTextDocumentLayout, QTextDocument
    from PyQt5.QtCore import QSize
    
    class HTMLDelegate(QStyledItemDelegate):
        def __init__(self, parent=None):
            super(HTMLDelegate, self).__init__(parent)
            self.doc = QTextDocument(self)
    
        def paint(self, painter, option, index):
            painter.save()
            options = QStyleOptionViewItem(option)
            self.initStyleOption(options, index)
            self.doc.setHtml(options.text)
            options.text = ""
            style = QApplication.style() if options.widget is None \
                else options.widget.style()
            style.drawControl(QStyle.CE_ItemViewItem, options, painter)
    
            ctx = QAbstractTextDocumentLayout.PaintContext()
            if option.state & QStyle.State_Selected:
                ctx.palette.setColor(QPalette.Text, option.palette.color(
                    QPalette.Active, QPalette.HighlightedText))
            else:
                ctx.palette.setColor(QPalette.Text, option.palette.color(
                    QPalette.Active, QPalette.Text))
            textRect = style.subElementRect(QStyle.SE_ItemViewItemText, options, None)
            if index.column() != 0:
                textRect.adjust(5, 0, 0, 0)
            constant = 4
            margin = (option.rect.height() - options.fontMetrics.height()) // 2
            margin = margin - constant
            textRect.setTop(textRect.top() + margin)
    
            painter.translate(textRect.topLeft())
            painter.setClipRect(textRect.translated(-textRect.topLeft()))
            self.doc.documentLayout().draw(painter, ctx)
            painter.restore()
    
        def sizeHint(self, option, index):
            return QSize(self.doc.idealWidth(), self.doc.size().height())
    
    class GUI(QDialog):
        def __init__(self):
            super(GUI, self).__init__()
            dirname = os.path.dirname(os.path.abspath(__file__))
            uic.loadUi(os.path.join(dirname,'main.ui'), self)
    
            delegate = HTMLDelegate(self.lwOptions)
            self.lwOptions.setItemDelegate(delegate)
    
            for ordinal in ['first', 'second', 'third', 'fourth', 'fifth', 'sixth', 'seventh', 'eighth', 'ninth']:
                item = QListWidgetItem('This is the <b>{}</b> word. This is the <b>{}</b> word. This is the <b>{}</b> word.'.format(ordinal, ordinal, ordinal))
                self.lwOptions.addItem(item)
    
    
    if __name__ == '__main__':
        import sys
        app = QApplication(sys.argv)
        window = GUI()
        window.show()
        sys.exit(app.exec_())
    

    代码运行良好,但是,如果文本长度大于 QListWidget (需要时会自动添加垂直滚动条。)

    我尝试通过ScrollBarAlwaysOn强制显示,它只显示一个不工作的水平滚动条。

    我需要如何更改代码以在需要时自动添加水平滚动条?

    1 回复  |  直到 6 年前
        1
  •  1
  •   eyllanesc RAHUL KUMAR    6 年前

    我在上一个答案中所做的计算是不正确的,因为返回的大小 sizeHint() 是相对于上一项的。一个简单的解决方案是使用 Qt::SizeHintRole

    import os
    from PyQt5 import QtCore, QtGui, QtWidgets, uic
    
    class HTMLDelegate(QtWidgets.QStyledItemDelegate):
        def __init__(self, parent=None):
            super(HTMLDelegate, self).__init__(parent)
            self.doc = QtGui.QTextDocument(self)
    
        def paint(self, painter, option, index):
            painter.save()
            options = QtWidgets.QStyleOptionViewItem(option)
            self.initStyleOption(options, index)
            self.doc.setHtml(options.text)
            options.text = ""
            style = QApplication.style() if options.widget is None \
                else options.widget.style()
            style.drawControl(QtWidgets.QStyle.CE_ItemViewItem, options, painter)
    
            ctx = QtGui.QAbstractTextDocumentLayout.PaintContext()
            if option.state & QtWidgets.QStyle.State_Selected:
                ctx.palette.setColor(QtGui.QPalette.Text, option.palette.color(
                    QtGui.QPalette.Active, QtGui.QPalette.HighlightedText))
            else:
                ctx.palette.setColor(QtGui.QPalette.Text, option.palette.color(
                    QtGui.QPalette.Active, QtGui.QPalette.Text))
            textRect = style.subElementRect(QtWidgets.QStyle.SE_ItemViewItemText, options, None)
            if index.column() != 0:
                textRect.adjust(5, 0, 0, 0)
            constant = 4
            margin = (option.rect.height() - options.fontMetrics.height()) // 2
            margin = margin - constant
            textRect.setTop(textRect.top() + margin)
    
            painter.translate(textRect.topLeft())
            painter.setClipRect(textRect.translated(-textRect.topLeft()))
            self.doc.documentLayout().draw(painter, ctx)
            painter.restore()
            s = QtCore.QSize(self.doc.idealWidth(), self.doc.size().height())
            index.model().setData(index, s, QtCore.Qt.SizeHintRole)
    
    class GUI(QtWidgets.QDialog):
        def __init__(self):
            super(GUI, self).__init__()
            dirname = os.path.dirname(os.path.abspath(__file__))
            uic.loadUi(os.path.join(dirname,'main.ui'), self)
    
            delegate = HTMLDelegate(self.lwOptions)
            self.lwOptions.setItemDelegate(delegate)
    
            for ordinal in ['first', 'second', 'third', 'fourth', 'fifth', 'sixth', 'seventh', 'eighth', 'ninth']:
                item = QtWidgets.QListWidgetItem('This is the <b>{}</b> word. This is the <b>{}</b> word. This is the <b>{}</b> word.'.format(ordinal, ordinal, ordinal))
                self.lwOptions.addItem(item)
    
    if __name__ == '__main__':
        import sys
        app = QtWidgets.QApplication(sys.argv)
        window = GUI()
        window.show()
        sys.exit(app.exec_())
    

    enter image description here