PDA

Ver la Versión Completa : Mostrar Fotos D' 5 En 5


cesarjbf
31-08-2003, 13:15:01
la pregunta es acerca de como me recomendabas mostrar 600 fotos jpg's,la idea es que se necesitan mostrar en forma de thumbnails de 5 en 5 por cada vez que se presiona un botton,un boton para mostrar las proximas 5(>>) y otro para mostrar las 5 anteriores(<<)yo decia que si era buena idea utilizar una forma por cada 5 fotos pero tendrian que ser 120 formas, no es malo para una sola aplicacion cargarla de tantas formas,hay otra manera? ........ Gracias de antemano

cesarjbf
10-09-2003, 14:10:20
listo,ya esta corregido el hilo ahora si puedo esperar la respuesta

andres1569
10-09-2003, 14:46:19
Hola:

Desde luego, ni se te ocurra crear 120 formularios para almacenar en cada uno 5 imágenes diferentes, a parte eso supone cargar en exceso el programa con imágenes, aunque estas sean en formato jpg.

Imagino que las imágenes se cargan desde una carpeta que tienes localizada. Voy a suponer que tienen nombres serializados, al estilo de Imagen0001, Imagen0002, ..., con ellos se simplifica la cosa porque ya sabemos de antemano los nombres de los ficheros que queremos cargar.

Supongamos también que has metido 5 componentes TImage en el formulario. Pues bien, creas una variable global NumImagen de tipo Integer, que almacena el número de la primera serie de imágenes que estás mostrando. Defines en el formulario un método CargaImagenes como el siguiente:


var
NumImagen : Integer;

Implementation

procedure TForm1.CargaImagenes;
var
NomArchivo : String;
begin
NomArchivo := DirImagenes + '\Imagen';
Image1.LoadFromFile (NomArchivo + FormatFloat ('0000', NumImagen + 1) + '.jpg');
Image2.LoadFromFile (NomArchivo + FormatFloat ('0000', NumImagen + 2) + '.jpg');
Image3.LoadFromFile (NomArchivo + FormatFloat ('0000', NumImagen + 3) + '.jpg');
Image4.LoadFromFile (NomArchivo + FormatFloat ('0000', NumImagen + 4) + '.jpg');
Image5.LoadFromFile (NomArchivo + FormatFloat ('0000', NumImagen + 5) + '.jpg');
end;

// inicializamos los TImage al cargar el formulario
procedure TForm1.Form1Create;
begin
NumImagen := 0;
CargaImagenes;
end;

procedire TForm1.BotonSiguientesClick (Sender: TObject);
begin
if NumImagen = 595 then EXIT;
Inc (NumImagen, 5);
CargaImagenes;
end;

procedire TForm1.BotonAnterioresClick (Sender: TObject);
begin
if NumImagen = 0 then EXIT;
Dec (NumImagen, 5);
CargaImagenes;
end;

Desarrollando esta idea debes conseguir lo que buscas. Si no tienes las imágenes con nombres serializados, de forma que se pueda deducir su nombre, lo ideal sería cargar en una lista (TStringList) los nombres de todos los ficheros .jpg, e ir cogiendo de ahí cada vez los nombres de los ficheros que queremos mostrar. Si tienes alguna duda sobre cómo hacer esto, vuelve a postear

Saludos

cesarjbf
17-09-2003, 10:37:24
MUCHAS GRACIAS CON ESTO MI PROBLEMA ESTA RESUELTO............

__cadetill
17-09-2003, 10:59:26
Hola

Si no tubieran nombres serializados, lo que puedes hacer es cargar el nombre de todas las imagenes en un TStringList mediante FindFirst y FindNext y utilizar la misma idea que te ha comentado el amigo Andrés pero con el índice (apuntador) del TStringList

cesarjbf
07-10-2003, 03:08:24
El problemma esta en que al momento de estar cambiando las imagenes tardan un tiempo no muy considerable en cargarse pero si uno quiere avanzar rapido hacia adelante entonces ese tiempo si es notorio, las imagenes tienen que ser menores a 12kb para no tardar tanto en cargar pero esto le resta mucha resolucion, como podria solucionarse esto? tal vez cargandolas en memoria, pero son 1000 imagenes (Las imagenes son puras caratulas de discos compactos).

Gracias de antemano..............

andres1569
10-10-2003, 21:15:38
Hola César, te voy a dar algunas ideas:

Lo normal, si el usuario va a navegar mucho por las imágenes, es que no quiera hacerlo siempre de forma secuencial; me explico, si tenemos 500 imágenes y estamos situados en el grupo 6 a la 10, y queremos ver el grupo 276-280, lo lógico es que brindemos algún mecanismo (poner algún control adecuado) para que ese salto sea inmediato, sin tener que recorrer todas las imégenes intermedias. Para ello puedes poner un TSpinEdit donde el usuario escriba el nº de foto a visualizar (o el nº de la primera foto del grupo, siguiendo el ejemplo que pones), y un botón BotonVerImagenes que ejecute esa carga. De acuerdo al código que te puse, sería algo así:

