jueves, 25 de diciembre de 2014

Layouts en Android

image

Los layouts en Android son elementos no visuales que se usan para controlar la distribución, posición y dimensiones de los componentes que se inserten en su interior. En Android podemos usar dos formas para declarar layouts: la primera mediante código java y la segunda a partir de ficheros XML. Si comparamos estas dos formas, es sin dudas la segunda la más factible por varias razones entre las que se encuentra que existen herramientas visuales donde el programador o el diseñador pueden, de manera fácil diseñar una interfaz de usuario, otra ventaja es que es importante en términos de arquitectura tener separado la lógica de la interfaz de usuario de forma tal que si necesitamos hacer algunos ajustes en el diseño de la aplicación no conlleve a hacer grandes cambios en la aplicación.

En Android, los ficheros de layout codificados en XML se consideran recursos y se guardan dentro del directorio res/layout del proyecto.

Cada fichero XML está formado por un árbol de elementos que describen la forma en que los componentes y contenedores se acomodarán para definir la parte visual. Cada uno de estos elementos tiene atributos que se denominan propiedades y son las que describen cómo es que deben verse los elementos y su comportamiento en un contenedor.

Para cada elemento del XML se tendrá un nombre correspondiente a la clase base de ldirectorio Android. Por ejemplo si existe un elemento Button en el XML, se deberá tener un objeto de la clase Button en el código java.

Cuando una aplicación requiere hacer referencia desde el código java a uno de los elementos del XML es importante que el elemento tenga un identificador, la convención a utilizar para darle este valor de id es @+id/[nombreElemento] Este nombre tiene que ser único.

Después de haber diseñado la interfaz de usuario, es necesario compilar la aplicación para que en la clase R.java se genere un id que Android maneja de manera interna y que permite llamar a los ficheros desde el código java.

Dentro del método onCreate() de la actividadse escribirá la sentencia setLayoutView() con el parámetro R.layout.[nombreDelFicheroXML]. Si se quiere hacer uso del elemento en específico se usa el método findViewById() y se le pasa como parámetro el id del componente. Por ejemplo:

btn = (Button) findViewById(R.id.btn);

Con esta breve explicación acerca de los layouts y su definición en un fichero XML tenemos algunos elementos para el diseño de una aplicación Android, aunque sería bueno abundar un poco más en el tema para aprovechar más las potencialidades de estos recursos, de forma tal que hagamos aplicaciones más eficientes.

{ Leer Más }


lunes, 15 de diciembre de 2014

Conociendo la estructura de un proyecto Android.

En los artículos anteriores sobre la programación en Android, vimos una breve reseña de lo que es Android y su historia y luego abordamos cómo preparar nuestro ambiente de desarrollo para estar listos para adentrarnos en este fascinante mundo.

Pero antes de escribir una línea de código es importante conocer la estructura de un proyecto Android de forma que a la hora de programar podamos saber de forma exacta donde encontrar cada recurso.

Cuando creamos un proyecto de Android en Eclipse se crean una serie de carpetas y ficheros para luego generar la aplicación. Esta estructura será común para cualquier tipo de aplicaciones, independientemente de la versión para la que la estemos desarrollando o su complejidad.

A continuación veremos una imagen con la estructura inicial de Android:

image

Para un mejor entendimiento vamos a explicar los elementos principales de esta estructura de ficheros.

Carpeta src

Como su nombre lo sugiere, es en esta carpeta donde estará todo el código fuente de la aplicación, la programación de la interfaz gráfica, clases auxiliares, entre otras. En un inicio se creará el código elemental del Activity principal de la aplicación.

Carpeta res

Es en esta carpeta donde estarán los recursos necesarios que se utilizarán en la aplicación como son los textos, las imágenes, los videos, entre otros. Dentro de la carpeta res existen otras carpetas con el objetivo de organizar aún mejor los recursos.

image

Vamos a dar un breve repaso de lo que son cada una de destas carpetas

drawable: Contiene las imágenes. Se puede dividir en drawable-ldpi, drawable-mdpi y drawable-hdpi. Los recursos se pondrán en cada una de estas carpetas dependiendo de la resolución del dispositivo que esté consumiendo la aplicación.

layout: Contiene la estructura XML de las pantallas de la interfaz gráfica. Esta carpeta se puede subdividir en layout y layaout-land para diferenciar por la orientación del dispositivo.

anim: Contiene las animaciones de la aplicación.

menú: Contiene la definición de los menús de la aplicación.

values: Contiene recursos como son los textos, los estilos, los colores, entre otros.

xml: Contiene los ficheros XML utilizados por la aplicación.

raw: Contiene recursos adicionales que no sean XML.

Carpeta gen

Es en esta carpeta donde se almacena el código que se genera automáticamente al compilar el proyecto. De estos códigos es importante particularizar en el fichero R.java que es la que contiene la clase R que es donde se almacenan una serie de constantes con los ID de los recursos incluidos en la carpeta res. Esto nos posibilita que desde el código podamos acceder fácilmente a estos recursos.

Carpeta assets

En esta carpeta se van a almacenar los ficheros auxiliares. La diferencia de estos ficheros con los de que se pondrán en la carpeta res/draw es que estos últimos se podrán acceder desde el código a través de la ya mencionada clase R mientras que los de la carpeta assets se tendrá que acceder a través de su ruta.

Fichero AndroidManifest.xml

Es un fichero escrito en XML con los aspectos principales de la aplicación. Ahí nos vamos a encontrar el nombre, los componentes que usan, los permisos de la aplicación entre otras cosas.

Esto es lo esencial de la estructura de ficheros de un proyecto Android, conociendo esto, ya estaremos listos para próximamente desarrollar una pequeña aplicación para Android.

{ Leer Más }


martes, 9 de diciembre de 2014

Entorno de desarrollo en android.

clip_image001

En este tutorial aprenderemos cómo preparar el entorno de trabajo para comenzar a hacer aplicaciones para el sistema operativo Android.

Lo primero que debemos hacer es descargar los archivos y programas necesarios:

· IDE Eclipse que se puede descargar desde este link.

· SDK de Android que se puede descargar en la página de Android developers. Este paquete incluye muchas herramientas que nos van a hacer de mucha utilidad a la hora de programar para Android.

· Plugin ADT, que es un plugin para hacer de eclipse un ide ideal para hacer aplicaciones Android.

Primero debemos comprobar que nuestra computadora tienes los requerimientos mínimos que son necesarios para que todo se instale bien. Esto se puede hacer leyéndolo en la página de Android developers.

Si el eclipse que tenemos está en un compactado, lo extraemos y lo ponemos en cualquier carpeta de nuestra PC. Cuando ejecutamos el eclipse lo primero que nos pide es una dirección que es donde el eclipse guardará todos los proyectos que vayamos creando. Si queremos que esto no nos lo pida más pues elegimos la opción de no preguntar de nuevo.

Ahora vamos a encargarnos del SDK. Este puede estar en un compactado o en un ejecutable. Si fuera un compactado pues lo extraemos en lo ponemos en cualquier carpeta de nuestra PC, si es un ejecutable pues en uno de los pasos nos pedirá la dirección de la carpeta hacia donde queremos instalar el SDK.

Es importante señalar que para que todo esto que estamos montando funcione es necesario el entorno Java por lo que busca los instaladores necesarios para que esto esté correctamente instalado en tu PC.

Para instalar el ADT podemos hacerlo de dos formas, la primera es en nuestro Eclipse vamos a la opción Help -> Install New Software… Damos un click sobre el botón Add, en el cuadro de Add Repository escribimos “ADT Plugin” en el campo Name y en Location escribimos la URL https://dl-ssl.google.com/android/eclipse/. Otra forma de hacer esto es descargarndo el plugin de internet y obteniéndolo de alguna otra vía y copiando en la carpeta plugins que está en el directorio donde esta intalado nuestro Eclipse.

Ahora vamos a decirle al Eclipse dónde es que está el SDK de Android. Para esto vamos a la opción Window -> Preferences… ahí seleccionamos la opción Android del panel de la izquierda y seleccionamos Browse… donde elegiremos la carpeta donde pusimos o instalamos nuestro SDK.

Ya casi tenemos listo nuestro entorno de desarrollo, ahora vamos a descargar los componentes esenciales de SDK. El SDK separa los elementos principales en componentes que se pueden instalar por separado. Para poder desarrollar en Android necesitamos al menos descargar una plataforma Android con sus herramientas asociadas por lo que vamos a Windows -> Android SDK and AVD Manager y elegimos Avaible packages en el panel de la izquierda. En el panel de la derecha vamos a poder ver todas las opciones disponibles para descargar.

clip_image002

