#!/usr/bin/env python # -*- coding: utf-8 -*- # # Copyright 2012 S. Daniel Francis # # 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. from gettext import gettext as _ import gobject import gtk from sugar.activity.widgets import ToolbarButton from sugar.graphics.toolbarbox import ToolbarBox from sugar.graphics.toolbutton import ToolButton from sugar.graphics.toggletoolbutton import ToggleToolButton from sugar.graphics.radiotoolbutton import RadioToolButton from sugar.graphics.colorbutton import ColorToolButton from sugar.datastore import datastore from helpbutton import HelpButton class ViewToolbar(ToolbarButton): __gsignals__ = {'zoom-in': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, tuple()), 'zoom-out': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, tuple()), 'zoom-reset': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, tuple()), 'scale-range': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, tuple()), 'fullscreen': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, tuple()), 'connect-points': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, (gobject.TYPE_BOOLEAN,)), 'decimal-scale': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, tuple()), 'radian-scale': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, tuple()), 'custom-scale': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, tuple()), 'scale': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, tuple()), 'show-grid': (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, (gobject.TYPE_BOOLEAN,))} def __init__(self): ToolbarButton.__init__(self) self.props.icon_name = 'toolbar-view' self.toolbar = gtk.Toolbar() self.props.page = self.toolbar self.setup_scale_range() separator = gtk.SeparatorToolItem() separator.show() self.toolbar.insert(separator, -1) show_grid = ToggleToolButton('insert-table') #FIXME: Or correctly said, FIX SUGAR TOOLKIT. # Need accelerator for ToggleToolButton. #show_grid.props.accelerator = 'G' show_grid.set_tooltip(_('Show grid')) show_grid.set_active(True) show_grid.connect('toggled', lambda w: self.emit('show-grid', w.get_active())) show_grid.show() self.toolbar.insert(show_grid, -1) self.setup_connect_points() separator = gtk.SeparatorToolItem() separator.show() self.toolbar.insert(separator, -1) self.setup_scale_style() separator = gtk.SeparatorToolItem() separator.show() self.toolbar.insert(separator, -1) self.setup_zoom() fullscreen_button = ToolButton('view-fullscreen') fullscreen_button.props.tooltip = _('Fullscreen') fullscreen_button.props.accelerator = 'Return' fullscreen_button.connect('clicked', lambda w: self.emit('fullscreen')) fullscreen_button.show() self.toolbar.insert(fullscreen_button, -1) def setup_zoom(self): zoom_in_item = ToolButton('zoom-in') zoom_in_item.props.tooltip = _('Zoom in') zoom_in_item.connect('clicked', lambda w: self.emit('zoom-in')) zoom_in_item.props.accelerator = 'plus' zoom_in_item.show() self.toolbar.insert(zoom_in_item, -1) zoom_out_item = ToolButton('zoom-out') zoom_out_item.props.tooltip = _('Zoom out') zoom_out_item.props.accelerator = 'minus' zoom_out_item.connect('clicked', lambda w: self.emit('zoom-out')) zoom_out_item.show() self.toolbar.insert(zoom_out_item, -1) zoom_reset_item = ToolButton('zoom-original') zoom_reset_item.props.tooltip = _('Reset zoom') zoom_reset_item.props.accelerator = '0' zoom_reset_item.connect('clicked', lambda w: self.emit('zoom-reset')) zoom_reset_item.show() self.toolbar.insert(zoom_reset_item, -1) def setup_scale_range(self): scale_range = ToolButton('cell-size') scale_range.set_tooltip(_('Scale range')) scale_range.props.accelerator = 'R' range_palette = scale_range.get_palette() self.setup_scale_range_palette(range_palette) self.toolbar.insert(scale_range, -1) scale_range.connect('clicked', lambda w: range_palette.popup(immediate=True, state=1)) scale_range.show() def setup_scale_range_palette(self, palette): scale_range_table = gtk.Table(4, 2, False) x_min_label = gtk.Label(_('X min') + ' =') self.x_min_entry = gtk.Entry() self.x_min_entry.set_size_request(90, -1) self.x_min_entry.set_alignment(0) self.x_min_entry.connect('activate', lambda w: self.emit('scale-range')) scale_range_table.attach(x_min_label, 0, 1, 0, 1, xpadding=5) scale_range_table.attach(self.x_min_entry, 1, 2, 0, 1) x_max_label = gtk.Label(_('X max') + ' =') self.x_max_entry = gtk.Entry() self.x_max_entry.set_size_request(90, -1) self.x_max_entry.set_alignment(0) self.x_max_entry.connect('activate', lambda w: self.emit('scale-range')) scale_range_table.attach(x_max_label, 0, 1, 2, 3, xpadding=5) scale_range_table.attach(self.x_max_entry, 1, 2, 2, 3) y_min_label = gtk.Label(_('Y min') + ' =') self.y_min_entry = gtk.Entry() self.y_min_entry.set_size_request(90, -1) self.y_min_entry.set_alignment(0) self.y_min_entry.connect('activate', lambda w: self.emit('scale-range')) scale_range_table.attach(y_min_label, 2, 3, 0, 1, xpadding=5) scale_range_table.attach(self.y_min_entry, 3, 4, 0, 1) y_max_label = gtk.Label(_('Y max') + ' =') self.y_max_entry = gtk.Entry() self.y_max_entry.set_size_request(90, -1) self.y_max_entry.set_alignment(0) self.y_max_entry.connect('activate', lambda w: self.emit('scale-range')) scale_range_table.attach(y_max_label, 2, 3, 2, 3, xpadding=5) scale_range_table.attach(self.y_max_entry, 3, 4, 2, 3) palette.set_content(scale_range_table) scale_range_table.show_all() def get_scale_range(self): return [self.x_min_entry.get_text(), self.x_max_entry.get_text(), self.y_min_entry.get_text(), self.y_max_entry.get_text()] def setup_connect_points(self): connect_points_item = ToggleToolButton('connect-points') #connect_points_item.props.accelerator = 'P' connect_points_item.set_tooltip(_('Connect points')) connect_points_item.set_active(True) connect_points_item.connect('toggled', lambda w: self.emit('connect-points', connect_points_item.get_active())) connect_points_item.show() self.toolbar.insert(connect_points_item, -1) def get_scale(self): return self.x_scale_entry.get_text(), self.y_scale_entry.get_text() def setup_scale_style(self): decimal_item = RadioToolButton() decimal_item.set_named_icon('decimal') decimal_item.set_tooltip(_('Decimal scale')) decimal_item.props.accelerator = 'D' decimal_item.connect('toggled', lambda w: self.emit('decimal-scale')) decimal_item.show() self.toolbar.insert(decimal_item, -1) radians_item = RadioToolButton() radians_item.set_named_icon('radian') radians_item.props.accelerator = 'R' radians_item.set_tooltip(_('Radians scale')) radians_item.set_group(decimal_item) radians_item.connect('toggled', lambda w: self.emit('radian-scale')) radians_item.show() self.toolbar.insert(radians_item, -1) custom_item = RadioToolButton() custom_item.set_named_icon('custom') custom_item.set_tooltip(_('Custom scale')) custom_item.props.accelerator = 'C' custom_item.set_group(radians_item) custom_item.connect('toggled', lambda w: self.emit('custom-scale')) custom_item.show() scale_palette = custom_item.get_palette() self.setup_scale_palette(scale_palette) self.toolbar.insert(custom_item, -1) def setup_scale_palette(self, palette): scale_table = gtk.Table(2, 2, False) self.x_scale_entry = gtk.Entry() self.x_scale_entry.connect('activate', lambda w: self.emit('scale')) self.x_scale_entry.set_size_request(90, -1) self.y_scale_entry = gtk.Entry() self.y_scale_entry.connect('activate', lambda w: self.emit('scale')) self.y_scale_entry.set_size_request(90, -1) x_scale_label = gtk.Label(_('X scale')) x_scale_label.set_alignment(0, .5) y_scale_label = gtk.Label(_('Y scale')) y_scale_label.set_alignment(0, .5) scale_table.attach(x_scale_label, 0, 1, 0, 1, xpadding=5) scale_table.attach(self.x_scale_entry, 1, 2, 0, 1) scale_table.attach(y_scale_label, 0, 1, 1, 2, xpadding=5) scale_table.attach(self.y_scale_entry, 1, 2, 1, 2) palette.set_content(scale_table) scale_table.show_all() class GraphPlotterToolbarBox(ToolbarBox): __gsignals__ = {'append-function': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, tuple()), 'remove-function': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, tuple()), 'color-updated': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, (gobject.TYPE_PYOBJECT,)), 'evaluate': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, (gobject.TYPE_STRING,)), 'save-png': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, (gobject.TYPE_STRING,))} def __init__(self, activity_button, stop_button, activity): ToolbarBox.__init__(self) self.activity = activity activity_button.page.share.hide() export_as_png = ToolButton('save-as-image') export_as_png.props.tooltip = _('Save as image') export_as_png.connect('clicked', self.export_as_png) export_as_png.show() activity_button.page.insert(export_as_png, -1) self.toolbar.insert(activity_button, 0) activity_button.show() self.view_toolbar = ViewToolbar() self.view_toolbar.show() self.toolbar.insert(self.view_toolbar, -1) add_function = ToolButton('gtk-add') add_function.props.accelerator = 'A' add_function.set_tooltip(_('Append a new function')) add_function.connect('clicked', lambda w: self.emit('append-function')) add_function.show() self.toolbar.insert(add_function, -1) remove_function = ToolButton('gtk-remove') remove_function.props.accelerator = 'E' remove_function.set_tooltip(_('Remove the selected function')) remove_function.connect('clicked', lambda w: self.emit('remove-function')) remove_function.show() self.toolbar.insert(remove_function, -1) self.line_color_btn = ColorToolButton() self.line_color_btn.set_title(_('Plot color')) self.line_color_btn.props.accelerator = 'O' self.line_color_btn.connect('notify::color', lambda w, pspec: self.emit('color-updated', w.get_color())) self.line_color_btn.show_all() self.toolbar.insert(self.line_color_btn, -1) self.setup_help_button() separator = gtk.SeparatorToolItem() separator.show() separator.set_expand(True) separator.set_draw(False) self.toolbar.insert(separator, -1) evaluate_label = gtk.Label('x = ') evaluate_label.show() evaluate_label_item = gtk.ToolItem() evaluate_label_item.add(evaluate_label) evaluate_label_item.show() self.toolbar.insert(evaluate_label_item, -1) self.evaluate_entry = gtk.Entry() self.evaluate_entry.set_size_request(150, -1) self.evaluate_entry.connect('changed', lambda w: self.emit('evaluate', w.get_text())) self.evaluate_entry.show() evaluate_entry_item = gtk.ToolItem() evaluate_entry_item.add(self.evaluate_entry) evaluate_entry_item.show() self.toolbar.insert(evaluate_entry_item, -1) self.separator = gtk.SeparatorToolItem() self.separator.show() self.toolbar.insert(self.separator, -1) vbox = gtk.VBox() self.xlabel = gtk.Label() self.xlabel.set_alignment(0.5, 0.5) self.xlabel.modify_fg(gtk.STATE_NORMAL, gtk.gdk.color_parse('#FFFFFF')) self.xlabel.show() vbox.pack_start(self.xlabel, True, True, 0) self.ylabel = gtk.Label() self.ylabel.set_alignment(0.5, 0.5) self.ylabel.modify_fg(gtk.STATE_NORMAL, gtk.gdk.color_parse('#FFFFFF')) self.ylabel.show() vbox.pack_start(self.ylabel, True, True, 0) vbox.show() item = gtk.ToolItem() item.add(vbox) item.show() self.toolbar.insert(item, -1) separator = gtk.SeparatorToolItem() separator.set_draw(False) separator.set_expand(True) separator.show() self.toolbar.insert(separator, -1) self.toolbar.insert(stop_button, -1) stop_button.show() def export_as_png(self, widget): jobject = datastore.create() jobject.metadata['title'] = self.activity.metadata["title"] jobject.metadata['mime_type'] = "image/png" self.emit('save-png', '/tmp/graph-plotter.png') jobject.set_file_path('/tmp/graph-plotter.png') datastore.write(jobject) def setup_help_button(self): helpbutton = HelpButton() helpbutton.add_section(_('Plotting functions')) helpbutton.add_paragraph(_('Append a new function.'), 'gtk-add') helpbutton.add_paragraph(_('Remove a function.'), 'gtk-remove') helpbutton.add_paragraph( _('Choose the color for the selected function.'), 'color-preview') helpbutton.add_section(_('Watching the graph')) helpbutton.add_paragraph(_('Zoom into the graph.'), 'zoom-in') helpbutton.add_paragraph(_('Zoom out from the graph.'), 'zoom-out') helpbutton.add_paragraph(_('Reset zoom level.'), 'zoom-original') helpbutton.add_paragraph(_('Customize the scale range. \n\ Remember to press enter after editing the entries.'), 'cell-size') helpbutton.add_paragraph( _('You can zoom in directly into the graph by selecting an area to expand')) helpbutton.add_paragraph( _('Hold the Shift key and move the mouse to traverse the graph.')) helpbutton.add_paragraph( _('Do click for switch between the mouse pointing modes.')) helpbutton.add_section(_('Some settings')) helpbutton.add_paragraph(_('Connect the plotted points.'), 'connect-points') helpbutton.add_paragraph(_('Show the grid.'), 'insert-table') helpbutton.add_section( _('From algebraic expressions to linear expressions')) helpbutton.add_paragraph(_('Multiplication: ') + '*') helpbutton.add_paragraph(_('Division: ') + '/') helpbutton.add_paragraph(_('Integer division: ') + '//') helpbutton.add_paragraph(_('Power: ') + '^') helpbutton.show_all() self.toolbar.insert(helpbutton, -1)