NovaMonoFix
Errores PHP
X
Usuario
Password
0 FPS

Tutorial WINAPI C++ 1.6 (Creación del ObjetoMarcador)

14 de Abril del 2010 por Josep Antoni Bover, 0 visitas, 0 comentarios, 0 votos
Categorías : Windows, Programación, C y C++.
La web está en segundo plano, animación en pausa.
Cargando animación...
Tutorial WINAPI C++ 1.6 (Creación del ObjetoMarcador)

A partir de todo lo que vimos en el capitulo anterior la cosa esta en echarle imaginación. Ahora nos toca hacer un marcador para la calculadora que nos mostrara las operaciones y el resultado. La idea es hacer una parte para ver las operaciones que se están evaluando, y otra parte para mostrar el resultado actual de la operación.

Ademas este objeto sera el encargado de apuntar y resolver las operaciones de la calculadora, por lo que tambien se mostrara como responder a pulsaciones del teclado.

Al querer mostrar las operaciones independientemente del resultado nos va a hacer falta ingeniar un sistema para almacenar dichas operaciones con el mismo orden en que se han introducido.

Archivo : ObjetoMarcador.h
1
2
3
4
5
6
7
8
9
// Enumeracion de los tipos de datos para la calculadora
enum ObjetoMarcador_TipoDatos {
TipoDatos_INDEFINIDO = -1,
TipoDatos_Valor = 0,
TipoDatos_Suma = 1,
TipoDatos_Resta = 2,
TipoDatos_Multiplicacion = 3,
TipoDatos_Division = 4
};

Lo siguiente será crear un objeto que simbolice tanto un valor como una operación, pero ojo solo puede ser o un valor, o una operación. La idea es tener una lista de objetos que se irá ampliando a medida que se añadan valores o operaciones. De esta forma será fácil recrear la operación que se está evaluando en la calculadora.

Archivo : ObjetoMarcador.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
// Contenedor de una porción de datos de la calculadora
class ObjetoMarcador_Datos {
public: /////////////////////////// Miembros publicos
// -Constructor por defecto
ObjetoMarcador_Datos(void) {
_Tipo = TipoDatos_INDEFINIDO;
_Valor = 0.0f;
};
// -Constructor asignador de datos
ObjetoMarcador_Datos(const ObjetoMarcador_TipoDatos nTipo,
const double nValor = 0.0f) {
_Tipo = nTipo;
_Valor = nValor;
};
// -Destructor
~ObjetoMarcador_Datos(void) { };
// -Función que retorna el tipo
inline ObjetoMarcador_TipoDatos Tipo(void) const { return _Tipo; };
// -Función que retorna el valor
inline double Valor(void) const { return _Valor; };
private : ///////////////////////// Miembros privados
// -Valor
double _Valor;
// -Tipo de dato
ObjetoMarcador_TipoDatos _Tipo;
};

Esta clase tiene un constructor para indicarle que tipo de operación es, o en el caso de que sea un valor especificaremos el tipo correctamente y le pasaremos el valor en el segundo parámetro. Por lo demás todos sus miembros serán privados y solo podremos acceder a ellos para lectura.

Ahora que tenemos una implementación para almacenar los datos de la calculadora podemos proceder a crear la clase que va a contener el objeto marcador. Este objeto no requerirá eventos de mouse ni de teclado, aunque necesitaremos re-emplazar el Evento_Pintar , hacer unas funciones Pintar y Repintar, hacer unas funciones para que desde la ventana principal de la calculadora podamos añadir valores y operaciones, definir una lista que contenga todos los valores y operaciones, y hacer una función que calculara las operaciones y mostrara el resultado.