De este listado podrás seleccionar todas aquellas que quieras. Las plataformas se adaptan a la versión de Android por lo que si su teléfono tiene instalado Android 2.2 pues deberá descargar esta plataforma o una de las inferiores para poder probar las aplicaciones en él. Dependiendo del número de plataformas y componentes que hayas decidido descargarte, esta parte puede tardarte un poco.

Con estos pasos hemos configurado nuestro entorno para comenzar a desarrollar para Android. Ya estamos listo para hacer nuestra primera aplicación. Espero que les haya sido de ayuda.

{ Leer Más }


jueves, 4 de diciembre de 2014

¿Qué es Android?

image

Imagino que en la actualidad hablar de Android debe ser un tema bastante conocido para todos, pero en este artículo trataré de contar en pocas palabras la historia del que se ha convertido en el sistema operativo más usado en los celulares del mundo.

La historia de Android comienza cuando el empresario y desarrollador Andy Rubin se le ocurrió la idea de desarrollar un sistema operativo para celulares basado en la filosofía Open Source, de forma de que fuera de código abierto, adaptable a cualquier hardware y que tuviera un entorno de desarrollo único que permitiera crear aplicaciones que corrieran en el sistema operativo y que cualquier hardware lo soportara.

El sistema operativo Android se compone de aplicaciones que se ejecutan en un framework Java de aplicaciones orientadas a objetos sobre el núcleo de las bibliotecas de Java en una máquina virtual Dalvik con compilación en tiempo de ejecución. Las bibliotecas escritas en lenguaje C incluyen un administrador de interfaz gráfica (surface manager), un framework OpenCore, una base de datos relacional SQLite, una Interfaz de programación de API gráfica OpenGL ES 2.0 3D, un motor de renderizado WebKit, un motor gráfico SGL, SSL y una biblioteca estándar de C Bionic. El sistema operativo está compuesto por 12 millones de líneas de código, incluyendo 3 millones de líneas de XML, 2,8 millones de líneas de lenguaje C, 2,1 millones de líneas de Java y 1,75 millones de líneas de C++.

clip_image002

Android tiene una gran cantidad de desarrolladores que escriben aplicaciones para extender la funcionalidad de los dispositivos. En la actualidad existen más de 1 millón de aplicaciones, de las cuales dos tercios son gratis.

El sistema operativo ha tenido numerosas actualizaciones desde su liberación inicial. Estas actualizaciones básicamente arreglan bugs y agregan nuevas funcionalidades. Es interesante el hecho de que estas actualizaciones se han desarrollado bajo un nombre relacionado con postres en orden alfabético.

Para nosotros los programadores existen muchas oportunidades de poder hacer nuestro aporte a esta gran cantidad de aplicaciones existentes. Las aplicaciones de desarrollan habitualmente en el lenguaje java con Android Software Development Kit (Android SDK) pero existen otras herramientas de desarrollo incluyendo un Kit de Desarrollo Nativo para aplicaciones o extensiones en C, C++, Google App Inventor, un entorno visual para programadores novatos e incluso el Mono for Android, una aplicación para convertir aplicaciones de Windows pone en aplicaciones Android. También es posible usar bibliotecas Qt gracias al proyecto Necesitas SDK.

El desarrollo de aplicaciones para Android no requiere aprender lenguajes complejos de programación, todo lo que hace falta es un conocimiento básico en Java y tener el kit de desarrollo de software (SDK) el cual se puede adquirir gratuitamente.

Con este breve recuento del sistema operativo Android espero que los que todavía no lo conocen se documenten más sobre el mismo y que los desarrolladores de software comiencen a adentrarse en el fascinante mundo del desarrollo de aplicaciones Android.

{ Leer Más }


lunes, 16 de junio de 2014

Lógica de aplicación en QtQuick con JavaScript.

QtQuick es una biblioteca del framework Qt para el desarrollo de aplicaciones con QML. En este artículo veremos cómo implementar la lógica de una aplicación con interfaz gráfica desarrollada en QML mediante un fichero JavaScript, para esto emplearemos un ejemplo desarrollado con la versión 5.2 de Qt. Lo primero que haremos será crear un proyecto de QtQuick. En la pantalla de bienvenida de Qt Creator seleccionamos “New Project”.

clip_image001

Luego en el asistente de nuevo proyecto escogemos la plantilla “Applications” y de las opciones escogemos “Qt Quick Application

clip_image003 clip_image004

Al seleccionar esta plantilla aparece el asistente para llenar los datos del proyecto. Introducimos el nombre, la ubicación, la selección de componentes de Qt Quick (dejamos por defecto Qt Quick 2), el kit de compilación (por defecto) y por último seleccionamos “Finish”. Una vez creado el proyecto se abre el “main.qml” que debe verse así:

clip_image006

Ahora vamos a crear el fichero JavaScript, para esto damos clic derecho en la raíz del árbol del proyecto y seleccionamos “Add new…”. Al abrir el asistente escogemos como plantilla “Qt” y en las opciones seleccionamos “JS File”:

clip_image008 clip_image010

Al abrir el asistente para crear el fichero ponemos el nombre, por ejemplo “logic”, y en la ubicacion seleccionamos la carpeta donde se encuentran los QML del proyecto; esto facilita la localizacion del fichero a la hora de importarlo. Dejamos las opciones por defecto y damos clic en “Finish”. Ya está creado el fichero, ahora vamos a diseñar una inerfaz sencilla para calcular el cuadrado de un número entrado por el usuario. Para esto sustituimos el código del main.qml por esto:

Rectangle {
width: 360
height: 200

Column{
anchors.centerIn: parent
spacing: 10

Rectangle{
anchors.horizontalCenter: parent.horizontalCenter
color: "lightgrey"; width: 40; height: 20

TextInput{
id: inputNum
anchors.fill: parent; color: "black"
font.pointSize: 15.0
}
}

Text { id: result; text: qsTr("El cuadrado es: ")
anchors.horizontalCenter: parent.horizontalCenter }

Text {
text: qsTr("CALCULAR")
anchors.horizontalCenter: parent.horizontalCenter

MouseArea {
anchors.fill: parent
onClicked: {

}
}
}
}
}

Se utiliza un elemento TextInput para obtener la entrada del usuario, un elemento Text para mostrar el resultado y otro para ejecutar el método que implementaremos más adelante. Es importante señalar aquí la propiedad “id” del TextInput y del Text que mostrará el resultado. Esta propiedad es el identificador mediante el cual se puede acceder a todas las propiedades del objeto desde el fichero JavaScript o desde los métodos de los otros elementos en el mismo QML; por eso es necesario que sea único para cada objeto que se declare en el fichero. Ahora, vamos a escribir la directiva que permite importar el fichero JavaScript desde el código QML, en la parte superior del código agregar:

import "logic.js" as Logic

Esto significa que vamos a utilizar los métodos implementados en el fichero “logic.js” a través del id “Logic”. La manera específica de hacerlo lo veremos más adelante. Ahora, abrimos el fichero JavaScript e implementamos el método para calcular el cuadrado del número entrado por el usuario:

function square(num) {
var r = num * num
result.text = "EL cuadrado es: " + r
}

Es un método sencillo, pero lo importante aquí es notar que podemos cambiar la propiedad “text” del elemento Text del fichero QML a través del identificador definido. Lo mismo podemos hacer con cualquiera de sus otras propiedades.

Ya podemos usar el método implementado, para esto vamos al método onClicked del elemento MouseArea y escribimos lo siguiente:

Logic.square(inputNum.text)

Aquí notar dos cosas: primero, que podemos acceder a los métodos del fichero JavaScript a través del id definido en la directiva import como si fuera un objeto y segundo, que desde aquí también podemos acceder a las propiedades de los elementos QML mediante su identificador. En este caso el texto del TextInput.

De esta manera se ha implementado un método sencillo, pero en una aplicación más compleja donde se necesiten más métodos la manera de llamarlos es la misma. Una vez terminado todo solo falta probarlo, para esto compilamos la aplicación y comprobamos que todo funcione bien.

Hasta aquí el artículo de hoy, en resumen hemos visto cómo utilizar los métodos de un fichero JavaScript en el código QML y como acceder a las propiedades de los elementos QML a través de su identificador.

{ Leer Más }


martes, 20 de mayo de 2014

Como colocar Widgets dentro de un QHeaderView usando el framework Qt.

clip_image002

Hace poco tuve la necesidad de lograr colocar QComboBoxes en las secciones dentro de un QHeaderView, en un inicio pensé que esto sería bastante sencillo, ya que podría utilizar el método setViewportMargins () y poner los widgets en la parte superior. Pero por desgracia esto no es posible hacerlo porque los itemviews establecen los márgenes de ventana gráfica y si se establece fuera de este puede causar problemas por lo que no se recomienda este enfoque.

