How to show origin image size in qml Image component? - qml

I tried to show origin image size in QML Image component,code like:
Window {
visible: true
width: 640
height: 480
title: qsTr("Hello World")
Rectangle {
id: imageRect
width: parent.width
height: parent.height
anchors.fill: parent
DragHandler {
acceptedButtons: Qt.RightButton
target: sourceImage
xAxis.enabled: true
yAxis.enabled: true
}
Image {
id: sourceImage
source: "qrc:/2.jpg"
width: sourceSize.width
height: sourceSize.height
MouseArea {
width:parent.width
height:parent.height
onClicked: {
print(mouse.x, mouse.y)
print(sourceImage.x, sourceImage.y)
}
}
}
}
}
but the image is not fit with the parent's size, how can i fit the image to its parent and keep image origin size?

By default, the Image component will automatically set width and height to source image contents. You use this if you don't want any scaling to occur.
In the following example, I render the original Image, the Hubble view of the Pillars of Creation. I put it inside a Flickable with ScrollBars so that you can pan the image.
import QtQuick
import QtQuick.Controls
Page {
property string mouseInfo
Flickable {
id: flickable
anchors.fill: parent
contentWidth: image.width + 20
contentHeight: image.height + 20
clip: true
ScrollBar.vertical: ScrollBar {
width: 20
policy: ScrollBar.AlwaysOn
}
ScrollBar.horizontal: ScrollBar {
height: 20
policy: ScrollBar.AlwaysOn
}
Image {
id: image
source: "https://www.arcgis.com/sharing/rest/content/items/d0ecaa3fd9ef45c8ad255989ef6ded07/data"
MouseArea {
anchors.fill: parent
onClicked: mouseInfo = `${mouseX},${mouseY}`;
}
}
}
footer: Frame {
Text {
text: "image: %1x%2 mouseInfo:%3".arg(image.width).arg(image.height).arg(mouseInfo)
}
}
}
You can Try it Online!

Related

QML Image size is ignored

I have a ToolButton in QML with an image with size 48x48 pixels:
ToolButton {
contentItem: Image {
source: "Icons/idea48.png"
}
}
if I set width and height nothing changes:
ToolButton {
contentItem: Image {
source: "Icons/idea48.png"
width: 5
height: 5
}
}
on the screen it is still 48x48.
And even adding fill Mode does not help:
ToolButton {
visible: scene.serviceMode
contentItem: Image {
source: "Icons/idea48.png"
width: 10
height: 10
fillMode: Image.Stretch
sourceSize: {
width: 48
height: 48
}
}
}
the sourceSize should be 48 to render image with high pixel density.
I also tried to put Image inside Item, but with no success:
ToolButton {
contentItem: Item {
width: 24
height: 24
Image {
source: "Icons/idea48.png"
fillMode: Image.Stretch
sourceSize: {
width: 48
height: 48
}
}
}
}
Qt Quick Controls 2.3 (Qt 5.10) adds built-in support for button icons. By default, different styles may request different icon sizes, according to their design guidelines, but you can easily override the icon size.
ToolButton {
icon.width: 24
icon.height: 24
icon.source: "Icons/idea48.png"
}
What comes to high-DPI support, consider providing #Nx versions like the Gallery example does: http://code.qt.io/cgit/qt/qtquickcontrols2.git/tree/examples/quickcontrols2/gallery/icons/gallery?h=5.10
Answer 1
Set the sourceSize of the Image in order to influence its implicitWidth and implicitHeight, which are used by the ToolButton to determine the size of the contentItem.
import QtQuick 2.7
import QtQuick.Controls 2.0
import QtQuick.Layouts 1.3
ApplicationWindow {
visible: true
width: 640
height: 480
header: ToolBar {
RowLayout {
anchors.fill: parent
ToolButton {
contentItem: Image {
source: "Icons/idea48.png"
sourceSize.width: 10
sourceSize.height: 10
fillMode: Image.Pad
}
}
}
}
}
Answer 2
Put the Image inside an Item so that the Image is not resized by the ToolButton and its dimensions remain exactly as specified by width and height.
import QtQuick 2.7
import QtQuick.Controls 2.0
import QtQuick.Layouts 1.3
ApplicationWindow {
visible: true
width: 640
height: 480
header: ToolBar {
RowLayout {
anchors.fill: parent
ToolButton {
contentItem: Item {
Image {
source: "Icons/idea48.png"
width: 10
height: 10
}
}
}
}
}
}
Answer 3
Force the size of the contentItem.
import QtQuick 2.7
import QtQuick.Controls 2.0
import QtQuick.Layouts 1.3
ApplicationWindow {
visible: true
width: 640
height: 480
header: ToolBar {
RowLayout {
anchors.fill: parent
ToolButton {
contentItem: Image {
source: "Icons/idea48.png"
}
Component.onCompleted: {
contentItem.width = 10
contentItem.height = 10
}
}
}
}
}

