Target-Action模式

很久以前,Apple和IBM公司成立了一个公司Taligent,开发一些类似Cocoa的工具和库,在Taligent已经比较成熟的时候,在一个商业交流会议上,我遇到了该公司的一个工程师.我请他开发一个简单的程序:<wbr>在一个window上创建一个button. 当点击这个button, 在textfield上面显示 "hello world".这个工程师创建了一个工程,并且开始疯狂的开始了类继承:从window和button以及event handler等基本类继承自己的子类. 然后写了n多的代码让button和textfield显示在window上. 45分钟后,我必须得离开了,可以他仍然没有完成. 在那时候我就断定,这个公司没有前途.果然,若干年后,他们关门大吉了.</wbr>

很多的C++和Java工具库和Taligent工具库思想差不多.开发者需要从标准类中继承,编写很多的代码来控制window上显示的控件. 很多类似的工具库确实在使用.

如果使用AppKit framework. 我们很少去继承类去处理windows ,buttons或是events. 相反,我们会使用现有的类. 同样你也不需要编写代码去控制window中的控件.nib文件能包含所有的信息. 整个程序只会包含少数几行重要的代码.开始,你会决定很惊讶.但是,随着时间,你会发现它的优雅和美妙.

<wbr></wbr>

通过学习NSControl可以很好的理解Appkit framework. NSButton,NSSlider,NSTextView和NSColorWell都是NSControl的子类. 每个control都包含target和action. target是一个指向其它对象的指针.action是会发给target的message(selector).<wbr>回忆一下我们在第二章给两个按钮设置的target和action: 我们把foo对象设置成两个按钮的target.一个按钮的action设置成 seed;另一个设置成generate. 如图5.1</wbr>

当用户和控件交互是. action 消息就会发送给它们的target. 例如当点击一个button,它的action消息将发送给它的target. 如图5.2

action方法接受一个参数: sender.该参数可以让接收者(target)知道是哪一个控件发送的这个action消息. 通常,我们会访问sender来获得更多的信息.比如,一个check box 在勾选的时候发送action消息. 而接受者接受到消息后会访问checkbox,得到它是否是选中的.

- (IBAction)toggleFoo:(id)sender

{

<wbr><wbr><wbr> BOOL isOn =[sender state];</wbr></wbr></wbr>

<wbr><wbr><wbr> ...</wbr></wbr></wbr>

}

要更好得理解NSControl, 我们要进一步了解它的父类继承关系:NSControl继承NSView. 而NSView又继承NSResponder. NSResponder的父类就是NSObject.这个继承关系中的每个节点类都增加了一些实现去做某些功能.如图5.3


在继承关系根部是NSObject. 所有的类都从它继承,同时继承了NSObject实现了一些基本方法:retain, release, dealloc,和init.NSResponder是NSObject的子类. 它实现了一些事件处理的方法,象mouseDown: keyDown:等等.NSView是NSResponder的子类. NSView描述了window上的一块区域,来显示自己.我们可以创建NSView的子类来画图,或是让用户拖拽数据.而NSContro继承NSView,并增加了target和action.

<wbr></wbr>

-- 一些常用的NSControl子类

在使用控件前,我们简要的来学习下3个常用控件类:NSButton, NSSlider,NSTextFeild.

<wbr></wbr>

- NSButton

NSButton实例对象可以有几个不同的外表: 椭圆形,方形,复选框.在点击它们时,它们有不同的行为. 还可以给按钮设置图标和声音. 在Interface Builder中选择NSButton的Attributes Inspector 如图5.4


我们可能经常给按钮发送3个消息:

<wbr></wbr>

- (void)setEnabled:(BOOL)yn

用户可以点击enabled的按钮, disabled的按钮会是灰色的.

<wbr></wbr>

- (int)state

一般对于复选框而言,如果按钮是勾选的, 返回NSOnState(1) ,没有勾选则为NSOffState (0).

<wbr></wbr>

- (void)setState:(int)aState

该方法可以使复选框勾选或不勾选.

<wbr></wbr>

--NSSlider

NSSlider实例-slider可以是横向或是纵向.它可以设置成当拖动时连续不断的发送消息,或是只有当拖动结束(用户mouse up)才发送消息.slider还可以设置标尺,把拖动的改变值限制在一个刻度 图 5.5.同时我们还可以创建圆形的slider


两个常用方法

<wbr></wbr>

- (void)setFloatValue:(float)x

移动slide到x位置

<wbr></wbr>

- (float)floatValue

得到当前值(位置)

<wbr></wbr>

-- NSTextField

