17

I have a label that sometimes contain a long text with no spaces (path in the computer).

So word-wrap wraps it very weirdly.

Is there a way to make the word-wrap of the label break in the middle of the word or not only at white spaces?

Andreas Haferburg
  • 5,189
  • 3
  • 37
  • 63
sara
  • 3,824
  • 9
  • 43
  • 71

5 Answers5

8

This isn't elegant but does work...
So say header class has Private:

QLabel *thisLabel;
QString *pathName;
QString *pathNameClean;

and of course defining thisLabel some where. so it would be nice if it was this simple....

thisLabel->setWordWrap(true);

that's fine IF AND ONLY IF the word has break points (WHICH PATHS SHOULD AVOID)

SO keep your actual path in a separate string if you need it for QFile purposes later. Then manually define a character per line number, and insert the spaces into the string.... so we'll say 50 chars is a good width...

    pathNameClean = new QString(pathName);

    int c = pathName->length();

    if( c > 50)
    {
        for(int i = 1; i <= c/50; i++)
        {
            int n = i * 50;
            pathName->insert(n, " ");
        }
    }
    thisLabel->setText(pathName);

Shazam.... simulated WordWrap with no original spaces...

just remember that pathName string is now just for pretty QLabel purposes and that the pathNameClean string is the actual path. Qt programs will crash if you try to open a file with a space injected path.....

(if there's no simple class method it's likely just a few lines of code to do... and why problem solving is a programmers best tool!)