QML: Center variable text and image

I need to horizonally center a variable-length text (red box) and an image (yellow box) in a big box (green box). The text shall wrap if it does not fit the box.
Existing code:
Item {
id: bigBox
x: 255
y: 0
width: 800
height: 100
Image {
id: imageBox
source: "image.png"
width: 52
height: 46
anchors.left: parent.left
anchors.leftMargin: 12
anchors.verticalCenter: parent.verticalCenter
horizontalAlignment: Image.AlignLeft
verticalAlignment: Image.AlignVCenter
fillMode: Image.Pad
}
Text {
id: textBox
anchors.left: symbol.right
anchors.leftMargin: 12
anchors.right: parent.right
anchors.verticalCenter: parent.verticalCenter
text: qsTr("heading text")
font.pixelSize: 36
font.bold: true
horizontalAlignment: Text.AlignCenter
}
}
Update:
Actual running code and a real screenshot showing the problem:
import QtQuick 2.0
Rectangle {
id: mask
x: 0
y: 0
width: 800
height: 430
color: "#FFFFFF"
property int pageState: 0
Rectangle {
x: 0
y: 0
width: 111
height: 100
color: "#0000FF"
}
Item {
id: whitespace
x: 117
y: 0
width: 800-x
height: 100
Row {
anchors.centerIn: parent
Image {
id: symbol
source: "../img/pepper.png"
width: 52
height: 46
anchors.verticalCenter: parent.verticalCenter
fillMode: Image.Pad
}
Text {
id: heading
property var texts: ["Active Blabla","Active Blaaaaaaah Blaaaah ","Active Blabla and Blaaaaaaah Blaaaah"]
anchors.verticalCenter: parent.verticalCenter
color: "#333191"
text: texts[pageState]
font.family: "Liberation Sans"
font.pixelSize: 36
font.bold: true
horizontalAlignment: Text.AlignLeft
verticalAlignment: Text.AlignVCenter
wrapMode: Text.WrapAtWordBoundaryOrAnywhere
textFormat: Text.PlainText
width: Math.min(150,contentWidth)
}
}
}
Rectangle {
id: stage
x: 0
y: 106
width: parent.width
height: parent.height-y
color: "#FFFF00"
}
Timer {
interval: 1000 // milliseconds
triggeredOnStart: true
repeat: true
running: true
onTriggered: {
pageState=(pageState+1)%3;
}
}
}
The white area top right is the green box in the first picture. The timer simply runs through the three texts, like the real application would do.
Changing the first parameter of Math.min() to 600 does not change anything.
My set of hacks to solve that problem:
Item {
// ...
Text {
function escapeHTML(text)
{
return text.replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">").replace(/"/g,""");
}
function toHTML(imageUrl,text)
{
var textlines=escapeHTML(text).replace(/\r?\n/,"\n").split("\n");
var retval="<center>";
retval=retval+'<table><tr>';
if (imageUrl!="") {
retval=retval+'<td rowspan="'+(textlines.length)+'"><img src="'+imageUrl+'"></td>';
retval=retval+'<td rowspan="'+(textlines.length)+'"> </td>';
}
for (var i=0; i<textlines.length; i++) {
if (i>0) {
retval=retval+"<tr>";
}
retval=retval+'<td>'+textlines[i]+'</td></tr>';
}
retval=retval+'</table>';
retval=retval+"</center>";
return retval;
}
anchors.fill: parent
verticalAlignment: Text.AlignVCenter
textFormat: Text.RichText
text: toHtml("../img/pepper.png",qsTr("heading"))
}
}
<center> centers the text in the available space, but not the image
(it stays left - bug!).
<table> can be centered by <center>, and it can contain the
image.
Wrapping does not work as expected, so the translated text returned
by qsTr() has to contain linebreaks at the right positions.
toHTML() splits at the linebreaks and generates table rows from that.
The image needs a table cell with rowspan, or else the image is placed too high relative to the text.
And finally, verticalAlignment: Text.AlignVCenter places all of that vertically centered -- except for the table borders that I luckily don't need. (If you are bored, add border=1 to the <table> tag.)
And no, the HTML and CSS subset supported by Text.RichText does not support vertical alignment. The rendering engine behaves like ancient browsers, you have to stack hacks and workarounds as if it was still 1996.
import QtQuick 2.3
import QtQuick.Controls 1.2
import QtQuick.Layouts 1.2
Item {
id: bigBox
width: 800
height: 100
Row {
anchors.centerIn: parent
Image {
anchors.verticalCenter: parent.verticalCenter
source: "blue.png"
}
Text {
anchors.verticalCenter: parent.verticalCenter
// maximum width of the text
width: Math.min(150, contentWidth)
text: qsTr("heading")
wrapMode: Text.WordWrap
horizontalAlignment: Text.AlignHCenter
}
}
}