NSTextField实例对象文本框,能让用户输入单行文本.文本框可以是可编辑(容许输入),也可以是不可编辑.通常不可编辑的文本框就是窗体上的文本标签.相对于button和slider.文本框相对复杂一些,以后我们还会讨论到其中奥秘.图5.6是在Interface Builder里面 NSTextField的属性


我们看到如果当文本框为空的时候,会自动包含了灰色的站位文本

<wbr></wbr>

NSSecureTextField是NSTextField的子类,处理类似密码文本.用户的输入会由*号代替.我们也不能拷贝,剪切其中的文本.

<wbr></wbr>

NSTextField常用方法:

- (NSString *)stringValue

- (void)setStringValue:(NSString*)aString

得到和设置文本框中的文本

<wbr></wbr>

- (NSObject *)objectValue

- (void)setObjectValue:(NSObject *)obj

这些方法得到和设置文本框中任意对象类型数据

当你需要使用formatter时,这会很有帮助.NSFormatter负责把字符串转换为另外的类型.如果没有相关的NSFormatter指定,这些方法会使用对象obj的descripte方法返回的字符串.

举个例子: 我们需要一个文本框来让用户输入日期.我们不希望用户之间输入文本.而是一个NSCalendarDate对象.通过绑定一个NSDateFormatter,可以保证文本框的objectVaule方法返回一个NSCalendarDate对象,而setObjectValue:可以接受一个NSCalendarDate对象,NSDateFormatter会把NSCalendarDate对象转换成我们相应的文本(在23章,我们会创建自己的formatter)

图5.7显示了其他我们可能用到的控件. 试着吧它们拖放到你的window上,看看它们有什么属性.编译运行程序看看它们是怎么响应的吧.


开始SpeakLine例子

<wbr></wbr>

我们来创建一个简单的例子来试着使用控件. 这个例子容许用户在文本框中输入一行文本,然后使用MacOSX的 speech synthesizer来朗读这行文本. 当我们完成它后,样子如图5.8


图5.9是我们将要创建的对象,以及它们的关系图.其中所以一NS为前缀的是cocoaframework中已经有的类.我们创建了AppController类

使用XCode,创建一个Cocoa Applictiaon. 并命名为SpeakLine.

<wbr></wbr>

布局界面 (nib file)

双击MainMenu.nib,打开Interface Builder. 从LibraryWindow中拖处一个文本框和两个按钮.双击文本框,修改文本为"Peter Piper picked a peck ofpickled peppers",(或是你希望朗读的文字.) 把按钮的标题改为 Speak 和 Stop. 如图5.8

<wbr></wbr>

回到XCode, 我们创建一个类: AppController.AppController将是两个按钮的target.每个按钮都会触发一个不同的action方法.编写AppController.h

#import<Cocoa/Cocoa.h>

<wbr></wbr>

@interface AppController : NSObject

{

<wbr><wbr><wbr> IBOutletNSTextField *textField;</wbr></wbr></wbr>

}

- (IBAction)sayIt:(id)sender;

- (IBAction)stopIt:(id)sender;

@end

<wbr></wbr>

在nib文件中创建一个AppController对象: 拖一个蓝色的NSObject正反体到docwindow. 在Identity Inspector中,把他的class设置成AppController. 如图5.10


-- 使用Interface Builder连接

对象连接就像我们做人员介绍: "小明,这是小强".如果你认为小强也有必要知道小明.你会说"小强,这位就是小明." 在InterfaceBuilder中,我们从个某对象拖动到它想知道的那个对象,从而建立连接.

<wbr></wbr>

比如.当用户点击Stop按钮, 按钮发送一个消息给AppController.那么,按钮对象就要"知道"AppController对象.这里,我们从按钮对象Control-Drap到AppController对象.这时会弹出一个面板,我们可以利用它来知道action为stopIt:如图5.11


同样的,我们Conrtrol-drap Speak按钮,设置action为sayIt:

<wbr></wbr>

为了能够朗读文本框中的文字, AppController对象需要得到文本框中的文本.因此,AppController对象有一个指向文本框的指针. Control-Click(按住Control点击鼠标).当outlets列表出现后,从textField拖动到文本框上.如图5.12


到现在,我们设置了几乎所有对象关系图 5.9中的对象连接. 除了speechSynth.它将通过代码而不是Interface Builder来连接.

<wbr></wbr>

-NSWindow的 initialFirstResponder outlet

当我们的程序运行,窗口出现后, 用户如果没有点击文本框,他没有办法输入文本.我们可以设置:当窗口弹出,哪一个view可以接受用户的键盘输入. Control-clickwindow图标.在弹出面板上拖拽initialFirstResponder到文本框

