sábado, 26 de noviembre de 2016

Capítulo 85: Moviendo una figura en Python


Diario de un programador. Día 187

Moviendo una figura en Python.

Es el momento de ver cómo mover una figura en Python. Esto es algo que ya vimos en su oportunidad en Small Basic, así que lo implementaremos aquí. Tk no es muy potente para mover gráficos o realizar animaciones, por lo que aquí se verá será algo sencillo (Ya llegaremos a algo más complejo con otra herramienta) que servirá para tener una idea en cómo funciona esto de las animaciones.

En el primer ejemplo, se mostrará cómo crear una esfera que se desplaza por la ventana. Se empezará por crear la esfera y luego se procederá a moverla. El siguiente código sirve para mostrar una esfera en pantalla.

from tkinter import *
ventana = Tk()
lienzo = Canvas(ventana, width = 400, height = 400)
lienzo.pack()

lienzo.create_oval(10,10,50,50, fill = "blue")


Ahora lo siguiente es agregar el código que permita que la figura se mueva. El siguiente código es el encargado de realizar esa tarea.

import time
from tkinter import *
ventana = Tk()
lienzo = Canvas(ventana, width = 400, height = 400)
lienzo.pack()
lienzo.create_oval(10,10,50,50, fill = "blue")

for x in range(0,60):
    lienzo.move(1,5,0)
    ventana.update()
    time.sleep(0.05)

Como se puede ver, se ha agregado en la primera línea una instrucción "import time", la cual permite utilizar la función sleep, con la cual logramos controlar la velocidad en que se ejecuta el programa. Para mover la figura, se utilizó una instrucción "for", la cual genera un ciclo que va desde 0 a 59. La siguiente línea utiliza la función "move". Esta función requiere de tres parámetros. El primero se refiere al identificador de la figura. Cuando se crea una figura, esta adquiere el identificador 1, si se crea otra adquiere el identificador 2 y así sucesivamente. El siguiente parámetro se refiere al desplazamiento "x" de la figura y el tercer parámetro se refiere al desplazamiento "y" de la figura. En este caso, la función lienzo.move(1,5,0) quiere decir "mover la figura con identificador 1, 5 pixeles a la derecha y 0 pixeles hacia abajo", como estamos dentro de un bucle "for", entonces la figura se estará desplazando constantemente 5 pixeles hacia la derecha hasta que finalice la instrucción.
La siguiente línea ventana.update(), obliga a actualizar la ventana en cada ciclo. De no utilizar esta instrucción, la ventana no se actualizaría sino hasta que finalice el bucle y no se vería el efecto del movimiento ya que la figura se desplazaría directamente hacia su posición final. La última línea time.sleep(0.05), permite que el programa se ejecute más lento, con esto se logra apreciar el movimiento. Debido a que el ciclo se ejecuta tan rápido que si no lo frenamos un poco entonces tampoco veríamos su desplazamiento. Esta función, acepta como parámetro un número que indica en cuantos milisegundos se debe retrasar  el programa.
La siguiente modificación al programa anterior hará que la figura avance y retroceda constantemente. Esto se logró mediante un ciclo while-True

import time
from tkinter import *
ventana = Tk()
lienzo = Canvas(ventana, width = 400, height = 400)
lienzo.pack()
lienzo.create_oval(10,10,50,50, fill = "blue")

while True:
    for x in range(0,60):
        lienzo.move(1,5,0)
        ventana.update()
        time.sleep(0.05)

    for x in range(0,60):
        lienzo.move(1,-5,0)
        ventana.update()
        time.sleep(0.05)

Lo siguiente a realizar es intentar mover la figura presionando una tecla. Para poder hacer esto, es necesario crear un evento, los cuales trabajan mediante el uso de funciones.
Entonces, vamos a modificar el primer programa, ese que hacía que la figura se moviera por sí sola hacia la derecha. Al borrarle algunas líneas el código quedaría de la siguiente manera:

from tkinter import *

ventana = Tk()
lienzo = Canvas(ventana, width = 400, height = 400)
lienzo.pack()
lienzo.create_oval(10,10,50,50, fill = "blue")

