Assigning QObject pointer works via assignment but not binding - qml

I have something similar to the following code snippets. I am simplifying the code here for attempted brevity.
First, a subclass of QAbstractListModel with the following data() implementation, and Q_INVOKABLE get_thing() method, which returns a pointer to a QObject subclass, QML_thing:
QVariant data(QModelIndex& index, int role) {
const auto& thing = m_data.at(index.row()); // shared pointer to QML_thing
switch(role)
{
case Qt::DisplayRole:
return thing->name(); // this works
case WholeThingRole:
return QVariant::fromValue(QML_thing*>(thing.get());
}
}
QML_thing* getThing(int index) const
{
const thing = m_data.at(index); // shared pointer
return thing.get();
}
Next, I have a Repeater in a QML file that has this code:
Repeater {
id: repeater
model: thingModel
ThingDelegate {
thing: wholeThing // This calls the role, but ends up being null
}
onItemAdded {
item.thing = model.getThing(index) // this works, but 'breaks' the binding
}
}
My question is: why doesn't the thing: binding in QML work, but the thing = version does?

Related

How to call a native method with a std::vector as argument and retrieve the native object in wrapped class

I have a native C++ method, from external unchangable DLL, that expects a std::vector as a argument with 2 native objects. (for example sake, it's an image library returning width/height of a 2 images)
I would like the native objects in the std::vector to be referenced in my own wrapped MyManagedImageObj.
Somehow the std::vector seems to copy values and has no way to add pointers (correct?); so after calling the NativeMethod; I need to copy the properties (width/height) back again to MyManagedImageObj.
I thought about first declaring the std::vec and getting the pointer of the results and put that in MyManagedImageObj as a pointer. But if I understand it correctly std::vector will clean that native memory up once out of scope. (my c++ experience is 1 week; c# long time)
Is there a better way to do this without reassigning the properties one by one?
Ej
The code looks like this:
//create managed object that wraps also native pointer.
MyManagedImageObj^ obj1 = gcnew MyManagedImageObj();
MyManagedImageObj^ obj2 = gcnew MyManagedImageObj();
//keep list of result
List<MyManagedImageObj^>^ resultList;
resultList->Add(obj1);
resultList->Add(obj2);
//call to native method. Dereference pointers of native wrapped objects... not working?
std::vector<DLLNativeImageObj> nativeImageVec { *obj1->GetInstance(), *obj2->GetInstance() };
bool result = otherNativePtr->NativeMethod(nativeImageVec);
//we still need copy it over results now to 'our' managed objects.
int i = 0;
for (DLLNativeImageObj c : nativeImageVec)
{
resultList[i]->ImageHeight = c.imageHeight;
resultList[i]->ImageWidth = c.imageWidth;
++i;
}
The MyManagedImageObj class looks like this:
//MyManagedImageObj.h
public ref class MyManagedImageObj
{
protected:
DLLNativeImageObj* m_Instance;
public:
MyManagedImageObj(DLLNativeImageObj* instance)
: m_Instance(instance)
{
};
~MyManagedImageObj() //destructor will be called whenever we do 'delete'
{
if (m_Instance != nullptr)
{
delete m_Instance;
}
}
!MyManagedImageObj() //finalizer, called by the garbage collector when it's destroys the wrapper object. So safety check to dispose unmanaged item.
{
if (m_Instance != nullptr)
{
delete m_Instance;
}
}
DLLNativeImageObj* GetInstance() //return the pointer to the unmanaged object
{
return m_Instance;
}
property uint32_t ImageWidth
{
public:
uint32_t get()
{
return m_Instance->imageWidth;
}
public:
void set(uint32_t value)
{
m_Instance->imageWidth = value;
}
}
property uint32_t ImageHeight
{
public:
uint32_t get()
{
return m_Instance->imageHeight;
}
public:
void set(uint32_t value)
{
m_Instance->imageHeight = value;
}
}
};
//MyManagedImageObj.cpp
MyManagedImageObj::MyManagedImageObj(new DLLNativeImageObj())
{
// new keyword important: it returns a pointer to the location and does not get deleted when out of scope. Manual delete is required.
}

mobx challenge: getters and setters into an observable array

I'm trying to write getters and setters into an observable array and it isn't working. The code below gives me the following error: Error: [MobX] No annotations were passed to makeObservable, but no decorator members have been found either
I've tried different combinations of decorators, but nothing seems to work. The behavior I want is whenever AppModel.text is updated, any UI rending the getter for text should update. Also whenever gonext() is called on the object, then any UI rending from AppModel.text should update and render data from the new 0 item on the array.
class DataThing
{
#observable text?: string = "foo";
}
class AppModel
{
get text() { return this.items[0].text}
set text(value: string | undefined) { this.items[0].text = value;}
items: DataThing[] = observable( new Array<DataThing>());
constructor() {
makeObservable(this);
this.items.push(new DataThing());
}
gonext() { this.items.unshift(new DataThing()); }
}
EDIT:
I ended up doing the following, but would still like to understand how to index into an array in an observable way.
class DataThing
{
#observable text?: string = "zorp";
constructor(){makeObservable(this);}
}
class AppModel
{
#observable _current?:DataThing;
get current() {return this._current;}
items: DataThing[] = observable( new Array<DataThing>());
constructor() {
makeObservable(this);
this.gonext();
}
gonext() {
this.items.unshift(new DataThing());
this._current = this.items[0];
}
}

Changed event for JavaScript Binding in Qml?

I have a property which is defined by a JavaScript expression (currentContainer):
Item {
id: theContainer
property alias currentIndex: theListView.currentIndex
signal onCurrentIndexChanged()
property MyCustomCppContainer currentContainer: {
if(theListView.currentIndex >= 0)
theModel.getCustomContainer(theListView.currentIndex)
else
null
}
signal onCurrentContainerChanged() // nobody calls this signal (yet)
MyCustomCppModel {
id: theModel
}
ListView {
id: theListView
anchors.fill: parent
model: theModel
currentIndex: -1
onCurrentIndexChanged: theContainer.onCurrentIndexChanged()
/* Other properties stripped for example */
}
}
Sadly I always get the last selected container, not the currently selected one:
ContainerItem {
onCurrentIndexChanged: {
//On first change, currentContainer is null
//though the first one was selected
//After selecting the second entry
//I get the result I expected last time
console.log(currentContainer.name);
}
}
I think a solution would be to have another signal for currentContainer: onCurrentContainerChanged().
But who calls this special signal?
I could solve this using a C++ helper class:
class PropertyChangedHelper : public QObject
{
Q_OBJECT
Q_PROPERTY(QVariant theProperty WRITE setTheProperty NOTIFY thePropertyChanged)
public:
PropertyChangedHelper(QObject* parent = nullptr) : QObject(parent) {}
virtual ~PropertyChangedHelper() {}
void setTheProperty(QVariant) {
Q_EMIT thePropertyChanged();
}
Q_SIGNALS:
void thePropertyChanged();
private:
Q_DISABLE_COPY(PropertyChangedHelper)
};
Usage is quite simple:
PropertyChangedHelper {
theProperty: containerItem.currentContainer
onThePropertyChanged: {
console.log(containerItem.currentContainer.name);
}
}
I am not sure if this violates any Qml/QtQuick philosophies, but it works.

can not read get property of undefined angular 2 error

hi I am trying to get city name from google api but getting that error below is my code
appcomponent class
import {Component, OnInit} from 'angular2/core';
import {marketComponent} from './market.component';
import {RouteConfig, ROUTER_DIRECTIVES} from 'angular2/router';
import {introComponent} from './intro.component';
import {geoService} from './service.geo';
import {JSONP_PROVIDERS} from 'angular2/http';
declare var google: any;
#Component({
selector: 'my-app',
templateUrl: 'app/app.component.html',
directives: [ROUTER_DIRECTIVES],
providers: [JSONP_PROVIDERS, geoService]
})
#RouteConfig([
{ path: '/intro', name: 'Intro', component: introComponent, useAsDefault: true },
{ path: '/market', name: 'Market', component: marketComponent },
])
export class AppComponent {
constructor(private _http: geoService) { }
public maps;
public cat_error: Boolean = false;
public xml_Latitude :string;
public xml_Lang: string;
ngOnInit() {
if (navigator.geolocation) {
navigator.geolocation.getCurrentPosition(this.showPosition);
} else {
alert("Geolocation is not supported by this browser.");
}
var input: any = document.getElementById('google_places_ac');
var autocomplete = new google.maps.places.Autocomplete(input, {});
google.maps.event.addListener(autocomplete, 'place_changed', function () {
var place = autocomplete.getPlace();
console.log(place)
});
}
showPosition(position) {
this.xml_Latitude = position.coords.latitude;
this.xml_Lang = position.coords.longitude;
this._http.getPlaces(this.xml_Latitude, this.xml_Lang).subscribe(
data => { this.maps = data },
err => { this.cat_error = true }
);
var result = this.maps.results;
var city = result[0].address_components[4].long_name + "," + result[0].address_components[6].long_name;
alert(city);
}
}
and geoservice file
import {Injectable} from 'angular2/core';
import { Response, Jsonp} from 'angular2/http';
import 'rxjs/add/operator/map';
#Injectable()
export class geoService {
constructor(private http: Jsonp) { }
public xml_Latitude: string;
public xml_Lang: string;
public getPlaces(xml_Latitude, xml_Lang) {
return this.http.get(`http://maps.googleapis.com/maps/api/geocode/json?latlng=
'${this.xml_Latitude}','${this.xml_Lang}'&sensor=true`)
.map((res: Response) => res.json())
.catch(this.handleError);
}
private handleError(error: Response) {
console.error(error);
return error.json().error || 'Server error';
}
}
error also says getplaces is not a function, I think I am missing something but don't know what....
In addition to the callback ordering problem identified by Thierry, you have a lost this context on this line:
navigator.geolocation.getCurrentPosition(this.showPosition);
The Problem
You have the classic JavaScript problem known as the incorrect this context.
The this keyword in JavaScript behaves differently than in does in other languages like C# and Java.
How this works
The this keyword, in a function, is determined as follows:
* If the function was created through a call to .bind, the this value is the argument provided to bind
* If the function was invoked through a method call, e.g. expr.func(args), then this is expr
* Otherwise
* If the code is in strict mode, this is undefined
* Otherwise, this is window (in a browser)
Let's look at how this works in practice:
class Foo {
value = 10;
doSomething() {
// Prints 'undefined', not '10'
console.log(this.value);
}
}
let f = new Foo();
window.setTimeout(f.doSomething, 100);
This code will print undefined (or, in strict mode, throw an exception).
This is because we ended up in the last branch of the decision tree above.
The doSomething function was invoked, the function wasn't a result of a bind call, and it wasn't invoked in a method syntax position.
We can't see the code for setTimeout to see what its invocation looks like, but we don't need to.
Something to realize is that all doSomething methods point to the same function object.
In other words:
let f1 = new Foo();
let f2 = new Foo();
// 'true'
console.log(f1.doSomething === f2.doSomething);
We know that setTimeout can only see the function we passed it, so when it invokes that function,
there's no way for it to know which this to provide.
The this context has been lost due to our referencing the method without invoking it.
The Red Flag
Once you know about this problems, they're easy to spot:
class Foo {
value = 10;
method1() {
doSomething(this.method2); // DANGER, method reference without invocation
}
method2() {
console.log(this.value);
}
}
The Solution
You have a few options here, each with its own trade-offs.
The best option depends on how often the method in question is invoked from differing call sites.
Arrow Function in Class Definition
Instead of using the normal method syntax, use an arrow function to initialize a per-instance member.
class DemonstrateScopingProblems {
private status = "blah";
public run = () => {
// OK
console.log(this.status);
}
}
let d = new DemonstrateScopingProblems();
window.setTimeout(d.run); // OK
Good/bad: This creates an additional closure per method per instance of your class. If this method is usually only used in regular method calls, this is overkill. However, if it's used a lot in callback positions, it's more efficient for the class instance to capture the this context instead of each call site creating a new closure upon invoke.
Good: Impossible for external callers to forget to handle this context
Good: Typesafe in TypeScript
Good: No extra work if the function has parameters
Bad: Derived classes can't call base class methods written this way using super.
Bad: The exact semantics of which methods are "pre-bound" and which aren't create an additional non-typesafe contract between your class and its consumers.
Function Expression at Reference Site
Shown here with some dummy parameters for explanatory reasons:
class DemonstrateScopingProblems {
private status = "blah";
public something() {
console.log(this.status);
}
public run(x: any, y: any) {
// OK
console.log(this.status + ': ' + x + ',' + y);
}
}
let d = new DemonstrateScopingProblems();
// With parameters
someCallback((n, m) => d.run(n, m));
// Without parameters
window.setTimeout(() => d.something(), 100);
Good/bad: Opposite memory/performance trade-off compared to the first method
Good: In TypeScript, this has 100% type safety
Good: Works in ECMAScript 3
Good: You only have to type the instance name once
Bad: You'll have to type the parameters twice
Bad: Doesn't easily work with variadic parameters
I think that you should move the result block into the subscribe callback associated the getPlaces method call:
showPosition(position) {
this.xml_Latitude = position.coords.latitude;
this.xml_Lang = position.coords.longitude;
this._http.getPlaces(this.xml_Latitude, this.xml_Lang).subscribe(
data => {
this.maps = data;
var result = this.maps.results; // <----------
var city = result[0].address_components[4].long_name + "," + result[0].address_components[6].long_name;
alert(city);
},
err => { this.cat_error = true }
);
}
It's because this.maps is undefined before the callback is called. And you try tyo get the result attribute before (this.maps.results).
Edit
I also see a problem at the line navigator.geolocation.getCurrentPosition. You could refactor your code this way:
if (navigator.geolocation) {
navigator.geolocation.getCurrentPosition((position) => { // <----
this.showPosition(position);
});
} else {
alert("Geolocation is not supported by this browser.");
}

