How to use own objects in ColumnLayout in QML? - qt

I would like to produce a table where in every Row is a Text() and a ComboBox(). I want the comboboxes are aligned right and also the left side of the text labels something like that:
I have the main qml:
import QtQuick 2.12
import QtQuick.Window 2.12
import QtQuick.Controls 2.12
import QtQuick.Layouts 1.12
Window {
id: wind
width: 640
height: 480
visible: true
title: qsTr("Public Transport Searcher")
property string oz: "Odjezdová zastávka"
property string pz: "Příjezdová zastávka"
property string co: "Čas odjezdu"
Rectangle{
id: rect
x: wind.width/16
implicitHeight: parent.height/3*2
implicitWidth: wind.width/8*7
anchors.right: parent.right
anchors.left: parent.left
anchors.margins: 20
radius: 5
color: "lightblue"
ColumnLayout{
id: cl
spacing: 30
width: parent.width
anchors.top: parent.top
anchors.left: parent.left
anchors.margins: 20
anchors.fill: parent
Row{
id: r1
Layout.alignment: Qt.AlignTop
//Layout.alignment: Qt.AlignTop
Layout.margins: 15
Text{
id: r1t1
text: "Vyhledávač jízdních řádů"
font.pointSize: 16
font.bold: true
}
}
Line{
id: r2
tt2: oz
Layout.alignment: Qt.AlignLeft
//anchors.top: r1.bottom
}
Line{
id: r3
tt2: pz
Layout.alignment: Qt.AlignLeft
}
Line{
id: r4
tt2: co
Layout.alignment: Qt.AlignLeft
}
}
}
}
And a separate Line.qml item:
import QtQuick 2.15
import QtQuick.Controls 2.12
Item {
property string tt2: "v"
Text{
id: txt
text: tt2
anchors.verticalCenter: parent.verticalCenter
anchors.left: parent.left
}
ComboBox{
id: cb
anchors.verticalCenter: parent.verticalCenter
//anchors.fill: parent
anchors.left: txt.right
}
}
Now it runs but the all Rows are overwritting in the top left corner.
I know that I could use the GridLayout but I want to take advantage from the own objects for one row (in case I have much such rows) to avoid copy paste technic and initialise every object of the row separate.
Could you show me please how to do that in an elagant way?

The problem is that your Line items do not have any implicit size which the ColumnLayout can read from. That is, the Line's base is just an Item whose default implicitHeight and implicitWidth are both 0 - so the ColumnLayout renders them improperly. You have several options.
One option is to add implicitHeight/implicitWidth (or Layout.preferredWidth/Layout.preferredHeight) to your Line's Item:
import QtQuick 2.12
import QtQuick.Controls 2.12
Item {
implicitHeight: 30
implicitWidth: parent.width
property string tt2: "v"
Text{
id: txt
text: tt2
anchors.verticalCenter: parent.verticalCenter
anchors.left: parent.left
}
ComboBox{
id: cb
anchors.verticalCenter: parent.verticalCenter
//anchors.fill: parent
anchors.left: txt.right
}
}
A different way of accomplishing this would be to change the base of the Line to a RowLayout (and remove the interior anchors which would conflict):
import QtQuick 2.12
import QtQuick.Controls 2.12
import QtQuick.Layouts 1.12
RowLayout {
property string tt2: "v"
Text {
id: txt
text: tt2
Layout.preferredWidth: 200 // to align ComboBoxes, all text must have same width
// Layout.fillWidth: true // a different option for aligning ComboBoxes
}
ComboBox {
id: cb
}
}

Related

How to call data from elsewhere when I click a button in QML

