I'm using a QML ColumnLayout. When I review it with Qt Design Studio, I see that all alignment options are disabled:
I wonder why.
My ColumnLayout is inside a Rectangle and contains some buttons:
Rectangle {
anchors {
top: parent.top
bottom: parent.bottom
right: parent.right
}
width: // ...
ColumnLayout {
id: toolsColumn
width: // ...
ToolbarButton {
// ...
}
ToolbarButton {
// ...
}
}
}
Also, such statements inside ColumnLayout won't take effect:
Layout.alignment: Qt.AlignHCenter | Qt.AlignVCenter
Related
I am using a template to create an app using QT Creator and QML and am hoping to create a landing page that allows users to select which "page" they want to navigate to by clicking an icon.
I've figured out how to get a button on the landing page and have it open another page. However, I am using x and y positions of the button and it doesn't scale correctly when the window size changes.
Ultimately, I am trying to put 6 buttons on the landing page in a way that scales correctly.
I have attached an image of my ideal Landing Page design and have also attached code for what I already have.
I hope I was able to explain this well enough. Please let me know if I can clarify anything.
import QtQuick 2.2
import QtQuick 2.6
import QtQuick.Controls 1.1
import QtQuick.Controls.Styles 1.4
import "components" as Components
//BACKGROUND COLOR
Rectangle {
signal signInClicked(string tourId)
color: "#242424"
AnimatedImage {
anchors.fill: parent
source: app.landingpageBackground
fillMode: Image.PreserveAspectCrop
visible: source > ""
}
Rectangle {
anchors.fill: parent
gradient: Gradient {
GradientStop { position: 0.0; color: "#00000000";}
GradientStop { position: 1.0; color: "#00000000";}
}
}
//TITLE TEXT
Text {
id: titleText
anchors {
left: parent.left
right: parent.right
top: parent.top
topMargin: app.height/10
}
font.family: app.customTitleFont.name
text: app.info.title
font {
pointSize: 60
pointSize: app.titleFontSize * 1.4
}
color: "#00000000"
horizontalAlignment: Text.AlignHCenter
wrapMode: Text.Wrap
}
Button {
id: signInButton
anchors {
horizontalCenter: parent.horizontalCenter
bottom: parent.bottom
bottomMargin: 60 * app.scaleFactor
}
opacity: 0.0
style: ButtonStyle {
id: btnStyle
property real width: parent.width
label: Text {
id: lbl
text: signInButton.text
anchors.centerIn: parent
verticalAlignment: Text.AlignVCenter
horizontalAlignment: Text.AlignHCenter
width: parent.width
maximumLineCount: 2
elide: Text.ElideRight
wrapMode: Text.WordWrap
color: app.titleColor
font.family: app.customTextFont.name
font.pointSize: app.baseFontSize
}
background: Rectangle {
color: Qt.darker(app.headerBackgroundColor, 1.2)
border.color: app.titleColor
radius: app.scaleFactor * 2
}
}
height: implicitHeight < app.units(56) ? app.units(56) : undefined // set minHeight = 64, otherwise let it scale by content height which is the default behavior
width: Math.min(0.5 * parent.width, app.units(250))
text: qsTr("Let's Play!")
MouseArea{
anchors.fill: parent
onClicked: {
signInClicked("");
}
}
NumberAnimation{
id: signInButtonAnimation
target: signInButton
running: false
properties: "opacity"
from: 0.0
to: 1.0
easing.type: Easing.InQuad
duration: 1000
}
}
AboutPage {
id: aboutPage
}
NewsAndUpdates {
id: newsPage
}
ProgramsPage {
id: programsPage
}
Connections {
target: app
onUrlParametersChanged: {
if (app.urlParameters.hasOwnProperty("appid")) {
signInClicked(app.urlParameters.appid)
}
}
}
Component.onCompleted: {
signInButtonAnimation.start()
}
}
I think this doc should help you. You haven't really defined what you want very well, but I'll show you some examples so you can hopefully take from it what you need.
QML has the concept of "positioners" and "layouts". Positioners help automatically position your objects neatly on the screen. And Layouts try to do that too, but can also stretch your objects to fill available space.
Row:
You can arrange all your buttons in a row and center the row horizontally.
Row {
anchors.horizontalCenter: parent.horizontalCenter
Button { id: btn1 }
Button { id: btn2 }
...
}
Grid:
Similarly, Grid is a positioner that arranges objects into a grid:
Grid {
anchors.horizontalCenter: parent.horizontalCenter
columns: 3
Button { id: btn1 }
Button { id: btn2 }
...
}
GridLayout:
A GridLayout is just like a Grid, but it can also resize the objects to fill up the space. My opinion is layouts can do more, but they're often trickier to use. In this example, the first button should be a fixed size, while the second button should fill up the remaining width.
GridLayout {
anchors.fill: parent
rows: 2
Button { id: btn1; Layout.preferredWidth: 200 }
Button { id: btn2; Layout.fillWidth: true}
...
}
I have a QML ListView, and I'm trying to dynamically add elements to it. I want the background rectangle to also scale dynamically as elements are added/removed from the ListView. Right now I get a binding loop, and I understand what they are but I can't figure out where it's coming from. I played around changing the code a bit and I was able to get rid of the binding loop one time but then the ListView couldn't be scrolled. Anyone have any ideas?
import QtQuick 2.15
import QtQuick.Window 2.0
Window {
visible: true
width: 800
height: 800
Rectangle {
id: listContainer
height: childrenRect.height
width: parent.width
color: "transparent"
anchors {
top: parent.top
topMargin: 30
left: parent.left
leftMargin: 45
}
ListView {
anchors.top: parent.top
anchors.left: parent.left
anchors.right: parent.right
model: myModel
height: childrenRect.height
header:
Text {
z: 2
height: 50
text: "HEADER"
color: "black"
}
delegate: Component {
Item {
Text {
id: userName;
text: name;
color: "black";
font.pixelSize: 50
anchors {
left: parent.left
leftMargin: 20
}
}
Rectangle {
height: 1
color: 'black'
width: listContainer.width
anchors {
left: userName.left
top: userName.top
topMargin: - 12
leftMargin: -15
}
}
}
}
spacing: 80
}
}
ListModel {
id: myModel
}
/* Fill the model with default values on startup */
Component.onCompleted: {
for (var i = 0; i < 100; i++) {
myModel.append({
name: "Big Animal : " + i
})
}
}
}
EDIT: As suggested by #Aditya, the binding loop can be removed by having a static ListView height, but I don't want it to be that way. I'm using the rectangle as a background for the ListView and I want it to scale according to the ListView. For example, if I only add two elements, I want the rectangle to also scale for those two elements and not cover the entire screen. This causes a problem:
import QtQuick 2.15
import QtQuick.Window 2.0
Window {
visible: true
width: 800
height: 800
Rectangle {
id: listContainer
height: childrenRect.height
width: parent.width
color: "yellow"
anchors {
top: parent.top
topMargin: 30
left: parent.left
leftMargin: 45
}
ListView {
anchors.top: parent.top
anchors.left: parent.left
anchors.right: parent.right
model: myModel
height: 800//childrenRect.height
header:
Text {
z: 2
height: 50
text: "HEADER"
color: "black"
}
delegate: Component {
Item {
Text {
id: userName;
text: name;
color: "black";
font.pixelSize: 50
anchors {
left: parent.left
leftMargin: 20
}
}
Rectangle {
height: 1
color: 'black'
width: listContainer.width
anchors {
left: userName.left
top: userName.top
topMargin: - 12
leftMargin: -15
}
}
}
}
spacing: 80
}
}
ListModel {
id: myModel
}
/* Fill the model with default values on startup */
Component.onCompleted: {
for (var i = 0; i < 2; i++) {
myModel.append({
name: "Big Animal : " + i
})
}
}
}
I also tried separating the header from ListView into a different component and anchoring the listview below it and that worked. The only problem was it could not be scrolled with the listview. Worst case, I could make a scrolling animation for it but that seems like an inefficient solution and I'd like to know why this doesn't work.
You are probably also biting yourself with the Item as the top-level in the delegate, since that doesn't give any implicit size, which the ListView uses to calculate the scrolling needs. You can simply use Text directly as the delegate (you don't need the Component either) and put the line/rectangle inside. If doing so you can use the contentHeight property of ListView to size the background.
Furthermore, I would suggest to have the ListView as the top level and do any styling secondary, with which I mean, put the background Rectangle inside.
import QtQuick 2.12
import QtQuick.Window 2.12
Window {
width: 640
height: 480
visible: true
title: qsTr("Hello World")
ListView {
id: listView
model: 3
anchors.fill: parent
Rectangle { //background
color: "yellow"
z: -1
width: listView.width
height: listView.contentHeight
}
delegate: Text {
text: "name" + index
color: "black";
font.pixelSize: 50
leftPadding: 20
Rectangle {
height: 1
color: 'black'
width: listView.width
y: - 12
x: -15
}
}
spacing: 80
}
}
Btw, if you are going to put the ListView in some RowLayout or something, you probably also want implicitHeight: contentHeight in the ListView.
The binding loop is originating from the ListView's height: childrenRect.height statement. It looks like the ListView needs to be a fixed height, or at least not dependent on childrenRect. It is most likely how the ListView element knows that the view should be scrollable to view elements below.
It really depends on what you're trying to achieve with setting the height to match childrenRect, but in my case, ListView height is changing based on the children (per your desire presumably). With a 100 items the height came out to be 7970. With 5 items in the model, the result was 350. You can check this by adding a debug or console.log() with onHeightChanged However, as a result of this scaling, the ListView is assumed to be big enough to view the entire data set regardless of the window parent container size.
You do not need to scale the ListView height to match the contents; that is what it is built for. It allows scrolling because the contents are too big to be shown within its limited height.
I was able to achieve get rid of the binding loop and be able to scroll by simply changing the statement to a static value, which is the parent height of 800 as an example:
Window {
visible: true
width: 800
height: 800
Rectangle {
id: listContainer
height: childrenRect.height
width: parent.width
color: "transparent"
anchors {
top: parent.top
topMargin: 30
left: parent.left
leftMargin: 45
}
ListView {
anchors.top: parent.top
anchors.left: parent.left
anchors.right: parent.right
model: myModel
height: 800//childrenRect.height
header:
Text {
z: 2
height: 50
text: "HEADER"
color: "black"
}
delegate: Component {
Item {
Text {
id: userName;
text: name;
color: "black";
font.pixelSize: 50
anchors {
left: parent.left
leftMargin: 20
}
}
Rectangle {
height: 1
color: 'black'
width: listContainer.width
anchors {
left: userName.left
top: userName.top
topMargin: - 12
leftMargin: -15
}
}
}
}
spacing: 80
}
}
ListModel {
id: myModel
}
/* Fill the model with default values on startup */
Component.onCompleted: {
for (var i = 0; i < 100; i++) {
myModel.append({
name: "Big Animal : " + i
})
}
}
}
Edit:
I feel like you're trying to just secure a background for a scalable ListView. Having a static background as a container works but not very well for modern unser interfaces - any bounce effects or such will not move the rectangle. You could achieve this by anchoring the rectangle to the ListView element but it is a very roundabout way. Instead, you could just set a rectangle to style each element of the ListView delegate instead.
delegate: Component {
Item {
Rectangle{
width: listContainer.width
height: userName.height+13
//add 13 to adjust for margin set below
anchors {
left: userName.left
top: userName.top
topMargin: - 12
leftMargin: -15
//just copying from the other rectangle below
}
gradient: Gradient {
//I am just using gradient here for a better understanding of spacing. You could use color.
GradientStop { position: 0.0; color: "aqua" }
GradientStop { position: 1.0; color: "green" }
}
}
Text {
id: userName;
text: name;
color: "black";
font.pixelSize: 50
anchors {
left: parent.left
leftMargin: 20
}
}
Rectangle {
height: 1
color: 'black'
width: listContainer.width
anchors {
left: userName.left
top: userName.top
topMargin: - 12
leftMargin: -15
}
}
}
}
This will make sure that the rectangle background behind the ListView will look like it is scrolling with the items. In reality we have broken one rectangle into multiple and just set each element with one. You can also use this type of styling to achieve alternate colors in your list for example.
I want to create Tabs. So using Tab bar and Tab buttons i am creating tabs and using stack layout i am loading the respecting screen.
Note: i dont want to use loader. Using only Stack layout how can i load the screen.
Please suggest how can i do that?
Item {
id:screenTabs
property var tabname : [qsTr("Tab1"),qsTr("Tab2"),qsTr("Tab3"),qsTr("Tab4"),qsTr("Tab5")]
property var tabScreen : ["qrc:/Tabscreen1.qml","qrc:/Tabscreen2.qml","qrc:/Tabscreen3.qml","qrc:/Tabscreen4.qml","qrc:/Tabscreen5.qml"]
width : parent.width
height : parent.height
TabBar
{
id: bar
width: parent.width*0.95
anchors {
top: parent.top
topMargin: 15
left: parent.left
leftMargin: 10
}
Repeater
{
model: tabname.length
TabButton
{
text: tabname[index]
}
}
}
StackLayout
{
id: stacklyt
width: parent.width
height: parent.height
currentIndex: bar.currentIndex
anchors {
top: bar.bottom
}
Repeater
{
model: tabScreen.length
Loader
{
width: parent.width
height: parent.height
source: tabScreen[index]
}
}
}
}
Also without using loader i am doing below shown way but this is not what i am expecting:
StackLayout {
id: stackLayout
width: parent.width
height: parent.height
currentIndex: bar.currentIndex
anchors.top: bar.bottom
Item {
}
Item {
}
Item {
}
Item {
}
Item {
}
}
If you don't want to use the Loader I think you have to instantiate the component given the name (and not the URI) and put them in a Container type (here the StackLayout) as you mention in your second solution.
without using loader i am doing below shown way but this is not what i am expecting
Why is this not what you expect? Did you replace Item with your components? You might have to add an import statement for your components if you are getting an import error.
StackLayout
{
id: stacklyt
width: parent.width
height: parent.height
currentIndex: bar.currentIndex
anchors.top: bar.bottom
Tabscreen1 {}
Tabscreen2 {}
Tabscreen3 {}
Tabscreen4 {}
Tabscreen5 {}
}
I'm Developing an application with Qt 5.13.0 on a Windows 10 professional computer. I need to create a Whatsapp like message view, and to achieve that, I used a GridView component as a base. However, in order to draw the messages correctly, I need to create each GridView row with a different height, depending on the message text.
But to my surprise, I could not find any solution on the internet to achieve that, although I thought it was a simple formality. I tried a solution by myself, which I attached below, however it doesn't work. The issue is that all the rows take the height of the last resized one.
I have no doubt that Qt can do this, unfortunately, I have been looking for days now, and I can not find a solution to this issue. I simply have no idea about how to achieve that. So someone can explain to me how to create a GridView with variable height rows, or if the GridWiew isn't the appropriate component to do that, whcih I should use instead?
Here is my qss file:
import QtQuick 2.12
import QtQuick.Controls 2.5
import QtQuick.Window 2.12
import QtQuick.Layouts 1.11
Window
{
visible: true
width: 640
height: 480
title: qsTr("Grid view")
color: "#ffffff"
ColumnLayout
{
transformOrigin: Item.Center
spacing: 0
x: 0
y: 0
width: parent.width
height: parent.height
/**
* Grid view item
*/
Component
{
id: itGridItem
Item
{
Column
{
Rectangle
{
property int messageWidth: (gvMessageGrid.cellWidth / 2) - 50
id: itemRect
x: senderIsMyself ? 25 : gvMessageGrid.cellWidth - (25 + messageWidth)
y: 5
width: messageWidth
height: itemTextID.height + 20
color: senderIsMyself ? "#d5d5d5" : "#800b940e"
radius: 5
clip: true
Text
{
id: itemTextID
width: parent.width - 20
text: itemText
renderType: Text.NativeRendering
textFormat: TextEdit.RichText
wrapMode: Text.WordWrap
font.family: "Segoe UI Emoji"
font.pixelSize: 18
anchors.margins: 10
anchors.left: parent.left
anchors.top: parent.top
color: "#101010"
}
onHeightChanged: gvMessageGrid.cellHeight = height + 10
}
}
}
}
/**
* Messages grid view
*/
GridView
{
id: gvMessageGrid
y: 0
Layout.fillHeight: true
flickableDirection: Flickable.VerticalFlick
Layout.fillWidth: true
Layout.alignment: Qt.AlignHCenter | Qt.AlignVCenter
clip: true
contentWidth: 700
contentHeight: 300
cellWidth: contentWidth
cellHeight: 150
model: lmGridModel
delegate: itGridItem
ScrollBar.vertical: ScrollBar
{
visible: true
}
onWidthChanged: cellWidth = width
}
}
}
--- Edited on October 18, 2019
Following the eyllanesc suggestion below, here are 2 screenshots of what I want and what I get:
what I want
what I get
NOTE I'm using several languages, for test purposes, because my application must have international support. However the issue I face has nothing to do with that, i.e it's not an error with text calculation, because 1. The green rect sizes around the text are always correct, and 2. I face the exact same issue with pure English text.
I suggest you to use ListView instead of GridView. You can easily view anything inside full-width list item. Let me share with you my ListDelegate class from an old project. Pay attention to usage of isIncoming property in anchor binding, for example:
anchors {
left: isIncoming? undefined : parent.left
right: isIncoming? parent.right : undefined
}
Full listing:
import QtQuick 2.5
import "units.js" as Units
Rectangle {
id: chatMsgDelegRoot
property bool isIncoming: !model.out
property bool isSelected: model.isSelected
signal clicked(int index)
width: parent.width
height: dlgColumn.height + Units.gu(2.5)
color: "#edf1f5"
Column {
id: dlgColumn
spacing: Units.gu(4)
width: parent.width
anchors.verticalCenter: parent.verticalCenter
BorderImage {
id: borderImage
source: isIncoming?
(isSelected ? "/images/img/MsgOut_Selected_2.png" : "/images/img/MsgOut_2.png") :
(isSelected ? "/images/img/MsgIn_Selected_2.png" : "/images/img/MsgIn_2.png")
// Texture-dependent.
border {
left: isIncoming? 20 : 30
top: 20
right: isIncoming? 30 : 20
bottom: 35
}
anchors {
left: isIncoming? undefined : parent.left
right: isIncoming? parent.right : undefined
}
width: Math.max(content.width + Units.gu(15), Units.gu(21))
height: content.height + Units.gu(9)
MouseArea {
id: msgDelegateMa
anchors.fill: parent
onClicked: chatMsgDelegRoot.clicked(model.index)
}
Loader {
id: content
sourceComponent: model.type === "Text" ? textComponent : controlComponent
anchors {
left: isIncoming? undefined : parent.left
right: isIncoming? parent.right : undefined
leftMargin: Units.gu(10)
rightMargin: Units.gu(10)
top: parent.top
topMargin: Units.gu(4)
}
}
Text {
text: model.date.toTimeString()
font.pointSize: 8
font.italic: true;
color: "lightgrey"
anchors {
left: isIncoming? undefined : parent.left
right: isIncoming? parent.right : undefined
rightMargin: Units.gu(7.5)
leftMargin: Units.gu(7.5)
bottom: parent.bottom
bottomMargin: Units.gu(1)
}
}
} // BorderImage
} // Column
// TODO To separate files.
Component {
id: textComponent
Rectangle {
id: textComponentRoot
color: "#00000000"
width: msgText.paintedWidth
height: msgText.height
Text {
id: msgText
font.pointSize: 10
textFormat: Text.RichText
wrapMode: Text.WrapAtWordBoundaryOrAnywhere
width: chatMsgDelegRoot.width * 0.7
text: model.body
color: isSelected? "white" : "black"
}
}
} // Component
Component {
id: controlComponent
Rectangle {
id: textComponentRoot
color: "#00000000"
width: innerColumn.width
height: innerColumn.height
Column {
id: innerColumn
spacing: Units.gu(1)
Text {
id: fileNameText
font.pointSize: 10
wrapMode: Text.WrapAtWordBoundaryOrAnywhere
width: chatMsgDelegRoot.width * 0.7
elide: Text.ElideRight
text: "File transfer: " + model.body
color: isSelected? "white" : "black"
}
Row {
id: innerRow
anchors.right: parent.right
spacing: Units.gu(1)
SimpleButton {
id: allowBtn
width: Units.gu(15)
height: Units.gu(8)
text: "Allow"
}
SimpleButton {
id: denyBtn
width: Units.gu(15)
height: Units.gu(8)
text: "Deny"
}
}
} // Column
}
} // Component
}
I have a problem with my QML code. I have a different pages(each page is a different .qml file) and want to change from one page to another using vertical swipe/scroll. The QT already provides a swipe examples but are only for horizontal swipe view, and I want a vertical swipe. I also saw an examples for vertical swipe using ListView. However, in the list view, so far I only can put text.
I want to put a different QML view in each ListView row.There are some examples using external qml files in the "delegate", but none are working for me.
In other tutorials, I saw a code similar to this one:
//This is the main.qml
ListView {
id: iranCitiesList
snapMode: ListView.SnapOneItem
highlightRangeMode: ListView.StrictlyEnforceRange
model: ListModel
delegate: Loader {
height: childrenRect.height
width: parent.width
source: "Page1.qml"
}
}
Page1 { id: page }
//listModel.qml
import QtQuick 2.9
ListModel {
ListElement { sourceComponent: Page1}
ListElement { sourceComponent: Page2}
}
//Page1.qml
//The Page1.qml is very basic. Only displays a simple text
import QtQuick 2.9
Rectangle {
id: delegateItem
width: parent.width; height: 100
color: "white"
Text {
id: itexItem
font.pixelSize: 40
text: "Page1"
}
}
Here, the view from Page1.qml is loaded, but I cant scroll between pages.
Now, heres the code I'm working on. This one is a working list view.
Its very basic, but its working.
//main.qml
ListView {
id: listView
snapMode: ListView.SnapOneItem
highlightRangeMode: ListView.StrictlyEnforceRange
anchors {
top: parent.top
bottom: parent.bottom
left: parent.left
right: parent.right
}
model: ListModel {
id: listModel
ListElement {
text: "1"
}
ListElement {
text: "2"
}
}
delegate: Page {
width: ListView.view.width
height: ListView.view.height
Text {
anchors.centerIn: parent
text: model.text
}
}
}
As I said before, the Listview is loadded, I can scroll between each row but I want to put a more complex design in my list. Like an external qml view.
How can I do that?
Thanks!
You should read the dynamic object creation documentation : https://doc.qt.io/qt-5/qtqml-javascript-dynamicobjectcreation.html
ListView{
snapMode: ListView.SnapOneItem
highlightRangeMode: ListView.StrictlyEnforceRange
anchors {
top: parent.top
bottom: parent.bottom
left: parent.left
right: parent.right
}
model: ListModel {
ListElement { path: "file1.qml" }
ListElement { path: "file2.qml" }
}
delegate: Page {
width: ListView.view.width
height: ListView.view.height
Component.onCompleted: {
var component = Qt.createComponent(path)
if (component.status == Component.Ready)
component.createObject(this, {"width": width, "height": height});
}
}
}
This code should work, you just have to put your pages paths in the model.