Como se puede ver, se eliminó la parte que realizaba la animación, quedando solamente la parte correspondiente a la creación de la figura.
Como se dijo antes, para manejar el evento, es necesario crear una función. En este caso, la función se verá de la siguiente manera:

def moverEsfera(evento):
    lienzo.move(1,5,0)

La función a crear debe llevar un argumento (puede ser cualquier palabra), este argumento le sirve al módulo Tk para enviar información a la función. Para poder llamar a esta función, se utiliza la función bind_all, la cual recibe dos argumentos. El primer argumento es la tecla a presionar y el segundo parámetro corresponde a la función a ejecutar. Por ejemplo, si quisiera que al presionar la tecla "Enter" se ejecute la función "moverEsfera", la función bind_all debe quedar de la siguiente manera:

bind_all("<Keypress-Return">,moverEsfera)

Llevando lo anterior al programa, quedaría de la siguiente manera:

from tkinter import *

def moverEsfera(evento):
    lienzo.move(1,5,0)

ventana = Tk()
lienzo = Canvas(ventana, width = 400, height = 400)
lienzo.pack()
lienzo.create_oval(10,10,50,50, fill = "blue")
lienzo.bind_all("<KeyPress-Return>",moverEsfera)

Si todo salió bien, entonces al ejecutar el programa y presionar la tecla "Enter", la esfera debiera de desplazarse hacia la derecha.

Para hacer que la figura se desplace en otras direcciones, se pueden crear otras funciones para que cada función maneje una tecla distinta y realice una operación distinta. Por ejemplo, podemos crear cuatro funciones. Una función puede ser utilizada para mover la figura hacia arriba, otra hacia abajo, etc. y que cada función sea llamada mediante una tecla distinta. Esto podría quedar de la siguiente manera:

from tkinter import *

def derecha(evento):
    lienzo.move(1,5,0)

def izquierda(evento):
    lienzo.move(1,-5,0)

def abajo(evento):
    lienzo.move(1,0,5)

def arriba(evento):
    lienzo.move(1,0,-5)

ventana = Tk()
lienzo = Canvas(ventana, width = 400, height = 400)
lienzo.pack()
lienzo.create_oval(10,10,50,50, fill = "blue")

lienzo.bind_all("<KeyPress-Right>",derecha)
lienzo.bind_all("<KeyPress-Left>",izquierda)
lienzo.bind_all("<KeyPress-Down>",abajo)
lienzo.bind_all("<KeyPress-Up>",arriba)   

Entonces, al ejecutar el programa, se puede apreciar como la figura puede ser controlada con las flechas direccionales del teclado.

Una alternativa a este programa lo podemos lograr mediante el uso de la propiedad "keysym", la cual permite manejar las teclas que se han pulsado.

Ejemplo:


from tkinter import *

def moverFigura(evento):
    if evento.keysym == "Right":
        lienzo.move(1,5,0)
    elif evento.keysym == "Left":
        lienzo.move(1,-5,0)
    elif evento.keysym == "Down":
        lienzo.move(1,0,5)
    elif evento.keysym == "Up":
        lienzo.move(1,0,-5)       

ventana = Tk()
lienzo = Canvas(ventana, width = 400, height = 400)
lienzo.pack()
lienzo.create_oval(10,10,50,50, fill = "blue")

lienzo.bind_all("<KeyPress-Right>",moverFigura)
lienzo.bind_all("<KeyPress-Left>",moverFigura)
lienzo.bind_all("<KeyPress-Down>",moverFigura)
lienzo.bind_all("<KeyPress-Up>",moverFigura)
   
Mediante el uso de keysym, solo se trabaja en una función, que en este caso es "moverFigura".

Antes de seguir avanzando, quisiera mencionar algunas funciones de uso común que nos harán la vida más fácil en más de una ocasión.

FUNCIONES INTERNAS

abs: La función abs retorna el valor absoluto de un numero
ejemplo:
>>> abs(-15)
15

chr: Devuelve el carácter correspondiente al argumento ascii entregado
>>> chr(65)
'A'

float: Convierte un número entero o cadena en un número flotante o decimal
ejemplo:
>>> float(12)
12.0
>>> float("15")
15.0


