Reputation: 3934
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?
Upvotes: 18
Views: 28234
Reputation: 599
Based on the comment of MarSoft:
When using a path in a QLabel, insert the unicode character U+200B (zero width space) using '\u200B'
after every /
and/or \
to make word wrap work well.
Upvotes: 0
Reputation: 55
This issue will 100% occur, if you want to make any chat in Qt. There's my solution - works fine for most of the cases.
QString text = your_label->text();
QChar ch;
int max_message_width = your_label->width();
QFontMetrics message_metrics{your_label->font()};
// better to write font manually
int side_margins = your_label->contentsMargins().right() +
your_label->contentsMargins().left();
//not sure about contentsMargins(), just for example
for (int cur_len = 0, pos = 0; pos < text.size(); pos++) {
ch = text[pos];
if (ch == ' ' || ch == '\n')
cur_len = 0;
else {
cur_len += message_metrics.size(0, ch).width();
if (cur_len > (max_message_width - side_margins)) {
text.insert(pos, "\n");
cur_len = 0;
}
}
}
Note that these empty lines of word wrap will only work if you don't resize your QLabel.
Upvotes: 0
Reputation: 1
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;
}
};
Upvotes: -2
Reputation: 523
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.
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_())
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_())
Upvotes: 2
Reputation: 9986
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.
Upvotes: 7
Reputation: 1008
In 2020, PySide2, it is just:
tmp = QLabel()
tmp.setWordWrap(True)
Upvotes: -5
Reputation: 89
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!)
Upvotes: 8