Por lo tanto, la manera de conseguir que funcione es crear los widgets y colocarlos en el QHeaderView directamente, esto significa que tenemos que ajustar de forma manual cuando se cambia el tamaño de las secciones, se mueve o cuando el propio itemview se desplaza [1]. Así que para hacer esto tenemos que empezar por hacer una clase que herede de QHeaderView. En este caso me estoy centrando exclusivamente en una cabecera horizontal, ya que esto hace que sea más sencillo centrarse en lugar de tratar de darse cuenta de las secciones de dirección se están moviendo.

MyHorizontalHeader(QWidget *parent = 0) : QHeaderView(Qt::Horizontal, parent)
{
connect(this, SIGNAL(sectionResized(int, int, int)), this,
SLOT(handleSectionResized(int)));
connect(this, SIGNAL(sectionMoved(int, int, int)), this,
SLOT(handleSectionMoved(int, int, int)));
setMovable(true);
}

Hemos añadido dos slots aquí, uno es para el manejo de una sección cuando se cambia el tamaño y otro para cuando se mueve una sección. Vamos a llegar a ello dentro de poco, pero primero vamos a cubrir la inicialización de los widgets. No podemos hacer esto en el constructor (aunque podríamos hacerlo si sabemos más sobre el itemview que estará asociado, pero para este caso yo quiero ser lo más genérico posible), ya que no sabemos cuántas secciones habrá . Así que el mejor lugar para hacer esto es el método re-implementado showEvent().

void showEvent(QShowEvent *e)
{
for (int i=0;i<count();i++) {
if (!boxes[i]) {
QComboBox *box = new QComboBox(this);
boxes[i] = box;
}
boxes[i]->setGeometry(sectionViewportPosition(i), 0,
sectionSize(i) - 5, height());
boxes[i]->show();
}
QHeaderView::showEvent(e);
}

La matriz de las cajas es sólo un QMap <int, QComboBox *> con el entero que se refiere al índice lógico de la sección de la lista desplegable que está conectada. Con el fin de conseguir la posición correcta en la ventana gráfica para la sección utilizamos sectionViewportPosition () y sectionSize () se usa para obtener el tamaño de la sección. La razón del -5 existe solamente para que yo pueda ver un poco de la parte inferior para permitir el movimiento de las secciones. Si sólo desea ver el divisor para cambiar el tamaño de las secciones puede cambiar a -2.

Lo que queda entonces es reaccionar al cambio de tamaño de las secciones y el movimiento de las secciones con los siguientes slots:

void handleSectionResized(int i)
{
for (int j=visualIndex(i);j<count();j++) {
int logical = logicalIndex(j);
boxes[logical]->setGeometry(sectionViewportPosition(logical), 0,
sectionSize(logical) - 5, height());
}
}
void handleSectionMoved(int logical, int oldVisualIndex, int newVisualIndex)
{
for (int i=qMin(oldVisualIndex, newVisualIndex);i<count();i++){
int logical = logicalIndex(i);
boxes[logical]->setGeometry(sectionViewportPosition(logical), 0,
sectionSize(logical) - 5, height());
}
}

El código es fundamentalmente el mismo en ambos slots, pero el bucle está ajustado para limitar la cantidad de widgets tiene que ser tocado, cuando una sección cambia de tamaño sólo vamos a partir del índice visual de la redimensionada y llegaremos al resto de las secciones después de esa. Cuando se mueve una sección que tenemos que ir desde el primer índice visual accionado hasta el final del headerview, como las otras secciones tendrán que ser actualizados para adaptarse.
Hasta aquí todo bien, lo único que nos queda por hacer ahora es manejar el caso en que el itemview se desplaza ya que tenemos que cambiar la posición de los widgets de nuevo. Como el QHeaderView no ha sido notificado de ninguna manera que esto ocurre ya que está en la ventana que tenemos que conectar este a partir del propio itemview cuando se produce un desplazamiento. Para hacer esto necesitamos re-implementar scrollContentsBy ():

void scrollContentsBy(int dx, int dy)
{
QTableWidget::scrollContentsBy(dx, dy);
if (dx != 0)
horizHeader->fixComboPositions();
}

Lo que no se muestra aquí es el constructor que crea una instancia de la clase MyHorizontalHeader y establece con setHorizontalHeader (). Así que cada vez que el contenido se desplaza en cualquier dirección horizontal llamamos al método fixComboPositions() que hace:

void fixComboPositions()
{
for (int i=0;i<count();i++)
boxes[i]->setGeometry(sectionViewportPosition(i), 0,
sectionSize(i) - 5, height());
}

En efecto es el mismo que hace el método showEvent (), ya que no sabemos cuánto se ha hecho visible u oculta la forma más fácil es ir sobre todos los widgets.
Y ahí lo tienen, un enfoque bastante sencillo de conseguir widgets en un QHeaderView, explicado paso a paso, por lo que debería ser fácilmente adaptable para sus propias necesidades. Es todo por hoy esperamos que esta entrada le sea de utilidad.

{ Leer Más }


martes, 6 de mayo de 2014

QtSerialPort, un módulo de la biblioteca Qt que permite la comunicación a través del puerto serie.

clip_image002

El módulo QtSerialPort es una extensión (módulo add-on) para la biblioteca QT5, proporcionando una interfaz única para el hardware y los puertos serie virtuales [1]. Proporciona las funcionalidades básicas, que incluye la configuración, operaciones entrada / salida (I / O), obtener y establecer las señales de control de la interfaz RS-232.

Las interfaces seriales, debido a su sencillez y fiabilidad, siguen siendo populares en algunos sectores como el desarrollo de sistemas embebidos, robótica, etc.

Con el módulo QtSerialPort, los desarrolladores pueden reducir significativamente el tiempo necesario para implementar aplicaciones Qt que requieren acceso a una interfaz serial.

Historia
QtSerialPort se originó a partir de la biblioteca de terceros QSerialDevice [gitorious.org] (rama 2.0), que se mudó recientemente a un repositorio en https://codereview.qt-project.org/. Esto se hizo para permitir un desarrollo más abierto, y para reunir y coordinar a una comunidad que está interesada en el desarrollo de este módulo.

Funcionalidad
Actualmente, la API de módulo contiene dos clases: QSerialPort y QSerialPortInfo:

QSerialPort es la clase base del módulo y proporciona un conjunto de métodos y propiedades básicas para acceder a los recursos de los puertos serie.
Soporta los siguientes sistemas operativos:

Sistema Operativo

Estado de Sorporte

Nota

Windows NT/2K/XP/Vista/7

SI

Soporte completo

Windows CE

SI

Probado solo en emuladores de las plataformas 5 y 6

Gnu/Linux

SI

Soporte completo

MacOSX

SI

Soporte completo

Otros Unix

SI

Todos los compatibles con POSIX

Symbian1

SI

Parcialmente, probado solo en el emulador

QSerialPortInfo es una clase de ayuda. Proporciona información sobre los puertos serie disponibles en el sistema.

Soporta los siguientes sistemas operativos:

Sistema operativo

Estado de soporte

Nota

Windows NT/2K/XP/Vista/7

SI

Soporte completo (using SetupAPI)

Windows CE

SI

Probado solo en emuladores de las plataformas 5 y 6

Gnu/Linux

SI

Soporte completo (using libudev or simple search in /dev)

MacOSX

SI

Soporte completo

Otros Unix

SI

Todos los compatibles con POSIX (only simple search in /dev)

Symbian2

SI

Parcialmente, probado solo en el emulador

Ver el código fuente

Puesto en marcha recientemente un espejo público del proyecto de repositorio en Gitorious [qt.gitorious.org]

Ahora todo el mundo puede ver libremente y rápidamente los últimos cambios a través de un navegador web.

Obtener el código fuente

Para obtener la instantánea actual del código fuente como un archivo, haga clic en este enlace [qt.gitorious.org].

Para aquellos que quieren usar Git puede ejecutar el siguiente comando:

  1. git clone git://gitorious.org/qt/qtserialport.git

Construcción e Instalación

Construir e instalar desde la línea de comandos

Antes de la construcción usted necesita:

· Instalar Perl 3

· Asegurarse de que las variables de entorno están establecidas correctamente:

- Correctamente especificado la ruta de acceso al Qt4/Qt5 instalado

- Correctamente especificado la ruta para usar el compilador

- Correctamente especificado la ruta de acceso al Perl 3

· Crear un directorio de construcción que esté en el mismo nivel que el directorio con el código fuente

- /

- / SerialPort-src

- / SerialPort-build

Los siguientes son los procedimientos recomendados para la construcción de la biblioteca QtSerialPort en Qt4/Qt5 desde la línea de comandos.

  • cd serialport-build
  • qmake .. / serialport-src/qtserialport.pro
  • make [o 'nmake' para el compilador MSVC o '-make mingw32' para el compilador MinGW]
  • make install [o 'nmake install' para el compilador MSVC o 'mingw32-make install' para el compilador MinGW]

