Mostrando las entradas con la etiqueta Timer. Mostrar todas las entradas
Mostrando las entradas con la etiqueta Timer. Mostrar todas las entradas

martes, 21 de octubre de 2014

El Demo del Día: Capturar y Reproducir Pantallas en WinForms

Capturar y Reproducir Pantallas en WinForms

En este post veremos como crear una aplicación que permita lanzar cualquier aplicación o programa (archivo exe) y como capturamos la pantalla (en realidad las ventanas) de dicha aplicación hasta que esta finalice, ya sea en forma normal (por el cierre del usuario) o por una excepción No controlada por la aplicación.

Requerimiento

A veces no tenemos el código fuente de un programa que presenta problemas en su funcionamiento y no se sabe porque se cae, ya que No tiene Log de errores o una forma de saber porque se cerró.

Solución

Para resolver el problema vamos a construir una aplicación en Windows Forms que permita lanzar la aplicación o programa, luego capturar sus pantallas y finalmente mostrar la secuencia de pantallas como si fuera un video, aunque no lo sea (por el momento).

La técnica consiste en crear un objeto Pantalla con un par de propiedades: Fecha y Hora de Captura e Imagen a Capturar, luego lanzaremos la aplicación con Process.Start y habilitaremos un Timer que cada segundo verifique si el proceso esta en ejecución y si lo esta enviaremos la tecla capturar ventana y recuperaremos del Clipboard la imagen guardándola en el objeto y agregándola a una lista de objetos.

Si se salio de la aplicación analizada, se serializará en forma binaria y se guardará la lista de objetos en un archivo llamado "PrintScreen.dll" e inmediatamente se habilitará otro Timer que permite mostrar cada imagen de la lista de imagen.

Crear la Aplicación Windows en Windows Forms

Crear un Nuevo Proyecto "Aplicación de Windows Forms" en C# llamado: "CapturaPantalla" y agregar la clase "bePantalla" como sigue:

using System;
using System.Collections.Generic;
using System.Drawing;

namespace CapturaPantalla
{
    [Serializable]
    public class bePantalla
    {
        public DateTime FechaHora { get; set; }
        public Image Imagen { get; set; }
    }
}

Nota: Se usa el atributo Serializable sobre la clase bePantalla porque vamos a guardarlo en un archivo binario serializando.

Cambiar de nombre al formulario por: "frmCaptura" y arratrar los siguientes controles:
- Panel: pnlCaptura
- Label: lblTitulo con el Text="Selecciona el Programa a Capturar la Pantalla"
- TextBox: txtArchivo
- Button: btnIniciar con Text="Iniciar"
- Timer: tmrCaptura con Interval=1000
- Timer: tmrReproduce con Interval=1000

El diseño de la pantalla debe quedar como se muestra a continuación:


Escribir el siguiente código en el formulario "frmCaptura":

using System;
using System.Collections.Generic; //List
using System.Windows.Forms; //Form, SendKeys, Clipboard, etc
using System.Drawing; //Image, Bitmap, Size
using System.Diagnostics; //Process
using System.IO; //FileStream, FileMode, FileAccess, FileShare
using System.Runtime.Serialization.Formatters.Binary; //BinaryFormatter

namespace CapturaPantalla
{
    public partial class frmCaptura : Form
    {
        private Process proceso;
        private List<bePantalla> lbePantalla;
        private int c;

        public frmCaptura()
        {
            InitializeComponent();
        }

        private void mostrarDialogoAbrir(object sender, EventArgs e)
        {
            OpenFileDialog ofd = new OpenFileDialog();
            ofd.Title = "Selecciona un archivos de programa";
            ofd.Filter = "Archivos de programa|*.exe";
            if (ofd.ShowDialog().Equals(DialogResult.OK))
            {
                txtArchivo.Text = ofd.FileName;
            }
        }

        private void iniciarCaptura(object sender, EventArgs e)
        {
            this.Visible = false;
            pnlCaptura.Visible = false;
            lbePantalla = new List<bePantalla>();
            proceso = Process.Start(txtArchivo.Text);
            tmrCaptura.Enabled = true;
            tmrCaptura.Start();
        }

