jueves, 16 de abril de 2020

TkInter - de marciano a humano- seccion 7 - Button widget (widget para crear botones)

 

Para crear un botón en una ventana o marco de nivel superior llamado padre:

w=tk.Button(parent, opction=value, ...)

El constructor devuelve el nuevo widget de botón.

Tabla 5. Opciones de widget de botones

activebackground

Color de fondo cuando el botón está debajo del cursor. ejemplo:

activebackground=’#F50743’

Ejemplo sin el cursor arriba


Ejemplo con el cursor arriba:

IMPORTANTE: no hay que olvidarse del #. Consulte la seccion 5.3.

activeforeground

Color de primer plano cuando el botón está debajo del cursor. ejemplo:

activeforeground=’#F50743’

Ejemplo sin el cursor arriba:

Ejemplo con el cursor arriba:

anchor

Es donde se coloca el texto dentro del botón. Consulte la Sección 5.5. Los valores posibles son:

NW

N

NE

W

CENTER

E

SW

S

SE

Ejemplo con anchor="nw"

Ejemplo con anchor="n"

Ejemplo con anchor="ne"

Ejemplo con anchor="w"

Ejemplo con anchor="center"

Ejemplo con anchor="e"

Ejemplo con anchor="sw"

Ejemplo con anchor="s"

Ejemplo con anchor="se"

bd o borderwidth

Ancho del borde alrededor del exterior del botón, ver Sección 5.1. El valor predeterminado es dos píxeles pero en este ejemplo lo vemos exagerado en 15:

borderwidth=15 o bd=15 (puede escribirse de las dos formas)

bg o background

Color de fondo normal, igual que el caso anterior se puede escribir de dos formas:

background="#38EB5C"

 o bg="#38EB5C"

bitmap

Nombre de uno de los mapas de bits estándar para mostrar en el botón (en lugar de texto). los siguientes ejemplos:

- bitmap="error"

- bitmap="gray75"

- bitmap="gray50"

- bitmap="gray25"

- bitmap="gray12"

- bitmap="hourglass"

- bitmap="info"

- bitmap="questhead"

- bitmap="question"

- bitmap="warning"

dan como resultado lo siguiente:

command

Función o método a llamar cuando se hace clic en el botón. ejemplo:

command=funcX()

cursor

Selecciona el cursor que se mostrará cuando el mouse esté sobre el botón. Ver Sección 5.8.

default

Los botones pueden estar activados o desactivados tk.NORMAL es el valor predeterminado, use tk.DISABLED si el botón debe ser inicialmente deshabilitado (atenuado, no responde a los clics del mouse).

Disabledforeground

Color de primer plano utilizado cuando el botón está desactivado.

fg o foreground

Color normal de primer plano (texto).

font

Fuente de texto que se utilizará para la etiqueta del botón.

height

Altura del botón en líneas de texto (para botones de texto) o píxeles (para imágenes).

highlightbackground

Color del foco resaltado cuando el widget no tiene foco.

highlightcolor

El color del foco resaltado cuando el widget tiene foco.

highlightthickness

Espesor del foco resaltado.

image

Imagen que se mostrará en el botón (en lugar de texto).

justify

Cómo mostrar varias líneas de texto:

tk.LEFT para justificar a la izquierda cada línea

tk.CENTER para centrarlos

tk.RIGHT para justificar a la derecha cada línea.

overrelief

El estilo de relieve que se utilizará mientras el mouse está en el botón, su valor por defecto es tk.REISED. Consulte la Sección 5.6.

padx

Relleno adicional a la izquierda y a la derecha del texto. Consulte la Sección 5.1 para los posibles valores de relleno.

pady

Relleno adicional encima y debajo del texto.

relief

Especifica el tipo de relieve para el botón (consulte la Sección 5.6). El valor predeterminado es tk.RAISED.

repeatdelay

Ver el intervalo de repetición, a continuación.

repeatinterval

Normalmente, un botón se dispara solo una vez cuando el usuario suelta el botón del mouse.

Si desea que el botón se active a intervalos regulares siempre que el botón del mouse se mantiene presionada, establezca esta opción en un número de milisegundos que se utilizará entre se repite y establece el retraso de repetición en la cantidad de milisegundos que esperará antes de comenzar a repetir. Por ejemplo, si especifica repeatdelay=500, repeatinterval=100 el botón se disparará después de medio segundo, y cada décima de segundo después, hasta que el usuario suelte el botón del mouse. Si el usuario no mantiene pulsado el botón del ratón, al menos 500 milisegundos, el botón se disparará normalmente.

