From ea01dc5606e3d3cbba90d30f080ed6cbef1d7d21 Mon Sep 17 00:00:00 2001 From: Ezequiel Pereira Date: Fri, 29 Jan 2016 17:34:52 +0000 Subject: Ported to Gtk 3 * Fixed start * Ported to Gtk 3 * This is a GCI 2015 task: https://codein.withgoogle.com/tasks/5743379048562688/ --- diff --git a/ConstellationsFlashCards.py b/ConstellationsFlashCards.py index dcb13eb..4a99f76 100644..100755 --- a/ConstellationsFlashCards.py +++ b/ConstellationsFlashCards.py @@ -1,7 +1,8 @@ # Constellations Flash-Cards # +# TODO: Add a "clear results" button to the "results" toolbar. +# # Copyright (c) 2010 by David A. Wallace -# Copyright (c) 2012 Aneesh Dogra # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -40,26 +41,55 @@ # # The members and officers of the Amateur Telescope Makers of Boston who encouraged # and educated me when I first discovered the wonder that is our planet's night sky. +# +# ------------------------------------------------------------------------------- +# +# INDEX +# +# (Line numbers given below are approximate, within 5 lines, usually.) +# +# Line No. Content +# -------- -------------------------------------- +# 140 Version and date displayed by "About" function. +# 170 Start of the code -- trig algorithms +# 185 Definition for the ChartDisplay (main GUI) class +# 200 Area_Expose callback (screen refresh) +# 225 Event callback for controls +# 285 convert (ra,dec) to (x,y) +# 330 Code to plot the constellation figure: +# 340 The outline and field +# 385 Plot the map +# 405 Plot the stars for a particular constellation +# 425 Plot a Constellation figure +# 450 Draw symbol of Star +# 460 Choose a constellation ID +# 480 Determine size of constellation +# 620 Fill the "names" combobox +# 660 Erase the screen +# 675 Definition for the ConstellationsFlashCards Activity class +# 685 Define the dictionary of constellation names and the array of +# constellation IDs +# 695 Establish the toolbars +# 780 Read configuration file and metadata +# 820 Write configuration file and metadata +# 840 Update configuration file +# # =================================== IMPORTS =================================== from gi.repository import Gtk from gi.repository import Gdk +from gi.repository import PangoCairo import sys import os from math import sin, cos, tan, asin, acos, atan, pi, sqrt, atan2 import random -import cairo from sugar3.activity import activity -from sugar3.graphics.style import Color -from sugar3.activity.widgets import ActivityToolbarButton -from sugar3.activity.widgets import StopButton -from sugar3.graphics.toolbutton import ToolButton from sugar3.activity.activity import get_bundle_path -from sugar3.graphics.toolbarbox import ToolbarButton from sugar3.graphics.toolbarbox import ToolbarBox -from sugar3.graphics.toolbutton import ToolButton +from sugar3.activity.widgets import StopButton +from sugar3.activity.widgets import ActivityToolbarButton import logging from gettext import gettext @@ -95,16 +125,16 @@ figures = constellations.data # ------------------------------------------------------------------------------- # # controls on second menubar ("Quiz"): -labelq1 = Gtk.Label(_("Name")) -cbq1 = Gtk.ComboBoxText() -buttonq1 = ToolButton('ConstellationQuestion') -buttonq1.set_tooltip(_("Tell me")) -buttonq2 = ToolButton('ConstellationNext') -buttonq2.set_tooltip(_("Another")) - -labelr1 = Gtk.Label(_(" constellations seen. ")) -labelr3 = Gtk.Label(_(" correct on first try. ")) -labelr4 = Gtk.Label(_(" correct on second try.")) +labelq1 = Gtk.Label(_("Name: ")) +model = Gtk.ListStore(str) +cbq1 = Gtk.ComboBox.new_with_model(model) +renderer_text = Gtk.CellRendererText() +cbq1.pack_start(renderer_text, True) +cbq1.add_attribute(renderer_text, "text", 0) +buttonq1 = Gtk.Button(_("Tell me")) +buttonq2 = Gtk.Button(_("Another")) +# controls on third menubar ("Results"): +labelr1 = Gtk.Label(_(" constellations seen.")) name_from_abbrev = {} constellations = [] @@ -122,7 +152,6 @@ constellations = [] # a session starts, we multiply the point score for each constellation by 0.8 as it is # read in. # -colors = ['#FFFFFF', '#000000', '#CCCCCC'] score = {} seen = [] session_count = 1 @@ -150,11 +179,10 @@ class ChartDisplay(Gtk.DrawingArea): def __init__(self, context): super(ChartDisplay, self).__init__() self.context = context + self.colors = {} self.canplot = False - self.pangolayout = self.create_pango_layout("") - self.add_events(Gdk.EventMask.BUTTON_PRESS_MASK | \ - Gdk.ModifierType.BUTTON1_MASK | \ - Gdk.ModifierType.BUTTON2_MASK) + self.add_events(Gdk.EventMask.BUTTON_PRESS_MASK | + Gdk.EventMask.BUTTON1_MOTION_MASK | Gdk.EventMask.BUTTON2_MOTION_MASK) self.id = "" self.cname = "" self.points = 5 @@ -173,9 +201,23 @@ class ChartDisplay(Gtk.DrawingArea): 2 * self.margin self.xoffset = (self.screensize[0] - self.diameter) / 2 - self.margin self.yoffset = (self.screensize[1] - self.diameter) / 2 - self.margin - self.ctx = self.get_window().cairo_create() - self.canplot = True - self.plotchart(False) + + self.gc = self.get_window().cairo_create() + self.pangolayout = PangoCairo.create_layout(self.gc) + +# Establish color selections (need only do this once). + + if (len(self.colors) == 0): + self.colors[0] = Gdk.Color.parse('white')[1] + self.colors[1] = Gdk.Color.parse('black')[1] + self.colors[2] = Gdk.Color.parse('red')[1] + self.colors[3] = Gdk.Color.parse('gray')[1] + self.colors[4] = Gdk.Color.parse('green')[1] + self.canplot = True + self.plotchart(True) + else: + self.plotchart(False) + def callback(self, widget, data=None): global score @@ -188,32 +230,31 @@ class ChartDisplay(Gtk.DrawingArea): if (data == None): pass - elif (data == "tell_me"): self.context.identifyobject.set_label(_("This constellation is named ") + \ self.cname) - labelr1.set_label(str(quiz_count) + _(" constellations seen. ")) - labelr3.set_label(str(correct_first_count) + _(" correct on first try. ")) - labelr4.set_label(str(correct_second_count) + _(" correct on second try.")) + labelr1.set_label(str(quiz_count) + _(" constellations seen. ") + + str(correct_first_count) + _(" correct on first try. ") + + str(correct_second_count) + _(" correct on second try.")) cbq1.set_sensitive(False) buttonq1.set_sensitive(False) - elif (data == "another"): self.context.identifyobject.set_label("") cbq1.set_sensitive(True) buttonq1.set_sensitive(True) quiz_count = quiz_count + 1 - labelr1.set_label(str(quiz_count) + _(" constellations seen. ")) - self.context.answer_status.set_text(_("")) - labelr3.set_label(str(correct_first_count) + _(" correct on first try. ")) - labelr4.set_label(str(correct_second_count) + _(" correct on second try.")) + labelr1.set_label(str(quiz_count) + _(" constellations seen. ") + + str(correct_first_count) + _(" correct on first try. ") + + str(correct_second_count) + _(" correct on second try.")) self.plotchart(True) - + self.queue_draw() elif (data == "select_name"): if (cbq1.get_active() >= 0): - name = cbq1.get_active_text() + tree_iter = cbq1.get_active_iter() + model = cbq1.get_model() + name = model[tree_iter][0] if (name == self.cname): - self.context.answer_status.set_text(_("That is correct.")) + self.context.identifyobject.set_label(_("That is correct.")) id = self.id score[id] = score[id] + self.points if (self.points == 5): @@ -225,31 +266,18 @@ class ChartDisplay(Gtk.DrawingArea): cbq1.set_sensitive(False) buttonq1.set_sensitive(False) else: - self.context.answer_status.set_text(_("Sorry, that is not the correct name.")) + self.context.identifyobject.set_label(_("Sorry, that is not the correct name.")) self.points = self.points - 2 if (self.points < 0): self.points = 0 - labelr1.set_label(str(quiz_count) + _(" constellations seen. ")) - labelr3.set_label(str(correct_first_count) + _(" correct on first try. ")) - labelr4.set_label(str(correct_second_count) + _(" correct on second try.")) - - elif data == 'reset_results': - score = 0 - seen = 0 - quiz_count = 0 - correct_first_count = 0 - correct_second_count = 0 - # Let's update the results now - labelr1.set_label(str(quiz_count) + _(" constellations seen. ")) - labelr3.set_label(str(correct_first_count) + _(" correct on first try. ")) - labelr4.set_label(str(correct_second_count) + _(" correct on second try.")) - self.context.write_file(self.context.datafile) - + labelr1.set_label(str(quiz_count) + _(" constellations seen. ") + + str(correct_first_count) + _(" correct on first try. ") + + str(correct_second_count) + _(" correct on second try.")) else: pass - return False + # Convert equatorial coordinates to pixel position(x, y) in normalized form on a square # of self.diameter pixels. RA is in hours; dec is in degrees. @@ -299,54 +327,55 @@ class ChartDisplay(Gtk.DrawingArea): # Methods for drawing the chart: def plotchart(self, newplot): - if self.canplot: - self.plot_field() - self.plot_sky(newplot) + if (self.canplot): + self.plot_field() + self.plot_sky(newplot) return True - def plot_field(self): -# Erase prior plot + def plot_field(self): if (not self.canplot): return - self.cleararea() - color_rgb = Color(colors[0]).get_rgba()[:-1] # we don't need alpha - self.ctx.set_source_rgb(*color_rgb) - self.ctx.rectangle(self.xoffset + self.margin - 2, - self.yoffset + self.margin - 2, - self.diameter + 4, - self.diameter + 4) - self.ctx.fill() + # Plot sky square - color_rgb = Color(colors[1]).get_rgba()[:-1] # we don't need alpha - self.ctx.set_source_rgb(*color_rgb) - self.ctx.rectangle(self.xoffset + self.margin - 2, - self.yoffset + self.margin - 2, - self.diameter + 4, - self.diameter + 4) - self.ctx.stroke() + + self.cleararea() + self.gc.set_source_rgb((1/65536.0)*self.colors[0].red, + (1/65536.0)*self.colors[0].green, + (1/65536.0)*self.colors[0].blue) + self.gc.rectangle(self.xoffset + self.margin - 2, + self.yoffset + self.margin - 2, self.diameter + 4, self.diameter + 4) + self.gc.fill() # label the cardinal points. - self.ctx.move_to(self.xoffset + self.margin + self.diameter / 2 - 10, - self.margin - 30) - self.ctx.show_text(_("N")) - - self.ctx.move_to(self.xoffset + self.margin + self.diameter / 2 - 10, - 2 * self.margin + self.diameter - 20) - self.ctx.show_text(_("S")) - - self.ctx.move_to(self.xoffset + self.margin - 30, - self.margin + self.diameter / 2 - 10) - self.ctx.show_text(_("E")) - self.ctx.move_to(self.xoffset + self.margin + self.diameter + 10, - self.margin + self.diameter / 2 - 10) - self.ctx.show_text(_("W")) - color_rgb = Color(colors[1]).get_rgba()[:-1] # we don't need alpha - self.ctx.set_source_rgb(*color_rgb) + self.gc.set_source_rgb((1/65536.0)*self.colors[1].red, + (1/65536.0)*self.colors[1].green, + (1/65536.0)*self.colors[1].blue) + self.pangolayout.set_text(_("N"), -1) + self.gc.move_to(self.xoffset + self.margin + self.diameter / 2 - 10, + self.margin - 30) + PangoCairo.show_layout(self.gc, self.pangolayout) + self.gc.stroke() + self.pangolayout.set_text(_("S"), -1) + self.gc.move_to(self.xoffset + self.margin + self.diameter / 2 - 10, + 2 * self.margin + self.diameter - 30) + PangoCairo.show_layout(self.gc, self.pangolayout) + self.gc.stroke() + self.pangolayout.set_text(_("E"), -1) + self.gc.move_to(self.xoffset + self.margin - 30, + self.margin + self.diameter / 2 - 10) + PangoCairo.show_layout(self.gc, self.pangolayout) + self.gc.stroke() + self.pangolayout.set_text(_("W"), -1) + self.gc.move_to(self.xoffset + self.margin + self.diameter + 10, + self.margin + self.diameter / 2 - 10) + PangoCairo.show_layout(self.gc, self.pangolayout) + self.gc.stroke() return True + def plot_sky(self, choose): if (choose): self.cnumber = random.randrange(len(constellations)) @@ -361,6 +390,7 @@ class ChartDisplay(Gtk.DrawingArea): if (choose): self.fill_names_combobox() + def plot_stars(self, id): # Plot the stars. @@ -376,7 +406,7 @@ class ChartDisplay(Gtk.DrawingArea): (px, py) = self.radectoxy((ra, dec)) px = px + self.diameter / 2.0 py = py + self.diameter / 2.0 - starsize = (4 + 2 * int(7.0 - mag))/3 + starsize = 4 + 2 * int(7.0 - mag) px = px + self.margin - 2 + self.xoffset - starsize / 2 py = py + self.margin - 2 + self.yoffset - starsize / 2 if (mag <= 6.0): @@ -403,15 +433,20 @@ class ChartDisplay(Gtk.DrawingArea): py2 = py2 + self.diameter / 2.0 px2 = px2 + self.margin - 2 + self.xoffset py2 = py2 + self.margin - 2 + self.yoffset - line_ctx = self.get_window().cairo_create() - line_ctx.move_to(px1, py1) - line_ctx.line_to(px2, py2) - line_ctx.stroke() + self.gc.set_source_rgb((1/65536.0)*self.colors[1].red, + (1/65536.0)*self.colors[1].green, + (1/65536.0)*self.colors[1].blue) + self.gc.move_to(int(px1), int(py1)) + self.gc.line_to(int(px2), int(py2)) + self.gc.stroke() + def plot_star(self, px, py, starsize): - self.ctx = self.get_window().cairo_create() - self.ctx.arc(px, py, starsize, 0, 360*64) - self.ctx.fill() + self.gc.save() + self.gc.arc(int(px), int(py), starsize, 0, 2 * pi) + self.gc.fill() + self.gc.restore() + def pick_constellation(self): global seen @@ -571,59 +606,50 @@ class ChartDisplay(Gtk.DrawingArea): def fill_names_combobox(self): - global correct_first_count - cbq1.remove_all() +# Create a list of five names, initialized to "" - NUMBER_OF_CHOICES = 4 -# Decide the NUMBER_OF_CHOICES - if correct_first_count > 0: - NUMBER_OF_CHOICES = int(10 * float(correct_first_count / \ - len(constellations))) - if NUMBER_OF_CHOICES < 4: - NUMBER_OF_CHOICES = 4 - -# Create a list of NUMBER_OF_CHOICES names, initialized to "" - names = [""] * NUMBER_OF_CHOICES - numbers = [-1] * NUMBER_OF_CHOICES + names = ["", "", "", "", ""] + numbers = [-1, -1, -1, -1, -1] + cbq1.get_model().clear() # Now set one of these names to self.cname. - k = random.randrange(NUMBER_OF_CHOICES) + k = random.randrange(5) names[k] = self.cname numbers[k] = self.cnumber -# Choose len(names) - 1 additional constellation names (by random choice). -# Add these names to the list, being sure not to overwrite any non-blank value -# or use the same name twice. +# Choose four additional constellation names (by random choice). Add these names to the +# list, being sure not to overwrite any non-blank value or use the same name twice. i = 0 - for i in range(NUMBER_OF_CHOICES): + while (i < 4): r = random.randrange(len(constellations)) if not (r in numbers): id = constellations[r] cname = name_from_abbrev[id] - for j in range(NUMBER_OF_CHOICES): + for j in range(5): if (names[j] == ""): names[j] = cname numbers[j] = r i = i + 1 break + +# Fill cbq1 with the five strings. + + for i in range(5): + cbq1.get_model().append([names[i]]) - for i in range(NUMBER_OF_CHOICES): - cbq1.append_text(names[i]) def cleararea(self): + # Clear the drawing surface - color_rgb = Color(colors[2]).get_rgba()[:-1] # we don't need alpha - self.ctx = self.get_window().cairo_create() - self.ctx.set_source_rgb(*color_rgb) - self.ctx.rectangle(0, - 0, - self.screensize[0], - self.screensize[1]) - self.ctx.fill() + self.gc.set_source_rgb((1/65536.0)*self.colors[3].red, + (1/65536.0)*self.colors[3].green, + (1/65536.0)*self.colors[3].blue) + self.gc.rectangle(1, 1, self.screensize[0], self.screensize[1]) + self.gc.fill() # ========================= ConstellationsFlashCards Object ========================== @@ -637,7 +663,7 @@ class ConstellationsFlashCards(activity.Activity): activity.Activity.__init__(self, handle) self.datafile = os.path.join(activity.get_activity_root(),\ "data", "C_FC.cfg") - self.chart = ChartDisplay(self) + # Build the translation from constellation name to constellation ID (needed so we can # have a list of names to choose from). At the same time, make an array of constellation # IDs so the randomizer can pick one. @@ -651,88 +677,47 @@ class ConstellationsFlashCards(activity.Activity): # Create toolbox - toolbar_box = ToolbarBox() - - # Activity Toolbar - activity_button = ActivityToolbarButton(self) - toolbar_box.toolbar.insert(activity_button, -1) - - # Quiz Toolbar - self.quiz_toolbar = Gtk.Toolbar() - label_container = Gtk.ToolItem() - label_container.add(labelq1) - label_container.show_all() - self.quiz_toolbar.insert(label_container, -1) - label_container = Gtk.ToolItem() - label_container.add(cbq1) - label_container.show_all() - self.quiz_toolbar.insert(label_container, -1) - self.quiz_toolbar.insert(buttonq1, -1) - self.quiz_toolbar.insert(buttonq2, -1) - buttonq1.show() - buttonq2.show() - - label_container = Gtk.ToolItem() - self.answer_status = Gtk.Label(_("")) - label_container.add(self.answer_status) - label_container.show_all() - self.quiz_toolbar.insert(label_container, -1) - self.answer_status.show() - - self.quiz_toolbar.show_all() - quiz_toolbar_button = ToolbarButton( - page=self.quiz_toolbar, - icon_name='ConstellationNewGame') - quiz_toolbar_button.show() - toolbar_box.toolbar.insert(quiz_toolbar_button, -1) - - # Reset Results - reset_button = ToolButton('system-restart') - reset_button.set_tooltip(_('Reset Results')) - reset_button.connect('clicked', self.chart.callback, 'reset_results') - toolbar_box.toolbar.insert(reset_button, -1) - reset_button.show() - - # Results - - hbox = Gtk.HBox() - hbox.pack_start(labelr1, False, True, 0) - hbox.pack_start(labelr3, False, True, 0) - hbox.pack_start(labelr4, False, True, 0) - hbox.show_all() - - label_container = Gtk.ToolItem() - label_container.add(hbox) - label_container.show_all() - toolbar_box.toolbar.insert(label_container, -1) + toolbox = ToolbarBox() + self.set_toolbar_box(toolbox) + + self.max_participants = 1 + + activity_button_toolbar = ActivityToolbarButton(self) + toolbox.toolbar.insert(activity_button_toolbar, 0) + activity_button_toolbar.show() separator = Gtk.SeparatorToolItem() separator.props.draw = False separator.set_expand(True) - toolbar_box.toolbar.insert(separator, -1) + toolbox.toolbar.insert(separator, -1) separator.show() - # The stop button stop_button = StopButton(self) - stop_button.props.accelerator = 'Q' - toolbar_box.toolbar.insert(stop_button, -1) + stop_button.props.accelerator = 'q' + toolbox.toolbar.insert(stop_button, -1) stop_button.show() - self.set_toolbar_box(toolbar_box) - toolbar_box.show_all() - quiz_toolbar_button.set_expanded(True) - # Create the GUI objects. scrolled = Gtk.ScrolledWindow() scrolled.set_policy(Gtk.PolicyType.NEVER, Gtk.PolicyType.AUTOMATIC) + scrolled.props.shadow_type = Gtk.ShadowType.NONE + self.chart = ChartDisplay(self) eb = Gtk.EventBox() - vbox = Gtk.VBox() + vbox = Gtk.VBox(False) self.identifyobject = Gtk.Label("") vbox.pack_start(self.identifyobject, False, True, 0) + hbox = Gtk.HBox(False) + hbox.pack_start(labelr1, True, True, 0) + vbox.pack_start(hbox, False, False, 0) + hbox = Gtk.HBox(False) + hbox.pack_start(labelq1, False, True, 0) + hbox.pack_start(cbq1, True, True, 0) + hbox.pack_start(buttonq1, True, True, 0) + hbox.pack_start(buttonq2, True, True, 0) + vbox.pack_start(hbox, False, False, 0) vbox.pack_start(self.chart, True, True, 0) - self.identifyobject.override_background_color(Gtk.StateType.NORMAL, - Gdk.RGBA(*Color(colors[2]).get_rgba())) + eb.modify_bg(Gtk.StateType.NORMAL, Gdk.color_parse("gray")) # Stack the GUI objects. @@ -753,6 +738,7 @@ class ConstellationsFlashCards(activity.Activity): # Show the GUI stack. + toolbox.show() self.chart.show() eb.show() scrolled.show() @@ -761,15 +747,14 @@ class ConstellationsFlashCards(activity.Activity): # If C_FC.cfg exists, get the previous scores. self.read_file(self.datafile) - labelr1.set_label(str(quiz_count) + _(" constellations seen. ")) - labelr3.set_label(str(correct_first_count) + _(" correct on first try. ")) - labelr4.set_label(str(correct_second_count) + _(" correct on second try.")) - + labelr1.set_label(str(quiz_count) + _(" constellations seen. ") + + str(correct_first_count) + _(" correct on first try. ") + + str(correct_second_count) + _(" correct on second try.")) # Establish initial state of controls and do a plot. - self.chart.area_expose_cb(None, None) # make sure we have the surface ready self.chart.plotchart(True) + def read_file(self, filename=""): global score global quiz_count @@ -812,7 +797,9 @@ class ConstellationsFlashCards(activity.Activity): def write_file(self, filename=""): - # Write the values for the scores of all constellations. +# Write the values for the scores of all constellations. + +# Note: currently not used! if (filename == ""): self.identifyobject.set_label("Write_file: no filename given.") diff --git a/setup.py b/setup.py index 69e1427..02a76eb 100755 --- a/setup.py +++ b/setup.py @@ -1,5 +1,5 @@ #!/usr/bin/env python from sugar3.activity import bundlebuilder if __name__ == "__main__": - bundlebuilder.start() + bundlebuilder.start("StarChart") -- cgit v0.9.1