procedure TForm1.BotonVerImagenesClick (Sender: TObject);
begin
// para que coja a partir de la primera imagen del grupo de 5
// hacemos la siguiente instrucción
NumImagen := 5 * (SpìnEdit1.Value div 5);
if NumImagen = 600 then NumImagen := 595; // chapucilla casera
CargaImagenes;
end;
Acuérdate de fijar en el SpinEdit la propiedad MaxValue al total de imágenes disponibles (600 en tu caso)

Si aún así ves que algo va lento, o que el usuario sigue empecinado en recorrer las imágenes con los botones Anterior y Siguiente, deberías plantearte un poco más de programación, creando 5 objetos TPicture en el formulario e ir cargándolos uno tras otro mediante su método LoadFromFile (comprobando tras cada carga que el usuario no haya pulsado de nuevo un botón de avance/retroceso). Si se han cargado las 5 imágenes en sus respectivos TPicture, se dibujan mediante el método Draw sobre un Canvas (el del formulario mismo, o sobre un TPaintBox). Si el usuario pulsa un botón mientras se están cargando las imágenes, entonces no se dibuja nada y se empieza a cargar el siguiente grupo. De esta forma, todo irá más ligero.

Espero haberme explicado, un saludo

guillotmarc
10-10-2003, 23:11:14
Hola.

Además, cuando muestras en pantalla 5 imagenes, podrías ir cargando en memória las 5 siguientes y las 5 anteriores, de esta forma cuando quiera pasar a la siguiente página, será immediato.

Es decir, aprovechas el tiempo muerto en que el usuario está mirando las 5 imagenes en pantalla, para poner en memória las imagenes adyacentes.

De esta forma, el programa hará más trabajo, puesto que cargará imagenes en memoria que igual no nos van a hacer falta, pero el usuario tendrá la impresión de que es más rápido, puesto que el trabajo extra se hace en un tiempo muerto (mientras está mirando las fotos), y en cambio cuando el usuario seleccione una opción (Anterior, Siguiente) el programa responderá mucho más rápido.

Saludos.

roman
11-10-2003, 00:20:57
Vamos a ver:

andres1569 indica un punto crucial: ver las imágenes aleatoriamente y no secuencialmente.

marcguillot sugiere optimizar la visualización cargando imágenes por adelantado.

Ambos puntos son buenos pero algo incompatibles pues si seguimos la idea de andres1569 será difícil saber cuáles imágenes serán las próximas a cargar y si seguimos la idea de marcguillot será difícil dejar el acceso secuencial.

Sin embargo, ¿por qué no dejar que Windows se encargue de cuándo cargar las imágenes y aún así permitir un acceso aleatorio?

La propuesta que hago es usar un ListView en modo virtual


OwnerData := true;


En modo virtual nosotros especificamos cuántos items hay


ListView.Items.Count := 1000000; // ¡Incluso más si lo desean!


pero no se los damos de un sólo golpe sino que usamos el evento OnData del ListView. Este evento nos da un Item "falso" con sólo una propiedad asignada: el índice del item que debe mostrarse en ese momento. Nosotros llenamos el resto:


Item.Caption := Lista[Item.Index];


donde Lista podría ser un StringList que llenamos al principio del programa con los nombres de los archivos.

Ahora bien, ¿de dónde y cuándo sacamos la imágen de cada ítem?

Llenar un ImageList con todas las imágenes es desde luego impensable ya que justamente deseamos evitar cargar todas las imágenes al mismo tiempo.

Pero podemos usar el evento OnCustomDrawItem del ListView para cargar la imágen y dibujarla:


var
Rect: TRect;
Bmp: TBitmap;
X, Y: Integer;

begin
// Cargar la imagen
Bmp := TBitmap.Create;
Bmp.LoadFromFile('ruta al archivo' + Item.Caption);

// Obtener el rectángulo que ocupa el icono del item
ListView_GetItemRect(ListView.Handle, Item.Index, Rect, LVIR_ICON);

// Para centrar la imagen en el área del icono
X := Rect.Left + ((Rect.Right - Rect.Left) - Bmp.Width) div 2;
Y := Rect.Top + ((Rect.Bottom - Rect.Top) - Bmp.Height) div 2;

ListView.Canvas.Draw(L, T, Bmp);
Bmp.Free;


El área de los iconos deberá ser lo suficietemente grande para que quepa la imagen. Como las imágenes son "thumbnails" supongo que serán del mismo tamaño, digamos de 160x160.

Entonces sólo hay que asignar al ListView un ImageList con sus propiedades Width y Height en 160.