state

Establezca esta opción en tk.DISABLED para atenuar el botón y hacer que no se elimine. Tiene el valor tk.ACTIVE cuando el mouse está sobre él. El valor predeterminado es tk.NORMAL.

takefocus

Normalmente, el enfoque del teclado visita los botones, consulte la Sección 53, y un carácter de espacio actúa igual que un mouse haga clic, "presionando" el botón. Puede establecer la opción de enfoque automático en cero para evite que el foco visite el botón.

text

Texto que se muestra en el botón. Use líneas nuevas internas para mostrar texto múltiple líneas.

textvariable

Una instancia de StringVar() que está asociada con el texto en este botón. Si se cambia la variable, el nuevo valor se mostrará en el botón de forma automática. Consulte la Sección 52.

underline

El valor predeterminado es -1, lo que significa que ningún carácter del texto en el botón será subrayada. Si no es negativo, el carácter de texto correspondiente estará subrayado. Por ejemplo, underline=1 subrayaría el segundo carácter del texto del botón.

width

Ancho del botón en letras (si se muestra texto) o píxeles (si se muestra un imagen).

Wraplength

Si este valor se establece en un número positivo, las líneas de texto se ajustarán para ajustarse dentro de esta longitud. Para conocer los posibles valores, consulte la Sección 5.1.


Métodos en objetos Button:

.flash()

Hace que el botón parpadee varias veces entre los colores activos y normales. Deja el botón en el estado original. Ignorado si el botón está deshabilitado.

.invoke()

Llama a la devolución de llamada de comando del botón y devuelve lo que devuelve esa función. No tiene ningún efecto si el botón está desactivado o no hay devolución de llamada.

Hasta el próximo jueves, salud y saludos!
Mauricio José Tobares

jueves, 9 de abril de 2020

TkInter - de marciano a humano - sección 6 - manejo de errores

 

La excepción planteada por la mayoría de los errores de programación es tk.TclError, Tkinter no tiene un buen manejo de propagación pero se pueden utilizar decoradores try/except, en la documentación oficial de Python (en el capítulo 9) se trata el tema del manejo de excepciones, así es que tomaremos la información de dicho capítulo para llenar este espacio vacío que nos deja TkInter.

Hasta ahora los mensajes de error no habían sido más que mencionados, pero si probaste los ejemplos probablemente hayas visto algunos. Hay (al menos) dos tipos diferentes de errores:

- errores de sintaxis.

- excepciones.


6.1 Errores de sintaxis

Los errores de sintaxis, también conocidos como errores de interpretación, son quizás el tipo de queja más común que surgen cuando todavía se está aprendiendo Python:

>>> while True print('Hola mundo')

    File "<stdin>", line 1

    while True print('Hola mundo')

                                  ^

SyntaxError: invalid syntax

El intérprete repite la línea culpable y muestra una pequeña ‘flecha’ que apunta al primer lugar donde se detectó el error. Este es causado por (o al menos detectado en) el símbolo que precede a la flecha, en el ejemplo, el error se detecta en la función print(), ya que faltan dos puntos (':') antes del mismo. Se muestran el nombre del archivo y el número de línea para que sepas dónde mirar en caso de que la entrada venga de un programa.


6.2 Excepciones

Incluso si la declaración o expresión es sintácticamente correcta, puede generar un error cuando se intenta ejecutarla. Los errores detectados durante la ejecución se llaman excepciones, y no son incondicionalmente fatales, sin embargo, la mayoría de las excepciones no son manejadas por los programas, y resultan en mensajes de error como los mostrados aquí:

>>> 10 * (1/0)

Traceback (most recent call last):

    File "<stdin>", line 1, in <module>

ZeroDivisionError: division by zero

 

>>> 4 + spam*3

Traceback (most recent call last):

    File "<stdin>", line 1, in <module>

NameError: name 'spam' is not defined

 

>>> '2' + 2

Traceback (most recent call last):

    File "<stdin>", line 1, in <module>

TypeError: Can't convert 'int' object to str implicitly

La última línea de los mensajes de error indica qué sucedió. Las excepciones vienen de distintos tipos, y el tipo se imprime como parte del mensaje, los tipos en el ejemplo son:

- ZeroDivisionError

- NameError

- TypeError