Archivo : ObjetoMarcador.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
// Clase que hereda ObjetoControl y se convierte en el marcador para nuestra calculadora
class ObjetoMarcador : public ObjetoControl {
public : /////////////////////////////// Miembros publicos
// -Constructor
ObjetoMarcador(void);
// -Destructor
~ObjetoMarcador(void);
// -Función para crear el control
HWND CrearMarcador( HWND hWndParent, const int cX, const int cY,
const int cAncho, const int cAlto,
COLORREF nColor_Texto = RGB(255, 255, 255),
COLORREF nColor_DegradadoSuperior = RGB(180, 180, 180),
COLORREF nColor_DegradadoInferior = RGB( 60, 60, 60),
COLORREF nColor_BordeExterno = RGB(128, 128, 128),
COLORREF nColor_BordeInterno = RGB(190, 190, 190) );
/////////////////////////////////////// Funciones de pintado
// -Función que enlaza con el evento WM_PAINT
LRESULT Evento_Pintar(HDC hDC, PAINTSTRUCT &PS);
// -Función que pinta todo el control
void Pintar(HDC hDC);
// -Función que repinta el control si es necesario
void Repintar(void);
/////////////////////////////////////// Funciones para los datos
// -Función que agrega un numero al valor actual
void AgregarNumero(const TCHAR Numero);
// -Función que agrega una coma decimal al valor actual
void AgregarPunto(void);
// -Función que borra el último caracter introducido
void BorrarCaracter(void);
// -Función que valida el valor actual, y agrega una operacion a la lista
void AgregarOperacion(const ObjetoMarcador_TipoDatos nTipo);
// -Función que imprime el resultado y borra la lista
void Resultado(void);
// -Función que borra la lista de operaciones y el valor actual
void CE(void);
/////////////////////////////////////// Aspecto grafico
// -Macro que crea funciones para asignar y obtener el COLORREF _Color_Texto
AGREGAR_COLOR(Color_Texto);
// -Macro que crea funciones para asignar y obtener el COLORREF _Color_DegradadoSuperior
AGREGAR_COLOR(Color_DegradadoSuperior);
// -Macro que crea funciones para asignar y obtener el COLORREF _Color_DegradadoInferior
AGREGAR_COLOR(Color_DegradadoInferior);
// -Macro que crea funciones para asignar y obtener el COLORREF _Color_BordeExterno
AGREGAR_COLOR(Color_BordeExterno);
// -Macro que crea funciones para asignar y obtener el COLORREF _Color_BordeInterno
AGREGAR_COLOR(Color_BordeInterno);
// -Macro que crea la función Fuente_Desactivada, y la variable HFONT _Fuente_Operaciones
AGREGAR_FUENTE(Fuente_Operaciones);
// -Macro que crea la función Fuente_Desactivada, y la variable HFONT _Fuente_Resultado
AGREGAR_FUENTE(Fuente_Resultado);
protected : //////////////////////////// Miembros privados
// -Función para quitar ceros que sobren a la izquierda
size_t _FormatearStr(TCHAR *String);
// -Función que borra todos los datos de la lista
void _BorrarLista(void);
// -Vector que contiene los valores y operaciones
std::vector<ObjetoMarcador_Datos *> _Lista;
// -Valor que indica si se ha introducido la coma decimal en el valor actual
bool _Punto;
// -Valor actual en string
TCHAR _ValorActual[256];
// -Tamaño del valor actual en caracteres.
size_t _TamValorActual;
};

Pensad que los valores los introduciremos vía teclado o con los botones, y la única forma de saber cuándo se ha terminado de introducir un valor es cuando se añade una operación.
Por ejemplo pulsamos el botón 1, el botón 2, el botón 3, y el botón +. Hasta que no se ha pulsado el botón + no podemos saber si el valor esta completo, en vez de pulsar el botón + se podría pulsar el botón 4, con lo que el valor en vez de ser 123 seria 1234. Por ello tenemos la función AgregarNumero la cual ira almacenando los números que se pulsen, la función AgregarPunto que agregara una coma decimal, y la función AgregarOperacion que añadirá el valor y la operación a la lista.

En el tutorial anterior creamos y pintamos el control de una forma similar, así que omitiré las clases para colores y fuentes, la función CrearMarcador y las funciones de repintado.

