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.
Related
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())
I built a website using splitLayout that lays out elements horizontally, dividing the available horizontal space into multiple parts. I used 'cellWidths = c("1000px", "200px")', so I got an element with width 1000px and a second element with width 200px.
However, what I actually want is that the second element should fit exactly between the right margin of the first element and the right margin of the browser window, so that when the size and/or shape of the browser window is changed, the width of the second element is adjusted accordingly. I tried: 'cellWidths = c("1000px", "calc(100vw-1000px)")', but unfortunately 'calc' cannot be used here.
I thought about using CSS, including something like 'style = "width: "calc(100vw-1000px)"' within the second element, but until now, I did not have any success by trying that.
The complete code is as follows:
library(shiny)
ui <- tagList(
splitLayout(
cellWidths = c("1000px", "200px"),
cellArgs = list(style = "padding: 6px; white-space: normal;"),
fluidPage(
style = "border: 1px solid silver; height: 627px;"
),
fluidPage(
style = "border: 1px solid silver; height: 627px",
p("Lorem ipsum dolor sit amet, ut alterum facilis disputationi vis, summo percipitur sed ea. Stet senserit persequeris at duo, vis atqui vituperata ex, et has omittam expetenda persequeris. Id usu causae meliore, dolorem lucilius perpetua id vim. Vim at homero timeam viderer, dicunt concludaturque ea eum. Tempor ceteros facilisi ei pro, ea tantas adipisci scribentur vix.")
)
)
)
server <- function(input, output, session) {}
shinyApp(ui = ui, server = server)
Any suggestions?
Unfortunately, I'm not an R programmer, and while I can can take guesses at what 'splitLayout' and 'fluidPage' accomplish for you, I don't know enough to tell you definitively what the fix is.
However, what you're describing can be achieved via CSS by using flexbox. In case you're unfamiliar with it, here's some good information:
https://medium.freecodecamp.org/an-animated-guide-to-flexbox-d280cf6afc35
https://css-tricks.com/snippets/css/a-guide-to-flexbox/
I am trying to use bootstrap tooltip for something it probably wasn't designed for, namely the possibility of showing a quite large text. I have managed to tweek it to my needs, with one exception. The width of the text box shown is way too narrow, unless i set it to a fixed width, but then it looks awfully stupid when displaying my short texts. I did override the standard max-width of 200px, but still it would not display even any wider than 120.797px
I tried to make a short simple example, and to my surprise, it works perfectly in my fiddle. with no width property set, it still grows to 800px, as set in max-width. So I tried to figure out what was different between my simple fiddle, and my larger web page. I used the google chrome dev-tools, to show me all set css properties, as I expected some unknown source to influence the width. Normally all properties set by css, are displayed with an arrow on the left side of each property, telling you exactly where, and through which file and selector statement this property is set. Here is what I found:
So, every other property gives a reference, while width remains a mysterious unknown. Notice that the font color is dark red. If this was calculated from other properties, it normally would be faded. I've tried to research what this might mean, but the official documentation leaves no clues. This page was the closest thing I could find.
How do I find out where this CSS-value comes from?
Here is a link to the fiddle. The tooltip text is a random news source from today, in norwegian. Code below, as per Stackoverflow rules:
HTML:
<span class="nav-text" data-toggle="tooltip" data-placement="right" title="" data-original-title="– Vi har nok satt oss litt i respekt i dag for de andre nasjonene. De vet nå at vi ikke er uredde og ikke er redd for å bare gå fra dem i front, sa Johannes Thingnes Bø om den solide seieren.
For da Emil Hegle Svendsen sendte Johannes Thingnes Bø ut på den siste etappen sammen med franske Antonin Guigonnat og italienske Therry Chenal var det mye som skulle gå galt for at ikke stryningen skulle sikre norsk stafettseier.
Og stryningen gikk knallhardt ut og viste konkurrentene ryggen allerede i den første motbakken.
– Jeg er ganske trygg og selvsikker når jeg går ut på siste etappe, sa ankermannen til NRK etter målgang.
En god generalprøve før OL
Det var et godt fornøyd stafettlag etter seieren. Stafetten i Ruhpolding var den store generalprøven for det som skal være det norske OL-laget i Pyeongchang i februar.
– Det var en god følelse. Jeg tar gjerne samme løpsopplegg neste gang det er stafett, sa Thingnes Bø etter seieren med et klart hint til stafetten i OL.">
Hover for tooltip
</span>
CSS:
div.tooltip{
top:0px !important;
}
div.tooltip>div.tooltip-arrow{
top:15px !important;
}
div.tooltip>div.tooltip-inner{
max-width:800px !important;
white-space: pre-wrap;
text-align: left;
}
And standard init of bootstrap tooltip, javascript:
$('[data-toggle="tooltip"]').tooltip();
I have a Qml component that is reasonably large so that I want to make it a reusable component but it is too small/non-generic such that I want to avoid creating its own .qml file.
It seems like Components are the right method to define reusable objects in the same file, but when I do that I don't know how to access and change properties of the contained objects.
More specifically, imagine having an in-file definition like this
Component {
id: myReusableComponent
// This cannot be set because component does not allow properties
// (Would work fine in a separate file myReusableComponent.qml)
// property alias string innerText: innerText.text
Rectangle {
id: rect
width: 200
height: 200
color: "red"
Text {
id: innerText
text: "Want to set text later"
}
}
How can I reuse this component later, while changing some properties of it?
I know the following is not valid syntax, but I want to use it similarly to this:
Loader {
id: hello
sourceComponent: myReusableComponent
item.innerText: "Hello" }
Text { text: "Some other stuff in between" }
Loader {
id: world
sourceComponent: myReusableComponent
item.anchors.left: hello.right
item.rect.width: 100
item.rect.color: "blue"
item.innerText: "World" }
Loader {
id: excl
sourceComponent: myReusableComponent
item.rect.color: "green"
item.innerText: "!!!" }
etc...
Any ideas? Or is there a fundamentally different way of doing this?
What I essentially want is reusability of QML objects that are defined in place while still being able to change the properties of them.
This seems to be related but does not solve the problem of creating multiple objects. Repeaters seem to be useful but don't give me the flexibility I want.
NOTE: I will add some remarks about the answers here since they might get overlooked in the comments.
I like all three answers from Blabbouze, derM and ddriver!
I accepted Blabbouze's answer because it provides a concrete solution that is closest to what I was looking for.
However, I also didn't know about the overhead of Loaders and might consider using a different approach after reading derM's answer.
Finally, dynamic object creation as suggested by ddriver is not exactly what I was looking for but might be useful for others.
Thanks everyone!
I think you are looking for onLoaded() signal. It is emitted when the Loader have successfully created the Component.
You can then access your loaded type properties with item.
import QtQuick 2.7
import QtQuick.Controls 2.0
ApplicationWindow {
visible: true
width: 640
height: 480
title: qsTr("Hello World")
Component {
id: myReusableComponent
Rectangle {
id: rect
property alias innerText: innerText.text
width: 200
height: 200
color: "red"
Text {
id: innerText
text: "Want to set text later"
}
}
}
Loader {
id: hello
sourceComponent: myReusableComponent
onLoaded: {
item.innerText = "hello"
}
}
Loader {
id: world
sourceComponent: myReusableComponent
anchors.left: hello.right
onLoaded: {
item.width = 100
item.color = "blue"
item.innerText = "World"
}
}
}
At first I will try to explain why you do not want to do it
If the Component is reused - even in one file and not only by the means of Repeaters, ListViews e.t.c - you should consider creating a seperate file, to keep your file clean and readable.
If you create inline-components, and then create the instances by the means of a Loader just to be able to create them, they come with an overhead, that can be tolerated if the component is really enormous, or you really need the Loader for the possibility to change the source dynamically. Otherwise, it helps you only to make everything complex, and raise the ressource consumption.
There is almost no penalty for outlaying the code into multiple files. So do it, when ever you have a reasonable, logically enclosed unit - and especially when you are going to reuse it multiple times in a file.
Use the Component when you are planning to use this as a delegate. Some perfere to directly declare the delegate where it is used. I do so, if there are only few properties to set.
Especially if you have multiple Views that share the same delegate-prototype, it is a good idea to use the Component.
If you really need to use a Loader (for the reasons you need a Loader and not for non-dynamic object creation) then you can use a Component to avoid the need of the onLoaded-event. It enables you to preconfigure the Component, and set the event-handlers without the need of a Connections-Object.
Ok - now a short answer to your question:
You can create instances of a Component by many means:
Loader
Views (ListView, GridView, Repeater ...)
Dynamic Object Creation with JS (componentID.createObject(parent)) at any point where you can execute JS code.
Read: http://doc.qt.io/qt-5/qtqml-javascript-dynamicobjectcreation.html#creating-objects-dynamically
You could also try to use dynamic object creation with components.
So you can have a property Rectangle obj: null, and then:
Component.onCompleted: {
obj = myReusableComponent.createObject(parentItem, {"width" : 100, "color" : "blue" }
}
In addition to Blabbouze's answer, aside from assignments (which are not auto updated as bindings) you can also have bindings if you use the format:
item.prop = Qt.binding(function() { bindingExpressions... })
In Qt5.15 there is a new inline component syntax:
component <component name> : BaseType {
// declare properties and bindings here
}
In the following example, I define NormalText from Text and, subsequently, I define HeadingText from NormalText, then, I use them all in the same QML file:
import QtQuick 2.15
import QtQuick.Controls 2.15
import QtQuick.Layouts 1.15
Page {
anchors.fill: parent
component NormalText : Text {
Layout.fillWidth: true
font.pointSize: 12
wrapMode: Text.WordWrap
}
component HeadingText : NormalText {
font.pointSize: 14
font.bold: true
}
ColumnLayout {
width: parent.width
HeadingText { text: "Introduction" }
NormalText { text: "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum." }
NormalText { text: "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum." }
}
}
You can Try it Online!
References:
https://www.qt.io/blog/new-qml-language-features-in-qt-5.15
I have a long description for my plot. And I also need to put some other information under the plot as well.
But I can't figure it out how I can do it. This is what I have so far,
plot(1:10)
title(
main = "Foo",
sub = 'Description \n Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Aenean commodo ligula eget dolor. Aenean massa. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. ',
font.sub = 1,
font.main = 1
)
result is not good as the description is too long and it has been cut off, also the word description is overlapping with the xlable,
How can I box the description under the plot?
This is what I am after,
an additional info - time period.
wrap the description in a grey box.
adjust the text to the left.
Are these possible?
Just a little lesson about typography: the sub argument of your function allows you to put a sub-title. To he honest such elements are not used very often. What you are trying to achieve is to put a caption (which is a very different elements). Typographic conventions say that captions are placed below the plots for graphs and images and above tables. If you use a preparation system like LaTeX or a word processors I advise you to do not put a caption "inside" the plot (say, as a part of the plot itself) by using R functions but to just put your plain plot without captions in your document and then put captions by using tools available for your editors (word etc. or LaTeX or Knitr or even Powerpoint etc. ).
Anyway, my little and boring lesson about typography does not solve your problem. Here I provide a solution with the layout function of base R and the textplot function of the gplots package but as always I bet there is a more efficient and elegant way to achieve the same goal.
library(gplots)
tex <- "Description Lorem ipsum dolor sit amet, consectetuer adipiscing elit. \nAenean commodo ligula eget dolor. Aenean massa. Cum sociis natoque \npenatibus et magnis dis parturient montes, ascetur ridiculus mus."
layout(matrix(c(2,1)), 0.5, 2)
par(mar=c(1,1,1,1))
textplot(tex, halign = "center")
par(mar=c(1,1,1,1))
plot(1:10)
title(main = "Foo", font.main = 1)
just to go back to boring stuff: this way of doing is very tricky and inefficient from my point of view so I advise you to use other tools as I've said previously.
Last but not least, I think that the right syntax for line breaks is \nword to break and not \n word to break
Here is how the result look:
It can be done by using "mtext", and no space between "\n" and following words, eg. "\nLorem".
text<- "Description \nLorem ipsum dolor sit amet, consectetuer adipiscing elit. Aenean commodo ligula eget dolor. \nAenean massa. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus \nmus. "
par(mar = c(8, 4, 3, 3))
plot(1:10, main = "Foo")
mtext(text, side = 1, line = 6, cex = 0.8, adj = 0)
## line is margin line, starting at 0 counting outwards; adj=0, left or bottom align, 1, right or top align