Instantiating a "native" QML type dynamically - qt

Is it possible to pick between different types of sub-components dynamically (during instantiation)?
For example, some pseudocode (using Qt 5.9):
//MyComp.qml
import QtQuick 2.9
import QtQuick.Layouts 1.3
Item {
property bool useLayout: true
//Here I want to allow the user to choose
//whether a ColumnLayout or Column is used
//(e.g., by means of the useLayout property)
ColumnLayout { //Or Column
...
}
...
}
//main.qml
import QtQuick 2.9
import QtQuick.Layouts 1.3
import QtQuick.Controls 2.9
ApplicationWindow {
width: 640
height: 480
...
MyComp {
id: a
useLayout: false
...
}
}

I don't think there is a way to do exactly what you want, without a lot of javascript. The cleanest way to do this, that I can think of would be the following.
You could make the ColumnLayout invisible and set the Column as the parent of its children with something like this:
//MyComp.qml
import QtQuick 2.9
import QtQuick.Layouts 1.3
import QtQuick.Controls 2.5
Item {
property bool useLayout: true
ColumnLayout {
id: columnLayout
visible: useLayout
Component.onCompleted: {
if (!useLayout) {
butt1.parent = column;
butt2.parent = column;
butt3.parent = column;
}
}
Button {
id: butt1
text: "butt 1"
}
Button {
id: butt2
text: "butt 2"
}
Button {
id: butt3
text: "butt 3"
}
}
Column {
id: column
visible: !useLayout
}
}

Related

Access element in other QML file

I am trying to access items that are in a Repeater, using JavaScript. This works as long as everything is in a single QML file. I understand that, when I split things into two files, the id of the Repeater moves out of the scope of my script.
What I don't understand is how I best solve this problem, since I cannot export the id as property.
Here is a MWE with two files:
import QtQuick 2.9
import QtQuick.Window 2.2
import QtQuick.Controls 1.0
import QtQuick.Layouts 1.2
Window {
id: root
visible: true
width: 640
height: 480
ColumnLayout {
Mwe2 {}
Button {
text: "Test"
onClicked: {
console.log("Clicked");
rect.itemAt(1).color = 'yellow'; # <- out of scope
}
}
}
}
File Mwe2.qml
import QtQuick 2.9
import QtQuick.Controls 1.0
import QtQuick.Layouts 1.2
RowLayout {
spacing: 2
Repeater {
id: rect
model: 3
Rectangle {
color: 'red'
width: 50
height: 20
}
}
}
As you indicate, the ids have the scope of the qml file, so if you want to access those elements you must expose it through a property, function, etc. of the top level of the qml. In this case for simplicity I will use an alias.
import QtQuick 2.9
import QtQuick.Controls 1.0
import QtQuick.Layouts 1.2
RowLayout {
property alias repeater: rect
spacing: 2
Repeater {
id: rect
model: 3
Rectangle {
color: 'red'
width: 50
height: 20
}
}
}
import QtQuick 2.9
import QtQuick.Window 2.2
import QtQuick.Controls 1.0
import QtQuick.Layouts 1.2
Window {
id: root
visible: true
width: 640
height: 480
ColumnLayout {
Mwe2 {
id: mwe2
}
Button {
text: "Test"
onClicked: {
console.log("Clicked")
mwe2.repeater.itemAt(1).color = "yellow"
}
}
}
}

newline symbol in qml Textfield, text property not working

Is there a possibility to insert a newline character into the text property of a TextField component:
import QtQuick 2.6
import QtQuick.Controls 1.2
import QtQuick.Window 2.2
Window {
visible: true
width: 640
height: 480
Grid {
TextField {
text: "Text\nhere"
}
}
}
prints a blank instead of a newline in the Textfield
According to the docs:
TextField is used to accept a line of text input. [...]
Therefore you can not have multiple lines in that item. You must use another item like TextArea
import QtQuick 2.6
import QtQuick.Controls 1.2
import QtQuick.Window 2.2
Window {
visible: true
width: 640
height: 480
Grid {
TextArea {
text: "Text\nshere"
}
}
}

