24 de noviembre de 2008

Personalizar Iconos en un JTree


A mas de uno se le habra ocurrido crear un JTree con la necesidad de cambiar los iconos por defecto con los que viene el arbol. Lastimosamente el IDE Netbeans (con el que trabajo frecuentemente) no nos permite hacer mucho (tal igual como lo que pasa con los JTable). En pocas palabras, necesitamos recordar nuestros conceptos de Herencia e Interfaces.


En mi caso particular, me planteo desarrollar un programa de mensajería instantanea (IM), el modelo que presentan los JTree y los arboles en general es muy adecuado para agrupar contactos por estado, por grupo, etc. y decidi usarlo en el proyecto que me encontraba desarrollando. Un aproximado podria ser el siguiente:

Figura 1: Modelo de JTree a crear


Donde, los iconos de contacto1 y contacto2 deben ser de un color distinto al de contacto3 y contacto4, el grupo conectados y no conectados deben tener un mismo icono y bueno, el icono de la raiz no tiene importancia.


CONCEPTOS PREVIOS:


Cuando un JTree necesita pintar los datos que contiene, esta llama a otra para obtener detalles de este dibujado. Esta clase a la que llama se denomina DefaultTreeCellRenderer y proporciona informacion de cada hoja, del icono de cada nodo dentro del arbol.


La clase DefaultTreeCellRenderer implementa a su vez la interfaz TreeCellRenderer , la cual, haciendo uso del metodo getTreeCellRendererComponent devuelve toda la informacion necesaria para dibujar un arbol y sus nodos.


El JTree, en su método getCellRenderer() nos devuelve la clase que está usando para saber cómo dibujar los datos. Con su método setCellRenderer() podríamos pasarle una clase hecha a nuestra medida. Haciendo uso de estos métodos, nosotros podemos redefinir los iconos en el arbol (aunque de una forma muy limitada), haciendo uso de los métodos:



  • setLeafIcon() para cambiar el icono de los nodos hoja (nodos sin hijos).

  • setOpenIcon() para cambiar el icono de los nodos con hijos que están desplegados (sus hijos son visibles).

  • setClosedIcon() para cambiar el icono de los nodos con hijos que están cerrados (los hijos no son visibles).


Sin embargo, esta solucion no permite distinguir entre los hijos de tal o cual padre dentro del arbol (para el caso de los hijos del nodo "Conectados" y los hijos del nodo "No Conectados").


Con todo lo que hemos descrito solo nos quedan dos cosas por hacer: Crear una clase que implemente la interfaz TreeCellRenderer (lo cual implica la construccion completa del metodo getTreeCellRendererComponent, lo cual no es muy adecuado) o crear una clase que herede de la superClase DefaultTreeCellRenderer y sobre la cual hariamos solo algunas modificaciones finales para lograr lo que buscamos.


Asumimos que ya tenemos en el proyecto un objeto instancia de JTree, la cual se denomina "arbol". Lo que haremos es modificar los datos que contiene por defecto para colocar los que en realidad necesitamos para nuestro proposito. Esto lo hacemos con el método remodelarArbol()






public void remodelarArbol(){

//DefaultTreeCellRenderer dt = (DefaultTreeCellRenderer)arbol.getCellRenderer();

DefaultTreeModel def = (DefaultTreeModel)arbol.getModel();

DefaultMutableTreeNode contactos = new DefaultMutableTreeNode("Contactos");

DefaultMutableTreeNode conectados = new DefaultMutableTreeNode("Conectados");

DefaultMutableTreeNode noconectados = new DefaultMutableTreeNode("No Conectados");

DefaultMutableTreeNode c1 = new DefaultMutableTreeNode("Contacto 1");

DefaultMutableTreeNode c2 = new DefaultMutableTreeNode("Contacto 2");

DefaultMutableTreeNode nc1 = new DefaultMutableTreeNode("Contacto 3");

DefaultMutableTreeNode nc2 = new DefaultMutableTreeNode("Contacto 4");

conectados.add(c1);

conectados.add(c2);

noconectados.add(nc1);

noconectados.add(nc2);

contactos.add(conectados);

contactos.add(noconectados);

def.setRoot(contactos);

arbol.setModel(def);

arbol.setCellRenderer(new RendererArbol());//añadimos nuevo renderer

}