<wbr></wbr>

-- 实现AppController 类

<wbr></wbr>

现在我们来编写一些代码.回到XCode.打开AppController.h文件.给AppController添加一个NSSpeechSynthesizer类型的成员变量:speechSynth

#import<Cocoa/Cocoa.h>

<wbr></wbr>

@interface AppController : NSObject

{

<wbr><wbr><wbr> IBOutletNSTextField *textField;</wbr></wbr></wbr>

<wbr><wbr><wbr><strong>NSSpeechSynthesizer *speechSynth;</strong></wbr></wbr></wbr>

}

- (IBAction)sayIt:(id)sender;

- (IBAction)stopIt:(id)sender;

<wbr></wbr>

@end

<wbr></wbr>

打开AppController.m文件.在这里我们要让程序动起来

#import "AppController.h"

<wbr></wbr>

@implementation AppController

<wbr></wbr>

- (id)init

{

<wbr><wbr><wbr> [superinit];</wbr></wbr></wbr>

<wbr></wbr>

<wbr><wbr><wbr> // Logs canhelp the beginner understand what</wbr></wbr></wbr>

<wbr><wbr><wbr> // ishappening and hunt down bugs.</wbr></wbr></wbr>

<wbr><wbr><wbr>NSLog(@"init");</wbr></wbr></wbr>

<wbr></wbr>

<wbr><wbr><wbr> // Create anew instance of NSSpeechSynthesizer</wbr></wbr></wbr>

<wbr><wbr><wbr> // with thedefault voice.</wbr></wbr></wbr>

<wbr><wbr><wbr> speechSynth= [[NSSpeechSynthesizer alloc] initWithVoice:nil];</wbr></wbr></wbr>

<wbr><wbr><wbr> returnself;</wbr></wbr></wbr>

}

<wbr></wbr>

- (IBAction)sayIt:(id)sender

{

<wbr><wbr><wbr> NSString*string = [textField stringValue];</wbr></wbr></wbr>

<wbr></wbr>

<wbr><wbr><wbr> // Is thestring zero-length?</wbr></wbr></wbr>

<wbr><wbr><wbr> if ([stringlength] == 0) {</wbr></wbr></wbr>

<wbr><wbr><wbr><wbr><wbr><wbr><wbr>NSLog(@"string from %@ is of zero-length", textField);</wbr></wbr></wbr></wbr></wbr></wbr></wbr>

<wbr><wbr><wbr><wbr><wbr><wbr><wbr>return;</wbr></wbr></wbr></wbr></wbr></wbr></wbr>

<wbr><wbr><wbr> }</wbr></wbr></wbr>

<wbr><wbr><wbr> [speechSynthstartSpeakingString:string];</wbr></wbr></wbr>

<wbr><wbr><wbr> NSLog(@"Havestarted to say: %@", string);</wbr></wbr></wbr>

}

<wbr></wbr>

- (IBAction)stopIt:(id)sender

{

<wbr><wbr><wbr>NSLog(@"stopping");</wbr></wbr></wbr>

<wbr><wbr><wbr> [speechSynthstopSpeaking];</wbr></wbr></wbr>

}

@end

<wbr></wbr>

好了,编译运行我们的程序.现在我们可以开始朗读文本,并随时停止朗读

<wbr></wbr>

注意: 一个菜单项(NSMenuItem对象)同样有target和action.我们在这章所讨论的东西一样适合于菜单项.

<wbr></wbr>

思考: 通过代码来设置target

<wbr></wbr>

我们注意到,控件的action是一个selector. NSControl有一个方法:

- (void)setAction:(SEL)aSelector

<wbr></wbr>

那,我们怎么获取一个selector呢? 我们可以使用Objective-C编译指令@selector来查找selector[在那里查找呢.在self中,也就是当前类里面].比如,要设置一个按钮的action为drawMickey:.我们可以这样做

SEL mySelector;

mySelector = @selector(drawMickey:);

[myButton setAction:mySelector];

在编译的时候, @selector(drawMickey:)将会被selectordrawMickey: 来代替.

<wbr></wbr>

如果你要在运行时查找selector.可以使用NSSelectorFromString()

SEL mySelector;

mySelector =NSSelectorFromString(@"drawMickey:");

[myButtonsetTarget:someObjectWithADrawMicke<wbr>yMethod];</wbr>

[myButton setAction:mySelector];

<wbr></wbr>

挑战

<wbr></wbr>

