\mode* % Reset \mode from slides.tex \section{\en{Overview of Sugar and GTK+}\es{Visión general de Sugar y GTK+}} \note{Before starting, ask how everyone got on with the exercises from part 2.} \en{The Sugar platform is what runs on the XO. Sugar itself is written in Python, but uses a system called GTK+ as its underlying UI toolkit. GTK+ is a popular UI toolkit, used by many projects on Linux (not just on the XO).} \es{La plataforma de Sugar es lo que se ejecuta en la XO. Sugar sí está escrito en Python, pero utiliza un sistema llamado GTK+ como su kit de herramientas de interfaz de usuario (IU) subyacente. GTK+ es un popular juego de herramientas de interfaz de usuario, que se utiliza por muchos proyectos en Linux (y no sólo en la XO).} \en{Sugar's distinctive appearance is due to a theme it applies to the default GTK+ widgets. Without this theme, activities on the XO would look very similar to normal Linux programs (such as gedit).} \es{El aspecto distintivo de Sugar es debido a un tema que se aplica a los widgets GTK+ predeterminados. Sin este tema, las actividades en la XO se vería muy similar a los programas normales de Linux (como gedit).} \en{As with Python, there are two major versions of GTK+ available at the moment: GTK+~2 and GTK+~3. GTK+~3 is the recommended version, and is available on the XO. All software should be written using GTK+~3 using GIR (GObject Introspection) and \emph{not} PyGTK (which is out of date).} \es{Al igual que con Python, hay dos versiones principales de GTK+ disponibles en este momento: GTK+~2 y GTK+~3. GTK+~3 es la versión recomendada, y está disponible en la XO. Todo el software debe ser escrito usando GTK+~3 y GIR (introspección GObject) y \emph{no} PyGTK (que es obsoleta).} \en{Similarly, there are two versions of Sugar code available: \texttt{sugar} and \texttt{sugar3}. \texttt{sugar} uses GTK+~2, whereas \texttt{sugar3} uses GTK+~3. All new software should be written using \texttt{sugar3}.} \es{Del mismo modo, hay dos versiones de código de Sugar disponibles: \texttt{sugar} y \texttt{sugar3}. \texttt{sugar} usa GTK+~2, mientras que \texttt{sugar3} usa GTK+~3. Todas las novedades software debe ser escrito con \texttt{sugar3}.} \begin{frame}{\en{Sugar and GTK+}\es{Sugar y GTK+}} \begin{itemize} \item{\en{All XO activities use Sugar} \es{Todas las actividades XO utilizan Sugar}} \item{\en{Sugar is built on GTK+~3}\es{El Sugar se basa en GTK+~3}} \item{\en{Use \texttt{sugar3} and GTK+~3} \es{Debe utilizar \texttt{sugar3} y GTK+~3}} \end{itemize} \end{frame} \section{\en{Introduction to GTK+}\es{Introducción a GTK+}} \en{GTK+ is based on the concept of a hierarchy of \emph{widgets}. A widget is a single graphical control, such as a button, some text, a toolbar, or a window. These widgets are built into a hierarchy, with child widgets inside parent widgets. For example, when a button is shown next to an image, a menu bar and a label in a window, this widget hierarchy is used:} \es{GTK+ se basa en el concepto de una jerarquía de \emph{widgets}. Un widget es un control gráfico único, tal como un botón, un texto, una barra de herramientas, o una ventana. Estos widgets están integradas en una jerarquía, con widgets hijos dentro de los widgets de los padres. Por ejemplo, cuando se muestra un botón al lado de la imagen, una barra de menús y una etiqueta en una ventana, se utiliza esta jerarquía de controles:} \begin{frame}{\en{GTK+ widget hierarchy}\es{Jerarquía de widgets de GTK+}} \begin{itemize} \item{\texttt{Gtk.Window}\begin{itemize} \item{\texttt{Gtk.Box}\begin{itemize} \item{\texttt{Gtk.MenuBar}} \item{\texttt{Gtk.Label}} \item{\texttt{Gtk.Image}} \item{\texttt{Gtk.Button}} \end{itemize}} \end{itemize}} \end{itemize} \end{frame} \begin{frame}{\en{GTK+ widget hierarchy}\es{Jerarquía de widgets de GTK+}} \begin{figure}[h!] \centering \includegraphics[width=0.4\textwidth]{example-ui.png} \caption{\en{The example UI.} \es{Un ejemplo de un IU.}} \label{fig:example-ui} \end{figure} \end{frame} \en{Widgets are roughly divided between normal controls and layout containers (such as \texttt{Gtk.Box} in the example hierarchy). Other code in GTK+ supports key bindings, drag-and-drop and the clipboard.} \es{Widgets están divididos más o menos entre los controles normales y contenedores de diseño (como \texttt{Gtk.Box} en la jerarquía de ejemplo). Otro código en GTK+ soporta atajos de teclado, arrastrar y soltar y el portapapeles.} \note{Give an interactive demo of Glade and \texttt{example-ui.ui} now.} \en{\emph{Glade} is a popular tool for building UIs with GTK+. It isn't normally used when creating Sugar activities, but is useful for exploring the widget hierarchy.} \es{\emph{Glade} es una herramienta popular para la creación de IUs con GTK+. No se suele utilizar cuando se crean actividades para Sugar, pero es útil para explorar la jerarquía de widgets.} \section{\en{Building an interactive UI}\es{Construcción de una IU interactiva}} \en{To build a UI in GTK+, construct a Python object for each widget, and then call methods on the container widgets to add child widgets to parents and thus construct a hierarchy.} \es{Para construir una IU en GTK+, construir un objeto de Python para cada widget, y luego llamar a métodos en los widgets de contenedores para añadir hijos a los padres y así la construcción de una jerarquía.} \begin{frame}[fragile] \frametitle{\en{Constructing a GTK+ widget hierarchy} \es{Construcción de una jerarquía de widgets de GTK+}} \begin{lstlisting}[language=Python,basicstyle={\ttfamily\footnotesize}] from gi.repository import Gtk window = Gtk.Window(title='Hola Mundo') # Crear widget contenedor. box = Gtk.Box() box.set_orientation(Gtk.Orientation.VERTICAL) window.add(box) # Crear una etiqueta, una imagen y un boton. label = Gtk.Label('Logo OLPC:') box.add(label) image = Gtk.Image() image.set_from_file('olpc-square_logo.jpg') box.add(image) button = Gtk.Button() button.set_label('Boton') box.add(button) # Mostrar la IU. window.show_all() Gtk.main() \end{lstlisting} \end{frame} \en{In this example, the last two lines of code require explanation. Every GTK+ widget can be hidden, and all are hidden by default. Before they can be used by the user they need to be shown --- either by calling the \texttt{show()} method on each widget, or by calling the \texttt{show\_all()} method on one of the container widgets which contains them (such as the top-level window, for example).} \es{En este ejemplo, las dos últimas líneas de código requieren de una explicación. Cada widget de GTK+ se puede ocultar, y todos se oculta de manera predeterminada. Antes de que puedan ser utilizados por el usuario, es necesario que se muestra --- o bien llamando al método \texttt{show()} de cada widget, o llamando al método \texttt{show\_all()} en uno de los widgets de contenedor que los contiene (como la ventana de nivel superior, por ejemplo).} \en{The last line of code starts the GTK+ \emph{main loop}. This is what handles user input and drawing of widgets in a UI. The main loop is an infinite loop which:} \es{La última línea de código se inicia el \emph{bucle principal} de GTK+. Esto es lo que se encarga de la entrada del usuario y el dibujo de widgets en una IU. El bucle principal es un bucle infinito que:} \begin{frame}{\en{GTK+ main loop}\es{Bucle principal de GTK+}} \begin{enumerate} \item{\en{Receives input events from the user (such as mouse movements or keyboard button presses)} \es{Recibe los eventos de entrada del usuario (por ejemplo, los movimientos del ratón o pulsaciones de botones del teclado)}} \item{\en{Calls \emph{event handlers} for those events} \es{Llamadas \emph{controladores de eventos} para los eventos}} \item{\en{Waits for the next input event, then loops} \es{Espera a que el próximo evento de entrada, entonces se realiza un bucle}} \end{enumerate} \end{frame} % TODO: Diagram of the main loop? \en{This is a standard event-based programming model. It means that after calling the \texttt{Gtk.main()} function, all code in your program is executed inside callbacks from this main loop. A callback is where the main loop calls a function provided by your code: the main loop \emph{calls back} to your code.} \es{Este es un modelo de programación estándar basado en eventos. Esto significa que después de llamar a la función \texttt{Gtk.main()}, todo el código de su programa se ejecuta dentro de callbacks de este bucle principal. Un callback es donde el bucle principal llama a una función proporcionada por su código: el bucle principal \emph{vuelve a llamar} a su código.} \section{\en{Signal handling}\es{Manejo de señales}} \en{Almost all callbacks in GTK+ are for \emph{signals} from widgets. These correspond to events on the widgets. For example, the \texttt{Button} widget emits a \texttt{clicked} signal when the user clicks it. Zero or more callbacks can be \emph{connected} for this signal, all of which will be called from the main loop when the button is clicked.} \es{Casi todas las callbacks en GTK+ son para señales de widgets. Estos corresponden a eventos en los widgets. Por ejemplo, el widget \texttt{Button} emite una señal de \texttt{clicked} cuando el usuario hace clic en él. Cero o más callbacks se pueden conectar a esta señal, todos los cuales serán llamados desde el bucle principal cuando se hace clic en el botón.} \en{A signal handler can be connected by calling the \texttt{connect()} method, passing the name of the signal, and the name of the function to use as a callback. This function must accept the parameters specified in the documentation for the signal. It is common practice (though not required) to suffix callback function names with \texttt{\_cb} (which stands for `callback').} \es{Un manejador de la señal se puede conectar mediante una llamada al método \texttt{connect()}, pasando el nombre de la señal, y el nombre de la función para utilizarla como un callback. Esta función debe aceptar los parámetros especificados en la documentación de la señal. Es una práctica común (aunque no es obligatorio) a los nombres de funciones callback con sufijo \texttt{\_cb} (que significa `callback').} \en{In this example, we subclass \texttt{Gtk.Window} so that the signal callback can be included as a private method of the window. This is a tidy way to do things, as it prevents callbacks from different windows getting mixed up.} \es{En este ejemplo, subclase \texttt{Gtk.Window} de modo que el callback de la señal puede ser incluido como un método privado de la ventana. Esta es una manera ordenada de hacer las cosas, ya que evita que callbacks de diferentes ventanas se mezclen.} \begin{frame}[fragile] \frametitle{\en{GTK+ signal handlers}\es{Manejadores de señales de GTK+}} \begin{lstlisting}[language=Python,basicstyle={\ttfamily\footnotesize}] # Subclase Window para crear una ventana. class MyWindow(Gtk.Window): def __init__(self): super(MyWindow, self).__init__() # Crear y anadir un boton. button = Gtk.Button('Hola Mundo') button.show() self.add(button) # Conecte un manejador de senales. button.connect('clicked', self.__button_clicked_cb) def __button_clicked_cb(self, button): print('Boton se hizo clic!') window = MyWindow() window.show() Gtk.main() \end{lstlisting} \end{frame} \en{Signal handling is one of the key features of GTK+, and is worth understanding.} \es{Manipulación de las señales es una de las características principales de GTK+, y vale la pena comprensión.} \begin{frame}{\en{Signal handling tutorials}\es{Tutoriales sobre el manejo de señales}} \begin{itemize} \item{\url{\en{https://developer.gnome.org/gnome-devel-demos/stable/signals-callbacks.py.html.en} \es{https://developer.gnome.org/gnome-devel-demos/stable/signals-callbacks.py.html.es}}} \item{\url{http://python-gtk-3-tutorial.readthedocs.org/en/latest/basics.html\#main-loop-and-signals}} \end{itemize} \end{frame} \section{\en{GTK+ documentation}\es{Documentación de GTK+}} \en{GTK+ is a huge library, and covering it in detail here would take too long. There are several good tutorials for using GTK+ in Python, available in several languages. It is recommended that you read them and practice the exercises they give. The \href{https://developer.gnome.org/gtk3/stable/ch03.html}{GTK+ widget gallery} contains screenshots of every GTK+ widget, and links to their API documentation.} \es{GTK+ es una gran librería, y cubriendo en detalle aquí tomaría demasiado tiempo. Hay varios buenos tutoriales para el uso de GTK+ en Python, disponible en varios idiomas. Se recomienda que los lea y aplique los ejercicios que dan. La \href{https://developer.gnome.org/gtk3/stable/ch03.html}{galería de widgets de GTK+} contiene imágenes de todos los widgets de GTK+, y enlaces a la documentación de la API.} \begin{frame}{\en{GTK+ tutorials}\es{Tutoriales de GTK+}} \begin{itemize} \item{\en{\url{https://developer.gnome.org/gnome-devel-demos/stable/tutorial.py.html.en}} \es{\url{https://developer.gnome.org/gnome-devel-demos/stable/tutorial.py.html.es}}} \item{\en{\url{https://developer.gnome.org/gnome-devel-demos/stable/beginner.py.html.en}} \es{\url{https://developer.gnome.org/gnome-devel-demos/stable/beginner.py.html.es}}} \item{\url{http://python-gtk-3-tutorial.readthedocs.org/en/latest/index.html}} \item{\url{https://developer.gnome.org/gtk3/stable/ch03.html}} \end{itemize} \end{frame} \en{The GTK+ API documentation is currently only available for the C programming language. However, converting function names between C and Python is fairly simple, so you can use the C API documentation for Python.} \es{La documentación del API de GTK+ sólo está disponible para el lenguaje de programación C. Sin embargo, la conversión de nombres de funciones entre C y Python es bastante simple, por lo que puede utilizar la documentación de la API C con Python.} \en{To convert a C function name to a Python method name, remove underscores, change it to camelcase, and separate `gtk', the class name, and the method name. Python constructors become \texttt{*\_new()} functions in C. Constant values are entirely capitalised in C, and only partially capitalised in Python.} \es{Para convertir un nombre de función C para un nombre de método Python, retire subrayado, cambiar a CamelCase, y separado `gtk' del nombre de la clase y del nombre del método. Constructores Python vuelven funciones \texttt{*\_new()} en C. Valores constantes están completamente activados en C, y sólo parcialmente capitalizado en Python.} \begin{frame}{\en{GTK+ API documentation}\es{Documentación de la API de GTK+}} \begin{block}{} \url{https://developer.gnome.org/gtk3/stable/gtkobjects.html} \end{block} \begin{block}{\en{Conversion}\es{Conversión}} {\small\begin{align*} \mathbf{Python} &\leftrightarrow \mathbf{C} \\ \mathtt{Gtk.Window.set\_title()} &\leftrightarrow \mathtt{gtk\_window\_set\_title()} \\ \mathtt{Gtk.Button()} &\leftrightarrow \mathtt{gtk\_button\_new()} \\ \mathtt{Gtk.ToggleButton.get\_active()} &\leftrightarrow \mathtt{gtk\_toggle\_button\_get\_active()} \\ \mathtt{Gtk.Orientation.VERTICAL} &\leftrightarrow \mathtt{GTK\_ORIENTATION\_VERTICAL} \end{align*}} \end{block} \end{frame} \section{\en{Writing an activity}\es{Escribir una actividad}} \en{Fundamentally, a Sugar activity is a GTK+ application which provides a single GTK+ \texttt{Window} widget for the activity to use. This window forms the root of the activity's widget hierarchy, just as in a normal GTK+ application. Events and callbacks are handled similarly.} \es{Fundamentalmente, una actividad de Sugar es una aplicación de GTK+ que proporciona un solo widget \texttt{Window} de GTK+ para la actividad de usar. Esta ventana se forma la raíz de la jerarquía de widgets de la actividad, al igual que en una aplicación normal de GTK+. Eventos y callbacks se manejan de manera similar.} \en{To create an activity, subclass the \texttt{sugar3.activity.activity.Activity} class in Python. This \texttt{Activity} class provides a method to parent your activity's widget tree: \texttt{set\_canvas()}. By creating a widget hierarchy and passing it to this function, the interface for an activity can be created.} \es{Para crear una actividad, debe subclase de la clase \texttt{sugar3.activity.activity.Activity} en Python. Esta clase \texttt{Activity} proporciona un método para adjuntar la jerarquía de widgets de su actividad: \texttt{set\_canvas()}. Mediante la creación de una jerarquía de widgets y pasarlo a esta función, se puede crear la interfaz para una actividad.} \begin{frame}[fragile] \frametitle{\en{Creating an activity UI}\es{Creación de una IU de la actividad}} \begin{lstlisting}[language=Python,basicstyle={\ttfamily\small}] from sugar3.activity import activity class HolaMundoActivity(activity.Activity): def __init__(self, handle): super(HolaMundoActivity, self).__init__(handle) box = Gtk.Box() label = Gtk.Label('Hola Mundo!') button = Gtk.Button('Boton') label.show() box.add(label) button.show() box.add(button) box.show() self.set_canvas(box) \end{lstlisting} \end{frame} \en{When subclassing \texttt{sugar3.activity.activity.Activity}, three methods must be implemented. \texttt{\_\_init()\_\_} is the normal object constructor, and is called when your activity is started by the user. The \texttt{handle} parameter provides the activity ID and access to sharing services (which aren't covered here).} \es{Al crear subclases de \texttt{sugar3.activity.activity.Activity}, tres métodos se deben implementar. \texttt{\_\_init\_\_()} es el constructor de objeto normal, y se llama cuando su actividad se inicia por el usuario. El parámetro \texttt{handle} proporciona el ID de la actividad y el acceso a servicios para compartir (que no se cubre en este manual).} \en{\texttt{read\_file} is called after \texttt{\_\_init\_\_} when resuming an activity from the Journal. The \texttt{file\_path} parameter is the path of a file containing the saved Journal data. Similarly, \texttt{write\_file} is called when the user wants to save the activity to the Journal (either explicitly, when closing the activity, or when switching to another activity), and the \texttt{file\_path} is the path of a file which the Journal entry should be saved to.} \es{\texttt{read\_file} es llamada después \texttt{\_\_init\_\_} cuando se reanuda la actividad en el Diario. El parámetro \texttt{file\_path} es la ruta de un archivo que contiene los datos de diario guardados. Del mismo modo, \texttt{write\_file} se llama cuando el usuario desea guardar la actividad con el Diario (ya sea de manera explícita, al cerrar la actividad, o cuando se cambia a otra actividad), y el \texttt{file\_path} es la ruta de un archivo que la entrada de Diario debe ser guardado en.} \begin{frame}{\en{Required activity methods} \es{Métodos necesarios para actividades}} \begin{itemize} \item{\texttt{\_\_init\_\_(self, handle)}} \item{\texttt{read\_file(self, file\_path)}} \item{\texttt{write\_file(self, file\_path)}} \end{itemize} \end{frame} \en{The \texttt{Activity} class provides several other methods (apart from \texttt{set\_canvas()}) which can be called from within the activity code to perform various tasks such as forcing the activity to be saved to the Journal, closing the activity, and starting to share the activity on the network. Look at the \href{https://github.com/sugarlabs/sugar-toolkit-gtk3/blob/master/src/sugar3/activity/activity.py}{source code} for \texttt{sugar3.activity.activity.Activity} for details.} \es{La clase \texttt{Activity} ofrece varios métodos (además de \texttt{set\_canvas()}), que se puede llamar desde dentro del código de actividad para realizar diversas tareas, tales como obligar a la actividad que se guardan en el Diario, el cierre de la actividad, y empezar a compartir la actividad en la red. Mira \href{https://github.com/sugarlabs/sugar-toolkit-gtk3/blob/master/src/sugar3/activity/activity.py}{el código} de \texttt{sugar3.activity.activity.Activity} para más detalles.} \en{The most important thing to do in the \texttt{\_\_init\_\_} method is to add a toolbar to the activity. Amongst other things, this adds a close button to the activity.} \es{La cosa más importante que hacer en el método \texttt{\_\_init\_\_} es agregar una barra de herramientas a la actividad. Entre otras cosas, esto se agrega un botón de cierre de la actividad.} \begin{frame}[fragile] \frametitle{\en{Adding a toolbar}\es{Adición de una barra de herramientas}} \begin{lstlisting}[language=Python] def __init__(self, handle): # Cree la caja de herramientas de # actividad estandar. toolbar_box = ToolbarBox() self.set_toolbar_box(toolbar_box) toolbar_box.show() main_toolbar = toolbar_box.toolbar activity_button = widgets.ActivityToolbarButton(self) main_toolbar.insert(activity_button, 0) activity_button.show() \end{lstlisting} \end{frame} \section{\en{Packaging an activity}\es{Empaquetar una actividad}} \en{All the files for an activity are bundled together in a special format to form a single \texttt{.xo} file which can be executed by the XO. An \texttt{.xo} file is just a renamed Zip archive, but it must contain three key files: \texttt{setup.py}, \texttt{activity/activity.info} and \texttt{activity/activity.svg}.} \es{Todos los archivos de una actividad se agrupan en un formato especial para formar un único archivo \texttt{.xo} que puede ser ejecutado por el XO. Un archivo \texttt{.xo} es sólo un archivo Zip cambiado de nombre, pero debe contener tres archivos principales: \texttt{setup.py}, \texttt{activity/activity.info} y \texttt{activity/activity.svg}.} \es{Los nombres de archivo en Inglés no se deben traducir al Español, como el código de Sugar espera encontrar los nombres de los archivos exactos.} \begin{frame}{\en{XO bundle format}\es{Formato de los paquetes de XO}} \begin{itemize}\item{\texttt{\en{ActivityName.activity}\es{NombreDeActividad.activity}}\begin{itemize} \item{\texttt{\en{activityname.py}\es{nombredeactividad.py}}} \item{\texttt{setup.py}} \item{\texttt{activity/}\begin{itemize} \item{\texttt{activity.info}} \item{\texttt{activity.svg}} \end{itemize}} \item{\texttt{icons/}\begin{itemize} \item{\en{any-custom-icons.svg}\es{los-iconos-personalizados.svg}} \item{$\cdots$} \end{itemize}} \item{\texttt{po/}\begin{itemize} \item{\texttt{de.po}} \item{\texttt{es.po}} \item{\texttt{it.po}} \item{$\cdots$} \item{\texttt{\en{ActivityName.pot}\es{NombreDeActividad.pot}}} \end{itemize}} \end{itemize}}\end{itemize} \end{frame} \en{\texttt{activity.info} contains metadata about the activity, such as its name, description, ID, version and software licence. \texttt{activity.svg} is the icon for the activity. \texttt{setup.py} is used during development to build and test the activity, and is the same for all activities. \texttt{activityname.py} contains the code for the activity, and can have any name --- the actual filename is referenced in \texttt{activity.info}.} \es{\texttt{activity.info} contiene metadatos acerca de la actividad, tales como su nombre, descripción, ID, la versión y licencia de software. \texttt{activity.svg} es el icono de la actividad. \texttt{setup.py} se utiliza durante el desarrollo para construir y probar la actividad, y es el mismo para todas las actividades. \texttt{nombredeactividad.py} contiene el código para la actividad, y puede tener cualquier nombre --- el nombre de archivo real se hace referencia en la \texttt{activity.info}.} % TODO: Add a lstdefinelanguage for this if I get time. \begin{frame}[fragile] \frametitle{\texttt{activity.info}} \begin{lstlisting} [Activity] name = Pascal Triangle summary = Addition game using Pascal's triangle. activity_version = 1 host_version = 1 bundle_id = hn.educatrachos.pascaltriangle icon = activity exec = sugar-activity pascaltriangle.PascalTriangleActivity license = GPLv2+ \end{lstlisting} \end{frame} \begin{frame}[fragile] \frametitle{\texttt{setup.py}} \begin{lstlisting}[language=Python] #!/usr/bin/env python from sugar.activity import bundlebuilder if __name__ == "__main__": bundlebuilder.start() \end{lstlisting} \end{frame} \en{The \texttt{icons} directory is optional, and contains any custom icons which your activity uses (for example, in toolbars). The XO ships a few standard icons, but not many, so activities have to provide their own in many cases.} \es{La carpeta \texttt{icons} es opcional, y contiene los iconos personalizados que utiliza su actividad (por ejemplo, en las barras de herramientas). La XO proporciona unos iconos estándar, pero no muchos, por lo que las actividades tienen que proporcionar su propia en muchos casos.} \en{The \texttt{po} directory is also optional (though highly recommended) and contains translations of all the UI strings in the activity, so it can be run in other languages. By convention, all UI strings in Python are written in English --- so when writing an activity, you need to provide a Spanish translation as \texttt{es.po}. \texttt{ActivityName.pot} is a template for the \texttt{.po} files. Internationalisation will be covered in more detail later.} \es{La carpeta \texttt{po} también es opcional (aunque muy recomendable) y contiene las traducciones de todas las cadenas de la IU en la actividad, por lo que se puede ejecutar en otros idiomas. Por convención, todas las cadenas de la IU en Python están escritos en Inglés --- así cuando se escribe una actividad, es necesario proporcionar una traducción al Español como \texttt{es.po}. \texttt{NombreDeActividad.pot} es una plantilla para los archivos \texttt{.po}. Internacionalización se explica con más detalle más adelante.} \section{\en{Version control}\es{Control de versiones}} \en{Creating a new activity starts with creating the directory structure for it, then by setting up version control. Version control is a way of cataloguing the history of development of some code by storing multiple \emph{revisions} of each file. This allows examination of the changes between each revision (as the software is developed), and also allows the code to be reverted to a previous revision if problems are found in later code. Importantly, version control systems make it easy to distribute code across the Internet, by \emph{pushing} it to remote repositories.} \es{Crear una nueva actividad se inicia con la creación de la estructura de carpetas para que, a continuación, mediante el establecimiento de control de versiones. El control de versiones es una manera de catalogar la historia del desarrollo de un código mediante el almacenamiento de múltiples \emph{revisiones} de cada archivo. Esto permite que el examen de los cambios entre cada revisión (como se desarrolla el software), y también permite que el código para revertir a una revisión anterior si los problemas se encuentran en el código más adelante. Es importante destacar que los sistemas de control de versiones facilitan la distribución del código a través de Internet, \emph{empujándolo} a los repositorios remotos.} \en{The recommended version control system for use with Sugar activities is `git', a popular system used by many open source projects. There isn't time to give a full introduction to git here, so only a few commands will be demonstrated.} \es{El sistema de control de versiones se recomienda su uso en las actividades de Sugar es `git', un sistema popular y utilizado por muchos proyectos de software libre. No hay tiempo para dar una introducción completa a git aquí, así que sólo unos pocos comandos se demostrarán.} \en{Executed from a terminal, in the activity's directory, \texttt{git~init} creates a new git repository. (This only needs to be perfomed once.) \texttt{git~add} adds files to git's list of staged files, which can be seen by running \texttt{git~status}. \texttt{git~commit} creates a new revision in the repository, with the changes which were staged using \texttt{git~add}. The whole repository can then be pushed to a server. \texttt{git~log} shows the existing revisions in the repository.} \es{Ejecutado desde un terminal, en la carpeta de la actividad, \texttt{git~init} crea un nuevo repositorio de git. (Necesario sólo una vez.) \texttt{git~add} agrega archivos a la lista de archivos de git organizaron, que se puede ver mediante la ejecución de \texttt{git~status}. \texttt{git~commit} crea una nueva revisión en el repositorio, con los cambios que se organizaron con \texttt{git~add}. Todo el repositorio puede entonces ser empujada a un servidor. \texttt{git~log} muestra las revisiones existentes en el repositorio.} \begin{frame}{\en{Introduction to git}\es{Introducción a git}} \begin{itemize} \item{\texttt{git~init}} \item{\texttt{git~add \en{file1.py file2.py}\es{archivo1.py archivo2.py} \dots}} \item{\texttt{git~status}} \item{\texttt{git~commit}} \item{\texttt{git~log}} \end{itemize} \end{frame} \begin{frame}{\en{git resources}\es{Recursos para git}} \begin{block}{\en{Introductions}\es{Introducciones}} \begin{itemize} \item{\url{http://try.github.io/}} \item{\url{http://gitimmersion.com/}} \end{itemize} \end{block} \begin{block}{\en{References}\es{Referencias}} \begin{itemize} \item{\url{http://git-scm.com/book\es{/es}}} \item{\url{http://gitref.org/}} \end{itemize} \end{block} \end{frame} \en{Building the XO bundle is achieved using \texttt{setup.py}. When developing locally, run \texttt{./setup.py~dev} from a terminal in the activity's directory to set up the activity to run on your computer with \texttt{sugar-emulator} (discussed later). To build the \texttt{.xo} file for distribution, run \texttt{./setup.py~dist\_xo}. When building the \texttt{.xo} file, the script will ignore files which aren't committed to git, so ensure all relevant files (but not files which have been automatically generated) have been added to git using \texttt{git~add}.} \es{Construyendo el paquete XO se consigue utilizando \texttt{setup.py}. En el desarrollo a nivel local, ejecute \texttt{./setup.py~dev} desde un terminal en la carpeta de la actividad para establecer la actividad se ejecute en su equipo con \texttt{sugar-emulator} (explicado más adelante). Para generar el archivo \texttt{.xo} para la distribución, ejecute \texttt{./setup.py~dist\_xo}. Cuando se construye el archivo \texttt{.xo}, el script pasará por alto los archivos que no se han añadido a git, para asegurar que todos los archivos relevantes (pero no los archivos que se han generado de forma automática), se han añadido git utilizando \texttt{git~add}.} \begin{frame}{\texttt{setup.py}} \begin{itemize} \item{\texttt{./setup.py~dev}} \item{\texttt{./setup.py~dist\_xo}} \end{itemize} \end{frame} \section{\en{Emulating Sugar for testing}\es{Emulando Sugar para probar}} \en{A recommended way to develop activities is to use \texttt{sugar-emulator}, a program which can be installed on most desktop Linux distributions. It allows the Sugar environment to be emulated on a desktop, meaning a new activity can be tested without being copied over to a physical XO.} \es{Una forma recomendada para el desarrollo de actividades es utilizar \texttt{sugar-emulator}, un programa que se puede instalar en la mayoría de las distribuciones de Linux. Permite el entorno Sugar a emular en un escritorio, lo que significa una nueva actividad puede ser probado sin copiarse a un XO físico.} \en{To run \texttt{sugar-emulator}, simply type its name into the terminal. For extra debugging output, also set the \texttt{SUGAR\_LOG\_LEVEL} environment variable. To run the emulator in a different language, set the \texttt{LANG} environment variable.} \es{Para ejecutar \texttt{sugar-emulator}, simplemente escriba su nombre en el terminal. Para la salida de depuración extra, también establezca la variable de entorno \texttt{SUGAR\_LOG\_LEVEL}. Para ejecutar el emulador en un idioma diferente, establezca la variable de entorno \texttt{LANG}.} \begin{frame}{\texttt{sugar-emulator}} \begin{itemize} \item{\texttt{sugar-emulator}} \item{\texttt{SUGAR\_LOG\_LEVEL=debug LANG=es sugar-emulator}} \end{itemize} \end{frame} \note{Give an interactive demo of \texttt{sugar-emulator} at this point.} \en{Debugging an activity is similar to debugging any other Python program: the simplest way is to use the \texttt{print()} function to print out values from the program, and manually check whether they're what's expected --- or whether those points in the program are reached at all.} \es{Depuración de la actividad es similar a depurar cualquier otro programa de Python: la forma más sencilla es utilizar la función \texttt{print()} para imprimir los valores del programa, y comprobar manualmente si son lo que se espera --- o si esos puntos en el programa se alcanzan en absoluto.} \en{When using \texttt{sugar-emulator}, text outputted using \texttt{print()} won't appear in the normal terminal. Instead, it is saved to a log file in \texttt{\textasciitilde/.sugar/default/logs} which is specific to the activity. A new log file is created every time the activity is run in a single \texttt{sugar-emulator} session, so the number $N$ changes.} \es{Al usar \texttt{sugar-emulator}, texto emite utilizando \texttt{print()} no aparecerá en el terminal normal. En su lugar, se guarda en un archivo de registro en \texttt{\textasciitilde/.sugar/default/logs}, que es específica de la actividad. Un nuevo archivo de registro se crea cada vez que la actividad se ejecuta en una sola sesión de \texttt{sugar-emulator}, por lo que los cambios en el número $N$.} \en{For a more flexible logging solution which doesn't have to be removed before publishing the activity, use the \href{http://docs.python.org/2/library/logging.html}{Python \texttt{logging} module}. Create a \texttt{logger} object and call methods on it to print messages with different severities.} \es{Para una solución de registro más flexible que no tiene que ser removido antes de la publicación de la actividad, el uso del \href{http://docs.python.org/2/library/logging.html}{módulo \texttt{logging} de Python}. Crear un objeto \texttt{logger} y llamar a métodos en él para imprimir mensajes con diferentes niveles de gravedad.} \begin{frame}[fragile] \frametitle{\en{Sugar logging}\es{Registro en Sugar}} \begin{block}{\en{Log files}\es{Archivos de registro}} {\large{\texttt{\textasciitilde/.sugar/default/logs/\en{activity.id}\es{actividad.id}-$N$.log}}} \end{block} \begin{block}{\en{Logging code}\es{Código de registro}} \begin{lstlisting}[language=Python] import logging self._logger = logging.getLogger('nombre-de-mi-actividad') self._logger.error('Este es un error') self._logger.warning('...una advertencia') self._logger.debug('...un mensaje de depuracion') \end{lstlisting} \end{block} \end{frame} \section{\en{Internationalisation}\es{Internationalisation}} \en{Internationalisation is an important part of writing an activity, as it allows your activity to be used in other languages. Internationalisation is mostly about translation, but other things must be considered (such as currencies, date formats, and whether text is written left-to-right or right-to-left). Adding translation support to an activity is easier; the others are more complex and are not covered here.} \es{La internacionalización es una parte importante de la escritura una actividad, ya que permite que la actividad que se utilizará en otros idiomas. La internacionalización es sobre todo acerca de la traducción, pero otras cosas se debe considerar (como monedas, formatos de fecha y si el texto se escribe de izquierda a derecha o de derecha a izquierda). Adición de apoyo a la traducción de una actividad es más fácil; los otros son más complejos y no están cubiertos aquí.} \en{To add translation support, a library called \texttt{gettext} is used. For each UI string to be translated, the English string is passed through the \texttt{\_} (underscore) function provided by \texttt{gettext}, which returns either a translated version, or the original string if no translation is available.} \es{Para añadir soporte de traducción, se utiliza una librería llamada \texttt{gettext}. Para cada cadena de IU sea traducida, la cadena de Inglés se pasa a través de la función \texttt{\_} (guión bajo) proporcionado por \texttt{gettext}, que devuelve una versión traducida, o la cadena original si no hay traducción disponible.} \begin{frame}[fragile] \frametitle{\en{Translation support}\es{Apoyo a la traducción}} \begin{lstlisting}[language=Python] from gettext import gettext as _ # Previoso: Gtk.Button('Hello world!') button = Gtk.Button(_('Hello world!')) \end{lstlisting} \end{frame} \en{In addition to these code changes, a set of \texttt{.po} translation files have to be created. These give the translations of the English strings into other languages, one file per language. Each file is written manually by a human translator, working from a \texttt{.pot} template file. This \texttt{.pot} file must be periodically updated to reflect changes in the strings marked for translation in the program code.} \es{Además de estos cambios en el código, un conjunto de archivos de traducción \texttt{.po} tienen que ser creados. Estos dan las traducciones de las cadenas en Inglés a otros idiomas, un archivo por cada idioma. Cada archivo se escribe manualmente por un traductor humano, trabajando desde un archivo de plantilla \texttt{.pot}. Este archivo \texttt{.pot} debe actualizarse periódicamente para reflejar los cambios en las cuerdas marcadas para la traducción en el código del programa.} \en{To generate the \texttt{.pot} file, run \texttt{./setup.py~genpot}. You can then copy the \texttt{.pot} file to (for example) \texttt{es.po} and write the Spanish translation. Don't forget to commit both files to git.} \es{Para generar el archivo \texttt{.pot}, ejecutar \texttt{./setup.py~genpot}. A continuación, puede copiar el archivo \texttt{.pot} a (por ejemplo) \texttt{es.po} y escribir la traducción en Español. No te olvides de añadir los dos archivos a git.} \begin{frame}{\en{Generating a POT}\es{Generación de un POT}} \begin{itemize} \item{\texttt{./setup.py genpot}} \item{\texttt{cd po}} \item{\texttt{cp \en{ActivityName}\es{NombreDeActividad}.pot es.po}} \item{\en{Edit \texttt{es.po} to create the Spanish translation.} \es{Editar \texttt{es.po} para crear la traducción en Español}} \end{itemize} \end{frame} \note{Give an interactive example of generating a POT file and changing it to a PO file now.} \en{Activities whose source code is hosted on \url{http://git.sugarlabs.org} can use a web-based service called Pootle to get translations from teams all over the world. This is a valuable service, and more information about setting it up is available online.} \es{Actividades cuyo código está alojado en \url{http://git.sugarlabs.org} pueden utilizar un servicio basado en web llamado Pootle para obtener traducciones de los equipos de todo el mundo. Este es un servicio valioso, y más información sobre su puesta en marcha está disponible en línea.} \begin{frame}{\en{Adding Pootle support}\es{Añadir soporte de Pootle}} \begin{block}{\en{Reference}\es{Referencía}} \en{\url{http://en.flossmanuals.net/make-your-own-sugar-activities/going-international-with-pootle/}} \es{\url{http://en.flossmanuals.net/como-hacer-una-actividad-sugar/internacionalizarse-con-pootle-god-100/}} \end{block} \begin{block}{\en{Overview}\es{Visión de conjunto}} \begin{enumerate} \item{\en{Push activity code to git.sugarlabs.org, as described below.} \es{Empuje código de actividad a git.sugarlabs.org, tal como se describe a continuación.}} \item{\en{Allow the `pootle' user to commit to git.} \es{Permitir que el usuario `pootle' para crear commites en git.}} \item{\en{File a bug report requesting Pootle support for your project.} \es{Presentar un informe de error solicitando apoyo Pootle para su proyecto.}} \end{enumerate} \end{block} \end{frame} \section{\en{Publishing an activity}\es{Publicar una actividad}} \en{After finishing the activity, testing it on relevant versions of the XO (in emulation and on physical machines), and generating a \texttt{.xo} bundle, the activity needs to be published to \url{http://activities.sugarlabs.org} for users to download.} \es{Después de terminar la escritura de la actividad, la probación en las versiones pertinentes de la XO (en la emulación y en máquinas físicas), y la generación de un paquete de \texttt{.xo}, la actividad tiene que ser publicada a \url{http://activities.sugarlabs.org/es-ES/} para que los usuarios descargar.} \en{Before publishing the activity, its source code must be made available on \url{http://git.sugarlabs.org} so that others can view it and improve on it. Educatrachos has \href{https://git.sugarlabs.org/+educatrachos}{a team page} on that website, and anyone from Educatrachos who is writing an activity should become a member of the team. An administrator (like me) can do that.} \es{Antes de la publicación de la actividad, su código debe estar disponible en \url{http://git.sugarlabs.org} para que otros puedan verlo y mejorar en él. Educatrachos tiene \href{https://git.sugarlabs.org/+educatrachos}{una página del equipo} en ese sitio web, y cualquier persona de Educatrachos que está escribiendo una actividad debe convertirse en un miembro del equipo. Un administrador (como yo) puede hacer eso.} \en{To add an activity to \url{http://git.sugarlabs.org}, add a new project for it, then create a repository in that project. Name both after the activity. Once the repository's been created, add it as a \texttt{remote} called \texttt{origin} in your local git repository, then push the \texttt{master} branch to it. Whenever you commit to the local repository from that point onwards, you must subsequently push the commit to the remote so that it is visible online.} \es{Para añadir una actividad a \url{http://git.sugarlabs.org}, agregue un nuevo proyecto para que, a continuación, crear un repositorio en ese proyecto. Nombra dos después de la actividad. Después ha creado el repositorio, añadirlo como el \texttt{remote} se llama \texttt{origin} en su repositorio git local, empujar la rama \texttt{master} a ella. Cada vez que se crea un commit en el repositorio local de ese punto en adelante, debe posteriormente empujar el commit con el mando a distancia para que sea visible en Internet.} \begin{frame}{\en{Publishing activity source code} \es{Publicación del código de una actividad}} \begin{enumerate} \item{\en{Register on \href{http://git.sugarlabs.org}{git.sugarlabs.org}:} \es{Registrarse en \href{http://git.sugarlabs.org}{git.sugarlabs.org}:} \url{https://git.sugarlabs.org/users/new}} \item{\en{Have your account added to the Educatrachos team:} \es{Haga que su cuenta agregada al equipo Educatrachos:} \url{https://git.sugarlabs.org/+educatrachos/memberships/new}} \item{\en{Create a new project owned by the Educatrachos team:} \es{Crear un nuevo proyecto de propiedad del equipo Educatrachos:} \url{https://git.sugarlabs.org/projects/new}} \item{\en{Add a new repository to the project. Enable merge requests so others can suggest changes to the code.} \es{Añadir un nuevo repositorio para el proyecto. Permitir solicitudes de fusiones para que otros pueden sugerir cambios en el código.}} \item{\en{Copy the `SSH' URI from ``Clone \& push URLs'' on the repository page. e.g.\ \url{gitorious@git.sugarlabs.org:pascal-triangle/pascal-triangle.git}} \es{Copie el URI `SSH' desde ``Clone \& push URLs'' en la página del repositorio. Por ejemplo, \url{gitorious@git.sugarlabs.org:pascal-triangle/pascal-triangle.git}}} \item{\en{In a terminal:}\es{En un terminal:} \texttt{cd \en{path/to/my-activity}\es{camino/a/mi-actividad}}} \item{\texttt{git~remote add origin \emph{SSH URI}}} \item{\texttt{git~push -u origin master}} \end{enumerate} \end{frame} \en{With the source code published online, the activity can be added to \url{http://activities.sugarlabs.org}. You must register an account on that website, then join the \href{http://activities.sugarlabs.org/en-US/developers}{Developer Hub} and follow the process to submit a new activity.} \es{Con el código fuente publicada en línea, la actividad se puede añadir a \url{http://activities.sugarlabs.org/es-ES/}. Debe registrar una cuenta en ese sitio web, y luego unirse el Centro de Desarrollo y seguir el proceso para enviar una nueva actividad.} \en{Newly submitted activities are initially marked as private. Once enough details have been filled out for an activity, it is moved to the sandbox, when users can start downloading it. Once enough users have downloaded and rated the activity, it can be peer-reviewed and promoted to public status.} \es{Actividades recién enviados inicialmente se marcan como privados. Una vez que suficientes detalles se han llenado de una actividad, se trasladó a la caja de arena, cuando los usuarios pueden empezar a descargarlo. Una vez que suficientes usuarios han descargado y evaluado la actividad, puede ser revisada por pares y promovido a la condición pública.} \begin{frame}{\en{Publishing an activity bundle} \es{Publicación de un paquete de una actividad}} \begin{enumerate} \item{\en{Register on \href{http://activities.sugarlabs.org}{activities.sugarlabs.org}: \url{http://activities.sugarlabs.org/en-US/sugar/users/register}} \es{Registrarse en: \href{http://activities.sugarlabs.org/es-ES/} {activities.sugarlabs.org}: \url{http://activities.sugarlabs.org/es-ES/sugar/users/register}}} \item{\en{Join the `Developer Hub': \url{http://activities.sugarlabs.org/en-US/developers}} \es{Únete a la `Centro de desarrolladores': \url{http://activities.sugarlabs.org/es-ES/developers}}} \item{\en{Follow the process to submit a new activity: \url{http://activities.sugarlabs.org/en-US/developers/addon/submit}} \es{Siga el proceso para enviar una nueva actividad: \url{http://activities.sugarlabs.org/es-ES/developers/addon/submit}}} \end{enumerate} \end{frame} \section{\en{Conclusion}\es{Conclusión}} \en{In summary: one of the key aims of writing your own activities should be to work in partnership with the worldwide Sugar and OLPC community. This is sometimes called ``working upstream''. They are happy to answer questions and help solve problems, and in return they would be happy to receive new activities and contributions which can be re-used by other OLPC deployments. As with any community, the tighter people integrate, the better the results for everyone.} \es{En resumen: uno de los objetivos principales de escribir sus propias actividades debería ser trabajar en colaboración con la comunidad mundial de Sugar y OLPC. Esto a veces se llama ``trabajando río arriba''. Ellos están dispuestos a responder preguntas y ayudar a resolver problemas, y en cambio estarían felices de recibir las nuevas actividades y las contribuciones que pueden ser reutilizados por otros despliegues de OLPC. Al igual que con cualquier comunidad, el pueblo más estrictas integran, mejores serán los resultados para todos.} \en{This is the end of the training, but not the end of the learning. I encourage you to practice everything you've learned by writing activities. The best way to learn programming is through practice. I am happy to answer absolutely any questions either in person or by e-mail, even after I've left Honduras. I would be thrilled to help get activities published by Educatrachos once I return to the UK.} \es{Este es el final de la capacitación, pero no el final del aprendizaje. Les animo a practicar todo lo que has aprendido al escribir algunas actividades. La mejor manera de aprender es a través de la práctica de programación. Estoy encantado de responder a cualquier pregunta en absoluto, ya sea en persona o por e-mail, incluso después de que me he dejado Honduras. Yo estaría encantado de ayudar a que las actividades publicadas por Educatrachos cuando regreso al Reino~Unido.} \note{After a break, it would be a good idea to give an interactive demo of creating an entire activity from scratch.} \section{\en{Miscellany}\es{Miscelánea}} \en{Again, not all the features of Sugar can be covered in the available time. Here are the topics which haven't been covered, along with links to relevant documentation. Writing activities in HTML5 wasn't covered because the version of Sugar deployed in Honduras is too old to support them.} \es{Una vez más, no todas las características de Sugar se pueden cubrir en el tiempo disponible. Estos son los temas que no han sido cubiertos, así como enlaces a la documentación pertinente. Las actividades de escritura en HTML5 no estaba cubierto por la versión de Sugar desplegado en Honduras es demasiado viejo para apoyarlos.} \begin{frame}{\en{What's missing}\es{Temas no abarcados}} \begin{itemize} \item{\en{Drawing custom widgets with Cairo:} \es{Dibujo widgets personalizados con Cairo:} % FIXME: This should actually be the following, but it doesn't exist yet: % https://developer.gnome.org/gnome-devel-demos/unstable/widget_drawing.py.html.en \href{http://tecnocode.co.uk/misc/platform-demos/widget_drawing.py.xhtml}{(1)}, \href{http://www.tortall.net/mu/wiki/CairoTutorial}{(2)}, \href{https://sites.google.com/site/randomcodecollections/home/python-gtk-3-pango-cairo-example}{(3)}, \href{http://ptomato.name/advanced-gtk-techniques/html/custom-container.html}{(4)}, \href{http://lotsofexpression.blogspot.com/2012/04/python-gtk-3-example-implementing-cairo.html}{(5)}} \item{\en{Shared activities:}\es{Actividades compartidas:} \en{\href{http://en.flossmanuals.net/make-your-own-sugar-activities/making-shared-activities/}{(1)}} \es{\href{http://en.flossmanuals.net/como-hacer-una-actividad-sugar/making-shared-activities-vc/}{(1)}}} \item{\en{Text-to-speech:}\es{Texto a voz:} \en{\href{http://en.flossmanuals.net/make-your-own-sugar-activities/adding-text-to-speech/}{(1)}} \es{\href{http://en.flossmanuals.net/como-hacer-una-actividad-sugar/adding-text-to-speach/}{(1)}}} \item{\en{Games with PyGame:}\es{Juegos con PyGame:} \en{\href{http://en.flossmanuals.net/make-your-own-sugar-activities/making-activities-using-pygame/}{(1)}} \es{\href{http://en.flossmanuals.net/como-hacer-una-actividad-sugar/construir-actividades-usando-pygame/}{(1)}}} \item{HTML5: \href{http://developer.sugarlabs.org/web-architecture.md.html}{(1)}} \end{itemize} \end{frame} \begin{frame}{\en{Links}\es{Enlaces}} \begin{block}{\en{Important links}\es{Enlaces importantes}} \begin{itemize} \item{\en{\url{http://en.flossmanuals.net/make-your-own-sugar-activities/}} \es{\url{http://en.flossmanuals.net/como-hacer-una-actividad-sugar/heredar-activity-de-sugaractivity-jm-50/}}} \item{\url{http://python-gtk-3-tutorial.readthedocs.org/en/}} \item{\url{http://doc.sugarlabs.org/epydocs/}} \end{itemize} \end{block} \begin{block}{\en{Other links}\es{Enlaces otros}} \begin{itemize} \item{\url{http://wiki.sugarlabs.org/go/Features/GTK3/Porting}} \item{\url{http://wiki.sugarlabs.org/go/Human_Interface_Guidelines/The_Sugar_Interface}} \item{\url{http://wiki.sugarlabs.org/go/Development_Team/Almanac}} \item{\url{http://wiki.sugarlabs.org/go/Activity_Team}} \end{itemize} \end{block} \end{frame} \begin{frame}{\en{Questions?}\es{¿Preguntas?}} \en{Any questions so far?} \es{¿Hay preguntas hasta ahora?} \end{frame}