Personal Website

My Web: MindEchoes.com

Friday, April 6, 2012

Quick Form con Qt y Python

Esto es una idea que se me ocurrió ayer y simplemente tenia ganas de hacerlo... ÚTIL? no lo se...quizás lo pueda mejorar un poco más, agregarle un par de cosas y hacer algún plugin de ninja que te permita crear formularios de ABM de forma automática para tu aplicación basados en las tablas de la base de datos... una idea nomas... Pero bueno, les dejo el código, luego la explicación y por último el ejemplo:

from PyQt4.QtGui import QDialog
from PyQt4.QtGui import QLineEdit
from PyQt4.QtGui import QPushButton
from PyQt4.QtGui import QLabel
from PyQt4.QtGui import QMessageBox
from PyQt4.QtGui import QVBoxLayout
from PyQt4.QtGui import QHBoxLayout
from PyQt4.QtGui import QFormLayout
from PyQt4.QtGui import QSpacerItem
from PyQt4.QtGui import QSizePolicy
from PyQt4.QtCore import SIGNAL


class QuickForm(QDialog):

    def __init__(self, **kwargs):
        super(QuickForm, self).__init__()
        self.required_fields = []

        vbox = QVBoxLayout(self)
        self.form_title = QLabel()
        self.form_subtitle = QLabel()
        vbox.addWidget(self.form_title)
        vbox.addWidget(self.form_subtitle)
        vbox.addSpacerItem(QSpacerItem(0, 20))

        form_layout = QFormLayout()
        vbox.addLayout(form_layout)

        hbox = QHBoxLayout()
        self.form_accept = QPushButton('Accept')
        self.form_cancel = QPushButton('Cancel')
        hbox.addSpacerItem(QSpacerItem(20, 0,
            QSizePolicy.Expanding))
        hbox.addWidget(self.form_cancel)
        hbox.addWidget(self.form_accept)

        self.form_operations = {
        'form_window_title': lambda x: self.setWindowTitle(x),
        'form_title': lambda x: self.form_title.setText(x),
        'form_subtitle': lambda x: self.form_subtitle.setText(x),
        'form_accept': lambda x: self.form_accept.setText(x),
        'form_cancel': lambda x: self.form_cancel.setText(x),
        'form_modal': lambda x: self.setModal(x),
        }
        for key in kwargs:
            if not key.startswith('form_'):
                result = self.check_content(key, kwargs[key])
                setattr(self, key, result[1])
                form_layout.addRow(result[0], result[1])
            else:
                self.form_operations.get(
                    key, lambda x: None)(kwargs[key])
        #Hide items if they are empty
        self.form_title.setVisible(
            not self.form_title.text().isEmpty())
        self.form_subtitle.setVisible(
            not self.form_subtitle.text().isEmpty())

        vbox.addSpacerItem(QSpacerItem(0, 20))
        vbox.addLayout(hbox)

        self.connect(self.form_accept, SIGNAL("clicked()"),
            self.accept_clicked)
        self.connect(self.form_cancel, SIGNAL("clicked()"),
            self.cancel_clicked)

    def accept_clicked(self):
        accepted = True
        for attr in self.required_fields:
            if attr.text().isEmpty():
                accepted = False
                break
        if accepted:
            self.accept()
        else:
            QMessageBox.information(self, "Invalid Form",
                "Some required fields are not completed.")

    def cancel_clicked(self):
        self.reject()

    def check_content(self, key, content):
        value = None
        if isinstance(content, basestring):
            value = (content, QLineEdit())
        elif isinstance(content, (dict)):
            text = content.get('text', key)

            args = content.get('args', ())

            widget = content.get('widget', None)
            if hasattr(widget, '__call__'):
                widget = widget(*args)
            else:
                widget = QLineEdit()

            if content.get('required', False):
                self.required_fields.append(widget)
                text += ' (*)'

            value = (text, widget)

        return value


