Does QtObject have a destroy() method? - qt

This code runs without error:
import QtQuick 2.6
import QtQuick.Window 2.2
Window {
visible: true
QtObject {
id: foo
}
Component.onCompleted: {
foo.destroy()
}
}
But the docs for QtObject don't say it has a destroy() method. So does it?

The destroy() method is for deleting dynamically created objects in QML. The QtObject in your example is statically created, and the above documentation says that you cannot call destroy() on such objects:
This would result in an error, since objects can only be dynamically destroyed if they were dynamically created.
I am not sure why you do not get an error in your example, but this is definitely not something you should do.

Related

Qt quick how to connect signal and slots from different file

I'm new to QML.
I want to connect a signal from a .qml file to a slot in another .qml.
The problem is that I haven't instantiate the object in that file because I want to use a StackView in the main.qml to see on the screen the objects in that .qml files.
in main.qml I have:
StackView {
id: mStackId
anchors {
top: topSectionId.bottom
bottom: parent.bottom
left: parent.left
right: parent.right
}
initialItem: homePage
}
Component {
id: homePage
Home {id: homePageObj}
}
Component {
id: housePage
House {id: housePageObj}
}
Component {
id: createRoomPage
CreateRoom {id: createRoomPageObj}
}
where Home, House and CreateRoom are the .qml files I want to make to connect.
I've tried:
Component.onCompleted:
createRoomPage.newRoom.connect(homePage.createNewRoom)
where newRoom is the signal and createNewRoom is the slot, but Qt Creator gives me the error:
TypeError: Cannot call method 'connect' of undefined
I've also tried to use createRoomPageObj instead of createRoomPage and homePageObj instead of homePage but I get the error:
ReferenceError: createRoomPageObj is not defined
I've read I could use Connections, but documentation is not too clear to me.
This is not a matter of wrong Connections usage, the createRoomPage simply is not instantiated. You need a backend which can do the logic and to which you can connect from the views.
In the simplest form, you can make a QtObject
QtObject {
id: backend
signal newRoom(name)
}
StackView {
...
}
You can use backend.newRoom("Kitchen") from the CreateRoomPage.
EDIT after question about persistence
You can also create the QObject as a singleton:
pragma Singleton
import QtQuick 2.4
QtObject {
signal newRoom(name)
}
In this case the filename will determine the name to use in the rest of the program. So if naming above piece of code Backend.qml, you can use as follows:
Connections {
target: Backend
function onNewRoom(name) { .... }
}

How to find out the QQmlEngine instance of a singleton object?

I have a class SingletonBaseClass that is defined in C++ and later extended in QML. In the qmldir file, it is marked as a Singleton:
// qmldir file
singleton SingletonClass 1.0 SingletonClass.qml
// SingletonClass.qml
import QtQml 2.0
pragma Singleton
SingletonBaseClass {
// ...
}
This is the base class:
class SingletonBaseClass : public QObject
{
Q_OBJECT
public:
SingletonBaseClass(QObject* parent = nullptr) {}
// Get the owner engine
Q_INVOKABLE void someMethodCalledFromQml()
{
QQmlEngine* ownerEngine = ?
}
// Get the single instance of the given engine
static SingletonBaseClass* instance(QQmlEngine* engine)
{
SingletonBaseClass* instance = ?
return instance;
}
};
How can I retrieve the QQmlEngine instance in SingletonBaseClass?
How can I get a pointer to my singleton instance from within a static function?
Note: I have multiple QML engine instances in my application.
[Update]: I wasn't satisfied with the suggested workarounds and so I finally contributed this patch for 1. and QQmlEngine::singletonInstance() for 2. Both changes will be available in Qt 5.12.
If you want to get the QQmlEngine you can use the contextForObject() method as I show below:
Q_INVOKABLE void someMethodCalledFromQml()
{
QQmlEngine *ownerEngine = QQmlEngine::contextForObject(this)->engine();
qDebug()<<ownerEngine;
}
singletons in qml do not have a parent so a way to access them is by creating a property, for that we create a .qml and create an object with that property as the Singleton:
qrc:/tmp.qml
import QtQuick 2.0
QtObject {
property var obj: SingletonClass
}
and then this element is accessed by the following code:
static SingletonBaseClass* instance(QQmlEngine* engine)
{
QQmlComponent component(engine, QUrl("qrc:/tmp.qml"));
QObject *item = component.create();
SingletonBaseClass *instance = qvariant_cast<SingletonBaseClass *>(item->property("obj"));
return instance;
}
In the following link we show an example.

calling javascript in another file in qml

I am trying to override the onClosing event in a QML application window.
The qml for the window is simple as:
ApplicationWindow {
id: rootWindow
objectName: "window"
visible: true
width: 800
height: 480
property Component loginForm: LoginView {}
onClosing: {
loginForm.logout()
}
}
The LoginView view is simple as well:
Rectangle {
id: view
function logout() {
console.log("Logout called");
}
}
Now, as is, it returns an error:
TypeError: Property 'logout' of object QQmlComponent(0x9287150) is not a function
I also tried loginForm.view.logout() and this returns in:
TypeError: Cannot call method 'logout' of undefined
I believe QML is having trouble because your property is of type Component. You are assigning a LoginView, which is an inheritance descendent of Component, to a property of type Component. If you change your property to be of type LoginView, it will work:
property LoginView loginForm : LoginView{}
If this isn't actually a property that you want to be exported by the main module, you can simply instantiate it without creating a property, but still giving it a module-scope identifier:
LoginView{ id: loginForm }
Doing either of these will give you access to that function.