Ahora pasemos a ver el núcleo de las operaciones para la calculadora, empezaremos por la función AgregarNumero :

Archivo : ObjetoMarcador.cpp
1
2
3
4
5
6
7
8
// Función que agrega un numero al valor actual
void ObjetoMarcador::AgregarNumero(const TCHAR Numero) {
if (Numero == TEXT('0') && _TamValorActual == 0) return;
_ValorActual[_TamValorActual] = Numero;
_TamValorActual ++;
_ValorActual[_TamValorActual] = TEXT('\0');
Repintar();
};

Esta función básicamente agrega un numero al string que contiene el valor actual, lo único destacable es que si el numero que se va a agregar es 0 y no hay caracteres en el valor actual, este valor no se añadirá ya que los ceros a la izquierda no nos sirven de nada. Ahora veamos la función AgregarPunto :

Archivo : ObjetoMarcador.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// Funcion que agrega una coma decimal al valor acutal
void ObjetoMarcador::AgregarPunto(void) {
if (_Punto == true) {
MessageBeep(-1);
return;
}
// Si no hay nada agregamos un cero
if (_TamValorActual == 0) {
_ValorActual[_TamValorActual] = TEXT('0');
_TamValorActual ++;
}
_ValorActual[_TamValorActual] = TEXT('.');
_TamValorActual ++;
_ValorActual[_TamValorActual] = TEXT('\0');
_Punto = true;
Repintar();
};

En esta función lo primero que hacemos es mirar el valor _Punto, ya que si esta en true quiere decir que ya hay una coma decimal en el valor actual, y por lo tanto no debemos añadir más. En segundo lugar, si no hay ningún carácter añadiremos un cero al valor actual. Y por último se añade un punto al valor, y se asigna _Punto a true. Veamos la función AgregarOperacion :

Archivo : ObjetoMarcador.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// Función que agrega una operación
void ObjetoMarcador::AgregarOperacion(const ObjetoMarcador_TipoDatos nTipo) {
// Convertimos el _ValorActual a un numero decimal
double nValor = _wtof(_ValorActual);
// Si el valor es 0 no agregamos nada
if (nValor == 0.0f) return;
// Añadimos el valor a la lista
ObjetoMarcador_Datos *nDatosValor = new ObjetoMarcador_Datos(TipoDatos_Valor, nValor);
_Lista.push_back(nDatosValor);
// Añadimos la operacion a la lista
ObjetoMarcador_Datos *nDatosOperacion = new ObjetoMarcador_Datos(nTipo);
_Lista.push_back(nDatosOperacion);
_ValorActual[0] = TEXT('0');
_ValorActual[1] = TEXT('\0');
_TamValorActual = 0;
_Punto = false;
Repintar();
};

Lo primero es determinar si el valor no es cero para añadirlo a la lista. Si el valor es válido creamos una clase ObjetoMarcador_Datos en la que almacenamos el valor actual, luego creamos otra clase ObjetoMarcador_Datos en la que añadimos la operación que se va a realizar. Por último borramos el contenido de _ValorActual, y asignamos _Punto a false, de forma que podamos empezar a añadir un nuevo valor actual.

Ahora veamos la función BorrarCaracter :

Archivo : ObjetoMarcador.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
// Función que borra el último caracter
void ObjetoMarcador::BorrarCaracter(void) {
// Hay un caracter en el valor actual
if (_TamValorActual == 1) {
_ValorActual[0] = TEXT('0');
_ValorActual[1] = TEXT('\0');
_TamValorActual = 0;
_Punto = false;
}
// Hay caracteres en el valor actual
else if (_TamValorActual > 1) {
_TamValorActual --;
if (_ValorActual[_TamValorActual] == TEXT('.')) _Punto = false;
_ValorActual[_TamValorActual] = TEXT('\0');
}
else { // Sin caracteres que borrar en el valor actual
size_t TamLista = _Lista.size();
// Miramos si hay mas valores en la lista de valores
if (TamLista > 1) {
_TamValorActual = swprintf_s(_ValorActual, 256, TEXT("%f"), _Lista[TamLista - 2]->Valor());
_TamValorActual = _FormatearStr(_ValorActual);
delete _Lista[TamLista -1];
delete _Lista[TamLista -2];
_Lista.resize(TamLista -2);
_Punto = false;
// Miramos si el nuevo valor tiene una coma decimal
for (size_t n = 0; n < _TamValorActual; n++) {
if (_ValorActual[n] == TEXT('.')) _Punto = true;
}
}
else { // No hay mas valores en la lista, reafirmamos el valor 0
_ValorActual[0] = TEXT('0');
_ValorActual[1] = TEXT('\0');
_TamValorActual = 0;
}
}
Repintar();
};

