From 017a3bee19e677289f63f6eaef3495458ae7400c Mon Sep 17 00:00:00 2001 From: Sai Vineet Date: Mon, 06 Jan 2014 10:54:57 +0000 Subject: Added Palette framework Also a bugix about the clear trace and the clear all tool's active or non active state. --- diff --git a/activity.py b/activity.py index 129974b..823143a 100644 --- a/activity.py +++ b/activity.py @@ -41,6 +41,8 @@ from sugar.graphics.toolbarbox import ToolbarBox from sugar.graphics.toolbarbox import ToolbarButton from sugar.graphics.style import GRID_CELL_SIZE from sugar.datastore import datastore +from sugar.graphics.icon import Icon +from sugar.graphics import style import tools import physics @@ -214,19 +216,122 @@ class PhysicsActivity(activity.Activity): button.set_group(None) firstButton = button - if c.name == tools.EraseAllTool.name: - # Add a separator - Erase all tool is a dangerous tool - separator = gtk.SeparatorToolItem() - _insert_item(create_toolbar, separator, -1) - separator.show() - button.set_tooltip(c.toolTip) button.set_accelerator(c.toolAccelerator) button.connect('clicked', self.radioClicked) + palette = self._build_palette(c) + if palette is not None: + button.get_palette().set_content(palette) _insert_item(create_toolbar, button, -1) button.show() self.radioList[button] = c.name + def __icon_path(self, name): + activity_path = activity.get_bundle_path() + icon_path = os.path.join(activity_path, 'icons', + name+".svg") + return icon_path + + def _build_palette(self, tool): + if tool.palette_enabled: + if tool.palette_mode == tools.PALETTE_MODE_SLIDER_ICON: + settings = tool.palette_settings + hbox = gtk.HBox() + hbox.set_size_request(-1, 50) + + icon1 = Icon() + icon1.set_file(self.__icon_path(settings['icon1'])) + icon2 = Icon() + icon2.set_file(self.__icon_path(settings['icon2'])) + + adj = gtk.Adjustment(lower=settings['min'], + upper=settings['max'], + step_incr=1) + slider = gtk.HScale(adjustment=adj) + slider.set_size_request(200, 25) + slider.connect("value-changed", self._slider_palette, + tool.name) + slider.set_value(settings['min']) + + hbox.pack_start(icon1, False, False, 0) + hbox.pack_start(slider, True, True, 0) + hbox.pack_start(icon2, False, False, 0) + hbox.show_all() + return hbox + elif tool.palette_mode == tools.PALETTE_MODE_SLIDER_LABEL: + table = gtk.Table(rows=len(tool.palette_settings), columns=2) + + table.set_row_spacings(spacing=style.DEFAULT_SPACING) + table.set_col_spacings(spacing=style.DEFAULT_SPACING) + for row, settings in enumerate(tool.palette_settings): + label = gtk.Label(settings["label"]) + + adj = gtk.Adjustment(lower=settings['min'], + upper=settings['max'], + step_incr=1) + slider = gtk.HScale(adjustment=adj) + + slider.set_size_request(200, 50) + slider.connect("value-changed", self._slider_label_palette, + tool.name, settings['data']) + slider.set_value(settings['min']) + + table.attach(label, + left_attach=0, + right_attach=1, + top_attach=row, + bottom_attach=row+1) + table.attach(slider, + left_attach=1, + right_attach=2, + top_attach=row, + bottom_attach=row+1) + table.show_all() + return table + elif tool.palette_mode == tools.PALETTE_MODE_ICONS: + vbox = gtk.VBox() + for settings in tool.palette_settings: + hbox = gtk.HBox() + firstButton = None + for i in range(0, settings['icon_count']): + button = RadioToolButton( + named_icon=settings['icons'][i]) + if firstButton: + button.set_group(firstButton) + else: + button.set_group(None) + firstButton = button + button.connect('clicked', + self._palette_icon_clicked, + tool.name, + settings['name'], + settings['icon_values'][i]) + if settings['active'] == settings['icons'][i]: + button.set_active(True) + hbox.pack_start(button, False, False, 0) + vbox.add(hbox) + vbox.show_all() + return vbox + + return None + + def _palette_icon_clicked(self, button, toolname, value_name, value): + for tool in tools.allTools: + if tool.name == toolname: + tool.palette_data[value_name] = value + + def _slider_label_palette(self, slider, toolname, dataname): + value = slider.get_value() + for tool in tools.allTools: + if tool.name == toolname: + tool.palette_data[dataname] = value + + def _slider_palette(self, slider, toolname): + value = slider.get_value() + for tool in tools.allTools: + if tool.name == toolname: + tool.palette_data = value + def clear_trace_alert_cb(self, alert, response): self.remove_alert(alert) if response is gtk.RESPONSE_OK: @@ -259,13 +364,14 @@ class PhysicsActivity(activity.Activity): if response_id is gtk.RESPONSE_OK: pygame.event.post(pygame.event.Event(pygame.USEREVENT, action='clear_all')) - - clear_all_alert = ConfirmationAlert() - clear_all_alert.props.title = _('Are You Sure?') - clear_all_alert.props.msg = \ - _('All your work will be discarded. This cannot be undone!') - clear_all_alert.connect('response', clear_all_alert_cb) - self.add_alert(clear_all_alert) + if len(self.game.world.world.GetBodyList()) > 2: + print len(self.game.world.world.GetBodyList()) + clear_all_alert = ConfirmationAlert() + clear_all_alert.props.title = _('Are You Sure?') + clear_all_alert.props.msg = \ + _('All your work will be discarded. This cannot be undone!') + clear_all_alert.connect('response', clear_all_alert_cb) + self.add_alert(clear_all_alert) def radioClicked(self, button): pygame.event.post(pygame.event.Event(pygame.USEREVENT, diff --git a/icons/anvil.svg b/icons/anvil.svg new file mode 100644 index 0000000..f088e41 --- /dev/null +++ b/icons/anvil.svg @@ -0,0 +1,48 @@ + +image/svg+xml + + \ No newline at end of file diff --git a/icons/basketball.svg b/icons/basketball.svg new file mode 100644 index 0000000..4b9ecfe --- /dev/null +++ b/icons/basketball.svg @@ -0,0 +1,86 @@ + + + +image/svg+xml + + + + + + + + + \ No newline at end of file diff --git a/icons/bowling-ball.svg b/icons/bowling-ball.svg new file mode 100644 index 0000000..f18f465 --- /dev/null +++ b/icons/bowling-ball.svg @@ -0,0 +1,50 @@ + + + +image/svg+xml + + \ No newline at end of file diff --git a/icons/feather.svg b/icons/feather.svg new file mode 100644 index 0000000..e05c446 --- /dev/null +++ b/icons/feather.svg @@ -0,0 +1,55 @@ + +image/svg+xml + + + + + + \ No newline at end of file diff --git a/icons/grass.svg b/icons/grass.svg new file mode 100644 index 0000000..0e52491 --- /dev/null +++ b/icons/grass.svg @@ -0,0 +1,48 @@ + +image/svg+xml + + \ No newline at end of file diff --git a/icons/ice-skate.svg b/icons/ice-skate.svg new file mode 100644 index 0000000..c2215bd --- /dev/null +++ b/icons/ice-skate.svg @@ -0,0 +1,52 @@ + +image/svg+xml + + + + \ No newline at end of file diff --git a/icons/motor-rabbit.svg b/icons/motor-rabbit.svg new file mode 100644 index 0000000..cc146d7 --- /dev/null +++ b/icons/motor-rabbit.svg @@ -0,0 +1,64 @@ + +image/svg+xmlSlice 1 +Slice 1 +Created with Sketch (http://www.bohemiancoding.com/sketch) + + + + + + + \ No newline at end of file diff --git a/icons/motor-turtle.svg b/icons/motor-turtle.svg new file mode 100644 index 0000000..b7fb511 --- /dev/null +++ b/icons/motor-turtle.svg @@ -0,0 +1,64 @@ + +image/svg+xmlSlice 1 +Slice 1 +Created with Sketch (http://www.bohemiancoding.com/sketch) + + + + + + + \ No newline at end of file diff --git a/icons/sandpaper.svg b/icons/sandpaper.svg new file mode 100644 index 0000000..414fc8f --- /dev/null +++ b/icons/sandpaper.svg @@ -0,0 +1,55 @@ + +image/svg+xml + + + + + + \ No newline at end of file diff --git a/icons/tennis-ball.svg b/icons/tennis-ball.svg new file mode 100644 index 0000000..fc8d418 --- /dev/null +++ b/icons/tennis-ball.svg @@ -0,0 +1,60 @@ + +image/svg+xml + + + + + + \ No newline at end of file diff --git a/icons/wood.svg b/icons/wood.svg new file mode 100644 index 0000000..1f0bc6d --- /dev/null +++ b/icons/wood.svg @@ -0,0 +1,398 @@ + +image/svg+xml + + + + http://thenounproject.com + + The Noun Project + + Icon Template + + Reminders + + + + + + + + + + + + + + Strokes + + Try to keep strokes at 4px + + Minimum stroke weight is 2px + + For thicker strokes use even + + numbers: 6px, 8px etc. + + Remember to expand strokes + + before saving as an SVG + + + Size + + Cannot be wider or taller than + + 100px (artboard size) + + Scale your icon to fill as much of + + the artboard as possible + + + Ungroup + + If your design has more than one + + shape, make sure to ungroup + + + Save as + + Save as .SVG and make sure + + “Use Artboards” is checked + + 100px + + .SVG + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/physics.py b/physics.py index b1894ad..c44f5ea 100644 --- a/physics.py +++ b/physics.py @@ -36,7 +36,7 @@ from pygame.color import * sys.path.append('lib/') # If your architecture is different, comment these lines and install # the modules in your system. -sys.path.append('lib/Box2D-2.0.2b1-py2.5-linux-i686.egg') +sys.path.append('lib/Box2D-2.0.2b2-py2.7-linux-i686.egg') import Box2D as box2d import elements @@ -54,9 +54,6 @@ class PhysicsGame: # Create the name --> instance map for components self.toolList = {} for c in tools.allTools: - if c.name == tools.EraseAllTool.name: - self.toolList[c.name] = c(self, activity) - continue self.toolList[c.name] = c(self) self.currentTool = self.toolList[tools.allTools[0].name] # Set up the world (instance of Elements) @@ -148,9 +145,9 @@ class PhysicsGame: if self.world.run_physics: bodies_present = len(self.world.world.GetBodyList()) clear_all_active = self.activity.clear_all.get_sensitive() - if (bodies_present > 1) and clear_all_active is False: + if (bodies_present > 2) and clear_all_active is False: self.activity.clear_all.set_sensitive(True) - elif (bodies_present > 1) is False and \ + elif (bodies_present > 2) is False and \ clear_all_active is True: self.activity.clear_all.set_sensitive(False) diff --git a/tools.py b/tools.py index 7911168..bb9dbf8 100644 --- a/tools.py +++ b/tools.py @@ -31,6 +31,10 @@ from sugar.activity import activity import gtk import math +PALETTE_MODE_SLIDER_ICON = 0 +PALETTE_MODE_ICONS = 1 +PALETTE_MODE_SLIDER_LABEL = 2 + # Tools that can be superlcassed class Tool(object): @@ -38,6 +42,8 @@ class Tool(object): icon = 'icon' toolTip = 'Tool Tip' toolAccelerator = None + palette_enabled = False + palette_data = None def __init__(self, gameInstance): self.game = gameInstance @@ -53,16 +59,15 @@ class Tool(object): toggle = self.game.world.run_physics self.game.world.run_physics = not toggle elif event.action == 'clear_all': - if len(self.game.world.world.GetBodyList()) > 1: - # Get bodies and destroy them too - for body in self.game.world.world.GetBodyList(): - self.game.world.world.DestroyBody(body) - - # Add ground, because we destroyed it before - self.game.world.add.ground() - # Also clear the points recorded in pens. - self.game.full_pos_list = \ - [[] for _ in self.game.full_pos_list] + # Get bodies and destroy them too + for body in self.game.world.world.GetBodyList(): + self.game.world.world.DestroyBody(body) + + # Add ground, because we destroyed it before + self.game.world.add.ground() + # Also clear the points recorded in pens. + self.game.full_pos_list = \ + [[] for _ in self.game.full_pos_list] elif event.action == 'focus_in': self.game.in_focus = True elif event.action == 'focus_out': @@ -132,6 +137,36 @@ class CircleTool(Tool): toolTip = _('Circle') toolAccelerator = _('c') + palette_enabled = True + palette_mode = PALETTE_MODE_ICONS + palette_settings = [ + { + "name": "density", + "icon_count": 3, + "icons": ["feather", "wood", "anvil"], + "icon_values": [0.5, 1.0, 10.0], + "active": "wood" + }, + { + "name": "restitution", + "icon_count": 3, + "icons": ["basketball", "tennis-ball", "bowling-ball"], + "icon_values": [1, 0.16, 0.01], + "active": "tennis-ball" + }, + { + "name": "friction", + "icon_count": 3, + "icons": ["ice-skate", "grass", "sandpaper"], + "icon_values": [0.5, 1, 2], + "active": "grass" + }] + palette_data = { + "density": 1.0, + "restitution": 0.16, + "friction": 1 + } + def __init__(self, gameInstance): Tool.__init__(self, gameInstance) self.pt1 = None @@ -144,9 +179,11 @@ class CircleTool(Tool): self.pt1 = tuple_to_int(event.pos) elif event.type == MOUSEBUTTONUP: if event.button == 1: - self.game.world.add.ball(self.pt1, self.radius, - dynamic=True, density=1.0, - restitution=0.16, friction=0.5) + self.game.world.add.ball( + self.pt1, self.radius, + dynamic=True, density=self.palette_data['density'], + restitution=self.palette_data['restitution'], + friction=self.palette_data['friction']) self.pt1 = None def draw(self): @@ -173,6 +210,36 @@ class BoxTool(Tool): toolTip = _('Box') toolAccelerator = _('b') + palette_enabled = True + palette_mode = PALETTE_MODE_ICONS + palette_settings = [ + { + "name": "density", + "icon_count": 3, + "icons": ["feather", "wood", "anvil"], + "icon_values": [0.5, 1.0, 10.0], + "active": "wood" + }, + { + "name": "restitution", + "icon_count": 3, + "icons": ["basketball", "tennis-ball", "bowling-ball"], + "icon_values": [1, 0.16, 0.01], + "active": "tennis-ball" + }, + { + "name": "friction", + "icon_count": 3, + "icons": ["ice-skate", "grass", "sandpaper"], + "icon_values": [0.5, 1, 2], + "active": "grass" + }] + palette_data = { + "density": 1.0, + "restitution": 0.16, + "friction": 1 + } + def __init__(self, gameInstance): Tool.__init__(self, gameInstance) self.pt1 = None @@ -192,13 +259,14 @@ class BoxTool(Tool): self.rect = pygame.Rect(self.pt1, (-self.width, -self.height)) self.rect.normalize() - self.game.world.add.rect(self.rect.center, - max(self.rect.width, 10) / 2, - max(self.rect.height, 10) / 2, - dynamic=True, - density=1.0, - restitution=0.16, - friction=0.5) + self.game.world.add.rect( + self.rect.center, + max(self.rect.width, 10) / 2, + max(self.rect.height, 10) / 2, + dynamic=True, + density=self.palette_data['density'], + restitution=self.palette_data['restitution'], + friction=self.palette_data['friction']) self.pt1 = None def draw(self): @@ -226,6 +294,36 @@ class TriangleTool(Tool): toolTip = _('Triangle') toolAccelerator = _('t') + palette_enabled = True + palette_mode = PALETTE_MODE_ICONS + palette_settings = [ + { + "name": "density", + "icon_count": 3, + "icons": ["feather", "wood", "anvil"], + "icon_values": [0.5, 1.0, 10.0], + "active": "wood" + }, + { + "name": "restitution", + "icon_count": 3, + "icons": ["basketball", "tennis-ball", "bowling-ball"], + "icon_values": [1, 0.16, 0.01], + "active": "tennis-ball" + }, + { + "name": "friction", + "icon_count": 3, + "icons": ["ice-skate", "grass", "sandpaper"], + "icon_values": [0.5, 1, 2], + "active": "grass" + }] + palette_data = { + "density": 1.0, + "restitution": 0.16, + "friction": 1 + } + def __init__(self, gameInstance): Tool.__init__(self, gameInstance) self.pt1 = None @@ -262,11 +360,12 @@ class TriangleTool(Tool): self.vertices = constructTriangleFromLine(self.pt1, mouse_x_y) - self.game.world.add.convexPoly(self.vertices, - dynamic=True, - density=1.0, - restitution=0.16, - friction=0.5) + self.game.world.add.convexPoly( + self.vertices, + dynamic=True, + density=self.palette_data['density'], + restitution=self.palette_data['restitution'], + friction=self.palette_data['friction']) self.pt1 = None self.vertices = None @@ -296,6 +395,36 @@ class PolygonTool(Tool): toolTip = _('Polygon') toolAccelerator = _('p') + palette_enabled = True + palette_mode = PALETTE_MODE_ICONS + palette_settings = [ + { + "name": "density", + "icon_count": 3, + "icons": ["feather", "wood", "anvil"], + "icon_values": [0.5, 1.0, 10.0], + "active": "wood" + }, + { + "name": "restitution", + "icon_count": 3, + "icons": ["basketball", "tennis-ball", "bowling-ball"], + "icon_values": [1, 0.16, 0.01], + "active": "tennis-ball" + }, + { + "name": "friction", + "icon_count": 3, + "icons": ["ice-skate", "grass", "sandpaper"], + "icon_values": [0.5, 1, 2], + "active": "grass" + }] + palette_data = { + "density": 1.0, + "restitution": 0.16, + "friction": 1 + } + def __init__(self, gameInstance): Tool.__init__(self, gameInstance) self.vertices = None @@ -319,11 +448,12 @@ class PolygonTool(Tool): self.vertices = [[i[0] - delta_x, i[1] - delta_y] for i in self.previous_vertices] self.safe = True - self.game.world.add.complexPoly(self.vertices, - dynamic=True, - density=1.0, - restitution=0.16, - friction=0.5) + self.game.world.add.complexPoly( + self.vertices, + dynamic=True, + density=self.palette_data['density'], + restitution=self.palette_data['restitution'], + friction=self.palette_data['friction']) self.vertices = None elif (event.type == MOUSEBUTTONUP or event.type == MOUSEBUTTONDOWN): @@ -336,11 +466,12 @@ class PolygonTool(Tool): if distance(tuple_to_int(event.pos), self.vertices[0]) < 15 \ and self.safe: self.vertices.append(self.vertices[0]) # Connect polygon - self.game.world.add.complexPoly(self.vertices, - dynamic=True, - density=1.0, - restitution=0.16, - friction=0.5) + self.game.world.add.complexPoly( + self.vertices, + dynamic=True, + density=self.palette_data['density'], + restitution=self.palette_data['restitution'], + friction=self.palette_data['friction']) self.previous_vertices = self.vertices[:] self.vertices = None elif distance(tuple_to_int(event.pos), self.vertices[0]) < 15: @@ -375,6 +506,36 @@ class MagicPenTool(Tool): toolTip = _('Draw') toolAccelerator = _('d') + palette_enabled = True + palette_mode = PALETTE_MODE_ICONS + palette_settings = [ + { + "name": "density", + "icon_count": 3, + "icons": ["feather", "wood", "anvil"], + "icon_values": [0.5, 1.0, 10.0], + "active": "wood" + }, + { + "name": "restitution", + "icon_count": 3, + "icons": ["basketball", "tennis-ball", "bowling-ball"], + "icon_values": [1, 0.16, 0.01], + "active": "tennis-ball" + }, + { + "name": "friction", + "icon_count": 3, + "icons": ["ice-skate", "grass", "sandpaper"], + "icon_values": [0.5, 1, 2], + "active": "grass" + }] + palette_data = { + "density": 1.0, + "restitution": 0.16, + "friction": 1 + } + def __init__(self, gameInstance): Tool.__init__(self, gameInstance) self.vertices = None @@ -395,10 +556,11 @@ class MagicPenTool(Tool): for i in self.previous_vertices] self.safe = True if self.vertices and self.safe: - self.game.world.add.complexPoly(self.vertices, dynamic=True, - density=1.0, - restitution=0.16, - friction=0.5) + self.game.world.add.complexPoly( + self.vertices, dynamic=True, + density=self.palette_data['density'], + restitution=self.palette_data['restitution'], + friction=self.palette_data['friction']) self.previous_vertices = self.vertices[:] self.vertices = None elif event.type == MOUSEMOTION and self.vertices: @@ -554,6 +716,17 @@ class MotorTool(Tool): icon = 'motor' toolTip = _('Motor') toolAccelerator = _('m') + # Palette settings + palette_enabled = True + palette_mode = PALETTE_MODE_SLIDER_ICON + palette_settings = { + "icon1": "motor-turtle", + "icon2": "motor-rabbit", + "min": 0, + "max": 500 + } + # Default Value + palette_data = 10 def __init__(self, gameInstance): Tool.__init__(self, gameInstance) @@ -568,7 +741,9 @@ class MotorTool(Tool): self.jb1 = self.game.world.get_bodies_at_pos( tuple_to_int(event.pos)) if self.jb1: - self.game.world.add.motor(self.jb1[0], self.jb1pos) + self.game.world.add.motor( + self.jb1[0], self.jb1pos, + speed=self.palette_data) self.jb1 = self.jb1pos = None def cancel(self): @@ -657,54 +832,6 @@ class DestroyTool(Tool): self.vertices = None -class EraseAllTool(Tool): - name = 'Erase All' - icon = 'destroy-all' - toolTip = _('Erase all') - - def __init__(self, gameInstance, activity=None): - super(EraseAllTool, self).__init__(gameInstance) - self.game = gameInstance - self.response_alert = None - self.activity = activity - - def handleToolEvent(self, event, action=False): - if event.type == MOUSEBUTTONDOWN: - if not action: - # Add alert for confirm the delete all action. - alert = ConfirmationAlert() - alert.props.title = _('Delete all shapes?') - alert.props.msg = _('This cannot be undone!') - alert.connect('response', self.alert_info, event) - self.activity.add_alert(alert) - return - else: - if self.response_alert: - self.response_alert = False - # Obtain all figures - bodys = [] - for body in self.game.world.world.GetBodyList(): - bodys.append(body) - - # Erase all ;) - for body in bodys: - self.game.world.world.DestroyBody(body) - - # The ground has deleted, restore.. - self.game.world.add.ground() - else: - pass - - def alert_info(self, alert, response_id, event): - self.activity.remove_alert(alert) - if response_id is gtk.RESPONSE_OK: - self.response_alert = True - elif response_id is gtk.RESPONSE_CANCEL: - self.response_alert = False - - self.handleToolEvent(event, True) - - # Track tool class TrackTool(Tool): name = 'Track' @@ -762,11 +889,30 @@ class ChainTool(Tool): toolTip = _("Chain") toolAccelerator = "i" + # Palette settings + palette_enabled = True + palette_mode = PALETTE_MODE_SLIDER_LABEL + palette_settings = [ + { + "label": "Chain Distance", + "min": 25, + "max": 50, + "data": "distance" + }, + { + "label": "Chain Circle Size", + "min": 1, + "max": 10, + "data": "radius" + }] + palette_data = { + "distance": 25, + "radius": 1 + } + def __init__(self, gameInstance): Tool.__init__(self, gameInstance) self.jb1 = self.jb2 = self.jb1pos = self.jb2pos = None - self.circle_distance = 25 - self.circle_radius = 5 def handleToolEvent(self, event): Tool.handleToolEvent(self, event) @@ -806,14 +952,12 @@ class ChainTool(Tool): first_iter = True prevcircle = None prevpos = None - for p in range(5, d, self.circle_distance): + for p in range(5, d, int(self.palette_data['distance'])): x = x1 + p * math.cos(bearing) y = y1 + p * math.sin(bearing) circle = self.game.world.add.ball( - (x, y), self.circle_radius, dynamic=True) + (x, y), self.palette_data['radius'], dynamic=True) circle.userData['color'] = (0, 0, 0) - circle = self.game.world.get_bodies_at_pos( - tuple_to_int((x, y)))[0] if first_iter: self.game.world.add.joint(circle, bodyA, (x, y), pos1, False) first_iter = False -- cgit v0.9.1