Adding TabButton dynamically to TabBar in QML

I am trying to add a tabButton to TabBar dynamically on pressing a button but i have spent a lot of time searching but i am not getting how to add, below is the code which i am working on :
MyTabButton.qml
import QtQuick 2.4
import QtQuick.Controls 2.2
Item
{
property int BtnWidth:0
property int BtnHeight:0
property string BtnText: ""
property bool isChecked : false
TabButton
{
id:tabBtn
text:BtnText
width:BtnWidth
height:BtnHeight
}
}
MainForm.qml
import QtQuick 2.4
import QtQuick.Controls 2.2
Rectangle
{
Button
{
id:button
width:100
height:100
anchors.top:parent.top
text:qStr("Add")
onClicked{
//How to add logic here to add tab in below tabBar.
}
}
TabBar
{
id:tabBar
anchors.top:button.bottom
width:500
height:500
}
}
Example:
import QtQuick 2.7
import QtQuick.Controls 2.0
ApplicationWindow {
id: window
width: 360
height: 630
visible: true
header: TabBar {
id: tabBar
}
Component {
id: tabButton
TabButton { }
}
Button {
text: "Add"
anchors.centerIn: parent
onClicked: {
var tab = tabButton.createObject(tabBar, {text: "Tab " + tabBar.count})
tabBar.addItem(tab)
}
}
}
You need to have something like a Component that is a TabButton. Your file MyTabButton.qml won't result in a TabButton, but instead an Item containing a TabButton, but with this, your TabBar does not know what to do.
So your file will need to have TabButton as root element
//MyTabButton.qml
import QtQuick 2.4
import QtQuick.Controls 2.2
TabButton
{
id: tabBtn
// customize as you like
}
Then you create a Component of this in your file where you want to use it. (e.g. main.qml)
import QtQuick 2.4
import QtQuick.Controls 2.0
ApplicationWindow {
width: 800
height: 600
visible: true
TabBar {
id: tabBar
width: 800
height: 50
}
// The component is like a factory for MyTabButtons now.
// Use myTabButton.createObject(parent, jsobject-with-property-assignments) to create instances.
Component {
id: myTabButton
MyTabButton {
/// EDIT ACCORDING TO YOUR COMMENTS ***
Connections {
target: tabBar
onCurrentIndexChanged: doSomething()
}
/// EDIT OVER
}
}
Button {
anchors.centerIn: parent
// Create a object out of the component, and add it to the container
onClicked: tabBar.addItem(myTabButton.createObject(tabBar /*, { object to set properties }*/))
}
}
TabBar inherits Container, which has addItem().
Try it in Window
Row {
anchors.fill: parent
TabBar {
id: tabBar
currentIndex: 0
width: parent.width - addButton.width
TabButton { text: "TabButton" }
}
Component {
id: tabButton
TabButton { text: "TabButton" }
}
Button {
id: addButton
text: "+"
flat: true
onClicked: {
tabBar.addItem(tabButton.createObject(tabBar))
console.log("added:", tabBar.itemAt(tabBar.count - 1))
}
}
}

Interaction between two QML files