        private void capturarPantalla(object sender, EventArgs e)
        {
            if (proceso != null && !proceso.HasExited)
            {
                SendKeys.SendWait("%{PRTSC}");
                Image img = Clipboard.GetImage();
                if (img != null)
                {
                    bePantalla obePantalla = new bePantalla();
                    obePantalla.FechaHora = DateTime.Now;
                    obePantalla.Imagen = img;
                    lbePantalla.Add(obePantalla);
                }
            }
            else
            {
                this.Visible = true;
                tmrCaptura.Stop();
                tmrCaptura.Enabled = false;
                using(FileStream fs=new FileStream("PrintScreen.dll",
                    FileMode.Create, FileAccess.Write,FileShare.Write))
                {
                    BinaryFormatter bf = new BinaryFormatter();
                    bf.Serialize(fs, lbePantalla);
                }
                tmrReproduce.Enabled = true;
                tmrReproduce.Start();
                c = 0;
            }
        }

        private void reproducirCaptura(object sender, EventArgs e)
        {
            if (lbePantalla.Count>0)
            {
                if (c < lbePantalla.Count)
                {
                    this.Text = String.Format("{0} de {1} - {2}",c+1,
                        lbePantalla.Count,lbePantalla[c].FechaHora);
                    this.BackgroundImage = new Bitmap(lbePantalla[c].Imagen,
                        new Size(this.ClientRectangle.Width,this.ClientRectangle.Height));
                    c++;
                }
                else c = 0;
            }
        }
    }
}

Nota: La asociación de funciones controladoras de eventos es la siguiente:
- mostrarDialogoAbrir: Evento Click del botón "btnAbrir"
- iniciarCaptura: Evento Click del botón "btnIniciar"
- capturarPantalla: Evento Tick del timer "tmrCaptura"
- reproducirCaptura: Evento Tick del timer "tmrReproduce"

Ejecutar y Probar la Aplicación Windows Forms

Grabar la aplicación, ejecutarla y se mostrará la siguiente ventana:


Seleccionar un archivo de programa a ejecutar y capturar sus pantallas, por ejemplo seleccione el archivo llamado: "AppCapturar.exe" que viene en la carpeta bin\debug del Demo y se mostrará la ventana con dicha aplicación.

Nota: En ese momento el panel con el archivo seleccionado se ha ocultado para después mostrar en toda la ventana o formulario las pantallas capturadas.

Esta aplicación permite ingresar los datos del Empleado: Código (string), Nombre (string) y Sueldo (decimal) en un objeto, pero No hay una validación para el tipo de datos.

Ingresar unos cuantos empleados, como se muestra en la figura:


Después de ingresar correctamente varios, probar ingresar textos en el sueldo y al grabar la aplicación se caerá e inmediatamente finalizará regresando a la aplicación anterior (CapturaPantalla).

Se creará un archivo llamado "PrintScreen.dll" con la lista de objetos (pantallas) serializadas y se mostrará el contenido de la lista en el mismo formulario como si fuese un video, tal como se muestra a continuación:


Nota: La imagen de la pantalla capturada se ha ajustado al tamaño del formulario o ventana.

Comentario

Si bien es cierto, existen muchos programas que capturan pantalla, lo graban en un video y lo reproducen, en este demo, hemos visto que es relativamente sencillo capturar y reproducir mediante poco código.

En nuestro caso hemos usado los métodos "SendKeys.SendWait" para capturar la ventana activa, "Clipboard.GetImage" para recuperar de la memoria la imagen y una Lista de Objetos para almacenar las pantallas y luego reproducirlas.

En un post futuro la secuencia de imágenes o pantallas capturadas la grabaremos en un formato de video, como un archivo AVI, MPEG o MP4. Inténtelo desde ahora y diviértanse en el intento.

Descarga:
Demo22_CapturaReproducePantalla

jueves, 2 de octubre de 2014

El Demo del Día: Gráficos de Lineas y Curvas en WinForms

Gráficos de Lineas y Curvas en WinForms

Hoy veremos como crear gráficos en .NET dinamicamente usando la clase Graphics del espacio de Nombres System.Drawing, la cual tiene métodos que permiten dibujar textos, lineas, curvas, etc.

Para el ejemplo lo haremos mediante un Timer que genera números al azar con los cuales se crearan los puntos a graficar, pero en un caso real, en vez del Timer podemos leer de una base de datos una serie de datos o también recibir por el puerto serial o via sockets leer el valor de un dispositivo conectado a la computadora, por ejemplo un termómetro, balanza electrónica, medición de latidos del corazón, pulsos, etc.