Nota: en los sistemas * nix es posible que se requieran privilegios de superusuario:

sudo make install

Construir e instalar desde la QtCreator

Antes de la construcción de lo que necesita:

  • Instalar el Perl 3 y estar convencido de que ha especificado correctamente la ruta al Perl en un ambiente global.
  • Estar convencido de que las cadenas de herramientas que desee ( kits) del QtCreator se ha configurado correctamente

Los procedimientos recomendados para la construcción de la biblioteca QtSerialPort para Qt4/Qt5 del QtCreator:

  • Descargar y desempaquetar el código fuente de QtSerialPort
  • Ejecutar QtCreator y abra el archivo de proyecto "qtserialport.pro"
  • Llegar a " Projects->(Your Kit)->Build->Build Steps "
  • Añadir un nuevo make "Build Step" y escribir en los " argumentos" (“Make arguments) el objetivo a compilar.
  • Haga clic en el menú "Rebuild Project qtserialport".

Como resultado, la biblioteca QtSerialPort será compilada e instalado automáticamente en la instancia de Qt deseada (depende del Kit seleccionado). El uso QtCreator - la manera más sencilla y rápida de instalación manual de la biblioteca.

Nota: en los sistemas *nix este método puede fallar si Qt fue instalado desde los repositorios en los directorios del sistema. Los privilegios de superusuario podrían ser necesarios para ejecutar "make install".

3 Perl sólo se requiere en el caso de QT5, consulte aquí [qt- project.org]. Al usar Qt4 omita este punto.

//**

Uso

Para utilizar la biblioteca adicione serialport al fichero *.pro de tu proyecto.

Qt4
  1. CONFIG += serialport
Qt5
  1. QT += serialport

Incluya los archivos de cabecera de QtSerialPort donde sea necesario:

  1. ...
  2. #include <QtSerialPort/QSerialPort>
  3. #include <QtSerialPort/QSerialPortInfo>
  4. ...
Ejemplo simple

Debajo se muestra un ejemplo sencillo del main.cpp:

#include <QtCore/QCoreApplication>
#include <QtCore/QDebug>
#include <QtSerialPort/QSerialPort>
#include <QtSerialPort/QSerialPortInfo>
QT_USE_NAMESPACE
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
// Example use QSerialPortInfo
foreach (const QSerialPortInfo &info, QSerialPortInfo::availablePorts()) {
qDebug() << "Name : " << info.portName();
qDebug() << "Description : " << info.description();
qDebug() << "Manufacturer: " << info.manufacturer();
// Example use QSerialPort
QSerialPort serial;
serial.setPort(info);
if (serial.open(QIODevice::ReadWrite))
serial.close();
}
return a.exec();
}

Nota: CONFIG += serial port / QT += serialport debe ser la primera o la segunda linea en tu fichero .pro.

Es todo por hoy esperamos que esta entrada le hay resultado de utilidad.

{ Leer Más }


martes, 29 de abril de 2014

Yii: un excelente framework para PHP

clip_image001

Yii es un framework para PHP, que posee un alto rendimiento, es basado en componentes y permite desarrollar aplicaciones de gran escala rápidamente. Permite la máxima reusabilidad en la programación web y acelera el proceso de desarrollo de software. El nombre Yii (pronunciado /i:/) es por fácil (en inglés: easy), eficiente (en inglés: efficient) y extensible (en inglés: extensible). Yii es un framework totalmente basado en Programación Orientada a Objetos (OOP). Recientemente muchos desarrolladores web que antes usaban el framework de PHP CodeIgniter, se han cambiado a Yii, debido a que la empresa que creó CodeIgniter, llamada EllisLab, ha anunciado que ya no brindara más soporte técnico a CodeIgniter y que está buscando otra empresa que se quiera hacer cargo del desarrollo de este framework [1]. Esto ha provocado que CodeIgniter se haya quedado muy atrás con respecto a otros framework de PHP, entre los cuales se destaca Yii framework [2].

A continuación mencionaremos algunas ventajas de Yii haciendo comparaciones con CodeIgniter:

  1. Generador de código Gii. El cual permite crear plantillas de modelos, vistas, controladores y formularios. La generación de CRUD realmente se destaca, pues solamente tiene que darle el nombre de tabla de base de datos, y se crea el modelo con todos los atributos. Usted sólo define el tipo de datos (numérico, fecha, etc.), si se requiere el campo, y esas reglas son aplicadas siempre cuando intenta guardar / actualizar los datos. En CodeIgniter, es necesario validarlas en cada acción. La diferencia es que CodeIgniter es orientado a los formularios, mientras Yii está orientado a los datos.
  2. Manejo de formularios. Los formularios generados por Gii utilizan campos "activos". Esto significa que cuando algún campo no se valida, yii mostrara el mismo formulario para solucionar el problema con todos los datos rellenados.
  3. Componente Cuadrícula (Grid) HTML. Permite mostrar los datos en forma de tabla con la clasificación automática, la paginación, la coloración de las filas pares e impares, etc.
  4. Integración de jQuery. Esto significa que cosas como el selector de fecha o campos de entrada de auto-completado suelen ser una línea de código PHP y Yii se encarga de generar todo lo que se requiera de código JavaScript, HTML y CSS.
  5. Traducciones. La creación de sitios web multilingües en Yii es realmente fácil. Con CodeIgniter usted tendría que crear su propia forma de hacerlo.
  6. Las relaciones de base de datos. Yii soporta la carga diferida. Esto significa que usted no tiene que escribir JOINs cada vez que necesita obtener un valor de la tabla relacionada (por ejemplo: nombre del autor de un blog). Si usted tiene una instancia de ActiveRecord del blog post como $post, sólo tiene que hacer referencia al nombre del autor así: $post->author->name. Yii sería ejecutar el SQL necesario para conseguirlo.
  7. La consistencia. Yii es una opción mucho mejor si tiene varios desarrolladores trabajando en el proyecto. Yii introduce normas de cómo deben hacerse las cosas, y no hay reinventar la rueda. Esto significa que todos los desarrolladores crearan el código de forma que otros puedan usarlo fácilmente.

Para qué es bueno utilizar Yii?

Yii es un framework genérico de programación Web que puede ser utilizado para todo tipo de aplicaciones Web. Gracias a que es liviano de correr y está equipado con soluciones de cacheo sofisticadas, es adecuado para desarrollar aplicaciones de gran tráfico como portales, foros, sistemas de administración de contenidos (CMS), Sistemas de comercio electrónico (e-commerce), etc.

Cómo se compara Yii con otros frameworks?

Como la mayoría de los frameworks PHP, Yii es un framework MVC (modelo-vista-controlador).Yii sobresale frente a otros frameworks PHP en su eficiencia, su gran cantidad de características y su clara documentación. Yii ha sido diseñado cuidadosamente desde el principio para el desarrollo de aplicaciones de Web. No es ni un subproducto de un proyecto ni un conglomerado de trabajo de terceros. Es el resultado de la vasta experiencia de los autores en desarrollo de aplicaciones Web y de la investigación y la reflexión de los más populares los frameworks de programación Web y aplicaciones.

Esto es todo por hoy, en próximas entradas estaremos profundizando más sobre yii framework.

{ Leer Más }


lunes, 14 de abril de 2014

Ejemplo Mandelbrot, programación concurrente con el Framework Qt.

clip_image002

El ejemplo Mandelbrot, demuestra la programación concurrente usando el Framework Qt, específicamente explica cómo usar un hilo trabajador para realizar operaciones computacionales complejas sin bloquear el hilo principal del ciclo de eventos [1].

clip_image004

El cómputo pesado en el presente ejemplo es el conjunto Mandelbrot, probablemente el fractal más famoso del mundo.

En la vida real, el enfoque aquí descrito es aplicable a una amplia gama de problemas, incluyendo la red de E / S síncrona y acceso a base de datos, donde la interfaz de usuario debe ser capaz de responder mientras una operación pesada está teniendo lugar.

La aplicación Mandelbrot admite zoom y el scroll con el ratón o el teclado. Para evitar la congelación de bucle de eventos del hilo principal (y, en consecuencia, la interfaz de usuario de la aplicación), ponemos todo el cálculo fractal en un subproceso de trabajo independiente. El hilo emite una señal cuando se termina de hacer la representación del fractal.

