PRÁCTICA 2. VISUAL C++ (VC)
1 Introducción y objetivos En la presente práctica se pretender ofrecer la segunda toma de contacto con el entorno
de desarrollo que se usa en la asignatura para programar en el lenguaje C/C++. En la
primera toma de contacto el alumno aprendió a programar DLLs para implementar
comportamientos de robots para el simulador. En esta segunda toma de contacto se
pretende que el alumno sea capaz de hacer pequeñas aplicaciones basadas en un entorno
gráfico que le servirán para representar gráficamente el estado del robot y su
"pensamiento". Evidentemente no se pretenden hacer entornos gráficos sofisticados, ni
siquiera perderse en todos los vericuetos que eso supone. La parte gráfica, únicamente
debe ser una herramienta que pone en contacto al robot con el alumno de forma visual.
De forma no casual, este entorno se utiliza de la misma forma que el Embedded Visual
C++ que se usa para programar los dispositivos móviles usados en la asignatura
"Sistemas robóticos móviles: sistemas mecatrónicos básicos", continuación de ésta.
Los objetivos de esta práctica son:
• Saber realizar y depurar proyectos sencillos en el Visual C++. Estos proyectos están
orientados representar el estado del robot y a practicar programación en C++,
aunque en esta práctica no se haga. Estas aplicaciones constan de un interfaz gráfico
y de un reloj que manda ejecutar ciertos procedimientos de forma periódica. Por
ello, los proyectos se basarán en una aplicación con una ventana (llamada también
Dialogo) que servirá de interfaz con el usuario y de un timer que se usará para
ejecutar algoritmos de forma periódica.
• Entender cómo se hacen interfaces gráficos sencillos, de forma que la práctica sea
una guía de referencia para futuras prácticas.
En esta segunda toma de contacto el procedimiento a seguir es el siguiente:
• En primer lugar se aprenderá a crear un proyecto basado en una ventana de Dialogo.
Es importantísimo realizar todos los pasos exactamente tal y como se indica
en la práctica, sino es muy fácil perderse en la misma. Este procedimiento debe
seguirse siempre que se quiera crear un nuevo proyecto gráfico en sucesivas
prácticas.
• Posteriormente se irán adquiriendo conocimientos para poder personalizar el
interfaz de la aplicación, hasta hacerlo interactivo con el usuario.
• Un aspecto importante es cómo se usan los timers, para poder mandar ejecutar
procesos de forma periódica.
• Se volverá a hacer hincapié en los conceptos necesarios para poder depurar (buscar
errores) un programa.
2 Entregables La práctica se da por concluida con los entregables que serán evaluados. Los
entregables consisten en:
• Realizar demostraciones al profesor de la sección 10 y 11.
• Dejar ver al profesor una lista de lo que has aprendido en la práctica que habrás
anotado en tu cuadernillo.
• Dejar ver al profesor una lista de los detalles que estimas importantes para otras
prácticas y que habrás anotado en tu cuadernillo. Por ejemplo, detalles de C++,
de entorno de desarrollo, …
3 Creación de un nuevo proyecto El objetivo es crear un proyecto basado en una ventana de Dialogo. Es
importantísimo realizar todos los pasos exactamente tal y como se indica en la
práctica, sino es muy fácil perderse en la misma. Este procedimiento debe seguirse
siempre que se quiera crear un nuevo proyecto en sucesivas prácticas.
1. Abrir Microsoft Visual Studio C++
1. Ir al menú Archivo->Nuevo->Proyecto.
2. Seleccionar en el cuadro de dialogo “Nuevo Proyecto” las siguientes opciones:
• Asegurarse de elegir ¡¡¡¡Aplicación MFC!!! • Project name: Dlg
• Location (siguiendo el ejemplo de la primera práctica): d:\robots\. Este
directorio tiene que existir; por ello, es necesario crearlo con el Explorador
de Windows.
• Resto de opciones por defecto como en figura
3. Pulsar Aceptar.
4. En el nuevo cuadro de dialogo seleccionar Basada en cuadros de diálogo, el
resto de opciones como se indica a continuación
5. Comprobar en Clases generadas que se van a generar las clases CDlgApp y
CDlgDlg.
6. Pulsar Finalizar.
4 Descripción de la ventana de proyecto En la siguiente figura se muestran los nombres de las diferentes ventanas que hay en el
proyecto.
En la ventana de Workspace hay tres pestañas:
1. Explorador de soluciones: es el lugar donde se encuentran nuestros ficheros de código C/C++.
2. Vista de Clases: no la vamos a usar.
3. Administrador de propiedades: no la vamos a usar
4. Vista de Recursos: es donde vamos a tocar para poder cambiar el interfaz gráfico
de nuestra aplicación.
Una vez creado el proyecto VC crea varios ficheros, ver Ventana workspace-
>Explorador de soluciones->Archivos de código fuente (antes de seguir leer la
sección 13):
• Dlg.cpp/.h: fichero donde se encuentra el código de la aplicación principal; es decir,
el equivalente a main. Este fichero no es necesario tocarlo en ninguna aplicación que
hagamos en el laboratorio.
• Dlg.rc: En este fichero se encuentran almacenados los recursos que usa la
aplicación. Estos recursos son las ventanas, botones, etc. Este fichero no se puede
tocar directamente. De hecho si se hace doble clic sobre él se muestra un entorno
gráfico donde se puede modificar el aspecto de la ventana de Dialogo del proyecto.
• DlgDlg.cpp/.h: Es el código de la ventana de Dialogo creada. Este fichero es el
punto de partida que se tiene que tocar para poder desarrollar el programa que va a ir
en el robot.
• Stdafx.cpp/.h: Es un fichero que siempre existe en todos los proyectos, y que no
hace falta entender pero que es necesario para que todo funcione.
• Targetever.h: Es un fichero que indica la plataforma destino. No vamos a necesitar
modificarlo.
Ventana de
workspace Ventana de código
Ventana de mensajes
Barra de selección
Para ejecutar el programa pulsar F5 o Build->Start Debug->Go.
NOTA IMPORTANTE: el #include <stdafx.h> siempre tiene que existir en todo
fichero *.cpp y debe ser el primero de todos. ¿por qué? sólo Microsoft lo sabe.
5 Cambiar el aspecto del Dialogo 1) Seleccionar el fichero de recursos donde se muestra la ventana de Dialogo.
2) Hacer doble clic sobre “Ventana Workspace->Vista de Recursos->Dlg->Dlg.rc-
>Dialog->IDD_DLG_DIALOG”
3) Borramos la etiqueta que hay por defecto en el Dialogo. Se selecciona y se pulsa
el botón suprimir.
4) Si no se puede ver la ventana de controles, activarla en “Ver->Cuadro de
herramientas”. Añadimos una etiqueta al Dialogo. Elegir “Static Text” de la
ventana de controles que aparece en el punto 2 y arrastrar hasta el lugar que
queramos de la ventana de Dialogo.
5) Escribir “hola mundo”. Hacer clic con el botón derecho del ratón y seleccionar
propiedades. En la pestaña “Apariencia” escribir “hola mundo” en “Caption”.
6) Ejecutar el programa de nuevo.
6 Dialogo interactivo En esta sección es necesario seguir los pasos tal y como se explica. No se pueden añadir
más componentes de los indicados, ni borrarlos. En tal caso, en posteriores apartados se
pueden tener problemas. Para controlar más volver a leer la sección 13.
1) Seleccionar el fichero de recursos donde se muestra la ventana de Dialogo.
2) Hacer doble clic sobre “Ventana Workspace->Vista de Recursos->Dlg->Dlg.rc-
>Dialog->IDD_DLG_DIALOG”
3) Añadimos un cuadro de texto. Elegir “Edit Control” de la ventana de controles
que aparece en el punto 2 y arrastrar hasta el lugar que queramos de la ventana.
4) Añadimos otro cuadro de texto. Elegir “Edit Box” de la ventana de controles
que aparece en el punto 2 y arrastrar hasta el lugar que queramos de la ventana.
5) Añadimos un botón. Elegir “Button” de la ventana de controles que aparece en
el punto 2 y arrastrar hasta el lugar que queramos de la ventana.
6) Escribir “Run” en el botón. Hacer clic con el botón derecho del ratón y
seleccionar propiedades. En la pestaña “Apariencia” escribir “Run” en
“Caption”.
7) Hacer doble click sobre el botón. Aceptar el nombre por defecto. Se creará una
función que se ejecutará cuando se pulse el botón
void CDlgDlg::OnBnClickedButton1()
8) Escribir el siguiente código en la función que se acaba de crear. Pulsar F1 sobre
la función que queráis ayuda. Este código copia el texto del cuadro de texto
primero sobre el segundo. Las funciones vienen explicadas en 13.2.1.
char uText[10]; // variable donde se almacena el t exto unsigned int nelem; // número de caracteres del t exto nelem = GetDlgItemText(IDC_EDIT1,(LPTSTR) uText ,10); // coge el texto SetDlgItemText(IDC_EDIT2, (LPTSTR) uText); // lo copia al otro cuadro de texto SetDlgItemInt(IDC_EDIT1, 12,false); // ejemplo de como se escribe un número UpdateData(false); // actualiza los datos en pant alla
9) Ejecutar el programa
Es necesario hacer notar que el cuadro de texto que se añadió primero se llama por
defecto IDC_EDIT1 y que el segundo cuadro de texto se llama IDC_EDIT2. Si se
quieren modificar esos nombres hacer clic con el botón derecho del ratón sobre el
cuadro de texto y seleccionar “Propiedades->Varios->ID”. Evidentemente si los
nombres cambian, entonces el código que hace referencia a ellos es necesario también
cambiarlos.
7 A golpe de reloj El objetivo es crear un contador que se incremente cada 10 segundos. De esta manera se
aprende a manejar un timer, esencial para programar un robot.
1) Ir a la ventana de recursos
2) Hacer clic en botón derecho del ratón (sobre la ventana de dialogo a la que se
accede mediante "Ventana Workspace->Vista de Recursos->Dlg->Dlg.rc-
>Dialog->IDD_DLG_DIALOG"), botón derecho propiedades y en la ventana de
propiedades, con el botón Mensajes pulsado, ordenar de A..Z y para el evento
WM_TIMER seleccionar <agregar> OnTimer.
3) En DlgDlg.cpp se ha añadido una nueva función, que hay que completar como
sigue (¡¡¡Ojo la función ya existe, no hay que copiarla, sólo rellenarla!)
void CDlgDlg::OnTimer(UINT_PTR nIDEvent) { // La funcion se ejecuta cada vez que el timer ter mina la cuenta, esto después de que pasan los 10 segundos KillTimer(1); // borrado de la cuenta del timer, s e pone a 0 SetDlgItemInt(IDC_EDIT1, i++,false); // aumenta e n 1 el contador, el numero del Edit Box UpdateData(false); // actualiza los datos en pant alla SetTimer(1,10000, NULL); // inicializa el timer; c ada 10 segundos aumenta la cuenta CDialog::OnTimer(nIDEvent); }
4) Añadir la siguiente variable global en DlgDlg.cpp int i = 0;
5) En la función “BOOL CDlgDlg::OnInitDialog()” añadir justo antes del “return”:
SetTimer(1, 10000,NULL); // inicializa el timer par a que cuente 10 segundos
6) Ejecutar el programa. ¿qué hace?
7) ¿Qué crees que hace KillTimer y SetTimer ?
8) ¿Qué crees que pasa si no se hace el paso 5?
8 Debug de programas En esta sección se va a tratar de cómo depurar programas. Un Punto de Interrupción
(Breakpoint) es un lugar del código donde se le dice a VC que pause la ejecución del
programa cuando llegue al mismo. Sirve para ver el estado de las variables en ese
momento o para seguir ejecutando el programa de una línea en una línea.
1) Añadir un Punto de Interrupción en la siguiente línea de la función OnTimer.
Encima de la línea clic botón derecho del ratón y ejecutar “Punto de
interrupción->Insertar punto de interrupción”
SetDlgItemInt(IDC_EDIT1, i++,false); //aumenta e n 1 el contador
2) Ejecutar el programa
3) Cuando el programa se para en el Punto de Interrupción, ver el valor de la
variable i en la ventana de watch. “Depurar->Ventanas->Automático”
4) A partir de ese punto ejecutar línea a línea el código bien pulsando la tecla F10,
o bien “Depurar->Paso a paso por procedimientos”. Observar como cambia el
valor de la variable i en la ventana de watch. El comando “Depurar->Paso a paso
por instrucciones” sirve para que si la ejecución de la línea contiene una función,
poder introducirse en la misma.
5) Investigar el comando de Botón derecho->Ejecutar hasta el cursor”.
9 Gráficos El objetivo de esta sección es aprender a hacer gráficos muy simples con VC. Para
entrar en calor, sustituye el código de la función OnTimer por el siguiente:
void CDlgDlg::OnTimer(UINT nIDEvent) { // Es lo primero que se tiene que hacer. // Desactiva el timer para que no pueda volver a // lanzar el evento OnTimer mientras se atiende KillTimer(1); // coje el objeto gráfico de la ventana para dibuj ar en él CDC *pDC=GetDC( ); // define el color rojo unsigned long rojo = RGB(255,0,0); unsigned long verde = RGB(0,255,0); unsigned long color; // elije la pluma que desea usar en el dibujo CPen pen; // pluma de trazo continuo de anchura 3 y de color rojo pen.CreatePen(PS_SOLID, 3, rojo); pDC->SelectObject(&pen); if (i%2) color = verde; else color = rojo; // elije la brocha con la que se pinta el relleno CBrush brush(color); pDC->SelectObject(&brush);
i++; // dibuja una elipse pDC->Ellipse(50,70,70,90); UpdateData(false); // actualiza los datos en pant alla // reactiva el timer para que vuelva a saltar en // el siguiente ciclo SetTimer(1,5000, NULL); CDialog::OnTimer(nIDEvent); }
¿Qué hace?. Jugad con distintas brochas y plumas.
En el link
http://www.iit.upcomillas.es/~alvaro/teaching/Clases/Programacion/Practicas/P2clases
Dlg.zip
se encuentra una clase llamada CDibuja que se encarga de dibujar figuras geométricas
por pantalla. Para más información sobre cómo dibujar, remitirse a la ayuda de
Microsoft http://msdn.microsoft.com/library/. Para entender la clase CDibuja leer la
sección 14.
10 Ejercicio Hacer un programa usando la clase CDibuja que muestre un círculo en pantalla que en
función de un número introducido por un cuadro de texto se coloree de color rojo si el
número es mayor que 0 y de color verde si el número es menor o igual que 0.
1) Hacerlo con un botón de validación
2) Hacerlo muestreando cada 5 segundos
Para coger un número de un cuadro de texto (Edit Box):
int num; // número donde se va a guardar el result ado num = GetDlgItemInt(IDC_EDIT1, NULL, true); // Cog e un número con signo de IDC_EDIT1
Para usar la clase CDibuja es necesario seguir los siguientes pasos:
1. Instalar la clase CDibuja en el proyecto.
a. Descomprimir el ZIP en el directorio del proyecto.
b. Añadir los ficheros nuevos (Dibuja.cpp, CPunto.cpp, CPunto.h y
Dibuja.h), en la ventana de proyecto, en la pestaña Explorador de
soluciones->Archivos de codigo fuente, pulsar botón derecho y elegir
“Agregar elemento existente”. Seleccionar los cpp. Repetir el proceso
para los .h en Explorador de soluciones->Archivos de encabezado.
2. En el fichero DlgDlg.h añadir la línea dentro de la clase CDlgDlg que define
un puntero a la clase CDibuja denominado m_p_dibuja .
protected : CDibuja* m_p_dibuja; HICON m_hIcon;
3. En el fichero DlgDlg.cpp añadir una línea en la función
CDlgDlg::OnInitDialog que crea la memoria para alojar el objeto al que
apunta el puntero m_p_dibuja . m_p_dibuja = new CDibuja(GetDC());
4. En el fichero DlgDlg.cpp añadir una línea en el destructor de la clase CDlgDlg
que se encarga de eliminar de la memoria el objeto al que apunta m_p_dibuja.
CDlgDlg::~CDlgDlg(){ if (m_p_dibuja != NULL) delete m_p_dibuja; }
5. Declarar el destructor anterior en el fichero “DlgDlg.h”, justo debajo del
constructor.
6. Poner #include “Dibuja.h” en el fichero “DlgDlg.h”.
7. Utilizar el puntero m_p_dibuja dentro de la función CDlgDlg::OnTimer
como se describe en la sección 14.
Hacer demostración al profesor.
11 Posición del robot Se quiere dibujar en una aplicación de supervisión, el trayecto que sigue el robot en el
simulador en tiempo real. Para ello, se deberá partir del programa realizado en la
práctica anterior que escribía en un fichero la posición del robot. Este programa se
encuentra resuelto en la siguiente dirección:
http://www.iit.upcomillas.es/~alvaro/teaching/Clases/Programacion/Practicas/P2clases
Dllsencilla.zip
Simultáneamente ese fichero es leído por la aplicación de supervisión (sólo la posición
calculada a partir de los encoders), la cual dibujará el trayecto seguido por el robot en la
pantalla. Se recomienda usar el mismo sistema de referencia que usa el simulador.
Hacer demostración al profesor.
Pistas:
• Para leer un fichero: se usa el operador >>, tal y como se muestra a
continuación.
• Para repintar la pantalla se puede llamar a la función RedrawWindow() desde
cualquier función de CDlgDlg.
#include <fstream>
...float x,y,z;std::ifstream fin("C:\\posicion.txt");if (!fin.fail()) { // si lo ha podido abrir
// lee x y z hasta que termine el ficherowhile (!fin.eof())
fin >> x >> y >> z;}fin.close();
...
12 Ejercicio opcional para aquellos que les sobre t iempo Dibujar el robot con un aspecto similar al del simulador, moviéndose por la ventana de
la aplicación realizada, según se mueve por el simulador.
13 Introducción a la programación orientada a objet os y a interfaces de Windows
13.1 Clases Una clase es una estructura de datos que contiene variables (también llamadas
propiedades o estados), y funciones (también llamados métodos o comportamientos).
Como estructura de datos que es, cuando se define se crea un nuevo tipo de datos, cuyo
nombre se indica al lado de la palabra clave class . La forma de declarar una clase en
C++ es (las declaraciones en C++ siempre van en los ficheros *.h):
Tanto las variables como las funciones pueden ser públicas o privadas. Aquellas que
son públicas son accesibles desde fuera de la clase; es decir, cualquier función que no se
encuentre dentro de la clase puede acceder a parte pública de la clase. Aquellas que son
privadas sólo son accesibles desde las funciones que pertenecen a la clase.
Cuando se crea una variable del tipo de la clase, se ha creado un objeto; es decir, una
variable es a un tipo de dato como un objeto es a una clase. La creación de un objeto de
una clase se denomina instanciar la clase; es decir, un objeto de una clase es una
instancia de la misma.
El acceso a las variables y funciones públicas de un objeto se hace poniendo el nombre
del objeto seguido de un punto y el nombre de la variable o función. Por ejemplo:
int n = robot.get_numero_sensores(); // guarda en n el número de sensores
En toda clase existen unas funciones miembro muy especiales que son:
• El constructor: es una función que tiene el mismo nombre que el de la clase y no
se puede llamar directamente, sino que se llama en el momento de la creación de
un objeto de la clase.
• El destructor: es una función que tiene el mismo nombre que la clase, pero
precedido de ~. No se puede llamar directamente, sino que se llama de forma
automática en el momento en el que un objeto desaparece.
void main(void) { // llama al constructor de robot ya que se crea CRobot robot(4); // crea un robot con 4 sensores. Instancia un robot int n = robot.get_numero_senosres(); // guarda en n el número de sensores // error!!!! no se puede acceder a la parte priva da de una clase desde fuera n = robot.m_numero_sensores; }; // se llama al destructor de robot ya que se sal e de la función main
class CRobot { public: // Accesible desde fuera de la clase int get_numero_sensores(); // método CRobot(int numero_sensores); // constructor ~CRobot(); // destructor private: // Inaccesible desde fuera de la clase CSensor *vector_sensores; // sensores que tiene e l robot int m_numero_sensores; // propiedad };
Se ha visto que la declaración de una clase se hace en los ficheros *.h. Pues bien, la
definición de una clase se hace en los ficheros *.cpp:
Cada función va precedida del nombre de la clase seguido de ::, para que se sepa que las
funciones son de la clase.
13.2 Programación de interfaces en Windows Las ventanas en Windows son clases específicas que ha desarrollado Microsoft para que
puedan ser manejadas de forma sencilla. Una ventana en Windows está formada por
controles. Los controles son componentes que se sitúan en una ventana y sirven para
interaccionar con el usuario.
Aunque hay muchas clases para gestionar ventanas (CWnd, CWindow, CDialog), en
esta sección sólo se va a explicar CDialog. CDialog es la ventana más básica de
Windows y permite situar controles encima de ella. Cuando se quiere desarrollar una
aplicación basada en una ventana de diálogo, se crea una clase con el nombre de la
aplicación que es una ventana de diálogo. En esta clase se pueden ir incorporando
diferentes tipos de controles, los cuales formarán parte de las propiedades o variables
que tiene la clase. Además se pueden añadir nuevas variables o funciones de forma que
finalmente la clase creada es una ventana de dialogo con un aspecto y características
especiales que son las que el programador ha elegido.
Si por ejemplo se crea una aplicación que se llama “Ejemplos”, la clase de diálogo se
llamará “CEjemplosDlg”. A continuación se muestra un ejemplo de clase ventana de
dialogo que tiene un botón que se llama “BotonRun”.
// constructor de robot CRobot::CRobot(int numero_sensores) { m_numero_sensores = numero_sensores; vector_sensores = new CSensor[m_numero_sensores]; }; // destructor de robot CRobot::~CRobot(){ delete[] vector_sensores; } // función miembro int CRobot::get_numero_sensores(){ return(m_numero_sensores); }
Además las ventanas tienen un conjunto de eventos asociados; es decir, acciones que
pueden suceder en un momento determinado. Desde le punto de vista de la
programación un evento no es más que una función, que se ejecuta cuando se produce el
evento. El evento más importante de una ventana es:
• OnTimer : las ventanas tienen asociado un timer, que es un contador de tiempo.
Gracias a este contador de tiempo se puede hacer que se ejecuten de forma
periódica un conjunto de instrucciones.
13.2.1 Controles Un control es una clase que sirve de interfaz con el usuario y se coloca en una ventana
de diálogo. Los controles dentro de un dialogo pueden tener eventos asociados, que no
son más que funciones que se ejecutan en el momento en que se produce dicho evento.
Dichas funciones tienen un nombre con el prefijo On para indicar que es un evento; por
ejemplo OnChange.
Hay muchos tipos de controles pero los más importantes son:
• CButton : es la clase que permite crear un botón. El evento por defecto es el
OnClic.
• CEdit : es la clase que le permite al usuario introducir datos en un programa. El
evento por defecto asociado es el OnChange.
Windows provee un conjunto de controles para el manejo de controles y son:
• unsigned int GetDlgItemInt(int id_del_control, int* conversion_ok, int
con_signo): función que obtiene el número entero que se ha escrito en el
control. Se usa fundamentalmente con controles CEdit . Se pasa como parámetro
el identificador del control, una variable booleana que indica si el número lo
interpreta con signo o no. Devuelve el número entero que contiene el control y
en el parámetro segundo devuelve si la transformación del texto del control al
número ha sido correcta. Hay que tener en cuenta que todos los interfaces con el
usuario son en modo texto y por lo tanto para poder obtener un número hay que
hacer una conversión de texto a número que puede ser o no correcta.
• int GetDlgItemText( int id_del_control, LPTSTR text o, int
cantidad_de_texto_maxima): función que obtiene el texto que hay escrito en el
control. Se usa fundamentalmente con controles CEdit . Se pasa como parámetro
class CEjemplosDlg : public CDialog { public: CEjemplosDlg(CWnd* pParent = NULL); // constructor // funciones públicas de la clase int get_contador(); // para devolver el valor del contador private: // variables y funciones privadas globales a esta clase
// que quiera usar el programador int contador; // para llevar cuentas void control(); // para controlar un proceso // funciones eventos que se van a asociar a contro les //{{AFX_MSG(CEjemplosDlg)
virtual BOOL OnInitDialog(); // evento de visualiz ación de la ventana
afx_msg void OnBotonRun(); // evento de clic de un botón
//}}AFX_MSG
DECLARE_MESSAGE_MAP()
};
el identificador del control, una cadena de caracteres (que tiene que tener
reservada memoria) donde se va a escribir el texto y el número máximo de
caracteres que puede coger del control (este parámetro debe ser menor que el
número de caracteres que se han reservado para la cadena de caracteres del
parámetro segundo).
• void SetDlgItemText(int id_del_control, LPCTSTR tex to_a_escribir): función que escribe en texto en un control. Se usa fundamentalmente con
controles CEdit . Se pasa como parámetro el identificador del control, y la cadena
de texto que se tiene que escribir en el control.
• void SetDlgItemInt(int id_del_control, unsigned int valor, int
con_signo): escribe un número entero en un control. Se usa fundamentalmente
con controles CEdit . Se pasa como parámetro el identificador del control, el
valor que se tiene que escribir y si se quiere que se escriba con o sin signo. Es
necesario recordar que como todos los interfaces con el usuario son en modo
texto, esta función hace una conversión de número a texto y por lo tanto necesita
información de si debe interpretar el número con o sin signo.
A continuación se muestra un ejemplo de cómo se usan estas funciones:
13.2.2 Gestión de controles desde el entorno de des arrollo Desde la ventana de recursos del entorno de desarrollo, haciendo clic con el botón
derecho sobre un control y accediendo al apartado de propiedades se puede modificar el
identificador (ID) de dicho control, o lo que es lo mismo se puede cambiar su nombre
(no deja de ser el nombre de una “variable” un tanto especial), que por defecto tiene uno
genérico relacionado con el tipo de control que es. Además se pueden modificar sus
propiedades (caption, visible, tipo de letra,…). No se debe confundir el nombre que
tiene un control a la hora de programar (ID), con el texto que aparece por ejemplo sobre
un botón o al lado de un check box (caption).
El nombre que se asigna al control deberá ser intuitivo, ya que a la hora de escribir el
código o de revisarlo será éste el que se tiene que tener en cuenta.
Por defecto los controles tienen asociado un evento, o lo que es lo mismo una acción del
usuario sobre los mismos. Por ejemplo en el caso de un botón el usuario puede apretar
el botón (surge el evento OnButton) y en el caso de un cuadro de texto el usuario puede
cambiar el texto que aparece en el mismo (OnChange). No confundir este último
ejemplo con el texto que aparece grabado sobre un botón.
int i = 10;
unsigned short sout[] = TEXT("hola"); // reserva 5 caracteres de memoria
unsigned char cout[] = "hola";
unsigned short in[MAX]; // reserva MAX caracteres de memoria
CString s = cout;
int ok;
SetDlgItemText(IDC_EDIT1,sout);
SetDlgItemText(IDC_EDIT3,s);
SetDlgItemInt(IDC_EDIT2,i,false);
i = GetDlgItemInt(IDC_EDIT2,&ok,false);
if (ok)
GetDlgItemText(IDC_EDIT1,in,MAX-1);
Cuando se produce dicho evento se pueden ejecutar una serie de acciones, que serán
programadas como una función que se ejecutará cada vez que se realice el evento sobre
el control correspondiente. Se pueden añadir diferentes eventos a cada control, pero por
ahora sólo se va a describir el evento principal o por defecto de cada uno de ellos.
El evento por defecto “aparecerá” si se hace doble clic sobre el control. Entonces se crea
una función en el fichero de código asociado al control donde se pueden programar las
acciones a ejecutar cuando surja el evento. El nombre de esta función puede ser
modificado, pero se aconseja mantener el nombre de la misma para poder relacionarlo
fácilmente con el control correspondiente. Además informa del tipo de evento y el
control asociado al mismo.
A continuación se van a comentar las modificaciones que hay que hacer sobre un
programa si se deciden eliminar o modificar controles:
• Si se decide modificar el nombre de un control se debe tener en cuenta que no solo
hay que hacerlo en la ventana de recursos sino también en todas las zonas de código
que lo referencien; exactamente en el fichero *.cpp en la zona de
BEGIN_MESSAGE_MAP, ver ejemplo a continuación.
• Si se quiere borrar un control, no hará falta eliminar nada en el código, ya que se
mantiene tal cual y esto no generará ningún tipo de error. Siempre y cuando no
añadamos ningún control del mismo tipo, en cuyo caso hay que tomar la solución
del siguiente punto.
• Por último si decidimos borrar un control y crear uno nuevo del mismo tipo que
tenga asociado el mismo evento debemos ponerle el mismo nombre para simplificar.
14 Explicación de clase CDibuja A continuación se muestra el código de una clase que se encarga de dibujar formas
geométricas por pantalla:
// fichero *.h class CEjemplosDlg : public CDialog { public: CEjemplosDlg(CWnd* pParent = NULL); // constructor // funciones públicas de la clase int get_contador(); // para devolver el valor del contador private: // variables y funciones privadas globales a esta clase // que quiera usar el programador int contador; // para llevar cuentas void control(); // para controlar un proceso // funciones eventos que se van a asociar a contro les //{{AFX_MSG(CEjemplosDlg) virtual BOOL OnInitDialog(); // evento de visualiz ación de la ventana afx_msg void OnBotonRun(); // evento de clic de un botón //}}AFX_MSG DECLARE_MESSAGE_MAP() }; // fichero *.cpp // lugar para los includes #include "stdafx.h" // constructor CEjemplosDlg::CEjemplosDlg(CWnd* pParent /*=NULL*/) : CDialog(CEjemplosDlg::IDD, pParent){ .... } // asociación del evento de un control a una funció n BEGIN_MESSAGE_MAP(CEjemplosDlg, CDialog) //{{AFX_MSG_MAP(CEjemplosDlg) // asocia el evento de clic del botón IDC_BOTON_RU N a la función OnBotonRun ON_BN_CLICKED(IDC_BOTON_RUN, OnBotonRun) //}}AFX_MSG_MAP END_MESSAGE_MAP() // función que atiende el evento de arranque de la ventana de diálogo BOOL CEjemplosDlg::OnInitDialog() { ... } // función que atiende el evento de clic del botón IDC_BOTON_RUN void CEjemplosDlg::OnBotonRun() { ... }
class CDibuja { private : CDC* m_pDC; /** Zona donde se puede dibujar*/ public : CDibuja(CDC* dc); ~CDibuja(); /** Dibuja una polilinea * vPuntos vector<CPunto>: vector de puntos de lo s que consta el polilinea * ulColor unsigned long: color del reborde de la polilinea */ void PoliLinea( const std::vector<CPunto>& vPuntos, unsigned long ulColor); /** Dibuja un poligono * vPuntos vector<CPunto>: vector de puntos de lo s que consta el poligono * ulColor unsigned long: color del reborde del p olígono * ulRelleno unsigned long: color del relleno del polígnono */ void Poligono( const std::vector<CPunto>& vPuntos, unsigned long ulColor, unsigned long ulRelleno); /** Dibuja una elipse * p1 CPunto: punto superior izdo del cuadrado en el que está inscrita la elipse * p2 CPunto: punto inferior decho del cuadrado e n el que está inscrita la elipse * ulColor unsigned long: color del reborde de la elipse * ulRelleno unsigned long: color del relleno de la elipse */ void Elipse( const CPunto& p1, const CPunto& p2, unsigned long ulColor, unsigned long ulRelleno); };
La clase tiene tres funciones públicas que se apoyan en la clase CPunto . Aunque se
verá en detalle en clase, esta clase guarda las coordendas x e y de un punto en el
espacio. Cuando se necesita un vector de puntos se usa la clase vector. Esta clase
pertenece a la STL (Estandard Template Library) que no se va a ver en este curso;
simplemente se quiere que se sepa usar aunque no se entienda ya que requiere
conocimientos más avanzados de C++ como son los Templates. A continuación se
explica como se maneja la función PoliLinea :
#include ”CPunto.h”#include <vector>
...unsigned long rojo = RGB(255,0,0);// define un vector de CPunto de tres elementosstd::vector<CPunto> v(3); // define tres puntosCPunto p3(200,200), p4(300,300), p5(200,300);// rellena el vector con los puntosv[0] = p3; v[1] = p4; v[2] = p5;// llama a la funciónm_p_dibuja->PoliLinea(v,rojo);
...
Es importante hacer notar que el acceso a las funciones de una clase cuando se
utiliza un puntero que apunta al objeto, se hace con “->” en vez de “.”.
Las los parámetros de las funciones en C++ pueden variar respecto a la notación en C.
Por ejemplo:
• const std::vector<CPunto>& vPuntos: es un parámetros que se pasa por referencia,
ya que tiene &, cosa que no se puede hacer en C. Acordarse de que en C los
parámetros siempre se pasan por copia. Esto significa que en la función se pasa
el propio vector de puntos y se podría modificar, todo ello sin usar punteros
como se puede ver dentro de la función. Al pasar un parámetro por referencia, se
consigue ganar mucho tiempo ya que el programa no tiene que hacer una copia
del mismo para ser utilizado en una función. Hacer copias implica reserva y
destrucción de memoria, que son procesos muy costosos en tiempo. Para evitar
que un parámetro se pueda modificar dentro de la función se le pone el
calificador const . De esa manera se consiguen las ventajas del paso por copia
que garantiza que una función no modifica una variable pasada por parámetro y
por otro lado se consigue la ventaja del paso por referencia que es la eficiencia.