diff options
Diffstat (limited to 'gtk2modules/canvas.py')
-rw-r--r-- | gtk2modules/canvas.py | 363 |
1 files changed, 363 insertions, 0 deletions
diff --git a/gtk2modules/canvas.py b/gtk2modules/canvas.py new file mode 100644 index 0000000..fd52259 --- /dev/null +++ b/gtk2modules/canvas.py @@ -0,0 +1,363 @@ +# Copyright 2012-2013 S. Daniel Francis <francis@sugarlabs.org> +# +# 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, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, +# MA 02110-1301, USA. + +import logging +logger = logging.getLogger('canvas') +import json + +import gtk +from math import * + +from sweetener import profile +from sweetener import colors + +import expressions +from graph import Graph +from graph import SCALE_TYPE_DEC +from graph import SCALE_TYPE_RAD +from graph import SCALE_TYPE_CUST +from functions import FunctionsList + +x_res = 1 +x_max = '5.0' +x_min = '-5.0' +x_scale = '1.0' +y_max = '3.0' +y_min = '-3.0' +y_scale = '1.0' + + +class Canvas(gtk.HPaned): + #def write_file(self, file_path): + #x, y, w, h = self.graph.get_allocation() + #pix_buffer = gtk.gdk.Pixbuf(gtk.gdk.COLORSPACE_RGB, False, 8, w, h) + #pix_buffer.get_from_drawable(self.graph.pix_map, + #self.graph.pix_map.get_colormap(), + #0, 0, 0, 0, w, h) + #pix_buffer.save(file_path, 'png') + + def write_file(self, path): + jfile = open(path, 'w') + + _list = [] + for i in self.functions_list.get_list(): + _list.append([colors.color2string(i[0]), i[2]]) + + graph_props = {'list': _list, + 'xmin': self.graph.xmin, + 'xmax': self.graph.xmax, + 'ymin': self.graph.ymin, + 'ymax': self.graph.ymax} + + try: + json.dump(graph_props, jfile) + finally: + jfile.close() + + def read_file(self, path): + jfile = open(path, 'r') + try: + self.starting_program = False + graph_props = json.load(jfile) + self.functions_list.model.clear() + for i in graph_props['list']: + self.functions_list.append_function(gtk.gdk.color_parse(i[0]), + i[1]) + self.update_graph(None, self.functions_list.get_list()) + except Exception, err: + logger.error(str(err)) + finally: + jfile.close() + + def __init__(self, toolbar_box, activity): + gtk.HPaned.__init__(self) + self.activity = activity + self.x_max = x_max + self.x_min = x_min + self.x_scale = x_scale + self.y_max = y_max + self.y_min = y_min + self.y_scale = y_scale + self.w = 0 + self.h = 0 + self._resize_queue = 4 + self._resize_constant = 4 + self.graph = Graph() + self.activity.connect('key-press-event', + self.graph._key_press_event) + self.activity.connect('key-release-event', + self.graph._key_release_event) + self.graph.connect('update-x-y', self._update_coordinates) + self.starting_program = True + self.graph.connect('repopulate-parameter-entries', + self.parameter_entries_repopulate) + self.toolbar_box = toolbar_box + self.exports = {'image/x-generic': self.graph.save_png} + self.toolbar_box.connect('append-function', self._append_function) + self.toolbar_box.connect('remove-function', self._remove_function) + self.toolbar_box.connect('color-updated', self._update_line_color) + self.toolbar_box.connect('evaluate', self._evaluate_cb) + # FIXME: Have to connect correctly all the motions in toolbar + self.toolbar_box.connect('motion_notify_event', self._event_motion) + self.toolbar_box.view.connect('zoom-in', self.zoom_in) + self.toolbar_box.view.connect('zoom-out', self.zoom_out) + self.toolbar_box.view.connect('zoom-reset', self.zoom_reset) + self.toolbar_box.view.connect('scale-range', + self.update_parameters) + self.toolbar_box.view.connect('fullscreen', self._fullscreen) + self.toolbar_box.view.connect('connect-points', + self.connect_points) + self.toolbar_box.view.connect('decimal-scale', + self.scale_dec) + self.toolbar_box.view.connect('radian-scale', + self.scale_rad) + self.toolbar_box.view.connect('custom-scale', + self.scale_cust) + self.toolbar_box.view.connect('scale', self._update_scale) + self.toolbar_box.view.connect('show-grid', + lambda w, d: self.graph.set_show_grid(d)) + #self.toolbar_box.show() + self.functions_list = FunctionsList() + self.functions_list.connect('motion_notify_event', self._event_motion) + self.functions_list.connect('list-updated', + self.update_graph) + self.functions_list.connect('function-selected', + self._update_color_selection) + self.functions_list.show() + functions_box = gtk.VBox() + function_entry = gtk.Entry() + function_entry.show() + function_entry.connect('activate', self._new_function_cb) + functions_box.pack_start(function_entry, False, True, 0) + functions_window = gtk.ScrolledWindow() + functions_window.add(self.functions_list) + functions_window.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC) + functions_window.set_hadjustment(self.functions_list.get_hadjustment()) + functions_window.set_vadjustment(self.functions_list.get_vadjustment()) + functions_window.show() + functions_box.pack_start(functions_window, True, True, 0) + hbox = gtk.HBox() + self.xlabel = gtk.Label() + self.xlabel.set_alignment(0.5, 0.5) + self.xlabel.show() + hbox.pack_start(self.xlabel, True, True, 0) + self.ylabel = gtk.Label() + self.ylabel.set_alignment(0.5, 0.5) + self.ylabel.show() + hbox.pack_start(self.ylabel, True, True, 0) + hbox.show() + functions_box.pack_start(hbox, False, True, 0) + self.pack1(functions_box) + functions_box.show_all() + self.toolbar_box.view.x_min_entry.set_text(self.x_min) + self.toolbar_box.view.x_max_entry.set_text(self.x_max) + self.toolbar_box.view.x_scale_entry.set_text(self.x_scale) + self.toolbar_box.view.y_min_entry.set_text(self.y_min) + self.toolbar_box.view.y_max_entry.set_text(self.y_max) + self.toolbar_box.view.y_scale_entry.set_text(self.y_scale) + self.pack2(self.graph) + self.graph.show() + self.connect('expose-event', self._paned_expose_event_cb) + + def new(self): + self.functions_list.model.clear() + self._append_function(None) + + def _event_motion(self, widget, event): + self.graph.pointer = None + self.graph.queue_draw() + self.xlabel.set_text('') + self.ylabel.set_text('') + + def _update_coordinates(self, widget, x, y): + self.xlabel.set_text("x = " + str(int(x * 100) / 100.0)) + self.ylabel.set_text("y = " + str(int(y * 100) / 100.0)) + + def _fullscreen(self, widget): + self.previous_size = self._resize_constant + self._resize_queue = 0 + self.activity.fullscreen() + + def _new_function_cb(self, widget): + text = widget.get_text() + if len(self.functions_list.get_list()) % 2 == 0: + color = profile.get_fill_color(self) + else: + color = profile.get_stroke_color(self) + self.functions_list.append_function(color, text) + self.update_graph(None, self.functions_list.get_list()) + widget.set_text('') + number = len(self.functions_list.get_list()) + if number == 1: + self.toolbar_box.remove_function.sensitive = False + else: + self.toolbar_box.remove_function.sensitive = True + + def _update_scale(self, widget=None): + xscale, yscale = self.toolbar_box.view.get_scale() + self.graph.set_x_scale(xscale) + self.graph.set_y_scale(yscale) + self.update_graph(None, self.functions_list.get_list()) + + def _evaluate_cb(self, widget, x): + safe_dict = self.graph.safe_dict + try: + x = eval(expressions.convert(x), {'__builtins__': {}}, safe_dict) + safe_dict['x'] = x + except: + safe_dict['x'] = None + self.functions_list.evaluate(safe_dict) + + def update_parameters(self, widget): + xmin, xmax, ymin, ymax = self.toolbar_box.view.get_scale_range() + self.graph.set_xmin(xmin) + self.graph.set_xmax(xmax) + self.graph.set_ymin(ymin) + self.graph.set_ymax(ymax) + self.update_graph(None, self.functions_list.get_list()) + + def _paned_expose_event_cb(self, widget, event): + #logger.debug('Paned expose') + x, y, w, h = self.get_allocation() + if self._resize_queue == None: + if self.w != w or self.h != h: + self._resize_queue = self._resize_constant + self.w = w + self.h = h + min_position = self.get_property('min-position') + max_position = self.get_property('max-position') + if self._resize_queue != None: + #logger.debug('Resize a %f percent from %s %s' % + # (self._resize_queue * 10, min_position, max_position)) + if self._resize_queue == 0: + # logger.debug('Resize to %d' % min_position) + self.set_position(min_position) + else: + pos = (max_position - min_position) /\ + self._resize_queue + min_position + self.set_position(int(pos)) + # logger.debug('Resize to %d' % pos) + self._resize_queue = None + position = self.get_position() + self._resize_constant = float(max_position - min_position) / position if position != 0 else 0 + + def parameter_entries_repopulate(self, widget=None): + # set text in entries for parameters + self.toolbar_box.view.x_min_entry.set_text( + str(self.graph.xmin)) + self.toolbar_box.view.x_max_entry.set_text( + str(self.graph.xmax)) + self.toolbar_box.view.x_scale_entry.set_text( + str(self.graph.x_scale)) + self.toolbar_box.view.y_min_entry.set_text( + str(self.graph.ymin)) + self.toolbar_box.view.y_max_entry.set_text( + str(self.graph.ymax)) + self.toolbar_box.view.y_scale_entry.set_text( + str(self.graph.y_scale)) + + def zoom_in(self, widget, event=None): + 'Narrow the plotted section by half' + center_x = (self.graph.xmin + self.graph.xmax) / 2 + center_y = (self.graph.ymin + self.graph.ymax) / 2 + range_x = (self.graph.xmax - self.graph.xmin) + range_y = (self.graph.ymax - self.graph.ymin) + + self.graph.xmin = center_x - (range_x / 4) + self.graph.xmax = center_x + (range_x / 4) + self.graph.ymin = center_y - (range_y / 4) + self.graph.ymax = center_y + (range_y / 4) + + self.parameter_entries_repopulate() + self.update_graph(None, self.functions_list.get_list()) + + def zoom_out(self, widget, event=None): + """Double the plotted section""" + center_x = (self.graph.xmin + self.graph.xmax) / 2 + center_y = (self.graph.ymin + self.graph.ymax) / 2 + range_x = (self.graph.xmax - self.graph.xmin) + range_y = (self.graph.ymax - self.graph.ymin) + + self.graph.xmin = center_x - (range_x) + self.graph.xmax = center_x + (range_x) + self.graph.ymin = center_y - (range_y) + self.graph.ymax = center_y + (range_y) + + self.parameter_entries_repopulate() + self.update_graph(None, self.functions_list.get_list()) + + def zoom_reset(self, widget, event=None): + """Set the range back to the user's input""" + + self.graph.xmin = -5.0 + self.graph.ymin = -3.0 + self.graph.xmax = 5.0 + self.graph.ymax = 3.0 + self.toolbar_box.view.x_min_entry.set_text(self.x_min) + self.toolbar_box.view.x_max_entry.set_text(self.x_max) + self.toolbar_box.view.x_scale_entry.set_text(self.x_scale) + self.toolbar_box.view.y_min_entry.set_text(self.y_min) + self.toolbar_box.view.y_max_entry.set_text(self.y_max) + self.toolbar_box.view.y_scale_entry.set_text(self.y_scale) + self.update_graph(None, self.functions_list.get_list()) + + def scale_dec(self, widget): + self.graph.scale_style = SCALE_TYPE_DEC + self.update_graph(None, self.functions_list.get_list()) + + def scale_rad(self, widget): + self.graph.scale_style = SCALE_TYPE_RAD + self.update_graph(None, self.functions_list.get_list()) + + def scale_cust(self, widget): + self.graph.scale_style = SCALE_TYPE_CUST + self.update_graph(None, self.functions_list.get_list()) + + def connect_points(self, widget, connect_points): + self.graph.connect_points = connect_points + self.update_graph(None, self.functions_list.get_list()) + + def _update_line_color(self, widget, color): + self.functions_list.set_current_line_color(color) + self.update_graph(None, self.functions_list.get_list()) + + def _update_color_selection(self, widget, color): + self.toolbar_box.color_item.set_color(color) + self.update_graph(None, self.functions_list.get_list()) + + def _remove_function(self, widget): + self.functions_list.remove_function() + self.update_graph(None, self.functions_list.get_list()) + return len(self.functions_list.get_list()) + + def _append_function(self, widget): + if len(self.functions_list.get_list()) % 2 == 0: + color = profile.get_fill_color(self) + else: + color = profile.get_stroke_color(self) + self.functions_list.append_function(color) + self.update_graph(None, self.functions_list.get_list()) + return len(self.functions_list.get_list()) + + def update_graph(self, widget, info): +# self._evaluate_cb(None, self.toolbar_box.evaluate_entry.get_text()) + self.graph.reload_functions([(func, + color, + selected) for color, + name, + func, + selected in info]) |