Entonces, cual es la idea de esto?
Teniendo esta clase, al instanciarla podemos pasarle una serie de argumentos para que nos cree un formulario de forma dinámica, y algunos de estos argumentos nos sirven igual para customizar el formulario, por ejemplo:

  • form_window_title: Nos deja customizar el titulo de la ventana.
  • form_title: Nos deja agregar un titulo dentro del formulario.
  • form_subtitle: Agrega un subtitulo dentro del formulario.
  • form_accept: Modifica el texto del botón de Aceptar ("Accept" por defecto)
  • form_cancel: Modifica el texto del botón de Cancelar ("Cancel" por defecto)
  • form_modal: Establece si el dialogo es de tipo modal o no.
Estas de arriba son "keys" ya definidas, pero luego, por cualquier otra Key que le pasemos al constructor de QuickForm, lo que va a hacer es crear un atributo del objeto con el nombre de esa key (por defecto ese atributo seria un QLineEdit) entonces una vez cerrado el formulario podemos consultar los valores de los campos directamente accediendo a estos widgets con el nombre de variable igual al que pusimos en las keys.

Por ejemplo:


qf = QuickForm(name="Ingrese Nombre:", surname="Ingrese Apellido:")
qf.show()

# Una vez que el usuario ingreso los datos, podemos hacer:

print qf.name.text()
print qf.surname.text()

De esta forma, podemos de crear de forma rápida, fácil y genérica, todos estos tipos de formularios que muchas veces suele ser un trabajo muy repetitivo cuando son necesarios.

Otra posibilidad también,  es la de customizar un poco más con que widget va a ser representado nuestra key, las opciones son:
  • text: Estable el texto que se mostrara al lado del widget para brindarle algún tipo de referencia al usuario.
  • widget: Recibe la clase a ser instanciada para asociar al widget.
  • args: Recibe alguna serie de argumentos (una tupla) que querríamos pasarle al constructor de este widget al ser instanciado.
  • required: Booleano para decir si este campo es requerido que este completado por el usuario para poder aceptar el formulario.
Todos estos campos son opcionales, por customizar alguno, no es necesario tener que completar todos.

Entonces podriamos tener algo al estilo:

qf = QuickForm(name={"text": "Ingrese Nombre:", "required": True}, 
               surname="Ingrese Apellido:" ,
               age={"text": "Ingrese Edad:", "widget": QSpinBox})
qf.show()

Y bueno, ahora un ejemplo completo de uso:
(Hice "import *" aunque no me gusta solo para simplificar el código...)


import sys

from PyQt4.QtGui import *
from PyQt4.QtCore import *

import quick_form


class MyWindow(QWidget):

    def __init__(self):
        super(MyWindow, self).__init__()

        vbox = QVBoxLayout(self)
        vbox.addWidget(QLabel("Completar Formulario"))

        btn = QPushButton("Abrir Formulario")
        vbox.addWidget(btn)

        self.connect(btn, SIGNAL("clicked()"), self.open_form)

    def open_form(self):
        self.qf = quick_form.QuickForm(
            form_window_title="Ingrese Usuario",
            form_title="<h2>Datos de Usuario</h2>",
            form_accept="Save",
            name={"text": "Ingrese Nombre:", "required": True},
            surname="Ingrese Apellido:",
            age={"text": "Ingrese Edad:", "widget": QSpinBox},
            weight={"text": "Ingrese peso:", "widget": QSlider,
                    "args": (Qt.Horizontal,)}
        )

        self.connect(self.qf, SIGNAL("accepted()"),
            self.print_values)
        self.qf.show()

    def print_values(self):
        print 'VALORES INGRESADOS:'
        print "Nombre:", self.qf.name.text()
        print "Apellido:", self.qf.surname.text()
        print "Edad:", self.qf.age.value()
        print "Peso:", self.qf.weight.value()


app = QApplication(sys.argv)
w = MyWindow()
w.show()