int: Convierte un número flotante o cadena a número entero.
ejemplo:
>>> int(14.5)
14
>>> int("10")
10

len: Cuenta la cantidad de elementos de una lista o cadena(incluyendo espacios).
ejemplo:
>>> len("Hola mundo")
10
>>> lista = [1,2,3,4,5]
>>> len(lista)
5

max: Devuelve el elemento mayor de una lista o cadena
>>> lista = [1,2,3,4,5]
>>> max(lista)
5
>>> cadena = "abcde"
>>> max(cadena)
'e'

min: Devuelve el elemento menor de una lista o cadena
>>> lista = [1,2,3,4,5]
>>> min(lista)
1
>>> cadena = "abcde"
>>> min(cadena)
'a'

ord: Regresa el equivalente ascii de un caracter
>>> ord("A")
65

pow: Potencia de un número. El primer argumento es la base mientras que el segundo es el exponente.
>>> pow(5,2)
25

round: Redondea un número decimal hacia abajo.
>>> round(14,1)
14
>>> round(14,9)
14

sorted: Ordena una lista de menor a mayor.
>>> numeros = [2,1,4,6,3,5]
>>> sorted(numeros)
[1, 2, 3, 4, 5, 6]

str: Convierte un número en una cadena.
>>> str(10)
'10'
>>> 

sum: Suma los elementos de una lista
>>> lista = [1,2,3,4,5]
>>> sum(lista)
15

Esto es todo por ahora. Saludos

Gustavo J. Cerda Nilo
Noviembre 2016



viernes, 18 de noviembre de 2016

Capítulo 84. Programa en archivos separados en C++


Diario de un programador. Día 186

C++.

Crear clases en archivos separados


Al igual que lo hicimos en Java en su oportunidad, esta vez veremos cómo crear clases en archivos separados, con la finalidad de poder dividir el trabajo y crear clases reutilizables.
Vamos a crear un ejemplo muy sencillo, se trata de un programa que simplemente muestra un mensaje.  Este mensaje será creado por una función que se encuentra en una clase que está en otro archivo. Para este ejemplo, se crearán dos archivos, uno que contendrá la clase main y otra que contendrá la función del mensaje.

El archivo que contiene la función del mensaje, será guardado con extensión .h, mientras que la clase main será creada como siempre en un archivo de extensión .cpp

Voy a crear ambos archivos. A uno lo llamaré funcion.h y al otro inicio.cpp


Primero empezaré editando el archivo funcion.h En este archivo crearé una clase y una función que me permita mostrar un simple mensaje por pantalla. Esto quedaría de la siguiente manera:

#include<iostream>
using std::cout;
using std::endl;

class ClaseMensaje{
  public:

  void funcionSaludo(){
    cout << "Mensaje de prueba"<< endl;
  }
};

Como se puede ver, es una simple clase con una función que muestra un mensaje. Ahora voy a editar el archivo inicio.cpp, el cual contiene la función main y enlazar la clase creada en él.
Para poder incluir la clase anteriormente creada, se debe escribir la línea #include "funcion.h". Al hacer esto, la clase queda enlazada al archivo cpp y se podrá trabajar normalmente como se había hecho hasta ahora. Esto quedaría de la siguiente manera:

#include<iostream>
#include "funcion.h"
using std::cout;
using std::endl;

int main(void){

  ClaseMensaje mensaje;
  mensaje.funcionSaludo();

return 0;
}

Al compilar y ejecutar este pequeño ejemplo, esto es lo que muestra por la pantalla


Vamos a ver otro ejemplo. En esta oportunidad vamos a crear un programa que solicite un nombre y una edad. Esto es algo más complejo que el ejemplo anterior pero no tanto. Las funciones que guarden estos datos, se encontrarán en otra clase. Lo primero que voy a crear será la clase de las funciones. Esto quedaría de la siguiente manera:

//archivo.h – clase que mantiene funciones
#include<iostream>
#include<string>
using std::string;

//prototipo
 void setEdad(int);
 void setNombre(string);
 int getEdad();
 string getNombre();

class ClaseFuncion{
  private:
    int edad;
    string nombre;