Si nos fijamos en la ultima linea, añadimos a nuestro arbol remodelado una nueva clase que se encargará de su dibujado. Esta clase la tenemos que crear y ajustar a nuestras necesidades.



import javax.swing.tree.*;

import javax.swing.*;

import java.awt.*;



/**

*

* @author Jhon Anthony Alvarez Borja

*/

public class RendererArbol extends DefaultTreeCellRenderer{



/** Creates a new instance of RendererArbol */

ImageIcon conectados;

ImageIcon noConectados;

public RendererArbol() {

conectados = new ImageIcon("src/Resources/Pack MSN/Enligne.png");

noConectados = new ImageIcon("src/Resources/Pack MSN/Horsligne.png");

}



public Component getTreeCellRendererComponent(JTree tree, Object value, boolean selected, boolean expanded, boolean leaf, int row, boolean hasFocus)

{

super.getTreeCellRendererComponent(tree,value,selected,expanded,leaf,row,hasFocus);

String val = value.toString();

ImageIcon i;

DefaultMutableTreeNode nodo = (DefaultMutableTreeNode)value;

if(val.compareTo("Conectados")!=0 && val.compareTo("No Conectados")!=0 && val.compareTo("Contactos")!=0){

TreeNode t = nodo.getParent();

if(t!=null){

if(t.toString().compareTo("Conectados")==0){

setIcon(conectados);

}else if(t.toString().compareTo("No Conectados")==0){

setIcon(noConectados);

}

}

}

return this;

}

}






Lo que hacemos en esta clase es implementar el metodo getTreeCellRendererComponent modificando solo lo que nos interesa
lo cual es en este caso los íconos para los usuarios conectados y no conectados. Al hacer uso de la palabra reservada y el metodo super.getTreeCellRendererComponent lo que hacemos es llamar al método original de la clase y lineas abajo, colocamos las modificaciones que queremos hacer al momento de mostrar un determinado nodo en funcion de nuestras necesidades. En este caso basta con identificar el nombre de cada nodo padre, y de acuerdo a ello colocamos el icono correspondiente a conectado o no conectado.

Espero les sea de ayuda este pequeño tutorial. Aca les dejo el codigo fuente




Fuentes:

12 de noviembre de 2008

AUDIO DIGITAL


Actualmente, el avance de la tecnología computacional ha permitido la digitalizacion de la información, entre la que tambien encontramos al sonido. Esta evolucion en el sonido ha implicado la mejora notoria en la calidad de sonido en equipos de alta fidelidad (Hi-Fi), sin embargo el cambio delo analogico a lo digital ha provocado una serie de problemas diversos, los cuales han sido corregidos en su gran mayoria. He aquí una introducción y algunos conceptos indispensables para los interesados en procesamiento de señales y aficionados al audio digital.

En el procesamiento de señales es común encontrar este término, y muchas veces incluso confundido por la mayoría de las personas. En esta sección trataremos de despejar algunas dudas respecto a este término para profundizar mas adelante en el desarrollo de temas más complejos.

La frecuencia de muestreo viene a ser el número de muestras por unidad de tiempo que se toman de una señal continua (analógica) para poder producir una señal discreta (digital), durante el proceso necesario para transformarla de analógica a digital.

La frecuencia de muestreo, como todas las frecuencias, se expresa en Hz (hercios o ciclos por segundo).

Obviamente, la transformación de una onda analógica a digital ocasiona la perdida de información, pero matemáticamente es demostrable la reconstrucción completa de una onda bajo el Teorema de muestreo de Nyquist-Shannon.

TEOREMA DE NYQUIST - SHANNON