Durante el tiempo en que el subproceso de trabajo esta recalculando el fractal para reflejar la nueva posición factor de zoom, el hilo principal, simplemente escala el pixmap previamente representado para proporcionar una respuesta inmediata. El resultado no se ve tan bueno como lo que el subproceso de trabajo con el tiempo termina dando, pero al menos hace que la aplicación sea más sensible. En la secuencia de imágenes que a continuación mostramos se ve: la imagen original, la imagen escalada, y la imagen representada nuevamente.

clip_image006clip_image008 clip_image009

Del mismo modo, cuando el usuario se desplaza, el mapa de pixeles anterior se desplaza inmediatamente, revelando las áreas no pintadas más allá del borde del mapa de píxeles, mientras que la imagen se representa por el subproceso de trabajo.

clip_image011 clip_image013 clip_image015

La aplicación consta de dos clases:

· RenderThread es una subclase QThread que representa (render) el conjunto de Mandelbrot.

· MandelbrotWidget es una subclase QWidget que muestra el conjunto de Mandelbrot en la pantalla y permite al usuario las operaciones de ampliar (zoom) y desplazar (scroll).

Definición de la clase RenderThread

Vamos a empezar con la definición de la clase RenderThread:

class RenderThread : public QThread
{
Q_OBJECT

public:
RenderThread(QObject *parent = 0);
~RenderThread();

void render(double centerX, double centerY, double scaleFactor, QSize resultSize);

signals:
void renderedImage(const QImage &image, double scaleFactor);

protected:
void run();

private:
uint rgbFromWaveLength(double wave);

QMutex mutex;
QWaitCondition condition;
double centerX;
double centerY;
double scaleFactor;
QSize resultSize;
bool restart;
bool abort;

enum { ColormapSize = 512 };
uint colormap[ColormapSize];
};

La clase hereda de QThread de esa forma adquiere la capacidad de ejecutarse en un subproceso independiente. Además del constructor y destructor, render () es la única función pública. Cada vez que el hilo realiza la representación de una imagen, emite la señal renderedImage.

La función protegida run () se reimplementada de QThread. Se llama automáticamente cuando se inicia el hilo.

En la sección privada, tenemos un QMutex, un QWaitCondition, y algunos otros miembros de datos. La exclusión mutua protege al otro miembro de datos.

Implementación de la clase RenderThread

RenderThread::RenderThread(QObject *parent)
: QThread(parent)
{
restart = false;
abort = false;

for (int i = 0; i < ColormapSize; ++i)
colormap[i] = rgbFromWaveLength(380.0 + (i * 400.0 / ColormapSize));
}

En el constructor, inicializamos las operaciones de reiniciar y abortar las variables en false. Estas variables controlan el flujo de la función run (). También se inicia arreglo de mapa de colores, que contiene una serie de colores RGB


 

RenderThread::~RenderThread()
{
mutex.lock();
abort = true;
condition.wakeOne();
mutex.unlock();

wait();
}

El destructor se puede llamar en cualquier momento mientras el hilo está activo. Fijamos abortar en true para decirle a run () que debe detener la ejecución tan pronto como sea posible. También hacemos una llamada a QWaitCondition::wakeOne() para despertar el hilo si está durmiendo. (Como veremos cuando examinemos run (), el hilo es puesto a dormir cuando no tiene nada que hacer.)

Lo importante a notar aquí es que run () se ejecuta en su propio hilo (el subproceso de trabajo), mientras que el constructor y el destructor RenderThread (así como la función render ()) son llamados por el subproceso que creó el subproceso de trabajo. Por lo tanto, necesitamos un mutex para proteger los accesos a las variables de abortar y condición, que pueden ser leídas en cualquier momento el método run ().

Al final del destructor, que llamamos QThread::wait() para esperar a que el método run () ha salido antes de invocar el destructor de la clase base.

void RenderThread::render(double centerX, double centerY, double scaleFactor,
QSize resultSize)
{
QMutexLocker locker(&mutex);

this->centerX = centerX;
this->centerY = centerY;
this->scaleFactor = scaleFactor;
this->resultSize = resultSize;

if (!isRunning()) {
start(LowPriority);
} else {
restart = true;
condition.wakeOne();
}
}

La función render () es llamada por el MandelbrotWidget cada vez que necesita generar una nueva imagen del conjunto de Mandelbrot. Los parámetros CenterX, CenterY y scaleFactor especifican la parte del fractal para representar; resultSize especifica el tamaño de la QImage resultante.

La función almacena los parámetros de las variables miembro. Si el hilo no se está ejecutando, se inicia; de otro modo, se establece el reinicio a true (diciendo al run () que debe detener cualquier cálculo sin terminar y empezar de nuevo con los nuevos parámetros) y se despierta el hilo, que podría estar durmiendo.

void RenderThread::run()
{
forever {
mutex.lock();
QSize resultSize = this->resultSize;
double scaleFactor = this->scaleFactor;
double centerX = this->centerX;
double centerY = this->centerY;
mutex.unlock();

La función run() es muy grande, por lo cual se divide en dos partes. El cuerpo de la función es un bucle infinito que se inicia mediante el almacenamiento de los parámetros de representación en variables locales. Como de costumbre, protegemos los accesos a las variables miembro utilizando el mutex de la clase. El almacenamiento de las variables miembro en variables locales nos permite minimizar la cantidad que de código que necesita ser protegido por un mutex. Esto asegura que el hilo principal nunca tendrá que bloquear durante demasiado tiempo cuando se necesita acceder a las variables miembros de RenderThread (por ejemplo, en el render ()). La palabra clave forever, es como foreach, una seudo-palabra clave de Qt.

int halfWidth = resultSize.width() / 2;
int halfHeight = resultSize.height() / 2;
QImage image(resultSize, QImage::Format_RGB32);

const int NumPasses = 8;
int pass = 0;
while (pass < NumPasses) {
const int MaxIterations = (1 << (2 * pass + 6)) + 32;
const int Limit = 4;
bool allBlack = true;

for (int y = -halfHeight; y < halfHeight; ++y) {
if (restart)
break;
if (abort)
return;

uint *scanLine =
reinterpret_cast<uint *>(image.scanLine(y + halfHeight));
double ay = centerY + (y * scaleFactor);

for (int x = -halfWidth; x < halfWidth; ++x) {
double ax = centerX + (x * scaleFactor);
double a1 = ax;
double b1 = ay;
int numIterations = 0;

do {
++numIterations;
double a2 = (a1 * a1) - (b1 * b1) + ax;
double b2 = (2 * a1 * b1) + ay;
if ((a2 * a2) + (b2 * b2) > Limit)
break;

++numIterations;
a1 = (a2 * a2) - (b2 * b2) + ax;
b1 = (2 * a2 * b2) + ay;
if ((a1 * a1) + (b1 * b1) > Limit)
break;
} while (numIterations < MaxIterations);

if (numIterations < MaxIterations) {
*scanLine++ = colormap[numIterations % ColormapSize];
allBlack = false;
} else {
*scanLine++ = qRgb(0, 0, 0);
}
}
}

if (allBlack && pass == 0) {
pass = 4;
} else {
if (!restart)
emit renderedImage(image, scaleFactor);
++pass;
}
}

Luego viene el núcleo del algoritmo. En lugar de tratar de crear una imagen perfecta del conjunto de Mandelbrot, hacemos varias pasadas y generamos más y más precisas (y computacionalmente costosas) aproximaciones del fractal.

Si descubrimos dentro del bucle que la variable restart se ha establecido en true (por render ()), rompemos el bucle de inmediato, por lo que el control vuelve rápidamente a la parte superior del bucle externo (el bucle forever) y buscamos a los nuevos parámetros de renderizado. Del mismo modo, si descubrimos que la variable abort se ha establecido en true (por el destructor RenderThread), salimos de la función de inmediato, terminando el hilo.

El algoritmo principal está más allá del alcance de este tutorial.

        mutex.lock();
if (!restart)
condition.wait(&mutex);
restart = false;
mutex.unlock();
}
}

Una vez que hemos terminado con todas las iteraciones, llamamos QWaitCondition::wait() para poner el hilo a dormir mediante el llamado, a menos que la variable restart tenga valor true. No tiene sentido mantener un subproceso de trabajo en bucle indefinidamente mientras no hay nada que hacer.

