Pyqt QTablewidget 自动换行

Pyqt QTablewidget wordwrap lines

如果 QTableWidgetcolumn 的宽度大于设置的宽度(有点像 excel 允许文本a/be 自动换行)。 现在我的每个单元格的孔文本都写在一个很长的行中,但我希望在 destopapplication 中看起来 at/readable 很好。因此,我希望将文本保留在一个单元格中,但 wordwrap/make 文本中有新行。

这是我的代码,其中包含 self.table.setWordWrap(True) 命令,它不会执行任何操作。请注意,整个 qtablewidget 已被覆盖以对文本进行着色(我认为这可能是 wordwrap 可能无法工作的问题):

from PyQt5.QtWidgets import *
from PyQt5 import QtCore, QtGui, QtWidgets, QtPrintSupport
import sys

found_words_num =int ('100')
data_single = {'Position':['hallo i like to do this and it would be nice if there was a new line. othere lines and so one.','b'], 'LV-Text': ['c','d'], 'Fundwörter': ['e','f'], 'Hersteller': ['g','h']}
words_in_columns = ['a','b']

textMargins = 12
borderMargins = 10


class HighlightDelegate(QtWidgets.QStyledItemDelegate):
    def __init__(self, parent=None):
        super(HighlightDelegate, self).__init__(parent)
        self.doc = QtGui.QTextDocument(self)
        self._filters = []

    def paint(self, painter, option, index):
        painter.save()
        options = QtWidgets.QStyleOptionViewItem(option)
        self.initStyleOption(options, index)
        self.doc.setPlainText(options.text)
        self.apply_highlight()
        options.text = ""
        style = QtWidgets.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)

        if index.column() != 0:
            textRect.adjust(5, 0, 0, 0)

        the_constant = 4
        margin = (option.rect.height() - options.fontMetrics.height()) // 2
        margin = margin - the_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 apply_highlight(self):
        cursor = QtGui.QTextCursor(self.doc)
        cursor.beginEditBlock()
        fmt = QtGui.QTextCharFormat()
        fmt.setForeground(QtCore.Qt.red)
        for f in self.filters():
            highlightCursor = QtGui.QTextCursor(self.doc)
            while not highlightCursor.isNull() and not highlightCursor.atEnd():
                highlightCursor = self.doc.find(f, highlightCursor)
                if not highlightCursor.isNull():
                    highlightCursor.mergeCharFormat(fmt)
        cursor.endEditBlock()

    @QtCore.pyqtSlot(list)
    def setFilters(self, filters):
        if self._filters == filters: return
        self._filters = filters

    def filters(self):
        return self._filters


class main_result_pos(QtWidgets.QWidget):
    def __init__(self, parent=None):
        super(main_result_pos, self).__init__(parent)

        self.setGeometry(50, 50, 1100, 850)  # Fenstergröße festlegen

        self.table = QTableWidget(self)
        self.table.setGeometry(QtCore.QRect(10, 10, 900, 600))
        self.table.setRowCount(found_words_num)
        self.table.setColumnCount(6)
        self.table.setSortingEnabled(True)
        self.table.setWordWrap(True)

        self._delegate = HighlightDelegate(self.table)  ##Klasse aufrufen
        self.table.setItemDelegate(self._delegate)
        le = QtWidgets.QLineEdit()
        le.textChanged.connect(self.on_textChanged)

        search_list = [word for column in words_in_columns for word in column]
        list_c_str = ' '.join(search_list)
        le.setText(list_c_str)

        horHeaders = []
        for col, key in enumerate(sorted(data_single.keys())):
            horHeaders.append(key)
            for row, item in enumerate(data_single[key]):
                newitem = QTableWidgetItem(item)
                newitem.setTextAlignment(QtCore.Qt.AlignCenter)
                self.table.setItem(row, col, newitem)

        self.table.setHorizontalHeaderLabels(['A', 'B','C', 'D' , 'E', 'F'])

        self.table.resizeRowsToContents()
        self.table.resizeColumnsToContents()

    @QtCore.pyqtSlot(str)
    def on_textChanged(self, text):
        self._delegate.setFilters(list(set(text.split())))
        self.table.viewport().update()


def main():
    app = QApplication(sys.argv)
    ex = main_result_pos()
    ex.show()
    sys.exit(app.exec_())