Según este teorema, para poder reconstruir con exactitud la forma de una onda, es necesario que la frecuencia de muestreo sea siempre el doble de la frecuencia a muestrear. Atendiendo al margen audible de los humanos, que va desde los 20 Hz hasta los 20 kHz, es necesario que la frecuencia de muestreo sea como mínimo de 40 000 muestras por segundo. Por otro lado, cada bit de una muestra de audio añade 6 dB de margen dinámico (margen que hay entre el nivel de referencia y el ruido de fondo de un determinado sistema, medido en decibelios), por lo que con 8 bits obtendríamos 48 dB, con 16 bits 96 dB y con 24 bits 144 dB. Ahora, como el margen dinámico útil es aproximadamente 110 dB ( sin llegar al umbral del dolor ) y el de los sistemas analógicos de cinta magnética de unos 72 dB, pareció en principio que codificar a 16 bits era una buena alternativa.

La alta tasa de muestreo de otro formato de audio de reciente aparición, el SACD o Super Audio CD, es una consecuencia del uso de una tecnología denominada modulación Sigma-Delta (Direct Stream Digital). Si bien la tasa de muestreo es 64 veces la del CD-Audio, es necesario tener presente que se trata de una cuantificación de 1 bit (en lugar de los 16 empleados en el CD-Audio) y basado en técnicas de Noise Shaping (modelado de ruido). No es posible, por tanto, establecer comparaciones superficiales con el PCM de CD-Audio, ya que en este caso la relación señal-ruido no es constante respecto de la frecuencia (en CD-Audio el ruido de cuantificación es independiente de la frecuencia y sólo depende de los intervalos de amplitud empleados en el proceso de cuantificación, es decir, de unos 98,09 dB constantes para los 16 bits de este estándar CD-Audio en todo el espectro útil).

Un SACD puede registrar y reproducir señales con componentes de hasta 33 kHz con una relación señal-ruido equivalente al de un CD-Audio (aunque 33 kHz está casi una octava por encima del máximo audible y, por tanto, una ventaja sobre el CD-Audio de dudosa utilidad) y mantener una relación señal-ruido de aproximadamente 122 dB para el espectro audible (un potencial, el equivalente aproximado a 20 bits,3 también de dudosa utilidad práctica como formato final de usuario). Entre las ventajas objetivas de estos formatos recientes (DVD-Audio y SACD) se encuentra el potencial multicanal (registro de más de dos canales) y la capacidad para el empleo de técnicas de protección de copia (algo de extraordinario interés para las compañías discográficas). Ninguna prueba doble-ciego realizada en condiciones controladas ha probado que existan diferencias audibles entre estos formatos denominados de "alta resolución"


ALIASING

Todo lo explicado anteriormente es correcto, sin embargo existen sonidos que el oído humano no puede escuchar (aquellos armónicos que se generan más allá de los 20 000 Hz). Cuando digitalizamos una onda con tales armónicos presentes, según Nyquist, se produce el fenómeno de aliasing, el cual convierte esas frecuencias no audibles y las cuales no las percibimos en frecuencias que si lo son y que no se encuentran presentes en el sonido original.

Cito un ejemplo no mío pero muy didáctico: Lo que ocurre cuando vemos la rueda de un carro en una película, aunque el carro va hacia adelante nosotros vemos que los aros de la rueda van en sentido inverso. No hay concordancia entre el periodo de rotación de la rueda y la frecuencia de muestreo del cine (que es de 24 frames/sec). Para evitar este tipo de problemas se tiene que añadir un filtro de paso bajo de corte por encima de la frecuencia que se corresponda con la mitad de frecuencia de muestreo. Este tipo de filtro es conocido como “filtro anti-aliasing”. Para este problema, en los CD’s de audio se estandarizó que la frecuencia de muestreo sea de 44.1 kHz para poder utilizar filtros con una pendiente desde los 22.050 kHz.


FRECUENCIAS TIPICAS DE MUESTREO


Las investigaciones en esta rama continuan, los sistemas de audio tienden a tener mayor capacidad de mustreo y mayor resolucion, asimismo los medios para almacenar esa informacion avanzan a la misma velocidad, con costes mas asequibles a los usuarios.

Continuaremos proximamente con aplicaciones DSP en ondas de audio, espero les haya motivado con esta introduccion a este inmenso campo de procesamiento de señales.

Fuentes:

[1] Los males del audio digital. Jitter, aliasing, errores de cuantización

[2] http://es.wikipedia.org/wiki/Frecuencia_de_muestreo