diff options
author | Walter Bender <walter@sugarlabs.org> | 2010-04-02 16:27:20 (GMT) |
---|---|---|
committer | Walter Bender <walter@sugarlabs.org> | 2010-04-02 16:27:20 (GMT) |
commit | 8162f477074c485f417ffc7e5b2eb0f1022d3068 (patch) | |
tree | af0ee443b1537019e5743cf40aa70c93ca80b0ae /abacus_window.py |
New project
Diffstat (limited to 'abacus_window.py')
-rw-r--r-- | abacus_window.py | 507 |
1 files changed, 507 insertions, 0 deletions
diff --git a/abacus_window.py b/abacus_window.py new file mode 100644 index 0000000..32aa053 --- /dev/null +++ b/abacus_window.py @@ -0,0 +1,507 @@ +# -*- coding: utf-8 -*- +#Copyright (c) 2010, Walter Bender + +# 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 +# the Free Software Foundation; either version 3 of the License, or +# (at your option) any later version. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the +# Free Software Foundation, Inc., 59 Temple Place - Suite 330, +# Boston, MA 02111-1307, USA. + +from constants import * + +import pygtk +pygtk.require('2.0') +import gtk +from gettext import gettext as _ +import math +import os + +try: + from sugar.graphics import style + GRID_CELL_SIZE = style.GRID_CELL_SIZE +except: + GRID_CELL_SIZE = 0 + +from sprites import * + +def load_image(path, name, w, h): + """ create a pixbuf from a SVG stored in a file """ + return gtk.gdk.pixbuf_new_from_file_at_size( + os.path.join(path+name+'.svg'), int(w), int(h)) + +class Abacus(): + + def __init__(self, canvas, path, parent=None): + """ Abacus class """ + self.path = path + self.activity = parent + + if parent is None: # Starting from command line + self.sugar = False + self.canvas = canvas + else: # Starting from Sugar + self.sugar = True + self.canvas = canvas + parent.show_all() + + self.canvas.set_flags(gtk.CAN_FOCUS) + self.canvas.add_events(gtk.gdk.BUTTON_PRESS_MASK) + self.canvas.add_events(gtk.gdk.BUTTON_RELEASE_MASK) + self.canvas.add_events(gtk.gdk.POINTER_MOTION_MASK) + self.canvas.connect("expose-event", self._expose_cb) + self.canvas.connect("button-press-event", self._button_press_cb) + self.canvas.connect("button-release-event", self._button_release_cb) + self.canvas.connect("motion-notify-event", self._mouse_move_cb) + self.width = gtk.gdk.screen_width() + self.height = gtk.gdk.screen_height()-GRID_CELL_SIZE + self.sprites = Sprites(self.canvas) + self.scale = gtk.gdk.screen_height()/900.0 + self.dragpos = 0 + self.press = None + + self.chinese = Suanpan(self) + self.japanese = Soroban(self) + self.russian = Schety(self) + + self.chinese.show() + self.japanese.hide() + self.russian.hide() + self.mode = self.chinese + + def _button_press_cb(self, win, event): + win.grab_focus() + x, y = map(int, event.get_coords()) + self.dragpos = y + self.press = self.sprites.find_sprite((x,y)) + if self.press is not None and self.press.type != 'bead': + self.press = None + return True + + def _mouse_move_cb(self, win, event): + if self.press is None: + self.dragpos = 0 + return True + win.grab_focus() + x, y = map(int, event.get_coords()) + dy = y-self.dragpos + self.mode.move_bead(self.press, dy) + + def _button_release_cb(self, win, event): + if self.press == None: + return True + self.press = None + self.mode.label(self.mode.value()) + return True + + def _expose_cb(self, win, event): + self.sprites.redraw_sprites() + return True + + def _destroy_cb(self, win, event): + gtk.main_quit() + + +class Suanpan(): + + def __init__(self, abacus): + """ create a Chinese abacus: 15 by (5,2) """ + self.abacus = abacus + self.num_rods = 15 + self.bot_beads = 5 + self.top_beads = 2 + # 5 beads + 2 spaces, divider, 2 bead + 2 spaces + h = (self.bot_beads+2+1+self.top_beads+2)*BHEIGHT*self.abacus.scale + w = (self.num_rods+1)*(BWIDTH+BOFFSET)*self.abacus.scale + dy = 4*BHEIGHT*self.abacus.scale + x = (self.abacus.width-w)/2 + y = (self.abacus.height-h)/2 + + # Draw the rods... + self.rods = [] + o = (BWIDTH+BOFFSET-5)*self.abacus.scale/2 + dx = (BWIDTH+BOFFSET)*self.abacus.scale + for i in range(self.num_rods): + self.rods.append(Sprite(self.abacus.sprites, x+i*dx+o, y, + load_image(self.abacus.path, "chinese_rod", + 10*self.abacus.scale, h))) + + for i in self.rods: + i.type = 'rod' + + # and then the beads. + self.beads = [] + o = (BWIDTH-BOFFSET)/2*self.abacus.scale/2 + for i in range(self.num_rods): + for b in range(self.top_beads): + self.beads.append(Sprite(self.abacus.sprites, x+i*dx+o, + y+b*BHEIGHT*self.abacus.scale, + load_image(self.abacus.path, "white", + BWIDTH*self.abacus.scale, + BHEIGHT*self.abacus.scale))) + for b in range(self.bot_beads): + self.beads.append(Sprite(self.abacus.sprites, x+i*dx+o, + y+(7+b)*BHEIGHT*self.abacus.scale, + load_image(self.abacus.path, "white", + BWIDTH*self.abacus.scale, + BHEIGHT*self.abacus.scale))) + + for i in self.beads: + i.type = 'bead' + i.state = 0 + + # Draw the dividing bar on top + self.bar = Sprite(self.abacus.sprites, x, y+dy, + load_image(self.abacus.path, "divider_bar", + w, BHEIGHT*self.abacus.scale)) + + self.bar.type = 'frame' + self.bar.set_label_color('white') + + def value(self): + """ Return a string representing the value of each rod """ + string = '' + v = [] + for r in range(self.num_rods+1): # +1 for overflow + v.append(0) + + # Tally the values on each rod. + for i, b in enumerate(self.beads): + r = i/(self.top_beads+self.bot_beads) + j = i%(self.top_beads+self.bot_beads) + if b.state == 1: + if j < self.top_beads: + v[r+1] += 5 + else: + v[r+1] += 1 + + # Carry to the left if a rod has a value > 9. + for j in range(self.num_rods): + if v[len(v)-j-1] > 9: + v[len(v)-j-1] -= 10 + v[len(v)-j-2] += 1 + + # Convert values to a string. + for j in v: + if string != '' or j > 0: + string += str(j) + return(string) + + def hide(self): + for i in self.rods: + i.hide() + for i in self.beads: + i.hide() + self.bar.hide() + + def show(self): + for i in self.rods: + i.set_layer(100) + for i in self.beads: + i.set_layer(101) + self.bar.set_layer(102) + + def label(self, string): + """ Label the crossbar with the string. (USed with self.value) """ + self.bar.set_label(string) + + def move_bead(self, bead, dy): + """ Move a bead (or beads) up or down a rod. """ + i = self.beads.index(bead) + b = i%(self.top_beads+self.bot_beads) + + if b < self.top_beads: + if dy > 0 and bead.state == 0: + bead.move_relative((0, 2*BHEIGHT*self.abacus.scale)) + bead.state = 1 + # make sure beads below this bead are also moved + if b == 0 and self.beads[i+1].state == 0: + self.beads[i+1].move_relative((0, + 2*BHEIGHT*self.abacus.scale)) + self.beads[i+1].state = 1 + elif dy < 0 and bead.state == 1: + bead.move_relative((0, -2*BHEIGHT*self.abacus.scale)) + # make sure beads above this bead are also moved + if b == 1 and self.beads[i-1].state == 1: + self.beads[i-1].move_relative((0, + -2*BHEIGHT*self.abacus.scale)) + self.beads[i-1].state = 0 + bead.state = 0 + else: + if dy < 0 and bead.state == 0: + bead.move_relative((0, -2*BHEIGHT*self.abacus.scale)) + bead.state = 1 + # make sure beads above this bead are also moved + for ii in range(b-self.top_beads+1): + if self.beads[i-ii].state == 0: + self.beads[i-ii].move_relative((0, + -2*BHEIGHT*self.abacus.scale)) + self.beads[i-ii].state = 1 + elif dy > 0 and bead.state == 1: + bead.move_relative((0, 2*BHEIGHT*self.abacus.scale)) + bead.state = 0 + # make sure beads below this bead are also moved + for ii in range(self.top_beads+self.bot_beads-b): + if self.beads[i+ii].state == 1: + self.beads[i+ii].move_relative((0, + 2*BHEIGHT*self.abacus.scale)) + self.beads[i+ii].state = 0 + + +class Soroban(): + + def __init__(self, abacus): + """ create a Japanese abacus: 15 by (4,1) """ + self.abacus = abacus + self.num_rods = 15 + self.bot_beads = 4 + self.top_beads = 1 + # 4 beads + 2 spaces, divider, 1 bead + 2 spaces + h = (self.bot_beads+2+1+self.top_beads+2)*BHEIGHT*self.abacus.scale + w = (self.num_rods+1)*(BWIDTH+BOFFSET)*self.abacus.scale + dy = 3*BHEIGHT*self.abacus.scale + x = (self.abacus.width-w)/2 + y = (self.abacus.height-h)/2 + + # Draw the rods... + self.rods = [] + o = (BWIDTH+BOFFSET-5)*self.abacus.scale/2 + dx = (BWIDTH+BOFFSET)*self.abacus.scale + for i in range(self.num_rods): + self.rods.append(Sprite(self.abacus.sprites, x+i*dx+o, y, + load_image(self.abacus.path, "japanese_rod", + 10, h))) + + for i in self.rods: + i.type = 'rod' + + # and then the beads. + self.beads = [] + o = (BWIDTH-BOFFSET)/2*self.abacus.scale/2 + for i in range(self.num_rods): + for b in range(self.top_beads): + self.beads.append(Sprite(self.abacus.sprites, x+i*dx+o, + y+b*BHEIGHT*self.abacus.scale, + load_image(self.abacus.path, "white", + BWIDTH*self.abacus.scale, + BHEIGHT*self.abacus.scale))) + for b in range(self.bot_beads): + self.beads.append(Sprite(self.abacus.sprites, x+i*dx+o, + y+(6+b)*BHEIGHT*self.abacus.scale, + load_image(self.abacus.path, "white", + BWIDTH*self.abacus.scale, + BHEIGHT*self.abacus.scale))) + + for i in self.beads: + i.type = 'bead' + i.state = 0 + + # Draw the dividing bar on top + self.bar = Sprite(self.abacus.sprites, x, y+dy, + load_image(self.abacus.path, "divider_bar", + w, BHEIGHT*self.abacus.scale)) + + self.bar.type = 'frame' + self.bar.set_label_color('white') + + def hide(self): + for i in self.rods: + i.hide() + for i in self.beads: + i.hide() + self.bar.hide() + + def show(self): + for i in self.rods: + i.set_layer(100) + for i in self.beads: + i.set_layer(101) + self.bar.set_layer(102) + + def value(self): + """ Return a string with the value of each rod. """ + string = '' + v = 0 + for i, b in enumerate(self.beads): + if b.state == 1: + if i%(self.top_beads+self.bot_beads) < self.top_beads: + v += 5 + else: + v += 1 + if i%(self.top_beads+self.bot_beads) == \ + self.top_beads+self.bot_beads-1: + if string != '' or v > 0: + string += str(v) + v = 0 + return(string) + + def label(self, string): + """ Label the crossbar with the string. (USed with self.value) """ + self.bar.set_label(string) + + def move_bead(self, bead, dy): + """ Move a bead (or beads) up or down a rod. """ + i = self.beads.index(bead) + b = i%(self.top_beads+self.bot_beads) + + if b < self.top_beads: + if dy > 0 and bead.state == 0: + bead.move_relative((0, 2*BHEIGHT*self.abacus.scale)) + bead.state = 1 + elif dy < 0 and bead.state == 1: + bead.move_relative((0, -2*BHEIGHT*self.abacus.scale)) + bead.state = 0 + else: + if dy < 0 and bead.state == 0: + bead.move_relative((0, -2*BHEIGHT*self.abacus.scale)) + bead.state = 1 + # make sure beads above this bead are also moved + for ii in range(b-self.top_beads+1): + if self.beads[i-ii].state == 0: + self.beads[i-ii].move_relative((0, + -2*BHEIGHT*self.abacus.scale)) + self.beads[i-ii].state = 1 + elif dy > 0 and bead.state == 1: + bead.move_relative((0, 2*BHEIGHT*self.abacus.scale)) + bead.state = 0 + # make sure beads below this bead are also moved + for ii in range(self.top_beads+self.bot_beads-b): + if self.beads[i+ii].state == 1: + self.beads[i+ii].move_relative((0, + 2*BHEIGHT*self.abacus.scale)) + self.beads[i+ii].state = 0 + + +class Schety(): + + def __init__(self, abacus): + """ create a Russian abacus: 15 by 10 """ + self.abacus = abacus + self.num_rods = 15 + self.bot_beads = 10 + # 10 beads + 2 spaces + h = (self.bot_beads+2)*BHEIGHT*self.abacus.scale + w = (self.num_rods+1)*(BWIDTH+BOFFSET)*self.abacus.scale + x = (self.abacus.width-w)/2 + y = (self.abacus.height-h)/2 + + # Draw the rods... + self.rods = [] + o = (BWIDTH+BOFFSET-5)*self.abacus.scale/2 + dx = (BWIDTH+BOFFSET)*self.abacus.scale + for i in range(self.num_rods): + self.rods.append(Sprite(self.abacus.sprites, x+i*dx+o, y, + load_image(self.abacus.path, "russian_rod", + 10, h))) + + for i in self.rods: + i.type = 'rod' + + # and then the beads. + self.beads = [] + o = (BWIDTH-BOFFSET)/2*self.abacus.scale/2 + for i in range(self.num_rods): + if i == 10: + for b in range(4): + if b in [1,2]: + color = 'black' + else: + color = 'white' + self.beads.append(Sprite(self.abacus.sprites, x+i*dx+o, + y+(8+b)*BHEIGHT*self.abacus.scale, + load_image(self.abacus.path, color, + BWIDTH*self.abacus.scale, + BHEIGHT*self.abacus.scale))) + else: + for b in range(self.bot_beads): + if b in [4,5]: + color = 'black' + else: + color = 'white' + self.beads.append(Sprite(self.abacus.sprites, x+i*dx+o, + y+(2+b)*BHEIGHT*self.abacus.scale, + load_image(self.abacus.path, color, + BWIDTH*self.abacus.scale, + BHEIGHT*self.abacus.scale))) + + for i in self.beads: + i.type = 'bead' + i.state = 0 + + # Draw a bar for the label on top + self.bar = Sprite(self.abacus.sprites, x, y-BHEIGHT*self.abacus.scale, + load_image(self.abacus.path, "divider_bar", + w, BHEIGHT*self.abacus.scale)) + + self.bar.type = 'frame' + self.bar.set_label_color('white') + + def hide(self): + for i in self.rods: + i.hide() + for i in self.beads: + i.hide() + self.bar.hide() + + def show(self): + for i in self.rods: + i.set_layer(100) + for i in self.beads: + i.set_layer(101) + self.bar.set_layer(102) + + def value(self): + # TODO: cascade and account for short rod + """ Return a string with the value of each rod. """ + string = '' + v = 0 + for i, b in enumerate(self.beads): + if b.state == 1: + v += 1 + if i%self.bot_beads == self.bot_beads-1: + if string != '' or v > 0: + string += str(v) + v = 0 + return(string) + + def label(self, string): + """ Label the crossbar with the string. (USed with self.value) """ + self.bar.set_label(string) + + def move_bead(self, bead, dy): + # TODO: take into account short rod in rod calculations + i = self.beads.index(bead) + r = i/self.bot_beads + if r < 10: + o = 2 + b = i%self.bot_beads + n = self.bot_beads + elif r == 10: + o = 8 + b = i%self.bot_beads + n = 4 + else: + s = 2 + b = (i+6)%self.bot_beads + n = self.bot_beads + if dy < 0 and bead.state == 0: + bead.move_relative((0, -o*BHEIGHT*self.abacus.scale)) + bead.state = 1 + # make sure beads above this bead are also moved + for ii in range(b+1): + if self.beads[i-ii].state == 0: + self.beads[i-ii].move_relative((0, + -o*BHEIGHT*self.abacus.scale)) + self.beads[i-ii].state = 1 + elif dy > 0 and bead.state == 1: + bead.move_relative((0, o*BHEIGHT*self.abacus.scale)) + bead.state = 0 + # make sure beads below this bead are also moved + for ii in range(n-b): + if self.beads[i+ii].state == 1: + self.beads[i+ii].move_relative((0, + o*BHEIGHT*self.abacus.scale)) + self.beads[i+ii].state = 0 |