Qt window resize height to fit QLabel text - qt

I want to create a window which height should be equal to the height of the child QLabel, the QLabel can be set to any size text and setWrap is True. Here is my current code:
class TextWindow(QMainWindow):
def __init__(self, parent=None):
super().__init__(parent)
self.setFixedWidth(230)
self.centralwidget = QWidget(self)
self.centralwidget.setObjectName("centralwidget")
self.horizontalLayout = QHBoxLayout(self.centralwidget)
self.horizontalLayout.setSizeConstraint(QLayout.SizeConstraint.SetMinimumSize)
self.horizontalLayout.setContentsMargins(0, 0, 0, 0)
self.horizontalLayout.setSpacing(0)
self.horizontalLayout.setObjectName("horizontalLayout")
self.label = QLabel(self.centralwidget)
self.label.setWordWrap(True)
self.label.setObjectName("label")
self.horizontalLayout.addWidget(self.label)
self.setCentralWidget(self.centralwidget)
self.label.setText("Lorem ipsum dolor sit amet, consectetur adipiscing elit. Curabitur non urna nisl. Integer venenatis aliquet faucibus. Nam tristique massa a vestibulum congue. Vivamus nisi felis, volutpat vitae neque quis, pharetra tristique massa. Duis tincidunt et nulla a hendrerit. Vestibulum in molestie lectus.")
Actual behavior:
The text is cut off if there is a lot of it and if there is not enough of it, then there are huge margins between the edge of the window and the text, I want the window height to wrap the text, that is, the height of the window needs to be equal to the size of the text. How can this be done?

This is explained in the Layout Issues section of the layout documentation:
The use of rich text in a label widget can introduce some problems to the layout of its parent widget. Problems occur due to the way rich text is handled by Qt's layout managers when the label is word wrapped".
Note that, as a rule of thumb, you shall never rely on the size hint of a label that uses word-wrapped text. This is related to the many problems text laying out has, like unexpected font behavior, system font scaling, high DPI settings. In reality, it's a good thing that the default behavior doesn't consider the possible size hint of a word-wrapped label, otherwise there could be serious performance issues, especially while resizing.
Truth is, word-wrapped text should always be displayed in a scroll area (using a QTextEdit or with a QLabel that is placed inside a QScrollArea); the default API cannot know the infinite possibilities in which some text could be shown, and should always aim for the best result with optimal performance.
The problem is caused by the fact that there's no way to know the "best" size hint of a label that can wrap its contents, and layout managers should always try to be as fast as possible in their response, otherwise you'd risk recursion or unnecessary overhead, even for a simple label.
IF you are completely sure about the fixed width of the label, you should explicitly set it:
self.label.setFixedWidth(230)
If the width cannot be known beforehand, due to the presence of other widgets in the layout that might change the available width for the label, then you can rely on the QLabel heightForWidth(), set a minimum height for it based on the current width, and finally make a delayed call to the adjustSize() of the main window (the delay is required to allow the whole layout management to update the size hints).
from random import randrange
from PyQt5.QtCore import *
from PyQt5.QtWidgets import *
def randomText():
return ' '.join(['Lorem ipsum'] * randrange(10, 50)) + '\n\nThe end.'
class TextWindow(QMainWindow):
def __init__(self, parent=None):
super().__init__(parent)
self.setFixedWidth(230)
central = QWidget()
layout = QGridLayout(central)
layout.addWidget(QToolButton())
self.label = QLabel(randomText())
self.label.setWordWrap(True)
layout.addWidget(self.label, 0, 1)
layout.addWidget(QPushButton('Hello'), 1, 0, 1, 2)
self.setCentralWidget(central)
def setMessage(self, message=''):
self.label.setText(message or randomText())
self.updateSize()
def updateSize(self):
self.centralWidget().layout().activate()
self.label.setMinimumHeight(0)
self.label.setMinimumHeight(
self.label.heightForWidth(self.label.width()))
QTimer.singleShot(0, self.adjustSize)
def resizeEvent(self, event):
super().resizeEvent(event)
self.updateSize()
app = QApplication([])
w = TextWindow()
QTimer.singleShot(1000, w.setMessage)
w.show()
app.exec()

