I have this signal
class SystemUICfgScanner
{
/*code here*/
signals:
void error(QString desc);
/*more code*/
};
In QML I use an InfoBanner this way:
InfoBanner
{
property string infodetails: ""
id: systemuicfgErrorBanner
text: "Error: " + infodetails
Connections
{
target: cfgScanner
onError: infodetails = desc
}
}
When error(QString) signal is emitted, I'm getting this error
Invalid write to global property "infodetails"
What am I doing wrong?
Thanks in advance
Try to reference InfoBanner instance by id:
InfoBanner
{
property string infodetails: ""
id: systemuicfgErrorBanner
text: "Error: " + infodetails
Connections
{
target: cfgScanner
onError: systemuicfgErrorBanner.infodetails = desc
}
}
Related
I have data in a dictionary which is [String: String]. What I want to provide is an interface to the user to edit the values in the dictionary, while the keys remain fixed. I can see how to display the values, but putting them into a TextField is what I want, and haven't been able to find how to do.
Here is the code:
struct dictionaryEditor: View {
#Binding var entries: [String: String]
var body: some View {
List {
ForEach(entries.sorted(by: <), id: \.key) { key, value in
HStack {
Text(key)
TextField("", text: $entries[key])
}
}
}
}
}
This doesn't compile, with no fewer than three errors on the TextField line:
Cannot convert value of type 'Slice<Binding<[String : String]>>' to expected argument type 'Binding'
Cannot convert value of type 'String' to expected argument type 'Range<Binding<[String : String]>.Index>'
Referencing subscript 'subscript(_:)' on 'Binding' requires that '[String : String]' conform to 'MutableCollection'
So obviously I am doing things incorrectly, but I am lost trying to find what the correct way would be, and haven't been able to find an answer in an internet search. Can anyone point me in the right direction?
you could try this simple approach:
struct dictionaryEditor: View {
#Binding var entries: [String: String]
var body: some View {
List {
ForEach(entries.keys.sorted(by: <), id: \.self) { key in
HStack {
Text(key)
TextField("", text: Binding(
get: { entries[key]! },
set: { entries[key] = $0 }
))
}
}
}
}
}
struct ContentView: View {
#State var entries: [String: String] = ["key1":"val1", "key2":"val2", "key3":"val3", "key4":"val4"]
var body: some View {
dictionaryEditor(entries: $entries)
Button(action: { print("----> entries: \(entries)") }) {
Text("print entries")
}
}
}
The problem is that entries[key] returns an optional String value while the text parameter of TextField expects a Binding of non optional String.
You can create an optional binding extension and then you can use it safely:
extension Binding where Value == String? {
var optionalBind: Binding<String> {
.init(
get: {
wrappedValue ?? ""
}, set: {
wrappedValue = $0
}
)
}
}
Then you can just add the optionalBind to your code:
struct dictionaryEditor: View {
#Binding var entries: [String: String]
var body: some View {
List {
ForEach(entries.sorted(by: <), id: \.key) { key, value in
HStack {
Text(key)
TextField("", text: $entries[key].optionalBind) // <--
}
}
}
}
}
I am trying to implement a keyClick with a shift modifier but it doesn't work. Below is a basic setup of what I am trying to do. The first test_case1 can perform what I am doing but I'd like the second test_case2 to work as well but with using the Qt.ShiftModifier.
../MyTextBox.qml
Page {
id: page1
objectName: "page1"
TextField {
id: lastNameField
objectName: "lastNameField"
text: qsTr("")
}
}
tst_page.qml
import "../"
Item {
width: 800
height: 600
MyTextBox {
id: page1
}
TestCase {
id: "txtBox"
when: windowShown
function test_case1 () {
//var qmlObj = findChild(page1, "lastNameField")
var qmlObj = page1.lastNameField
// Bring to focus
mouseClick(qmlObj, Qt.LeftButton, Qt.NoModifier)
// Keypress
keyPress("Y")
keyPress("e")
keyPress("s")
tryCompare(qmlObj, "text", "Yes") // pass
}
function test_case2 () {
var qmlObj = page1.lastNameField
// Bring to focus
mouseClick(qmlObj, Qt.LeftButton, Qt.NoModifier)
// Keypress
keyClick(QT.Key_Y, Qt.ShiftModifier)
keyClick(QT.Key_E)
keyClick(QT.Key_S)
tryCompare(qmlObj, "text", "Yes") // fail
}
}
}
test output
PASS : test_case1()
FAIL! : test_case2()
Actual (): yes
Expected (): Yes
Edit: Added a simple project to github for testing.
UPDATED AFTER ADJUSTMENTS
So after amending my code from the suggestions and working with the visibility of my image and pushing the dates to an array I am still not seeing the individually marked dates, my new code is:
property var arrayFromFirebase: []
onFirebaseReady: {
firebaseDb.getUserValue("dates", {
orderByChild: true
}, function(success, key, value) {
if(success) {
console.log(JSON.stringify(value))
for( date in value){arrayFromFirebase.push(date.date)}
}
})
}
Image {
visible: arrayFromFirebase.indexOf(styleData.date.getDate()) > -1
}
My log still reads the arrayFromFirebase in the format of:
[{"date":"2018-10-01T21:17:00.926"},{"date":"2018-10-02T12:00:00.000"},{"date":"2018-10-03T12:00:00.000"},{"date":"2018-10-06T12:00:00.000"},{"date":"2018-10-07T12:00:00.000"},{"date":"2018-10-08T12:00:00.000"}]
ORIGINAL QUESTION
I have my a calendar which my users save selected dates which are stored in firebase, my calendar is built with example in the link
QtQuick Calendar
My user dates are stored to firebase, and when i read them i can receive them in the log using JSON.stringify,
When writing the saved dates to my database I use the following code:
property var userData: {
"selectedDates": [
{ },
]
}
AppButton {
id: saveButton
text: "Save & Request"
anchors.right: parent.right
textColor: "#4e4e4e"
backgroundColor: "#d1d1d1"
onClicked: {
userData.selectedDates.push({ "date": calendar.selectedDate});
console.log(JSON.stringify(userData));
firebaseDb.setUserValue("dates", userData.selectedDates)
}
}
Then when reading the dates I use the following:
FirebaseDatabase {
config: customConfig
onFirebaseReady: {
firebaseDb.getUserValue("dates", {
startAt: {
Key: "1/date",
}
}, function(success, key, value) {
if(success) {
console.log(JSON.stringify(value))
}
})
}
}
Once this is read the log displays the dates as follows:
[{"date":"2018-10-01T21:17:00.926"},{"date":"2018-10-02T12:00:00.000"},{"date":"2018-10-03T12:00:00.000"},{"date":"2018-10-06T12:00:00.000"},{"date":"2018-10-07T12:00:00.000"},{"date":"2018-10-08T12:00:00.000"}]
I want to take this string of dates and add markers to my calendar to show they are booked?
How would I go about this?
The Type FirebaseDatabase is documented here
CALENDAR QML CODE
CalendarQMLCode
UPDATE (after #derM comment)
No need to use sqlite DB model
Just in the dayDelegate we play with the event indicator image visibility
For that after getting your data from firebase, save it to an array (we name it arrayFromFireBase) and check if styleData.date is in that array
FirebaseDatabase {
config: customConfig
onFirebaseReady: {
firebaseDb.getUserValue("dates", {
startAt: {
Key: "1/date",
}
}, function(success, key, value) {
if(success) {
console.log(JSON.stringify(value))
// value here is JSON ARRAY
// value =[{"date":"2018-10-01T21:17:00.926"},{"date":"2018-10-02T12:00:00.000"}]
// so value[0].date ="2018-10-01T21:17:00.926"
//then you gonna insert here your dates in arrayFromFireBase as strings as follow
for(i=0 ; i < arrayFromFireBase.length-1 ; i++)
arrayFromFireBase.push(new Date(value[i].date).getTime())
}
})
}
}
Calendar {
...
style: CalendarStyle {
dayDelegate: Item {
...
Image {
// HERE WE MUST COMPARE TIME (we used Date.getTime()) FROM FIREBASE with the calendar's date converted to time also
visible: arrayFromFireBase.indexOf(styleData.date.getTime()) > -1
...
source: "qrc:/images/eventindicator.png"
}
}
}
}
OLD ANSWER
Part of the code was taken from Qt Quick Controls - Calendar Example
Just added void SqlEventModel::insertDates(QStringList dates)
C++ part
SqlEventModel::SqlEventModel()
{
createConnection();
}
void SqlEventModel::createConnection()
{
QSqlDatabase db = QSqlDatabase::addDatabase("QSQLITE");
db.setDatabaseName(":memory:");
if (!db.open()) {
qFatal("Cannot open database");
return;
}
}
void SqlEventModel::insertDates(QStringList dates){
QSqlQuery query;
// We store the time as seconds because it's easier to query.
// It depends on what you want as columns for your events
query.exec("create table Event (name TEXT, startDate DATE, startTime INT,
endDate DATE, endTime INT)");
foreach(QString date in dates){
// It depends on what you want as columns for your events
query.exec(QString("insert into Event values('%1', '%2',
%3, '%4', %5)").arg(date[0]).arg(date[1]).arg(date[2]).arg(date[3]).arg(date[4]));
}
return;
}
QML part
SqlEventModel {
id: eventModel
}
FirebaseDatabase {
config: customConfig
onFirebaseReady: {
firebaseDb.getUserValue("dates", {
startAt: {
Key: "1/date",
}
}, function(success, key, value) {
if(success) {
console.log(JSON.stringify(value))
//Insert here your dates in sql model
eventModel.insertDates(value)
}
})
}
}
So, after a development holiday, I have a working solution! So see below (excluding all excess code) and thanks to everyone for your help!! (especially #Redanium for your patience)
Main.qml:
App {
property var arrayFromFireBase: []
onLoggedIn: {
firebaseDb.getUserValue("dates/selectedDates", {},
function(success, key, value) {
if(success) {
for(var idx in value)
arrayFromFireBase.push(new Date(value[idx].date).getTime())
})}}
Then on my calendar page the following:
CalendarPage.qml:
Page {
AppButton {
id: saveButton
text: "Save & Request"
onClicked: {
userData.selectedDates.push({"date": calendar.selectedDate});
console.log(JSON.stringify(userData));
firebaseDb.setUserValue("dates", userData)
}
}
property var userData: {
"selectedDates": [{}]
}
Image {
visible: arrayFromFireBase.indexOf(styleData.date.getTime()) > -1
}
}
I'm trying to read all children of part of my database from one command, so I can update Firebase and it will automatically display in my app as the various titles.
the part of my database that I want to read is as follows:
public
bigqueryobject
title1
title2
title3
title4
I am working in Qt and have tried different combinations using orderByKey, orderByChild and orderByValue with the following code:
firebaseDb.getValue("public/bigqueryobject",{
orderByKeys: true
}, function(success, key, value) {
if(success) {
console.debug("Read user value for key", key, "fromFBDB: ", value);
myArray.push(value); combobox.model = myArray
}
})
when doing the above my log states:
"Read user value for key bigqueryobject fromFBDB: [object Object]
Read Value [object Object] for keybigqueryobject"
yet no responses are displayed, what could be the issue here?!?
So after previously trying to push the read value to an array to add to my combobox I was only getting one dropdown option with all read values in one row; simply removing the array worked perfectly, code below!
onFirebaseReady: {
firebaseDb.getValue("locationsDepartments/locations", {
orderByValue: true
}, function(success, key, value) {
if(success) {
combobox.model = value
}
})
}
Quick2.ComboBox {
id: combobox
model: []
delegate: Quick2.ItemDelegate {
width: combobox.width
height: combobox.height
contentItem: AppText {
text: modelData
}
highlighted: combobox.highlightedIndex == index
}
contentItem: AppText {
width: combobox.width - combobox.indicator.width - combobox2.spacing
text: combobox.displayText
wrapMode: Text.NoWrap
}
}
I have a modal window in Angular 4 that works fine but if the user clicks on the background / parent page the modal is closed.
I have found some solutions that suggest using backdrop='static' and keyboard=false when opening the modal but our modal uses a local Dialog class with a BehaviorSubject object so is opened using the .next method. I've also tried setting these attributes using div config but to no avail.
Therefore I'm looking for another solution, maybe using CSS or another setting / attribute that can be directly applied to the parent page or modal HTML.
See below for some of the relevant code.
dialog.component.ts:
constructor(private location: PlatformLocation,
private _dialog: DialogService,
private router: Router) { }
open() {
this.showDialog = true;
const body = document.body;
body.classList.add('cell-modal-open');
}
close() {
this.dialog = undefined;
}
private handleDialog(d: Dialog) {
if (!d) {
this.close();
} else if (d.template) {
if (this.showDialog) {
this.close();
}
this.dialog = d;
this.open();
}
}
ngOnInit() {
this.subscription = this
._dialog
.getDialog()
.subscribe({
next: (d) => { this.handleDialog(d); console.log('subscribed dialog') },
error: (err) => this.handleDialogError(err)
});
this.initialiseRoutingEventListeners();
}
dialog.service.ts
private d: Dialog = { template: null, size: DialogSizeEnum.XLarge };
private dialogSubject = new BehaviorSubject<Dialog>({ template: null, size: DialogSizeEnum.XLarge });
constructor() { }
showDialog(template: TemplateRef<any>, size = DialogSizeEnum.XLarge, requiresAction = false) {
Object.assign(this.d, { template: template, size: size, requiresAction: requiresAction });
if (this.d !== null) {
this.dialogSubject.next(this.d);
}
}
getDialog(): BehaviorSubject<Dialog> {
return this.dialogSubject;
}
clear() {
this.dialogSubject.next(null);
}
Any suggested approaches are welcome!
Added flag to the close() method and adding condition to only set to undefined if true (i.e. from a valid location).