0

I am trying to make a multi line QLabel with a text without space but delimited by comma. ex : 'Cat,Dog,Rabbit,Train,Car,Plane,Cheese,Meat,Door,Window'

enter image description here

I have found that multiline is possible with setWordWrap but it breaks based on spaces.

How would it be possible to break line based on comma ?

Here is an example of code :

from PySide2.QtWidgets import *


class MainWindow(QMainWindow):
    def __init__(self, *args, **kwargs):
        super(MainWindow, self).__init__(*args, **kwargs)
        self.setGeometry(500,100,50,100)

        line = QLabel()
        line.setMaximumWidth(150)
        line.setText('Cat,Dog,Rabbit,Train,Car,Plane,Cheese,Meat,Door,Window')
        line.setWordWrap(True)

        self.setCentralWidget(line)

        self.show()


if __name__ == '__main__':
    app = QApplication([])
    window = MainWindow()
    app.exec_()
eyllanesc
  • 235,170
  • 19
  • 170
  • 241
Pythmalion
  • 14
  • 4
  • You need the words to be all on separate lines or to act like word wrap and fit the container? – BlueGhost Feb 17 '21 at 20:49
  • I would like to have the same mechanism than word wrap. In this example, it should break after Car, – Pythmalion Feb 17 '21 at 20:54
  • 3
    @Pythmalion A quick and dirty fix is to insert a [zero-width-space](https://en.wikipedia.org/wiki/Zero-width_space) (`\u200b`) after every comma, since word-wrapping breaks after *any* kind of whitespace. – ekhumoro Feb 18 '21 at 00:27
  • @ekhumoro Thanks, maybe a bit dirty but it makes the job. To my opinion this is a/the solution ! – Pythmalion Feb 18 '21 at 11:18

1 Answers1

0

One way of doing it would be to edit the text according to QLabel size.

The following triggers on every line size event, making this a costly solution.

First we add a signal :

class MainWindow(QMainWindow):
    resized = QtCore.pyqtSignal()

Then we connect signal with a method ,set wordwrap to False and add custom resize event that triggers every time label gets a new size:

self.line.setWordWrap(False)
self.line.resizeEvent = self.on_resize_event
self.resized.connect(self.add_spaces)

on_resize_event which handles size changes and triggers add_spaces :

def on_resize_event(self, event):
    if not self._flag:
        self._flag = True
        self.resized.emit()
        QtCore.QTimer.singleShot(100, lambda: setattr(self, "_flag", False))
    print(f"Resized line: {self.line.size()}")
    return super(MainWindow, self).resizeEvent(event)

Lastly we have a add_spaces method which calculates maximum line length and splits on comma.

def add_spaces(self):
    size = self.line.size()
    text = self.mystring
    result_string = ""
    temp_label = QLabel()
    temp_text = ""

    #Split the chunks by delimiter
    chunks = text.split(",")

    for i,chunk in enumerate(chunks):
        temp_text += chunk + ",";
        if len(chunks) > i+1:
            temp_label.setText(temp_text + chunks[i+1] + ",")
            width = temp_label.fontMetrics().boundingRect(temp_label.text()).width()
            if width >= size.width():
                result_string += temp_text + "\n"
                temp_text = ""
        else:
            result_string += temp_text

    self.line.setText(result_string)

Full code:

from PyQt5 import QtCore
from PyQt5.QtWidgets import QMainWindow, QLabel, QApplication


class MainWindow(QMainWindow):
    resized = QtCore.pyqtSignal()
    def __init__(self, *args, **kwargs):
        super(MainWindow, self).__init__(*args, **kwargs)
        self._flag = False

        self.line = QLabel()
        self.line.setStyleSheet("background-color: grey;color:white;")
        self.line.setMaximumWidth(300)
        self.line.setMinimumWidth(20)

        self.mystring = 'Cat,Dog,Rabbit,Train,Car,Plane,Cheese,Meat,Door,Window,very,long,list of words,list of words,word,word,word,list of word,word,list of word,list of word'

        self.line.setText(self.mystring)



        self.setCentralWidget(self.line)

        self.line.setWordWrap(False)
        self.line.resizeEvent = self.on_resize_event
        self.resized.connect(self.add_spaces)

        self.show()
        self.add_spaces()


    def on_resize_event(self, event):
        if not self._flag:
            self._flag = True
            self.resized.emit()
            QtCore.QTimer.singleShot(100, lambda: setattr(self, "_flag", False))
        print(f"Resized line: {self.line.size()}")
        return super(MainWindow, self).resizeEvent(event)

    def add_spaces(self):
        size = self.line.size()
        text = self.mystring
        result_string = ""
        temp_label = QLabel()
        temp_text = ""

        #Split the chunks by delimiter
        chunks = text.split(",")

        for i,chunk in enumerate(chunks):
            temp_text += chunk + ",";
            if len(chunks) > i+1:
                temp_label.setText(temp_text + chunks[i+1] + ",")
                width = temp_label.fontMetrics().boundingRect(temp_label.text()).width()
                if width >= size.width():
                    result_string += temp_text + "\n"
                    temp_text = ""
            else:
                result_string += temp_text

        self.line.setText(result_string)




if __name__ == '__main__':
    app = QApplication([])
    window = MainWindow()
    app.exec_()
BlueGhost
  • 166
  • 5
  • Be amare that changing geometry within a `resizeEvent` is not suggested, as it can easily lead to recursion especially with widgets that have adapting size hints just like QLabel. – musicamante Feb 18 '21 at 01:34
  • @musicamante Yea thats true. How should the `resizeEvent` be handled? At the moment i changed it to have a 100ms delay to prevent that. – BlueGhost Feb 18 '21 at 06:45
  • Thanks for the time spent on your answer. Might not be the answer but I learned other things and it show me how to overcome another of my problems :) – Pythmalion Feb 18 '21 at 11:19
  • @BlueGhost it should not call a resize at all, and a good (but *far from easy*) implementation would require proper subclass of QLabel, at least overriding `resizeEvent` to compute the correct word wrapping, `hasHeightForWidth` and `heightForWidth` for appropriate "ratio" hint, `sizeHint` for the possible "final" hint, `setText` for the minimum size and probably even paintEvent; all this would obviously work better if keeping an internal QTextDocument (which is what QLabel actually does). – musicamante Feb 18 '21 at 14:25