Get the size hint of the QLabel using the sizeHint() method and then use the result when you resize your window.
For example:
text = "Some very long text that will possible need to be wrapped and take up more than one line."
value = 3 # <--- raise or lower value to see adjustments
class Window(QMainWindow):
def __init__(self, parent=None):
super().__init__(parent=parent)
self.central = QWidget()
self.layout = QVBoxLayout(self.central)
self.setCentralWidget(self.central)
self.label = QLabel(text=text * value)
self.layout.addWidget(self.label)
self.label.setWordWrap(True)
self.resize(self.label.sizeHint())

Related

Using ch (character) unit on font-size

My question is very different from this question: Font scaling based on width of container
This previous question and answers do not mention the ch unit.
CSS Tricks shows an example of using the ch unit to size text, https://css-tricks.com/almanac/properties/f/font-size/.
I had assumed that setting font-size to say 75ch would set the font size so that a maximum of 75 characters (or zeros) would fit on a line, within its container. So that when the container width grows or shrinks, the font-size adapts accordingly, so that there is always 75 characters per line.
See The Elements of Typographic Style Applied to the Web http://webtypography.net/2.1.2 "Anything from 45 to 75 characters is widely regarded as a satisfactory length of line"
However it doesn't work like that. I can see the max-width: 75ch limits the width of the text to 75 characters. But that is an entirely different thing.
If the ch unit can't be used on font-size to create responsive font sizes depended on the width of the container, then what is ch based on when used on font-size (as per the CSS Tricks example)?
<div class="text-content">
<p>Nus moluptatur? Quid eum in nosandit, ut voluptae num dit faccumquis qui vollab inctaquam, undis antis et voluptae. Itatenditem qui tem nonecus repedi doluptae pre explautessum is dolupta doluptatum que perunt lant rero te dolestium fugitat emporeiur, ipit est od minim dolesti asitati onsedisci dit magnatecatur se nimini repe est voluptat. Alitatur seruptat. Dercipis nonsequ iandae venim erum que rerionectas ad quate aut undi dis es alit adis dia dio te miligen tinvelis nustis mi, ut militatur atiosam, etur aut eum ut ad qui nonet fugiam facculpa pro molenis et, consenim volorer ercienis endaniste est ut exeria dis voluptam si dolorat. Tationsed maximpellant maximod eiunt undisque imustruntus mintio blant lamet ea volupta tiores ducita qui dolut aut ex ex enitas soluptur? Ebit eium et et exeruntur? At autatisquas siti cone cuptaspedit lamusae sequidi coreperion ea illaut liquaec tusamus, aborepra qui to ellabore vellace atemporemqui ulparchilia nis sit utet is abo. Sa susdaerumquo voluptatibus corro que et laboribus repudipid es is vid maio. Ut doluptate conseniet que volumquia quam excerib erspernamet dolorio. Itaturehenti sum, estiur sinusciliquo bla es enimus autate ex entotas consendio. Ut lit optatibustes vit ute que mo milliciati re, odi dolum re velesto rercitae re elenis exerit omniate et, cuptibusdae quamus.</p>
</div>
.text-content {
max-width: 700px;
}
p {
font-size: 75ch;
}
See JS Fiddle https://jsfiddle.net/n98oq5Lc/
ch as a unit really only makes sense in most cases if you are dealing with a monospaced font - then 75ch max-width really would mean dont let the width go beyond 75 characters. But with non monospaced fonts CSS isn't going to count the characters it's going to use what it deems the width ch to mean (see e.g. https://developer.mozilla.org/en-US/docs/Learn/CSS/Building_blocks/Values_and_units If your text is lots of i characters it will have a different number of characters in the element than if it has lots of M.
You can however get a sort of responsive font size by defining it in terms of vw and your element width in terms of vw too. It wont gaurantee how many characters you get in a line (unless you go back to the monospaced idea) though.
I tried CH and it sucks, cause lets say you have word: LISBON ... first 2 characters LI are the same size as S, so based on max-width: 2ch; => LI-S-B-O-N
if you try ch3, then it will be LIS-B-ON
basically doesn't work as intented.
No.
ch is a width but font size is a height.
So 75ch merely adjust the vertical size of the font, not the width in respect to the container.

QDialog with rounded corners have black corners instead of being translucent

I need to create a QDialog with rounded translucent corners. The problem is when doing so, the corners are translucent, but are somehow filled in by the Window's alpha property making it black (which is my understanding of the cause of the problem)
Clearly visible is the rounded edges, but with a black 'background' of sorts. To confirm, this Dialog is a modal dialog launched by a QThread from QMainWindow. It is not the parent window.
The CSS for the QDialog (root) component is:
QDialog {
background-color: rgba(250, 250, 250, 255);
border-radius: 30px;
color: #3C3C3C;
}
with a Component Layout as shown below
I added a drop shadow effect on the QDialog too. This is the constructor code for the QDialog:
UpdateDialog::UpdateDialog(QWidget *parent) :
QDialog(parent),
ui(new Ui::UpdateDialog)
{
ui->setupUi(this);
setWindowFlags(Qt::FramelessWindowHint | Qt::Dialog);
QGraphicsDropShadowEffect* effect = new QGraphicsDropShadowEffect();
effect->setBlurRadius(5);
this->setGraphicsEffect(effect);
}
Notably, the parent QMainWindow has the following attributes set in its constructor
ui->setupUi(this);
statusBar()->hide();
setWindowFlags(Qt::FramelessWindowHint);
setAttribute(Qt::WA_TranslucentBackground, true);
// Set rounded corners style
setStyleSheet("QMainWindow {\n background-color:rgba(240,240,240,255);\n border-radius: 30px;\n}\n\nQDialog {\n border-radius: 30px;\n}");
// Add shadow to main window
QGraphicsDropShadowEffect* effect = new QGraphicsDropShadowEffect(ui->mainWindow);
effect->setBlurRadius(5);
effect->setOffset(4, 4);
this->setGraphicsEffect(effect);
How do I make this QDialog to have rounded translucent corners?
UPDATE: Here is a much better version. The pixelated corners were bugging me, among other things. This one looks smooth as silk and can be styled with either CSS or C++. It does require subclassing the QWidget to be rounded (unlike the first version), but it's worth it. Again I'm using a QMessageBox as the base widget here for simplicity (no layouts/etc), but it would work with any QWidget (may need a Qt::Dialog window flag added).
The message box implementation:
#include <QtWidgets>
class RoundedMessageBox : public QMessageBox
{
Q_OBJECT
public:
explicit RoundedMessageBox(QWidget *parent = nullptr) :
QMessageBox(parent)
{
// The FramelessWindowHint flag and WA_TranslucentBackground attribute are vital.
setWindowFlags(windowFlags() | Qt::FramelessWindowHint | Qt::WindowSystemMenuHint);
setAttribute(Qt::WA_TranslucentBackground);
}
qreal radius = 0.0; // desired radius in absolute pixels
qreal borderWidth = -1.0; // -1 : use style hint frame width; 0 : no border; > 0 : use this width.
protected:
void paintEvent(QPaintEvent *) override
{
if (!(windowFlags() & Qt::FramelessWindowHint) && !testAttribute(Qt::WA_TranslucentBackground))
return; // nothing to do
QPainter p(this);
p.setRenderHint(QPainter::Antialiasing);
// Have style sheet?
if (testAttribute(Qt::WA_StyleSheetTarget)) {
// Let QStylesheetStyle have its way with us.
QStyleOption opt;
opt.initFrom(this);
style()->drawPrimitive(QStyle::PE_Widget, &opt, &p, this);
p.end();
return;
}
// Paint thyself.
QRectF rect(QPointF(0, 0), size());
// Check for a border size.
qreal penWidth = borderWidth;
if (penWidth < 0.0) {
QStyleOption opt;
opt.initFrom(this);
penWidth = style()->pixelMetric(QStyle::PM_DefaultFrameWidth, &opt, this);
}
// Got pen?
if (penWidth > 0.0) {
p.setPen(QPen(palette().brush(foregroundRole()), penWidth));
// Ensure border fits inside the available space.
const qreal dlta = penWidth * 0.5;
rect.adjust(dlta, dlta, -dlta, -dlta);
}
else {
// QPainter comes with a default 1px pen when initialized on a QWidget.
p.setPen(Qt::NoPen);
}
// Set the brush from palette role.
p.setBrush(palette().brush(backgroundRole()));
// Got radius? Otherwise draw a quicker rect.
if (radius > 0.0)
p.drawRoundedRect(rect, radius, radius, Qt::AbsoluteSize);
else
p.drawRect(rect);
// C'est finí
p.end();
}
};
Example usage showing both CSS and C++ styling options:
int main(int argc, char *argv[])
{
//QApplication::setStyle("Fusion");
QApplication app(argc, argv);
// Dialog setup
RoundedMessageBox *msgBox = new RoundedMessageBox();
msgBox->setAttribute(Qt::WA_DeleteOnClose);
msgBox->setMinimumSize(300, 300);
msgBox->setWindowTitle("Frameless window test");
msgBox->setText("<h3>Frameless rounded message box.</h3>");
msgBox->setInformativeText("Lorem ipsum dolor sit amet, consectetur adipiscing elit. Aenean fermentum erat rhoncus, "
"scelerisque eros ac, hendrerit metus. Nunc ac lorem id tortor porttitor mollis. Nunc "
"tristique orci vel risus convallis, non hendrerit sapien condimentum. Phasellus lorem tortor, "
"mollis luctus efficitur id, consequat eget nulla. Nam ac magna quis elit tristique hendrerit id "
"at erat. Integer id tortor elementum, dictum urna sed, tincidunt metus. Proin ultrices tempus "
"lacinia. Integer sit amet fringilla nunc.");
if (1) {
// Use QSS style
app.setStyleSheet(QStringLiteral(
"QDialog { "
"border-radius: 12px; "
"border: 3.5px solid; "
"border-color: qlineargradient(x1: 1, y1: 1, x2: 0, y2: 0, stop: 0 #ffeb7f, stop: 1 #d09d1e); "
"background: qlineargradient(x1: 0, y1: 0, x2: 1, y2: 1, stop: 0 #ffeb7f, stop: 1 #d09d1e); "
"color: #003200; "
"}"
));
}
else {
// Use "native" styling
msgBox->radius = 12.0;
msgBox->borderWidth = 3.5;
QLinearGradient bgGrad(0, 0, 1, 1);
bgGrad.setCoordinateMode(QGradient::ObjectMode);
bgGrad.setColorAt(0.0, QColor("gold").lighter());
bgGrad.setColorAt(1.0, QColor("goldenrod").darker(105));
QLinearGradient fgGrad(bgGrad);
fgGrad.setStart(bgGrad.finalStop());
fgGrad.setFinalStop(bgGrad.start());
QPalette pal;
pal.setBrush(QPalette::Window, QBrush(bgGrad));
pal.setBrush(QPalette::Mid, QBrush(fgGrad));
pal.setBrush(QPalette::WindowText, QColor("darkgreen").darker());
msgBox->setPalette(pal);
msgBox->setForegroundRole(QPalette::Mid); // default is WindowText
msgBox->setBackgroundRole(QPalette::Window); // this is actually the default already
}
// Drop shadow doesn't work.
// QGraphicsDropShadowEffect* effect = new QGraphicsDropShadowEffect();
// effect->setBlurRadius(2);
// msgBox->setGraphicsEffect(effect);
msgBox->show();
return app.exec();
}
ORIGINAL: using a generic QMessageBox and setting a mask on it.
Turned out I could use a simple frameless dialog for an "about" message box in a new app... so here you go. This is the simplest method I can think of w/out re-implementing the widget painting process altogether (like in the Qt clock example). But there's clearly limits to this implementation, and I haven't tried it on a Mac yet.
Also, the drop shadow came in very handy... although you can't actually see it, it does a great job smoothing out the corners. Great idea, even if that wasn't the original intention. :)
#include <QtWidgets>
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
// Dialog setup. Actually use a QMessageBox for a shorter example.
QMessageBox *msgBox = new QMessageBox();
msgBox->setAttribute(Qt::WA_DeleteOnClose);
msgBox->setMinimumSize(300, 300);
msgBox->setWindowTitle("Frameless window test"); // might still be visible eg. in a taskbar
msgBox->setText("<h3>Frameless rounded message box.</h3>");
msgBox->setInformativeText("Lorem ipsum dolor sit amet, consectetur adipiscing elit. Aenean fermentum erat rhoncus, "
"scelerisque eros ac, hendrerit metus. Nunc ac lorem id tortor porttitor mollis. Nunc "
"tristique orci vel risus convallis, non hendrerit sapien condimentum. Phasellus lorem tortor, "
"mollis luctus efficitur id, consequat eget nulla. Nam ac magna quis elit tristique hendrerit id "
"at erat. Integer id tortor elementum, dictum urna sed, tincidunt metus. Proin ultrices tempus "
"lacinia. Integer sit amet fringilla nunc.");
// Here come the styling bits... First need the frameless window flag hint
msgBox->setWindowFlags(msgBox->windowFlags() | Qt::FramelessWindowHint | Qt::WindowSystemMenuHint);
// The desired border radius;
const int radius = 12;
// Style the box with CSS. Set the border radius here.
// The border style helps blend the corners, but could be omitted.
// The background is optional... could add other styling here too.
msgBox->setStyleSheet(QString(
"QDialog { "
"border-radius: %1px; "
"border: 2px solid palette(shadow); "
"background-color: palette(base); "
"}"
).arg(radius));
// The effect will not be actually visible outside the rounded window,
// but it does help get rid of the pixelated rounded corners.
QGraphicsDropShadowEffect* effect = new QGraphicsDropShadowEffect();
// The color should match the border color set in CSS.
effect->setColor(QApplication::palette().color(QPalette::Shadow));
effect->setBlurRadius(5);
msgBox->setGraphicsEffect(effect);
// Need to show the box before we can get its proper dimensions.
msgBox->show();
// Here we draw the mask to cover the "cut off" corners, otherwise they show through.
// The mask is sized based on the current window geometry. If the window were resizable (somehow)
// then the mask would need to be set in resizeEvent().
const QRect rect(QPoint(0,0), msgBox->geometry().size());
QBitmap b(rect.size());
b.fill(QColor(Qt::color0));
QPainter painter(&b);
painter.setRenderHint(QPainter::Antialiasing);
painter.setBrush(Qt::color1);
// this radius should match the CSS radius
painter.drawRoundedRect(rect, radius, radius, Qt::AbsoluteSize);
painter.end();
msgBox->setMask(b);
return app.exec();
}