budda
  • 89
  • 1
  • 2
  • 1
    It's even better to use Unicode character `ZWSP` (`U+200A`) - Zero-width Whitespace. If you use that character then the text will wrap where you insert it but will still be shown without spaces when it fits. – MarSoft May 13 '21 at 22:03
  • Or if you use `Soft Hyphen` (`U+00AD`) instead then there will be hyphen at the places where words are actually wrapped, again without visual effect on non-wrapped parts of text. – MarSoft May 14 '21 at 21:40
  • 1
    This will correctly work only with [monospaced fonts](https://en.wikipedia.org/wiki/Monospaced_font) – asd Jun 22 '21 at 09:10
6

One way is to use the QTextOption class with a QTextDocument instead of a QLabel. This let you use QTextOption::WrapMode. QTextOption::WrapAtWordBoundaryOrAnywhere should do what you want.

asd
  • 266
  • 7
  • 15
Luca Carlon
  • 9,546
  • 13
  • 59
  • 91
  • 3
    This is unacceptable since QTextEdit/QTextBrowser doesnt fit to contents. – chacham15 Dec 27 '12 at 20:39
  • Fit to content? I don't see such a request in the question. – Luca Carlon Dec 28 '12 at 10:20
  • You're right, but that is the default behavior with a QLabel, and not with a QTextEdit. Furthermore, its not easy making QTextEdit have that behavior. – chacham15 Dec 28 '12 at 16:03
  • 3
    The answer therefore seems to fulfill the requirements, which makes it perfectly acceptable I guess. If you have a better solution, then answer yourself. It might be useful to future viewers. – Luca Carlon Dec 28 '12 at 19:31
2

QLabel with other wrap mode

I happen to have this same question in 2021, so I here I will share with you some of the best answers I have found so far.

TextWrapAnywhere QLabel

Subclass QLabel and and implement the paintEvent, where you can set the text alignment to TextWrapAnywhere when you drawItemText.

from PyQt5.QtCore import Qt
from PyQt5.QtGui import QPainter
from PyQt5.QtWidgets import QApplication, QLabel, QMainWindow, QStyleOption, QVBoxLayout, QWidget, QStyle

class SuperQLabel(QLabel):
    def __init__(self, *args, **kwargs):
        super(SuperQLabel, self).__init__(*args, **kwargs)

        self.textalignment = Qt.AlignLeft | Qt.TextWrapAnywhere
        self.isTextLabel = True
        self.align = None

    def paintEvent(self, event):

        opt = QStyleOption()
        opt.initFrom(self)
        painter = QPainter(self)

        self.style().drawPrimitive(QStyle.PE_Widget, opt, painter, self)

        self.style().drawItemText(painter, self.rect(),
                                  self.textalignment, self.palette(), True, self.text())


class MainWindow(QMainWindow):
    def __init__(self, *args, **kwargs):
        super(MainWindow, self).__init__(*args, **kwargs)

        self.setFixedSize(100, 200)

        self.label = QLabel()
        self.label.setWordWrap(True)
        self.label.setText("1111111111111111111111111111")

        self.slabel = SuperQLabel()
        self.slabel.setText("111111111111111111111111111")

        self.centralwidget = QWidget()
        self.setCentralWidget(self.centralwidget)

        self.mainlayout = QVBoxLayout()
        self.mainlayout.addWidget(self.label)
        self.mainlayout.addWidget(self.slabel)

        self.centralwidget.setLayout(self.mainlayout)


if __name__ == "__main__":
    import sys
    app = QApplication(sys.argv)
    w = MainWindow()
    w.show()
    sys.exit(app.exec_())

enter image description here

Text Wrap at char instead of white space

According to this answer(in op's comment) provided by @ekhumoro, if you are looking for wrapping a line based on comma, you can insert a zero-width-space after the char you want to wrap and use the built in word wrap function.

Here is an example:

from PyQt5.QtCore import QRect, Qt
from PyQt5.QtGui import QPainter
from PyQt5.QtWidgets import QApplication, QLabel, QMainWindow, QStyleOption, QStylePainter, QVBoxLayout, QWidget, QStyle


class CommaWrapableQLabel(QLabel):
    def __init__(self, *args, **kwargs):
        super(CommaWrapableQLabel, self).__init__(*args, **kwargs)

    def setWordWrapAtAnychar(self, char):
        newtext = self.text().replace(char, f"{char}\u200b")
        self.setText(newtext)
        self.setWordWrap(True)


class MainWindow(QMainWindow):
    def __init__(self, *args, **kwargs):
        super(MainWindow, self).__init__(*args, **kwargs)

        self.setFixedSize(100, 200)

        self.label = QLabel()
        self.label.setWordWrap(True)
        self.label.setText(
            'Dog,Rabbit,Train,Car,Plane,Cheese,Meat,Door,Window')

        self.slabel = CommaWrapableQLabel()
        self.slabel.setText(
            'Dog,Rabbit,Train,Car,Plane,Cheese,Meat,Door,Window')
        self.slabel.setWordWrapAtAnychar(",")

        self.centralwidget = QWidget()
        self.setCentralWidget(self.centralwidget)

        self.mainlayout = QVBoxLayout()
        self.mainlayout.addWidget(self.label)
        self.mainlayout.addWidget(self.slabel)

        self.centralwidget.setLayout(self.mainlayout)


if __name__ == "__main__":
    import sys
    app = QApplication(sys.argv)
    w = MainWindow()
    w.show()
    sys.exit(app.exec_())

enter image description here

Ted
  • 468
  • 2
  • 8
-1

Black magic solution

As said from other answers, you can reimplement paintEvent() for QLabel to pass the Qt::TextWrapAnywhere flag.

However, overriding paintEvent() may cause unexpected side effects since the default implementation of paintEvent() contains a lot of extra features besides just painting the text as is.

Actually the alignment flags passed to QStyle::drawItemText is stored in a private member QLabelPrivate::align. I came up with the idea to force rewrite the value of it with the Qt::TextWrapAnywhere flag set, and it works. This workaround requires a bit of C++ black magic.

class WorkaroundLabel: public QLabel {
    Q_OBJECT
public:
    WorkaroundLabel(QString text, QWidget* parent): QLabel(text, parent) {
        setWordWrap(true);
        ushort* p;
        if (FindAlignAddr(&p)) {
            *p = (*p | Qt::TextWrapAnywhere);
        } else {
            // workaround failed
        }
    }
    virtual ~WorkaroundLabel() {}
protected:
    // "ushort align;" in qtbase/src/widgets/widgets/qlabel_p.h
    bool FindAlignAddr(ushort** out) {
        Qt::Alignment align = alignment();
        void* d_raw = (void*) d_ptr.data();
        ushort* p = reinterpret_cast<ushort*>(d_raw);
        for (int i = 0; i < 1024; i += 1) {
            setAlignment(Qt::AlignLeft);
            ushort a = *p;
            setAlignment(Qt::AlignRight);
            ushort b = *p;
            if (a != b) {
                *out = p;
                setAlignment(align);
                return true;
            }
            p++;
        }
        setAlignment(align);
        return false;
    }
};
-5

In 2020, PySide2, it is just:

 tmp = QLabel()
 tmp.setWordWrap(True)    
Nicola Mingotti
  • 860
  • 6
  • 15
  • 5
    It doesn't solve the case with no spaces in the text, read the question: "Is there a way to make the word-wrap of the label break in the middle of the word or not only at white spaces?" – matthieu Sep 11 '20 at 11:45
  • you may be right. i don't remember, i used qt for a short while. you can provide the right solution in any case, since you read better. good luck. – Nicola Mingotti Sep 12 '20 at 20:19