Q_PROPERTY
to access their QML. Then most of the work happens on the QML side. QML has some verification methods and error checks, for example console.assert, but since QML is translated in JavaScript and does not have a strong typing, therefore, it cannot verify the type conformity. A typo in the variable name will lead to the fact that QML may not notice this error, and calculate this variable as declared new. A simple example: Container { layout: DockLayout { } Label{ text:ListItemData.titel } }
ListView
container. Through ListItemData
we get access to the data to be displayed. I made a small mistake here, in fact, the element is called the title
, and titel
is in German. Moreover, the German did not immediately notice. And QML will not notice the more. You can insert any word here, QML does not check for this kind of error, not at compile time or at run time. Nothing will be displayed, perhaps you are lucky to find a warning in the console. By the way, if you configure the IDE correctly, the message should appear exactly, even when debugging on the device.Q_ASSERT
does the checking while the application is running. For all elements of Cascades involved in QML, there is a class in C ++ that instantiates dynamically for each element during the execution of the application. The Cascades API allows you to search for objects of these classes and provides a way to control certain things. ListView
also has a class that supplies items for a ListView
from C ++: ListItemProvider
. This class has its own interface: virtual bb::cascades::VisualNode* createItem(bb::cascades::ListView* listview,const QString& type); virtual void updateItem(bb::cascades::ListView* listview,bb::cascades::VisualNode* node,const QString& type,const QVariantList& indexPath, const QVariant& data);
ListItemProvider
for each individual ListView
, and I would like to solve this problem once and for all, so I have my own generic ListItemProvider
. Since all checks occur while the application is running, C ++ templates are not an option, let's take a look at the following implementation. Before I go to crateItem, a brief install on addType, an auxiliary method for declaring handlers for each type: void ListViewItemProvider::addType(const QString& type, const QString& qmlasset, const listitem_callback& callback) { bb::cascades::QmlDocument* doc = bb::cascades::QmlDocument::create(qmlasset); if(!doc->hasErrors()) { doc->setParent(this); type_map.insert(type,doc); callback_map.insert(type,callback); }//TODO add additional error checking & handling }
QString
objects, so QMap<QString, QmlDocument*>
will be sufficient to store the supported types. This method converts a qml-resource (for example, file:///partial.qml
- approx. Translator ) into a QmlDocument
using QmlDocument::create
. By the way, QmlDocument::create
does not actually return a QmlDocument*
, as written in the example above. It returns a reference to the utility class Builder
to create a QmlDocument
's, then it looks like it produces an implicit conversion to QmlDocument*
. In principle, there is nothing interesting here, let's go further, the createItem
method: bb::cascades::VisualNode* ListViewItemProvider::createItem(bb::cascades::ListView* listview,const QString& type) { if(type_map.find(type)!=type_map.end()) { bb::cascades::Container* node = type_map[type]->createRootObject<bb::cascades::Container>(); return node; } Q_ASSERT_X(false,__FUNCTION__,type +" TYPE not handled"); bb::cascades::Container* con = new bb::cascades::Container(0); bb::cascades::Label* label = new bb::cascades::Label(con); label->setText("ERROR"); return con; }
QmlDocument::createRootObject
, which returns a pointer to the created object. This is a template, so we need to know in advance the type of object to create. So far I have decided for myself that for all UI elements I will use the Container
as the root object. Perhaps the VisualNode*
type, which is the return value of the createItem
method, is also suitable here. The most interesting thing here is error handling, how should we do this? Here Q_ASSERT_X
comes to the Q_ASSERT_X
and notifies you of an error. But in the case of assembly without debugging, it will not tell anything, and nevertheless the method should return some value. About 0 does not say anything in the documentation, so there is no reason to think that this is the correct return value for this method. But the documentation clearly states that the returned pointer will belong to the ListView
Even if we return 0 (fortunately, the BB10 developers provided for it), this will hide the error from the tester. So I decided to return a small Container
with a Label
with an error text. Maybe I could formulate the message better, but in this version the tester will notice this error more likely. Alternatively, an exception could be thrown, but after that, control is transferred back to the Cascades API and Qt, and this is not the best option, since Qt and Cascades do not use exceptions, although they are supported in BB10.updateItem
method. And again it does not work out easier, because we are writing generic code. In the end, the loaded QML file must be loaded with the correct values, which was also one of the reasons why I decided to updateItem
method: if(callback_map.find(type)!=callback_map.end()) { #ifndef USE_BOOST (*callback_map[type])(node,indexPath,data); #else callback_map[type](node,indexPath,data); #endif }
USE_BOOST
, but for such a callback, the C ++ programmer must first consider boost::function
. And since BlackBerry claims that boost is supported, I certainly use it. And here it turns out that it is not so easy, at least my toolbar falls with an error in boost/type_traits/detail/cv_trait_impl.hpp
. I know that boost is being used by many, so this is most likely an error due to the settings of my system or the Linux distribution. The error seems to come from the preprocessor, on the GCC version 4.6.3, the error text says about the mismatch of the brackets. I preferred to patch my version of boost on the local machine and reported this to the boost community and to the BlackBerry. If you use boost under BB10, then you better use the boost version from GitHub's BlackBerry . Since not everyone needs a boost, I also made a version without it, just in case the boost breaks again. void ApplicationUI::callbackMyListItem(bb::cascades::VisualNode* node,const QVariantList& indexPath, const QVariant& data) { bb::cascades::ImageView* image = node->findChild<bb::cascades::ImageView*>("imageview"); Q_ASSERT(image); if(image) { QString name_image = "image"; //this must be correct! QVariantMap map = data.toMap(); bool hasdata = map.contains(name_image); Q_ASSERT(hasdata); if(hasdata) image->setImageSource(map[name_image].toUrl()); } }
VisualNode
inherited from a QObject
, so the child objects can be obtained via findChild
, which returns 0 if nothing is found. Therefore it is appropriate to use Q_ASSERT
here to test this case. Then there is a search for data in QVariantMap
. Since, in any case, we need some image, we check the presence of an element in the QVariantMap container. If not, then Q_ASSERT
comes to the rescue again. This callback is simply registered with boost::bind
.main.cpp
. Momentics and QtCreator can create a working application for my DevAlpha device, so development under BB10 in QtCreator is possible, but there is room to grow.Source: https://habr.com/ru/post/192064/
All Articles