Añadir tracking del ratón en controles Cocoa

febrero 23, 2010

A veces necesitamos controlar cuando el ratón entra en alguno de los controles de nuestra aplicación. Para ello tenemos varias alternativas, quizás la mas sencilla pasa por utilizar el mensaje addTrackingRect que admite cualquier control Cocoa ( ya que este mensaje es de la clase NSView, y todos los controles son subclases de vista )

Los parámetros de dicho mensaje son, el rectángulo que delimitará el area de seguimiento, el propietario de dicho tracking, es decir, el que recibirá las llamadas a los mensajes mouseEntered y mouseExited, y los dos últimos parámetros consisten en un espacio para pasar un dato de usuario que luego se nos pasará en la llamada al mensaje que procesa el evento, y un valor booleano que indica si se lanza un evento cuando se establece la zona de seguimiento y el ratón ya estaba dentro de dicha zona.

Aparte del propio mensaje al control, debemos establecer que queremos recibir eventos de ratón, cosa que hacemos enviando el mensaje setAcceptsMouseMovedEvents a la ventana de nuestra aplicación. Y por supuesto debemos añadidir los dos métodos que gestionarán los eventos.

[window setAcceptsMouseMovedEvents:YES];
[myControl addTrackingRect:[myControl bounds]
                     owner:self
                  userData:nil
              assumeInside:NO];

/*...*/
- (void) mouseEntered:(NSEvent *) theEvent
{
	NSLog(@"mouse Entered");
}

- (void) mouseExited: (NSEvent *) theEvent
{
	NSLog(@"mouse Exited");
}

A partir de OSX 10.5 existe otro método para hacer esto mismo. Esto es usando el mensaje addTrackingArea a la misma vista.

El párametro que admite este mensaje es de tipo NSTrackingArea, que podemos construir usando la llamada initWithRect:options:owner:userInfo.

Estos parámetros son similares a lo anterior, sin embargo, la novedad y el beneficio están en las opciones. Típicamente las opciones son:

  • NSTrackingMouseEnteredAndExited, NSTrackingMouseMoved NSTrackingCursorUpdate, para indicar que tipo de eventos queremos que se nos notifiquen.
  • NSTrackingActiveWhenFirstResponder, NSTrackingActiveInKeyWindow, NSTrackingActiveInActiveApp y NSTrackingActiveAlways, que indica cuando se enviaran los mensajes.
  • NSTrackingAssumeInside, NSTrackingInVisibleRectNSTrackingEnabledDuringMouseDrag, que nos sirven para indicar varios aspectos configurables.

El código que cubre esta parte resultaría en:

[window setAcceptsMouseMovedEvents:YES];
[myControl addTrackingArea:
    [[NSTrackingArea alloc] initWithRect:[myControl bounds]
								 options:
                   NSTrackingMouseEnteredAndExited | NSTrackingMouseMoved | NSTrackingActiveAlways
								   owner:self
								userInfo:nil
	]
];

/*...*/
- (void) mouseEntered:(NSEvent *) theEvent
{
	NSLog(@"mouse Entered");
}

- (void) mouseExited: (NSEvent *) theEvent
{
	NSLog(@"mouse Exited");
}

- (void)mouseMoved:(NSEvent *)theEvent
{
	NSLog(@"mouse Exited");
}

Como vemos el último método analizado es mas flexible, pues nos permite especificar mejor lo que queremos capturar, sin embargo perderiamos compatibilidad con versiones anteriores a Leopard. En nuestras manos queda la decisión de que método elegir.


Construyendo una aplicación Cocoa sin Interface Builder

febrero 15, 2010

Inmediatamente después de leer el título de la entrada os preguntareis, y ¿por qué no usar el Interface Builder para hacer una aplicación Cocoa?. Realmente el uso de esta herramienta nos ayuda en gran medida a la hora de realizar las interfaces gráficas de nuestro programa. ¿Entonces?.

La razón de esta entrada es que no todo el mundo puede usar esta herramienta, en muchas ocasiones nos olvidamos de que no todos los usuarios de ordenador interactuamos con él de la misma manera, no todos podemos usar los métodos  habituales de entrada, esto es, el habitual teclado y ratón, y para este tipo de usuarios, usar ciertas herramientras puede constituir una barrera de uso muy alta. Como supongo que ya habreis adivinado me estoy refiriendo a personas que como Tyflos, tienen la desgracia de no poder usar su vista.

Desarrollar una aplicación sin Interface Builder es una tarea compleja, pero perfectamente posible, todos los controles y el comportamiento puede ser realizado en código.