El ImageList no lo llenamos, es sólo para que el área de los iconos sea adecuada.

No lo he probado a consciencia más que nada porque en estos momentos no dispongode 1000 imágenes :p.

Lo que hice fue usar una sóla imagen cargando la lista de nombres de archivos con el mismo nombre de manera que realmente se está cargando cada vez la imágen.

Lo probé con 100,000 items y funciona bien.

La prueba final será con las 1000 mágenes distintas pero creo que vale la pena intentarlo.

// Saludos

guillotmarc
11-10-2003, 01:11:26
Hola.

Creo que mi propuesta y la de Andrés són compatibles. Por eso empezé con un Además porqué realmente me parece muy recomendable la proposición de Andrés.

Es absurdo obligar a pasar por todas las imagenes para ir a una imagen determinada. Por lo que es muy adecuado poner unos botones de inicio final y Ir a, (o una combo, un SpinEdit, ...).

Aunque cuando saltamos a una imagen (a un grupo de 5 imagenes en realidad), siempre podemos aprovechar, una vez mostradas las imagenes en pantalla, para ir cargando las siguientes imagenes, por si al usuario le da por ir a las anteriores o siguientes (són las imagenes con más probabilidades de ser seleccionadas por el usuario).

El tema del ListView me parece perfectamente correcto, como implementación concreta a utilizar. Puesto que además de permitir el salto a un punto determinado, propuesto por Andrés. También podemos aprovechar, una vez ha cargado la imagen el evento OnCustomDraw, para poner en memória (supongamos en unos TJpeg), las imagenes adyacentes, por si el usuario hace un movimiento secuencial, podamos utilizar esas imagenes en memória en la siguiente llamada al OnCustomDraw (disminuyendo drasticamente el tiempo que tardará en ejecutarse).

NOTA : Aunque utilizar un ListView me parece una solución correcta, tampoco no la encuentro necesaria. Puesto que 5 TPicture, y una combo o un SpinEdit (para saltar a una imagen determinada), me parecen más sencillos de implementar, e igualmente validos.

Saludos.

cesarjbf
22-10-2003, 15:00:27
Muchas gracias por responder...
El problema esta en que por cuestiones de hardware el usuario del aparato no cuenta mas que con dos botones para recorrer las imagenes, entonces solo queda la posibilidad de recorrerlas de 8 en 8 hasta las 1000 que existen, en realidad tendra que presionar 125 veces la tecla adelante para llegar desde la 0001 hasta la 1000, esto le tomara al usuario aproximadamente 60 segundos si es que tarda 60milisegundos entre serie y serie para recorrerlas todas, entonces cual seria la mas optima si en este momento esta tardando 1 minuto lo cual causa la desesperacion de los demas usuarios que quieren usar ell aparato?
Gracias.........

guillotmarc
22-10-2003, 15:31:09
Hola.

Si no quieres modificar la interfaz de usuario (añadir un pequeño TextBox con un SpinEdit opcional para indicar un salto a una imagen determinada). Entonces seguramente lo mejor será que utilizes un Thread (hilo de ejecución) secundario para cargar las imagenes (con la posiblidad de cancelar la carga antes que termine).

Eso te permite, que pulsen el botón, empiezen a cargarse las imagenes en un nuevo thread, y si cuando aún no han terminado de cargarse las imagenes, se vuelve a pulsar el botón, cancelas el thread que está cargando las imagenes, y abres un nuevo thread para cargar las ocho siguientes.

Consulta en la ayuda de Delphi, la clase TThread.

Saludos.

guillotmarc
22-10-2003, 15:44:10
El thread te permite una gran velocidad de respuesta en el momento de darle a siguiente. Puesto que cuando le des a siguiente va a empezar a cargar immediatamente las siguientes 8 imagenes (aunque de las imagenes actuales solo haya llegado a mostrar 2, el resto no se llegaran a cargar al cancelarse el thread en que se hacia esa carga).

Si quieres optimizar la visualización de las siguientes 8 imagenes, puedes añadir dentro del thread la carga anticipada de las 8 imagenes siguientes y anteriores, que comenté en un mensaje anterior. Como la carga anticipada de imagenes también se hará dentro del thread secundario, si el usuario se mueve rápido entre las imagenes, la carga anticipada no se hará puesto que no finalizará el thread en que debe hacerse, por lo que no vamos a tener ninguna perdida de velocidad por esta causa. En cambio si el usuario se detiene unos segundos a mirar las imagenes, la carga anticipada finalizará correctamente al poder ejecutarse todo el thread, y cuando el usuario cambie a las siguientes imagenes, el efecto será que se cargan immediatamente (puesto que ya las tendremos en memoria). NOTA : Seguramente esta optimización de realizar una carga anticipada de imagenes, la deberías dejar como una segunda fase, programando primero que se carguen correctamente las imagenes a mostrar, en un Thread.

Saludos.