applying formatting to a character range in textflows in Flex 4.10 SDK

When using any version of Flex 4.10 SDK the following code apply's the format to an entire paragraph instead of a specific character range.
https://issues.apache.org/jira/browse/FLEX-33791
<?xml version="1.0"?>
<s:Application xmlns:fx="http://ns.adobe.com/mxml/2009" xmlns:s="library://ns.adobe.com/flex/spark" creationComplete="OnCreationComplete(event)">
<s:TextArea width="100%" height="100%" id="txt" editable="true">
<s:content>
Lorem ipsum dolor sit amet, consectetur adipiscing elit.<s:br/>
Vivamus eu erat ac est ullamcorper egestas eget nec mauris.<s:br/>
</s:content>
</s:TextArea>
<fx:Script><![CDATA[
import flashx.textLayout.edit.EditManager;
import flashx.textLayout.formats.TextLayoutFormat;
import mx.events.FlexEvent;
private function OnCreationComplete(event:FlexEvent):void
{
var objFormat:TextLayoutFormat = new TextLayoutFormat();
objFormat.backgroundColor = 0xB9CCFF;
txt.selectRange(5, 8);
var objManager:EditManager = txt.textFlow.interactionManager as EditManager;
objManager.applyFormat(objFormat, objFormat, objFormat);
}
]]></fx:Script>
</s:Application>
The three parameters for applyFormat are for three different ways the format can be applied.
The first parameter, "leafFormat" will get applied to LeafElement objects like SpanElement (or nodes, if you prefer to think of the XML that TLF generates) and will actually create a new leaf if the current (or supplied) SelectionState doesn't encompass an entire LeafElement.
The second parameter, "paragraphFormat" will get applied to the entire paragraph that the current (or supplied) SelectionState is a part of. So if I select only a few characters from a paragraph and then call applyFormat, passing in a background color for the "paragraphFormat" parameter, the entire paragraph will get the background color.
The third parameter, "containerFormat" I've never used and haven't really looked into at all. I would guess that it applies the format to the entire ContainerController object that helps lay out the text.
You can safely pass null (or completely different formats) in for any of the four parameters.
So, in short, I think to fix your problem you just change you function call to:
objManager.applyFormat(objFormat, null, null);

