I think that this is not a question regarding especially Qt but a quaestion of a lack of programming expeience.
I derived a class from QTreeWidgetItem and I added some bolean flags. When i initialize a QTreeWidget I add two of them by
_NewItem1=MyQTreeWidgetItem(_treewidget);
than later I add some items by
_NewItem1_1=MyQTreeWidgetItem(_NewItem1);
_NewItem1_1->boleanvalue1=true;
If I later want to return these Items I call
(MyQTreeWidgetItem)_NewItem1->child(i)
but this of course just returns me a MyQTreeWidgetItem with newly initialized bolean flags.
Do I have to override the child function to retun the true Items which I initialized earlier?
_NewItem1->child(i) returns pointer to QTreeWidgetItem, which is a base class for MyQTreeWidgetItem. You have cast is safely to MyQTreeWidgetItem, taking into account it may be also real QTreeWidgetItem. This is achieved with dynamic_cast in C++, which checks type at runtime.
QTreeWidgetItem *item = _NewItem1->child(i);
MyQTreeWidgetItem *myItem = dynamic_cast<MyQTreeWidgetItem>(item);
if (myItem) {
qDebug() << myItem->boleanvalue1;
} else {
qDebug() << item << "is not of type MyQTreeWidgetItem";
}
On the other hand, type-casting operator () allows to convert any type into any other type without any checking, if this kind of conversion is possible. Like, for example, you've converted pointer to QTreeWidget into object of type MyQTreeWidgetItem. The subsequent access to the variable will produce either a run-time error or a unexpected result.
Related
The values are like is not null, canconvert is true and is valid. But still segmentation fault occurs when trying to cast. I am debugging and the value set might have been deleted just before setValue is called with a new value.
QVariant m_model = 0;
is a member var
void Handler::setValue(const QVariant &var)
{
bool isNull = m_model.isNull();
bool canConvert = m_model.canConvert<QObject*>();
bool isValid = m_model.isValid();
if(!m_model.isNull() && m_model.canConvert<QObject*>()) {
bool sConverts = (nullptr != m_model.value<QObject*>());
}
m_model = var;
}
Can someone tell why it crashes when still m_model has some data in it.
First some problems with your code:
QVariant::canConvert() returns whether the type of the value is convertible at all, not if the actually contained value can be converted. Example: you create a QVariant with a QString as value. Calling myValue.canConvert<double>() will return true, even if your string was "nonsense". That's because QVariant is generally able to convert strings like "1.0" to a double value of 1.0.
QVariant::isValid() returns whether the contained type is not QMetaType::UnknownType. This mostly only happens when you create a QVariant without assigning a value.
QVariant m_model = 0; will create a QVariant with type int. Don't do that. Just declare it without the = 0 part. QVariant (like other value types in Qt, i.e. QString, QSize etc.) doesn't need to be initialized to be null, empty or invalid. Declaring it like you did will make isValid() return true, though you might intend it to return false.
That said, i'm unsure of what you want to achieve in your code snippet. Basically you're testing m_model before assigning a new untested value var to it (why???).
As you said your value var (or probably the contained value) might have been deleted just before setValue() is called. If the QObject has been deleted and the pointer has not been set to nullptr explicitly you're handing a QVariant with a dangling pointer to the function. As setValue() doesn't test the new value var you will have that same dangling pointer in m_model, resulting in a crash whenever you try accesing it.
I have a QQuickItem that corresponds to a MapPolyline object. The polyline has a property called path, which is defined in the documentation to be of type list<coordinate>. coordinate is a type that maps to QGeoCoordinate in the C++ world. I'm trying to figure out how to set this property's value from C++.
If I check the QMetaObject for the item and look for the type that it reports for the path property, it indicates a type of QJSValue. It's unclear to me how I can set this value from C++ using either QObject::setProperty() or QQmlProperty::write(). I've tried the following:
I tried creating a QJSValue that is of array type, with each element holding the coordinate values that I want, something like this:
void set_property_points(QQuickItem *item, const QVector<QGeoCoordinate> &pointList)
{
// Get the QML engine for the item.
auto engine = qmlEngine(item);
// Create an array to hold the items.
auto arr = engine->newArray(pointList.size());
// Fill in the array.
for (int i = 0; i < pointList.size(); ++i) arr.setProperty(i, engine->toScriptValue(pointList[i]));
// Apply the property change.
item->setProperty("path", arr.toVariant());
}
This didn't work; the call to setProperty() returns false.
I also tried stuffing the list of points into a QVariantList, which seems to be the best match I can find in C++ for a list<coordinate> (QGeoCoordinate is capable of being placed in a QVariant):
/// Apply a list of `QGeoCoordinate` points to the specified `QQuickItem`'s property.
void set_property_points(QQuickItem *item, const QVector<QGeoCoordinate> &pointList)
{
QVariantList list;
for (const auto &p : pointList) list.append(QVariant::fromValue(p));
item->setProperty("path", list);
}
This didn't work either; same results.
This process doesn't seem to be well-documented. What format do I need to put my data into to make this work?
Turns out that a third approach that isn't mentioned in the documentation actually seems to work. I needed to set the property like this:
QJSValue arr; // see above for how to initialize `arr`
item->setProperty("path", QVariant::fromValue(arr));
I have a QGuiAppplication derived class (called Sy_application) that I've wrapped with another (called Sy_application_qml, with extra some QML specific features) and then registered that as a singleton with the QML engine.
This all works fine from the first opened top-level window, but the second (and presumably any more) only gets null from the singleton. Adding some debug:
// The static factory method used in the qmlRegisterSingletonType call.
QObject* Sy_application_qml::factory( QQmlEngine* engine,
QJSEngine* scriptEngine )
{
Q_UNUSED( engine )
Q_UNUSED( scriptEngine )
qDebug() << "Creating";
return new Sy_application_qml();
}
// SY_APP is the qApp macro casted to my Sy_application type.
QObject* Sy_application_qml::get()
{
qDebug() << "Getting:" << SY_APP;
return SY_APP;
}
// This is an example of it's use within QML
onPositiveClicked: {
console.log( Sy_application_qml );
Sy_application_qml.get().newProject( sampleRate.value, frameRate.value );
close();
}
And the debug output:
// Opening first window.
Creating
// Creating second
qml: Sy_application_qml(0x1895b30)
Getting: Sy_application(0x7fff80f051c0)
// Attempting to create third
qml: Sy_application_qml(0x1895b30)
Getting: Sy_application(0x7fff80f051c0)
qrc:/qml/gui/dialogs/Sy_newProjectDialog.qml:62: TypeError: Cannot call method 'newProject' of null
As you can see the singleton is still present and the C++ Sy_application instance is being returned correctly, but is appearing as null on the QML side. Any reason why it works for one window and not another?
So I finally worked it out, and it had nothing to do with QML singletons or having multiple top-level windows - but it had everything to do with passing QObject pointers to the QML engine from Q_INVOKABLE methods (the Sy_application_qml::get() method in my case).
From the docs:
Objects not-created by QML have CppOwnership by default. The exception
to this is objects returned from C++ method calls; in these cases, the
ownership of the returned objects will be set to JavaScriptOwnerShip.
Note this applies only to explicit invocations of Q_INVOKABLE methods
or slots, and not to property getter invocations.
So the QML engine had deleted my Sy_application pointer. The solution was nice and simple:
QObject* Sy_application_qml::factory( QQmlEngine* engine,
QJSEngine* scriptEngine )
{
Q_UNUSED( engine )
Q_UNUSED( scriptEngine )
QQmlEngine::setObjectOwnership( SY_APP, QQmlEngine::CppOwnership );
return new Sy_application_qml();
}
I am trying to use QStandardItemModel to do the same thing as QStringListModel (just for practice):
http://programmingexamples.net/wiki/Qt/ModelView/StandardItemModel
However, one cell shows up, but it is empty, as opposed to containing "text" as I would expect. Can anyone explain this? Is this the right way to go about using the QStandardItemModel (i.e. constructing QStandardItems and feeding them to the model?)
Actually, I'm surprised you aren't getting a crash. You are creating item0 on the stack, then passing a pointer to it to the QList. When that method leaves scope, item0 is deleted, and your list contains a pointer to the rotting area of memory that used to hold a QStandardItem.
{
QStandardItem item0("test");
QList<QStandardItem*> items;
items.insert(0, &item0); // Doesn't transfer ownership
model->appendRow (items);
} // Out of scope! Oh no!
Typically you would just create the new item, then add it using something like QStandardItemModel::setItem, like this:
QStandardItem *item0 = new QStandardItem("test");
model->setItem(0, 0, item); // transfers ownership of item0 to the model
I'm trying to send a QStandardItemModel-derived object to PythonQt, but I'm a little confused on how it needs to be sent. When I was using boost::python I had several controls like boost::noncopyable to ensure I wasn't recreating this object, but sharing it with python. I also had constructs to provide a boost shared pointer to python from inside python.
class Scene : public boost::enable_shared_from_this<Scene>, public QStandardItemModel
In PythonQt, however, I'm not sure what's available. The function call takes a QVariantList for all the function parameters.
QVariant PythonQt::call(PyObject* object, const QString &callable, const QVariantList &args = QVariantList))
What I'm confused about now is how to get my object to python via a QVariant. Since its derived from QStandardItemModel, I figured it would already be register
void MyObject::someFunction(QString fileName)
{
QVariant myObjectV = qVariantFromValue(this);
// send to python
...
}
But this gives me the following error:
'qt_metatype_id' : is not a member of 'QMetaTypeId<MyObject>'
I've tried registering it after I declare my class, but this throws a different error.
class MyObject : public QStandardItemModel
{
Q_OBJECT
...
};
Q_DECLARE_METATYPE(MyObject)
QStandardItemModel::QStandardItemModel(const QStandardItemModel&) is private within this context.
I actually get the error twice--once in header where I add the Q_DECLARE_METATYPE and in another header, which has a class which always derives from QStandardItemModel but is otherwise unrelated.
Is Q_DECLARE_METATYPE even the correct way to go about converting this object to a QVariant?
BOOST_PYTHON_MODULE(scene)
{
class_("Scene");
}
Yes, by default, QVariant can take one of te following types - http://doc.qt.io/qt-4.8/qvariant.html#Type-enum - and they are not enough for your task. You should declare additional types by yourself via qmetatype system. Thus you shoud call qRegisterMetaType() function.