I want to use some qml file(main.qml) but before that I need to get some authentication info from user(login, pass). So I want to do this in the second window(login.qml). I saw Qt.createComponent for opening second qml file, but I can't get any information from this type of window.
So how can I use some information from the second window in the first window?
Or how can I dynamically load these items(main.qml, login.qml) in the parent qml file?
So how can I use some information from the second window in the first
window?
This is just one way of doing it:
main.qml
import QtQuick 2.4
import QtQuick.Controls 1.3
import QtQuick.Window 2.2
import QtQuick.Layouts 1.1
ApplicationWindow {
width: 400
height: 400
visible: true
ColumnLayout {
id: logItems
height: 200
Button {
id: loginButton
onClicked: loginForm.visible = true
text: "Log in"
}
Login {
anchors.top: loginButton.bottom
id: loginForm
visible: false
onLoginInfo: {
logInfo.text = "User:" + user + " password: " + password
}
}
}
Text {
id: logInfo
anchors.top : logItems.bottom
}
}
Login.qml
import QtQuick 2.4
import QtQuick.Controls 1.3
import QtQuick.Layouts 1.1
Item {
signal loginInfo(string user, string password)
ColumnLayout {
RowLayout {
TextField {
id: user
}
TextField {
id: password
}
}
Button {
text: "Submit"
onClicked: loginInfo(user.text, password.text)
}
}
}
How can I dynamically load QML items from separate files/resources in
another QML file?
Qt.CreateComponent allows you to use JavaScript to dynamically create and use QML code from other files.

QML: move to next control in form

How can I move focus from one control to next one inside QML form?
By default it works with Tab button but I need to change it to Enter.
All the control are ordered with Gridlayout with 2 columns.
I've defined a new component, TextFieldMoveOnReturn.qml
import QtQuick 2.0
import QtQuick.Controls 1.1
TextField {
Keys.onReturnPressed: nextItemInFocusChain().forceActiveFocus()
}
If you use this one instead of TextField, you get the required behaviour
edit a better solution: define a new component GridLayoutNextOnReturn.qml
import QtQuick 2.0
import QtQuick.Layouts 1.1
GridLayout {
Keys.onReturnPressed: {
for (var i = 0; i < children.length; ++i)
if (children[i].focus) {
children[i].nextItemInFocusChain().forceActiveFocus()
break
}
}
}
and use normal TextField inside - works like a charm
In order to make it more robust and flexible, you should make same behaviour
for Tab and Enter/Return keys.
Handle keyPressed event and use KeyNavigation.tab instead of nextItemInFocusChain to focus next element as follow:
import QtQuick 2.12
import QtQuick.Controls 1.12
Column {
TextField {
id: field1
KeyNavigation.tab: field2
activeFocusOnTab: true
Keys.onReturnPressed: KeyNavigation.tab.forceActiveFocus();
}
TextField {
id: field2
KeyNavigation.tab: field3
activeFocusOnTab: true
Keys.onReturnPressed: KeyNavigation.tab.forceActiveFocus();
}
TextField {
id: field3
KeyNavigation.tab: field1
activeFocusOnTab: true
Keys.onReturnPressed: KeyNavigation.tab.forceActiveFocus();
}
}
So you controlled order of focus and users can use both tab and return keys interchangeably which results better UX.
Whenever you want to change order, just change KeyNavigation.tab values :)
Note: I extremely suggest you to avoid using nextItemInFocusChain because of future changes and flexibility
You can use onEditingFinished:
import QtQuick 2.2
import QtQuick.Controls 1.2
import QtQuick.Layouts 1.1
Rectangle {
width: 400
height: 400
GridLayout {
anchors.fill: parent
columns: 2
Label {
text: "Name"
}
TextField {
onEditingFinished: addressEdit.focus = true
}
Label {
text: "Address"
}
TextField {
id: addressEdit
}
}
}
Use Keys.onReturnPressed and forceActiveFocus()
import QtQuick 2.2
import QtQuick.Controls 1.2
import QtQuick.Layouts 1.1
Rectangle {
width: 400
height: 400
GridLayout {
anchors.fill: parent
columns: 2
Label {
text: "Name"
}
TextField {
Keys.onReturnPressed: organizationEdit.forceActiveFocus()
}
Label {
text: "Organization"
}
TextField {
id: organizationEdit
Keys.onReturnPressed: addressEdit.forceActiveFocus()
}
Label {
text: "Address"
}
TextField {
id: addressEdit
}
}
}

Resources