How to define global variables in Angular 2 in a way that I can use them for property binding in templates?

In my Angular 2 (beta 14) application I need to keep track of user login status in order to hide/display certain elements.
The issue I'm getting is that property binding is not working the way I did as follows.
I created a class to store and update global variables:
app-global.ts
import {Injectable} from "angular2/core";
#Injectable() export class AppGlobals {
// use this property for property binding
public isUserLoggedIn: boolean = false;
setLoginStatus(isLoggedIn){
this.isUserLoggedIn = isLoggedIn;
}
getLoginStatus(){
return this.isUserLoggedIn;
} }
In the login component I import AppGlobals
export class LoginComponent {
constructor(private _appGlobals: AppGlobals) { }
and set login state by
this._appGlobals.setLoginStatus(true);
In another component I inject AppGlobals as I do in LoginComponent
I define a class (component)'s property
isLoggedIn: boolean = this._appGlobals.isUserLoggedIn; // I also tried by using the getter instead of the public property (see above)
which I then use in the component's template to show/hide a certain element:
<!-- here I also tried with {{!isLoggedIn}} but results in a syntax error whereas using [(hidden)] instead of [hidden] changes nothing -->
<div id="some-element" [hidden] = "!isLoggedIn">
Finally, the binding works but there is no update (this component is part of AppComponent template and shown in every page) when another component (e.g. LoginComponent) sets the login status.
EDIT I tried to apply Gunter's answer but I get the following errors:
app/app-globals.ts(10,54): error TS2346: Supplied parameters do not match any signature of call target.
app/app-globals.ts(13,29): error TS2339: Property 'emit' does not exist on type 'BehaviorSubject<boolean>'.
Error at line 10 comes from [SOLVED]
public isUserLoggedIn:BehaviorSubject = new BehaviorSubject().startWith(false);
and it's apparently caused by BehaviorSubject expecting 1 parameter
Error at line 13 comes from
this.isUserLoggedIn.emit(isLoggedIn);
and it's apparently cause by a non-existing emit method.
Also, I don't understand how shall I use AppGlobals so that the property binding auto updates in another component (see last example before EDIT)
Further, in LoginComponent I replaced isLoggedIn boolean type with BehaviorSubject because isUserLoggedIn has type BehaviorSubject in AppGlobals
but
this._appGlobals.isUserLoggedIn.subscribe(value => this.isLoggedIn = value);
returns a TypeError:
Assigned expression type boolean is not assignable to type BehaviorSubject
isLoggedIn: boolean = this._appGlobals.isUserLoggedIn;
is a one-time action that copies the value at the time when this line is executed. If you want subsequent changes to be propagated use observables
import {Observable} from 'rxjs/Observable';
import 'rxjs/add/operator/share';
import 'rxjs/add/operator/startWith';
import {BehaviorSubject} from 'rxjs/BehaviorSubject';
#Injectable()
export class AppGlobals {
// use this property for property binding
public isUserLoggedIn:BehaviorSubject<boolean> = new BehaviorSubject<boolean>(false);
setLoginStatus(isLoggedIn){
this.isUserLoggedIn.next(isLoggedIn);
}
}
and use it like:
export class LoginComponent {
constructor(private _appGlobals: AppGlobals) {
this._appGlobals.isUserLoggedIn.subscribe(value => this.isLoggedIn = value);
}
See also https://stackoverflow.com/a/35568924/217408
had a similar need, ended up implementing this via simple getter - and it binds the property in the template (--> changes propagate). less code.
you can set the global var either directly or implement setters/getters in the Globals class.
globals.ts
export class Globals{
public static server_call_in_progress:boolean = true;
}
app.component.ts
import {Globals} from "./shared/globals";
export class AppComponent{
constructor(){}
ngOnInit(){}
get server_call_in_progress(){
return Globals.server_call_in_progress;
}
}
app.component.html
<div *ngIf="server_call_in_progress">
<div class="loader"></div>
</div>

How to connect a JavaScript function to a property signal?

I'm new to Qml and having some trouble connecting a javascript handler to a property's signal. I have a C++ object with a property and signal.
class CppObject : public QObject
{
Q_OBJECT
Q_PROPERTY(QVariant value READ getValue WRITE setValue NOTIFY valueChanged)
signals:
void valueChanged(const QVariant &);
};
The objects are created through a C++ factory method and I'm able to bind the values
and changes to Qml properties. This all works.
property CppObject obj: cppProxy.PropertyFactory("foo");
Text
{
x: 100;
y: 100;
text: parent.obj.value;
}
For some properties, I'd like to connect the valueChanged signal to a javascript function.
I've been up and down through the Qml documentation and have tried a bunch of stuff without
any luck. I figured something like this should work, but doesn't
function objEventHandler()
{
console.log('objEventHandler() ran')
}
Component.onCompleted:
{
obj.value.valueChanged.connect(objEventHandler);
}
What is the best way to do this?
You can also connect as you've tried in your example, but the form is:
Component.onCompleted:
{
obj.valueChanged.connect(objEventHandler);
}
The signal is not a property of the 'value' object, but of 'obj'.
It's simple using the connections object.
Connections
{
target: obj;
onValueChanged: console.log('changed');
}

Resources