sys.exit(app.exec_())



#SALIDA al apretar "Save":
#-------
#
#VALORES INGRESADOS:
#Nombre: Diego
#Apellido: Sarmentero
#Edad: 26
#Peso: 50

Y el resultado de esto seria:


Uno de los problemas, es que las cosas al ser keys de un diccionario aparecen en cualquier orden, pero bue... eso se soluciona fácil y después lo hare si se me ocurre algún lado donde meter esto :P

Wednesday, April 4, 2012

Nostalgia de Juegos

Nunca fui muy "gamer", los que me conocen lo saben, muchas veces digo: "mmm programar o jugar??... no, jugar cansa mucho y programar es mas divertido" (/me siente las miradas asesinas de los gamers)... de hecho soy muy particular para los juegos, y hay muy pocos que me atrapen MALLLLL, la gran mayoría lo juego y en cuanto lo dejo, por ahí me lo olvido por meses! Es más, si tuviera que decir cual es mi juego favorito... probablemente eligiria "Day of Tentacle":


Pero ayer me empecé a acordar de cuando era chico, y compraba las revistas de los últimos juegos, etc. Porque siempre estuve fascinado con los gráficos de los juegos, muchas veces lo que me llamaba de un juego eran los graficos, y cada vez que salian juegos nuevos con gráficos mas potentes me re emocionaba! Y me acuerdo de esto, porque ayer Filly me paso un tema del soundtrack de Mass Effect:



MUY BUEN TEMA!
Pero eso me llevo a acordarme de otro BUENISIMOOOOOO soundtrack que escuchaba de chico, que hasta me puse a buscar los temas en las carpetas del juego y me los copie a un cd de musica (notese la vejez de las cosas que digo :P), y recordando la musica, fue que recordé los gráficos que tenían los juegos de esa época que TANTO ME FASCINABAN! (y que cada vez que salia algo mejor decia: "impresionante! esto se ve re real!"... REAL?! jeje)

PANDEMONIUM 

Después otro juego que empezaba muy bueno (digo empezaba porque solo tenia una demo que venia en las revistas que compraba... y nunca lo pude conseguir completo... en esa época no era tan fácil y ni tenia internet :P) fue el:

NORMALITY


Vale aclarar que cuando jugué ese juego... esos gráficos para mi eran: "GUAU! ESTO SE VE INCREÍBLE!"... si... lo se... :P Y ahora el juego por el que estoy escribiendo el post... el juego del que me acorde por el INCREIBLE soundtrack que tenia!

MECHWARRIOR 2 

 Intro:



Gameplay:



Cabe aclarar que cuando me sente a jugar este juego... unos 13 años atrás aproximadamente, y siendo que donde yo vivía no llegaban las cosas mas nuevas tampoco, pensé: "NOOO ESTO ES CASI REALIDAD VIRTUAL!!!" (así me emocionaba con cada gráfico copado que veía en esos tiempos jeje)

Lo peor de todo, es que no me pintaba tanto el combate, sino como estaba simulado el robot, entonces en lugar de ir a matar a todos, me ponía a explorar el terreno, alejándome de "la zona de batalla" y ver hasta donde llegaban realmente los niveles... que tanto me podía acercar a esas montañas y cosas que veía a lo lejos.




Para mi sorpresa, termine descubriendo que cuando llegaba a los limites del nivel, sin siquiera haber tenido que matar a nadie... GANABA EL NIVEL! \o/... así que prácticamente termine el juego sin tener que dispararle a nadie :P ... igual después volví a jugarlo tratando de matar robots, pero ya había visto lo que mas me interesaba jeje. Y bueno, para terminar con la nostalgia, les dejo acá el soundtrack de este buenísimo juego (CUYO SOUNDTRACK ES AUN MEJOR! y no es algo que perdió calidad con los años :P)



Para descargar: Mechwarrior 2 Soundtrack