I put a RangeSlider which is from QtQuick.Controls 2.x into a Component. I've bound its min.value to my model object, which is a subclass of QObject and has been exposed to QML context.
I can access it from QML using the name: "settings". The control is supposed to read settings in the Loader's onLoaded:{...} to initialize itself. I have a Binding-Object outside of the Loader to write back any changes of the min.value.
But the control always set the settings' properties first, so I can not initialize it with settings's perperties.
Loader {
id: loader
sourceComponent: ctrl
onLoaded: {
loader.item.min = settings.min
}
}
Binding {
target: settings
property: "min"
value: loader.item.min.value
}
Component {
id: ctrl
Item {
property alias min: slider.first
RangeSlider {
id: slider
...
}
}
}
I want to assign loader.item.min.value (i.e. slider.first.value) the value of settings.min, but before this assignment happens, the settings.min is changed to slider.first.value. After the user set a new value, I want the settings.min to be set to slider.first.value.
Is there anybody know how to implement this correctly?
You can use the when-property of the the Binding-Object to deactivate the binding, until you have set the inital value. Set it initally to false, and change it to true in the first line of your onLoaded-handler
Also very interesting is the delayed-property. I can't guarantee you that this will work, but it might be an elegant solution. I have not tried it out yet.
Related
My app has 3 level:
- Main Form
- Settings
- Device searching
From the main form I open others using:
var component = Qt.createComponent("qrc:/touch/content/SettingsMain.qml");
win = component.createObject(rootWindow2);
In main form I created object Network (it is C++ class)
Network{
id: net1
}
Object "net1" is accesible by other QML objects which were not invoked by above code creating component. Unfortunately, all QML objects created by using the code above do not see "net1".I need something like global object for all QML files. Any ideas?
You should use a singleton for that, it exists for that exact purpose:
// in main.cpp
qmlRegisterSingletonType(QUrl(QStringLiteral("qrc:/touch/content/SettingsMain.qml")), "Core", 1, 0, "Settings");
Then you can access that from every QML file by importing:
import Core 1.0
//.. and use it
Settings.someProperty
Settings.someFoo()
You would also have to add a pragma Singleton line in the beginning of SettingsMain.
You can also skip registering the singleton from C++ if you implement a qmldir file, but IMO registering in C++ is better when the singleton is an integral part of the application.
When using qml singletons, you don't need to create the instance yourself, it will be automatically created.
Your question is ambigious as to what you actually want to be "global", I assume settings is one thing you would like to be global.
You can also register C++ objects as singletons in QML, for example:
qmlRegisterSingletonType<Network>("Core", 1, 0, "Network", someFooReturningValidNetworkPtr);
Singleton is not the only way. QML provides lots of ways to get same results.
Another way is to pass the net1 id in as a property when calling Qt.createObject(). Example below:
main.qml
import QtQuick 2.7
import QtQuick.Controls 2.0
ApplicationWindow {
visible: true
width: 640
height: 480
Item {
id: rootWindow2
property Item settingsMain
Network{
id: net1
}
Component.onCompleted: {
var component = Qt.createComponent("qrc:/touch/content/SettingsMain.qml");
settingsMain = component.createObject(rootWindow2, {"net1": net1});
}
}
}
SettingsMain.qml
import QtQuick 2.0
Item {
property Item net1
Component.onCompleted: {
console.log("SettingsMain.qml: can I see net1? %1".arg(net1 ? "yes" : "no"))
}
}
I want to automatically make links (e.g. https://xmpp.org/) into the text of a Text element clickable, so the link can be opened in a browser (without manually copying the link).
I can't add e.g. manually in my code, because the input comes directly from users.
Has Qt a simple solution for this in QtQuick/QML?
You can use something like that(Regex is from this answer);
Text {
property string text2: "http://www.google.com"
text: isValidURL(text2) ? ("<a href='"+text2+"'>"+text2+"</a>") : text2
onLinkActivated:{
if (isValidURL(text2)){
Qt.openUrlExternally(text2)
}
}
function isValidURL(str) {
var regexp = /(ftp|http|https):\/\/(\w+:{0,1}\w*#)?(\S+)(:[0-9]+)?(\/|\/([\w#!:.?+=&%#!\-\/]))?/
return regexp.test(str);
}
}
You can use TextArea or TextEdit components, set textFormat property to TextEdit.RichText and listen to onLinkActivated signal.
E.g.
TextArea {
id: ...
textFormat: TextEdit.RichText
onLinkActivated: Qt.openUrlExternally( link )
}
Note: in order the link in browser you need to use Qt.openUrlExternally
One hint, in order to make the component not editable (so that user can not type in), DO NOT set enabled property (inherited from Item) to false, use readOnly property instead. Setting enabled would make link unclickable.
I have created a ListView in QML, and I want to be able to implement something like an active item, using an QAbstractListModel as the model that the QML uses. To be more specific, I am using a generic object model, as described in the answer to this question. However, in my QML delegate I have something like this:
Component
{
id: itemDlgt
Rectangle
{
id: rec
width: 50
height: 50
color: "#645357"
property bool itemActive: false
AbstractItem //AbstractItem is the class that my object model uses. Its only property is a boolean value
{
id: s
}
MouseArea
{
anchors.fill: parent
onClicked:
{
s.status = !s.status
itemActive= s.status // using this to trigger onItemActiveChanged
console.log(s.status)
console.log(index)
}
}
onItemActiveChanged:
{
if (itemActive == true)
rec.color = "#823234"
else
rec.color = "#645357"
}
}
}
What I want to do, is have only one item in the ListView to hold a true value at a time. As soon as another item is clicked, I want to set the AbstractItem of the previously selected item to false, then set the AbstractItem of the new item to true.
Of course, I could use something like this:
ListView
{
id: view
anchors.fill: parent
clip: true
model: myAbstractModel
delegate: itemDlgt
spacing: 5
focus: true //using focus could allow to highlight currently selected item,
//by using ListView.isCurrentItem ? "blue": "red"
}
But this doesn't seem to work with a QAbstractListModel, since neither arrow keys, nor clicking on an item seems to highlight the current item.
In addition, I want that item to be highlighted again, in the event that the ListView is forced to reset itself from the c++ side, when I use beginResetModel() and endResetModel(). If I were using a QAbstractListModel as described in Qt Documentation, I would be able to do that easily, by saving the index of the selected item, and storing it until a new item was selected. In other words, something like this:
//somewhere in QAbstractListModel's subclass .h file
int x; // temporary storage for keeping currently selected item
//QAbstractListModel's subclass .cpp file
changeCurrentItem(int index) // function that gets called when user selects an item
{
//...
//Checking if x has a value, If not, I simply set the
//needed item's value to true, and then set x to the value of index.
//Else do the following...
m_ItemList.at(x).setToFalse();
m_ItemList.at(index).setToTrue();
x = index;
}
But I was facing several issues when I was using that, which is the reason why I decided to use a generic object model, which seems to be more flexible.
Finally, I want to be able to send a signal to the c++ side of the code whenever the currently selected item changes, which is trivial with a MouseArea, but I know not of a method to do that using the ListView's focus property, should that be an option.
To make long things short, this is my question in a few words:
Am I missing something in regards to QML code, that would allow me to highlight the currently selected item, while also being able to keep it active after reseting ListView, and being able to send a signal to c++ when it changes?
If not, is there a way to implement a function inside my generic object model, that keeps track of the currently selected item, so that I can highlight it?
If using the ListView's currentIndex property is the goto approach.
Simply set the color in the delegate via:
color: index == view.currentIndex ? "blue": "red"
And don't forget that in order for this to work, you must set the active index, it doesn't work by magic, and by default it will be -1, so no item will be highlighted:
//in the delegate mouse area
onClicked: view.currentIndex = index
The same applies to keyboard events too, you will have to tell the view what to do with the events, so you can have the up and down keys increment and decrements the current index.
There is another approach, which is more applicable if you want to detach the active item functionality from any view, and have it at item level:
property ItemType activeItem: null
function setActiveItem(item) {
if (activeItem) activeItem.active = false
activeItem = item
activeItem.active = true
}
The focus property only specifies whether an item has keyboard event focus or not.
If you want to signal on the the index change, use onCurrentIndexChanged
I have a global singleton "Settings" which holds application settings. When I try to run the following code I get a QML CheckBox: Binding loop detected for property "checked":
CheckBox {
checked: Settings.someSetting
onCheckedChanged: {
Settings.someSetting = checked;
}
}
It is obvious why this error occurs, but how can I correctly implement this functionality without a binding loop? E.g. I want to save the current checked state of the checkbox in the settings singleton.
I am using Qt 5.4 and Qml Quick 2.
Regards,
Don't bind it. Because the check box does not fully depend on Setting.someSetting.
When a user clicked the checkbox, the CheckBox.checked is changed by itself. At the same time, the property binding is no longer valid. Settings.someSetting cannot modify the CheckBox after it is clicked by user. Therefore, the checked: Settings.someSetting binding is wrong.
If you want to assign an initial value to the check box when the component is ready, use Component.onCompleted to assign it:
CheckBox {
id: someSettingCheckBox
Component.onCompleted: checked = Settings.someSetting
onCheckedChanged: Settings.someSetting = checked;
}
If you are working on a more complex scenario, the Setting.someSetting may be changed by some other things during runtime and the state of the check box is required to be changed simultaneously. Catch onSomeSettingChanged signal and explicitly changed the check box. Submit the value of someSettingCheckBox to Settings only when the program/widget/dialog/xxx finished.
CheckBox { id: someSettingCheckBox }
//within the Settings, or Connection, or somewhere that can get the signal.
onSomeSettingChanged: someSettingCheckBox.checked = someSetting
I prefer this solution
// Within the model
Q_PROPERTY(bool someSetting READ getSomeSetting WRITE setSomeSetting NOTIFY someSettingChanged)
void SettingsModel::setSomeSetting(bool checkValue) {
if (m_checkValue != checkValue) {
m_checkValue = checkValue;
emit someSettingChanged();
}
}
// QML
CheckBox {
checked: Settings.someSetting
onCheckedChanged: Settings.someSetting = checked
}
The trick is you protect the emit with an if check in the model. This means you still get a binding loop but only a single one, not an infinite one. It stops when that if check returns false thereby not emitting to continue the loop. This solution is very clean, you do not get the warning, and yet you still get all the benefits of the binding.
I want to talk about the limitations of the other solutions presented
CheckBox {
Component.onCompleted: checked = Settings.someSetting
onCheckedChanged: Settings.someSetting = checked;
}
In this solution you lose your binding. It can only have a default setting on creation and be changed by the user. If you expand your program such that other things change the values in your model, this particular view will not have a way to reflect those changes.
Settings {
id: mySettings
onSomeSettingChanged: checkBox.checked = someSetting
}
CheckBox {
id: checkBox
onCheckedChanged: mySettings.someSetting = checked
}
This solution was mentioned to address these problems but never written out. It is functionally complete. Model changes are reflected, the user can change the data, and there are no binding loops because there are no bindings; only two discrete assignments. (x: y is a binding, x = y is an assignment)
There are a couple problems with this. The first is that I think its ugly and inelegant, but that is arguably subjective. It seems fine here but if you have a model representing 10 things in this view, this turns into signal spaghetti. The bigger problem is that it does not work well with delegates because they only exist on demand.
Example:
MyModel {
id: myModel
// How are you going to set the check box of a specific delegate when
// the model is changed from here?
}
ListView {
id: listView
model: myModel.namesAndChecks
delegate: CheckDelegate {
id: checkDelegate
text: modelData.name
onCheckStateChanged: modelData.checkStatus = checked
}
}
You can actually do it. I've made up custom QML signals and connections to do it, but the code complexity makes me want to hurl, and even worse you could possibly be forcing creation of a delegate when it is not necessary.
If you don't want to make a binding loop - don't make a binding, use a proxy variable, for example. Other simple solution can be to check the value:
CheckBox {
checked: Settings.someSetting
onCheckedChanged: {
if (checked !== Settings.someSetting) {
Settings.someSetting = checked;
}
}
}
You can also make two-way binding to resolve this issue:
CheckBox {
id: checkBox
Binding { target: checkBox; property: "checked"; value: Settings.someSetting }
Binding { target: Settings; property: "someSetting"; value: checkBox.checked }
}
Sometimes it is useful to separate input and output values in control. In this case control always displays real value and it can also show a delay to the user.
CheckBox {
checked: Settings.someSetting
onClicked: Settings.someSetting = !checked
}
I'm trying to implement a BB10 settings menu, looking like the one in the Calendar app for example. The question here is, which components should I use? Using a ListView with an XML model looks great, but is incompatible with translation. Using a C++ model looks overkill for a simple menu with a couple of entries…
There's probably an established pattern somewhere, but I can't find it.
Screenshot of the Calendar app settings view
What you want is the expendable content property of the title bar:
I would create a QML object that you can re-use for each entry with properties for title and image.
So for example, something perhaps like this:
SettingEntry.qml
Container {
property alias title:title.Text
signal click()
TextView {
id: title
text: "[title goes here]"
}
gestureHandlers: [
TapHandler {
onTapped: {
click();
}
}
]
}
Then in your settings page you would use it like a normal object:
Page {
Container {
SettingEntry {
title: "General"
onClick: {
//open general page
}
}
SettingEntry {
title: "Invitation Settings"
}
}
}
The above is obviously very simplified, you would want to include an icon image, add translation code and add visual adjustments like filling the width and padding.
It should however give you a good idea of where to start.
I also included a gesturehandler and signal to show you how to handle events such as a click.