uint RenderThread::rgbFromWaveLength(double wave)
{
double r = 0.0;
double g = 0.0;
double b = 0.0;

if (wave >= 380.0 && wave <= 440.0) {
r = -1.0 * (wave - 440.0) / (440.0 - 380.0);
b = 1.0;
} else if (wave >= 440.0 && wave <= 490.0) {
g = (wave - 440.0) / (490.0 - 440.0);
b = 1.0;
} else if (wave >= 490.0 && wave <= 510.0) {
g = 1.0;
b = -1.0 * (wave - 510.0) / (510.0 - 490.0);
} else if (wave >= 510.0 && wave <= 580.0) {
r = (wave - 510.0) / (580.0 - 510.0);
g = 1.0;
} else if (wave >= 580.0 && wave <= 645.0) {
r = 1.0;
g = -1.0 * (wave - 645.0) / (645.0 - 580.0);
} else if (wave >= 645.0 && wave <= 780.0) {
r = 1.0;
}

double s = 1.0;
if (wave > 700.0)
s = 0.3 + 0.7 * (780.0 - wave) / (780.0 - 700.0);
else if (wave < 420.0)
s = 0.3 + 0.7 * (wave - 380.0) / (420.0 - 380.0);

r = pow(r * s, 0.8);
g = pow(g * s, 0.8);
b = pow(b * s, 0.8);
return qRgb(int(r * 255), int(g * 255), int(b * 255));
}

La función rgbFromWaveLength () es una función auxiliar que convierte una longitud de onda a un valor RGB compatible con QImages 32 bits. Se llama desde el constructor para inicializar el arreglo de mapa de colores con colores agradables.

 

Definición de la clase MandelbrotWidget


La clase MandelbrotWidget utiliza RenderThread para dibujar el conjunto de Mandelbrot en la pantalla. Aquí está la definición de la clase:

class MandelbrotWidget : public QWidget
{
Q_OBJECT

public:
MandelbrotWidget(QWidget *parent = 0);

protected:
void paintEvent(QPaintEvent *event);
void resizeEvent(QResizeEvent *event);
void keyPressEvent(QKeyEvent *event);
#ifndef QT_NO_WHEELEVENT
void wheelEvent(QWheelEvent *event);
#endif
void mousePressEvent(QMouseEvent *event);
void mouseMoveEvent(QMouseEvent *event);
void mouseReleaseEvent(QMouseEvent *event);

private slots:
void updatePixmap(const QImage &image, double scaleFactor);
void zoom(double zoomFactor);

private:
void scroll(int deltaX, int deltaY);

RenderThread thread;
QPixmap pixmap;
QPoint pixmapOffset;
QPoint lastDragPos;
double centerX;
double centerY;
double pixmapScale;
double curScale;
};

El widget reimplementa muchos controladores de eventos de QWidget. Además, cuenta con un método slot updatePixmap () que realiza la conexión a la señal renderedImage () del subproceso de trabajo para actualizar la pantalla cada vez que lleguen nuevos datos desde el hilo.

Entre las variables privadas, tenemos hilo de tipo RenderThread y mapa de píxeles, que contiene la última imagen renderizada.

Implementación de la clase MandelbrotWidget

const double DefaultCenterX = -0.637011f;
const double DefaultCenterY = -0.0395159f;
const double DefaultScale = 0.00403897f;

const double ZoomInFactor = 0.8f;
const double ZoomOutFactor = 1 / ZoomInFactor;
const int ScrollStep = 20;

La aplicación se inicia con algunas constantes que vamos a necesitar más adelante.

MandelbrotWidget::MandelbrotWidget(QWidget *parent)
: QWidget(parent)
{
centerX = DefaultCenterX;
centerY = DefaultCenterY;
pixmapScale = DefaultScale;
curScale = DefaultScale;

connect(&thread, SIGNAL(renderedImage(QImage,double)), this, SLOT(updatePixmap(QImage,double)));

setWindowTitle(tr("Mandelbrot"));
#ifndef QT_NO_CURSOR
setCursor(Qt::CrossCursor);
#endif
resize(550, 400);

}

La parte interesante del constructor es la llamada a los métodos qRegisterMetaType() y QObject::connect(). Vamos a empezar con la llamada al connect().

Aunque se parece a una conexión de señal-ranura estándar entre dos QObjects, ya que la señal se emite en un subproceso distinto al subproceso donde vive el receptor, la conexión es efectivamente una conexión en cola (queued connection). Estas conexiones son asíncronas (es decir, no-bloqueantes), significa que el slot será llamado en algún momento después de la declaración emit. Lo que es más, la ranura se invocará en el hilo en el que el receptor vive. Aquí, la señal se emite en el subproceso de trabajo, y la ranura se ejecuta en el hilo GUI cuando el control vuelve al bucle de eventos.

Con conexiones en cola, Qt debe guardar una copia de los argumentos que se han pasado a la señal para que pueda pasarlos a la ranura más adelante. Qt sabe tomar de copia de muchos tipos de C + + y Qt, pero QImage no es uno de ellos. Por tanto, debemos llamar a la función de plantilla qRegisterMetaType() antes de que podamos utilizar QImage como parámetro de conexiones de cola.

void MandelbrotWidget::paintEvent(QPaintEvent * /* event */)
{
QPainter painter(this);
painter.fillRect(rect(), Qt::black);

if (pixmap.isNull()) {
painter.setPen(Qt::white);
painter.drawText(rect(), Qt::AlignCenter, tr("Rendering initial image, please wait..."));
return;
}

En paintEvent(), se comienza por llenar el fondo con negro. Si no tenemos nada aún para pintar (mapa de pixels es nulo), se muestra un mensaje en el widget que pide al usuario que ser paciente y termina la función de inmediato.

    if (curScale == pixmapScale) {
painter.drawPixmap(pixmapOffset, pixmap);
} else {
double scaleFactor = pixmapScale / curScale;
int newWidth = int(pixmap.width() * scaleFactor);
int newHeight = int(pixmap.height() * scaleFactor);
int newX = pixmapOffset.x() + (pixmap.width() - newWidth) / 2;
int newY = pixmapOffset.y() + (pixmap.height() - newHeight) / 2;

painter.save();
painter.translate(newX, newY);
painter.scale(scaleFactor, scaleFactor);
QRectF exposed = painter.matrix().inverted().mapRect(rect()).adjusted(-1, -1, 1, 1);
painter.drawPixmap(exposed, pixmap, exposed);
painter.restore();
}

Si el mapa de píxeles tiene el factor de escala correcto, podemos extraer el mapa de píxeles directamente sobre el widget. De lo contrario, podemos aumentar la escala y traducimos el sistema de coordenadas (coordinate system) antes de sacar el mapa de pixels. Mediante el mapeo inverso rectángulo del widget usando la matriz de pintado a escala, también nos aseguramos de que sólo las áreas expuestas del mapa de píxeles se dibujan. Las llamadas a QPainter::save() y QPainter::restore() aseguran de que todo el proceso de pintado que se realiza después utiliza el sistema de coordenadas estándar.

QString text = tr("Use mouse wheel or the '+' and '-' keys to zoom. "
"Press and hold left mouse button to scroll.");
QFontMetrics metrics = painter.fontMetrics();
int textWidth = metrics.width(text);

painter.setPen(Qt::NoPen);
painter.setBrush(QColor(0, 0, 0, 127));
painter.drawRect((width() - textWidth) / 2 - 5, 0, textWidth + 10, metrics.lineSpacing() + 5);
painter.setPen(Qt::white);
painter.drawText((width() - textWidth) / 2, metrics.leading() + metrics.ascent(), text);
}

Al final del controlador de eventos de pintura, trazamos una cadena de texto y un rectángulo semitransparente en la parte superior del fractal.

void MandelbrotWidget::resizeEvent(QResizeEvent * /* event */)
{
thread.render(centerX, centerY, curScale, size());
}

Cada vez que el usuario cambia el tamaño del widget, llamamos al método render () para comenzar a generar una nueva imagen, con los mismo parámetros: centerX, CenterY y curScale pero con el nuevo tamaño de widget.

Nótese que confiamos en que resizeEvent () se llama automáticamente por Qt cuando el widget se muestra la primera vez para generar la imagen del primer momento.

void MandelbrotWidget::keyPressEvent(QKeyEvent *event)
{
switch (event->key()) {
case Qt::Key_Plus:
zoom(ZoomInFactor);
break;
case Qt::Key_Minus:
zoom(ZoomOutFactor);
break;
case Qt::Key_Left:
scroll(-ScrollStep, 0);
break;
case Qt::Key_Right:
scroll(+ScrollStep, 0);
break;
case Qt::Key_Down:
scroll(0, -ScrollStep);
break;
case Qt::Key_Up:
scroll(0, +ScrollStep);
break;
default:
QWidget::keyPressEvent(event);
}
}

El manejador del evento de presionar una tecla proporciona algunas asociaciones de teclas para el beneficio de los usuarios que no disponen de un ratón. Las funciones zoom () y scroll () serán cubiertos más adelante.

void MandelbrotWidget::wheelEvent(QWheelEvent *event)
{
int numDegrees = event->delta() / 8;
double numSteps = numDegrees / 15.0f;
zoom(pow(ZoomInFactor, numSteps));
}