这个练习一定对你意义非凡. 虽然前面在我的指示下能训练完成朗读的例子.这个练习毕竟是你自己完成的.多参考一些前面的例子.你一定行

<wbr></wbr>

创建一个只有一个窗口的程序(不是document-base).图5.13显示的是程序启动后,你没有任何输入时的样子.图5.14显示了,用户作了一些输入后的样子.



当用户输入一些文本,点击按钮. 下面的文本标签将显示输入的文本,并计算出文本的字符数.

<wbr></wbr>

怎么使用Cocoa现有的类去实现这些功能对你很重要.在这个练习中你会认识NSTextField类的两个方法

- (NSString *)stringValue;

- (void)setStringValue:(NSString*)aString;

<wbr></wbr>

同时你也会发现NSString的两个方法很有帮助

- (int)length;

+ (NSString *)stringWithFormat:(NSString*),...;

<wbr></wbr>

你还会创建一个controller对象, 并包含2个outlet和一个action. (恩,虽然有点难,但你一定能完成它. 加油!!)

<wbr></wbr>

<wbr></wbr>

--调试建议

<wbr></wbr>

现在你不再是简单的从书中拷贝代码,而是动手自己编写一些代码了.我想是时候给你一些代码调试的建议了

时刻注意console: 一旦Cocoa对象抛出异常,它会在console中记录相关信息.如果你没注意console.你就不能发现这些错误.

开发过程中使用debug 编译设定: release编译设定移除了一个debuggingsymbol. 因此调试器会没有办法正常调试

<wbr></wbr>

以下是一些常见问题及其解决方法:

<wbr></wbr>

1、没有任何响应: 你可能忘记了在Interface Builder做对象连接.因此,指针为nil.还记得给nil发消息什么都不会做

2、做了连接,还是没有响应: 方法名字是否有拼错. Objective-C是大小写敏感的.所以setFoo: 和 setfoo:是不同的方法. 设置一个断点,看看方法得到调用了么?

3、程序崩溃: 给一个已经释放调的对象发送消息,将会导致你的程序崩溃.(如果你使用了garbagecollector,问题将很难解决). 解决这类问题可能比较难-毕竟,产生问题的对象已经释放掉了.一个方法是设置对象僵化来替代释放.当我们给一个僵化的对象发送消息是,debugger会抛出异常显示一些描述, 比如"You triedto send the message -count to a freed instance of class Fido".并且debugger会停止在发送消息那行.

<wbr></wbr>

在XCode中双击executable . 在Info面板上添加两个环境变量:NSZombiesEnabled为YES. CFZombieLevel 为16. 如图5.15


4、对象没有释放,照样崩溃: 检查你的参数类型. 例如下面就会崩溃

int x = 5;

NSLog(@"x is %@", x);

<wbr></wbr>

5、没法通过Interface Builder做对象连接: 是不是.h 搞错了,是不是忘掉分号: .还是变量定义出错, 比如NSTabView写成NSTableView. 仔细找找看吧.






  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
回答: 在Windows上使用ROS Action控制小乌龟的过程如下: 1. 首先,在功能包目录下创建一个名为"action"的文件夹。可以使用命令"mkdir action"来创建该文件夹。 2. 进入"action"文件夹,可以使用命令"cd action"来进入该文件夹。 3. 创建一个名为"turtleMove.action"的文件,可以使用命令"touch turtleMove.action"来创建该文件。 4. 使用文本编辑器(如gedit)打开"turtleMove.action"文件,并编写以下代码: ``` # Define the goal float64 turtle_target_x # Specify Turtle's target position float64 turtle_target_y float64 turtle_target_theta --- # Define the result float64 turtle_final_x # Define the final position of the turtle float64 turtle_final_y float64 turtle_final_theta --- # Define a feedback message float64 present_turtle_x # Define the current position of the turtle float64 present_turtle_y float64 present_turtle_theta ``` 这段代码定义了一个ROS Action的消息类型,包含了目标位置、最终位置和反馈信息。 通过以上步骤,你已经成功创建了一个ROS Action文件,并定义了相应的消息类型。接下来,你可以在Windows上使用ROS来控制小乌龟,实现目标位置的控制和实时反馈信息的获取。 #### 引用[.reference_title] - *1* *2* [ROS通信模式编程及分布式通信控制小乌龟](https://blog.csdn.net/qq_57160761/article/details/129508062)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control_2,239^v3^insert_chatgpt"}} ] [.reference_item] - *3* [ROS学习-小乌龟解析](https://blog.csdn.net/qq_43419705/article/details/103098756)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control_2,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值