diff options
author | Walter Bender <walter.bender@gmail.com> | 2012-10-03 11:42:54 (GMT) |
---|---|---|
committer | Walter Bender <walter.bender@gmail.com> | 2012-10-03 11:42:54 (GMT) |
commit | c711bd0a4392ff8bb66d1b9a9ffe6e48ba5c8c6b (patch) | |
tree | 0a0fab376cda03882ca0d95ac877776d54251282 | |
parent | f0b6e0d1d3ca19acd760de3c95acde290e136cfa (diff) |
rebase to Chart code; add new charts for journal data
-rw-r--r-- | NEWS | 6 | ||||
-rw-r--r-- | activity.py | 666 | ||||
-rw-r--r-- | activity/activity.info | 5 | ||||
-rw-r--r-- | charthelp.py | 30 | ||||
-rw-r--r-- | charts.py | 34 | ||||
-rw-r--r-- | help.py | 17 | ||||
-rw-r--r-- | helpbutton.py | 10 | ||||
-rw-r--r-- | icons/hbar.svg | 66 | ||||
-rw-r--r-- | icons/import-freespace.svg | 114 | ||||
-rw-r--r-- | icons/import-journal.svg | 147 | ||||
-rw-r--r-- | icons/import-turtle.svg | 158 | ||||
-rw-r--r-- | icons/line.svg | 34 | ||||
-rw-r--r-- | icons/pie.svg | 48 | ||||
-rw-r--r-- | icons/save-as-image.svg | 116 | ||||
-rw-r--r-- | icons/vbar.svg | 66 | ||||
-rw-r--r-- | readers.py | 340 | ||||
-rw-r--r-- | sugarpycha/__init__.py (renamed from pycha/__init__.py) | 0 | ||||
-rw-r--r-- | sugarpycha/bar.py (renamed from pycha/bar.py) | 15 | ||||
-rw-r--r-- | sugarpycha/chart.py (renamed from pycha/chart.py) | 45 | ||||
-rw-r--r-- | sugarpycha/color.py (renamed from pycha/color.py) | 8 | ||||
-rw-r--r-- | sugarpycha/line.py (renamed from pycha/line.py) | 5 | ||||
-rw-r--r-- | sugarpycha/pie.py (renamed from pycha/pie.py) | 26 | ||||
-rw-r--r-- | sugarpycha/polygonal.py (renamed from pycha/polygonal.py) | 8 | ||||
-rw-r--r-- | sugarpycha/radial.py (renamed from pycha/radial.py) | 8 | ||||
-rw-r--r-- | sugarpycha/scatter.py (renamed from pycha/scatter.py) | 2 | ||||
-rw-r--r-- | sugarpycha/stackedbar.py (renamed from pycha/stackedbar.py) | 8 | ||||
-rw-r--r-- | sugarpycha/utils.py (renamed from pycha/utils.py) | 1 | ||||
-rw-r--r-- | utils.py | 103 |
28 files changed, 1864 insertions, 222 deletions
@@ -1,3 +1,9 @@ +2 + +ENHANCEMENTS: +* Add chart of Sugar Activity usage +* Add chart of Turtle Block usage + 1 Initial release. diff --git a/activity.py b/activity.py index 3ff0edc..d7f2e9d 100644 --- a/activity.py +++ b/activity.py @@ -2,8 +2,10 @@ # -*- coding: utf-8 -*- # activity.py by: -# Agustin Zubiaga <aguz@sugarlabs.com> -# Rafael Ortiz <dirakx@gmail.com> +# Agustin Zubiaga <aguz@sugarlabs.org> +# Gonzalo Odiard <godiard@gmail.com> +# Manuel QuiƱones <manuq@laptop.org> +# Walter Bender <walter@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 @@ -20,221 +22,631 @@ # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA import gtk +import gobject +import pango import os -import statvfs +import simplejson +import locale import logging -import pango +import utils +from StringIO import StringIO from gettext import gettext as _ -from sugar import env -from sugar import profile - from sugar.activity import activity -from sugar.bundle.activitybundle import ActivityBundle +from sugar.activity.widgets import ActivityToolbarButton from sugar.activity.widgets import StopButton +from sugar.activity.widgets import ToolbarButton from sugar.graphics.toolbarbox import ToolbarBox from sugar.graphics.toolbutton import ToolButton +from sugar.graphics.radiotoolbutton import RadioToolButton +from sugar.graphics.colorbutton import ColorToolButton +from sugar.graphics.objectchooser import ObjectChooser from sugar.graphics.icon import Icon +from sugar.graphics.alert import Alert +from sugar.datastore import datastore -import help from charts import Chart - -_logger = logging.getLogger('AnalyzeJournal-activity') +from readers import FreeSpaceReader +from readers import JournalReader +from readers import TurtleReader +import charthelp + +# GUI Colors +_COLOR1 = utils.get_user_fill_color() +_COLOR2 = utils.get_user_stroke_color() +_WHITE = gtk.gdk.color_parse("white") + +# Paths +_ACTIVITY_DIR = os.path.join(activity.get_activity_root(), "data/") +_CHART_FILE = utils.get_chart_file(_ACTIVITY_DIR) + +# Logging +_logger = logging.getLogger('analyze-journal-activity') _logger.setLevel(logging.DEBUG) logging.basicConfig() -color = profile.get_color() -_FILL_COLOR = color.get_fill_color() -_STROKE_COLOR = color.get_stroke_color() + +class ChartArea(gtk.DrawingArea): + + def __init__(self, parent): + """A class for Draw the chart""" + super(ChartArea, self).__init__() + self._parent = parent + self.add_events(gtk.gdk.EXPOSURE_MASK | gtk.gdk.VISIBILITY_NOTIFY_MASK) + self.connect("expose-event", self._expose_cb) + + target = [("text/plain", 0, 0)] + self.drag_dest_set(gtk.DEST_DEFAULT_ALL, target, + gtk.gdk.ACTION_COPY | gtk.gdk.ACTION_MOVE) + self.connect('drag_data_received', self._drag_data_received) + + def _expose_cb(self, widget, event): + context = self.window.cairo_create() + + xpos, ypos, width, height = self.get_allocation() + + # White Background: + context.rectangle(0, 0, width, height) + context.set_source_rgb(255, 255, 255) + context.fill() + + # Paint the chart: + chart_width = self._parent.current_chart.width + chart_height = self._parent.current_chart.height + + cxpos = xpos + width / 2 - chart_width / 2 + cypos = ypos + height / 2 - chart_height / 2 + + context.set_source_surface(self._parent.current_chart.surface, + cxpos, + cypos) + context.paint() + + def _drag_data_received(self, w, context, x, y, data, info, time): + if data and data.format == 8: + io_file = StringIO(data.data) + reader = ClipboardReader(io_file) + self._parent._graph_from_reader(reader) + context.finish(True, False, time) + else: + context.finish(False, False, time) class AnalyzeJournal(activity.Activity): def __init__(self, handle): - activity.Activity.__init__(self, handle, False) + + activity.Activity.__init__(self, handle, True) self.max_participants = 1 + # CHART_OPTIONS + + self.x_label = "" + self.y_label = "" + self.chart_color = utils.get_user_fill_color('str') + self.chart_line_color = utils.get_user_stroke_color('str') + self.current_chart = None + self.charts_area = None + self.chart_data = [] + # TOOLBARS toolbarbox = ToolbarBox() - activity_button = ToolButton(self) - bundle = ActivityBundle(activity.get_bundle_path()) - icon = Icon(file=bundle.get_icon(), xo_color=color) - activity_button.set_icon_widget(icon) + activity_button = ActivityToolbarButton(self) + activity_btn_toolbar = activity_button.page + + activity_btn_toolbar.title.connect('changed', self._set_chart_title) + + save_as_image = ToolButton("save-as-image") + save_as_image.connect("clicked", self._save_as_image) + save_as_image.set_tooltip(_("Save as image")) + activity_btn_toolbar.insert(save_as_image, -1) + + save_as_image.show() toolbarbox.toolbar.insert(activity_button, 0) - activity_button.show() + + import_freespace = ToolButton("import-freespace") + import_freespace.connect("clicked", self.__import_freespace_cb) + import_freespace.set_tooltip(_("Read Freespace data")) + toolbarbox.toolbar.insert(import_freespace, -1) + import_freespace.show() + + import_journal = ToolButton('import-journal') + import_journal.connect('clicked', self.__import_journal_cb) + import_journal.set_tooltip(_('Read Journal data')) + toolbarbox.toolbar.insert(import_journal, -1) + import_journal.show() + + import_turtle = ToolButton('import-turtle') + import_turtle.connect('clicked', self.__import_turtle_cb) + import_turtle.set_tooltip(_('Read Turtle data')) + toolbarbox.toolbar.insert(import_turtle, -1) + import_turtle.show() + + separator = gtk.SeparatorToolItem() + separator.set_draw(True) + separator.set_expand(False) + toolbarbox.toolbar.insert(separator, -1) + + add_vbar_chart = RadioToolButton() + add_vbar_chart.connect("clicked", self._add_chart_cb, "vbar") + add_vbar_chart.set_tooltip(_("Vertical Bar Chart")) + add_vbar_chart.props.icon_name = "vbar" + charts_group = add_vbar_chart + toolbarbox.toolbar.insert(add_vbar_chart, -1) + + add_hbar_chart = RadioToolButton() + add_hbar_chart.connect("clicked", self._add_chart_cb, "hbar") + add_hbar_chart.set_tooltip(_("Horizontal Bar Chart")) + add_hbar_chart.props.icon_name = "hbar" + add_hbar_chart.props.group = charts_group + toolbarbox.toolbar.insert(add_hbar_chart, -1) + + add_pie_chart = RadioToolButton() + add_pie_chart.connect("clicked", self._add_chart_cb, "pie") + add_pie_chart.set_tooltip(_("Pie Chart")) + add_pie_chart.props.icon_name = "pie" + add_pie_chart.props.group = charts_group + add_pie_chart.set_active(True) + toolbarbox.toolbar.insert(add_pie_chart, -1) + + self.chart_type_buttons = [add_vbar_chart, + add_hbar_chart, + add_pie_chart] separator = gtk.SeparatorToolItem() separator.set_draw(True) separator.set_expand(False) toolbarbox.toolbar.insert(separator, -1) - update_btn = ToolButton('gtk-refresh') - update_btn.connect('clicked', self._analyze) - toolbarbox.toolbar.insert(update_btn, -1) + fullscreen_btn = ToolButton('view-fullscreen') + fullscreen_btn.set_tooltip(_('Fullscreen')) + fullscreen_btn.connect("clicked", self.__fullscreen_cb) + + toolbarbox.toolbar.insert(fullscreen_btn, -1) + + charthelp.create_help(toolbarbox.toolbar) separator = gtk.SeparatorToolItem() separator.set_draw(False) separator.set_expand(True) toolbarbox.toolbar.insert(separator, -1) - help.create_help(toolbarbox.toolbar) - stopbtn = StopButton(self) toolbarbox.toolbar.insert(stopbtn, -1) - toolbarbox.show_all() - self.set_toolbar_box(toolbarbox) - # CHART - self.chart = None - self.chart_data = [] - # CANVAS - self.area = Area(self) - self.set_canvas(self.area) + paned = gtk.HPaned() + box = gtk.VBox() + self.box = box + + # Set the info box width to 1/3 of the screen: + def size_allocate_cb(widget, allocation): + paned.disconnect(self._setup_handle) + box_width = allocation.width / 3 + box.set_size_request(box_width, -1) + + self._setup_handle = paned.connect('size_allocate', + size_allocate_cb) + + scroll = gtk.ScrolledWindow() + scroll.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC) + self.labels_and_values = ChartData(self) + scroll.add(self.labels_and_values) - self.area.show_all() + self.labels_and_values.connect("label-changed", self._label_changed) + self.labels_and_values.connect("value-changed", self._value_changed) + + box.pack_start(scroll, True, True, 0) + + paned.add1(box) + + # CHARTS AREA + + eventbox = gtk.EventBox() + self.charts_area = ChartArea(self) + self.charts_area.connect('size_allocate', self._chart_size_allocate) + + eventbox.modify_bg(gtk.STATE_NORMAL, _WHITE) + + eventbox.add(self.charts_area) + paned.add2(eventbox) + + self.set_canvas(paned) - # ANALYZE self.show_all() - self._analyze(None) - def _analyze(self, widget): + def _add_chart_cb(self, widget, type="vbar"): + self.current_chart = Chart(type) + + self.update_chart() + + def _chart_size_allocate(self, widget, allocation): + self._render_chart() + + def unfullscreen(self): + self.box.show() + activity.Activity.unfullscreen(self) + + def __fullscreen_cb(self, button): + self.box.hide() + self._render_chart(fullscreen=True) + activity.Activity.fullscreen(self) + + def _render_chart(self, fullscreen=False): + if self.current_chart is None or self.charts_area is None: + return + + try: + # Resize the chart for all the screen sizes + xpos, ypos, width, height = self.get_allocation() + + if fullscreen: + new_width = width + new_height = height + + if not fullscreen: + sxpos, sypos, width, height = self.charts_area.get_allocation() + + new_width = width - 40 + new_height = height - 40 + + self.current_chart.width = new_width + self.current_chart.height = new_height + + # Set options + self.current_chart.set_color_scheme(color=self.chart_color) + self.current_chart.set_line_color(self.chart_line_color) + + if self.current_chart.type == "pie": + self.current_chart.render(self) + else: + self.current_chart.render() + self.charts_area.queue_draw() + + except (ZeroDivisionError, ValueError): + pass + + return False + + def _update_chart_data(self): + if self.current_chart is None: + return + self.current_chart.data_set(self.chart_data) + self._update_chart_labels() + + def _set_chart_title(self, widget): + self._update_chart_labels(title=widget.get_text()) + + def _update_chart_labels(self, title=""): + if self.current_chart is None: + return + + if not title and self.metadata["title"]: + title = self.metadata["title"] + + self.current_chart.set_title(title) + self.current_chart.set_x_label(self.x_label) + self.current_chart.set_y_label(self.y_label) + self._render_chart() + + def update_chart(self): + if self.current_chart: + self.current_chart.data_set(self.chart_data) + self.current_chart.set_title(self.metadata["title"]) + self.current_chart.set_x_label(self.x_label) + self.current_chart.set_y_label(self.y_label) + self._render_chart() + + def _label_changed(self, treeview, path, new_label): + path = int(path) + self.chart_data[path] = (new_label, self.chart_data[path][1]) + self._update_chart_data() + + def _value_changed(self, treeview, path, new_value): + path = int(path) + self.chart_data[path] = (self.chart_data[path][0], float(new_value)) + self._update_chart_data() + + def _set_h_label(self, widget): + new_text = widget.get_text() + + if new_text != self.h_label._text: + self.x_label = new_text + self._update_chart_labels() + + def _set_v_label(self, widget): + new_text = widget.get_text() + + if new_text != self.v_label._text: + self.y_label = new_text + self._update_chart_labels() + + def _set_chart_color(self, widget, pspec): + self.chart_color = utils.rgb2html(widget.get_color()) + self._render_chart() + + def _set_chart_line_color(self, widget, pspec): + self.chart_line_color = utils.rgb2html(widget.get_color()) + self._render_chart() + + def _object_chooser(self, mime_type, type_name): + chooser = ObjectChooser() + matches_mime_type = False + + response = chooser.run() + if response == gtk.RESPONSE_ACCEPT: + jobject = chooser.get_selected_object() + metadata = jobject.metadata + file_path = jobject.file_path + + if metadata['mime_type'] == mime_type: + matches_mime_type = True + + else: + alert = Alert() + + alert.props.title = _('Invalid object') + alert.props.msg = \ + _('The selected object must be a %s file' % (type_name)) + + ok_icon = Icon(icon_name='dialog-ok') + alert.add_button(gtk.RESPONSE_OK, _('Ok'), ok_icon) + ok_icon.show() + + alert.connect('response', lambda a, r: self.remove_alert(a)) + + self.add_alert(alert) + + alert.show() + + return matches_mime_type, file_path, metadata['title'] + + def _graph_from_reader(self, reader): + self.labels_and_values.model.clear() self.chart_data = [] - free_space, used_space, total_space = self._get_space() + chart_data = reader.get_chart_data() + horizontal, vertical = reader.get_labels_name() + + # Load the data + for row in chart_data: + self._add_value(None, + label=row[0], value=float(row[1])) + + self.update_chart() - # Graph - self.chart_data.append((_('Free'), free_space)) - self.chart_data.append((_('Used'), used_space)) + def _add_value(self, widget, label="", value="0.0"): + data = (label, float(value)) + if not data in self.chart_data: + pos = self.labels_and_values.add_value(label, value) + self.chart_data.insert(pos, data) + self._update_chart_data() - self.chart = Chart() - self.chart.data_set(self.chart_data) - self.chart.set_type('pie') - self._resize_chart() - self.chart.render(self) + def _remove_value(self, widget): + path = self.labels_and_values.remove_selected_value() + del self.chart_data[path] + self._update_chart_data() - # Set info - f_type, t_type, u_type = 'MBs', 'MBs', 'MBs' + def __import_freespace_cb(self, widget): + reader = FreeSpaceReader() + self._graph_from_reader(reader) - if free_space >= 1024: - free_space = self._get_GBs(free_space) - f_type = 'GBs' + def __import_journal_cb(self, widget): + reader = JournalReader() + self._graph_from_reader(reader) - if total_space >= 1024: - total_space = self._get_GBs(total_space) - t_type = 'GBs' + def __import_turtle_cb(self, widget): + matches_mime_type, file_path, title = self._object_chooser( + 'application/x-turtle-art', _('Turtle')) + if matches_mime_type: + reader = TurtleReader(file_path) + self._graph_from_reader(reader) - if used_space >= 1024: - used_space = self._get_GBs(used_space) - u_type = 'GBs' + def _save_as_image(self, widget): + if self.current_chart: + jobject = datastore.create() - t = '<span foreground="%s"><b>%s</b></span>' % \ - (_FILL_COLOR, self._get_info_string('t')) + jobject.metadata['title'] = self.metadata["title"] + jobject.metadata['mime_type'] = "image/png" - ts = '<span foreground="%s"><b>%s</b></span> %s %s' % \ - (_STROKE_COLOR, self._get_info_string('ts'), total_space, t_type) + self.current_chart.as_png(_CHART_FILE) + jobject.set_file_path(_CHART_FILE) - us = '<span foreground="%s"><b>%s</b></span> %s %s' % \ - (_STROKE_COLOR, self._get_info_string('us'), used_space, u_type) + datastore.write(jobject) - fs = '<span foreground="%s"><b>%s</b></span> %s %s' % \ - (_STROKE_COLOR, self._get_info_string('fs'), free_space, f_type) + def load_from_file(self, f): + try: + data = simplejson.load(f) + finally: + f.close() - info = t + '\n' + ts + '\n' + us + '\n' + fs + self.metadata["title"] = data['title'] + self.x_label = data['x_label'] + self.y_label = data['y_label'] + self.chart_color = data['chart_color'] + self.chart_line_color = data['chart_line_color'] + self.current_chart.type = data['current_chart.type'] + chart_data = data['chart_data'] - self.area.text = info + # Update charts buttons + _type = data["current_chart.type"] + if _type == "vbar": + self.chart_type_buttons[0].set_active(True) - self.area.queue_draw() + elif _type == "hbar": + self.chart_type_buttons[1].set_active(True) - def _get_info_string(self, string): - if string == 't': - return _('Info:') + elif _type == "line": + self.chart_type_buttons[2].set_active(True) - elif string == 'ts': - return _('Total space:') + elif _type == "pie": + self.chart_type_buttons[3].set_active(True) - elif string == 'us': - return _('Used space:') + #load the data + for row in chart_data: + self._add_value(None, label=row[0], value=float(row[1])) - elif string == 'fs': - return _('Free space:') + self.update_chart() - def _resize_chart(self): - sx, sy, width, height = self.area.get_allocation() + def write_file(self, file_path): + self.metadata['mime_type'] = "application/x-chart-activity" + if self.current_chart: - new_width = width - 250 - new_height = height - 250 + data = {} + data['title'] = self.metadata["title"] + data['x_label'] = self.x_label + data['y_label'] = self.y_label + data['chart_color'] = self.chart_color + data['chart_line_color'] = self.chart_line_color + data['current_chart.type'] = self.current_chart.type + data['chart_data'] = self.chart_data - self.chart.width = new_width - self.chart.height = new_height + f = open(file_path, 'w') + try: + simplejson.dump(data, f) + finally: + f.close() - def _get_space(self): - stat = os.statvfs(env.get_profile_path()) - free_space = stat[statvfs.F_BSIZE] * stat[statvfs.F_BAVAIL] - total_space = stat[statvfs.F_BSIZE] * stat[statvfs.F_BLOCKS] + def read_file(self, file_path): + f = open(file_path, 'r') + self.load_from_file(f) - free_space = self._get_MBs(free_space) - total_space = self._get_MBs(total_space) - used_space = total_space - free_space - _logger.info('Free space: %s/%s MBs' % (free_space, total_space)) +class ChartData(gtk.TreeView): - return free_space, used_space, total_space + __gsignals__ = { + 'label-changed': (gobject.SIGNAL_RUN_FIRST, None, [str, str], ), + 'value-changed': (gobject.SIGNAL_RUN_FIRST, None, [str, str], ), } - def _get_MBs(self, space): - space = space / (1024 * 1024) + def __init__(self, activity): - return space + gtk.TreeView.__init__(self) - def _get_GBs(self, space): - space = space / 1024 + self.model = gtk.ListStore(str, str) + self.set_model(self.model) - return space + # Label column + column = gtk.TreeViewColumn(_("Label")) + label = gtk.CellRendererText() + label.set_property('editable', True) + label.connect("edited", self._label_changed, self.model) -class Area(gtk.DrawingArea): + column.pack_start(label) + column.set_attributes(label, text=0) + self.append_column(column) - def __init__(self, parent): - super(Area, self).__init__() - self._parent = parent - self.add_events(gtk.gdk.EXPOSURE_MASK | gtk.gdk.VISIBILITY_NOTIFY_MASK) - self.connect('expose-event', self._expose_cb) + # Value column - self.text = '' + column = gtk.TreeViewColumn(_("Value")) + value = gtk.CellRendererText() + value.set_property('editable', True) + value.connect("edited", self._value_changed, self.model, activity) - pango_context = self.get_pango_context() - self.layout = pango.Layout(pango_context) - self.layout.set_font_description(pango.FontDescription('13')) + column.pack_start(value) + column.set_attributes(value, text=1) - def _expose_cb(self, widget, event): - context = self.window.cairo_create() - gc = self.window.new_gc() + self.append_column(column) + self.set_enable_search(False) - x, y, w, h = self.get_allocation() + self.show_all() - # White Background: - context.rectangle(0, 0, w, h) - context.set_source_rgb(255, 255, 255) - context.fill() + def add_value(self, label, value): + selected = self.get_selection().get_selected()[1] + if not selected: + path = 0 - # Paint the chart: - cw, ch = self._parent.chart.width, self._parent.chart.height - cy = y + h / 2 - ch / 2 + elif selected: + path = self.model.get_path(selected)[0] + 1 - context.set_source_surface(self._parent.chart.surface, x, cy) - context.paint() + _iter = self.model.insert(path, [label, value]) + + self.set_cursor(self.model.get_path(_iter), + self.get_column(1), + True) + + _logger.info("Added: %s, Value: %s" % (label, value)) + + return path + + def remove_selected_value(self): + path, column = self.get_cursor() + path = path[0] + + model, iter = self.get_selection().get_selected() + self.model.remove(iter) + + return path + + def _label_changed(self, cell, path, new_text, model): + _logger.info("Change '%s' to '%s'" % (model[path][0], new_text)) + model[path][0] = new_text + + self.emit("label-changed", str(path), new_text) - # Write the info - self.layout.set_markup(self.text) - lh = (self.layout.get_pixel_extents()[1][2] * 3) / 1000 + def _value_changed(self, cell, path, new_text, model, activity): + _logger.info("Change '%s' to '%s'" % (model[path][1], new_text)) + is_number = True + number = new_text.replace(",", ".") + try: + float(number) + except ValueError: + is_number = False + + if is_number: + decimals = utils.get_decimals(str(float(number))) + new_text = locale.format('%.' + decimals + 'f', float(number)) + model[path][1] = str(new_text) + + self.emit("value-changed", str(path), number) + + elif not is_number: + alert = Alert() + + alert.props.title = _('Invalid Value') + alert.props.msg = \ + _('The value must be a number (integer or decimal)') + + ok_icon = Icon(icon_name='dialog-ok') + alert.add_button(gtk.RESPONSE_OK, _('Ok'), ok_icon) + ok_icon.show() + + alert.connect('response', lambda a, r: activity.remove_alert(a)) + + activity.add_alert(alert) + + alert.show() + + +class Entry(gtk.ToolItem): + + def __init__(self, text): + gtk.ToolItem.__init__(self) + + self.entry = gtk.Entry() + self.entry.set_text(text) + self.entry.connect("focus-in-event", self._focus_in) + self.entry.connect("focus-out-event", self._focus_out) + self.entry.modify_font(pango.FontDescription("italic")) + + self._text = text + + self.add(self.entry) + + self.show_all() - lx = x + cw - ly = y + h / 2 - lh / 2 + def _focus_in(self, widget, event): + if widget.get_text() == self._text: + widget.set_text("") + widget.modify_font(pango.FontDescription("")) - self.window.draw_layout(gc, lx, ly, self.layout) + def _focus_out(self, widget, event): + if widget.get_text() == "": + widget.set_text(self.text) + widget.modify_font(pango.FontDescription("italic")) diff --git a/activity/activity.info b/activity/activity.info index d7dcd15..c871038 100644 --- a/activity/activity.info +++ b/activity/activity.info @@ -1,7 +1,8 @@ [Activity] name = Analyze Journal -activity_version = 1 +activity_version = 2 bundle_id = org.sugarlabs.AnalyzeJournal -exec = sugar-activity activity.AnalyzeJournal -s +exec = sugar-activity activity.AnalyzeJournal icon = analyzejournal license = GPLv3+ +summary = chart of Sugar Journal activity diff --git a/charthelp.py b/charthelp.py new file mode 100644 index 0000000..6150141 --- /dev/null +++ b/charthelp.py @@ -0,0 +1,30 @@ +# Help for ChartActivity + +from gettext import gettext as _ + +from helpbutton import HelpButton + + +def create_help(toolbar): + helpitem = HelpButton() + toolbar.insert(helpitem, -1) + helpitem.show() + helpitem.add_section(_('Basic usage')) + helpitem.add_paragraph(_('First select data type:')) + helpitem.add_paragraph(_('The free space in the Journal;'), + 'import-freespace') + helpitem.add_paragraph(_('The types of Sugar Activities you have used;'), + 'import-journal') + helpitem.add_paragraph(_('The types of blocks used in Turtle Art.'), + 'import-turtle') + helpitem.add_paragraph(_('The graph title is the same as the Activity title')) + + helpitem.add_paragraph(_('You can change the type of graph:')) + helpitem.add_paragraph(_('Vertical bars'), 'vbar') + helpitem.add_paragraph(_('Horizontal bars'), 'hbar') + helpitem.add_paragraph(_('Lines'), 'line') + helpitem.add_paragraph(_('Pie'), 'pie') + + helpitem.add_section(_('Saving as an image')) + helpitem.add_paragraph(_('In the activity toolbar you have button to save the graph as an image'), + 'save-as-image') @@ -20,9 +20,9 @@ # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -import pycha.bar -import pycha.line -import pycha.pie +import sugarpycha.bar +import sugarpycha.line +import sugarpycha.pie import cairo import gobject @@ -49,7 +49,11 @@ class Chart(gobject.GObject): self.options = { 'legend': {'hide': True}, + 'titleFontSize': 16, 'axis': { + 'tickFontSize': 12, + 'labelFontSize': 14, + 'lineColor': '#b3b3b3', 'x': { 'ticks': [dict(v=i, label=l[0]) for i, l in enumerate(data)], @@ -60,9 +64,12 @@ class Chart(gobject.GObject): 'label': 'Y', } }, + 'stroke': { + 'width': 3 + }, 'background': { 'chartColor': '#FFFFFF', - 'lineColor': '#d1e5ec' + 'lineColor': '#CCCCCC' }, 'colorScheme': { 'name': 'gradient', @@ -76,9 +83,9 @@ class Chart(gobject.GObject): """Set the chart color scheme""" self.options["colorScheme"]["args"] = {'initialColor': color} - def set_line_color(self, color='#d1e5ec'): + def set_line_color(self, color='#000000'): """Set the chart line color""" - self.options["background"]["lineColor"] = color + self.options["stroke"]["color"] = color def set_x_label(self, text="X"): """Set the X Label""" @@ -92,7 +99,7 @@ class Chart(gobject.GObject): """Set chart type (vertical, horizontal, line, pie)""" self.type = type - def set_title(self, title="SimpleGraph Chart"): + def set_title(self, title="Chart"): """Set the chart title""" self.options["title"] = title @@ -104,20 +111,25 @@ class Chart(gobject.GObject): self.height) if self.type == "vbar": - chart = pycha.bar.VerticalBarChart(self.surface, self.options) + chart = sugarpycha.bar.VerticalBarChart(self.surface, self.options) elif self.type == "hbar": - chart = pycha.bar.HorizontalBarChart(self.surface, self.options) + chart = sugarpycha.bar.HorizontalBarChart(self.surface, + self.options) elif self.type == "line": - chart = pycha.line.LineChart(self.surface, self.options) + chart = sugarpycha.line.LineChart(self.surface, self.options) elif self.type == "pie": self.options["legend"] = {"hide": "False"} - chart = pycha.pie.PieChart(self.surface, self.options) + chart = sugarpycha.pie.PieChart(self.surface, self.options) self.dataSet = [(data[0], [[0, data[1]]]) for data in sg.chart_data] + else: + chart = sugarpycha.bar.HorizontalBarChart(self.surface, + self.options) + chart.addDataset(self.dataSet) chart.render() diff --git a/help.py b/help.py deleted file mode 100644 index cfdf38d..0000000 --- a/help.py +++ /dev/null @@ -1,17 +0,0 @@ -# Help for AnalizeJournal - -from gettext import gettext as _ - -from helpbutton import HelpButton - - -def create_help(toolbar): - helpitem = HelpButton() - toolbar.insert(helpitem, -1) - helpitem.add_section(_('Description')) - helpitem.add_paragraph(_('This activity gives you the possibility to graphically, the journal usage')) - helpitem.add_section(_('Usage')) - helpitem.add_paragraph(_('In the area you can view the info')) - helpitem.add_paragraph(_('You can update the data with this button'), - 'gtk-refresh') - helpitem.show() diff --git a/helpbutton.py b/helpbutton.py index c3e1569..639159d 100644 --- a/helpbutton.py +++ b/helpbutton.py @@ -39,14 +39,18 @@ class HelpButton(gtk.ToolItem): self._palette = help_button.get_palette() sw = gtk.ScrolledWindow() - sw.set_size_request(int(gtk.gdk.screen_width() / 3), - style.GRID_CELL_SIZE * 3) + sw.set_size_request(int(gtk.gdk.screen_width() / 2.8), + gtk.gdk.screen_height() - style.GRID_CELL_SIZE * 3) sw.set_policy(gtk.POLICY_NEVER, gtk.POLICY_AUTOMATIC) self._max_text_width = int(gtk.gdk.screen_width() / 3) - 20 self._vbox = gtk.VBox() self._vbox.set_homogeneous(False) - sw.add_with_viewport(self._vbox) + + hbox = gtk.HBox() + hbox.pack_start(self._vbox, False, True, 0) + + sw.add_with_viewport(hbox) self._palette.set_content(sw) sw.show_all() diff --git a/icons/hbar.svg b/icons/hbar.svg new file mode 100644 index 0000000..f042b84 --- /dev/null +++ b/icons/hbar.svg @@ -0,0 +1,66 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd" [ + <!ENTITY fill_color "#808080"> + <!ENTITY stroke_color "#FFFFFF"> +]> +<svg + xmlns="http://www.w3.org/2000/svg" + version="1.1" + width="62" + height="60" + viewBox="0 0 62 60" + id="svg2" + xml:space="preserve"><g + transform="translate(60.127119,11.292373)" + id="activity-browse" + style="display:block"> + + <g + transform="translate(-128.87712,-22.838983)" + id="g7" + style="display:inline"> + + + + + + + <g + transform="matrix(0.10822504,0,0,0.09945444,61.358446,34.085169)" + id="g6167"><g + transform="matrix(0,1.0881871,-0.9189596,0,426.39011,-308.03653)" + id="g3798" + style="fill:&fill_color;;fill-opacity:1;display:inline"><rect + width="81.443176" + height="272.19876" + rx="4.3524833" + ry="6.0284276" + x="373.97116" + y="55.900478" + id="rect2987" + style="color:#000000;fill:&fill_color;;fill-opacity:1;stroke:&stroke_color;;stroke-width:33.73588181;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" /><rect + width="81.443077" + height="360.47952" + rx="4.3524833" + ry="6.0284276" + x="499.32275" + y="-32.380226" + id="rect3757" + style="color:#000000;fill:&fill_color;;fill-opacity:1;stroke:&stroke_color;;stroke-width:33.73588181;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" /><rect + width="81.443077" + height="500.25723" + rx="4.3524833" + ry="6.0284276" + x="248.61969" + y="-172.15802" + id="rect3759" + style="color:#000000;fill:&fill_color;;fill-opacity:1;stroke:&stroke_color;;stroke-width:33.73588181;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" /><rect + width="81.443314" + height="215.79736" + rx="4.3524833" + ry="6.0284276" + x="123.26795" + y="112.30204" + id="rect3761" + style="color:#000000;fill:&fill_color;;fill-opacity:1;stroke:&stroke_color;;stroke-width:33.73588181;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" /></g></g></g> +</g></svg>
\ No newline at end of file diff --git a/icons/import-freespace.svg b/icons/import-freespace.svg new file mode 100644 index 0000000..19cf982 --- /dev/null +++ b/icons/import-freespace.svg @@ -0,0 +1,114 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<!-- Created with Inkscape (http://www.inkscape.org/) --> + +<svg + xmlns:dc="http://purl.org/dc/elements/1.1/" + xmlns:cc="http://creativecommons.org/ns#" + xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" + xmlns:svg="http://www.w3.org/2000/svg" + xmlns="http://www.w3.org/2000/svg" + xmlns:xlink="http://www.w3.org/1999/xlink" + version="1.1" + width="55" + height="55" + viewBox="0 0 55 55" + id="Layer_1" + xml:space="preserve"><metadata + id="metadata18"><rdf:RDF><cc:Work + rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type + rdf:resource="http://purl.org/dc/dcmitype/StillImage" /><dc:title></dc:title></cc:Work></rdf:RDF></metadata><defs + id="defs16"><linearGradient + id="linearGradient3775"><stop + id="stop3777" + style="stop-color:#3e3e3e;stop-opacity:1" + offset="0" /><stop + id="stop3779" + style="stop-color:#3e3e3e;stop-opacity:0" + offset="1" /></linearGradient><radialGradient + cx="26.6255" + cy="27.5" + r="20.991501" + fx="26.6255" + fy="27.5" + id="radialGradient3783" + xlink:href="#linearGradient3775" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(1,0,0,1.1075912,0,-2.9587571)" /> + + + + + + +<radialGradient + cx="26.6255" + cy="27.5" + r="20.991501" + fx="26.6255" + fy="27.5" + id="radialGradient3783-6" + xlink:href="#linearGradient3775-4" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(1,0,0,1.1075912,0,-2.9587571)" /><linearGradient + id="linearGradient3775-4"><stop + id="stop3777-0" + style="stop-color:#3e3e3e;stop-opacity:1" + offset="0" /><stop + id="stop3779-0" + style="stop-color:#3e3e3e;stop-opacity:0" + offset="1" /></linearGradient></defs> +<g + transform="translate(-4.1076373,0.46878985)" + id="g4139"><g + transform="translate(-2,0)" + id="g4135"><path + d="m 57.259649,14.549596 a 12.27034,11.60819 0 1 1 -24.54068,0 12.27034,11.60819 0 1 1 24.54068,0 z" + id="path6649" + style="color:#000000;fill:#999999;fill-opacity:1;fill-rule:nonzero;stroke:#ffffff;stroke-width:2;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" /><path + d="M 57.257798,14.347943 A 12.27034,11.608189 0 0 1 42.4122,25.89887 l 2.577109,-11.349274 z" + id="path6619" + style="color:#000000;fill:#666666;fill-opacity:1;fill-rule:nonzero;stroke:#ffffff;stroke-width:2;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" /></g><g + transform="matrix(0.55205508,0,0,0.55205508,49.574089,17.356986)" + id="g4382"><g + transform="translate(-80.093659,12.220029)" + id="g4308" + style="fill:none;stroke:#ffffff;stroke-opacity:1"><g + id="g4310" + style="fill:none;stroke:#ffffff;stroke-opacity:1"><path + d="m 6.736,49.002 h 24.52 c 2.225,0 3.439,-1.447 3.439,-3.441 v -27.28 c 0,-1.73 -1.732,-3.441 -3.439,-3.441 h -4.389" + id="path4312" + style="fill:none;stroke:#ffffff;stroke-width:3.5;stroke-linecap:round;stroke-linejoin:round;stroke-opacity:1" /></g></g><g + transform="translate(-80.093659,12.220029)" + id="g4314" + style="fill:none;stroke:#ffffff;stroke-opacity:1"><g + id="g4316" + style="fill:none;stroke:#ffffff;stroke-opacity:1"><path + d="m 26.867,38.592 c 0,1.836 -1.345,3.201 -3.441,4.047 L 6.736,49.002 V 14.84 l 16.69,-8.599 c 2.228,-0.394 3.441,0.84 3.441,2.834 v 29.517 z" + id="path4318" + style="fill:none;stroke:#ffffff;stroke-width:3.5;stroke-linecap:round;stroke-linejoin:round;stroke-opacity:1" /></g></g><path + d="m -70.669659,54.827029 c 0,0 -1.351,-0.543 -2.702,-0.543 -1.351,0 -2.703,0.543 -2.703,0.543" + id="path4320" + style="fill:none;stroke:#ffffff;stroke-width:2.25;stroke-linecap:round;stroke-linejoin:round;stroke-opacity:1" /><path + d="m -70.669659,44.226029 c 0,0 -1.239,-0.543 -2.815,-0.543 -1.577,0 -2.59,0.543 -2.59,0.543" + id="path4322" + style="fill:none;stroke:#ffffff;stroke-width:2.25;stroke-linecap:round;stroke-linejoin:round;stroke-opacity:1" /><path + d="m -70.669659,33.898029 c 0,0 -1.125,-0.544 -2.927,-0.544 -1.802,0 -2.478,0.544 -2.478,0.544" + id="path4324" + style="fill:none;stroke:#ffffff;stroke-width:2.25;stroke-linecap:round;stroke-linejoin:round;stroke-opacity:1" /><line + id="line4326" + y2="23.725029" + y1="58.753029" + x2="-66.884659" + x1="-66.884659" + style="fill:none;stroke:#ffffff;stroke-width:2.25;stroke-linecap:round;stroke-linejoin:round;stroke-opacity:1" /></g><g + transform="matrix(0,-1,-1,0,50.521049,88.007596)" + id="g4770-3"><g + transform="translate(34.0803,-1006.42)" + id="g4772-5"><polyline + id="polyline4774-1" + points="51.562,15.306 41.17,16.188 42.053,5.794" + style="fill:none;stroke:#ffffff;stroke-width:3.5;stroke-linecap:round;stroke-linejoin:round" + transform="matrix(-0.469241,0.469241,-0.469241,-0.469241,66.2906,1019.03)" /><path + d="m 39.363241,1033.1291 -0.05636,9.9115 -8.750608,0.067" + id="path4776-7" + style="fill:none;stroke:#ffffff;stroke-width:2.5;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" /></g></g></g></svg>
\ No newline at end of file diff --git a/icons/import-journal.svg b/icons/import-journal.svg new file mode 100644 index 0000000..0c13d10 --- /dev/null +++ b/icons/import-journal.svg @@ -0,0 +1,147 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<!-- Created with Inkscape (http://www.inkscape.org/) --> + +<svg + xmlns:dc="http://purl.org/dc/elements/1.1/" + xmlns:cc="http://creativecommons.org/ns#" + xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" + xmlns:svg="http://www.w3.org/2000/svg" + xmlns="http://www.w3.org/2000/svg" + version="1.1" + width="49.517162" + height="50.998993" + id="svg2"> + <defs + id="defs4" /> + <metadata + id="metadata7"> + <rdf:RDF> + <cc:Work + rdf:about=""> + <dc:format>image/svg+xml</dc:format> + <dc:type + rdf:resource="http://purl.org/dc/dcmitype/StillImage" /> + <dc:title></dc:title> + </cc:Work> + </rdf:RDF> + </metadata> + <g + transform="translate(-11.28338,3.0240822)" + id="g3103"> + <g + transform="matrix(0.55205508,0,0,0.55205508,58.11605,12.3324)" + id="g4382"> + <g + transform="translate(-80.093659,12.220029)" + id="g4308" + style="fill:none;stroke:#ffffff;stroke-opacity:1"> + <g + id="g4310" + style="fill:none;stroke:#ffffff;stroke-opacity:1"> + <path + d="m 6.736,49.002 h 24.52 c 2.225,0 3.439,-1.447 3.439,-3.441 v -27.28 c 0,-1.73 -1.732,-3.441 -3.439,-3.441 h -4.389" + id="path4312" + style="fill:none;stroke:#ffffff;stroke-width:3.5;stroke-linecap:round;stroke-linejoin:round;stroke-opacity:1" /> + </g> + </g> + <g + transform="translate(-80.093659,12.220029)" + id="g4314" + style="fill:none;stroke:#ffffff;stroke-opacity:1"> + <g + id="g4316" + style="fill:none;stroke:#ffffff;stroke-opacity:1"> + <path + d="m 26.867,38.592 c 0,1.836 -1.345,3.201 -3.441,4.047 L 6.736,49.002 V 14.84 l 16.69,-8.599 c 2.228,-0.394 3.441,0.84 3.441,2.834 v 29.517 z" + id="path4318" + style="fill:none;stroke:#ffffff;stroke-width:3.5;stroke-linecap:round;stroke-linejoin:round;stroke-opacity:1" /> + </g> + </g> + <path + d="m -70.669659,54.827029 c 0,0 -1.351,-0.543 -2.702,-0.543 -1.351,0 -2.703,0.543 -2.703,0.543" + id="path4320" + style="fill:none;stroke:#ffffff;stroke-width:2.25;stroke-linecap:round;stroke-linejoin:round;stroke-opacity:1" /> + <path + d="m -70.669659,44.226029 c 0,0 -1.239,-0.543 -2.815,-0.543 -1.577,0 -2.59,0.543 -2.59,0.543" + id="path4322" + style="fill:none;stroke:#ffffff;stroke-width:2.25;stroke-linecap:round;stroke-linejoin:round;stroke-opacity:1" /> + <path + d="m -70.669659,33.898029 c 0,0 -1.125,-0.544 -2.927,-0.544 -1.802,0 -2.478,0.544 -2.478,0.544" + id="path4324" + style="fill:none;stroke:#ffffff;stroke-width:2.25;stroke-linecap:round;stroke-linejoin:round;stroke-opacity:1" /> + <line + style="fill:none;stroke:#ffffff;stroke-width:2.25;stroke-linecap:round;stroke-linejoin:round;stroke-opacity:1" + x1="-66.884659" + x2="-66.884659" + y1="58.753029" + y2="23.725029" + id="line4326" /> + </g> + <g + transform="matrix(0,-1,-1,0,59.06301,82.98301)" + id="g4770-3"> + <g + transform="translate(34.0803,-1006.42)" + id="g4772-5"> + <polyline + transform="matrix(-0.469241,0.469241,-0.469241,-0.469241,66.2906,1019.03)" + style="fill:none;stroke:#ffffff;stroke-width:3.5;stroke-linecap:round;stroke-linejoin:round" + points="51.562,15.306 41.17,16.188 42.053,5.794" + id="polyline4774-1" /> + <path + d="m 39.363241,1033.1291 -0.05636,9.9115 -8.750608,0.067" + id="path4776-7" + style="fill:none;stroke:#ffffff;stroke-width:2.5;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" /> + </g> + </g> + <g + transform="matrix(0.55205508,0,0,0.55205508,80.6828,-11.330635)" + id="g4382-4"> + <g + transform="translate(-80.093659,12.220029)" + id="g4308-4" + style="fill:none;stroke:#ffffff;stroke-opacity:1"> + <g + id="g4310-7" + style="fill:none;stroke:#ffffff;stroke-opacity:1"> + <path + d="m 6.736,49.002 h 24.52 c 2.225,0 3.439,-1.447 3.439,-3.441 v -27.28 c 0,-1.73 -1.732,-3.441 -3.439,-3.441 h -4.389" + id="path4312-5" + style="fill:none;stroke:#ffffff;stroke-width:3.5;stroke-linecap:round;stroke-linejoin:round;stroke-opacity:1" /> + </g> + </g> + <g + transform="translate(-80.093659,12.220029)" + id="g4314-1" + style="fill:none;stroke:#ffffff;stroke-opacity:1"> + <g + id="g4316-6" + style="fill:none;stroke:#ffffff;stroke-opacity:1"> + <path + d="m 26.867,38.592 c 0,1.836 -1.345,3.201 -3.441,4.047 L 6.736,49.002 V 14.84 l 16.69,-8.599 c 2.228,-0.394 3.441,0.84 3.441,2.834 v 29.517 z" + id="path4318-2" + style="fill:none;stroke:#ffffff;stroke-width:3.5;stroke-linecap:round;stroke-linejoin:round;stroke-opacity:1" /> + </g> + </g> + <path + d="m -70.669659,54.827029 c 0,0 -1.351,-0.543 -2.702,-0.543 -1.351,0 -2.703,0.543 -2.703,0.543" + id="path4320-4" + style="fill:none;stroke:#ffffff;stroke-width:2.25;stroke-linecap:round;stroke-linejoin:round;stroke-opacity:1" /> + <path + d="m -70.669659,44.226029 c 0,0 -1.239,-0.543 -2.815,-0.543 -1.577,0 -2.59,0.543 -2.59,0.543" + id="path4322-2" + style="fill:none;stroke:#ffffff;stroke-width:2.25;stroke-linecap:round;stroke-linejoin:round;stroke-opacity:1" /> + <path + d="m -70.669659,33.898029 c 0,0 -1.125,-0.544 -2.927,-0.544 -1.802,0 -2.478,0.544 -2.478,0.544" + id="path4324-8" + style="fill:none;stroke:#ffffff;stroke-width:2.25;stroke-linecap:round;stroke-linejoin:round;stroke-opacity:1" /> + <line + style="fill:none;stroke:#ffffff;stroke-width:2.25;stroke-linecap:round;stroke-linejoin:round;stroke-opacity:1" + x1="-66.884659" + x2="-66.884659" + y1="58.753029" + y2="23.725029" + id="line4326-2" /> + </g> + </g> +</svg> diff --git a/icons/import-turtle.svg b/icons/import-turtle.svg new file mode 100644 index 0000000..d0869da --- /dev/null +++ b/icons/import-turtle.svg @@ -0,0 +1,158 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<!-- Created with Inkscape (http://www.inkscape.org/) --> + +<svg + xmlns:dc="http://purl.org/dc/elements/1.1/" + xmlns:cc="http://creativecommons.org/ns#" + xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" + xmlns:svg="http://www.w3.org/2000/svg" + xmlns="http://www.w3.org/2000/svg" + version="1.1" + width="55" + height="55" + viewBox="0 0 55 55" + id="svg2" + xml:space="preserve"><metadata + id="metadata25"><rdf:RDF><cc:Work + rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type + rdf:resource="http://purl.org/dc/dcmitype/StillImage" /><dc:title></dc:title></cc:Work></rdf:RDF></metadata><defs + id="defs33"> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +</defs><g + transform="matrix(0.55205508,0,0,0.55205508,44.618464,18.235971)" + id="g4382"><g + transform="translate(-80.093659,12.220029)" + id="g4308" + style="fill:none;stroke:#ffffff;stroke-opacity:1"> + <g + id="g4310" + style="fill:none;stroke:#ffffff;stroke-opacity:1"> + <path + d="m 6.736,49.002 h 24.52 c 2.225,0 3.439,-1.447 3.439,-3.441 v -27.28 c 0,-1.73 -1.732,-3.441 -3.439,-3.441 h -4.389" + id="path4312" + style="fill:none;stroke:#ffffff;stroke-width:3.5;stroke-linecap:round;stroke-linejoin:round;stroke-opacity:1" /> + </g> + </g><g + transform="translate(-80.093659,12.220029)" + id="g4314" + style="fill:none;stroke:#ffffff;stroke-opacity:1"> + <g + id="g4316" + style="fill:none;stroke:#ffffff;stroke-opacity:1"> + <path + d="m 26.867,38.592 c 0,1.836 -1.345,3.201 -3.441,4.047 L 6.736,49.002 V 14.84 l 16.69,-8.599 c 2.228,-0.394 3.441,0.84 3.441,2.834 v 29.517 z" + id="path4318" + style="fill:none;stroke:#ffffff;stroke-width:3.5;stroke-linecap:round;stroke-linejoin:round;stroke-opacity:1" /> + </g> + </g><path + d="m -70.669659,54.827029 c 0,0 -1.351,-0.543 -2.702,-0.543 -1.351,0 -2.703,0.543 -2.703,0.543" + id="path4320" + style="fill:none;stroke:#ffffff;stroke-width:2.25;stroke-linecap:round;stroke-linejoin:round;stroke-opacity:1" /><path + d="m -70.669659,44.226029 c 0,0 -1.239,-0.543 -2.815,-0.543 -1.577,0 -2.59,0.543 -2.59,0.543" + id="path4322" + style="fill:none;stroke:#ffffff;stroke-width:2.25;stroke-linecap:round;stroke-linejoin:round;stroke-opacity:1" /><path + d="m -70.669659,33.898029 c 0,0 -1.125,-0.544 -2.927,-0.544 -1.802,0 -2.478,0.544 -2.478,0.544" + id="path4324" + style="fill:none;stroke:#ffffff;stroke-width:2.25;stroke-linecap:round;stroke-linejoin:round;stroke-opacity:1" /><line + style="fill:none;stroke:#ffffff;stroke-width:2.25;stroke-linecap:round;stroke-linejoin:round;stroke-opacity:1" + x1="-66.884659" + x2="-66.884659" + y1="58.753029" + y2="23.725029" + id="line4326" /></g><g + id="g4084"><path + d="m 40.046106,34.391197 c -0.384814,0 -0.764757,-0.02436 -1.139134,-0.06959 l 0.965863,1.634591 0.951946,-1.60954 c -0.258167,0.02088 -0.515638,0.04454 -0.778675,0.04454 z" + id="path11" + style="fill:none;stroke:#ffffff;stroke-width:2.08759975;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" /><g + transform="matrix(0.69586655,0,0,0.69586655,20.911863,0.79545554)" + id="g13" + style="fill:none;stroke:#ffffff;stroke-width:3;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"><path + d="m 40.16,11.726 c -2.164,0 -3.958,1.555 -4.343,3.607 1.859,1.345 3.457,3.115 4.675,5.208 2.285,-0.172 4.094,-2.061 4.094,-4.39 0,-2.444 -1.982,-4.425 -4.426,-4.425 z" + id="path15" + style="fill:none;stroke:#ffffff;stroke-width:3;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" /><path + d="m 40.713,39.887 c -1.224,2.232 -2.86,4.131 -4.797,5.556 0.521,1.864 2.213,3.239 4.244,3.239 2.443,0 4.426,-1.98 4.426,-4.424 0,-2.255 -1.693,-4.096 -3.873,-4.371 z" + id="path17" + style="fill:none;stroke:#ffffff;stroke-width:3;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" /><path + d="m 14.273,39.871 c -2.253,0.206 -4.024,2.079 -4.024,4.387 0,2.443 1.98,4.424 4.424,4.424 2.064,0 3.784,-1.42 4.272,-3.332 -1.883,-1.416 -3.475,-3.289 -4.672,-5.479 z" + id="path19" + style="fill:none;stroke:#ffffff;stroke-width:3;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" /><path + d="m 19.026,15.437 c -0.343,-2.103 -2.154,-3.711 -4.353,-3.711 -2.444,0 -4.424,1.981 -4.424,4.424 0,2.382 1.886,4.31 4.245,4.406 1.186,-2.043 2.732,-3.784 4.532,-5.119 z" + id="path21" + style="fill:none;stroke:#ffffff;stroke-width:3;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" /></g><path + d="m 40.046106,9.537627 c 1.327713,0 2.59419,0.2860012 3.770205,0.784937 0.515637,-0.7487519 0.819731,-1.6540743 0.819731,-2.6324627 0,-2.5656599 -2.079945,-4.6463009 -4.646301,-4.6463009 -2.56566,0 -4.645605,2.080641 -4.645605,4.6463009 0,0.9936975 0.314531,1.9129372 0.846173,2.6679527 1.199674,-0.5212044 2.496074,-0.820427 3.855797,-0.820427 z" + id="path23" + style="fill:none;stroke:#ffffff;stroke-width:2.08759975;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" /><g + transform="matrix(0.69586655,0,0,0.69586655,20.911863,0.79545554)" + id="g25" + style="fill:none;stroke:#ffffff;stroke-width:3;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"><path + d="m 43.102,30.421 c 0,4.7344 -1.6452,9.2798 -4.5706,12.6275 -2.9254,3.3478 -6.8973,5.2305 -11.0344,5.2305 -4.1371,0 -8.109,-1.8827 -11.0344,-5.2305 -2.9254,-3.3477 -4.5706,-7.8931 -4.5706,-12.6275 0,-9.7966 7.0444,-17.858 15.605,-17.858 8.5606,0 15.605,8.0614 15.605,17.858 z" + id="path2988" + style="fill:none;stroke:#ffffff;stroke-width:3;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" /></g><g + transform="matrix(0.69586655,0,0,0.69586655,20.911863,0.79545554)" + id="g28" + style="fill:#ffffff;fill-opacity:1;stroke:none"><path + d="m 25.875,33.75 -1.542,-4.625 3.164,-2.587 3.615,2.626 -1.487,4.669 -3.75,-0.083 z" + id="path30" + style="fill:#ffffff;fill-opacity:1;stroke:none" /><path + d="m 27.501,41.551 c -3.968,-0.16 -5.543,-2.009 -5.543,-2.009 l 3.57,-4.163 4.465,0.168 3.132,4.12 c 0,0 -2.89,1.994 -5.624,1.884 z" + id="path32" + style="fill:#ffffff;fill-opacity:1;stroke:none" /><path + d="m 18.453,33.843 c -0.849,-2.968 0.172,-6.884 0.172,-6.884 l 4,2.167 1.493,4.629 -3.582,4.233 c 0,-10e-4 -1.465,-1.99 -2.083,-4.145 z" + id="path34" + style="fill:#ffffff;fill-opacity:1;stroke:none" /><path + d="m 19.458,25.125 c 0,0 0.5,-1.958 3.039,-3.822 2.237,-1.643 4.465,-1.72 4.465,-1.72 l -0.037,4.981 -3.521,2.75 -3.946,-2.189 z" + id="path2998" + style="fill:#ffffff;fill-opacity:1;stroke:none" /><path + d="M 32.084,27.834 28.625,24.959 29,19.75 c 0,0 1.834,-0.042 3.959,1.667 2.228,1.791 3.362,4.983 3.362,4.983 l -4.237,1.434 z" + id="path37" + style="fill:#ffffff;fill-opacity:1;stroke:none" /><path + d="m 31.292,34.042 1.313,-4.464 4.187,-1.536 c 0,0 0.677,2.663 -0.042,5.667 -0.54,2.256 -2.084,4.361 -2.084,4.361 l -3.374,-4.028 z" + id="path3002" + style="fill:#ffffff;fill-opacity:1;stroke:none" /></g></g><g + transform="matrix(0,-1,-1,0,43.9376,89.386573)" + id="g4770"><g + transform="translate(34.0803,-1006.42)" + id="g4772"><polyline + transform="matrix(-0.469241,0.469241,-0.469241,-0.469241,66.2906,1019.03)" + style="fill:none;stroke:#ffffff;stroke-width:3.5;stroke-linecap:round;stroke-linejoin:round" + points="51.562,15.306 41.17,16.188 42.053,5.794" + id="polyline4774" /><path + d="m 39.363241,1033.1291 -0.05636,9.9115 -8.750608,0.067" + id="path4776" + style="fill:none;stroke:#ffffff;stroke-width:2.5;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" /></g></g></svg>
\ No newline at end of file diff --git a/icons/line.svg b/icons/line.svg new file mode 100644 index 0000000..d6a1a5c --- /dev/null +++ b/icons/line.svg @@ -0,0 +1,34 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd" [ + <!ENTITY fill_color "#808080"> + <!ENTITY stroke_color "#FFFFFF"> +]> +<svg + xmlns="http://www.w3.org/2000/svg" + version="1.1" + width="62" + height="60" + viewBox="0 0 62 60" + id="svg2" + xml:space="preserve"><g + transform="translate(60.127119,11.292373)" + id="activity-browse" + style="display:block"> + + <g + transform="translate(-128.87712,-22.838983)" + id="g7" + style="display:inline"> + + + + + + + <g + transform="matrix(0.10822504,0,0,0.09945444,61.358446,34.085169)" + id="g6167"><path + d="m 594.07188,325.15231 0,-360.47952 L 434.51609,52.953494 274.96046,-175.105 115.4045,109.35506 l 0,215.79736 z" + id="rect3757" + style="color:#000000;fill:&fill_color;;fill-opacity:1;stroke:&stroke_color;;stroke-width:33.73588181;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" /></g></g> +</g></svg>
\ No newline at end of file diff --git a/icons/pie.svg b/icons/pie.svg new file mode 100644 index 0000000..6c4cd5f --- /dev/null +++ b/icons/pie.svg @@ -0,0 +1,48 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd" [ + <!ENTITY fill_color "#808080"> + <!ENTITY stroke_color "#FFFFFF"> +]> +<svg + xmlns="http://www.w3.org/2000/svg" + version="1.1" + width="62" + height="60" + viewBox="0 0 62 60" + id="svg2" + xml:space="preserve"><g + transform="translate(60.127119,11.292373)" + id="activity-browse" + style="display:block"> + + <g + transform="translate(-128.87712,-22.838983)" + id="g7" + style="display:inline"> + + + + + + + <g + transform="matrix(0.10822504,0,0,0.09945444,61.358446,34.085169)" + id="g6167"><g + id="g6651"><path + d="m 7.8813553,48.68644 a 27.584745,27.584745 0 1 1 -55.1694913,0 27.584745,27.584745 0 1 1 55.1694913,0 z" + transform="matrix(8.3764085,0,0,9.1150996,519.78133,-368.75859)" + id="path6649" + style="color:#000000;fill:#999999;fill-opacity:1;stroke:&stroke_color;;stroke-width:1.65464818;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" /><path + d="M -0.94352875,28.463043 A 27.584745,27.584745 0 1 1 -47.288136,48.686438 l 27.584746,2e-6 z" + transform="matrix(8.3764085,0,0,9.1150996,519.78133,-368.75859)" + id="path6617" + style="color:#000000;fill:&fill_color;;fill-opacity:1;stroke:&stroke_color;;stroke-width:1.65464818;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" /><path + d="M 7.8771928,48.20725 A 27.584745,27.584745 0 0 1 -25.496945,75.655921 L -19.70339,48.68644 z" + transform="matrix(8.3764085,0,0,9.1150996,519.78133,-368.75859)" + id="path6619" + style="color:#000000;fill:#666666;fill-opacity:1;stroke:&stroke_color;;stroke-width:1.65464818;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" /></g><path + d="m 7.8813553,48.68644 a 27.584745,27.584745 0 1 1 -55.1694913,0 27.584745,27.584745 0 1 1 55.1694913,0 z" + transform="matrix(8.3764085,0,0,9.1150996,519.78184,-368.75804)" + id="path6107" + style="color:#000000;fill:none;stroke:&stroke_color;;stroke-width:3.8608458;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" /></g></g> +</g></svg>
\ No newline at end of file diff --git a/icons/save-as-image.svg b/icons/save-as-image.svg new file mode 100644 index 0000000..365f578 --- /dev/null +++ b/icons/save-as-image.svg @@ -0,0 +1,116 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<!-- Created with Inkscape (http://www.inkscape.org/) --> + +<svg + xmlns:dc="http://purl.org/dc/elements/1.1/" + xmlns:cc="http://creativecommons.org/ns#" + xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" + xmlns:svg="http://www.w3.org/2000/svg" + xmlns="http://www.w3.org/2000/svg" + version="1.1" + width="55" + height="55" + viewBox="0 0 55 55" + id="svg2" + xml:space="preserve"><metadata + id="metadata25"><rdf:RDF><cc:Work + rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type + rdf:resource="http://purl.org/dc/dcmitype/StillImage" /><dc:title></dc:title></cc:Work></rdf:RDF></metadata><defs + id="defs33"> + + + + + + + + + + + + + + + + + + + + + + + + + </defs><g + transform="matrix(0.55205508,0,0,0.55205508,77.118464,18.235971)" + id="g4382"><g + transform="translate(-80.093659,12.220029)" + id="g4308" + style="fill:none;stroke:#ffffff;stroke-opacity:1"> + <g + id="g4310" + style="fill:none;stroke:#ffffff;stroke-opacity:1"> + <path + d="m 6.736,49.002 h 24.52 c 2.225,0 3.439,-1.447 3.439,-3.441 v -27.28 c 0,-1.73 -1.732,-3.441 -3.439,-3.441 h -4.389" + id="path4312" + style="fill:none;stroke:#ffffff;stroke-width:3.5;stroke-linecap:round;stroke-linejoin:round;stroke-opacity:1" /> + </g> + </g><g + transform="translate(-80.093659,12.220029)" + id="g4314" + style="fill:none;stroke:#ffffff;stroke-opacity:1"> + <g + id="g4316" + style="fill:none;stroke:#ffffff;stroke-opacity:1"> + <path + d="m 26.867,38.592 c 0,1.836 -1.345,3.201 -3.441,4.047 L 6.736,49.002 V 14.84 l 16.69,-8.599 c 2.228,-0.394 3.441,0.84 3.441,2.834 v 29.517 z" + id="path4318" + style="fill:none;stroke:#ffffff;stroke-width:3.5;stroke-linecap:round;stroke-linejoin:round;stroke-opacity:1" /> + </g> + </g><path + d="m -70.669659,54.827029 c 0,0 -1.351,-0.543 -2.702,-0.543 -1.351,0 -2.703,0.543 -2.703,0.543" + id="path4320" + style="fill:none;stroke:#ffffff;stroke-width:2.25;stroke-linecap:round;stroke-linejoin:round;stroke-opacity:1" /><path + d="m -70.669659,44.226029 c 0,0 -1.239,-0.543 -2.815,-0.543 -1.577,0 -2.59,0.543 -2.59,0.543" + id="path4322" + style="fill:none;stroke:#ffffff;stroke-width:2.25;stroke-linecap:round;stroke-linejoin:round;stroke-opacity:1" /><path + d="m -70.669659,33.898029 c 0,0 -1.125,-0.544 -2.927,-0.544 -1.802,0 -2.478,0.544 -2.478,0.544" + id="path4324" + style="fill:none;stroke:#ffffff;stroke-width:2.25;stroke-linecap:round;stroke-linejoin:round;stroke-opacity:1" /><line + style="fill:none;stroke:#ffffff;stroke-width:2.25;stroke-linecap:round;stroke-linejoin:round;stroke-opacity:1" + x1="-66.884659" + x2="-66.884659" + y1="58.753029" + y2="23.725029" + id="line4326" /></g><g + transform="matrix(1.1623273,0,0,1.1623273,-14.422024,-12.63995)" + id="g3882" + style="fill:none;stroke:#ffffff;stroke-width:2.15085721;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;display:inline"><g + id="g3884" + style="fill:none;stroke:#ffffff;stroke-width:2.15085721;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"><polygon + points="43.041,21.577 35.281,13.812 15.204,13.812 15.204,35.189 43.041,35.189 " + id="polygon3886" + style="fill:none;stroke:#ffffff;stroke-width:2.15085721;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" /><polyline + id="polyline3888" + points="35.281,13.812 35.281,21.577 43.041,21.577 " + style="fill:none;stroke:#ffffff;stroke-width:2.15085721;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" /></g></g><path + d="m 19.426691,12.275117 c -4.727185,0 -8.666312,4.714399 -8.666312,4.714399 0,0 3.939127,4.737646 8.666312,4.735322 4.729509,-0.0046 8.668637,-4.739971 8.668637,-4.739971 0,0 -3.939128,-4.713237 -8.668637,-4.70975 z m 0,8.039818 c -1.830666,0 -3.314958,-1.484292 -3.314958,-3.31612 0,-1.827179 1.484292,-3.314958 3.314958,-3.314958 1.828341,0 3.312632,1.487779 3.312632,3.314958 0,1.831828 -1.484291,3.31612 -3.312632,3.31612 z" + id="path3890" + style="fill:#ffffff;fill-opacity:1;stroke:none;display:inline" /><circle + cx="29.207001" + cy="25.863001" + r="1.294" + transform="matrix(1.1623273,0,0,1.1623273,-14.520241,-13.061294)" + id="circle3892" + style="fill:#ffffff;fill-opacity:1;stroke:none;display:inline" /><g + transform="matrix(1,0,0,-1,-24.850339,47.707501)" + id="g4770"><g + transform="translate(34.0803,-1006.42)" + id="g4772"><polyline + transform="matrix(-0.469241,0.469241,-0.469241,-0.469241,66.2906,1019.03)" + style="fill:none;stroke:#ffffff;stroke-width:3.5;stroke-linecap:round;stroke-linejoin:round" + points="51.562,15.306 41.17,16.188 42.053,5.794" + id="polyline4774" /><path + d="m 39.363241,1033.1291 -0.05636,9.9115 -8.750608,0.067" + id="path4776" + style="fill:none;stroke:#ffffff;stroke-width:2.5;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" /></g></g></svg>
\ No newline at end of file diff --git a/icons/vbar.svg b/icons/vbar.svg new file mode 100644 index 0000000..4d06f10 --- /dev/null +++ b/icons/vbar.svg @@ -0,0 +1,66 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd" [ + <!ENTITY fill_color "#808080"> + <!ENTITY stroke_color "#FFFFFF"> +]> +<svg + xmlns="http://www.w3.org/2000/svg" + version="1.1" + width="62" + height="60" + viewBox="0 0 62 60" + id="svg2" + xml:space="preserve"><g + transform="translate(60.127119,11.292373)" + id="activity-browse" + style="display:block"> + + <g + transform="translate(-128.87712,-22.838983)" + id="g7" + style="display:inline"> + + + + + + + <g + transform="matrix(0.10822504,0,0,0.09945444,61.358446,34.085169)" + id="g6167"><g + transform="translate(2.7213003,-2.9469823)" + id="g3798" + style="fill:&fill_color;;fill-opacity:1"><rect + width="81.443176" + height="272.19876" + rx="4.3524833" + ry="6.0284276" + x="373.97116" + y="55.900478" + id="rect2987" + style="color:#000000;fill:&fill_color;;fill-opacity:1;stroke:&stroke_color;;stroke-width:33.73588181;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" /><rect + width="81.443077" + height="360.47952" + rx="4.3524833" + ry="6.0284276" + x="499.32275" + y="-32.380226" + id="rect3757" + style="color:#000000;fill:&fill_color;;fill-opacity:1;stroke:&stroke_color;;stroke-width:33.73588181;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" /><rect + width="81.443077" + height="500.25723" + rx="4.3524833" + ry="6.0284276" + x="248.61969" + y="-172.15802" + id="rect3759" + style="color:#000000;fill:&fill_color;;fill-opacity:1;stroke:&stroke_color;;stroke-width:33.73588181;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" /><rect + width="81.443314" + height="215.79736" + rx="4.3524833" + ry="6.0284276" + x="123.26795" + y="112.30204" + id="rect3761" + style="color:#000000;fill:&fill_color;;fill-opacity:1;stroke:&stroke_color;;stroke-width:33.73588181;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" /></g></g></g> +</g></svg>
\ No newline at end of file diff --git a/readers.py b/readers.py new file mode 100644 index 0000000..7106b0b --- /dev/null +++ b/readers.py @@ -0,0 +1,340 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +# readers.py by: +# Agustin Zubiaga <aguz@sugarlabs.org> +# Walter Bender <walter@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 St, Fifth Floor, Boston, MA 02110-1301 USA + +import os +import glob +import statvfs + +from gettext import gettext as _ + +from sugar import env +from sugar import profile + + +class FreeSpaceReader(): + """Reader for Free Space + Measure free space on disk. + """ + + def __init__(self): + """Import chart data from file.""" + + space = self._get_space() + self._reader = ((_('Free space'), space[0]), + (_('Used space'), space[1])) + self.xlabel = "" + self.ylabel = "" + + def get_chart_data(self): + """Return data suitable for pyCHA.""" + + chart_data = [] + + for row in self._reader: + print row + label, value = row[0], row[1] + + if label == "XLabel": + self.xlabel = value + + elif label == "YLabel": + self.ylabel = value + + else: + chart_data.append((label, float(value))) + + return chart_data + + def _get_space(self): + stat = os.statvfs(env.get_profile_path()) + free_space = stat[statvfs.F_BSIZE] * stat[statvfs.F_BAVAIL] + total_space = stat[statvfs.F_BSIZE] * stat[statvfs.F_BLOCKS] + + free_space = self._get_MBs(free_space) + total_space = self._get_MBs(total_space) + used_space = total_space - free_space + + return free_space, used_space, total_space + + def _get_MBs(self, space): + space = space / (1024 * 1024) + + return space + + def _get_GBs(self, space): + space = space / 1024 + + return space + + def get_labels_name(self): + """Return the h_label and y_label names.""" + + return self.xlabel, self.ylabel + + +class TurtleReader(): + """Reader for Journal activity + + Import chart data from journal activity analysis + """ + + TACAT = {'clean':'forward', 'forward':'forward', 'back':'forward', + 'left':'forward', 'right':'forward', 'arc': 'arc', + 'xcor': 'coord', 'ycor': 'coord', 'heading': 'coord', + 'setxy2': 'setxy', 'seth': 'setxy', 'penup': 'pen', 'pendown': 'pen', + 'setpensize': 'pen', 'setcolor': 'pen', 'pensize': 'pen', + 'color': 'pen', 'setshade': 'pen', 'setgray': 'pen', 'shade': 'pen', + 'gray': 'pen', 'fillscreen': 'pen', 'startfill': 'fill', + 'stopfill': 'fill', 'plus2': 'number', 'minus2': 'number', + 'product2': 'number', 'division2': 'number', 'remainder2': 'number', + 'sqrt': 'number', 'identity2': 'number', 'and2': 'boolean', + 'or2': 'boolean', 'not': 'boolean', 'greater2': 'boolean', + 'less2': 'boolean', 'equal2': 'boolean', 'random': 'random', + 'repeat': 'repeat', 'forever': 'repeat', 'if': 'ifthen', + 'ifelse': 'ifthen', 'while': 'ifthen', 'until': 'ifthen', + 'hat': 'action', 'stack': 'action', 'storein': 'box', 'box': 'box', + 'luminance': 'sensor', 'mousex': 'sensor', 'mousey': 'sensor', + 'mousebutton2': 'sensor', 'keyboard': 'sensor', 'kbinput': 'sensor', + 'readpixel': 'sensor', 'see': 'sensor', 'time': 'sensor', + 'sound': 'sensor', 'volume': 'sensor', 'pitch': 'sensor', + 'resistance': 'sensor', 'voltage': 'sensor', 'video': 'media', + 'wait': 'media', 'camera': 'media', 'journal': 'media', + 'audio': 'media', 'show': 'media', 'setscale': 'media', + 'savepix': 'media', 'savesvg': 'media', 'mediawait': 'media', + 'mediapause': 'media', 'mediastop': 'media', 'mediaplay': 'media', + 'speak': 'media', 'sinewave': 'media', 'description': 'media', + 'push':'extras', 'pop':'extras', 'printheap':'extras', + 'clearheap':'extras', 'isheapempty2':'extras', 'chr':'extras', + 'int':'extras', 'myfunction': 'python', 'userdefined': 'python', + 'loadblock': 'python', 'loadpalette': 'python'} + TAPAL = {'forward': 'turtlep', 'arc': 'turtlep', 'coord': 'turtlep', + 'setxy': 'turtlep', 'pen': 'penp', 'fill': 'penp', 'number': 'numberp', + 'random': 'numberp', 'boolean': 'numberp', 'repeat': 'flowp', + 'ifthen': 'flowp', 'action': 'boxp', 'box': 'boxp', + 'sensor': 'sensorp', 'media': 'mediap', 'extras': 'extrasp', + 'python': 'extrasp'} + TASCORE = {'forward': 3, 'arc': 3, 'setxy': 2.5, 'coord': 4, 'turtlep': 5, + 'pen': 2.5, 'fill': 2.5, 'penp': 5, + 'number': 2.5, 'boolean': 2.5, 'random': 2.5, 'numberp': 0, + 'repeat': 2.5, 'ifthen': 7.5, 'flowp': 10, + 'box': 7.5, 'action': 7.5, 'boxp': 0, + 'media': 5, 'mediap': 0, + 'python': 5, 'extras': 5, 'extrasp': 0, + 'sensor': 5, 'sensorp': 0} + PALS = ['turtlep', 'penp', 'numberp', 'flowp', 'boxp', 'sensorp', 'mediap', + 'extrasp'] + PALNAMES = [_('turtle'), _('pen'), _('number'), _('flow'), _('box'), + _('sensor'), _('media'), _('extras')] + + def hasturtleblocks(self, path): + ''' Parse turtle block data and generate score based on rubric ''' + + fd = open(path) + blocks = [] + # block name is second token in each line + for line in fd: + tokens = line.split(',') + if len(tokens) > 1: + token = tokens[1].strip('" [') + blocks.append(token) + + score = [] + for i in range(len(self.PALS)): + score.append([self.PALNAMES[i], 0]) + cats = [] + pals = [] + + for b in blocks: + if b in self.TACAT: + if not self.TACAT[b] in cats: + cats.append(self.TACAT[b]) + for c in cats: + if c in self.TAPAL: + if not self.TAPAL[c] in pals: + pals.append(self.TAPAL[c]) + + for c in cats: + if c in self.TASCORE: + score[self.PALS.index(self.TAPAL[c])][1] += self.TASCORE[c] + + for p in pals: + if p in self.TASCORE: + score[self.PALS.index(p)][1] += self.TASCORE[p] + + return score + + def __init__(self, file): + + self._reader = self.hasturtleblocks(file) + self.xlabel = "" + self.ylabel = "" + + def get_chart_data(self): + """Return data suitable for pyCHA.""" + + chart_data = [] + + for row in self._reader: + label, value = row[0], row[1] + + if label == "XLabel": + self.xlabel = value + + elif label == "YLabel": + self.ylabel = value + + else: + chart_data.append((label, float(value))) + + return chart_data + + def get_labels_name(self): + """Return the h_label and y_label names.""" + + return self.xlabel, self.ylabel + + +MAX = 19 +DIROFINTEREST = 'datastore' +class ParseJournal(): + ''' Simple parser of datastore ''' + + def __init__(self): + self._dsdict = {} + self._activity_name = [] + self._activity_count = [] + homepath = os.environ['HOME'] + + for path in glob.glob(os.path.join(homepath, '.sugar', '*')): + if isdsdir(path): + self._dsdict[os.path.basename(path)] = [] + dsobjdirs = glob.glob( + os.path.join(path, DIROFINTEREST, '??')) + for dsobjdir in dsobjdirs: + dsobjs = glob.glob(os.path.join(dsobjdir, '*')) + for dsobj in dsobjs: + self._dsdict[os.path.basename(path)].append({}) + activity = isactivity(dsobj) + if not activity: + self._dsdict[os.path.basename(path)][-1][ + 'activity'] = 'media object' + else: + self._dsdict[os.path.basename(path)][-1][ + 'activity'] = activity + + for k, v in self._dsdict.iteritems(): + for a in v: + if 'activity' in a: + if a['activity'] in self._activity_name: + i = self._activity_name.index(a['activity']) + self._activity_count[i] += 1 + else: + self._activity_name.append(a['activity']) + self._activity_count.append(1) + + def get_sorted(self): + activity_tuples = [] + for i in range(len(self._activity_name)): + activity_tuples.append((self._activity_name[i].replace('Activity', + ''), + self._activity_count[i])) + sorted_tuples = sorted(activity_tuples, key=lambda x: x[1]) + activity_list = [] + count = 0 + length = len(sorted_tuples) + for i in range(length): + if i < MAX: + activity_list.append([sorted_tuples[length - i - 1][0], + sorted_tuples[length - i - 1][1]]) + else: + count += sorted_tuples[length - i - 1][1] + if count > 0: + activity_list.append([_('other'), count]) + return activity_list + + +class JournalReader(): + """Reader for Journal activity + + Import chart data from journal activity analysis + """ + + def __init__(self): + + self._reader = ParseJournal().get_sorted() + self.xlabel = "" + self.ylabel = "" + + def get_chart_data(self): + """Return data suitable for pyCHA.""" + + chart_data = [] + + for row in self._reader: + label, value = row[0], row[1] + + if label == "XLabel": + self.xlabel = value + + elif label == "YLabel": + self.ylabel = value + + else: + chart_data.append((label, float(value))) + + return chart_data + + def get_labels_name(self): + """Return the h_label and y_label names.""" + + return self.xlabel, self.ylabel + + +def hascomponent(path, component): + ''' Return metadata attribute, if any ''' + if not os.path.exists(os.path.join(path, 'metadata')): + return False + if not os.path.exists(os.path.join(path, 'metadata', component)): + return False + fd = open(os.path.join(path, 'metadata', component)) + data = fd.readline() + fd.close() + if len(data) == 0: + return False + return data + + +def isactivity(path): + ''' Return activity name ''' + activity = hascomponent(path, 'activity') + if not activity: + return False + else: + return activity.split('.')[-1] + + +def isdsdir(path): + ''' Only interested if it is a datastore directory ''' + if not os.path.isdir(path): + return False + if not os.path.exists(os.path.join(path, DIROFINTEREST)): + return False + return True diff --git a/pycha/__init__.py b/sugarpycha/__init__.py index 35bba09..35bba09 100644 --- a/pycha/__init__.py +++ b/sugarpycha/__init__.py diff --git a/pycha/bar.py b/sugarpycha/bar.py index 8a3168a..fa3698d 100644 --- a/pycha/bar.py +++ b/sugarpycha/bar.py @@ -15,9 +15,9 @@ # You should have received a copy of the GNU Lesser General Public License # along with PyCha. If not, see <http://www.gnu.org/licenses/>. -from pycha.chart import Chart, uniqueIndices -from pycha.color import hex2rgb -from pycha.utils import safe_unicode +from sugarpycha.chart import Chart, uniqueIndices +from sugarpycha.color import hex2rgb +from sugarpycha.utils import safe_unicode class BarChart(Chart): @@ -43,7 +43,7 @@ class BarChart(Chart): if len(uniqx) == 1: self.minxdelta = 1.0 else: - self.minxdelta = min([abs(uniqx[j] - uniqx[j-1]) + self.minxdelta = min([abs(uniqx[j] - uniqx[j - 1]) for j in range(1, len(uniqx))]) k = self.minxdelta * self.xscale @@ -70,7 +70,7 @@ class BarChart(Chart): h = self.layout.chart.h * bar.h if (w < 1 or h < 1) and self.options.yvals.skipSmallValues: - return # don't draw when the bar is too small + return # don't draw when the bar is too small if self.options.stroke.shadow: cx.set_source_rgba(0, 0, 0, 0.15) @@ -132,7 +132,6 @@ class VerticalBarChart(BarChart): xval, yval, yerr = item else: xval, yval = item - yerr = 0.0 x = (((xval - self.minxval) * self.xscale) + self.barMargin + (i * self.barWidthForSet)) @@ -154,7 +153,7 @@ class VerticalBarChart(BarChart): self.xticks = [(tick[0] + offset, tick[1]) for tick in self.xticks] def _getShadowRectangle(self, x, y, w, h): - return (x-2, y-2, w+4, h+2) + return (x - 2, y - 2, w + 4, h + 2) def _renderYVal(self, cx, label, labelW, labelH, barX, barY, barW, barH): x = barX + (barW / 2.0) - (labelW / 2.0) @@ -238,7 +237,7 @@ class HorizontalBarChart(BarChart): self._renderLine(cx, tick, False) def _getShadowRectangle(self, x, y, w, h): - return (x, y-2, w+2, h+4) + return (x, y - 2, w + 2, h + 4) def _renderXAxisLabel(self, cx, labelText): labelText = self.options.axis.x.label diff --git a/pycha/chart.py b/sugarpycha/chart.py index 5be11cd..2ce6e05 100644 --- a/pycha/chart.py +++ b/sugarpycha/chart.py @@ -21,8 +21,8 @@ import math import cairo -from pycha.color import ColorScheme, hex2rgb, DEFAULT_COLOR -from pycha.utils import safe_unicode +from sugarpycha.color import ColorScheme, hex2rgb, DEFAULT_COLOR +from sugarpycha.utils import safe_unicode class Chart(object): @@ -169,7 +169,7 @@ class Chart(object): if x_range_is_defined: self.minxval, self.maxxval = self.options.axis.x.range else: - xdata = [pair[0] for pair in reduce(lambda a, b: a+b, stores)] + xdata = [pair[0] for pair in reduce(lambda a, b: a + b, stores)] self.minxval = float(min(xdata)) self.maxxval = float(max(xdata)) if self.minxval * self.maxxval > 0 and self.minxval > 0: @@ -185,7 +185,7 @@ class Chart(object): if y_range_is_defined: self.minyval, self.maxyval = self.options.axis.y.range else: - ydata = [pair[1] for pair in reduce(lambda a, b: a+b, stores)] + ydata = [pair[1] for pair in reduce(lambda a, b: a + b, stores)] self.minyval = float(min(ydata)) self.maxyval = float(max(ydata)) if self.minyval * self.maxyval > 0 and self.minyval > 0: @@ -197,7 +197,7 @@ class Chart(object): else: self.yscale = 1.0 / self.yrange - if self.minyval * self.maxyval < 0: # different signs + if self.minyval * self.maxyval < 0: # different signs self.origin = abs(self.minyval) * self.yscale else: self.origin = 0.0 @@ -365,7 +365,7 @@ class Chart(object): cx.line_to(x2, y2) cx.close_path() cx.stroke() - + cx.set_source_rgb(*hex2rgb('#000000')) cx.select_font_face(self.options.axis.tickFont, cairo.FONT_SLANT_NORMAL, cairo.FONT_WEIGHT_NORMAL) @@ -384,6 +384,7 @@ class Chart(object): y = -height / 2.0 cx.move_to(x - xb, y - yb) cx.show_text(label) + cx.set_source_rgb(*hex2rgb(self.options.axis.lineColor)) if self.debug: cx.rectangle(x, y, width, height) cx.stroke() @@ -393,6 +394,7 @@ class Chart(object): y -= height / 2.0 cx.move_to(x - xb, y - yb) cx.show_text(label) + cx.set_source_rgb(*hex2rgb(self.options.axis.lineColor)) if self.debug: cx.rectangle(x, y, width, height) cx.stroke() @@ -405,7 +407,7 @@ class Chart(object): y = self.layout.y_ticks.y + tick[0] * self.layout.y_ticks.h text_position = ((self.layout.y_tick_labels.x - + self.layout.y_tick_labels.w / 2.0), y) + + self.layout.y_tick_labels.w / 2.0 - 5), y) return self._renderTick(cx, tick, x, y, @@ -419,7 +421,7 @@ class Chart(object): x = self.layout.x_ticks.x + tick[0] * self.layout.x_ticks.w y = self.layout.x_ticks.y - text_position = (x, (self.layout.x_tick_labels.y + text_position = (x, (self.layout.x_tick_labels.y + 5 + self.layout.x_tick_labels.h / 2.0)) return self._renderTick(cx, tick, @@ -493,7 +495,6 @@ class Chart(object): return cx.save() - cx.set_source_rgb(*hex2rgb(self.options.axis.lineColor)) cx.set_line_width(self.options.axis.lineWidth) if not self.options.axis.y.hide: @@ -504,6 +505,7 @@ class Chart(object): if self.options.axis.y.label: self._renderYAxisLabel(cx, self.options.axis.y.label) + cx.set_source_rgb(*hex2rgb(self.options.axis.lineColor)) self._renderYAxis(cx) if not self.options.axis.x.hide: @@ -514,6 +516,7 @@ class Chart(object): if self.options.axis.x.label: self._renderXAxisLabel(cx, self.options.axis.x.label) + cx.set_source_rgb(*hex2rgb(self.options.axis.lineColor)) self._renderXAxis(cx) cx.restore() @@ -534,7 +537,7 @@ class Chart(object): x = (self.layout.title.x + self.layout.title.w / 2.0 - title_width / 2.0) - y = self.layout.title.y - extents[1] + y = self.layout.title.y - extents[1] - 10 cx.move_to(x, y) cx.show_text(title) @@ -651,14 +654,14 @@ class Layout(object): self.chart = Area() self._areas = ( - (self.title, (1, 126/255.0, 0)), # orange - (self.y_label, (41/255.0, 91/255.0, 41/255.0)), # grey - (self.x_label, (41/255.0, 91/255.0, 41/255.0)), # grey - (self.y_tick_labels, (0, 115/255.0, 0)), # green - (self.x_tick_labels, (0, 115/255.0, 0)), # green - (self.y_ticks, (229/255.0, 241/255.0, 18/255.0)), # yellow - (self.x_ticks, (229/255.0, 241/255.0, 18/255.0)), # yellow - (self.chart, (75/255.0, 75/255.0, 1.0)), # blue + (self.title, (1, 126 / 255.0, 0)), # orange + (self.y_label, (41 / 255.0, 91 / 255.0, 41 / 255.0)), # grey + (self.x_label, (41 / 255.0, 91 / 255.0, 41 / 255.0)), # grey + (self.y_tick_labels, (0, 115 / 255.0, 0)), # green + (self.x_tick_labels, (0, 115 / 255.0, 0)), # green + (self.y_ticks, (229 / 255.0, 241 / 255.0, 18 / 255.0)), # yellow + (self.x_ticks, (229 / 255.0, 241 / 255.0, 18 / 255.0)), # yellow + (self.chart, (75 / 255.0, 75 / 255.0, 1.0)), # blue ) def update(self, cx, options, width, height, xticks, yticks): @@ -758,7 +761,7 @@ class Layout(object): if not axis.hide: extents = [cx.text_extents(safe_unicode( tick[1], options.encoding, - ))[2:4] # get width and height as a tuple + ))[2:4] # get width and height as a tuple for tick in ticks] if extents: widths, heights = zip(*extents) @@ -850,10 +853,10 @@ DEFAULT_OPTIONS = Option( bottom=10, ), stroke=Option( - color='#ffffff', + color='#000000', hide=False, shadow=True, - width=2 + width=1 ), yvals=Option( show=False, diff --git a/pycha/color.py b/sugarpycha/color.py index b01e0e1..fcf0784 100644 --- a/pycha/color.py +++ b/sugarpycha/color.py @@ -18,7 +18,7 @@ import math -from pycha.utils import clamp +from sugarpycha.utils import clamp DEFAULT_COLOR = '#3c581a' @@ -36,9 +36,9 @@ def hex2rgb(hexstring, digits=2): return hexstring top = float(int(digits * 'f', 16)) - r = int(hexstring[1:digits+1], 16) - g = int(hexstring[digits+1:digits*2+1], 16) - b = int(hexstring[digits*2+1:digits*3+1], 16) + r = int(hexstring[1:digits + 1], 16) + g = int(hexstring[digits + 1:digits * 2 + 1], 16) + b = int(hexstring[digits * 2 + 1:digits * 3 + 1], 16) return r / top, g / top, b / top diff --git a/pycha/line.py b/sugarpycha/line.py index 71116b1..07be5e6 100644 --- a/pycha/line.py +++ b/sugarpycha/line.py @@ -15,8 +15,8 @@ # You should have received a copy of the GNU Lesser General Public License # along with PyCha. If not, see <http://www.gnu.org/licenses/>. -from pycha.chart import Chart -from pycha.color import hex2rgb +from sugarpycha.chart import Chart +from sugarpycha.color import hex2rgb class LineChart(Chart): @@ -83,7 +83,6 @@ class LineChart(Chart): cx.set_source_rgb(*self.colorScheme[storeName]) cx.stroke() - cx.save() cx.set_line_width(self.options.stroke.width) if self.options.shouldFill: diff --git a/pycha/pie.py b/sugarpycha/pie.py index 9585c37..12544dc 100644 --- a/pycha/pie.py +++ b/sugarpycha/pie.py @@ -19,8 +19,8 @@ import math import cairo -from pycha.chart import Chart, Option, Layout, Area, get_text_extents -from pycha.color import hex2rgb +from sugarpycha.chart import Chart, Option, Layout, Area, get_text_extents +from sugarpycha.color import hex2rgb class PieChart(Chart): @@ -55,19 +55,19 @@ class PieChart(Chart): """Evaluates pie ticks""" self.xticks = [] if self.options.axis.x.ticks: - lookup = dict([(slice.xval, slice) for slice in self.slices]) + lookup = dict([(_slice.xval, _slice) for _slice in self.slices]) for tick in self.options.axis.x.ticks: if not isinstance(tick, Option): tick = Option(tick) - slice = lookup.get(tick.v, None) + _slice = lookup.get(tick.v, None) label = tick.label or str(tick.v) - if slice is not None: - label += ' (%.1f%%)' % (slice.fraction * 100) + if _slice is not None: + label += ' (%.1f%%)' % (_slice.fraction * 100) self.xticks.append((tick.v, label)) else: - for slice in self.slices: - label = '%s (%.1f%%)' % (slice.name, slice.fraction * 100) - self.xticks.append((slice.xval, label)) + for _slice in self.slices: + label = '%s (%.1f%%)' % (_slice.name, _slice.fraction * 100) + self.xticks.append((_slice.xval, label)) def _renderLines(self, cx): """Aux function for _renderBackground""" @@ -235,12 +235,12 @@ class PieLayout(Layout): self.radius = min(self.chart.w / 2.0, self.chart.h / 2.0) for tick in xticks: - slice = lookup.get(tick[0], None) + _slice = lookup.get(tick[0], None) width, height = get_text_extents(cx, tick[1], options.axis.tickFont, options.axis.tickFontSize, options.encoding) - angle = slice.getNormalisedAngle() + angle = _slice.getNormalisedAngle() radius = self._get_min_radius(angle, centerx, centery, width, height) self.radius = min(self.radius, radius) @@ -248,8 +248,8 @@ class PieLayout(Layout): # Now that we now the radius we move the ticks as close as we can # to the circle for i, tick in enumerate(xticks): - slice = lookup.get(tick[0], None) - angle = slice.getNormalisedAngle() + _slice = lookup.get(tick[0], None) + angle = _slice.getNormalisedAngle() self.ticks[i] = self._get_tick_position(self.radius, angle, self.ticks[i], centerx, centery) diff --git a/pycha/polygonal.py b/sugarpycha/polygonal.py index b470c87..bb5ced6 100644 --- a/pycha/polygonal.py +++ b/sugarpycha/polygonal.py @@ -19,10 +19,10 @@ import math import cairo -from pycha.chart import Chart -from pycha.line import Point -from pycha.color import hex2rgb -from pycha.utils import safe_unicode +from sugarpycha.chart import Chart +from sugarpycha.line import Point +from sugarpycha.color import hex2rgb +from sugarpycha.utils import safe_unicode class PolygonalChart(Chart): diff --git a/pycha/radial.py b/sugarpycha/radial.py index 9055e26..8bbd1f9 100644 --- a/pycha/radial.py +++ b/sugarpycha/radial.py @@ -19,10 +19,10 @@ import math import cairo -from pycha.chart import Chart -from pycha.line import Point -from pycha.color import hex2rgb -from pycha.utils import safe_unicode +from sugarpycha.chart import Chart +from sugarpycha.line import Point +from sugarpycha.color import hex2rgb +from sugarpycha.utils import safe_unicode class RadialChart(Chart): diff --git a/pycha/scatter.py b/sugarpycha/scatter.py index 27656de..001e786 100644 --- a/pycha/scatter.py +++ b/sugarpycha/scatter.py @@ -17,7 +17,7 @@ import math -from pycha.line import LineChart +from sugarpycha.line import LineChart class ScatterplotChart(LineChart): diff --git a/pycha/stackedbar.py b/sugarpycha/stackedbar.py index 92fdc7f..3daf6e3 100644 --- a/pycha/stackedbar.py +++ b/sugarpycha/stackedbar.py @@ -15,8 +15,8 @@ # You should have received a copy of the GNU Lesser General Public License # along with PyCha. If not, see <http://www.gnu.org/licenses/>. -from pycha.bar import BarChart, VerticalBarChart, HorizontalBarChart, Rect -from pycha.chart import uniqueIndices +from sugarpycha.bar import BarChart, VerticalBarChart, HorizontalBarChart, Rect +from sugarpycha.chart import uniqueIndices class StackedBarChart(BarChart): @@ -35,7 +35,7 @@ class StackedBarChart(BarChart): # Fix the yscale as we accumulate the y values stores = self._getDatasetsValues() n_stores = len(stores) - flat_y = [pair[1] for pair in reduce(lambda a, b: a+b, stores)] + flat_y = [pair[1] for pair in reduce(lambda a, b: a + b, stores)] store_size = len(flat_y) / n_stores accum = [sum(flat_y[j]for j in xrange(i, i + store_size * n_stores, @@ -55,7 +55,7 @@ class StackedBarChart(BarChart): if len(uniqx) == 1: self.minxdelta = 1.0 else: - self.minxdelta = min([abs(uniqx[j] - uniqx[j-1]) + self.minxdelta = min([abs(uniqx[j] - uniqx[j - 1]) for j in range(1, len(uniqx))]) k = self.minxdelta * self.xscale diff --git a/pycha/utils.py b/sugarpycha/utils.py index aefc5b4..9e1b692 100644 --- a/pycha/utils.py +++ b/sugarpycha/utils.py @@ -16,6 +16,7 @@ # You should have received a copy of the GNU Lesser General Public License # along with PyCha. If not, see <http://www.gnu.org/licenses/>. + def clamp(minValue, maxValue, value): """Make sure value is between minValue and maxValue""" if value < minValue: diff --git a/utils.py b/utils.py new file mode 100644 index 0000000..80716d8 --- /dev/null +++ b/utils.py @@ -0,0 +1,103 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +# utils.py by: +# Agustin Zubiaga <aguzubiaga97@gmail.com> + +# 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 St, Fifth Floor, Boston, MA 02110-1301 USA + +import os +import gtk + +from sugar import profile + + +def rgb2html(color): + """Returns a html string from a Gdk color""" + red = "%x" % int(color.red / 65535.0 * 255) + if len(red) == 1: + red = "0%s" % red + + green = "%x" % int(color.green / 65535.0 * 255) + + if len(green) == 1: + green = "0%s" % green + + blue = "%x" % int(color.blue / 65535.0 * 255) + + if len(blue) == 1: + blue = "0%s" % blue + + new_color = "#%s%s%s" % (red, green, blue) + + return new_color + + +def get_user_fill_color(type='gdk'): + """Returns the user fill color""" + color = profile.get_color() + + if type == 'gdk': + rcolor = gtk.gdk.Color(color.get_fill_color()) + + elif type == 'str': + rcolor = color.get_fill_color() + + return rcolor + + +def get_user_stroke_color(type='gdk'): + """Returns the user stroke color""" + color = profile.get_color() + + if type == 'gdk': + rcolor = gtk.gdk.Color(color.get_stroke_color()) + + elif type == 'str': + rcolor = color.get_stroke_color() + + return rcolor + + +def get_chart_file(activity_dir): + """Returns a path for write the chart in a png image""" + chart_file = os.path.join(activity_dir, "chart-1.png") + num = 0 + + while os.path.exists(chart_file): + num += 1 + chart_file = os.path.join(activity_dir, "chart-" + str(num) + ".png") + + return chart_file + + +def get_decimals(number): + """Returns the decimals count of a number""" + return str(len(number.split('.')[1])) + + +def get_channels(): + path = os.path.join('/sys/class/dmi/id', 'product_version') + try: + product = open(path).readline().strip() + + except: + product = None + + if product == '1' or product == '1.0': + return 1 + + else: + return 2 |