El controlador de eventos de la rueda es reimplementada para hacer el control nivel de zoom con la rueda del ratón. QWheelEvent::delta() devuelve el ángulo del movimiento de la rueda del ratón, en octavos de un grado. Para la mayoría de los ratones, un paso de la rueda corresponde a 15 grados. Averiguamos cuántos pasos ratón tenemos y determinamos el factor de zoom en consecuencia. Por ejemplo, si tenemos dos pasos de rueda en la dirección positiva (es decir, 30 grados), el factor de zoom se vuelve ZoomInFactor a la segunda potencia, es decir, 0,8 * 0,8 = 0,64.

void MandelbrotWidget::mousePressEvent(QMouseEvent *event)
{
if (event->button() == Qt::LeftButton)
lastDragPos = event->pos();
}

Cuando el usuario pulsa el botón izquierdo del ratón, almacenamos la posición del puntero del ratón en lastDragPos.

void MandelbrotWidget::mouseMoveEvent(QMouseEvent *event)
{
if (event->buttons() & Qt::LeftButton) {
pixmapOffset += event->pos() - lastDragPos;
lastDragPos = event->pos();
update();
}
}

Cuando el usuario mueve el puntero del ratón mientras se mantiene pulsado el botón izquierdo del ratón, ajustamos pixmapOffset para pintar el mapa de pixels en una posición desplazada y se llama al QWidget::update() para forzar un repintado.

void MandelbrotWidget::mouseReleaseEvent(QMouseEvent *event)
{
if (event->button() == Qt::LeftButton) {
pixmapOffset += event->pos() - lastDragPos;
lastDragPos = QPoint();

int deltaX = (width() - pixmap.width()) / 2 - pixmapOffset.x();
int deltaY = (height() - pixmap.height()) / 2 - pixmapOffset.y();
scroll(deltaX, deltaY);
}
}

Al soltar el botón izquierdo del ratón, actualizamos pixmapOffset tal como lo hicimos en un movimiento del ratón y reiniciamos lastDragPos a un valor predeterminado. A continuación, hacemos un llamado de scroll () para representar una nueva imagen para la nueva posición. (El ajuste de pixmapOffset no es suficiente, ya que las áreas reveladas al arrastrar el mapa de píxeles se dibujan en negro.)

void MandelbrotWidget::updatePixmap(const QImage &image, double scaleFactor)
{
if (!lastDragPos.isNull())
return;

pixmap = QPixmap::fromImage(image);
pixmapOffset = QPoint();
lastDragPos = QPoint();
pixmapScale = scaleFactor;
update();
}

El slot updatePixmap () se invoca cuando el subproceso de trabajo ha terminado de representar una imagen. Empezamos comprobando si un lastre está en vigor y no hacemos nada en ese caso. En el caso normal, guardamos la imagen en mapa de pixels e inicializar algunos de los otros miembros. Al final, llamamos QWidget::update() para actualizar la pantalla.

En este punto, uno podría preguntarse por qué usamos un QImage para el parámetro y un QPixmap para el miembro de datos. ¿Por qué no se adhieren a un tipo? La razón es que QImage es la única clase que admite la manipulación de píxeles directa, lo que necesitamos en el subproceso de trabajo. Por otro lado, antes de que una imagen se pueda dibujar en la pantalla, debe ser convertida en un mapa de pixels. Es mejor hacer la conversión de una vez por todas aquí, y no en paintEvent ().

void MandelbrotWidget::zoom(double zoomFactor)
{
curScale *= zoomFactor;
update();
thread.render(centerX, centerY, curScale, size());
}

En el zoom (), se recalcula curScale. Entonces llamamos QWidget::update() para dibujar un mapa de pixels a escala, y le pedimos al subproceso de trabajo para hacer una nueva imagen correspondiente al nuevo valor curScale.

void MandelbrotWidget::scroll(int deltaX, int deltaY)
{
centerX += deltaX * curScale;
centerY += deltaY * curScale;
update();
thread.render(centerX, centerY, curScale, size());
}

scroll() es similar a zoom(), excepto que los parámetros afectados son centerX y CenterY.

 

La función main()


La naturaleza multiproceso de la aplicación no tiene impacto en su función main (), que es tan simple como siempre:

int main(int argc, char *argv[])
{
QApplication app(argc, argv);
MandelbrotWidget widget;
widget.show();
return app.exec();
}

El código fuente de esta aplicación pueden descargarlo desde aquí.

Esto es todo por hoy, esperamos que este artículo le haya sido útil para aprender a usar un hilo trabajador para realizar operaciones computacionales complejas sin bloquear el hilo principal del ciclo de eventos. En próximas entradas de nuestro blog estaremos profundizando sobre otros temas de programación concurrente usando el framework Qt, que permite grandes potencialidades para el desarrollo de aplicaciones con excelente rendimiento.




{ Leer Más }


jueves, 3 de abril de 2014

Utilizando el componente Plupload desde Php para subir múltiples archivos

Plupload, disponible en http://www.plupload.com, es un excelente componente que podemos utilizar para subir múltiples archivos desde una interfaz atractiva y con pocas líneas de código. En este artículo mostraremos como utilizar este componente desde Php y especialmente utilizando CodeIgniter.

La interfaz básica de este componente es como la que se muestra en la siguiente imagen:

image

En su interfaz tenemos dos botones, uno para agregar archivos y otro para comenzar la subida al servidor de los archivos que hemos cargado. Lo primero que tenemos que realizar para poner a funcionar este componente es adicionar correctamente al proyecto los estilos (css), las imágenes y los archivos javascript que utiliza el componente, y que se encuentran disponibles en http://www.plupload.com/download/ .

Después en el código javascript agregamos la siguiente función:

<script type="text/javascript">
// Initialize the widget when the DOM is ready
$(function() {
$("#uploader").plupload({
// General settings
runtimes : 'html5,flash,silverlight,html4',
url : " controller/multiple_upload",
// Maximum file size
max_file_size : '10mb',
chunk_size: '1mb',
// Resize images on clientside if we can
resize : {
width : 200,
height : 200,
quality : 90,
crop: true // crop to exact dimensions
},
// Specify what files to browse for
filters : [
{title : "Image files", extensions : "jpg,gif,png"},
{title : "Zip files", extensions : "zip,avi"}
],

// Rename files by clicking on their titles
rename: true,
// Sort files
sortable: true,
});
});
</script>

Prestar atención al parámetro url que es donde especificaremos a que controller/function se va a invocar para realizar la subida de los ficheros. Existen otros parámetros interesantes como max_file_size para especificar el tamaño máximo de los archivos, filters que especifica qué tipo de archivos permitiremos subir desde el componente, o runtimes donde especificaremos con cuál plataforma queremos que funcione el componente, pudiéndose especificar más de una y se utilizarán la primera que aparezca de la lista definida.

En el código html colocamos el siguiente bloque de contenido, con el cual se visualizará nuestro componente:

<div id="uploader">
<p>Your browser doesn't have Flash, Silverlight or HTML5 support.</p>
</div>

Si el componente no funciona se muestra el error especificado. Al dar clic en el botón “Start Upload” se llamará a la función que hemos especificado para subir cada uno de los archivos. Como en este caso estamos utilizando CodeIgniter podemos utilizar la librería upload para subir archivos que nos proporciona este framework, y la función multiple_upload será como sigue:

public function multiple_upload()
{
$dir_upload = './uploads/';
$config['upload_path'] = $dir_upload;
$config['allowed_types'] = 'jpg|gif|png|avi|zip';
$config['max_size'] = '10240';
$config['overwrite'] = true;

$this->load->library('upload', $config);
if(!file_exists($config['upload_path'])){
mkdir ($config['upload_path'], 0775, true);
}
if ($this->upload->do_upload('file') )
{
//do
}
}

Otro comportamiento que podemos desear para utilizar este componente es no hacer la subida de los archivos a través del botón que se establece para ello, sino por ejemplo cuando enviamos un formulario completo. En este caso no utilizaríamos el botón plupload_start, una de las formas de ocultarlo es modificando el estilo del botón:

<style type="text/css">
.plupload_button.plupload_start
{
display:none;
}
</style>

Y como queremos subir los ficheros justo cuando se envía el formulario (en este caso current_form) tendríamos que especificarlo en el submit del mismo:

$(document).ready(function() {   
$("#current_form").submit(function(e) {
var uploader = $('#uploader').pluploadQueue();
// Files in queue upload them first
if (uploader.files.length > 0) {
// When all files are uploaded submit form
uploader.bind('StateChanged', function() {
if (uploader.files.length === (uploader.total.uploaded + uploader.total.failed)) {
$('form')[0].submit();
}
});
uploader.start();
return false;
}
//Other events
return true;
})
});

Con estos sencillos pasos tendremos una forma sencilla de subir múltiples ficheros utilizando el componente Plupload, ya sea cargándolos de una vez desde la forma básica o utilizando un formulario.

