Monthly Archives: 十一月 2012

C++ and QML integration

QML is designed so that data can easily be passed to and from C++. You can use the classes in the Qt Declarative module to load and modify QML documents from C++. In addition, QML and C++ objects can easily communicate using Qt signals and slots.

There are a number of reasons that you might want to mix QML and C++ in your application. For example, you might have a data source for a list that you define in C++, while the visuals for the list are in QML. Or you might define your UI in QML, but you must access functionality in the Qt Declarative module or another Qt class.

Loading a QML document from C++

An image demonstrating for qml is passed to c++.Loading a QML document into C++ is a common way that Cascades applications are created. You can define your UI in QML, and then load it into your C++ application to be displayed for the user.

To load a QML document into an application, you create a QmlDocument object by callingQmlDocument::create() and specifying the QML asset that you want to load. To display the content of the QmlDocument, you retrieve the root node using QmlDocument::createRootObject(), and display the content using Application::setScene().

The following example demonstrates how to create a Cascades app using QML for the UI:

main.cpp

int main(int argc, char **argv)
{
    Application app(argc, argv);
    QmlDocument *qml = QmlDocument::create("asset:///main.qml").parent(&app);
    AbstractPane *root = qml->createRootObject<AbstractPane>();
    app.setScene(root);
    return Application::exec();
}

main.qml