Image size different after second click on it

I have following minimal working example, taken from my current project:
import QtQuick 2.5
import QtQuick.Window 2.2
import QtQuick.Layouts 1.1
import QtQuick.Controls 1.4
Window {
visible: true
width: Screen.width/2
height: Screen.height/2
property real ueMinOpacity: 0.00
property real ueMaxOpacity: 1.00
Rectangle {
anchors.fill: parent
anchors.margins: 8
border.color: "#4682b4"
radius: 16
clip: true
gradient: Gradient {
GradientStop {
position: 0
color: "#ffffff"
} // GradientStop
GradientStop {
position: 1
color: "#303030"
} // GradientStop
} // Gradient
Rectangle {
anchors.fill: parent
antialiasing: true
border.color: "#4682b4"
border.width: 1
radius: 16
clip: true
gradient: Gradient {
GradientStop {
position: 0
color: "#ffffff"
} // GradientStop
GradientStop {
position: 1
color: "#000000"
} // GradientStop
} // Gradient
RowLayout {
spacing: 8
anchors.fill: parent
TextField {
id: ueProductSearchTextField
antialiasing: true
Layout.fillWidth: true
Layout.fillHeight: true
Layout.alignment: Qt.AlignLeft|Qt.AlignVCenter
Layout.margins: 8
placeholderText: qsTr("Enter product info")
} // TextField
Rectangle {
id: ueImageWrapper
Layout.fillWidth: true
Layout.fillHeight: true
Layout.alignment: Qt.AlignRight|Qt.AlignVCenter
Layout.margins: 8
antialiasing: true
border.color: "#4682b4"
border.width: 1
radius: 16
clip: true
visible: ueProductSearchTextField.length > 0
gradient: Gradient {
GradientStop {
position: 0
color: "#636363"
} // GradientStop
GradientStop {
position: 1
color: "#303030"
} // GradientStop
} // Gradient
Image {
anchors.fill: parent
source: "http://www.clipartbest.com/cliparts/9iR/gEX/9iRgEXXxT.png"
antialiasing: true
clip: true
smooth: true
fillMode: Image.PreserveAspectFit
horizontalAlignment: Image.AlignHCenter
verticalAlignment: Image.AlignVCenter
sourceSize.width: 96
sourceSize.height: 96
} // Image
MouseArea {
anchors.fill: parent
enabled: ueImageWrapper.visible
onClicked: {
ueProductSearchTextField.text="";
} // onClicked
} // MouseArea
onWidthChanged: {
print("ueImageWrapper.width:"+ueImageWrapper.width);
} // onWidthChanged
onHeightChanged: {
print("ueImageWrapper.height:"+ueImageWrapper.height);
} // onHeightChanged
} // Rectangle
} // RowLayout
} // Rectangle
} // Rectangle
} // Window
Now, the purpose of this Item/Rectangle is to filter database records according to TextField's entered value, which works perfectly. However, once TextField's text is not empty anymore (when user enters some string), on the right side of Layout Image for clearing text is shown via OpacityAnimator. Once the app is launched, I get following screenshot - clear text icon is hidden since there is not text in TextField:
Then, I enter some text into TextField and clear text icon pops up:
Then, for instance, I clear text by clicking on clear text icon and it (icon) is hidden again, which is ok:
And finally, I reenter text into TextField, clear text icon is visible again, but it has different size:
Why? I did not change the code. It must be some problem with Layouts, but I simply do not see it! Here is also a debug output from onWidthChanged and onHeightChanged handlers:
qml: ueImageWrapper.width:37.56521739130435
qml: ueImageWrapper.height:480
qml: ueImageWrapper.width:132.92307692307693
qml: ueImageWrapper.width:133.83783783783784
BaCaRoZzo's suggestion works, but I'm also a bit unsure about why it behaves the way it does. If you take a simpler example:
import QtQuick 2.6
import QtQuick.Window 2.2
import QtQuick.Layouts 1.0
import QtQuick.Controls 1.0
Window {
visible: true
width: 800
height: 800
Shortcut {
sequence: "Ctrl+Q"
onActivated: Qt.quit()
}
Item {
id: boundary
width: 400
height: 400
anchors.centerIn: parent
RowLayout {
anchors.fill: parent
Rectangle {
Layout.fillWidth: true
Layout.fillHeight: true
color: "steelblue"
}
Rectangle {
id: rect
Layout.fillWidth: true
Layout.fillHeight: true
color: "salmon"
visible: false
}
}
}
Rectangle {
anchors.fill: boundary
color: "transparent"
border.color: "black"
}
Button {
text: "Toggle visibility"
onClicked: rect.visible = !rect.visible
}
}
The second rectangle starts off being invisible, and is then shown/hidden by clicking the button. However, when it starts off as invisible, it never gets a size once shown. On the other hand, if it starts off visible, then it gets half the width of the layout.
If you read the documentation carefully, it doesn't say that it's necessary to set a preferredWidth/preferredHeight if you just want to make an item fill the available space. For that reason, it seems like a bug in how layouts handle initial visibility of their items. I'd suggest filing a bug report.