When I click a button in QML, I want the data from another place that I created before to come to the textfields in my table, how can I do it? Can I do this in QML or do I need to create a separate backend cpp file? Can you help me please?
For example this below code is one row of my table
Rectangle{
border.width: 2
border.color: "black"
id:rectangle_mov_mean_nokta_sayisi
Layout.alignment: Qt.AlignCenter
Layout.preferredWidth: mainWindow.width/8
Layout.preferredHeight: mainWindow.height/22
Layout.margins: -3
Layout.fillWidth: true
color: row_even
Text{
color:normal_text
id:text_rectangle_mov_mean_nokta_sayisi
text:"Mov Mean Nokta Sayısı"
anchors.centerIn: parent
}
}
Rectangle{
border.width: 2
border.color: "black"
id:rectangle_mov_mean_nokta_sayisi_deger
Layout.alignment: Qt.AlignCenter
Layout.preferredWidth: mainWindow.width/8
Layout.preferredHeight: mainWindow.height/22
Layout.margins: -3
Layout.fillWidth: true
color:row_even
TextField {
id:textfield_rectangle_mov_mean_nokta_sayisi_deger
anchors.centerIn: parent
placeholderText: qsTr("")
color:normal_text
}
}
There are 20 of these rectangles. When I click a button, a separate value will be displayed for each text field. I need to create these values elsewhere.
you should read Signal and Slots in QML.
For example :
If you have 3 separate QML files:
In main.qml:
import QtQuick 2.12
import QtQuick.Window 2.12
Window {
visible: true
width: 640
height: 480
title: qsTr("External Components with signals and slots")
Notifier{
id : notifierId
rectColor: "yellowgreen"
target: receiverId
}
Receiver {
id : receiverId
rectColor: "dodgerblue"
anchors.right: parent.right
}
Component.onCompleted: {
notifierId.notify.connect(receiverId.receiveInfo)//Connect signal to slot
}
}
and in Receiver.qml :
import QtQuick 2.12
Item {
property alias rectColor: receiverRectId.color
width: receiverRectId.width
height: receiverRectId.height
function receiveInfo( count){
receiverDisplayTextId.text = count
console.log("Receiver received number : "+ count)
}
Rectangle {
id : receiverRectId
width: 200
height: 200
color: "red"
Text {
id : receiverDisplayTextId
anchors.centerIn: parent
font.pointSize: 20
text : "0"
}
}
}
and in Notifier.qml:
import QtQuick 2.12
Item {
property alias rectColor: notifierRectId.color
width: notifierRectId.width
height: notifierRectId.height
property int count: 0
signal notify( string count)//Declare signal
property Receiver target : null
onTargetChanged: {
notify.connect(target.receiveInfo)
}
Rectangle {
id : notifierRectId
width: 200
height: 200
color: "red"
Text {
id : displayTextId
anchors.centerIn: parent
font.pointSize: 20
text : count
}
MouseArea{
anchors.fill: parent
onClicked: {
count++
notify(count)
}
}
}
}
you can see that by using signals and slots you can send data from Notifier.qml to Receiver.qml.

QML - Using ListView with dynamically generated ListElement

I have this QML with the ListView:
import QtQuick 2.0
import QtQuick.Controls 2.12
import QtQuick.Layouts 1.0
import smah.light 1.0
import smah.zone 1.0
Page {
id: page
property var lights: ({})
property var lightNames: ([])
title: "Device control"
ListView {
id: lightList
anchors.fill: parent
model: listModel
delegate:
Rectangle{
width: lightList.width
height: lightList.height / 8
}
}
ListModel {
id: listModel
dynamicRoles: true
Component.onCompleted: {
listModel.clear()
for (var i=0; i<lights.length; i++) {
var component = Qt.createComponent("LightControls.qml",listModel)
listModel.append(component.createObject(lightList, {light_name: lights[i].getName }))
}
}
}
}
The LightControls.qml is:
import QtQuick 2.0
import QtQuick.Controls 2.5
import smah.light 1.0
Rectangle {
id: rectangle
property int light_type: 0
property int light_id: 0
property var light_name: "_DEF"
width: parent.width
height: 50
color: "#040000"
Text {
id: txtName
color: "#fefdfd"
text: qsTr(light_name)
anchors.left: parent.left
anchors.leftMargin: 15
anchors.top: parent.top
anchors.topMargin: 8
font.pixelSize: 20
}
Slider {
id: slider
x: 179
y: 10
width: 302
height: 22
value: 0.5
}
Text {
id: txtValue
x: 517
width: 45
height: 15
color: "#ffffff"
text: qsTr("Text")
anchors.top: parent.top
anchors.topMargin: 8
font.pixelSize: 20
}
Button {
id: button
x: 694
y: 10
text: qsTr("Button")
}
}
I want a clean scrollable list with each of these items generated shown in it. I've looked at using a Repeater instead of the list, but I'll have more elements in the list than are desired on the screen. When running the program, everything is garbled into a single incoherent mess:
There are a few larger issues with your code:
You're trying to add a visual item to a ListModel, which expects ListElement objects. You can use append() to add rows to a ListModel.
Your root delegate item is a Rectangle, which doesn't manage the layout of its children. Use a RowLayout or Row instead.
Your delegate should be delegate: LightControls {}.

