# gcompris - searace # # Copyright (C) 2004, 2008 Bruno Coudoin # # 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. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, see . # import gobject import goocanvas import gcompris import gcompris.utils import gcompris.skin import gtk import gtk.gdk import random import math import time from gcompris import gcompris_gettext as _ class Boat: """The Boat Class""" # The Text View and Scrolled windows tb = None tv = None sw = None x = 0.0 y = 0.0 angle = 0 arrived = False won = False finish_time = 0 # To move the ship dx = 0.0 dy = 0.0 # The user commands parsing line = 0 # The boat item item = [] player = 0 # Store a timer object timer = 0 # Weather condition cache to avoid to find it each time condition = [] # Display the speed here speeditem = [] class Gcompris_searace: """The Boat Racing activity""" def __init__(self, gcomprisBoard): self.gcomprisBoard = gcomprisBoard self.board_paused = False # Some constants self.border_x = 30 self.sea_area = (self.border_x , 30, gcompris.BOARD_WIDTH-self.border_x , 350) self.weather = [] self.left_boat = Boat() self.left_boat.player = 0 self.right_boat = Boat() self.left_boat.player = 1 self.left_initial_boat_y = 150 self.right_initial_boat_y = 150 + 28 # The basic tick for object moves self.timerinc = 40 self.timer_turn = 15 self.timer1 = 0 self.timer2 = 0 # How to transform user visible sea size to pixels (calculated later) self.sea_ratio = 1 # We display what's going on here self.statusitem = [] def start(self): self.gcomprisBoard.level=1 self.gcomprisBoard.maxlevel=4 self.gcomprisBoard.sublevel=1 self.gcomprisBoard.number_of_sublevel=1 self.board_paused = False # Create our rootitem. We put each canvas item in it so at the end we # only have to kill it. The canvas deletes all the items it contains automaticaly. self.rootitem = goocanvas.Group(parent = self.gcomprisBoard.canvas.get_root_item()) gcompris.set_default_background(self.gcomprisBoard.canvas.get_root_item()) self.display_sea_area() self.root_weather_item = goocanvas.Group(parent = self.rootitem) # Display the weather now self.display_weather() # And finaly the players boats self.init_boats() gcompris.bar_set(gcompris.BAR_LEVEL|gcompris.BAR_REPEAT); # The OK Button item = goocanvas.Svg(parent = self.rootitem, svg_handle = gcompris.skin.svg_get(), svg_id = "#OK" ) zoom = 0.7 item.translate( (item.get_bounds().x1 * -1) + ( gcompris.BOARD_WIDTH / 2 + 25 ) / zoom, (item.get_bounds().y1 * -1) + (gcompris.BOARD_HEIGHT - 125) / zoom ) item.scale(zoom, zoom) item.connect("button_press_event", self.ok_event) gcompris.utils.item_focus_init(item, None) gcompris.bar_set_level(self.gcomprisBoard) gcompris.bar_location(gcompris.BOARD_WIDTH/2 - 90, -1, 0.6) def end(self): self.stop_race() # Remove the root item removes all the others inside it self.rootitem.remove() def pause(self, pause): self.board_paused = pause # There is a problem with GTK widgets, they are not covered by the help # We hide/show them here if(pause): self.left_boat.sw.hide() self.right_boat.sw.hide() else: self.left_boat.sw.show() self.right_boat.sw.show() self.repeat() return def ok(self): # This is a real go # We set a timer. At each tick an entry in each user box is read analysed and run if(not self.left_boat.timer and not self.right_boat.timer): self.left_boat.tv.set_editable(False) self.right_boat.tv.set_editable(False) self.race_one_command(self.left_boat) self.race_one_command(self.right_boat) else: self.statusitem.props.text = _("The race is already being run") # Called by gcompris when the user click on the level icon def set_level(self, level): self.stop_race() self.gcomprisBoard.level=level; self.gcomprisBoard.sublevel=1; # Set the level in the control bar gcompris.bar_set_level(self.gcomprisBoard); # Remove the root item removes all the others inside it self.root_weather_item.remove() self.root_weather_item = goocanvas.Group(parent = self.rootitem) # Display the weather now self.display_weather() self.init_boats() def repeat(self): # Want to rerun it self.stop_race() self.init_boats() def config(self): return def key_press(self, keyval, commit_str, preedit_str): return False # ---------------------------------------------------------------------- # ---------------------------------------------------------------------- # ---------------------------------------------------------------------- def stop_race(self): # Remove all the timers if self.timer1 : gobject.source_remove(self.timer1) self.timer1 = 0 if self.timer2 : gobject.source_remove(self.timer2) self.timer2 = 0 if self.left_boat.timer : gobject.source_remove(self.left_boat.timer) self.left_boat.timer = 0 if self.right_boat.timer : gobject.source_remove(self.right_boat.timer) self.right_boat.timer = 0 # Set the initial coordinates of the boats and display them def init_boats(self): self.left_boat.x = self.border_x self.left_boat.y = self.left_initial_boat_y self.right_boat.x = self.left_boat.x self.right_boat.y = self.right_initial_boat_y self.left_boat.angle = 0 self.right_boat.angle = 0 # Display the player boats if(self.left_boat.item): self.left_boat.item.remove() pixmap = gcompris.utils.load_pixmap("searace/top_boat_red.png") self.left_boat.item = goocanvas.Image(parent = self.rootitem, pixbuf = pixmap, x=self.left_boat.x, y=self.left_boat.y, ) self.left_boat.item.raise_(None) self.left_boat.item.connect("button_press_event", self.ruler_item_event) self.left_boat.item.connect("button_release_event", self.ruler_item_event) self.left_boat.item.connect("motion_notify_event", self.ruler_item_event) if(self.right_boat.item): self.right_boat.item.remove() pixmap = gcompris.utils.load_pixmap("searace/top_boat_green.png") self.right_boat.item = goocanvas.Image( parent = self.rootitem, pixbuf = pixmap, x=self.right_boat.x, y=self.right_boat.y, ) self.right_boat.item.raise_(None) self.right_boat.item.connect("button_press_event", self.ruler_item_event) self.right_boat.item.connect("button_release_event", self.ruler_item_event) self.right_boat.item.connect("motion_notify_event", self.ruler_item_event) # Reset command line processing as well. self.left_boat.line = 0 self.right_boat.line = 0 self.left_boat.arrived = False self.right_boat.arrived = False self.left_boat.won = False self.right_boat.won = False self.left_boat.speeditem.props.text = "" self.right_boat.speeditem.props.text = "" self.statusitem.props.text = "" # Let the user enter comands self.left_boat.tv.set_editable(True) if self.gcomprisBoard.mode == '1player': self.tux_move() else: self.right_boat.tv.set_editable(True) #---------------------------------------- # Display the whole playing field # This is called once only def display_sea_area(self): # Some constant to define the sea area # The sea area is defined in the global self.sea_area step_x = (self.sea_area[2] - self.sea_area[0])/20 step_y = (self.sea_area[3] - self.sea_area[1])/10 self.sea_ratio = step_x text_x = self.sea_area[0] - 15 text_y = self.sea_area[1] - 15 # We manage a 2 colors grid ci = 0 ca = 0xc6afffFFL cb = 0x1D0DFFFFL # The background item = goocanvas.Rect( parent = self.rootitem, x = self.sea_area[0], y = self.sea_area[1], width = self.sea_area[2] - self.sea_area[0], height = self.sea_area[3] - self.sea_area[1], fill_color_rgba=0xafe0ffFFL, line_width=0) item.connect("button_press_event", self.ruler_item_event) item.connect("button_release_event", self.ruler_item_event) item.connect("motion_notify_event", self.ruler_item_event) for y in range (self.sea_area[1], self.sea_area[3]+1, int(step_y)): if(ci%2): color = ca else: color = cb ci += 1 # Text number item = goocanvas.Text( parent = self.rootitem, text=int(ci), font=gcompris.skin.get_font("gcompris/content"), x=text_x, y=y, fill_color_rgba=cb, anchor = gtk.ANCHOR_CENTER ) item.connect("button_press_event", self.ruler_item_event) item.connect("button_release_event", self.ruler_item_event) item.connect("motion_notify_event", self.ruler_item_event) item = goocanvas.Polyline( parent = self.rootitem, points = goocanvas.Points([(self.sea_area[0], y), (self.sea_area[2], y)]), stroke_color_rgba = color, line_width = 1.0 ) item.connect("button_press_event", self.ruler_item_event) item.connect("button_release_event", self.ruler_item_event) item.connect("motion_notify_event", self.ruler_item_event) ci = 0 for x in range (self.sea_area[0], self.sea_area[2]+1, int(step_x)): if(ci%2): color = ca else: color = cb ci += 1 # Text number item = goocanvas.Text( parent = self.rootitem, text=int(ci), font=gcompris.skin.get_font("gcompris/content"), x=x, y=text_y, fill_color_rgba=cb, anchor = gtk.ANCHOR_CENTER ) item.connect("button_press_event", self.ruler_item_event) item = goocanvas.Polyline( parent = self.rootitem, points= goocanvas.Points([(x, self.sea_area[1]), (x, self.sea_area[3])]), stroke_color_rgba = color, line_width=1.0 ) item.connect("button_press_event", self.ruler_item_event) # The ARRIVAL LINE item = goocanvas.Polyline( parent = self.rootitem, points = goocanvas.Points([(self.sea_area[2], self.sea_area[1]-5), (self.sea_area[2], self.sea_area[3]+5)]), stroke_color_rgba = 0xFF0000FFL, line_width=5.0 ) item.connect("button_press_event", self.ruler_item_event) # The grid is done # ---------------- # The Programming input area LEFT self.left_boat.sw = gtk.ScrolledWindow() self.left_boat.sw.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_ALWAYS) self.left_boat.sw.set_shadow_type(gtk.SHADOW_ETCHED_OUT) w = 250.0 h = 160.0 y = 355.0 # The upper limit of the text boxes x_left = gcompris.BOARD_WIDTH/4 - 30 x_right = (gcompris.BOARD_WIDTH/4)*3 + 30 self.left_boat.tb = gtk.TextBuffer() self.left_boat.tv = gtk.TextView(self.left_boat.tb) self.left_boat.sw.add(self.left_boat.tv) command_example = _("right") + " 45\n" + _("forward") + " 5\n" + _("left") + " 45" self.left_boat.tb.set_text(command_example) self.left_boat.tv.set_wrap_mode(gtk.WRAP_CHAR) goocanvas.Widget( parent = self.rootitem, widget = self.left_boat.sw, x=x_left, y=y, width=w, height= h, anchor=gtk.ANCHOR_N) self.left_boat.tv.show() self.left_boat.sw.show() # The Programming input area RIGHT self.right_boat.sw = gtk.ScrolledWindow() self.right_boat.sw.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_ALWAYS) self.right_boat.sw.set_shadow_type(gtk.SHADOW_ETCHED_OUT) self.right_boat.tb = gtk.TextBuffer() self.right_boat.tv = gtk.TextView(self.right_boat.tb) self.right_boat.sw.add(self.right_boat.tv) command_example = _("left") + " 45\n" + _("forward") + " 5\n" + _("right") + " 45" self.right_boat.tb.set_text(command_example) self.right_boat.tv.set_wrap_mode(gtk.WRAP_CHAR) goocanvas.Widget( parent = self.rootitem, widget=self.right_boat.sw, x=x_right, y=y, width=w, height= h, anchor=gtk.ANCHOR_N) self.right_boat.tv.show() self.right_boat.sw.show() # Text Labels self.left_boat.speeditem = goocanvas.Text( parent = self.rootitem, text="", font=gcompris.skin.get_font("gcompris/content"), x=x_left, y=y-20, fill_color_rgba=0xFF0000FFL, anchor = gtk.ANCHOR_CENTER ) self.right_boat.speeditem = goocanvas.Text( parent = self.rootitem, text="", font=gcompris.skin.get_font("gcompris/content"), x=x_right, y=y-20, fill_color_rgba=0X027308FFL, anchor = gtk.ANCHOR_CENTER ) # The status area self.statusitem = goocanvas.Text( parent = self.rootitem, text="", font=gcompris.skin.get_font("gcompris/content"), x=gcompris.BOARD_WIDTH/2, y=y-12, fill_color_rgba=0X000a89FFL, anchor = gtk.ANCHOR_CENTER ) # The decoration boats pixmap = gcompris.utils.load_pixmap("searace/top_boat_red.png") item = goocanvas.Image( parent = self.rootitem, pixbuf = pixmap, ) gcompris.utils.item_rotate(item, -90); item.translate(-y-40, -10) pixmap = gcompris.utils.load_pixmap("searace/top_boat_green.png") item = goocanvas.Image( parent = self.rootitem, pixbuf = pixmap, ) gcompris.utils.item_rotate(item, -90); item.translate(-y-40, gcompris.BOARD_WIDTH-60) # The commands hl = 18 y += 7 text_color = 0x0000FFFFL goocanvas.Text( parent = self.rootitem, text=_("COMMANDS ARE"), font=gcompris.skin.get_font("gcompris/content"), x=gcompris.BOARD_WIDTH/2, y=y, fill_color_rgba=text_color, anchor = gtk.ANCHOR_CENTER ) goocanvas.Text( parent = self.rootitem, text=_("forward"), font=gcompris.skin.get_font("gcompris/content"), x=gcompris.BOARD_WIDTH/2, y=y+hl, fill_color_rgba= text_color, anchor = gtk.ANCHOR_CENTER ) goocanvas.Text( parent = self.rootitem, text=_("left"), font=gcompris.skin.get_font("gcompris/content"), x=gcompris.BOARD_WIDTH/2, y=y+hl*2, fill_color_rgba= text_color, anchor = gtk.ANCHOR_CENTER ) goocanvas.Text( parent = self.rootitem, text=_("right"), font=gcompris.skin.get_font("gcompris/content"), x=gcompris.BOARD_WIDTH/2, y=y+hl*3, fill_color_rgba=text_color, anchor = gtk.ANCHOR_CENTER ) # Weather condition is a 2 value pair (angle wind_speed) # Weather is a list of the form: # (rectangle coordinate) (weather) def display_weather(self): # Reset the weather list self.weather = [] # Some constant to define the sea area # The sea area is defined in the global self.sea_area slice_x = 5 + self.gcomprisBoard.level slice_y = 3 + self.gcomprisBoard.level step_x = (self.sea_area[2]-self.sea_area[0])/slice_x step_y = (self.sea_area[3]-self.sea_area[1])/slice_y stop_x = self.sea_area[0]+step_x*slice_x stop_y = self.sea_area[1]+step_y*slice_y for x in range (self.sea_area[0], stop_x, int(step_x)): for y in range (self.sea_area[1], stop_y, int(step_y)): #print x, step_x, self.sea_area[2] angle = 0 if(self.left_initial_boat_y>y and self.left_initial_boat_yy and self.right_initial_boat_y= coord[0] and boat.x <= coord[2] and boat.y >= coord[1] and boat.y <= coord[3]): return(condition) for conditions in self.weather: coord = conditions[0] condition = conditions[1] #print "Testing coord="+str(coord)+" Boat coord x="+str(x)+" y="+str(y) if(x >= coord[0] and x <= coord[2] and y >= coord[1] and y <= coord[3]): boat.condition = conditions #print "Found: x="+str(x)+" y="+str(y)+" coord="+str(coord) #print " angle=" + str(int(condition[0])) + " speed=" + str(condition[1]) return(condition) # Should not happen, return a normal condition anyway return(0,1) # Given a x y coord, return it's weather condition (no caching) def get_absolute_weather_condition(self, x, y): for conditions in self.weather: coord = conditions[0] condition = conditions[1] #print "Testing coord="+str(coord)+" Boat coord x="+str(x)+" y="+str(y) if(x >= coord[0] and x <= coord[2] and y >= coord[1] and y <= coord[3]): #print "Found: x="+str(x)+" y="+str(y)+" coord="+str(coord) #print " angle=" + str(int(condition[0])) + " speed=" + str(condition[1]) return(condition) # Should not happen, return a normal condition anyway return(0,1) # Return a wind score depending on an angle def get_wind_score(self, boat_angle, condition): # Calculate the timer inc depending on the wind angle + speed wind_angle = condition[0] - boat_angle if(wind_angle>360): wind_angle-=360 elif(wind_angle<-360): wind_angle+=360 if(abs(wind_angle)>180): wind_angle=180-(abs(wind_angle)-180) # Increase the timer depending on wind force and direction angle_pi = wind_angle*math.pi/180 cx = math.cos(angle_pi) penalty=3 if(cx<0): penalty*=5 return(cx*condition[1]*-1*penalty) # # Boat moving # ----------- def cmd_forward(self, boat, value): # print "Player " + str(boat.player) + " cmd_forward " + str(value) + " dx=" + str(boat.dx) + " dy=" + str(boat.dy) if(self.board_paused): boat.timer = 0 return value -= 1 if value <= 0: # Process next command self.race_one_command(boat) boat.timer = 0 return # Move it boat.x += 1 boat.y += 0 # We need to convert the coord to the rootitem coordinate to check limits (x, y)= self.gcomprisBoard.canvas.\ convert_from_item_space(boat.item, boat.x, boat.y) # Manage the wrapping if(yself.sea_area[3]): y = self.sea_area[1] (boat.x, boat.y)= self.gcomprisBoard.canvas.\ convert_to_item_space(boat.item, x, y) elif(x>self.sea_area[2]): boat.arrived = True boat.finish_time = time.time() #print "self.left_boat.finish_time" + str(self.left_boat.finish_time) #print "self.right_boat.finish_time" + str(self.right_boat.finish_time) if(not self.left_boat.won and not self.right_boat.won): boat.won = True elif(abs(self.left_boat.finish_time - self.right_boat.finish_time) < 1): # The two boat arrived in a close time frame (1s), it's a draw self.statusitem.props.text = _("This is a draw") self.left_boat.won = False self.right_boat.won = False boat.speeditem.props.text = "" boat.speeditem.props.text = "" if(self.left_boat.won): self.statusitem.props.text = _("The Red boat has won") boat.speeditem.props.text = "" elif(self.right_boat.won): self.statusitem.props.text = _("The Green boat has won") boat.speeditem.props.text = "" boat.timer = 0 return condition = self.get_weather_condition(boat) boat.item.props.x = boat.x boat.item.props.y = boat.y wind = self.get_wind_score(boat.angle, condition) #print "Player " + str(boat.player) + " wind_angle=" + str(abs(wind_angle)) + " condition=" + str(condition[1]) + " cx=" + str(cx) + " wind=" + str(wind) angle = condition[0] if(angle>180): angle = abs(angle-360) boat.speeditem.props.text = \ _("Angle:") + str(angle) + " " + _("Wind:") + str(int(wind)*-1) boat.speeditem.raise_(None) boat.timer = gobject.timeout_add(int(self.timerinc+wind), self.cmd_forward, boat, value) # Counter Clock wise rotation (use negative param to turn clock wise) def cmd_turn_left(self, boat, value): # print "Player " + str(boat.player) + " turn left " + str(value) if(self.board_paused): boat.timer = 0 return if value == 0: # Process next command self.race_one_command(boat) boat.timer = 0 return turn = -value boat.angle += turn if(boat.angle>360): boat.angle-=360 elif(boat.angle<360): boat.angle+=360 value = 0 gcompris.utils.item_rotate_relative(boat.item, turn); boat.timer = gobject.timeout_add(self.timer_turn, self.cmd_turn_left, boat, value) # Run the race def race_one_command(self, boat): if(self.board_paused): # Let the user enter commands boat.tv.set_editable(True) boat.line = 0 boat.timer = 0 return valid_cmd = False cmd = "" while(boat.line < boat.tb.get_line_count() and not valid_cmd): a = boat.tb.get_iter_at_line(boat.line) b = boat.tb.get_iter_at_line(boat.line) b.forward_to_line_end() cmd = boat.tb.get_text(a, b, False) boat.line+=1 if(cmd and cmd[0] == "\n"): boat.line+=1 # Processing cmd cmd = cmd.strip("\n\t ") if(cmd != "" and cmd[0] != "#"): valid_cmd = True # No more commands to process for this player if (boat.line > boat.tb.get_line_count() or not valid_cmd): # Let the user enter commands boat.tv.set_editable(True) # Ready to Restart boat.line = 0 boat.timer = 0 return #print "boat.line=" + str(boat.line) #print "Parsing command = " + cmd + "<<" cmds = cmd.split() # Manage default cases (no params given) if ( len(cmds) == 1 and cmd.startswith(_("forward")) ): cmd += " 1" elif ( len(cmds) == 1 and cmd.startswith(_("left")) ): cmd += " 45" elif ( len(cmds) == 1 and cmd.startswith(_("right")) ): cmd += " 45" elif ( len(cmds) > 2): boat.speeditem.props.text = _("Syntax error at line") \ + " " + str(boat.line) + "\n(" + cmd + ")" # Let the user enter commands boat.tv.set_editable(True) boat.line = 0 boat.timer = 0 return value = 0 if(len(cmds) == 2): try: value = int(cmd.split()[1]) except ValueError: # Let the user enter commands boat.tv.set_editable(True) boat.timer = 0 boat.speeditem.props.text = _("The command") + " '" + cmd.split()[0] + "' " + "at line" + " " + str(boat.line) + "\n" + "requires a number parameter" boat.line = 0 return if( cmd.startswith(_("forward"))): # Transform the value from user visible sea size to pixels value *= self.sea_ratio # Initialize the move boat.timer = gobject.timeout_add(self.timerinc, self.cmd_forward, boat, value) elif( cmd.startswith(_("left"))): boat.timer = gobject.timeout_add(self.timerinc, self.cmd_turn_left, boat, value) elif( cmd.startswith(_("right"))): boat.timer = gobject.timeout_add(self.timerinc, self.cmd_turn_left, boat, value*-1) else: # Let the user enter commands boat.tv.set_editable(True) boat.timer = 0 boat.speeditem.props.text = \ _("Unknown command at line") + " " + str(boat.line) + "\n(" + cmd.split()[0] + ")" boat.line = 0 # Will return a text string: the tux move def tux_move(self): # The sea area is defined in the global self.sea_area step_x = (self.sea_area[2]-self.sea_area[0])/20*2 step_y = (self.sea_area[3]-self.sea_area[1])/10 # Original boat position bx = self.right_boat.x by = self.right_boat.y ba = 0 one_path = [] # X loop while (bx <= self.sea_area[2] and bx >= self.sea_area[0]): score = 10000 # By default, go straight coord = (bx, by, ba, int(step_x)) # angle loop for boat_angle in [ -45, 0, 45 ]: a_pi = boat_angle * math.pi/180 # Find the point from current boat position with angle a and distance step_x x = bx + math.cos(a_pi)*step_x y = by + math.sin(a_pi)*step_x # Manage the wrapping line_style = goocanvas.LineDash([1.0]) if(y < self.sea_area[1]): y = self.sea_area[3] - (self.sea_area[1]-y) line_style = goocanvas.LineDash([5.0, 1.0, 5.0]) elif(y>self.sea_area[3]): y = self.sea_area[1] + (y - self.sea_area[3]) line_style = goocanvas.LineDash([5.0, 1.0, 5.0]) # Find shortest path to previous calculated point condition = self.get_absolute_weather_condition(x, y) wind = self.get_wind_score(boat_angle, condition) if(wind0): if(cumulative_distance): tux_move += _("forward") + " " + str(cumulative_distance) + "\n" cumulative_distance=0 tux_move += _("left") + " " + str(abs(int(ba-a))) + "\n" ba -= abs(int(ba-a)) cumulative_distance += int(d/self.sea_ratio) # Final move, add an ofset because we loose space in abs() tux_move += _("forward") + " " + str(cumulative_distance+2) + "\n" self.right_boat.tb.set_text(tux_move) self.right_boat.tv.set_editable(False) # ---------------------------------------- # The RULER # def ruler_item_event(self, widget, target, event=None): (x, y)= self.gcomprisBoard.canvas.\ convert_from_item_space(widget, event.x, event.y) if event.type == gtk.gdk.BUTTON_PRESS: if event.button == 1: self.pos_x = x self.pos_y = y self.ruleritem = goocanvas.Polyline( parent = self.rootitem, points = goocanvas.Points([(self.pos_x, self.pos_y), (x, y)]), stroke_color_rgba=0xFF0000FFL, line_width=2.0 ) return True if event.type == gtk.gdk.MOTION_NOTIFY: if event.state & gtk.gdk.BUTTON1_MASK: # Calc the angle and distance and display them in the status bar distance = math.sqrt((self.pos_x-x)*(self.pos_x-x)+(self.pos_y-y)*(self.pos_y-y)) distance = int(distance/self.sea_ratio) angle = math.atan2(abs(self.pos_x-x), abs(self.pos_y-y)) angle = int(angle*180/math.pi) angle = abs(angle - 90) self.statusitem.props.text = \ _("Distance:") + " " + str(distance) + " " + _("Angle:") + " " + str(angle) self.ruleritem.props.points = \ goocanvas.Points([(self.pos_x, self.pos_y), (x, y)]) if event.type == gtk.gdk.BUTTON_RELEASE: if event.button == 1: self.ruleritem.remove() self.statusitem.props.text = "" return True return False def ok_event(self, widget, target, event=None): self.ok()