La cadena mostrada como tipo de la excepción es el nombre de la excepción predefinida que ocurrió. Esto es verdad para todas las excepciones predefinidas del intérprete, pero no necesita ser verdad para excepciones definidas por el usuario (aunque es una convención útil). Los nombres de las excepciones estándar son identificadores incorporados al intérprete (no son palabras clave reservadas).

El resto de la línea provee un detalle basado en el tipo de la excepción y qué la causó.

La parte anterior del mensaje de error muestra el contexto donde la excepción sucedió, en la forma de un “trazado del error” listando líneas fuente, sin embargo, no mostrará líneas leídas desde la entrada estándar.

el siguiente enlace lista las excepciones predefinidas y sus significados:

https://docs.python.org/es/3/library/exceptions.html


6.3 Manejando excepciones

Es posible escribir programas que manejen determinadas excepciones. Mirá el siguiente ejemplo, que le pide al usuario una entrada hasta que ingrese un entero válido, pero permite al usuario interrumpir el programa (usando Control-C o lo que sea que el sistema operativo soporte), una interrupción generada por el usuario se señaliza generando la excepción KeyboardInterrupt.

>>> while True:

...     try:

...         x = int(input("Por favor ingrese un número: "))

...         break

...     except ValueError:

...         print("Oops! No era válido. Intente nuevamente...")

...

La declaración try funciona de la siguiente manera:

Primero, se ejecuta el bloque try (el código entre las declaración try y except).

Si no ocurre ninguna excepción, el bloque except se saltea y termina la ejecución de la declaración try.

Si ocurre una excepción durante la ejecución del bloque try, el resto del bloque se saltea. Luego, si su tipo coincide con la excepción nombrada luego de la palabra reservada except, se ejecuta el bloque except, y la ejecución continúa luego de la declaración try.

Si ocurre una excepción que no coincide con la excepción nombrada en el except, esta se pasa a declaraciones try de más afuera, si no se encuentra nada que la maneje, es una excepción no manejada, y la ejecución se frena con un mensaje como los mostrados arriba.

Una declaración try puede tener más de un except, para especificar manejadores para distintas excepciones. A lo sumo un manejador será ejecutado. Sólo se manejan excepciones que ocurren en el correspondiente try, no en otros manejadores del mismo try. Un except puede nombrar múltiples excepciones usando paréntesis, por ejemplo:

... except (RuntimeError, TypeError, NameError):

...     pass

Una clase en una clausula except es compatible con una excepción si la misma esta en la misma clase o una clase base de la misma (pero no de la otra manera — una clausula except listando una clase derivada no es compatible con una clase base). Por ejemplo, el siguiente código imprimirá B, C, D, en ese orden:

class B(Exception):

    pass

 

class C(B):

    pass

 

class D(C):

    pass

 

for cls in [B, C, D]:

    try:

        raise cls()

    except D:

        print("D")

    except C:

        print("C")

    except B:

        print("B")

Notese que si las clausulas de except estuvieran invertidas (con except B primero), habría impreso B, B, B — la primera clausula de except coincidente es usada.

El último except puede omitir nombrar qué excepción captura, para servir como comodín. Usá esto con extremo cuidado, ya que de esta manera es fácil ocultar un error real de programación. También puede usarse para mostrar un mensaje de error y luego re-generar la excepción (permitiéndole al que llama, manejar también la excepción):

import sys

 

try:

    f = open('miarchivo.txt')

    s = f.readline()

    i = int(s.strip())

except OSError as err:

    print("Error OS: {0}".format(err))

except ValueError:

    print("No pude convertir el dato a un entero.")

except:

    print("Error inesperado:", sys.exc_info()[0])

    raise

Las declaraciones try ... except tienen un bloque else opcional, el cual, cuando está presente, debe seguir a los except. Es útil para aquel código que debe ejecutarse si el bloque try no genera una excepción. Por ejemplo:

for arg in sys.argv[1:]:

    try:

        f = open(arg, 'r')

    except OSError:

        print('no pude abrir', arg)

    else:

        print(arg, 'tiene', len(f.readlines()), 'lineas')

        f.close()

El uso de else es mejor que agregar código adicional en el try porque evita capturar accidentalmente una excepción que no fue generada por el código que está protegido por la declaración try ... except.

Cuando ocurre una excepción, puede tener un valor asociado, también conocido como el argumento de la excepción. La presencia y el tipo de argumento depende del tipo de excepción.