How to restrict maximum lines of user input in TextEdit in QML

I am currently trying to implement a textedit within a rectangle. The problem is that the user is still able to type beyond the confines of the rectangle. I have set the wrapMode to TextEdit.Wrap, but the problem is that the text from the textedit can spill over from the bottom of the rectangle. I tried to fix this by making clip true, but the user is still able to type characters but not see it. What do I do?
import QtQuick 2.12
import QtQml.Models 2.12
import QtQuick.Controls 2.12
import QtQuick.Controls.Styles 1.4
import QtQuick.Layouts 1.12
import QtGraphicalEffects 1.12
import QtMultimedia 5.0
Rectangle{
anchors{
top: parent.top
topMargin: parent.height/15
left: parent.left
leftMargin: parent.width/15
right: parent.right
rightMargin: parent.width/15
bottom: parent.bottom
bottomMargin: parent.height/1.2
}
color: 'white'
z: 1
radius: 15
TextEdit{
clip: true
cursorPosition: 5
anchors.fill: parent
wrapMode: TextEdit.Wrap
}
}
This is an image of the text with rectangle: clip is not set and wrapMode: TextEdit.Wrap. This image is the opposite of what I want
There is actually no way to limit the maximum number of rows. Theoretically, since this is open source, you can do anything, but it will require some effort. I can suggest a workaround:
Rectangle {
id: container
width: 200
height: 40
anchors.centerIn: parent
color: "orange"
TextEdit {
id: txt
anchors.fill: parent
padding: 3
font.pixelSize: 14
focus: true
wrapMode: TextEdit.Wrap
onTextChanged: {
var pos = txt.positionAt(1, container.height + 1);
if(txt.length >= pos)
{
txt.remove(pos, txt.length);
}
}
}
}
This method just cuts off everything that is found outside of the box.

QML: QtQuick.Controls Tabs with Icons

I have been learning how to use QT Creator Tool so that I can build UI's quickly and easily. For my current project, I have to use QML to build my UI. I want to have tabs in my display. I would like to use an image in place of text in my tab. My code is below. I have tried to add a source but that did not help me add an icon. Does anyone know how to do this? All help would be greatly appreciated.
import QtQuick 2.5
import QtQuick.Window 2.2
import QtQuick.Controls 1.0
import QtQuick.Controls.Styles 1.2
Window {
visible: true
width: 640
height: 400
opacity: 1
TabView {
height: 300
anchors.rightMargin: 0
anchors.bottomMargin: 0
anchors.leftMargin: 0
anchors.topMargin: 0
anchors.fill: parent
Tab {
title: "Robot"
component: Qt.createComponent("RobotControls.qml")
}
Tab{
title: "Tab #2"
}
}
}
Elaborating on the answer from Simon Warta in Extending TabViewStyle styleData, here is what you could do :
Define a custom IconTab
You want to extend the Tab item so that you can specify an icon to display.
So add a new file to your project, called IconTab.qml:
IconTab.qml
import QtQuick.Controls 1.4
Tab{
property string icon
}
Define a custom TabViewStyle.
In order to use this new property, you must create your own TabViewStyle. You may have to redefine background and text size and colors so that it fits your app theme, but something like this could work:
MyStyle.qml
import QtQuick 2.5
import QtQuick.Controls.Styles 1.2
TabViewStyle {
tab: Item {
implicitWidth: Math.round(textitem.implicitWidth + image.width + 20)
implicitHeight: Math.round(textitem.implicitHeight + 10)
Rectangle
{
anchors.fill: parent
anchors.bottomMargin: 2
radius: 1
border.width: 1
border.color: "#AAA"
color:"transparent"
}
Rectangle
{
anchors.fill: parent
anchors.margins: 1
anchors.bottomMargin: styleData.selected ? 0 : 2
radius: 1
gradient: Gradient{
GradientStop{position:0; color:styleData.selected?"#EDEDED":"#E3E3E3"}
GradientStop{position:1; color:styleData.selected?"#DCDCDC":"#D3D3D3"}
}
}
Text {
id: textitem
anchors.fill: parent
anchors.leftMargin: 4 + image.width
anchors.rightMargin: 4
verticalAlignment: Text.AlignVCenter
horizontalAlignment: Text.AlignHCenter
text: styleData.title
elide: Text.ElideMiddle
}
Image
{
id: image
anchors.top: parent.top
anchors.bottom: parent.bottom
anchors.left: parent.left
anchors.margins: 2
anchors.leftMargin: 4
fillMode: Image.PreserveAspectFit
source: control.getTab(styleData.index).icon
}
}
}
Note how you can make use of the control property and the styleData.index to get your icon's url: control.getTab(styleData.index).icon
Put the pieces together
main.qml
import QtQuick 2.5
import QtQuick.Window 2.2
import QtQuick.Controls 1.0
Window {
visible: true
width: 640
height: 400
TabView {
id: tabView
anchors.fill: parent
style: MyStyle{}
IconTab {
title: "Tab #1"
icon: "icon.png"
}
IconTab{
title: "Tab #2"
}
IconTab{
title: "Tab #3"
icon: "icon.png"
}
}
}
Result