Crear la Aplicación Windows Forms

En el Visual Studio crear una aplicación en C# llamada: "GraficoLineasCurvas" y renombrar el formulario a "frmGrafico". Arrastrar solo un control Timer y llamarlo: "tmrGrafico" configurando sus propiedad Enabled en True, para que se habilite cada 100 milisegundos el gráfico.

El diseño del formulario debe quedar como se muestra en la figura:



Escribir el siguiente código en el formulario, sobre todo la función "graficarSeries" que esta asociado al evento "Tick" del timer "tmrGrafico":

using System;
using System.Drawing;
using System.Windows.Forms;

namespace GraficoLineasCurvas
{
    /*Creado por Luis Dueñas para el Blog*/
    public partial class frmGrafico : Form
    {
        private int x = 0;
        private int n = 40;
        private Point punto1Linea;
        private Point punto1Curva;

        public frmGrafico()
        {
            InitializeComponent();
        }

        private void graficarSeries(object sender, EventArgs e)
        {
            Random oAzar = new Random();
            int yLinea = oAzar.Next(this.Height-200) + 100;
            int yCurva = oAzar.Next(this.Height-200) + 100;
            if (x == 0)
            {
                punto1Linea = new Point(x,yLinea);
                punto1Curva = new Point(x, yCurva);
            }
            Point punto2Linea = new Point(x, yLinea);
            Point punto4Curva = new Point(x, yCurva);
            Graphics grafico = this.CreateGraphics();
            grafico.DrawString("Gráficos Dinámicos en .NET (Por Lduenas)",
                new Font("Arial",40),Brushes.Green,100,20);
            grafico.DrawLine(new Pen(Brushes.Red, 4), punto1Linea, punto2Linea);

            Point punto2Curva;
            Point punto3Curva;
            int c = (int)((x/n)-1)*n;
            if (punto1Curva.Y >= punto4Curva.Y)
            {
                punto2Curva = new Point(c + (n/4), punto4Curva.Y);
                punto3Curva = new Point(c+((3*n) / 4), punto1Curva.Y);              
            }
            else
            {
                punto2Curva = new Point(c+ (n / 4), punto1Curva.Y);
                punto3Curva = new Point(c+ ((3 *n) / 4), punto4Curva.Y);              
            }
            grafico.DrawBezier(new Pen(Brushes.Blue, 4), punto1Curva, punto2Curva,
            punto3Curva, punto4Curva);
         
            x += n;
            punto1Linea = punto2Linea;
            punto1Curva = punto4Curva;
            if (x >= this.Width)
            {
                x = 0;
                grafico.Clear(this.BackColor);
            }
        }
    }
}

Nota: Se crea la variable x para la posición de la columna y la variable y para la fila, ademas 2 variables punto1Linea y punto1Curva para guardar el punto anterior desde donde se iniciara el gráfico.

Para las lineas solo necesitamos 2 puntos: inicio (punto1Linea) y fin (punto2Linea), pero para la curva de Bézier necesitamos 4 puntos: punto de anclaje de inicio (punto1Curva), punto de control 1 (punto2Curva), punto de control 2 (punto3Curva), y punto de anclaje final (punto4Curva).

La variable n (por defecto 40) indica el intervalo en que aumenta las columnas o posición x.

Probar la Aplicación Creada

Grabar el proyecto y ejecutarlo con F5. Aparece una ventana maximizada como en la siguiente figura:




El texto con el título se mostrará de color verde, el gráfico de Lineas se mostrará en color rojo y el gráfico de Curvas en color azul.

Cuando llegue al final del ancho se borrará el dibujo y se creará nuevamente. Puedes experimentar modificando el tamaño del intervalo n de 40 a mas o menos.

Comentario Final

Es muy fácil crear gráficos dinámicos en .NET, ya sea de lineas o curvas, sin necesidad de recurrir a ningún Control, ya que estos solo se utilizan para graficar datos históricos (existentes den bases de datos, archivos de textos, xml, etc), pero, si deseamos graficar en tiempo real mejor es crear nuestro gráfico dinámicamente usando la clase Graphics de System.Drawing.

Descarga
Demo20_GraficoLineasCurvas


viernes, 26 de septiembre de 2014

El Demo del Día: Visor de Imágenes con Zoom en WinForms

Visor de Imágenes con Zoom en WinForms