Es todo, espero haya sido de utilidad.

{ Leer Más }


viernes, 28 de marzo de 2014

Principios básicos de la programación concurrente usando el Framework Qt.

clip_image002

El presente artículo tiene el objetivo de mostrar los principios básicos de la programación multi-hilo usando el Framework Qt.

Los hilos permiten hacer que los sistemas ejecuten tareas en paralelo, al igual que procesos. Entonces, ¿cómo se diferencian los hilos de los procesos? Mientras que usted está haciendo los cálculos en una hoja de cálculo, puede haber también un reproductor multimedia que se ejecuta en el mismo escritorio tocando su canción favorita. Aquí hay un ejemplo de dos procesos trabajando en paralelo: uno que ejecuta el programa de hoja de cálculo, una ejecución de un reproductor de música. La multitarea es un término muy conocido para esto. Una mirada más cercana al reproductor de música revela que allí son cosas que suceden en paralelo dentro de un único proceso. Mientras reproductor de música realiza el envío de la música para el controlador de audio, la interfaz de usuario con todas sus campanas y silbatos se actualiza constantemente. Para esto son los hilos - la concurrencia dentro de un único proceso [1].

Entonces, ¿cómo se implementa la concurrencia? Trabajo paralelo en las CPU de un solo núcleo, es una ilusión que es algo similar a la ilusión de imágenes en movimiento en el cine. Para los procesos, la ilusión se produce mediante la interrupción de trabajo del procesador en un proceso después de un tiempo muy corto. A continuación, el procesador se mueve al siguiente proceso. Para cambiar entre procesos, el contador de programa actual se guarda y contador de programa del próximo procesador se carga. Esto no es suficiente, ya que el mismo hay que hacer con los registros y cierta arquitectura y datos específicos del sistema operativo.

Así como una CPU puede alimentar dos o más procesos, también es posible dejar que la CPU ejecute en dos segmentos de código diferentes de un único proceso. Cuando se inicia un proceso, siempre se ejecuta un segmento de código y por lo tanto, se dice que el proceso ejecuta un hilo. Sin embargo, el programa puede optar por iniciar un segundo hilo. Entonces, dos secuencias de códigos diferentes se procesan simultáneamente dentro de un solo proceso. La concurrencia se logra en las CPU de un solo núcleo guardando repetidamente contadores de programa y luego los registros cargan contadores y registros del programa del próximo hilo. No se requiere la cooperación del programa para desplazarse entre los hilos activos. Un hilo puede estar en cualquier estado cuando se produce el cambio al siguiente contexto.

La tendencia actual en el diseño de la CPU es tener varios núcleos. Una aplicación de un único subproceso típico puede hacer uso de un solo núcleo. Sin embargo, un programa con múltiples hilos se puede asignar a varios núcleos, lo que hace que las cosas sucedan de una manera verdaderamente concurrente. Como resultado, la distribución del trabajo a más de un hilo puede hacer que un programa funcione mucho más rápido en las CPU multinúcleo debido a que núcleos adicionales pueden ser utilizados.

Hilo GUI e Hilo Trabajador

Como se mencionó, cada programa tiene un hilo cuando se inicia. Este hilo se llama el "hilo conductor" (también conocido como el "hilo GUI" en aplicaciones Qt). La interfaz gráfica de usuario Qt se debe ejecutar en este hilo. Todos los widgets y varias clases relacionadas, por ejemplo QPixmap, no trabajan en los hilos secundarios. Un subproceso secundario que comúnmente se conoce como un "subproceso de trabajo" ya que se utiliza para descargar el trabajo de procesamiento del hilo principal.

El acceso simultáneo a los datos

Cada hilo tiene su propia pila, lo que significa que cada hilo tiene su propio historial de llamadas y variables locales. A diferencia de los procesos, los hilos comparten el mismo espacio de direcciones. El siguiente diagrama muestra cómo se encuentran los bloques de construcción de los hilos en la memoria. Contador y los registros de hilos inactivos Programa suelen mantenerse en el espacio del núcleo. Hay una copia compartida del código y una pila separada para cada hilo.

clip_image004

Si dos hilos tienen un puntero al mismo objeto, es posible que los dos hilos tengan acceso a dicho objeto al mismo tiempo y esto potencialmente puede destruir la integridad del objeto. Es fácil imaginar las muchas cosas que pueden salir mal cuando dos métodos del mismo objeto se ejecutan simultáneamente.

A veces es necesario para acceder a un objeto a partir de diferentes hilos, por ejemplo, cuando los objetos que viven en diferentes hilos necesitan comunicarse. Desde hilos utilizan el mismo espacio de direcciones, es más fácil y más rápido para hilos para intercambiar datos de lo que es para los procesos. Los datos no tiene que ser serializado y copiado. Pasar punteros es posible, pero debe haber una coordinación estricta de lo que toques de hilo que se oponen. La ejecución simultánea de las operaciones en un objeto debe ser prevenida. Hay varias maneras de lograr esto y algunas de ellos se describen a continuación.

Entonces, ¿Cómo se pueden programar estas aplicaciones de manera segura? Todos los objetos creados en un hilo se pueden utilizar de manera segura dentro de ese hilo, siempre que otros hilos no tengan referencias a los mismos y los objetos no tienen acoplamiento implícito con otros hilos. Tal acoplamiento implícito puede suceder cuando los datos son compartidos entre instancias como miembros estáticos, singletons o datos globales. Debe familiarizarse con el concepto de clase hilo y funciones seguras y de reentrada.

Uso de hilos

Básicamente, existen dos escenarios fundamentales para el uso de los hilos:

  • Hacer un procesamiento más rápido, haciendo uso de los procesadores multinúcleo.
  • Mantener el hilo GUI u otros hilos críticos liberados al descargar el procesamiento de larga duración o el bloqueo de llamadas a otros hilos.


¿Qué Tecnología Qt se debe usar para el manejo de hilos?

A veces quieres hacer algo más que la ejecución de un método en el contexto de otro hilo. Es posible que desee tener un objeto que vive en otro hilo que proporciona un servicio al hilo GUI. Tal vez usted quiere otro hilo que siga con vida para siempre para sondear los puertos de hardware y enviar una señal al hilo GUI cuando algo notable ha sucedido. Qt proporciona diferentes soluciones para el desarrollo de aplicaciones con subprocesos. La solución correcta depende de la finalidad del nuevo hilo, así como el tiempo de vida que se requiere de ese hilo:

Vida útil del hilo

Tarea de Desarrollo

Solución

Una llamada

Ejecutar un método dentro de otro hilo y dejar el hilo cuando termine el método.

Qt proporciona diferentes soluciones:

Escribir una función y ejecutarla con QtConcurrent::run()

Derivar una clase de QRunnable y ejecutarla en el hilo global con QThreadPool::globalInstance()->start()

Derivar una clase de QThread, reimplementar el método QThread::run() y utilizar QThread::start() para ejecutarlo.

Una llamada

Operaciones se han de realizar en todos los hilos de un contenedor. El procesamiento deberá ser realizado utilizando todos los núcleos disponibles. Un ejemplo común es la producción de imágenes en miniatura de una lista de imágenes.

QtConcurrent proporciona la función de mapa () para aplicar las operaciones en cada elemento contenedor, filtro () para la selección de elementos de recipiente, y la opción de especificar una función de reducir para combinar los elementos restantes.

Una llamada

Una operación de larga duración tiene que ser puesto en otro hilo. Durante el curso de procesamiento, información de estado se debe enviar al hilo de la interfaz gráfica de usuario.

Utilice QThread, reimplementar run() y también las signal/slot sean necesarias. Conecte las señales a las ranuras (slots) del hilo GUI utilizando conexiones de señal / ranura (signal/slot) en cola.

Permanente

Tener un objeto de estar en otro hilo y dejar que se realicen diferentes tareas bajo petición. Esto significa que la comunicación hacia y desde el subproceso de trabajo que se requiere.

Derivar una clase de QObject e implementar las ranuras y las señales necesarias, mover el objeto a un hilo con un bucle de eventos de correr y comunicarse con el objeto a través de conexiones de señal / ranura en cola.

Permanente

Tener un objeto que vive en otro hilo, dejar al objeto realizar tareas repetitivas, como el sondeo de puertos y permitir la comunicación con el hilo GUI.

Igual que el anterior, pero también utilizar un temporizador en el subproceso de trabajo para implementar la encuesta. Sin embargo, la mejor solución para la encuesta es evitarla por completo. A veces, usar QSocketNotifier es una alternativa.

En próximas entradas estaremos profundizando sobre otros temas de programación concurrente usando el framework Qt.

{ Leer Más }


IconIconIcon