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.
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.
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

2 comentarios:
¿Porqué no pasas un campo priority al hash que sea opcional? Si lo pasas se ordena y si no es que no importa al programador.
Después solo tienes que hacer un sort antes de recorrerlos por priority y listo.
Gracias por seguir publicando, no usamos los mismos lenguajes pero está bien saber que aquí hay sabiduría QT
Si, igual queria ver una forma donde no sea necesario que la persona siempre tenga que definir la prioridad, sino quizas analizando como fue llamado parsear esa linea y poder sacar el orden de las keys.... ya vere.... sino terminara siendo la clasica: "order" o algo...
Publicar un comentario en la entrada