Unable to position custom styled Tumbler

I am trying to give a Tumbler my own style. I declare the Tumbler like this:
Tumbler {
style: MyTumblerStyle {}
height: UIConstants.smallFontSize * 10
width: UIConstants.smallFontSize * 3
TumblerColumn {
model: [0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24]
}
}
where MyTymblerStyle is defined like this:
TumblerStyle {
id: root
visibleItemCount: 5
background: Rectangle {}
foreground: Item {}
frame: Item {}
highlight: Item {}
delegate: Item {
id: delRoot
implicitHeight: (control.height) / root.visibleItemCount
Item {
anchors.fill: parent
Text {
text: styleData.value
font.pixelSize: UIConstants.smallFontSize
font.family: UIConstants.robotoregular
anchors.centerIn: parent
scale: 1.0 + Math.max(0, 1 - Math.abs(styleData.displacement)) * 0.6
color: styleData.current?UIConstants.color:"black"
opacity: 1 - Math.abs(styleData.displacement/(root.visibleItemCount-3))
}
}
}
}
I use it in a Row like this:
Row {
MyTumbler {}
StandardText {
color: UIConstants.color
text: "Uhr"
}
}
Now, the result looks like this:
As you can see, the "Uhr" text center is aligned to the top of the Tumbler. Also the Row does not seem to recognize the real width of the Tumbler.
Why? It does work when I do not use MyTumblerStyle.
The problem isn't your style, it's the width assignment.
It helps to break out the Rectangles at a time like this:
import QtQuick 2.4
import QtQuick.Controls 1.3
import QtQuick.Extras 1.3
ApplicationWindow {
title: qsTr("Hello World")
width: 300
height: 600
visible: true
Column {
anchors.centerIn: parent
Tumbler {
id: tumbler
width: 30
TumblerColumn {
model: 25
}
Component.onCompleted: print(width, height, implicitWidth, implicitHeight)
}
Rectangle {
width: tumbler.implicitWidth
height: tumbler.implicitHeight
color: "transparent"
border.color: "blue"
Text {
text: "Tumbler implicit size"
anchors.fill: parent
wrapMode: Text.Wrap
}
}
Rectangle {
width: tumbler.width
height: tumbler.height
color: "transparent"
border.color: "blue"
Text {
text: "The size you gave"
anchors.fill: parent
wrapMode: Text.Wrap
}
}
}
}
(I don't have access to UIConstants, so I guess the width you set)
The implicitWidth of Tumbler is calculated based on the width of each individual TumblerColumn. This allows you to set individual widths for columns, something that is necessary for scenarios where some are wider than others, for example:
So, you should also set the width of your column, or, preferably, only set the width of your column, and not the entire Tumbler:
import QtQuick 2.4
import QtQuick.Controls 1.3
import QtQuick.Extras 1.3
ApplicationWindow {
width: 300
height: 600
visible: true
Row {
anchors.centerIn: parent
Tumbler {
id: tumbler
TumblerColumn {
model: 25
width: 30
}
}
Text {
text: "Uhr"
}
}
}
This also explains why the Text is weirdly positioned; the Row sees 30 pixels, but the column still has its original (much wider) width.

QML Row element refuses to work

I know I should be using Row, Column etc. rather than items anchored by ID to make my code simpler and easier to read. But they refuse to work most of the time. For example, in this case:
import QtQuick 2.4
import QtQuick.Controls 1.3
import QtQuick.Layouts 1.1
ListView {
id: listView
anchors.fill: parent
topMargin: spacing
anchors.leftMargin: spacing
anchors.rightMargin: spacing
clip: true
spacing: 0.5 * pxPermm
model: SqlQueryModel {}
delegate: Rectangle {
id: delegateItem
color: "white"
height: 14 * pxPermm
width: listView.width
clip: true
Row {
id: row
anchors.fill: delegateItem
spacing: pxPermm
Image {
height: row.height
width: height
source: "qrc:/resources/ryba.jpg"
fillMode: Image.PreserveAspectCrop
}
Item {
id: textItem
height: row.height
Label {
anchors.left: textItem.left
anchors.top: textItem.top
text: nazov
font.bold: true
}
Label {
anchors.left: textItem.left
anchors.bottom: textItem.bottom
text: cas
}
}
}
}
}
This shows two Labels on the top of an Image in the delegate of list view. Not two labels to the right of the image, as you would expect. However, this code works:
import QtQuick 2.4
import QtQuick.Controls 1.3
import QtQuick.Layouts 1.1
ListView {
id: listView
anchors.fill: parent
topMargin: spacing
anchors.leftMargin: spacing
anchors.rightMargin: spacing
clip: true
spacing: 0.5 * pxPermm
model: SqlQueryModel {}
delegate: Rectangle {
id: delegateItem
color: "white"
height: 14 * pxPermm
width: listView.width
clip: true
Row {
id: row
anchors.fill: delegateItem
spacing: pxPermm
Image {
height: row.height
width: height
source: "qrc:/resources/ryba.jpg"
fillMode: Image.PreserveAspectCrop
}
Label {
text: nazov
font.bold: true
}
}
}
}
Of course I need to show more than one label in the delegate. What am I missing here?
It turns out that Item has zero width by default. The code works properly after the width is set:
Item {
id: textItem
height: row.height
width: childrenRect.width
// labels etc
}