El except puede especificar una variable luego del nombre de excepción. La variable se vincula a una instancia de excepción con los argumentos almacenados en instance.args. Por conveniencia, la instancia de excepción define __str__() para que se pueda mostrar los argumentos directamente, sin necesidad de hacer referencia a .args. También se puede instanciar la excepción primero, antes de generarla, y agregarle los atributos que se desee:

>>> try:

...    raise Exception('carne', 'huevos')

... except Exception as inst:

...    print(type(inst))    # la instancia de excepción

...    print(inst.args)     # argumentos guardados en .args

...    print(inst)          # __str__ permite imprimir args directamente,

...                         # pero puede ser cambiado en subclases de la exc

...    x, y = inst          # desempacar argumentos

...    print('x =', x)

...    print('y =', y)

...

<class 'Exception'>

('carne', 'huevos')

('carne', 'huevos')

x = carne

y = huevos

Si una excepción tiene argumentos, estos se imprimen como la última parte (el detalle) del mensaje para las excepciones que no están manejadas.

Los manejadores de excepciones no manejan solamente las excepciones que ocurren en el bloque try, también manejan las excepciones que ocurren dentro de las funciones que se llaman (inclusive indirectamente) dentro del bloque try. Por ejemplo:

>>> def esto_falla():

...     x = 1/0

...

>>> try:

...     esto_falla()

... except ZeroDivisionError as err:

...     print('Manejando error en tiempo de ejecución:', err)

...


6.4 Manejando error en tiempo de ejecución: division by zero


6.4.1 Levantando excepciones

La declaración raise permite al programador forzar a que ocurra una excepción específica. Por ejemplo:

>>> raise NameError('Hola')

Traceback (most recent call last):

  File "<stdin>", line 1, in <module>

NameError: Hola

El único argumento a raise indica la excepción a generarse. Tiene que ser o una instancia de excepción, o una clase de excepción (una clase que hereda de Exception). Si se pasa una clase de excepción, la misma sera instanciada implicitamente llamando a su constructor sin argumentos:

raise ValueError  # atajo para 'raise ValueError()'

Si se necesita determinar cuando una excepción fue lanzada pero no se la quiere manejar, una forma simplificada de la instrucción raise permite relanzarla:

>>> try:

...     raise NameError('Hola')

... except NameError:

...     print('Voló una excepción!')

...     raise

...

Voló una excepción!

Traceback (most recent call last):

  File "<stdin>", line 2, in <module>

NameError: Hola


6.4.2 Excepciones definidas por el usuario

Los programas pueden nombrar sus propias excepciones creando una nueva clase excepción (mirá Clases para más información sobre las clases de Python). Las excepciones, típicamente, deberán derivar de la clase Exception, directa o indirectamente.

Las clases de Excepciones pueden ser definidas de la misma forma que cualquier otra clase, pero usualmente se mantienen simples, a menudo solo ofreciendo un número de atributos con información sobre el error que leerán los manejadores de la excepción. Al crear un módulo que puede lanzar varios errores distintos, una práctica común es crear una clase base para excepciones definidas en ese módulo y extenderla para crear clases excepciones específicas para distintas condiciones de error:

class Error(Exception):

    """Clase base para excepciones en el módulo."""

    pass

 

class EntradaError(Error):

    """Excepción lanzada por errores en las entradas.

 

    Atributos:

        expresion -- expresión de entrada en la que ocurre el error

        mensaje -- explicación del error

    """

 

    def __init__(self, expresion, mensaje):

        self.expresion = expresion

        self.mensaje = mensaje

 

class TransicionError(Error):

    """Lanzada cuando una operacion intenta una transicion de estado no

    permitida.

 

    Atributos:

        previo -- estado al principio de la transición

        siguiente -- nuevo estado intentado

        mensaje -- explicación de por qué la transición no está permitida

    """

    def __init__(self, previo, siguiente, mensaje):

        self.previo = previo

        self.siguiente = siguiente

        self.mensaje = mensaje

La mayoría de las excepciones son definidas con nombres que terminan en Error, similares a los nombres de las excepciones estándar.

Muchos módulos estándar definen sus propias excepciones para reportar errores que pueden ocurrir en funciones propias.


6.4.3 Definiendo acciones de limpieza

La declaración try tiene otra cláusula opcional que intenta definir acciones de limpieza que deben ser ejecutadas bajo ciertas circunstancias. Por ejemplo:

>>> try:

...     raise KeyboardInterrupt

... finally:

...     print('Chau, mundo!')

...

Chau, mundo!

KeyboardInterrupt