Así que pongamonos manos a la obra ;), lanzamos el XCode y debemos crear un proyecto de tipo Cocoa. Este proyecto nos creará el esqueleto de una aplicación Cocoa incluyendo el fichero que habitualmente usamos para editarlo con el Interface Builder, cuyo nombre es MainMenu.xib. Aunque no vayamos a usarlo, es la forma mas comoda de crear la estructura de un proyecto Cocoa, ya que automaticamente se nos incluirán las dependencias con los frameworks necesarios y el código de inicialización de la aplicación. Es posible hacerlo desde un proyecto creado como “vacio” pero tendriamos que añadir los frameworks y este código inicial.

Una vez que tenemos el proyecto creado, nos vamos directamente al fichero “NombreDeLaAplicacion”AppDelegate.m que se nos ha creado. En este fichero tendremos el delegate de la aplicación, este delegate recibirá la llamada del mensaje “applicationDidFinishLaunching:(NSNotification*)”, cuando la aplicación este lista para funcionar.

En esta entrada, vamos a hacer algo muy sencillo pero que sienta las bases para seguir incorporando controles. El objetivo es poner un cuadro de texto, un botón, y una etiqueta para que nos muestre un mensaje. Además, de cara al objetivo de la entrada, la aplicación nos narrará cuando el botón esta seleccionado o el ratón esta encima de él, y también nos dirá cuando cambie el contenido de la etiqueta.

Lo primero que tenemos que hacer es declarar los componentes necesarios  para nuestra aplicación, para ello vamos al fichero .h y declaramos una vista, un botón, y dos campos de texto. Recordemos que las etiquetas son campos de texto. Además tambien declararemos el motor de sintetizador de voz y una función que nos ayudará a calcular la posición de donde pintar el control. Por lo tanto nuestro .h quedará así:

@interface PruebaNOIB2AppDelegate : NSObject <NSApplicationDelegate> {
    NSWindow *window;
	NSView *view;
	NSButton *mybutton;
	NSTextField *mytf, *mylabel;
	NSSpeechSynthesizer *spch;
}

- (NSRect) calculatePosition: (NSView *) referingView
					 xoffset: (int) xoff
					 yoffset: (int) yoff
					   width: (int) wdth
					  heigth: (int) hght;

@property (assign) IBOutlet NSWindow *window;

@end

Como no vamos a usar Interface Builder, debemos inicializar y posicionar dinámicamente todos los controles, para ello empezamos creando la vista:

view = [[NSView alloc] initWithFrame:NSMakeRect(0,
						0,
						[window contentMaxSize].width,
						[window contentMaxSize].height)];

El siguiente paso será crear el botón, y posicionarlo. Además le indicaremos que nosotros somos el Target de su Action, para que cuando se pinche sobre el botón, nos llegue a nosotros el mensaje.

mybutton = [[NSButton alloc] initWithFrame: NSMakeRect(50, 50, 100, 20)];
[mybutton setButtonType:NSToggleButton];
[mybutton setTarget:self];
[mybutton setAction:@selector(onButtonPressed)];

Para acabar con la creación de la interfaz, inicializaremos los botones y los posicionaremos:

mytf = [[NSTextField alloc] initWithFrame:
			[self calculatePosition:mybutton xoffset:10 yoffset:0 width:100 heigth:20]];

mylabel = [[NSTextField alloc] initWithFrame:
		   [self calculatePosition:mytf xoffset:10 yoffset:0 width:100 heigth:20]];
[mylabel setEditable:NO];
[mylabel setSelectable:NO];
[mylabel setDrawsBackground:NO];

Como vemos, utilizamos el método “calculatePosition” para calcular la posición del control dependiendo de la vista que le pasamos como paremtros. Todas las vistas tienen un mensaje llamado “frame” que devuelve un NSRect del que podemos sacar tanto el punto de inicio como el tamaño de la vista. Todos los controles de Cocoa son realmente vistas, de hecho una interfaz es un “arbol” de vistas. Por lo tanto lo que tenemos que hacer es añadir a la vista padre todas las subvistas de nuestros controles.

[view addSubview:mybutton];
[view addSubview:mytf];
[view addSubview:mylabel];

Finalmente le indicamos a la ventana, que la vista principal es la nuestra:

[window setContentView:view];

Antes de terminar con este método, tenemos que inicializar el motor de habla, e indicar que queremos capturar los eventos de ratón sobre nuestro botón. Esta forma de capturar los eventos bien puede ser una nueva entrada del blog 🙂