  public:
    //constructor
    ClaseFuncion(){
      edad = 0;
      nombre = "";
    }

    //funciones set
    void setEdad(int ed){
      edad = ed;
    }
    void setNombre(string nom){
      nombre = nom;
    }
    //funciones get
    int getEdad(){
      return edad;
    }
    string getNombre(){
      return nombre;
    }

};

//Archivo.cpp – archivo principal 
#include<iostream>
#include<string>
#include "Funciones.h"

using std::cout;
using std::cin;
using std::endl;
using std::string;
using std::getline;

int main(void){

  int edad;
  string nombre;
  ClaseFuncion datos;

  cout << "Ingresa tu nombre: ";
  getline(cin, nombre);
  cout << "Ingresa tu edad: ";
  cin >> edad;

  datos.setNombre(nombre);
  datos.setEdad(edad);

  cout << "Nombre ingresado: " << datos.getNombre() << endl;
  cout << "Edad ingresada: " << datos.getEdad() << endl;

return 0;
}


Como se puede apreciar, en este código se utilizó las funciones get y set para poder recuperar los valores que fueron solicitados en la clase principal. Esto es todo por ahora. Saludos.


Gustavo J. Cerda Nilo
Noviembre 2016


http://codigogx.blogspot.cl/2017/06/capitulo-88-separar-la-interfaz-de-la.html

lunes, 14 de noviembre de 2016

Capítulo 83. Punteros en C


Diario de un programador. Día 185


Punteros en C


En esta oportunidad vamos a ver una de las características que hace que el lenguaje C sea tan poderoso y el porqué es elegido por sobre otros para desarrollar sistemas operativos, me refiero a los punteros, también conocidos como apuntadores. Los punteros tienen la capacidad de trabajar directamente en la memoria como veremos a continuación. Entender los punteros es algo complicado así que voy a ir lo más pausado posible y dar muchos ejemplos para que quede claro, es indispensable entenderlos bien ya que los capítulos siguientes incluirán el uso de punteros.

Ventajas del uso de punteros

- En el caso de paso de variables a una función por dirección o referencia, es necesario emplear parámetros formales como punteros
- Permiten realizar operaciones de asignación dinámica de memoria
- Permiten efectuar operaciones con estructuras de datos dinámicas
Todo lo mencionado anteriormente lo iremos viendo a medida que vamos avanzando.

Una primera aproximación

Hasta ahora sabemos que las variables contienen algún valor en específico, por ejemplo la siguiente variable llamada num1 contiene el valor de 7:

int num1 = 7;

Hasta aquí, todo bien. En cambio, un puntero lo que contiene es la dirección de memoria de alguna variable.
Entonces, podemos decir que un puntero es una variable que en vez de contener un valor, contiene una dirección de memoria. En este sentido, se entiende que una variable se refiere directamente a un valor y un apuntador se refiere indirectamente a un valor
Entonces, ¿si un puntero es una variable, como se declara?. Esto lo veremos a continuación.

¿Cómo se declara un puntero?

Para que el compilador entienda que lo que queremos declarar es un puntero y no una variable, hay que agregar el signo * antes del nombre que escogeremos para nuestro puntero. El signo * se conoce como operador de indirección.

Por ejemplo:

int *ptrNum1;

Por cierto, leí que los apuntadores deben ser inicializados cuando son declarados, para evitar algún resultado inesperado. Un apuntador se puede inicializar con valor 0, NULL o una dirección.
Estas inicializaciones quedarían de la siguiente manera:

int *prtNum1 =  0;
int *prtNum1 =  NULL;
int *prtNum1 =  &variable;

Las tres maneras de inicializar un puntero son válidas. Destaqué la tercera opción ya que de esa manera se indica una dirección. Al agregar el signo & indicamos que lo que se debe asignar es una dirección de memoria. ¿Recuerdas que la función scanf usa el operador & para asignar la variable a la memoria? El operador & se le conoce como el operador de dirección.
Recordar y aprender entonces que:

- El operador de indirección es el *
- El operador de dirección es el &

Ya que sabemos esto, veamos un pequeño ejemplo en código a continuación.