Traceback (most recent call last):

  File "<stdin>", line 2, in ?

Una cláusula finally siempre es ejecutada antes de salir de la declaración try, ya sea que una excepción haya ocurrido o no. Cuando ocurre una excepción en la cláusula try y no fue manejada por una cláusula except (o ocurrió en una cláusula except o else), es relanzada luego de que se ejecuta la cláusula finally. El finally es también ejecutado a la salida cuando cualquier otra cláusula de la declaración try es dejada via break, continue o return. Un ejemplo más complicado:

>>> def dividir(x, y):

...     try:

...         result = x / y

...     except ZeroDivisionError:

...         print("¡división por cero!")

...     else:

...         print("el resultado es", result)

...     finally:

...         print("ejecutando la clausula finally")

...

>>> dividir(2, 1)

el resultado es 2.0

ejecutando la clausula finally

>>> dividir(2, 0)

¡división por cero!

ejecutando la clausula finally

>>> divide("2", "1")

ejecutando la clausula finally

Traceback (most recent call last):

  File "<stdin>", line 1, in <module>

  File "<stdin>", line 3, in divide

TypeError: unsupported operand type(s) for /: 'str' and 'str'

Como se puede ver, la cláusula finally es ejecutada siempre. La excepción TypeError lanzada al dividir dos cadenas de texto no es manejado por la cláusula except y por lo tanto es relanzada luego de que se ejecuta la cláusula finally.

En aplicaciones reales, la cláusula finally es útil para liberar recursos externos (como archivos o conexiones de red), sin importar si el uso del recurso fue exitoso.


6.4.4 Acciones predefinidas de limpieza

Algunos objetos definen acciones de limpieza estándar que llevar a cabo cuando el objeto no es más necesitado, independientemente de que las operaciones sobre el objeto hayan sido exitosas o no. Mirá el siguiente ejemplo, que intenta abrir un archivo e imprimir su contenido en la pantalla:

for linea in open("miarchivo.txt"):

    print(linea, end="")

El problema con este código es que deja el archivo abierto por un periodo de tiempo indeterminado luego de que esta parte termine de ejecutarse. Esto no es un problema en scripts simples, pero puede ser un problema en aplicaciones más grandes. La declaración with permite que objetos como archivos sean usados de una forma que asegure que siempre se los libera rápido y en forma correcta:

with open("miarchivo.txt") as f:

    for linea in f:

        print(linea, end="")

Luego de que la declaración sea ejecutada, el archivo f siempre es cerrado, incluso si se encuentra un problema al procesar las líneas. Objetos que, como los archivos, provean acciones de limpieza predefinidas lo indicarán en su documentación.

Hasta el próximo jueves, salud y saludos!
Mauricio José Tobares

jueves, 2 de abril de 2020

Ordenar y filtrar resultados de consultas sin saturar la base de datos con PHP y MySQL

Como ya me tiene acostumbrado la GENIA de Eugenia Bahit, aquí un exelentisimo tutorial de como utilizar inteligentemente consultas php para no hacer mil consultas sino en una misma consulta hacer mil cosas :D

Ordenar y filtrar resultados de consultas MySQL con PHP, sin saturar la base de datos

espero les sirva, leanlo con tranquilidad y si se ponen a practicar un poquito van a poder adaptar lo que enseña en el tutorial a miles de cosas

saludos y hasta el próximo post

Instalación y utilización de PIP - Python

hace nos meses vengo incursionando en el mundo de python y me topé con la dificultad de instalar nuevas librerías, en windows y linux que son los sistemas en los que me manejo, en linux es bastante simple pero... y el mundo de la ventana?? ahí está la cuestión!!! Para instalar PIP y poder utilizarlo revisé medio mundo web hasta dar con un blog que, sin caer en palabreríos explica de una forma simple y efectiva como instalar y utilizar esta erramienta que creo fundamental para python el blog es el siguiente: http://recursospython.com/guias-y-manuales/instalacion-y-utilizacion-de-pip-en-windows-linux-y-os-x/ espero les sea de gran utilidad, que lo disfruten!!

tutorial de .htaccess

Buscando y buscando logré dar con un exelente tutorial de .htaccess el cual es una muy buena referencia.

El tutorial trata sobde WP (wordpress para los que no lo entiendan) pero con decir que el .htaccess se utiliza en todo tipo de sistemas no es solamente para el WP sino que se puede utilizar facilmente en otras aplicaciones.