Lo primero que miramos en esta función es si el tamaño de valor actual es 1, y en ese caso asignamos un cero a valor actual y a su tamaño. En caso de que el valor actual sea mas grande que uno, borramos el ultimo carácter y nos fijamos si era un punto para asignar correctamente el valor _Punto.

Y si llegamos al caso de que valor actual es 0 quiere decir que no tenemos mas caracteres en el valor actual, pero puede que tengamos valores y operaciones en la lista, así que miramos el tamaño de la lista. Si el tamaño de la lista es mas grande que 1 lo que hacemos es asignar el ultimo valor de la lista al valor actual, y eliminar tanto la operación como el ultimo valor de la lista.

Por último nos queda ver la función Resultado :

Archivo : ObjetoMarcador.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
// Función que calcula el resultado, y reinicia todos los valores.
void ObjetoMarcador::Resultado(void) {
double Op1 = 0.0f;
double Op2 = 0.0f;
double nValor = _wtof(_ValorActual);
size_t i;
if (nValor == 0.0f) return;
// Añadimos el valor a la lista
ObjetoMarcador_Datos *nDatosValor = new ObjetoMarcador_Datos(TipoDatos_Valor, nValor);
_Lista.push_back(nDatosValor);
// Realizamos toda la serie de operaciones
Op1 = _Lista[0]->Valor();
for (i = 1; i < _Lista.size() -1; i += 2) {
Op2 = _Lista[i + 1]->Valor();
switch (_Lista[i]->Tipo()) {
case TipoDatos_Suma : Op1 = Op1 + Op2; break;
case TipoDatos_Resta : Op1 = Op1 - Op2; break;
case TipoDatos_Multiplicacion : Op1 = Op1 * Op2; break;
case TipoDatos_Division : Op1 = Op1 / Op2; break;
}
}
// Formateamos el resultado y lo introducimos en _ValorActual
swprintf_s(_ValorActual, 256, TEXT("%f"), Op1);
_TamValorActual = _FormatearStr(_ValorActual);
_Punto = false;
for (i = 0; i < _TamValorActual; i++) {
if (_ValorActual[i] == TEXT('.')) _Punto = true;
}
Repintar();
_BorrarLista();
}

Para mostrar correctamente toda la operación lo primero que hacemos es añadir el valor actual a la lista, luego hacemos un bucle que revisa todos los valores a partir de la segunda posición de la lista. La lista contiene los datos de la siguiente forma : VALOR, OPERACION, VALOR, OPERACION, VALOR, etc.. asi que si partimos del primer valor, sabemos que luego siempre vendrá una operación y un segundo valor. Por último formateamos correctamente el resultado, miramos si contiene una coma decimal para asignar el valor _Punto, repintamos el control, y borramos la lista de operaciones para comenzar una nueva lista.

Llegados a este punto ya hemos visto lo más importante a la hora de crear nuestro marcador. Si deseáis ver el marcador funcionando podéis descargar los ejemplos de este tutorial y echar un vistazo al ejemplo numero 6 :

La siguiente y última parte del tutorial será el punto y final para la calculadora, y consistirá en enlazar todo lo hecho anteriormente para ensamblar la aplicación : 1.7 - Terminando la calculadora

Descargar tutorial WinAPI completo Calculadora compilada