Implementando un puntero

El siguiente código muestra cómo asignar a un puntero la dirección de memoria de otra variable.

#include<stdio.h>

int main(void){
  int num1 = 7;
  int *ptrNum1 = &num1;
  printf("Valor de num1: %d\n", num1);
  printf("Valor del puntero prtNum1: %p\n", ptrNum1);
return 0;

}


En la línea 4 se crea una variable de tipo entero llamada num1, la cual tiene asignada el valor 7. En la línea 5 se crea una variable de tipo puntero llamada ptrNum1, la cual tiene asignada la dirección de la variable num1. Como se dijo antes, para asignar una dirección de memoria se utiliza el operador de dirección &.
La línea 7 imprime el valor de la variable num1, esta variable como ya dijimos contiene el valor 7. La línea 8 imprime el valor del puntero ptrNum1, como dijimos antes este puntero tiene asignado la dirección de memoria de la variable num1. Destaqué el modificador de formato que utiliza el puntero, el modificador %p se utiliza para mostrar el valor de los punteros, los cuales se muestran en formato hexadecimal. Si hubiese puesto un %d, mostraría su valor en formato decimal, pero por tratarse de direcciones de memoria, lo normal es mostrarlos en hexadecimal.
Al ejecutar este programa, me muestra el siguiente resultado


Muy bien, ahí tenemos una dirección de memoria, ¿pero cómo sabemos que esa dirección de memoria contiene el valor de la variable num1? Para este caso, utilizamos el operador de indirección *
Voy a agregar una línea más al programa anterior para averiguar el contenido de esa dirección de memoria.


#include<stdio.h>

int main(void){
  int num1 = 7;
  int *ptrNum1 = &num1;

  printf("Valor de num1: %d\n", num1);
  printf("Valor del puntero prtNum1: %p\n", ptrNum1);
  printf("Contenido del puntero prtNum1: %d\n", *ptrNum1);

return 0;
}



Vamos a ver otro ejemplo.
Vamos a crear un programa en el cual se tendrán dos variables. La primera variable tendrá un valor y la segunda variable será igualada a la primera. De esta forma:

int a = 7;
int b = a;

Lo siguiente que hará el programa será mostrar en pantalla el valor de ambas variables. Luego, se modificará el valor de la primera variable y se volverá a mostrar en pantalla el resultado. Como la segunda variable está igualada a la primera, lo normal sería pensar que su valor también será modificado. Veamos que sucede.

#include<stdio.h>

int main(void){
  int a = 7;
  int b = a;

  printf("valor de a: %d\n", a);
  printf("valor de b: %d\n\n", b);

  a = 10;
  printf("Nuevo valor de a: %d\n", a);
  printf("Nuevo valor de b: %d\n", b);

return 0;
}

Al ejecutar el programa, esto es lo que muestra:


El valor de la variable b no se modificó. Esto es porque la variable b, no es realmente una copia de la variable a. La variable b es una variable totalmente nueva que tiene su propia dirección de memoria y que se inició en un principio con el valor de la variable a. Es por esto que al modificar el valor de la variable a, la variable b se mantuvo sin cambios. Entonces ¿cómo hacemos para que la variable b actualice su valor a medida que la variable a se va modificando a lo largo del programa?, la respuesta es con el uso de punteros.

Vamos a modificar la variable b y la transformaremos en un puntero.


int main(void){
  int a = 7;
  int *b = &a;

  printf("valor de a: %d\n", a);
  printf("valor de b: %d\n\n", *b);

  a = 10;
  printf("Nuevo valor de a: %d\n", a);
  printf("Nuevo valor de b: %d\n", *b);

return 0;
}

Al ejecutar el programa con la modificación hecha, esto es lo que obtenemos.


Dejaremos este tutorial hasta aquí el día de hoy para que repasen bien este asunto antes de seguir ya que hay mucho más que aprender.
Buena suerte y saludos.

Gustavo J. Cerda Nilo
Septiembre 2016, Noviembre 2016



C++ El apuntador This

El apuntador This En C++, cada objeto tiene acceso a su propia dirección a través de un puntero o apuntador denominado This. Lo...