Page {
    property alias labelText: label.text     
    Container {           
        Label {
            id: label
            text: "Label"
        
                      
        Button {
            objectName: "button"
            text: "Button"
        }                              
    }
}

After you load a QML document into C++, you can modify its properties usingQObject::setProperty(). The following example shows how you can change the text for thelabelText property:

root->setProperty("labelText", "New text");

In this example, we can change the text because we define an alias to the text property for the Labelat the root of the QML document. Without an alias to the label’s text property, the value is not accessible because it’s not located at the root of the QML document.

When you set the scene in an application, the root object must be a subclass of AbstractPane. However, the root node of the QML document that you want to load doesn’t need to be an AbstractPane subclass. For example, if your main.qml file includes a Container control at its root, you can load this document and create a scene to display it as follows:

QmlDocument *qml = QmlDocument::create("asset:///main.qml");
Container *c = qml->createRootObject<Container>();
Application::instance()->setScene(Page::create().content(c));

Accessing child objects

Since QML documents are organized in a tree hierarchy, another option for accessing child properties and objects is to search for the object by using QObject::findChild(). In this example, we do a search for the button and change its text property:

QObject *newButton = root->findChild<QObject*>("button");
if (newButton)
    newButton->setProperty("text", "New button text");

To be able to search for the object using findChild(), the button must have an objectName.

Exposing C++ values and objects to QML

An image demonstrating how to expose C++ to qml.There are often cases that require you to embed data from C++ into QML that you loaded into your application. A convenient way of passing C++ data or objects to QML is by using theQDeclarativePropertyMap class. This allows you to define key-value pairs in C++ that you can bind to QML properties.

For example, this is how you could use QDeclarativePropertyMap to pass a name and phone number to QML.

QmlDocument *qml = QmlDocument::create("asset:///main.qml");
QDeclarativePropertyMap* propertyMap = new QDeclarativePropertyMap;
propertyMap->insert("name", QVariant(QString("Wes Barichak")));
propertyMap->insert("phone", QVariant(QString("519-555-0199")));
qml->setContextProperty("propertyMap", propertyMap);

In your QML document, you can access the values by specifying the property name and the key that you define in C++.

Label {
    text: "User name: " + propertyMap.name
}
Label {
    text: "Phone number: " + propertyMap.phone
}

Since QDeclarativePropertyMap accepts QVariant objects, you can pass a QObject to QML this way as well.

Exposing C++ objects to QML

An image displaying the application running on the device.

In an application, it’s possible to create a C++ object and expose it to QML. Before you can pass a C++ object to QML, there are some important Qt macros that you need to implement in the header file for the C++ class.

  • Q_OBJECT is a required macro for classes that use the signals and slots mechanism.
  • Q_PROPERTY exposes a class property to QML. It also defines the read and write values from and to the property, as well as the signal that is emitted with the property is changed.
  • Q_INVOKABLE exposes member functions so that they can be invoked from QML.

In the example header below, the class has a Q_PROPERTY called value. It also has three functions;value(), setValue(), and reset(). The reset() function is invokable from QML using theQ_INVOKABLE macro. The header also defines the signal that is emitted when the value property changes.

#ifndef MYCPPCLASS_H_
#define MYCPPCLASS_H_
#include <QObject>
#include <QMetaType>
class MyCppClass : public QObject {
    Q_OBJECT
    Q_PROPERTY(int value READ value WRITE setValue NOTIFY valueChanged)
public:
    MyCppClass();
    virtual ~MyCppClass();
    Q_INVOKABLE void reset();
    int value();
    void setValue(int i);
signals:
    void valueChanged(int);
private:
    int m_iValue;
};
#endif

And this is how you could implement the resulting class.

#include "MyCppClass.h"
MyCppClass::MyCppClass()
{
    m_iValue = 0;
}
MyCppClass::~MyCppClass()
{
}
int MyCppClass::value()
{
    return m_iValue;
}
void MyCppClass::setValue(int i)
{
    m_iValue = i;
    emit valueChanged(m_iValue);
}
void MyCppClass::reset()
{
    m_iValue = 0;
    emit valueChanged(m_iValue);
}

To expose MyCppClass to QML, you create an instance of the class and use setContextProperty()to expose it.

MyCppClass *cppObject = new MyCppClass();
qml->setContextProperty("cppObject", cppObject);

From QML, you can access the Q_PROPERTY by using the property name that is defined in the header file. In the example below, the value property is displayed in the text property of a label.

Label {
  text: "Value of cppObject: " +  cppObject.value
}

From QML, you can also change the value of a Q_PROPERTY, connect slots to its signals, and callQ_INVOKABLE functions. In the example below, a button is used to increase the value property each time the user presses it. The button also defines a custom slot and connects it to the property’svalueChanged() signal. When the slot is invoked, the slot changes the text on the button. A reset button is also used, which calls MyCppClass::reset() to reset the value property.

Button {
    id: increaseButton
    text: "Increase the value"
    onClicked: {
        cppObject.valueChanged.connect
                (increaseButton.onCppValueChanged);
        cppObject.value = cppObject.value + 1;
    }
    function onCppValueChanged (val) {
        increaseButton.text = "Set value to " + (val + 1);
    }
}
Button {
    id: resetButton
    text: "Reset the value"
    onClicked: {
        cppObject.reset ()
    }
}
Label {
    id: valueLabel
    text: "The value is " + cppObject.value
}

Using C++ classes in QML

In some instances, instead of passing a C++ object or values to QML, you may want to use the C++ class in QML directly. In the Cascades framework, there are a few ways that you can use a C++ class in QML directly, whether it’s a class from the core Qt library, or a class that you define yourself:

In both cases, you must register the class for QML, by using qmlRegisterType().

Using the attachedObjects property

An image displaying the application running on the device.

Since the attachedObjects property is a member of UIObject, you can use it to attach a QObject to almost any Cascades QML component that has a visual representation. For example, if you want access to the functionality in the QTimer class, this is how you would register the class for use in QML:

qmlRegisterType<QTimer>("my.library", 1, 0, "QTimer");

After you register the QTimer class, you can create and manipulate QTimer objects within your QML document. In the following example, a QTimer object is attached to a Label. When the page is created, the timer is started. When the timer is complete, the text for the label is updated with new text.

import bb.cascades 1.0
import my.library 1.0
Page {
    Label {
        id: timerTestLabel
        text: "Hello world"
            
        attachedObjects: [
            QTimer {
                id: timer
                interval: 1000
                onTimeout :{
                    timerTestLabel.text = "Timer triggered"
                }
            }
        ]
    }
        
    onCreationCompleted: {
        timer.start();
    }
}

For another example demonstrating how to use the attachedObjects property in an application, see the Signals and slots tutorial.

Subclassing CustomControl

An image displaying the application running on the device.

By subclassing CustomControl, you can use your C++ class in a QML document without attaching it to another component using attachedObjects. In the following header file, a simple class calledTextControl is declared. TextControl is composed of a text property, functions to set and get thetext property, and a signal that is emitted when the text changes.

#include <QObject>
#include <QString>
#include <bb/cascades/CustomControl>
class TextControl : public bb::cascades::CustomControl
{
    Q_OBJECT
    Q_PROPERTY(QString text READ text WRITE setText NOTIFY textChanged)
public:
    QString text();
    void setText(QString text);
signals:
    void textChanged();
};

To use the class in QML, first you must register the class.

qmlRegisterType<TextControl>("my.library", 1, 0, "TextControl");

After you register the class, you can import its library into a QML document, and use it the way you use any other QML component:

import bb.cascades 1.0
import my.library 1.0
Page {
    TextControl {
        text: "Custom text control"
    }
}

Injecting C++ objects into QML

An image displaying the application running on the device.

In previous sections, we’ve looked at how to define values and objects in C++ and use them in QML. We’ve also looked at how to expose C++ classes to QML as attached objects or custom controls. In this section, we’ll see how to create objects in C++ and inject them dynamically into QML.

Being able to inject C++ content into to QML is useful if you have a UI that is dynamically generated. For example, you might want the ability to add or remove containers from your screen during run time.

The first step is to provide an objectName for the QML component that you want to add content to (or remove from). In this case, content is going to be added to a Container.

Page {
    Container {
        objectName: "rootContainer"
    }
}

Next, you load the QML file into C++ and create a Page object using the root from the QML file. Once you have the root of the QML file, you use findChild() to search for the Container with the specified object name.

QmlDocument *qml = QmlDocument::create("main.qml");
// Sets the context property that we want to use from within QML.
// Functions exposed via Q_INVOKABLE will be found with this
// property name and the name of the function.
qml->setContextProperty("injection", this);
// Creates the root using the page node
Page *appPage = qml->createRootNode<Page>();
// Retrieves the root container from the page
Container *container = appPage->findChild<Container*>("rootContainer");

Now that you have the container you want to add content to, it’s just a matter of adding your content. Here’s how to add another container as the last child in the parent.

container->add(Container::create()
    .background(image)
    .preferredSize(200,200));

You aren’t restricted to just adding content though. If you wanted, you could remove components, replace components, or insert components at a specific index.

Here’s an example that demonstrates how to create containers in C++ and inject them into QML. The QML file contains a button that when clicked, calls a function in C++ that creates a new container and adds it to QML.

main.qml

import bb.cascades 1.0
Page {
    // Allows the user to scroll vertically
    ScrollView {
        scrollViewProperties {
            scrollMode: ScrollMode.Vertical
        }
        // Root container that containers from C++ are added to
        Container {
            objectName: "rootContainer"
            layout: StackLayout {}
            // Button that calls the C++ function to add a
            // new container. The selectedIndex from the drop down
            // is passed to C++.
            Button {
                text: "Add container"
                onClicked: {
                    injection.injectContainer();       
                }
            }
        }
    }
}

TestApp.cpp

#include "TestApp.hpp"
#include <bb/cascades/Application>
#include <bb/cascades/QmlDocument>
#include <bb/cascades/AbstractPane>
using namespace bb::cascades;
TestApp::TestApp(bb::cascades::Application *app)
: QObject(app)
{
    QmlDocument *qml = QmlDocument::create("asset:///main.qml").parent(this);
    qml->setContextProperty("injection", this);
    AbstractPane *root = qml->createRootObject<AbstractPane>();
    app->setScene(root);
}
void TestApp::injectContainer()
{
    // Creates the container and adds it to the root
    // container in qml
    mRootContainer->add(Container::create()
        .background(Color::Red)
        .preferredSize(200,200)
        .bottomMargin(20)
        .horizontal(HorizontalAlignment::Center));       
}

TestApp.hpp

#ifndef TestApp_HPP_
#define TestApp_HPP_
#include <QObject>
namespace bb { namespace cascades { class Application; }}
class TestApp : public QObject
{
    Q_OBJECT
public:
    TestApp(bb::cascades::Application *app);
    virtual ~Test() {}
    
    // By using Q_INVOKABLE we can call it from qml
    Q_INVOKABLE void injectContainer();
private:
    Page *appPage;
    Container *mRootContainer;
};
#endif /* Test_Hpp_ */

海因斯对 BB10 信心十足,认为其将取代笔记本电脑

来源:黑莓时光

 

纽约时报之前对 BlackBerry 的报道引起了众多反响,而海因斯在最近接受纽约时报的时候,表达了对 BlackBerry 10 的雄心壮志。他认为,BlackBerry 10 将会取代你的笔记本电脑。海因斯拍着自己放在口袋中的 BB10 说:以后,员工们进到办公室,将不再需要笔记本电脑,只需要把 BB10 接上显示器和键盘,就可以工作了,未来 3~5 年内你将不需要笔记本电脑了!不知道海因斯这样的信心,从何而来。

几点因素决定 BlackBerry 10 将巩固第三大移动平台地位

RIM 对于 BlackBerry 10 的市场定位比起 BlackBerry 7 时的“super Phone”来讲,明确了很多,那就是与微软的 Windows Phone 争夺 iOS 和 Android 之后的第三大移动平台。而这两歌阵营对已自己的形式都抱有很乐观的态度,比如著名的黑莓网站 BerryReview 就认为,有几点因素已经决定了 BlackBerry 10 几乎已经赢得了这场战争。大致概括一下原文分享给大家,这就好像美国大选,两方都说自己是拯救世界的强者,其实都难逃强词夺理之嫌,谁笑到最后还得销售出来见真章。

资讯来源:BerryReview

BerryReview  的 Brighton 最近听到一些开发者表示,宁可为 Windows Phone 开发应用,也不愿意再为 BlackBerry 10 开发,令他很不满意,他认为,短期来看,好像 Windows Phone 似乎占一些优势,但是用长远的眼光来看,还是赢不了 BlackBerry 的,主要原因有以下几点。

  • 1:BlackBerry 有 8000 万用户。

BlackBerry 的 8000 万用户应该是 RIM 最后的法宝了,Brighton 的观点是 BlackBerry 的 8000 万用户是微软不可比拟的,Windows Phone 总共才有 400 万的出货,8000 万用户每人换一台 BlackBerry 10,RIM 就赢了。

  • 2:BlackBerry 有广阔的国际市场。

别看在北美地区和欧洲地区 BlackBerry 形式差得很,但是在东南亚如印尼,马来西亚,又在非洲如尼日利亚等这些国家,BlackBerry 的普及率相当的高,几乎占据了半个以上的市场。而比起来微软在国际市场上的占有就不如 BlackBerry 了(这个结论不知道是怎么的出来的。)。

  • 3:BlackBerry 有深厚的品牌影响力。

Brighton 最后人为,BlackBerry 在消费者心中仍然有一个光明的形象,想比起来微软是做桌面 OS 的,转换到移动市场上来,专业度和给予消费者的信心都会不足。

综上三点,Brighton 认为开发者赢及早站好队伍,BlackBerry 一定会在这场战争中赢得最后的胜利。关于这些,你又怎么看呢?

第一个BlackBerry 10 Cascades应用

windows上的整体开发环境安装成功之后,就可以开发BlackBerry10 Cascades应用了。

下面进行第一个BlackBerry 10 Cascades应用。

1、首先打开IDE,然后File -> New -> BlackBerry Cascades C++ Project

BlackBerry 10 Cascades应用
2、然后输入项目名称,Next

BlackBerry 10 Cascades应用

3、选择模版,这里选择Cascades HelloWorld,然后Finish

BlackBerry 10 Cascades应用

4、新建完成之后,在左侧的Project Explorer里可以看到刚才新建的项目

BlackBerry 10 Cascades应用

5、在模拟器上运行该程序,需要启动模拟器,然后在项目上点击右键,如下图:

BlackBerry 10 Cascades应用

6、然后在项目上右键,选择Build Project,最后选择Run as -> BlackBerry C/C++ Application,稍等后即可在模拟器上看到运行结果,效果如下:

BlackBerry 10 Cascades应用

 

至此第一个BlackBerry 10 Cascades应用就完成了,其实BlackBerry 10 Cascades应用和PlayBook开发的相关配置等都是相同的,之前接触过PlayBook Native SDK开发的人员对于当前的Cascades开发会感到相当的熟悉。

接下来就该深入了解Cascades的相关开发了。

BlackBerry Cascades开发环境搭建(二)

根据黑莓世界大会中展示的BlackBerry 10 Dev Alpha来看,部分功能的确让人眼前一亮,例如:输入法的联想输入和照相机的功能。

而模拟器会是什么样子的呢?刚刚安装完模拟器之后,给我的第一感觉就是缩小版的PlayBook,无论是从界面还是手势等方面。

首先要安装VMware Player.  参考:

https://developer.blackberry.com/devzone/develop/simulator/simulator_systemrequirements.html

那下面来介绍下模拟器的安装。

1、下载模拟器安装文件

下载地址:https://bdsc.webapps.blackberry.com/cascades/download

BlackBerry 10 Dev Alpha Cascades

2、待安装文件下载完成之后,开始安装,点击Next

BlackBerry 10 Dev Alpha Cascades

3、选择我接受,继续Next

BlackBerry 10 Dev Alpha Cascades

4、选择安装路径,继续Next

BlackBerry 10 Dev Alpha Cascades

5、显示安装信息,Install

BlackBerry 10 Dev Alpha Cascades

6、开始安装

BlackBerry 10 Dev Alpha Cascades

7、安装成功

BlackBerry 10 Dev Alpha Cascades

8、打开安装路径,运行BlackBerry10Simulator.vmx

BlackBerry 10 Dev Alpha Cascades

9、启动模拟器,显示等待界面

BlackBerry 10 Dev Alpha Cascades

10、模拟器启动成功,可以看到界面了

BlackBerry 10 Dev Alpha Cascades

 

至此模拟器也就安装完成了,我们就可以开始开发第一个Cascades应用了。

BlackBerry Cascades开发环境搭建(一)

随着黑莓世界大会公布的BlackBerry 10 Native SDK包括的Cascades框架,你可以用来为黑莓平台开发印象深刻的应用。Cascades框架包括了工具和APIs,你可以使用Cascades类库和Qt类库来创建更多的应用。

下面针对在windows上如何搭建Cascades开发环境进行介绍。

1、首先下载SDK,在BlackBerry 10 Native SDK中包括了Cascades开发中所有的工具和类库。

下载地址:https://bdsc.webapps.blackberry.com/cascades/download

BlackBerry 10 Dev Alpha Cascades

2、下载完成后,打开安装程序开始安装,点击Next

BlackBerry 10 Dev Alpha Cascades

3、选择我接受,然后继续Next

BlackBerry 10 Dev Alpha Cascades

4、选择安装路径,继续Next

BlackBerry 10 Dev Alpha Cascades

5、显示安装信息,然后点击Install

BlackBerry 10 Dev Alpha Cascades

6、选择创建快捷方式

BlackBerry 10 Dev Alpha Cascades

7、开始安装

BlackBerry 10 Dev Alpha Cascades

8、安装完成

BlackBerry 10 Dev Alpha Cascades

全部安装完成后,启动IDE,显示loading界面

BlackBerry 10 Dev Alpha Cascades

然后显示欢迎界面

BlackBerry 10 Dev Alpha Cascades

 

至此SDK安装成功,下一节将简单介绍下BlackBerry 10 Dev Alpha模拟器的安装。

阳春城乡客运汽车时间表

序号 线 路 发车时间 途径站点 备 注
1 阳春 – 阳江 6:15 – 21:00 (每 15 分钟一班) 直达
2 阳春 – 春湾 7:30 – 21:00 (每 30 分钟一班) 直达
3 阳春 – 阳江 6:15 – 18:20 (每 20 分钟一班) 岗美、轮水
4 阳春 – 阳西 6:45 – 16:30 (每 1 小时一班) 河口
5 阳春 – 罗阳 6:15 – 18:15 (每 30 分钟一班) 春湾、凌霄
6 阳春 – 圭岗 7:00 – 9:40 (每 30 分钟一班) 合水、黄牛头
7 阳春 – 马贵 6:00 – 16:00 (每 1 小时一班) 三甲、双窖
8 阳春 – 双窖 6:30 – 20:00 (每 30 分钟一班) 潭水、三甲
9 阳春 – 八甲 6:45 – 20:30 (每 30 分钟一班) 潭水、三甲
10 阳春 – 永宁 7:00 – 16:00 (每 1 小时一班) 石录
11 阳春 – 岗美 6:20 – 18:10 (每 20 分钟一班) 公共汽车
12 阳春 – 合水 6:30 – 19:00 (每 20 分钟一班) 公共汽车 二运发车
13 阳春 – 潭水 6:00 – 18:30 (每 20 分钟一班) 公共汽车
14 阳春 – 黄牛头 6:30 – 18:40 (每 30 分钟一班) 公共汽车 二运发车
15 阳春 – 河口 6:40 – 16:30 (每 1 小时一班) 公共汽车
16 阳春 – 锡山 6:15 – 18:30 (每 30 分钟一班) 公共汽车
17 春湾 – 石望 7:00 – 17:30 (每 30 分钟一班) 公共汽车
18 三甲 – 山坪 7:10 – 17:00 (每 30 分钟一班) 公共汽车
19 阳春 – 蟠龙 8:00 – 16:00 (每 2 小时一班) 公共汽车 二运发车