Este post es a pedido de un alumno que quiere una aplicación en Windows Forms que muestre una secuencia de Imágenes y pueda realizar el Zoom.

Crear la Aplicación Windows Forms

Primero crearemos una aplicación en Windows Forms en C# llamada: "VisorImagenesZoom".
Cambiar de nombre al formulario por el de "frmVisor", luego diseñar la pantalla como se muestra en la siguiente figura:


Nota: También hay que incluir un Timer llamado "tmrPreview" que cada 3 segundos muestre las imágenes del directorio seleccionado.

Escribir el siguiente código en el formulario:

using System;
using System.Collections.Generic;
using System.Drawing;
using System.Windows.Forms;
using System.IO;

namespace VisorImagenesZoom
{
    public partial class frmVisor : Form
    {
        private int c;
        private int n;
        private Point punto1;

        public frmVisor()
        {
            InitializeComponent();
        }

        private void abrirDirectorio(object sender, EventArgs e)
        {
            FolderBrowserDialog fbd = new FolderBrowserDialog();
            if (fbd.ShowDialog().Equals(DialogResult.OK))
            {
                txtDirectorio.Text = fbd.SelectedPath;
                IEnumerable<string> archivos = Directory.EnumerateFiles(fbd.SelectedPath, "*.jpg");
                lstArchivo.Items.Clear();
                lstArchivo.BeginUpdate();
                foreach (string archivo in archivos)
                {
                    lstArchivo.Items.Add(Path.GetFileNameWithoutExtension(archivo));
                }
                lstArchivo.EndUpdate();
                txtTotalArchivos.Text = lstArchivo.Items.Count.ToString();
                tmrPreview.Enabled = true;
                tmrPreview.Start();
            }
        }

        private void mostrarImagenes(object sender, EventArgs e)
        {
            lstArchivo.SelectedIndex = c;
            string archivo = Path.Combine(txtDirectorio.Text, lstArchivo.Items[c].ToString() + ".jpg");
            picPreview.LoadAsync(archivo);
            c++;
            if (c == lstArchivo.Items.Count) c = 0;
        }

        private void configurarZoom(object sender, MouseEventArgs e)
        {
            if (e.Button.Equals(MouseButtons.Left))
            {
                if (n == 0)
                {
                    punto1 = e.Location;
                    tmrPreview.Stop();
                    tmrPreview.Enabled = false;
                }
                else
                {
                    if (n == 1)
                    {
                        Point punto2 = e.Location;
                        string archivo = Path.Combine(txtDirectorio.Text,
                                                   lstArchivo.Items[c].ToString() + ".jpg");
                        Bitmap bmp = new Bitmap(picPreview.Image);
                        Bitmap bmpZoom = new Bitmap(bmp.Width, bmp.Height);
                        Graphics g = Graphics.FromImage(bmpZoom);
                        float ratioX = (bmp.Width/picPreview.Width);
                        float ratioY = (bmp.Height / picPreview.Height);
                        int posX = (int)(ratioX * punto1.X);
                        int posY = (int)(ratioY * punto1.Y);
                        int ancho = (int)(ratioX*(punto2.X-punto1.X));
                        int alto = (int)(ratioY * (punto2.Y - punto1.Y));
                        Rectangle srcRect = new Rectangle(posX, posY, ancho, alto);
                        Rectangle dstRect = new Rectangle(0, 0, bmpZoom.Width, bmpZoom.Height);
                        g.DrawImage(bmp, dstRect, srcRect, GraphicsUnit.Pixel);
                        picPreview.Image = bmpZoom;
                        tmrPreview.Enabled = true;
                        tmrPreview.Start();
                    }
                }
                n++;
                if (n == 2) n = 0;
            }
        }
    }
}

Nota: Para programar el Zoom se ha asociado al evento MouseClick del PictureBox llamado "picPreview" donde en el primer clic se captura la ubicación del punto y se guarda en una variable "punto1" y en el segundo clic se captura el punto2 y se crea un rectangulo con la imagen para el Zoom. Solo hay que tener en cuenta que el Rectangulo debe ser en base a la imagen (Bitmap) del PictureBox y No al tamaño del control (con propiedad SizeMode en StrechImage), por lo cual se crea las variables ratios.

Probar la Aplicación Creada

Grabar el proyecto y ejecutarlo con F5. Seleccionar un directorio donde se encuentren archivos jpg y se mostraran en la lista tal como aparece en la siguiente figura:


Cada 3 segundos cambiara la imagen y se seleccionara en la lista el archivo que se esta mostrando, dar el primer clic sobre la imagen y luego el segundo clic mostrará el preview de la imagen, tal como sigue:


Comentario Final

En este post hemos visto como mostrar una secuencia de imágenes usando un Timer y también como realizar el Zoom de una Imagen usando el método DrawImage de la clase Graphics para dibujar un rectángulo con la imagen especificada al dar dos clics, uno para marcar el punto de inicio y otro para marcar el punto final.

Descarga:
Demo19_VisorImagenesZoom



martes, 17 de junio de 2014

El Demo del Día: Animación usando Sprites en Windows Forms

Animación usando Sprites en Windows Forms

El Demo de hoy es una animación en Windows Forms usando un sprite o archivo con una secuencia de imagenes. Los sprites permiten reducir el numero de accesos a archivos para una aplicación que trabaja con imagenes, sobre todo es muy usado en los juegos.

Paso 1: Archivo con el Sprite a Animar

En este ejemplo tenemos un archivo llamado "Caminante.jpg" que tiene 3 filas x 6 columnas, es decir 18 cuadros, pero en este caso solo hay 14 imágenes diferentes. La primera fila tiene 6 imágenes con posiciones hacia la derecha y la segunda fila tiene 6 imágenes con posiciones hacia la izquierda.
El archivo tiene una dimensión de 624 x 450 pixeles por lo que cada imagen seria de 104 de ancho por 150 de alto.



Paso 2: Diseño del Formulario Windows Forms en .NET

Debemos crear una Aplicación en C# para Windows Forms llamada: "Animacion_Sprite" y renombrar el formulario a "frmCaminante".
Primero agregar un control PictureBox llamado "picCaminante" de tamaño "50x50", luego 2 controles RadioButton: "rbDerecha" y "rbIzquierda".
Finalmente, agregar un Timer llamado: "tmrAnima" con la propiedad Enable en "true" y el Interval en "100" milisegundos, tal como se muestra en la figura:



Paso 3: Código del Formulario en C# .NET

Escribir el siguiente código en el formulario, la función cargarImagen esta asociada al evento "load" del formulario y la función "animarCaminante" esta asociado al evento "Tick" del Timer. Además la función "caminarDerecha" esta asociada al radio button "rbDerecha" y la función: "caminarIzquierda" esta asociada al radio button "rbIzquierda".

    public partial class frmCaminante : Form
    {
        Bitmap bmp;
        Rectangle rect;
        int posX,posY,x,c;

        public frmCaminante()
        {
            InitializeComponent();
        }

        private void cargarImagen(object sender, EventArgs e)
        {
            bmp = new Bitmap("Caminante.png");          
        }

        private void animarCaminante(object sender, EventArgs e)
        {
            rect = new Rectangle(posX, posY, 104, 150);
            picCaminante.Image = bmp.Clone(rect, bmp.PixelFormat);
            if (rbDerecha.Checked) animarDerecha();
            if (rbIzquierda.Checked) animarIzquierda();
        }

        private void animarDerecha()
        {
            posX += 104;
            c++;
            picCaminante.Left = x;
            if (x <= this.Width) { x += 10; }
            else { x = -picCaminante.Width; }
            if (c == 5)
            {
                c = 0;
                posX = 104;
            }
        }

        private void animarIzquierda()
        {
            posX += 104;
            c++;
            picCaminante.Left = x;
            if (x>-picCaminante.Width) { x -= 10; }
            else { x = this.Width; }
            if (c == 5)
            {
                c = 0;
                posX = 104;
            }
        }

        private void caminarDerecha(object sender, EventArgs e)
        {
            if (rbDerecha.Checked)
            {
                c = 0;
                posX = 104;
                posY = 0;
            }
        }

        private void caminarIzquierda(object sender, EventArgs e)
        {
            if (rbIzquierda.Checked)
            {
                c = 0;
                posX = 104;
                posY = 150;
            }
        }
    }

Paso 4: Ejecución del Formulario Windows Forms

Después de escribir el código de arriba, crear el archivo: "Caminante.jpg" en la carpeta bin/Debug de la aplicación y ejecutar pulsando F5. Por defecto la animación se inicia a la derecha, para cambiar seleccionar el radio button.


Descarga:
Demo04_Animacion_Sprite