El tutorial es una exelente traducción del tutorial de Paolo Tagliaferri (que le manda saludos a Pampita), el original lo pueden encontrar en http://vortexmind.net/2006-02-26-apache-htaccess-tweaking-tutorial/ pero como somos hispanoparlantes vamos a utilizar esta exelente traducción que se encuentra en el tutorial de .htaccess de www.enespanol.com.ar asique los animo a leerlo!!!

espero les sea de utilidad ya que no se ven todos los días esta clase de exelencias!!

Cómo ordenar una consulta de MySQL utilizando ORDER by y PDO

En algunas ocaciones nos encontramos con la necesidad de ordenar los resultados de una consulta, ya sea de menor a mayor, ascendente o descendente o como sea, en esta ocación voy a mostrar una manera de hacerlo con clases, MySQL y PDO. Lo primero que tenemos que hacer es crear la clase asique procedemos a cerearla:
class ordenar{}
bueno... logicamente con esto no vamos a hacer nada asique pasemos a lo siguiente que será crear la conexión a la base de datos pero antes debo aclarar algo, esta clase es hija de otra clase asique vamos a extender de una clase llamada PDO_class.php que es quien realmente tiene la conexión, como no es el objetivo de este post no voy a publicar como está echo esa clase PDO_class.php y quedará para otra ocación
class ordenar extends PDO_class { }
ahora colocaremos el constructor (como se supone)
class ordenar extends PDO_class { private $db; /** * Constructor, entrega la conexion a la base de datos */ public function __construct() { $this->db = $this->conectarDB(); } }
finalmente lo que haremos es crear un método en el cual haremos la consulta a la base de datos y agregaremos algunas otras cositas para darle un poquito mas de forma al asunto
class ordenar extends PDO_class { private $db; /** * Constructor, entrega la conexion a la base de datos */ public function __construct() { $this->db = $this->conectarDB(); } function listaOrdenada() { // A partir de este método vamos a listar los resultados $res = $this->db->query("SELECT * FROM tabla WHERE campo ORDER BY campo DESC"); if (!$res) echo 'ERROR: error en ordenar - function listaOrdenada linea XXX'; // esto es para personalizar un mensaje de error y saber en que línea falló el script (logicamente si falló) else { $algo = ''; foreach($res as $row) { $algo .= $row['campo'] . '
'; } echo $algo; } } }

Una muy breve explicación va a ser que en primer lugar tenemos una clase común y corriente extendida de un PDO_class que es quien tiene la conexión (ya en otro post haré algo con eso), luego tenemos un constructor que, valgame la redundancia, construye la conexión a la base de datos, luego tenemos el método que hace el listado ordenado segun lo que nosotros queramos y aquí vamos a detenernos a analizar un poquito mas a fondo. Lo primero con lo que nos encontramos en este método es con la consulta (nada fuera de lo normal para PDO), en nuestro caso lo que hacemos es consultar a la base de datos seleccionando TODO desde la tabla llamada "tabla" desde el campo llamado "campo", ordenado de tal forma que se tengan en cuenta los valores del campo llamado "campo" en orden descendente, osea que, analiza todos los registros de "campo" y los ordena segun su valor de forma descendente. Luego de la consulta lo que tenemos es una simple comprobación y un mensaje de error (es saludable y recomendable hacerlo de esta forma indicando donde estuvo el error, en este caso lo que indico es el nombre de la clase, el método y la línea donde se halla el posible error, para saber la linea y reemplazar el XXX simplemente miren a la izquierda de su editor y van a saber en donde estan parados!). Seguidamente lo que tenemos es lo siguiente: definimos la variable "$algo" con un valor vacío ($algo = '';), luego lo que hacemos es un foreach normal(foreach($res as $row)), el foreach mas típico, luego lo que hacemos es definir $algo con el valor del campo y generamos un bucle que hasta que no se terminen los resultados seguira agregando a $algo la cadena "$row['campo'] . '
';" donde $row['campo'] es el valor del registro en la tabla llamada "tabla" y en el campo llamado "campo" :). Por ultimo lo que tenemos es un "echo $algo" que imprime todo lo que pretendemos mostrar.

Bueno, esto fué todo por hoy, espero a alguien mas le sirva, porque a mi que soy bastante olvidadizo me sirve y mucho :)

TkInter - de marciano a humano - sección 13 - El Widget LabelFrame

  13 El widget LabelFrame El widget LabelFrame al igual que el widget Frame es un contenedor espacial, un área rectangular que puede...