Traducción: Leonardo De Luca
Si todavía no lo has hecho, lee las sesiones anteriores:
Todos los archivos de esta sesión están aquí: Sesión 4 en GitHub. Puedes usarlos o seguir las instrucciones comenzando con los archivos de la Sesión 3 y ver qué tan bien has trabajado.
Cuando terminamos la sesión 3 teníamos una aplicación básica de tareas pendientes, con funcionalidad muy limitada: puedes marcar tareas como ya hechas, pero no puedes editarlas, no puedes crear tareas nuevas, tampoco borrarlas, ni mucho menos filtrarlas.
Una aplicación muy limitada
Hoy vamos a comenzar a escribir código y a diseñar la IU para hacer esas cosas.
El concepto clave aquí son las Acciones.
Citemos el manual:
La clase QAction provee una acción abstracta de la interfaz de usuario que se puede insertar en los widgets.
En las aplicaciones se pueden invocar muchas ordenes comunes a través de menúes, botones de barras de herramientas y atajos de teclado. Dado que el usuario espera que cada orden se ejecute de la misma forma, sin importar la interfaz utilizada, es útil representar cada orden como una acción.
Las acciones se pueden agregar a los menúes y a las barras de herramientas y estarán sincronizadas automáticamente. Por ejemplo, en un procesador de texto, si el usuario presiona el botón Negrita de la barra de herramientas, el elemento Negrita del menú se activará automáticamente.
Una QAction puede contener un ícono, texto de menú, un atajo, texto de estado, texto "¿Qué es esto?", y texto emergente.
La belleza de las acciones es que no tienes que escribir código dos veces. ¿Por qué agregar un botón "Copiar" a una barra de herramientas, luego una entrada de menú "Copiar" y luego escribir dos manejadores?
Crea acciones para todo lo que el usuario pueda hacer y luego conéctalas a tu IU en los lugares correctos. Si colocas la acción en un menú, es una entrada de menú; si la colocas en una barra de herramientas, es un botón. Luego escribe un manejador para la acción, conéctalo a la señal apropiada y listo.
Empecemos con una acción sencilla: Borrar una tarea. Haremos la primera parte del trabajo, crear la acción y la IU, con Designer.
Primero iremos al Action Editor y obviamente haremos clic sobre el botón "New Action" y comenzaremos a crearla:
Crear una nueva acción
Algunos comentarios:
Una vez que creas la acción, no se marcará como "Used" en el editor de acciones. Esto se debe a que existe, pero no está disponible para el usuario en ningún lado de la ventana que estamos creando.
Hay dos lugares obvios para esta acción: una barra de herramientas y un menú.
Para añadir una acción a una barra de herramientas, primero asegúrate de que haya una. Si no tienes una en tu "Object Inspector" entonces haz clic derecho sobre MainWindow (ya sea sobre la propia ventana o sobre su entrada en el inspector) y elige "Add Tool Bar".
Puedes añadir tantas barras de herramientas como quieras, pero intenta utilizar una sola, salvo que tengas una muy buena razón (tendremos una en la sesión 5 ;-)
Luego de crear la barra de herramientas verás un espacio vacío entre el menú (que dice "Type Here") y el widget de la lista de tareas. Ese espacio es la barra de herramientas.
Arrastra el ícono de la acción desde el editor de acciones hasta la barra de herramientas.
¡Eso es todo!
La acción Borrar tarea ahora está en la barra de herramientas.
Bueno, esa fue una explicación bastante larga para una pequeña funcionalidad, ¿no es cierto? No te preocupes, será mucho más fácil añadir las próximas acciones, porque espero que cuando leas "añadí una acción llamada Nueva tarea" sepas de que se está hablando.
Y en la próxima sesión haremos justamente eso: crearemos nuestro primer diálogo.
Aquí puedes ver qué cambió entre la versión vieja y la nueva:
|
Generated by diff2html © Yves Bailly, MandrakeSoft S.A. 2001 diff2html is licensed under the GNU GPL. |
| session3/main.py | session4/main.py | |||
|---|---|---|---|---|
|
60 lines 1557 bytes Last modified : Thu Mar 5 02:03:34 2009 |
79 lines 2300 bytes Last modified : Sat Mar 7 02:06:29 2009 |
|||
| 1 | # -*- coding: utf-8 -*- | 1 | # -*- coding: utf-8 -*- | |
| 2 | 2 | |||
| 3 | """The user interface for our app""" | 3 | """The user interface for our app""" | |
| 4 | 4 | |||
| 5 | import os,sys | 5 | import os,sys | |
| 6 | 6 | |||
| 7 | # Import Qt modules | 7 | # Import Qt modules | |
| 8 | from PyQt4 import QtCore,QtGui | 8 | from PyQt4 import QtCore,QtGui | |
| 9 | 9 | |||
| 10 | # Import the compiled UI module | 10 | # Import the compiled UI module | |
| 11 | from windowUi import Ui_MainWindow | 11 | from windowUi import Ui_MainWindow | |
| 12 | 12 | |||
| 13 | # Import our backend | 13 | # Import our backend | |
| 14 | import todo | 14 | import todo | |
| 15 | 15 | |||
| 16 | # Create a class for our main window | 16 | # Create a class for our main window | |
| 17 | class Main(QtGui.QMainWindow): | 17 | class Main(QtGui.QMainWindow): | |
| 18 | def __init__(self): | 18 | def __init__(self): | |
| 19 | QtGui.QMainWindow.__init__(self) | 19 | QtGui.QMainWindow.__init__(self) | |
| 20 | 20 | |||
| 21 | # This is always the same | 21 | # This is always the same | |
| 22 | self.ui=Ui_MainWindow() | 22 | self.ui=Ui_MainWindow() | |
| 23 | self.ui.setupUi(self) | 23 | self.ui.setupUi(self) | |
| 24 | 24 | |||
| 25 | # Let's do something interesting: load the database contents | 25 | # Let's do something interesting: load the database contents | |
| 26 | # into our task list widget | 26 | # into our task list widget | |
| 27 | for task in todo.Task.query().all(): | 27 | for task in todo.Task.query().all(): | |
| 28 | tags=','.join([t.name for t in task.tags]) | 28 | tags=','.join([t.name for t in task.tags]) | |
| 29 | item=QtGui.QTreeWidgetItem([task.text,str(task.date),tags]) | 29 | item=QtGui.QTreeWidgetItem([task.text,str(task.date),tags]) | |
| 30 | item.task=task | 30 | item.task=task | |
| 31 | if task.done: | 31 | if task.done: | |
| 32 | item.setCheckState(0,QtCore.Qt.Checked) | 32 | item.setCheckState(0,QtCore.Qt.Checked) | |
| 33 | else: | 33 | else: | |
| 34 | item.setCheckState(0,QtCore.Qt.Unchecked) | 34 | item.setCheckState(0,QtCore.Qt.Unchecked) | |
| 35 | self.ui.list.addTopLevelItem(item) | 35 | self.ui.list.addTopLevelItem(item) | |
| 36 | 36 | |||
| 37 | def on_list_itemChanged(self,item,column): | 37 | def on_list_itemChanged(self,item,column): | |
| 38 | if item.checkState(0): | 38 | if item.checkState(0): | |
| 39 | item.task.done=True | 39 | item.task.done=True | |
| 40 | else: | 40 | else: | |
| 41 | item.task.done=False | 41 | item.task.done=False | |
| 42 | todo.saveData() | 42 | todo.saveData() | |
| 43 | 43 | |||
| 44 | def on_actionDelete_Task_triggered(self,checked=None): | |||
| 45 | if checked is None: return | |||
| 46 | # First see what task is "current". | |||
| 47 | item=self.ui.list.currentItem() | |||
| 48 | ||||
| 49 | if not item: # None selected, so we don't know what to delete! | |||
| 50 | return | |||
| 51 | # Actually delete the task | |||
| 52 | item.task.delete() | |||
| 53 | todo.saveData() | |||
| 54 | ||||
| 55 | # And remove the item. I think that's not pretty. Is it the only way? | |||
| 56 | self.ui.list.takeTopLevelItem(self.ui.list.indexOfTopLevelItem(item)) | |||
| 57 | ||||
| 58 | def on_list_currentItemChanged(self,current=None,previous=None): | |||
| 59 | if current: | |||
| 60 | self.ui.actionDelete_Task.setEnabled(True) | |||
| 61 | else: | |||
| 62 | self.ui.actionDelete_Task.setEnabled(False) | |||
| 44 | 63 | |||
| 45 | def main(): | 64 | def main(): | |
| 46 | # Init the database before doing anything else | 65 | # Init the database before doing anything else | |
| 47 | todo.initDB() | 66 | todo.initDB() | |
| 48 | 67 | |||
| 49 | # Again, this is boilerplate, it's going to be the same on | 68 | # Again, this is boilerplate, it's going to be the same on | |
| 50 | # almost every app you write | 69 | # almost every app you write | |
| 51 | app = QtGui.QApplication(sys.argv) | 70 | app = QtGui.QApplication(sys.argv) | |
| 52 | window=Main() | 71 | window=Main() | |
| 53 | window.show() | 72 | window.show() | |
| 54 | # It's exec_ because exec is a reserved word in Python | 73 | # It's exec_ because exec is a reserved word in Python | |
| 55 | sys.exit(app.exec_()) | 74 | sys.exit(app.exec_()) | |
| 56 | 75 | |||
| 57 | 76 | |||
| 58 | if __name__ == "__main__": | 77 | if __name__ == "__main__": | |
| 59 | main() | 78 | main() | |
| 60 | 79 |