QCheckBox word-wrap

Is there a way to wrap text in QCheckBox like it is done with QLabel?
label = QLabel( QString.fromUtf8('long text in here'))
label.setWordWrap(True)
I tried \n which will add a linebreak but this is not dynamic if I resize the window.
It seems there is a feature request for this at https://bugreports.qt.io/browse/QTBUG-5370.
The issue is not closed, so likely this feature is not implemented. This means that it is not currently possible to add word wrap to QCheckBox.
You can try various workarounds, such as having an empty-text QCheckBox and a QLabel to the right, or try putting a shorter text for the checkbox, with a label below it with the long explanation.
You can develop a Custom widgets for QtDesigner to use this feature graphically (or not).
CheckBoxWordWrap (dynamic when resize):
In QtDesigner:
You can also use the class and modify it if the behavior is not exactly what you want.
"This is my double \n line checkbox". Now you have double line checkbox
There unfortunately is no short and simple solution for this. We have to implement this on our own.
The complete implementation of a LineWrappedRadioButton class is already provided in this related answer. You can easily covert this code into a LineWrappedCheckBox class by simply deriving from QCheckBox instead of from QRadioButton there.
Use it like this:
int main(int argc, char **argv) {
QApplication app(argc, argv);
LineWrappedCheckBox checkBox("Lorem ipsum dolor sit amet, consectetur adipisici elit, sed eiusmod tempor incidunt ut labore et dolore magna aliqua.");
checkBox.show();
return app.exec();
}
This will produce the following result:

Qt how to break the LabelText of QInputDialog

Here is my example code:
QInputDialog* inDialog = new QInputDialog();
inDialog->setMaximumWidth(100);
inDialog->setLabelText(QString("long and very long......you can say very long"));
The input box showing really long (as long as the string), I was expected the way to set word-wrap for the LabelText, but it seem QInputDialog has no method for that!!!
What can I do now? Write my own InputDialog class? Oh no...!
I hope there is a better way for it!
I would do it myself, like this for example :
QString s = "Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut" ;
QString wrapped ;
if(s.length()>35)
{
wrapped = s.left(15) + QString(".....") + s.right(15) ;
}
else
{
wrapped = s ;
}
inDialog->setLabelText(wrapped) ;
I'm just starting with QT so this may not be the best way to get what you want but heres what I would do.
I would create my own custom input dialog which inherits QInputDialog. I would then override the setLabelText function to check if the string length is less than 100.
If it is less than 100 then you can go ahead and display it. If not then you can choose where to add yours dots and remove words in order to bring the size down.
Once its equal to 100 characters or less, you can display it.
I will try and write an example when I get home if you would like.

Resources