BlackBerry10 学习Signals和Slots

本文深入探讨了Cascades框架中利用信号与槽机制实现对象间通讯的方法,包括如何在类中定义信号与槽,以及如何在不同组件间进行信号与槽的绑定与响应。此外,还介绍了预定义的UI控件信号及其对应处理程序的自动创建,以及如何在C++中手动实现信号与槽的连接。最后,展示了如何创建自定义的信号与槽,并将其应用于实际场景中。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

文章摘要: 在Cascades框架中,使用Signals(信号)和slots(信号槽)机制来实现对象间通讯,如类A中封装了登录的相关操作,有一个signal为loginResult(bool result)函数,你可以在B中设置一个slot为onLoginResult(bool result)函数,把它们绑定起来:connect(A, SIGNAL(loginResult(bool)), B, onLoginResult(bool)),这样…

在Cascades框架中,使用Signals(信号)和slots(信号槽)机制来实现对象间通讯,如类A中封装了登录的相关操作,有一个signal为loginResult(bool result)函数,你可以在B中设置一个slot为onLoginResult(bool result)函数,把它们绑定起来:connect(A, SIGNAL(loginResult(bool)), B, onLoginResult(bool)),这样当A中emit loginResult(bool)的时候(发送信号),会通知调用B中onLoginResult(bool),这样就可以在B中进行相应的处理了,而且A和B的耦合度非常低。signal和slot都是类的成员函数,它们之间不是一对一的关系,你可以不为signal设置slot,也可以为一个signal设置多个slot;同时也可以让signal和另一个对象的signal绑定,这样当signal被emit的时候,另一个signal也会被emit。
1. 使用UI控件中预定义的signals和slots
Cascades框架中的很多UI控件声明了一些signals,当用户与app进行交互的时就会触发某个signal,如点击按钮、滑动滑块等,你可以在QML或者C++中处理这些信号。
(1)在QML中,操作起来比较简单,预定义signals对应的signal处理程序会自动创建,名字为:signal名字前+on且把signal首字母大写,采用驼峰命名风格。如:Button的一个signal为clicked(),对应的信号处理程序则为onClicked,如:

1
2
3
4
5
6
7
8
9
Page {
     content: Button {
         text: "Click me"
 
         onClicked: {
             text = "Clicked!"
         }
     }
}

一些预定义的signals也包含了参数,你可以在对应的信号处理程序中直接使用,如CheckBox控件的一个signal:checkedChanged (bool checked),你可以在onCheckedChanged中直接使用checked:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
Page {
     content: Container {
         CheckBox {
             onCheckedChanged: {
                 if (checked)
                     checkLabel.text = "Checked";
                 else
                     checkLabel.text = "Not checked";
             }
         }
 
         Label {
             id: checkLabel
             text: "Not checked"
         }
     } // end of Container
} // end of Page

(2)在C++中,需要使用QObject::connect(A, SIGNAL(a()), B, b())这样的形式将A中的signal和B中的slot连接起来,A发送信号,B接收信号。SIGNAL和SLOT是两个宏,目的就是为了把signal a和slot b转换成字符串常量。下面可以实现这样一个效果:通过拖动滑块改变一个矩形区域的宽度。实现思路很简单,把拖动滑块时发送的signal跟设置矩形区域宽度的slot连接起来,示例代码:

1
2
3
4
5
6
7
8
9
10
11
Slider* mySlider = Slider::create()
                     .from(100)
                     .to(500);
Container* mySizedContainer = Container::create()
                                .preferredWidth(100)
                                .preferredHeight(100)
                                .background(Color::DarkGreen);
bool res = QObject::connect(mySlider, SIGNAL(immediateValueChanged( float )),
                             mySizedContainer, SLOT(setPreferredWidth( float )));
Q_ASSERT(res);
Q_UNUSED(res);

由于,connect函数中的东东容易写错,所以这里使用Q_ASSERT()对返回值res进行检验。需要注意的是,signal和slot参数匹配时类型必须是一样的,如果signal参数为int,而slot参数为QString,就会connect不成功。不过参数的个数不需要是相同的,slot可以比signal的参数个数少,如signal(int i, String s)跟slot(int i)连接时,是可以connect成功的,只不过signal的String类型的参数被忽略掉了,不能出现signal参数比slot参数少。还要注意的是如果signal和slot的参数类型为Cascades定义的类型,则需要写上类的完全限定名:前面加上命名空间,如:

1
2
3
4
QObject::connect(myImageView,
                  SIGNAL(imageChanged(bb::cascades::Image*)),
                  myHandler,
                  SLOT(onImageChanged(bb::cascades::Image*)));

2. 创建自定义的signals和slots
(1)在QML中,自定义signal很简单,在函数前面加上signal关键字就行了,emit的时候直接调用函数。如:

1
2
3
4
5
6
7
8
9
10
11
Button {
     id: myButton
     signal clickedWithText(string currentText)
     text: "Hello world"
     onClicked: {
         clickedWithText(text)
     }
     onClickedWithText: {
         console.debug( "onClickedWithText: " + text)
     }
}