How to define a custom object as a role in QAbstractListModel?

My question is, that how can I specify a custom object as a role in a model derived from QAbstractListModel so when visualizing it within a ListView I can access its member variables. To have an example here is some simple code example:
This is my class representing my custom object:
class MyCustomObject {
public:
MyCustomObject(Qstring name, Qstring type);
QString getName();
QString getType();
private:
QString name;
QString type;
};
This is how the overridden data() function looks like now (but it is not working) of my MyModel derived from QAbsractListModel:
QVariant MyModel::data(const QModelIndex &index, int role) const {
if (index.row() < 0 || index.row() > m_atoms.count()) {
//if (!index.isValid()) {
return QVariant();
}
const MyData &data = m_data[index.row()];
if(role == SomeRole) {
return data.someString()
}
else if (role == MyCustomRole) {
return data.myCustomObject; // How can I do this?
}
return QVariant();
}
Here I specify the role names in MyModel:
QHash<int, QByteArray> AtomModel::roleNames() const {
QHash<int, QByteArray> roles;
roles[SomeRole] = "someRole";
roles[MyCustomRole] = "myCustomRole";
return roles;
}
and this is how my ListView looks like in QML code with an example how I would like to access MyCustomObject member variables in the delegate:
ListView {
width: 400
height: 400
model: myModel
delegate: Text {
text: "Type: " + myCustomRole.getType() + ", Name: " + myCustomRole.getName() + ", some string: " someRole
}
}
EDIT1: => fix needed copy constructor
When I am adding Q_DECLARE_METATYPE under my MyCustomObject I receive the following error:
call to implicitly-deleted copy constructor of `MyCustomObject`
in instantiation of member function 'QtMetaTypePrivate::QMetaTypeFunctionHelper<MyCustomObject, true>::Construct' requested here
in instantiation of function template specialization 'qRegisterNormalizedMetaType<MyCustomObject>' requested here QtMetaTypePrivate::QMetaTypeFunctionHelper<T>::Construct,
return qRegisterNormalizedMetaType<T>(normalizedTypeName, dummy, defined);
in instantiation of function template specialization 'qRegisterMetaType<MyCustomObject>' requested here
Q_DECLARE_METATYPE(MyCustomObject)
expanded from macro 'Q_DECLARE_METATYPE'
#define Q_DECLARE_METATYPE(TYPE) Q_DECLARE_METATYPE_IMPL(TYPE)
expanded from macro 'Q_DECLARE_METATYPE_IMPL'
const int newId = qRegisterMetaType< TYPE >(#TYPE,
copy constructor of 'MyCustomObject' is implicitly deleted because base class 'QObject' has a deleted copy constructor
class MyCustomObject : public QObject
'QObject' has been explicitly marked deleted here Q_DISABLE_COPY(QObject)
expanded from macro 'Q_DISABLE_COPY'
Class(const Class &) Q_DECL_EQ_DELETE;\
EDIT2:
So I have added all the necessary functions what #Evgeny has suggested. My code now compiles without errors, but I get a qml error on run time saying:
TypeError: Property 'getType' of object QVariant(MyCustomObject) is not a function
I have added Q_INVOKABLE in front of the getType() method and I also deriving MyCustomObject class from public QObject. I have added Q_DECLARE_METATYPE at the bottom of my MyCustomObject header file. In the constructor of MyCustomObject I call qRegisterMetaType<MyCustomObject>("MyCustomObject") and in my main I register the class also like this qmlRegisterType<MyCustomObject>("com.test.mycustomobject", 1, 0, "MyCustomObject")
This is how MyCustomObject class looks like now:
class MyCustomObject : public QObject {
public:
MyCustomObject();
MyCustomObject(Qstring name, Qstring type);
MyCustomObject(const MyCustomObject& obj);
~MyCustomObject();
Q_INVOKABLE QString getName();
Q_INVOKABLE QString getType();
private:
QString name;
QString type;
};
Q_DECLARE_METATYPE(MyCustomObject)
This is how the overridden data() function looks like now of my MyModel derived from QAbsractListModel:
QVariant MyModel::data(const QModelIndex &index, int role) const {
if (index.row() < 0 || index.row() > m_atoms.count()) {
//if (!index.isValid()) {
return QVariant();
}
const MyData &data = m_data[index.row()];
if(role == SomeRole) {
return data.someString()
}
else if (role == MyCustomRole) {
QVariant var; // this is the part, which has changed
var.setValue(data.myCustomObject);
return var;
}
return QVariant();
}
All other functions which I have posted originally are the same.
First of all you need to declare your custom object for Qt metatype system. You should use Q_DECLARE_METATYPE macro for this. Also you may need to use qRegisterMetaType function. Then you should register your object to use it with QML. You should use qmlRegisterType function for that.
Also make sure you use Q_INVOKABLE for your objects methods.