From dcc0955e675e19b0e8f1ac12bd4312cba5fac233 Mon Sep 17 00:00:00 2001 From: Philip Withnall Date: Tue, 03 Sep 2013 22:15:47 +0000 Subject: Add English exercises for part 3 of the course --- diff --git a/Makefile b/Makefile index 2653337..8467d7f 100644 --- a/Makefile +++ b/Makefile @@ -19,6 +19,7 @@ EN_EXERCISES = \ 13_extend-class \ 14_property \ 15_private-methods \ + 17_button-and-label \ $(NULL) ES_EXERCISES = \ 1_imprimir-nombres \ @@ -53,6 +54,7 @@ ZIPS = \ python-ejercicios.zip \ python-exercises.zip \ $(NULL) + EN_PYTHON_FILES = \ $(addprefix exercises/en/,$(addsuffix .py,$(EN_EXERCISES))) \ $(addprefix exercises/en/,$(addsuffix _solution.py,$(EN_EXERCISES))) \ @@ -63,6 +65,25 @@ ES_PYTHON_FILES = \ $(addprefix exercises/es/,$(addsuffix _solucion.py,$(ES_EXERCISES))) \ $(NULL) +EN_EXERCISE_FILES = \ + $(EN_PYTHON_FILES) \ + exercises/en/16_gnome-devel-demos.txt \ + exercises/en/17_button-and-label.py \ + exercises/en/17_button-and-label_solution.py \ + exercises/en/Exercise18.activity/ \ + exercises/en/18_gtk-to-activity.txt \ + exercises/en/Exercise19.activity/ \ + exercises/en/19_translation.txt \ + exercises/en/Exercise20.activity/ \ + exercises/en/20_packaging.txt \ + exercises/en/21_complete-activity.txt \ + $(NULL) + +ES_EXERCISE_FILES = \ + $(ES_PYTHON_FILES) \ + exercises/es/16_gnome-devel-demos.txt \ + $(NULL) + all: $(PDFS) $(ZIPS) presentation.pdf: presentation.tex $(TEX_FILES) $(IMAGE_FILES) @@ -74,15 +95,15 @@ handout.pdf: handout.tex $(TEX_FILES) $(IMAGE_FILES) pdflatex "\providecommand\locale{$(LANG)}\input{$<}" # Zip up the exercises. -python-exercises.zip: $(EN_PYTHON_FILES) +python-exercises.zip: $(EN_EXERCISE_FILES) zip --quiet --no-dir-entries $@ $^ -python-ejercicios.zip: $(ES_PYTHON_FILES) +python-ejercicios.zip: $(ES_EXERCISE_FILES) zip --quiet --no-dir-entries $@ $^ check: - $(foreach ex,$(EN_EXERCISES),pep8 exercises/en/${ex}.py exercises/en/${ex}_solution.py;) - $(foreach ex,$(ES_EXERCISES),pep8 exercises/es/${ex}.py exercises/es/${ex}_solucion.py;) + pep8 $(EN_PYTHON_FILES) + pep8 $(ES_PYTHON_FILES) clean: rm -f $(PDFS:.pdf=.aux) diff --git a/exercises/en/16_gnome-devel-demos.txt b/exercises/en/16_gnome-devel-demos.txt new file mode 100644 index 0000000..a1c013e --- /dev/null +++ b/exercises/en/16_gnome-devel-demos.txt @@ -0,0 +1,28 @@ +Exercise 16 is not assessed. Instead, you should go to: + https://developer.gnome.org/gnome-devel-demos/unstable/tutorial.py.html.en +and work through the following examples. For each example, copy the code into +a file and run it using Python. Read the documentation on the web pages and +ensure you understand how the example works. + +You should look at the following tutorial pages, at a minimum: + • 1.1 + • 2.1 + • 2.2 + • 2.3 + • 3.1 + • 4.1 + • 4.2 + • 4.3 + • 4.4 + • 5.1 + • 5.2 + • 5.4 + • 5.5 + • 5.7 + • 7.3 + • 8.2 + • 8.3 + • 9.1 + • 11.3 + +Note that tutorial page 14.1 is covered separately in a later exercise. diff --git a/exercises/en/17_button-and-label.py b/exercises/en/17_button-and-label.py new file mode 100644 index 0000000..2eee908 --- /dev/null +++ b/exercises/en/17_button-and-label.py @@ -0,0 +1,64 @@ + +#!/usr/bin/python +# coding=utf-8 + +"""This program should display a GTK+ window containing a button and label. + +You must add two widgets to the box in the window: a Gtk.Button and a +Gtk.Label. + +The Gtk.Button must show the text 'Change Label'. The Gtk.Label must show the +text 'Initial label.'. + +After you have added the two widgets, you must connect a callback to the +button's "clicked" signal. In this callback, you must change the Gtk.Label's +text to 'Final label.'. This means that when the program is run and the button +is clicked, the label should change its text. + +You can find documentation for the Gtk.Button "clicked" signal here: + https://developer.gnome.org/gtk3/stable/GtkButton.html#GtkButton-clicked + +Don't forget to document any new methods you add. + +Hint: Don't forget to call the show() method on the button and label to make +them visible after adding them to the box. +""" + +from gi.repository import Gtk + + +class SimpleWindow(Gtk.Window): + """A simple window which displays a label and a button.""" + def __init__(self): + super(SimpleWindow, self).__init__(title='Simple Window') + + # Set a default size for the window. + self.set_default_size(200, 200) + + # Set up a close handler for the window. Do not modify this. + self.connect('destroy', self.__window_destroy_cb) + + # Add a box to the window. + box = Gtk.Box() + box.set_orientation(Gtk.Orientation.VERTICAL) + box.set_spacing(8) + box.set_border_width(8) + box.show() + self.add(box) + + # You must add code here to create and set up the button and label. + # Don't forget to make them visible and add them to the box. + + # You must add a signal callback here to handle the button's "clicked" + # signal and change the label's text. + + def __window_destroy_cb(self, window): + """Callback to quit the program when the window is closed. + + Do not modify this.""" + Gtk.main_quit() + + +# Run the program. Do not modify this. +SimpleWindow().show() +Gtk.main() diff --git a/exercises/en/17_button-and-label_solution.py b/exercises/en/17_button-and-label_solution.py new file mode 100644 index 0000000..30d644b --- /dev/null +++ b/exercises/en/17_button-and-label_solution.py @@ -0,0 +1,77 @@ +#!/usr/bin/python +# coding=utf-8 + +"""This program should display a GTK+ window containing a button and label. + +You must add two widgets to the box in the window: a Gtk.Button and a +Gtk.Label. + +The Gtk.Button must show the text 'Change Label'. The Gtk.Label must show the +text 'Initial label.'. + +After you have added the two widgets, you must connect a callback to the +button's "clicked" signal. In this callback, you must change the Gtk.Label's +text to 'Final label.'. This means that when the program is run and the button +is clicked, the label should change its text. + +You can find documentation for the Gtk.Button "clicked" signal here: + https://developer.gnome.org/gtk3/stable/GtkButton.html#GtkButton-clicked + +Don't forget to document any new methods you add. + +Hint: Don't forget to call the show() method on the button and label to make +them visible after adding them to the box. +""" + +from gi.repository import Gtk + + +class SimpleWindow(Gtk.Window): + """A simple window which displays a label and a button.""" + def __init__(self): + super(SimpleWindow, self).__init__(title='Simple Window') + + # Set a default size for the window. + self.set_default_size(200, 200) + + # Set up a close handler for the window. Do not modify this. + self.connect('destroy', self.__window_destroy_cb) + + # Add a box to the window. + box = Gtk.Box() + box.set_orientation(Gtk.Orientation.VERTICAL) + box.set_spacing(8) + box.set_border_width(8) + box.show() + self.add(box) + + # Create and set up the label and button. + label = Gtk.Label('Initial label.') + label.show() + box.pack_start(label, True, True, 0) + + button = Gtk.Button('Change Label') + button.show() + box.pack_start(button, False, True, 0) + + # Connect the button callback. + button.connect('clicked', self.__button_clicked_cb) + + # Store the label as an object variable so it can be accessed from the + # button callback. + self._label = label + + def __button_clicked_cb(self, button): + """Callback to change the label text when the button is clicked.""" + self._label.set_text('Final label.') + + def __window_destroy_cb(self, window): + """Callback to quit the program when the window is closed. + + Do not modify this.""" + Gtk.main_quit() + + +# Run the program. Do not modify this. +SimpleWindow().show() +Gtk.main() diff --git a/exercises/en/18_gtk-to-activity.txt b/exercises/en/18_gtk-to-activity.txt new file mode 100644 index 0000000..8836099 --- /dev/null +++ b/exercises/en/18_gtk-to-activity.txt @@ -0,0 +1,3 @@ +The instructions for exercise 18 are in Exercise18.activity/exercise18.py, +which is the only file you need to modify. +The example solution is in Exercise18.activity/exercise18_solution.py. diff --git a/exercises/en/19_translation.txt b/exercises/en/19_translation.txt new file mode 100644 index 0000000..520f31a --- /dev/null +++ b/exercises/en/19_translation.txt @@ -0,0 +1,3 @@ +The instructions for exercise 19 are in Exercise19.activity/exercise19.py. +The example solution is in Exercise19.activity/exercise19_solution.py. No +example solution files are provided for the POT and PO files. diff --git a/exercises/en/20_packaging.txt b/exercises/en/20_packaging.txt new file mode 100644 index 0000000..c43ffeb --- /dev/null +++ b/exercises/en/20_packaging.txt @@ -0,0 +1,2 @@ +The instructions for exercise 20 are in Exercise20.activity/exercise20.py. There +is no example solution, but there are detailed instructions to follow. diff --git a/exercises/en/21_complete-activity.txt b/exercises/en/21_complete-activity.txt new file mode 100644 index 0000000..325275a --- /dev/null +++ b/exercises/en/21_complete-activity.txt @@ -0,0 +1,27 @@ +Exercise 20 + +For this final exercise, you must create a new Sugar activity from scratch. You +have two options of activities to create: + A. An ‘odd one out’ game. It should display four buttons on the screen, each + with a conjugation of a verb as its text. The four conjugations should be + different, and one should be incorrect. The user must click on the button + with the incorrect conjugation. If they do, their score is increased and + another set of four conjugations is displayed. If they click on the wrong + button, their score is not incremented and the conjugations do not change. + The user must click the odd one out to proceed. + The user’s score should be displayed in a label in the toolbar (ask if you + need help with this, or look at the code in PascalTriangle which does a + similar thing by putting a GtkScale in its toolbar). + B. Similar to the above game, but using mathematical equalities instead of + verb conjugations. The game should present the user with four equalities + (e.g. “5 × 6 + 3 = 33”, “(1 + 4) × 2 = 10”, etc.), one of which should be + incorrect. The user must pick the incorrect one to proceed. + +Once you’ve created the activity, tested it in emulation and tested it on a +physical XO, you should get someone else to review all of your code and see if +they can find any problems with it. Code review like this is always helpful, as +every programmer makes mistakes (no matter how experienced they are) and a +fresh pair of eyes can spot them. Ideally, your code should be reviewed by +someone who wrote the other type of activity to you, so they have to rely on +comments in your code to work out how the code functions. Remember to comment +your code well! diff --git a/exercises/en/Exercise18.activity/activity/activity.info b/exercises/en/Exercise18.activity/activity/activity.info new file mode 100644 index 0000000..e0ffeda --- /dev/null +++ b/exercises/en/Exercise18.activity/activity/activity.info @@ -0,0 +1,9 @@ +[Activity] +name = Exercise 18 +summary = Python exercise 18. Not a real activity. +activity_version = 1 +host_version = 1 +bundle_id = hn.educatrachos.exercise18 +icon = activity +exec = sugar-activity exercise18.Exercise18Activity +license = GPLv2+ diff --git a/exercises/en/Exercise18.activity/activity/activity.svg b/exercises/en/Exercise18.activity/activity/activity.svg new file mode 100644 index 0000000..580b0e6 --- /dev/null +++ b/exercises/en/Exercise18.activity/activity/activity.svg @@ -0,0 +1,163 @@ + + + +]> + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + diff --git a/exercises/en/Exercise18.activity/exercise18.py b/exercises/en/Exercise18.activity/exercise18.py new file mode 100644 index 0000000..753eb4a --- /dev/null +++ b/exercises/en/Exercise18.activity/exercise18.py @@ -0,0 +1,99 @@ +#!/usr/bin/python +# coding=utf-8 + +"""Convert a simple GTK+ program to be a Sugar activity. + +You must convert the simple GTK+ program from exercise 17 to be a Sugar +activity. + +All the other files for the activity (activity.info, setup.py, etc.) have been +provided already; you must simply modify the code in this file to derive the +class from sugar3.activity.activity.Activity instead of from Gtk.Window, and +make a few other changes. + + 1. Remove the “#!/usr/bin/python” line from the top of the file. + The first line of the file should now be “# coding=utf-8”. + 2. Rename the class to “Exercise18Activity”. + 3. Add a ‘handle’ parameter to __init__ and propagate it to the parent + constructor in the super() call. + 4. Remove the window title from the constructor super() call. + 5. Remove the call to set_default_size() and the window destroy callback. + 6. Add an activity toolbar (ToolbarBox), and add an activity button + (ActivityToolbarButton) and a stop button (StopButton) to it. + 7. Change the self.add() call to self.set_canvas(). + 8. In a terminal, change to this directory and run ‘./setup.py dev’ to add the + activity to Sugar on your computer. + 9. In a terminal, run ‘sugar-emulator’ and test the activity. + +Note that the necessary ‘import’ statements have already been added to the +file. + +Don't forget to document any new methods you add. + +Hint: The code necessary for step 6 is: + # Create the standard activity toolbox. + toolbar_box = ToolbarBox() + self.set_toolbar_box(toolbar_box) + toolbar_box.show() + + main_toolbar = toolbar_box.toolbar + + activity_toolbar_button = widgets.ActivityToolbarButton(self) + main_toolbar.insert(activity_toolbar_button, 0) + activity_toolbar_button.show() + + stop_button = widgets.StopButton(self) + stop_button.show() + main_toolbar.insert(stop_button, -1) +""" + +from gi.repository import Gtk +from sugar3.activity import activity, widgets +from sugar3.graphics.toolbarbox import ToolbarBox +from sugar3.graphics.toolbutton import ToolButton + + +class SimpleWindow(Gtk.Window): + """A simple window which displays a label and a button.""" + def __init__(self): + super(SimpleWindow, self).__init__(title='Simple Window') + + # Set a default size for the window. + self.set_default_size(200, 200) + + # Set up a close handler for the window. Do not modify this. + self.connect('destroy', self.__window_destroy_cb) + + # Add a box to the window. + box = Gtk.Box() + box.set_orientation(Gtk.Orientation.VERTICAL) + box.set_spacing(8) + box.set_border_width(8) + box.show() + self.add(box) + + # Create and set up the label and button. + label = Gtk.Label('Initial label.') + label.show() + box.pack_start(label, True, True, 0) + + button = Gtk.Button('Change Label') + button.show() + box.pack_start(button, False, True, 0) + + # Connect the button callback. + button.connect('clicked', self.__button_clicked_cb) + + # Store the label as an object variable so it can be accessed from the + # button callback. + self._label = label + + def __button_clicked_cb(self, button): + """Callback to change the label text when the button is clicked.""" + self._label.set_text('Final label.') + + def __window_destroy_cb(self, window): + """Callback to quit the program when the window is closed. + + Do not modify this.""" + Gtk.main_quit() diff --git a/exercises/en/Exercise18.activity/exercise18_solution.py b/exercises/en/Exercise18.activity/exercise18_solution.py new file mode 100644 index 0000000..da880e8 --- /dev/null +++ b/exercises/en/Exercise18.activity/exercise18_solution.py @@ -0,0 +1,101 @@ +# coding=utf-8 + +"""Convert a simple GTK+ program to be a Sugar activity. + +You must convert the simple GTK+ program from exercise 17 to be a Sugar +activity. + +All the other files for the activity (activity.info, setup.py, etc.) have been +provided already; you must simply modify the code in this file to derive the +class from sugar3.activity.activity.Activity instead of from Gtk.Window, and +make a few other changes. + + 1. Remove the “#!/usr/bin/python” line from the top of the file. + The first line of the file should now be “# coding=utf-8”. + 2. Rename the class to “Exercise18Activity”. + 3. Add a ‘handle’ parameter to __init__ and propagate it to the parent + constructor in the super() call. + 4. Remove the window title from the constructor super() call. + 5. Remove the call to set_default_size() and the window destroy callback. + 6. Add an activity toolbar (ToolbarBox), and add an activity button + (ActivityToolbarButton) and a stop button (StopButton) to it. + 7. Change the self.add() call to self.set_canvas(). + 8. In a terminal, change to this directory and run ‘./setup.py dev’ to add the + activity to Sugar on your computer. + 9. In a terminal, run ‘sugar-emulator’ and test the activity. + +Note that the necessary ‘import’ statements have already been added to the +file. + +Don't forget to document any new methods you add. + +Hint: The code necessary for step 6 is: + # Create the standard activity toolbox. + toolbar_box = ToolbarBox() + self.set_toolbar_box(toolbar_box) + toolbar_box.show() + + main_toolbar = toolbar_box.toolbar + + activity_toolbar_button = widgets.ActivityToolbarButton(self) + main_toolbar.insert(activity_toolbar_button, 0) + activity_toolbar_button.show() + + stop_button = widgets.StopButton(self) + stop_button.show() + main_toolbar.insert(stop_button, -1) +""" + +from gi.repository import Gtk +from sugar3.activity import activity, widgets +from sugar3.graphics.toolbarbox import ToolbarBox +from sugar3.graphics.toolbutton import ToolButton + + +class Exercise18Activity(activity.Activity): + """A simple window which displays a label and a button.""" + def __init__(self, handle): + super(Exercise18Activity, self).__init__(handle) + + # Create the standard activity toolbox. + toolbar_box = ToolbarBox() + self.set_toolbar_box(toolbar_box) + toolbar_box.show() + + main_toolbar = toolbar_box.toolbar + + activity_toolbar_button = widgets.ActivityToolbarButton(self) + main_toolbar.insert(activity_toolbar_button, 0) + activity_toolbar_button.show() + + stop_button = widgets.StopButton(self) + stop_button.show() + main_toolbar.insert(stop_button, -1) + + # Add a box to the window. + box = Gtk.Box() + box.set_orientation(Gtk.Orientation.VERTICAL) + box.set_spacing(8) + box.set_border_width(8) + box.show() + self.set_canvas(box) + + # Create and set up the label and button. + label = Gtk.Label('Initial label.') + label.show() + box.pack_start(label, True, True, 0) + + button = Gtk.Button('Change Label') + button.show() + box.pack_start(button, False, True, 0) + + # Connect the button callback. + button.connect('clicked', self.__button_clicked_cb) + + # Store the label as an object variable so it can be accessed from the + # button callback. + self._label = label + + def __button_clicked_cb(self, button): + """Callback to change the label text when the button is clicked.""" + self._label.set_text('Final label.') diff --git a/exercises/en/Exercise18.activity/setup.py b/exercises/en/Exercise18.activity/setup.py new file mode 100755 index 0000000..77fda74 --- /dev/null +++ b/exercises/en/Exercise18.activity/setup.py @@ -0,0 +1,4 @@ +#!/usr/bin/env python +from sugar.activity import bundlebuilder +if __name__ == "__main__": + bundlebuilder.start() diff --git a/exercises/en/Exercise19.activity/activity/activity.info b/exercises/en/Exercise19.activity/activity/activity.info new file mode 100644 index 0000000..3a0739c --- /dev/null +++ b/exercises/en/Exercise19.activity/activity/activity.info @@ -0,0 +1,9 @@ +[Activity] +name = Exercise 19 +summary = Python exercise 19. Not a real activity. +activity_version = 1 +host_version = 1 +bundle_id = hn.educatrachos.exercise19 +icon = activity +exec = sugar-activity exercise19.Exercise19Activity +license = GPLv2+ diff --git a/exercises/en/Exercise19.activity/activity/activity.svg b/exercises/en/Exercise19.activity/activity/activity.svg new file mode 100644 index 0000000..580b0e6 --- /dev/null +++ b/exercises/en/Exercise19.activity/activity/activity.svg @@ -0,0 +1,163 @@ + + + +]> + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + diff --git a/exercises/en/Exercise19.activity/exercise19.py b/exercises/en/Exercise19.activity/exercise19.py new file mode 100644 index 0000000..d912d00 --- /dev/null +++ b/exercises/en/Exercise19.activity/exercise19.py @@ -0,0 +1,106 @@ +# coding=utf-8 + +"""Add internationalisation support to a Sugar activity. + +Below is a simple Sugar activity which displays a different string every time +the button is pressed. You must modify it to add internationalisation support +so that its UI can be viewed in a different language. + +All the other files for the activity (activity.info, setup.py, etc.) have been +provided already; you must modify the code in this file to add +internationalisation support, generate a POT file, and then translate it to +Spanish. + +Note that the original strings in the program are always in English, by +convention. + + 1. Add an ‘import’ statement for the gettext module: + from gettext import gettext as _ + 2. Mark all the relevant strings in the program as translatable using the _() + function. + 3. Generate a POT file by executing ‘./setup.py genpot’ in a terminal (after + changing to this directory). + 4. Copy the generated po/Exercise19.pot file to po/es.po. + 5. Either open es.po in a text editor, or use a translation tool like + gtranslator, and translate the strings to Spanish. + 6. Run ‘./setup.py dev’ to add the activity to Sugar on your computer. + 7. Run ‘./setup.py build’ to compile the .po files into machine-readable .mo + files in the ‘locale’ directory. + 7. Run ‘LANG=es sugar-emulator’ to run the Sugar emulator in Spanish and test + the new translation. The whole activity should appear in Spanish. + +Note: It is not possible to use Pootle for this exercise because it’s an online +service and would require the exercise’s code to be uploaded to the internet. +""" + +from gi.repository import Gtk +from sugar3.activity import activity, widgets +from sugar3.graphics.toolbarbox import ToolbarBox + + +class Exercise19Activity(activity.Activity): + """A simple window which displays a label and a button.""" + def __init__(self, handle): + super(Exercise19Activity, self).__init__(handle) + + # Create the standard activity toolbox. + toolbar_box = ToolbarBox() + self.set_toolbar_box(toolbar_box) + toolbar_box.show() + + main_toolbar = toolbar_box.toolbar + + activity_toolbar_button = widgets.ActivityToolbarButton(self) + main_toolbar.insert(activity_toolbar_button, 0) + activity_toolbar_button.show() + + stop_button = widgets.StopButton(self) + stop_button.show() + main_toolbar.insert(stop_button, -1) + + # Add a box to the window. + box = Gtk.Box() + box.set_orientation(Gtk.Orientation.VERTICAL) + box.set_spacing(8) + box.set_border_width(8) + box.show() + self.set_canvas(box) + + # Set up a list of text strings to display in the label. + self._strings = ( + 'Initial string.', + 'Second string.', + 'Handle object is ‘%s’.' % handle, + 'Another string.', + ) + self._next_string = 0 + + # Create and set up the label and button. + label = Gtk.Label() + label.show() + box.pack_start(label, True, True, 0) + + button = Gtk.Button('Change Label') + button.show() + box.pack_start(button, False, True, 0) + + # Connect the button callback. + button.connect('clicked', self.__button_clicked_cb) + + # Store the label as an object variable so it can be accessed from the + # button callback, and then set the initial text for the label. + self._label = label + self.__rotate_label_text() + + def __button_clicked_cb(self, button): + """Callback to change the label text when the button is clicked.""" + self.__rotate_label_text() + + def __rotate_label_text(self): + """Change the label to display the next string in the rotation. + + The modulus operation is used to wrap self._next_string around when + it reaches the final index in the self._strings array. + """ + self._label.set_text(self._strings[self._next_string]) + self._next_string = (self._next_string + 1) % len(self._strings) diff --git a/exercises/en/Exercise19.activity/exercise19_solution.py b/exercises/en/Exercise19.activity/exercise19_solution.py new file mode 100644 index 0000000..4818446 --- /dev/null +++ b/exercises/en/Exercise19.activity/exercise19_solution.py @@ -0,0 +1,107 @@ +# coding=utf-8 + +"""Add internationalisation support to a Sugar activity. + +Below is a simple Sugar activity which displays a different string every time +the button is pressed. You must modify it to add internationalisation support +so that its UI can be viewed in a different language. + +All the other files for the activity (activity.info, setup.py, etc.) have been +provided already; you must modify the code in this file to add +internationalisation support, generate a POT file, and then translate it to +Spanish. + +Note that the original strings in the program are always in English, by +convention. + + 1. Add an ‘import’ statement for the gettext module: + from gettext import gettext as _ + 2. Mark all the relevant strings in the program as translatable using the _() + function. + 3. Generate a POT file by executing ‘./setup.py genpot’ in a terminal (after + changing to this directory). + 4. Copy the generated po/Exercise19.pot file to po/es.po. + 5. Either open es.po in a text editor, or use a translation tool like + gtranslator, and translate the strings to Spanish. + 6. Run ‘./setup.py dev’ to add the activity to Sugar on your computer. + 7. Run ‘./setup.py build’ to compile the .po files into machine-readable .mo + files in the ‘locale’ directory. + 7. Run ‘LANG=es sugar-emulator’ to run the Sugar emulator in Spanish and test + the new translation. The whole activity should appear in Spanish. + +Note: It is not possible to use Pootle for this exercise because it’s an online +service and would require the exercise’s code to be uploaded to the internet. +""" + +from gi.repository import Gtk +from sugar3.activity import activity, widgets +from sugar3.graphics.toolbarbox import ToolbarBox +from gettext import gettext as _ + + +class Exercise19Activity(activity.Activity): + """A simple window which displays a label and a button.""" + def __init__(self, handle): + super(Exercise19Activity, self).__init__(handle) + + # Create the standard activity toolbox. + toolbar_box = ToolbarBox() + self.set_toolbar_box(toolbar_box) + toolbar_box.show() + + main_toolbar = toolbar_box.toolbar + + activity_toolbar_button = widgets.ActivityToolbarButton(self) + main_toolbar.insert(activity_toolbar_button, 0) + activity_toolbar_button.show() + + stop_button = widgets.StopButton(self) + stop_button.show() + main_toolbar.insert(stop_button, -1) + + # Add a box to the window. + box = Gtk.Box() + box.set_orientation(Gtk.Orientation.VERTICAL) + box.set_spacing(8) + box.set_border_width(8) + box.show() + self.set_canvas(box) + + # Set up a list of text strings to display in the label. + self._strings = ( + _('Initial string.'), + _('Second string.'), + _('Handle object is ‘%s’.') % handle, + _('Another string.'), + ) + self._next_string = 0 + + # Create and set up the label and button. + label = Gtk.Label() + label.show() + box.pack_start(label, True, True, 0) + + button = Gtk.Button(_('Change Label')) + button.show() + box.pack_start(button, False, True, 0) + + # Connect the button callback. + button.connect('clicked', self.__button_clicked_cb) + + # Store the label as an object variable so it can be accessed from the + # button callback, and then set the initial text for the label. + self._label = label + self.__rotate_label_text() + + def __button_clicked_cb(self, button): + """Callback to change the label text when the button is clicked.""" + self.__rotate_label_text() + + def __rotate_label_text(self): + """Change the label to display the next string in the rotation. + + The modulus operation is used to wrap self._next_string around when + it reaches the final index in the self._strings array. + """ + self._label.set_text(self._strings[self._next_string]) + self._next_string = (self._next_string + 1) % len(self._strings) diff --git a/exercises/en/Exercise19.activity/setup.py b/exercises/en/Exercise19.activity/setup.py new file mode 100755 index 0000000..77fda74 --- /dev/null +++ b/exercises/en/Exercise19.activity/setup.py @@ -0,0 +1,4 @@ +#!/usr/bin/env python +from sugar.activity import bundlebuilder +if __name__ == "__main__": + bundlebuilder.start() diff --git a/exercises/en/Exercise20.activity/.gitignore b/exercises/en/Exercise20.activity/.gitignore new file mode 100644 index 0000000..6a592c9 --- /dev/null +++ b/exercises/en/Exercise20.activity/.gitignore @@ -0,0 +1,3 @@ +*.pyc +dist/ +locale/ diff --git a/exercises/en/Exercise20.activity/activity/activity.info b/exercises/en/Exercise20.activity/activity/activity.info new file mode 100644 index 0000000..d258ee6 --- /dev/null +++ b/exercises/en/Exercise20.activity/activity/activity.info @@ -0,0 +1,9 @@ +[Activity] +name = Exercise 20 +summary = Python exercise 20. Not a real activity. +activity_version = 1 +host_version = 1 +bundle_id = hn.educatrachos.exercise20 +icon = activity +exec = sugar-activity exercise20.Exercise20Activity +license = GPLv2+ diff --git a/exercises/en/Exercise20.activity/exercise20.py b/exercises/en/Exercise20.activity/exercise20.py new file mode 100644 index 0000000..766f453 --- /dev/null +++ b/exercises/en/Exercise20.activity/exercise20.py @@ -0,0 +1,76 @@ +# coding=utf-8 + +"""Package a Sugar activity to produce a bundle. + +Below is a simple Sugar activity which displays a static image. It is very +boring, but does not need changing. In this exercise, you must add the relevant +files to git and then build a bundle for the activity and install it on a +physical XO laptop. + +Before packaging the activity, an icon needs to be created. + + 1. Open Inkscape and create a new 48×48 pixel icon (‘icon_48x48’ template). + 2. Draw the icon, using only a single stroke colour and a single background + colour. Save it as activity/activity.svg. + 3. Open the created SVG file with a text editor and replace the stroke colours + as described in the ‘Create An Icon’ here: + http://en.flossmanuals.net/make-your-own-sugar-activities/package-the-activity/ + 4. Execute the following commands in a terminal open in the + Exercise20.activity/ directory. They will add the relevant files to git: + git init + git status + git add .gitignore exercise20.py setup.py images/logonew2.png + git add activity/activity.info + git add activity/activity.svg + git status + git commit + They create a new git repository in the current directory, check its + status, add the required files to the repository, check its status again + to ensure we don’t commit anything we don’t mean to, then commit the new + files and changes to the repository. + + The .gitignore file is a special file which lists files that git shouldn’t + allow you to add to the repository, and which will be hidden from the + output of `git status`. + 5. Run ‘./setup.py dist_xo’ to build an XO bundle. + 6. Copy the dist/Exercise20-1.xo file to a USB stick and install it on a + physical XO laptop. +""" + +from gi.repository import Gtk +from sugar3.activity import activity, widgets +from sugar3.graphics.toolbarbox import ToolbarBox +import os + + +class Exercise20Activity(activity.Activity): + """A simple window which displays a static image.""" + def __init__(self, handle): + super(Exercise20Activity, self).__init__(handle) + + # Create the standard activity toolbox. + toolbar_box = ToolbarBox() + self.set_toolbar_box(toolbar_box) + toolbar_box.show() + + main_toolbar = toolbar_box.toolbar + + activity_toolbar_button = widgets.ActivityToolbarButton(self) + main_toolbar.insert(activity_toolbar_button, 0) + activity_toolbar_button.show() + + stop_button = widgets.StopButton(self) + stop_button.show() + main_toolbar.insert(stop_button, -1) + + # Add an image to the window. + box = Gtk.Box() + box.show() + self.set_canvas(box) + + image = Gtk.Image() + filename = os.path.join(os.path.dirname(__file__), + 'images', 'logonew2.png') + image.set_from_file(filename) + image.show() + box.pack_start(image, True, True, 8) diff --git a/exercises/en/Exercise20.activity/images/logonew2.png b/exercises/en/Exercise20.activity/images/logonew2.png new file mode 100644 index 0000000..d523b0b --- /dev/null +++ b/exercises/en/Exercise20.activity/images/logonew2.png Binary files differ diff --git a/exercises/en/Exercise20.activity/setup.py b/exercises/en/Exercise20.activity/setup.py new file mode 100755 index 0000000..77fda74 --- /dev/null +++ b/exercises/en/Exercise20.activity/setup.py @@ -0,0 +1,4 @@ +#!/usr/bin/env python +from sugar.activity import bundlebuilder +if __name__ == "__main__": + bundlebuilder.start() -- cgit v0.9.1