spch = [[NSSpeechSynthesizer alloc] initWithVoice:nil];
[window setAcceptsMouseMovedEvents:YES];
[mybutton addTrackingRect:[mybutton bounds] owner:self userData:nil assumeInside:NO];

Como vemos, el código es bastante sencillo. Hacer una interfaz grande es muy laborioso, pero no complejo. Para que al pinchar sobre el botón, el texto de la etiqueta cambie, debemos implementar el action del botón. En nuestro caso:

- (void) onButtonPressed
{
	[mylabel setStringValue:[mytf stringValue]];
	[spch startSpeakingString:[NSString stringWithFormat:@"Label Value: %@",[mytf stringValue]]];
}

En este método también vemos el uso sencillo del motor de habla, solo debemos mandarle un mensaje “startSpeakingString:” a nuestro objeto NSSpeechSynthesizer. Finalmente debemos implementar los mensajes que atienden a los eventos de ratón sobre nuestro botón:

- (void) mouseEntered:(NSEvent *) theEvent
{
	[spch startSpeakingString:@"Mouse Entered"];
}

- (void) mouseExited: (NSEvent *) theEvent
{
	[spch startSpeakingString:@"Mouse Exited"];
}

Con todo esto, ejecutamos y la aplicación deberia funcionar sin problemas.

Como decía al inicio de esta entrada, el ejemplo es muy sencillo, pero pone las bases para seguir investigando en como realizar las aplicaciones de esta forma.


Construyendo un contador binario con Arduino

febrero 7, 2010

Del poco tiempo que tengo ultimamente para hacer cosillas, el Viernes por fin pude ir a la tienda de electrónica para comprar materiales :).

Básicamente compre una placa de prototipos, unos leds con resistencias y un LDR ( Resistencia variable según la luz ). De momento dejaremos el LDR para otra entrada y centremonos en las bombillas.

La conexión del circuito es bastante sencilla, simplemente enchufamos la salida digital del arduino al lado positivo del LED poniendo entre medias una resistencia, mientras que el lado negativo de cada LED iría al la conexión de tierra de la placa Arduino.

Como veis en el esquema, nada complicado 😉

Montandolo en la placa de prototipos, a mi me ha quedado algo así:

Una vez tenemos el circuito montado, viene la parte de la programación, antes de nada hay que descargarse el genial entorno de programación de la gente de Arduino. Os lo podeis descargar desde aquí.

El código en cuestión sería algo como:

int outPin[] = {13, 12, 11, 10, 9};

int delayValue = 600;

void setup()
{
  int i = 0;
  for ( i = 0; i < 5; i++)
    pinMode(outPin[i], OUTPUT);
}

void loop()
{

  int i = 0, j=0;

  for ( i = 0; i < 32; i++)
  {
     for ( j = 0; j < 5; j++)
     {
       if ( ( (i >> j) & 1 )  == 1 )
           digitalWrite(outPin[j], HIGH);
       else digitalWrite(outPin[j], LOW);
     }
     delay(delayValue);
  }
}

Como veis es bastante sencillo, declaramos un array con el conjunto de pines a los cuales estan enganchados nuestros LEDs. En la función setup, los configuramos en modo salida.

La chicha esta en la función loop(), esta se ejecutará una y otra vez, lo que hacemos es iterar por los 32 numeros que podemos mostrar, ya que tenemos 5 LEDs, y 2 elevado a 5 son 32. Por cada número, encedemos o apagamos cada led correspondiente. La clave esta en el bucle que realiza la operación “(i >> j) & 1”. Cada iteración del bucle divide el numero en cuestion por 2 y se queda unicamente con el bit de menor valor. Asi sabemos si tenemos que encender o no el LED.

Por ejemplo, para mostrar el número 21, que en binario es (10101) deberiamos encender los LEDs 0, 2 y 4 y dejar apagados el resto. El calculo sería:

  • LED 0: 21 (10101 & 1) -> 1 , encendido
  • LED 1: 21 / 2 = 10 (01010 & 1) -> 0, apagado
  • LED 2: 21 / 4 = 5 (00101 & 1) -> 1, encendido
  • LED 3: 21 / 8 = 2 (00010 & 1) -> 0, apagado
  • LED 4: 21 / 16 = 1 (00001 & 1) -> 1, encendido

Como vemos, el resultado del calculo es correcto. Básicamente lo que se hace es ir desplazando los bits hacia la derecha y quedandonos con el bit menos significativo.

Bueno, pues dada la maravillosa sencillez que caracteriza al Arduino, solo nos queda darle al boton de Upload, y veremos, como las lucecitas empiezan a encenderse.

Os dejo un video de mi resultado 🙂