How do I show a message box with Qt Quick Controls?

What is the equivalent of QMessageBox::information() when one wishes to write a QML application using Qt Quick Controls?
In Qt 6.3 and later you can use MessageDialog from QtQuick.Dialogs:
MessageDialog {
text: "The document has been modified."
informativeText: "Do you want to save your changes?"
buttons: MessageDialog.Ok | MessageDialog.Cancel
onAccepted: Qt.quit()
}
In Qt 6.2 and earlier you can use MessageDialog from Qt.labs.platform (using the same example code as above).
In Qt 5 you can use MessageDialog from QtQuick.Dialogs 1.x:
import QtQuick 2.2
import QtQuick.Dialogs 1.1
MessageDialog {
id: messageDialog
title: "May I have your attention please"
text: "It's so cool that you are using Qt Quick."
onAccepted: {
console.log("And of course you could only agree.")
Qt.quit()
}
Component.onCompleted: visible = true
}
You Can use Popup In QtQuick Controls 2 :
import QtQuick.Window 2.2
import QtQuick.Controls 2.0 // or import Qt.labs.controls 1.0
Window {
id: window
width: 400
height: 400
visible: true
Button {
text: "Open"
onClicked: popup.open()
}
Popup {
id: popup
x: 100
y: 100
width: 200
height: 300
modal: true
focus: true
closePolicy: Popup.CloseOnEscape | Popup.CloseOnPressOutsideParent
}
}
Ok this does the job (badly). Import the Window object:
import QtQuick.Window 2.1
Then add this to your main window (or you could put it in another file I guess):
function showMessage(text, title)
{
messageBox.text = text;
messageBox.title = title;
messageBox.visible = true;
}
Window {
id: messageBox
modality: Qt.ApplicationModal
title: ""
visible: false
property alias text: messageBoxLabel.text
color: parent.color
minimumHeight: 100
minimumWidth: 300
Label {
anchors.margins: 10
anchors.top: parent.top
anchors.left: parent.left
anchors.right: parent.right
anchors.bottom: messageBoxButton.top
horizontalAlignment: Text.AlignHCenter
wrapMode: Text.WordWrap
id: messageBoxLabel
text: ""
}
Button {
anchors.margins: 10
id: messageBoxButton
anchors.bottom: parent.bottom
anchors.horizontalCenter: parent.horizontalCenter
text: "Ok"
onClicked: messageBox.visible = false
}
}
Problems with it:
The window can be shrunk so that the text and button overlap.
The minimum window size is hard-coded rather than calculated from the text size.
You can't select the text.
Looks a bit shit.
Unfortunately, there isn't one, at least not in the shipping Qt Quick Controls as of Qt 5.1.1 :(
You need to add it to your project via a QObject wrapper.
// CenteredDialog.qml
import QtQml 2.2
import QtQuick 2.9
import QtQuick.Controls 2.2
Dialog {
parent: ApplicationWindow.overlay
x: (parent.width - width) / 2
y: (parent.height - height) / 2
focus: true
modal: true
property alias text: messageText.text
Label {
id: messageText
verticalAlignment: Text.AlignVCenter
horizontalAlignment: Text.AlignHCenter
anchors.fill: parent
}
standardButtons: Dialog.Ok
}
You can declare CenteredDialog { id: centeredDialog } somewhere, then in some event handler you may call:
centeredDialog.title = qsTr("Error!")
centeredDialog.text = qsTr("Access violation")
centeredDialog.visible = true

Resources