点击按钮的时候会emit clickedWithText,跟预定义的信号一样,会自动生成一个onXXsignal处理程序,因此在控制台会打印出内容 onClickedWithText:Hello World, 如图

你也可以将自定义的JavaScript函数指定为slot,然后跟自定义的signal connect起来,可以分别把signal和slot定义在不同的qml文件,如:新建MyLabel.qml,内容如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
import bb.cascades 1.0
 
Container {
     function handleText(message) {
         label.text = message
         console.debug( "handleText: " + message)
     }
     Label {
         id: label
         text: qsTr( "Hello World" )
         textStyle.base: SystemDefaults.TextStyles.BigText
         horizontalAlignment: HorizontalAlignment.Center
     }
}

main.qml中添加:

1
2
3
4
5
6
7
8
9
10
11
12
13
MyLabel {
      id: myLabel
}
 
Button {
     id: myButton
     signal clickedWithText(string currentText)
     text: "Click me"
     onClicked: {
         myButton.clickedWithText.connect(myLabel.handleText)
         myButton.clickedWithText( "Alex Zhou" )
     }
}

点击按钮的时候,handleText函数会被调用,打印日志(handleText: Alex Zhou):

(2)在C++中:在头文件中定义signals和slots,下面来模拟实现一个登陆验证的例子,创建LoginService类,用来进行登陆时验证用户名和密码,LoginService.h:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
/*
  * LoginService.h
  *
  *  Created on: 2013-3-23
  *      Author: Administrator
  */
 
#ifndef LOGINSERVICE_H_
#define LOGINSERVICE_H_
 
#include <QObject>
 
class LoginService : public QObject{
     Q_OBJECT
public :
     LoginService();
     virtual ~LoginService();
 
signals:
     void loginResult( bool result);
 
public slots:
     void startLogin( const QString& username, const QString &passwd);
 
};
 
#endif /* LOGINSERVICE_H_ */

声明了一个signal(登陆处理结果信号)和一个slot(验证登陆请求),来实现LoginService.cpp:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
/*
  * LoginService.cpp
  *
  *  Created on: 2013-3-23
  *      Author: Administrator
  */
 
#include "LoginService.h"
#include <QDebug>
 
LoginService::LoginService() {
     // TODO Auto-generated constructor stub
 
}
 
LoginService::~LoginService() {
     // TODO Auto-generated destructor stub
}
 
void LoginService::startLogin( const QString &username, const QString &passwd)
{
     qDebug() << "startLogin: username=" << username << "&password=" << passwd;
     if (username == "alexzhou" && passwd == "123456" )
         emit loginResult( true );
     else
         emit loginResult( false );
}

接着来创建UserLogin类,内部也封装了一个signal和一个slot,该类可以封装一些函数供qml调用。
UserLogin.h:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
/*
  * UserLogin.h
  *
  *  Created on: 2013-3-23
  *      Author: Administrator
  */
 
#ifndef USERLOGIN_H_
#define USERLOGIN_H_
 
#include <QObject>
 
class LoginService;
class UserLogin : public QObject{
     Q_OBJECT
public :
     UserLogin();
     virtual ~UserLogin();
 
signals:
     void login( const QString &username, const QString &passwd);
 
public slots:
     void onLoginResult( bool result);
 
private :
     LoginService *m_pLoginService;
};
 
#endif /* USERLOGIN_H_ */

UserLogin.cpp:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
/*
  * UserLogin.cpp
  *
  *  Created on: 2013-3-23
  *      Author: Administrator
  */
 
#include "UserLogin.h"
#include "LoginService.h"
#include <QDebug>
 
UserLogin::UserLogin() {
     // TODO Auto-generated constructor stub
     m_pLoginService = new LoginService();
     bool result = QObject::connect( this , SIGNAL(login( const QString&, const QString&)), m_pLoginService, SLOT(startLogin( const QString&, const QString&)));
     Q_ASSERT (result);
     result = QObject::connect(m_pLoginService, SIGNAL(loginResult( bool )), this , SLOT(onLoginResult( bool )));
     Q_ASSERT(result);
     Q_UNUSED(result);
 
     emit login( "alexzhou" , "123456" );
}
 
UserLogin::~UserLogin() {
     if (m_pLoginService)
     {
         delete m_pLoginService;
         m_pLoginService = NULL;
     }
}
 
void UserLogin::onLoginResult( bool result) {
     qDebug() << "login result: " << result;
}

这里在UserLogin类的构造函数中进行了相关绑定和emit login操作,只是为了方便,在实际开发中可以在UserLogin定义一个登陆接口,然后在登陆按钮的onClicked处理程序中调用该登陆接口。
使用的时候在(ProjectName).cpp的构造函数中加入UserLogin userLogin;就行了。运行程序,打印日志为:

转载请注明来自:Alex Zhou的程序世界,本文链接:http://codingnow.cn/blackberry/1173.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值