if __name__ == '__main__':
    main()

除了将垂直 header 的 sectionResizeMode 设置为 QHeaderView::ResizeToContents 之外,您还必须在 QTextDocument 中启用自动换行:

import sys
from PyQt5 import QtCore, QtGui, QtWidgets

class HighlightDelegate(QtWidgets.QStyledItemDelegate):
    def __init__(self, parent=None):
        super(HighlightDelegate, self).__init__(parent)
        self._filters = []
        self._wordwrap = False
        self.doc = QtGui.QTextDocument(self)

    def paint(self, painter, option, index):
        painter.save()
        options = QtWidgets.QStyleOptionViewItem(option)
        self.initStyleOption(options, index)
        self.doc.setPlainText(options.text)
        self.apply_highlight()

        if self._wordwrap:
            self.doc.setTextWidth(options.rect.width())
        options.text = ""

        style = QApplication.style() if options.widget is None else options.widget.style()
        style.drawControl(QtWidgets.QStyle.CE_ItemViewItem, options, painter)

        if self._wordwrap:
            painter.translate(options.rect.left(), options.rect.top())
            clip = QtCore.QRectF(QtCore.QPointF(), QtCore.QSizeF(options.rect.size()))
            self.doc.drawContents(painter, clip)
        else:
            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)

    def apply_highlight(self):
        cursor = QtGui.QTextCursor(self.doc)
        cursor.beginEditBlock()
        fmt = QtGui.QTextCharFormat()
        fmt.setForeground(QtCore.Qt.red)
        for f in self.filters():
            highlightCursor = QtGui.QTextCursor(self.doc)
            while not highlightCursor.isNull() and not highlightCursor.atEnd():
                highlightCursor = self.doc.find(f, highlightCursor)
                if not highlightCursor.isNull():
                    highlightCursor.mergeCharFormat(fmt)
        cursor.endEditBlock()

    @QtCore.pyqtSlot(list)
    def setFilters(self, filters):
        if self._filters == filters: return
        self._filters = filters
        self.parent().viewport().update()

    def filters(self):
        return self._filters

    def setWordWrap(self, on):
        self._wordwrap = on
        mode = QtGui.QTextOption.WordWrap if on else QtGui.QTextOption.WrapAtWordBoundaryOrAnywhere

        textOption = QtGui.QTextOption(self.doc.defaultTextOption())
        textOption.setWrapMode(mode)
        self.doc.setDefaultTextOption(textOption)
        self.parent().viewport().update()

data_single = {'Position':['hallo i like to do this and it would be nice if there was a new line. othere lines and so one.','b'], 'LV-Text': ['c','d'], 'Fundwörter': ['e','f'], 'Hersteller': ['g','h']}
words_in_columns = ['a','b']

class main_result_pos(QtWidgets.QWidget):
    def __init__(self, parent=None):
        super(main_result_pos, self).__init__(parent)
        self.table = QtWidgets.QTableWidget(100, 6)
        self.table.verticalHeader().setSectionResizeMode(QtWidgets.QHeaderView.ResizeToContents)
        self.table.setSortingEnabled(True)

        self._delegate = HighlightDelegate(self.table)
        self.table.setItemDelegate(self._delegate)
        le = QtWidgets.QLineEdit(textChanged=self.on_textChanged)

        search_list = [word for column in words_in_columns for word in column]
        list_c_str = ' '.join(search_list)
        le.setText(list_c_str)

        horHeaders = []
        for col, key in enumerate(sorted(data_single.keys())):
            horHeaders.append(key)
            for row, item in enumerate(data_single[key]):
                newitem = QtWidgets.QTableWidgetItem(item)
                self.table.setItem(row, col, newitem)
        self.table.setHorizontalHeaderLabels(horHeaders)

        self._delegate.setWordWrap(True)
        self.resize(640, 480)

        lay = QtWidgets.QVBoxLayout(self)
        lay.addWidget(le)
        lay.addWidget(self.table)

    @QtCore.pyqtSlot(str)
    def on_textChanged(self, text):
        self._delegate.setFilters(list(set(text.split())))

def main():
    app = QtWidgets.QApplication(sys.argv)
    ex = main_result_pos()
    ex.show()
    sys.exit(app.exec_())

if __name__ == '__main__':
    main()