3

How can I set the width of a QTableWidgetcolumn and make the text in each cell start at a new line if its width is bigger then the set width ( kind of like in excel that the text is allowed to have a/be wordwraped). Right now my hole text per cell is written in one really long line, but i would like that it is nice to look at/readable in the destopapplication. Therefor i would love to keep the text in one cell but have a wordwrap/make the text have new lines in it.

Here is my code which contians the self.table.setWordWrap(True) command which will not do anything. Please note that the whole qtablewidget is been overwritten to do coloring of the text (i think this might be the problem why wordwrap might not work):

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()
eyllanesc
  • 235,170
  • 19
  • 170
  • 241
Mady
  • 443
  • 7
  • 19
  • 1
    You have only one word and wordwrap will only create more lines if there are several words but that is not your case – eyllanesc Dec 13 '18 at 22:04
  • YOu were right, the example was not good. i edit the question and put more words in the line, but there is still no new line. – Mady Dec 14 '18 at 07:34
  • @eyllanesc Should i add anything to the question, to understand my problem any better ? – Mady Dec 14 '18 at 16:24
  • 1
    I understand your problem, when I have time I will help you – eyllanesc Dec 14 '18 at 17:12

1 Answers1

3

You have to enable the wordwrap in the QTextDocument in addition to setting the sectionResizeMode of the vertical header to QHeaderView::ResizeToContents:

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()

enter image description here

eyllanesc
  • 235,170
  • 19
  • 170
  • 241