gpxviewer-0.4.3/gpxviewer/pygtk_chart/data/tango.color0000664000175000017500000000007011752304676022512 0ustar andrewandrew#cc0000 #3465a4 #73d216 #f57900 #75507b #c17d11 #edd400 gpxviewer-0.4.3/gpxviewer/utils/iso8601/__init__.py0000664000175000017500000000002611752304676021475 0ustar andrewandrewfrom iso8601 import * gpxviewer-0.4.3/gpxviewer/utils/iso8601/iso8601.py0000664000175000017500000000624711752304676021062 0ustar andrewandrew"""ISO 8601 date time string parsing Basic usage: >>> import iso8601 >>> iso8601.parse_date("2007-01-25T12:00:00Z") datetime.datetime(2007, 1, 25, 12, 0, tzinfo=) >>> """ from datetime import datetime, timedelta, tzinfo import re __all__ = ["parse_date", "ParseError"] # Adapted from http://delete.me.uk/2005/03/iso8601.html ISO8601_REGEX = re.compile(r"(?P[0-9]{4})(-(?P[0-9]{1,2})(-(?P[0-9]{1,2})" r"((?P.)(?P[0-9]{2}):(?P[0-9]{2})(:(?P[0-9]{2})(\.(?P[0-9]+))?)?" r"(?PZ|(([-+])([0-9]{2}):([0-9]{2})))?)?)?)?" ) TIMEZONE_REGEX = re.compile("(?P[+-])(?P[0-9]{2}).(?P[0-9]{2})") class ParseError(Exception): """Raised when there is a problem parsing a date string""" # Yoinked from python docs ZERO = timedelta(0) class Utc(tzinfo): """UTC """ def utcoffset(self, dt): return ZERO def tzname(self, dt): return "UTC" def dst(self, dt): return ZERO UTC = Utc() class FixedOffset(tzinfo): """Fixed offset in hours and minutes from UTC """ def __init__(self, offset_hours, offset_minutes, name): self.__offset = timedelta(hours=offset_hours, minutes=offset_minutes) self.__name = name def utcoffset(self, dt): return self.__offset def tzname(self, dt): return self.__name def dst(self, dt): return ZERO def __repr__(self): return "" % self.__name def parse_timezone(tzstring, default_timezone=UTC): """Parses ISO 8601 time zone specs into tzinfo offsets """ if tzstring == "Z": return default_timezone # This isn't strictly correct, but it's common to encounter dates without # timezones so I'll assume the default (which defaults to UTC). # Addresses issue 4. if tzstring is None: return default_timezone m = TIMEZONE_REGEX.match(tzstring) prefix, hours, minutes = m.groups() hours, minutes = int(hours), int(minutes) if prefix == "-": hours = -hours minutes = -minutes return FixedOffset(hours, minutes, tzstring) def parse_date(datestring, default_timezone=UTC): """Parses ISO 8601 dates into datetime objects The timezone is parsed from the date string. However it is quite common to have dates without a timezone (not strictly correct). In this case the default timezone specified in default_timezone is used. This is UTC by default. """ if not isinstance(datestring, basestring): raise ParseError("Expecting a string %r" % datestring) m = ISO8601_REGEX.match(datestring) if not m: raise ParseError("Unable to parse date string %r" % datestring) groups = m.groupdict() tz = parse_timezone(groups["timezone"], default_timezone=default_timezone) if groups["fraction"] is None: groups["fraction"] = 0 else: groups["fraction"] = int(float("0.%s" % groups["fraction"]) * 1e6) return datetime(int(groups["year"]), int(groups["month"]), int(groups["day"]), int(groups["hour"]), int(groups["minute"]), int(groups["second"]), int(groups["fraction"]), tz) gpxviewer-0.4.3/gpxviewer/utils/timezone/__init__.py0000664000175000017500000000004311752304676022215 0ustar andrewandrewfrom timezone import LocalTimezone gpxviewer-0.4.3/gpxviewer/utils/timezone/timezone.py0000664000175000017500000000166611752304676022324 0ustar andrewandrewfrom datetime import tzinfo, timedelta # A class capturing the platform's idea of local time. # from python doc import time as _time STDOFFSET = timedelta(seconds = -_time.timezone) if _time.daylight: DSTOFFSET = timedelta(seconds = -_time.altzone) else: DSTOFFSET = STDOFFSET DSTDIFF = DSTOFFSET - STDOFFSET ZERO = timedelta(seconds = 0) class LocalTimezone(tzinfo): def utcoffset(self, dt): if self._isdst(dt): return DSTOFFSET else: return STDOFFSET def dst(self, dt): if self._isdst(dt): return DSTDIFF else: return ZERO def tzname(self, dt): return _time.tzname[self._isdst(dt)] def _isdst(self, dt): tt = (dt.year, dt.month, dt.day, dt.hour, dt.minute, dt.second, dt.weekday(), 0, -1) stamp = _time.mktime(tt) tt = _time.localtime(stamp) return tt.tm_isdst > 0 gpxviewer-0.4.3/gpxviewer/pygtk_chart/__init__.py0000664000175000017500000000417111752304676021550 0ustar andrewandrew#!/usr/bin/env python # # __init__.py # # Copyright 2008 Sven Festersen # # 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 2 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. """ This package contains four pygtk widgets for drawing simple charts: - line_chart.LineChart for line charts, - pie_chart.PieChart for pie charts, - bar_chart.BarChart for bar charts, - bar_chart.MultiBarChart for charts with groups of bars. """ __docformat__ = "epytext" __version__ = "beta" __author__ = "Sven Festersen, John Dickinson" __license__ = "GPL" __url__ = "http://notmyname.github.com/pygtkChart/" import os from pygtk_chart.basics import gdk_color_list_from_file COLOR_AUTO = 0 #Search share directories POSSIBLE_SHARE_DIRS = ["/usr/local/share/gpxviewer","/usr/share/gpxviewer"] prefix = "" for share_dir in POSSIBLE_SHARE_DIRS: if os.path.exists(share_dir): prefix = share_dir break COLORS = gdk_color_list_from_file(os.sep.join([prefix, "ui", "tango.color"])) #If share directories not available, run from source folder if len(COLORS) < 1: COLORS = gdk_color_list_from_file(os.sep.join([os.path.dirname(__file__), "data", "tango.color"])) #line style LINE_STYLE_SOLID = 0 LINE_STYLE_DOTTED = 1 LINE_STYLE_DASHED = 2 LINE_STYLE_DASHED_ASYMMETRIC = 3 #point styles POINT_STYLE_CIRCLE = 0 POINT_STYLE_SQUARE = 1 POINT_STYLE_CROSS = 2 POINT_STYLE_TRIANGLE_UP = 3 POINT_STYLE_TRIANGLE_DOWN = 4 POINT_STYLE_DIAMOND = 5 gpxviewer-0.4.3/gpxviewer/pygtk_chart/bar_chart.py0000664000175000017500000007116211752304676021742 0ustar andrewandrew# Copyright 2009 John Dickinson # Sven Festersen # # 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 2 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. """ Contains the BarChart widget. Author: John Dickinson (john@johnandkaren.com), Sven Festersen (sven@sven-festersen.de) """ __docformat__ = "epytext" import cairo import gtk import gobject import os import math import pygtk_chart from pygtk_chart.basics import * from pygtk_chart.chart_object import ChartObject from pygtk_chart import chart from pygtk_chart import label from pygtk_chart import COLORS, COLOR_AUTO MODE_VERTICAL = 0 MODE_HORIZONTAL = 1 def draw_rounded_rectangle(context, x, y, width, height, radius=0): """ Draws a rectangle with rounded corners to context. radius specifies the corner radius in px. @param context: the context to draw on @type context: CairoContext @param x: x coordinate of the upper left corner @type x: float @param y: y coordinate of the upper left corner @type y: float @param width: width of the rectangle in px @type width: float @param height: height of the rectangle in px @type height: float @param radius: corner radius in px (default: 0) @type radius: float. """ if radius == 0: context.rectangle(x, y, width, height) else: context.move_to(x, y + radius) context.arc(x + radius, y + radius, radius, math.pi, 1.5 * math.pi) context.rel_line_to(width - 2 * radius, 0) context.arc(x + width - radius, y + radius, radius, 1.5 * math.pi, 2 * math.pi) context.rel_line_to(0, height - 2 * radius) context.arc(x + width - radius, y + height - radius, radius, 0, 0.5 * math.pi) context.rel_line_to(-(width - 2 * radius), 0) context.arc(x + radius, y + height - radius, radius, 0.5 * math.pi, math.pi) context.close_path() class Bar(chart.Area): """ A class that represents a bar on a bar chart. Properties ========== The Bar class inherits properties from chart.Area. Additional properties: - corner-radius (radius of the bar's corners, in px; type: float) Signals ======= The Bar class inherits signals from chart.Area. """ __gproperties__ = {"corner-radius": (gobject.TYPE_INT, "bar corner radius", "The radius of the bar's rounded corner.", 0, 100, 0, gobject.PARAM_READWRITE)} def __init__(self, name, value, title=""): chart.Area.__init__(self, name, value, title) self._label_object = label.Label((0, 0), title) self._value_label_object = label.Label((0, 0), "") self._corner_radius = 0 def do_get_property(self, property): if property.name == "visible": return self._show elif property.name == "antialias": return self._antialias elif property.name == "name": return self._name elif property.name == "value": return self._value elif property.name == "color": return self._color elif property.name == "label": return self._label elif property.name == "highlighted": return self._highlighted elif property.name == "corner-radius": return self._corner_radius else: raise AttributeError, "Property %s does not exist." % property.name def do_set_property(self, property, value): if property.name == "visible": self._show = value elif property.name == "antialias": self._antialias = value elif property.name == "value": self._value = value elif property.name == "color": self._color = value elif property.name == "label": self._label = value elif property.name == "highlighted": self._highlighted = value elif property.name == "corner-radius": self._corner_radius = value else: raise AttributeError, "Property %s does not exist." % property.name def _do_draw(self, context, rect, n, i, mode, max_value, bar_padding, value_label_size, label_size, draw_labels): if mode == MODE_VERTICAL: self._do_draw_single_vertical(context, rect, n, i, mode, max_value, bar_padding, value_label_size, label_size, draw_labels) elif mode == MODE_HORIZONTAL: self._do_draw_single_horizontal(context, rect, n, i, mode, max_value, bar_padding, value_label_size, label_size, draw_labels) def _do_draw_single_vertical(self, context, rect, n, i, mode, max_value, bar_padding, value_label_size, label_size, draw_labels): bar_width = (rect.width - (n - 1) * bar_padding) / n bar_height = (rect.height - value_label_size - label_size) * self._value / max_value bar_x = rect.x + i * (bar_width + bar_padding) bar_y = rect.y + rect.height - bar_height - label_size context.set_source_rgb(*color_gdk_to_cairo(self._color)) draw_rounded_rectangle(context, bar_x, bar_y, bar_width, bar_height, self._corner_radius) context.fill() if self._highlighted: context.set_source_rgba(1, 1, 1, 0.1) draw_rounded_rectangle(context, bar_x, bar_y, bar_width, bar_height, self._corner_radius) context.fill() if draw_labels: #draw the value label self._value_label_object.set_text(str(self._value)) self._value_label_object.set_color(self._color) self._value_label_object.set_max_width(bar_width) self._value_label_object.set_position((bar_x + bar_width / 2, bar_y - 3)) self._value_label_object.set_anchor(label.ANCHOR_BOTTOM_CENTER) self._value_label_object.draw(context, rect) context.fill() #draw label self._label_object.set_text(self._label) self._label_object.set_color(self._color) self._label_object.set_max_width(bar_width) self._label_object.set_position((bar_x + bar_width / 2, bar_y + bar_height + 3)) self._label_object.set_anchor(label.ANCHOR_TOP_CENTER) self._label_object.draw(context, rect) context.fill() chart.add_sensitive_area(chart.AREA_RECTANGLE, (bar_x, bar_y, bar_width, bar_height), self) def _do_draw_single_horizontal(self, context, rect, n, i, mode, max_value, bar_padding, value_label_size, label_size, draw_labels): bar_width = (rect.width - value_label_size - label_size) * self._value / max_value bar_height = (rect.height - (n - 1) * bar_padding) / n bar_x = rect.x + label_size bar_y = rect.y + i * (bar_height + bar_padding) context.set_source_rgb(*color_gdk_to_cairo(self._color)) draw_rounded_rectangle(context, bar_x, bar_y, bar_width, bar_height, self._corner_radius) context.fill() if self._highlighted: context.set_source_rgba(1, 1, 1, 0.1) draw_rounded_rectangle(context, bar_x, bar_y, bar_width, bar_height, self._corner_radius) context.fill() if draw_labels: #draw the value label self._value_label_object.set_text(str(self._value)) self._value_label_object.set_color(self._color) self._value_label_object.set_position((bar_x + bar_width + 3, bar_y + bar_height / 2)) self._value_label_object.set_anchor(label.ANCHOR_LEFT_CENTER) self._value_label_object.draw(context, rect) context.fill() #draw label self._label_object.set_text(self._label) self._label_object.set_color(self._color) self._label_object.set_max_width(0.25 * rect.width) self._label_object.set_position((bar_x - 3, bar_y + bar_height / 2)) self._label_object.set_anchor(label.ANCHOR_RIGHT_CENTER) self._label_object.draw(context, rect) context.fill() chart.add_sensitive_area(chart.AREA_RECTANGLE, (bar_x, bar_y, bar_width, bar_height), self) def get_value_label_size(self, context, rect, mode, n, bar_padding): if mode == MODE_VERTICAL: bar_width = (rect.width - (n - 1) * bar_padding) / n self._value_label_object.set_max_width(bar_width) self._value_label_object.set_text(str(self._value)) return self._value_label_object.get_calculated_dimensions(context, rect)[1] elif mode == MODE_HORIZONTAL: self._value_label_object.set_wrap(False) self._value_label_object.set_fixed(True) self._value_label_object.set_text(str(self._value)) return self._value_label_object.get_calculated_dimensions(context, rect)[0] def get_label_size(self, context, rect, mode, n, bar_padding): if mode == MODE_VERTICAL: bar_width = (rect.width - (n - 1) * bar_padding) / n self._label_object.set_max_width(bar_width) self._label_object.set_text(self._label) return self._label_object.get_calculated_dimensions(context, rect)[1] elif mode == MODE_HORIZONTAL: self._label_object.set_max_width(0.25 * rect.width) self._label_object.set_text(self._label) return self._label_object.get_calculated_dimensions(context, rect)[0] def set_corner_radius(self, radius): """ Set the radius of the bar's corners in px (default: 0). @param radius: radius of the corners @type radius: int in [0, 100]. """ self.set_property("corner-radius", radius) self.emit("appearance_changed") def get_corner_radius(self): """ Returns the current radius of the bar's corners in px. @return: int in [0, 100] """ return self.get_property("corner-radius") class Grid(ChartObject): """ This class represents the grid on BarChart and MultiBarChart widgets. Properties ========== bar_chart.Grid inherits properties from ChartObject. Additional properties: - line-style (the style of the grid lines, type: a line style constant) - color (the color of the grid lines, type: gtk.gdk.Color) - show-values (sets whether values should be shown at the grid lines, type: boolean) - padding (the grid's padding in px, type: int in [0, 100]). Signals ======= The Grid class inherits signal from chart_object.ChartObject. """ __gproperties__ = {"show-values": (gobject.TYPE_BOOLEAN, "show values", "Set whether to show grid values.", True, gobject.PARAM_READWRITE), "color": (gobject.TYPE_PYOBJECT, "color", "The color of the grid lines.", gobject.PARAM_READWRITE), "line-style": (gobject.TYPE_INT, "line style", "The grid's line style", 0, 3, 0, gobject.PARAM_READWRITE), "padding": (gobject.TYPE_INT, "padding", "The grid's padding", 0, 100, 6, gobject.PARAM_READWRITE)} def __init__(self): ChartObject.__init__(self) #private properties: self._show_values = True self._color = gtk.gdk.color_parse("#dedede") self._line_style = pygtk_chart.LINE_STYLE_SOLID self._padding = 6 def do_get_property(self, property): if property.name == "visible": return self._show elif property.name == "antialias": return self._antialias elif property.name == "show-values": return self._show_values elif property.name == "color": return self._color elif property.name == "line-style": return self._line_style elif property.name == "padding": return self._padding else: raise AttributeError, "Property %s does not exist." % property.name def do_set_property(self, property, value): if property.name == "visible": self._show = value elif property.name == "antialias": self._antialias = value elif property.name == "show-values": self._show_values = value elif property.name == "color": self._color = value elif property.name == "line-style": self._line_style = value elif property.name == "padding": self._padding = value else: raise AttributeError, "Property %s does not exist." % property.name def _do_draw(self, context, rect, mode, maximum_value, value_label_size, label_size): n = maximum_value / (10 ** int(math.log10(maximum_value))) context.set_antialias(cairo.ANTIALIAS_NONE) set_context_line_style(context, self._line_style) labels = [] if mode == MODE_VERTICAL: delta = (rect.height - value_label_size - label_size) / n if self._show_values: max_label_size = 0 for i in range(0, int(n + 1)): y = rect.y + rect.height - i * delta - label_size value = maximum_value * float(i) / n value_label = label.Label((rect.x, y), str(value)) max_label_size = max(max_label_size, value_label.get_calculated_dimensions(context, rect)[0]) labels.append(value_label) max_label_size += 3 rect = gtk.gdk.Rectangle(int(rect.x + max_label_size), rect.y, int(rect.width - max_label_size), rect.height) for i in range(0, len(labels)): y = rect.y + rect.height - i * delta - label_size value_label = labels[i] value_label.set_position((rect.x - 3, y)) value_label.set_anchor(label.ANCHOR_RIGHT_CENTER) value_label.draw(context, rect) context.fill() for i in range(0, int(n + 1)): y = rect.y + rect.height - i * delta - label_size context.set_source_rgb(*color_gdk_to_cairo(self._color)) context.move_to(rect.x, y) context.rel_line_to(rect.width, 0) context.stroke() rect = gtk.gdk.Rectangle(rect.x + self._padding, rect.y, rect.width - 2 * self._padding, rect.height) elif mode == MODE_HORIZONTAL: delta = (rect.width - value_label_size - label_size) / n if self._show_values: max_label_size = 0 for i in range(0, int(n + 1)): x = rect.x + i * delta + label_size value = maximum_value * float(i) / n value_label = label.Label((x, rect.y + rect.height), str(value)) max_label_size = max(max_label_size, value_label.get_calculated_dimensions(context, rect)[1]) labels.append(value_label) max_label_size += 3 rect = gtk.gdk.Rectangle(rect.x, rect.y, rect.width, int(rect.height - max_label_size)) for i in range(0, len(labels)): x = rect.x + i * delta + label_size value_label = labels[i] value_label.set_position((x, rect.y + rect.height + 3)) value_label.set_anchor(label.ANCHOR_TOP_CENTER) value_label.draw(context, rect) context.fill() for i in range(0, int(n + 1)): x = rect.x + i * delta + label_size context.set_source_rgb(*color_gdk_to_cairo(self._color)) context.move_to(x, rect.y) context.rel_line_to(0, rect.height) context.stroke() rect = gtk.gdk.Rectangle(rect.x, rect.y + self._padding, rect.width, rect.height - 2 * self._padding) return rect #set and get methods def set_show_values(self, show): """ Set whether values should be shown. @type show: boolean. """ self.set_property("show-values", show) self.emit("appearance_changed") def get_show_values(self): """ Returns True if grid values are shown. @return: boolean. """ return self.get_property("show-values") def set_color(self, color): """ Set the color of the grid lines. @param color: the grid lines' color @type color: gtk.gdk.Color. """ self.set_property("color", color) self.emit("appearance_changed") def get_color(self): """ Returns the current color of the grid lines. @return: gtk.gdk.Color. """ return self.get_property("color") def set_line_style(self, style): """ Set the style of the grid lines. style has to be one of - pygtk_chart.LINE_STYLE_SOLID (default) - pygtk_chart.LINE_STYLE_DOTTED - pygtk_chart.LINE_STYLE_DASHED - pygtk_chart.LINE_STYLE_DASHED_ASYMMETRIC @param style: the new line style @type style: one of the constants above. """ self.set_property("line-style", style) self.emit("appearance_changed") def get_line_style(self): """ Returns the current grid's line style. @return: a line style constant. """ return self.get_property("line-style") def set_padding(self, padding): """ Set the grid's padding. @type padding: int in [0, 100]. """ self.set_property("padding", padding) self.emit("appearance_changed") def get_padding(self): """ Returns the grid's padding. @return: int in [0, 100]. """ return self.get_property("padding") class BarChart(chart.Chart): """ This is a widget that show a simple BarChart. Properties ========== The BarChart class inherits properties from chart.Chart. Additional properites: - draw-labels (set wether to draw bar label, type: boolean) - enable-mouseover (set whether to show a mouseover effect, type: boolean) - mode (the mode of the bar chart, type: one of MODE_VERTICAL, MODE_HORIZONTAL) - bar-padding (the sace between bars in px, type: int in [0, 100]). Signals ======= The BarChart class inherits signals from chart.Chart. Additional signals: - bar-clicked: emitted when a bar on the bar chart was clicked callback signature: def bar_clicked(chart, bar). """ __gsignals__ = {"bar-clicked": (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, (gobject.TYPE_PYOBJECT,))} __gproperties__ = {"bar-padding": (gobject.TYPE_INT, "bar padding", "The distance between two bars.", 0, 100, 16, gobject.PARAM_READWRITE), "mode": (gobject.TYPE_INT, "mode", "The chart's mode.", 0, 1, 0, gobject.PARAM_READWRITE), "draw-labels": (gobject.TYPE_BOOLEAN, "draw labels", "Set whether to draw labels on bars.", True, gobject.PARAM_READWRITE), "enable-mouseover": (gobject.TYPE_BOOLEAN, "enable mouseover", "Set whether to enable mouseover effect.", True, gobject.PARAM_READWRITE)} def __init__(self): super(BarChart, self).__init__() #private properties: self._bars = [] #gobject properties: self._bar_padding = 16 self._mode = MODE_VERTICAL self._draw_labels = True self._mouseover = True #public attributes: self.grid = Grid() #connect callbacks: self.grid.connect("appearance_changed", self._cb_appearance_changed) def do_get_property(self, property): if property.name == "padding": return self._padding elif property.name == "bar-padding": return self._bar_padding elif property.name == "mode": return self._mode elif property.name == "draw-labels": return self._draw_labels elif property.name == "enable-mouseover": return self._mouseover else: raise AttributeError, "Property %s does not exist." % property.name def do_set_property(self, property, value): if property.name == "padding": self._padding = value elif property.name == "bar-padding": self._bar_padding = value elif property.name == "mode": self._mode = value elif property.name == "draw-labels": self._draw_labels = value elif property.name == "enable-mouseover": self._mouseover = value else: raise AttributeError, "Property %s does not exist." % property.name #drawing methods def draw(self, context): """ Draw the widget. This method is called automatically. Don't call it yourself. If you want to force a redrawing of the widget, call the queue_draw() method. @type context: cairo.Context @param context: The context to draw on. """ label.begin_drawing() rect = self.get_allocation() rect = gtk.gdk.Rectangle(0, 0, rect.width, rect.height) #transform rect to context coordinates context.set_line_width(1) rect = self.draw_basics(context, rect) maximum_value = max(bar.get_value() for bar in self._bars) #find out the size of the value labels value_label_size = 0 if self._draw_labels: for bar in self._bars: value_label_size = max(value_label_size, bar.get_value_label_size(context, rect, self._mode, len(self._bars), self._bar_padding)) value_label_size += 3 #find out the size of the labels: label_size = 0 if self._draw_labels: for bar in self._bars: label_size = max(label_size, bar.get_label_size(context, rect, self._mode, len(self._bars), self._bar_padding)) label_size += 3 rect = self._do_draw_grid(context, rect, maximum_value, value_label_size, label_size) self._do_draw_bars(context, rect, maximum_value, value_label_size, label_size) label.finish_drawing() if self._mode == MODE_VERTICAL: n = len(self._bars) minimum_width = rect.x + self._padding + (n - 1) * self._bar_padding + n * 10 minimum_height = 100 + self._padding + rect.y elif self._mode == MODE_HORIZONTAL: n = len(self._bars) minimum_width = rect.x + self._bar_padding + 100 minimum_height = rect.y + self._padding + (n - 1) * self._bar_padding + n * 10 self.set_size_request(minimum_width, minimum_height) def draw_basics(self, context, rect): """ Draw basic things that every plot has (background, title, ...). @type context: cairo.Context @param context: The context to draw on. @type rect: gtk.gdk.Rectangle @param rect: A rectangle representing the charts area. """ self.background.draw(context, rect) self.title.draw(context, rect, self._padding) #calculate the rectangle that's available for drawing the chart title_height = self.title.get_real_dimensions()[1] rect_height = int(rect.height - 3 * self._padding - title_height) rect_width = int(rect.width - 2 * self._padding) rect_x = int(rect.x + self._padding) rect_y = int(rect.y + title_height + 2 * self._padding) return gtk.gdk.Rectangle(rect_x, rect_y, rect_width, rect_height) def _do_draw_grid(self, context, rect, maximum_value, value_label_size, label_size): if self.grid.get_visible(): return self.grid.draw(context, rect, self._mode, maximum_value, value_label_size, label_size) else: return rect def _do_draw_bars(self, context, rect, maximum_value, value_label_size, label_size): if self._bars == []: return #draw the bars chart.init_sensitive_areas() for i, bar in enumerate(self._bars): bar.draw(context, rect, len(self._bars), i, self._mode, maximum_value, self._bar_padding, value_label_size, label_size, self._draw_labels) #other methods def add_bar(self, bar): if bar.get_color() == COLOR_AUTO: bar.set_color(COLORS[len(self._bars) % len(COLORS)]) self._bars.append(bar) bar.connect("appearance_changed", self._cb_appearance_changed) #callbacks def _cb_motion_notify(self, widget, event): if not self._mouseover: return bars = chart.get_sensitive_areas(event.x, event.y) if bars == []: return for bar in self._bars: bar.set_property("highlighted", bar in bars) self.queue_draw() def _cb_button_pressed(self, widget, event): bars = chart.get_sensitive_areas(event.x, event.y) for bar in bars: self.emit("bar-clicked", bar) #set and get methods def set_bar_padding(self, padding): """ Set the space between two bars in px. @param padding: space between bars in px @type padding: int in [0, 100]. """ self.set_property("bar-padding", padding) self.queue_draw() def get_bar_padding(self): """ Returns the space between bars in px. @return: int in [0, 100]. """ return self.get_property("bar-padding") def set_mode(self, mode): """ Set the mode (vertical or horizontal) of the BarChart. mode has to be bar_chart.MODE_VERTICAL (default) or bar_chart.MODE_HORIZONTAL. @param mode: the new mode of the chart @type mode: one of the mode constants above. """ self.set_property("mode", mode) self.queue_draw() def get_mode(self): """ Returns the current mode of the chart: bar_chart.MODE_VERTICAL or bar_chart.MODE_HORIZONTAL. @return: a mode constant. """ return self.get_property("mode") def set_draw_labels(self, draw): """ Set whether labels should be drawn on bars. @type draw: boolean. """ self.set_property("draw-labels", draw) self.queue_draw() def get_draw_labels(self): """ Returns True if labels are drawn on bars. @return: boolean. """ return self.get_property("draw-labels") def set_enable_mouseover(self, mouseover): """ Set whether a mouseover effect should be shown when the pointer enters a bar. @type mouseover: boolean. """ self.set_property("enable-mouseover", mouseover) def get_enable_mouseover(self): """ Returns True if the mouseover effect is enabled. @return: boolean. """ return self.get_property("enable-mouseover") gpxviewer-0.4.3/gpxviewer/pygtk_chart/basics.py0000664000175000017500000000767011752304676021264 0ustar andrewandrew#!/usr/bin/env python # # misc.py # # Copyright 2008 Sven Festersen # # 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 2 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. """ This module contains simple functions needed by all other modules. Author: Sven Festersen (sven@sven-festersen.de) """ __docformat__ = "epytext" import cairo import gtk import os import pygtk_chart def is_in_range(x, (xmin, xmax)): """ Use this method to test whether M{xmin <= x <= xmax}. @type x: number @type xmin: number @type xmax: number """ return (xmin <= x and xmax >= x) def intersect_ranges(range_a, range_b): min_a, max_a = range_a min_b, max_b = range_b return max(min_a, min_b), min(max_a, max_b) def get_center(rect): """ Find the center point of a rectangle. @type rect: gtk.gdk.Rectangle @param rect: The rectangle. @return: A (x, y) tuple specifying the center point. """ return rect.width / 2, rect.height / 2 def color_gdk_to_cairo(color): """ Convert a gtk.gdk.Color to cairo color. @type color: gtk.gdk.Color @param color: the color to convert @return: a color in cairo format. """ return (color.red / 65535.0, color.green / 65535.0, color.blue / 65535.0) def color_cairo_to_gdk(r, g, b): return gtk.gdk.Color(int(65535 * r), int(65535 * g), int(65535 * b)) def color_rgb_to_cairo(color): """ Convert a 8 bit RGB value to cairo color. @type color: a triple of integers between 0 and 255 @param color: The color to convert. @return: A color in cairo format. """ return (color[0] / 255.0, color[1] / 255.0, color[2] / 255.0) def color_html_to_cairo(color): """ Convert a html (hex) RGB value to cairo color. @type color: html color string @param color: The color to convert. @return: A color in cairo format. """ if color[0] == '#': color = color[1:] (r, g, b) = (int(color[:2], 16), int(color[2:4], 16), int(color[4:], 16)) return color_rgb_to_cairo((r, g, b)) def color_list_from_file(filename): """ Read a file with one html hex color per line and return a list of cairo colors. """ result = [] if os.path.exists(filename): f = open(filename, "r") for line in f.readlines(): line = line.strip() result.append(color_html_to_cairo(line)) return result def gdk_color_list_from_file(filename): """ Read a file with one html hex color per line and return a list of gdk colors. """ result = [] if os.path.exists(filename): f = open(filename, "r") for line in f.readlines(): line = line.strip() result.append(gtk.gdk.color_parse(line)) return result def set_context_line_style(context, style): """ The the line style for a context. """ if style == pygtk_chart.LINE_STYLE_SOLID: context.set_dash([]) elif style == pygtk_chart.LINE_STYLE_DASHED: context.set_dash([5]) elif style == pygtk_chart.LINE_STYLE_DASHED_ASYMMETRIC: context.set_dash([6, 6, 2, 6]) elif style == pygtk_chart.LINE_STYLE_DOTTED: context.set_dash([1]) gpxviewer-0.4.3/gpxviewer/pygtk_chart/chart.py0000664000175000017500000005025411752304676021115 0ustar andrewandrew#!/usr/bin/env python # # plot.py # # Copyright 2008 Sven Festersen # # 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 2 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. """ Module Contents =============== This is the main module. It contains the base classes for chart widgets. - class Chart: base class for all chart widgets. - class Background: background of a chart widget. - class Title: title of a chart. Colors ------ All colors that pygtkChart uses are gtk.gdk.Colors as used by PyGTK. Author: Sven Festersen (sven@sven-festersen.de) """ __docformat__ = "epytext" import cairo import gobject import gtk import os import pango import pangocairo import pygtk from pygtk_chart.chart_object import ChartObject from pygtk_chart.basics import * from pygtk_chart import label COLOR_AUTO = 0 AREA_CIRCLE = 0 AREA_RECTANGLE = 1 CLICK_SENSITIVE_AREAS = [] def init_sensitive_areas(): global CLICK_SENSITIVE_AREAS CLICK_SENSITIVE_AREAS = [] def add_sensitive_area(type, coords, data): global CLICK_SENSITIVE_AREAS CLICK_SENSITIVE_AREAS.append((type, coords, data)) def get_sensitive_areas(x, y): res = [] global CLICK_SENSITIVE_AREAS for type, coords, data in CLICK_SENSITIVE_AREAS: if type == AREA_CIRCLE: ax, ay, radius = coords if (ax - x) ** 2 + (ay - y) ** 2 <= radius ** 2: res.append(data) elif type == AREA_RECTANGLE: ax, ay, width, height = coords if ax <= x <= ax + width and ay <= y <= ay + height: res.append(data) return res class Chart(gtk.DrawingArea): """ This is the base class for all chart widgets. Properties ========== The Chart class inherits properties from gtk.DrawingArea. Additional properties: - padding (the amount of free white space between the chart's content and its border in px, type: int in [0, 100]. Signals ======= The Chart class inherits signals from gtk.DrawingArea. """ __gproperties__ = {"padding": (gobject.TYPE_INT, "padding", "The chart's padding.", 0, 100, 16, gobject.PARAM_READWRITE)} def __init__(self): gtk.DrawingArea.__init__(self) #private properties: self._padding = 16 #objects needed for every chart: self.background = Background() self.background.connect("appearance-changed", self._cb_appearance_changed) self.title = Title() self.title.connect("appearance-changed", self._cb_appearance_changed) self.add_events(gtk.gdk.BUTTON_PRESS_MASK|gtk.gdk.SCROLL_MASK|gtk.gdk.POINTER_MOTION_MASK) self.connect("expose_event", self._cb_expose_event) self.connect("button_press_event", self._cb_button_pressed) self.connect("motion-notify-event", self._cb_motion_notify) def do_get_property(self, property): if property.name == "padding": return self._padding else: raise AttributeError, "Property %s does not exist." % property.name def do_set_property(self, property, value): if property.name == "padding": self._padding = value else: raise AttributeError, "Property %s does not exist." % property.name def _cb_appearance_changed(self, object): """ This method is called after the appearance of an object changed and forces a redraw. """ self.queue_draw() def _cb_button_pressed(self, widget, event): pass def _cb_motion_notify(self, widget, event): pass def _cb_expose_event(self, widget, event): """ This method is called when an instance of Chart receives the gtk expose_event. @type widget: gtk.Widget @param widget: The widget that received the event. @type event: gtk.Event @param event: The event. """ self.context = widget.window.cairo_create() self.context.rectangle(event.area.x, event.area.y, \ event.area.width, event.area.height) self.context.clip() self.draw(self.context) return False def draw_basics(self, context, rect): """ Draw basic things that every plot has (background, title, ...). @type context: cairo.Context @param context: The context to draw on. @type rect: gtk.gdk.Rectangle @param rect: A rectangle representing the charts area. """ self.background.draw(context, rect) self.title.draw(context, rect) #calculate the rectangle that's available for drawing the chart title_height = self.title.get_real_dimensions()[1] rect_height = int(rect.height - 3 * self._padding - title_height) rect_width = int(rect.width - 2 * self._padding) rect_x = int(rect.x + self._padding) rect_y = int(rect.y + title_height + 2 * self._padding) return gtk.gdk.Rectangle(rect_x, rect_y, rect_width, rect_height) def draw(self, context): """ Draw the widget. This method is called automatically. Don't call it yourself. If you want to force a redrawing of the widget, call the queue_draw() method. @type context: cairo.Context @param context: The context to draw on. """ rect = self.get_allocation() rect = gtk.gdk.Rectangle(0, 0, rect.width, rect.height) #transform rect to context coordinates context.set_line_width(1) rect = self.draw_basics(context, rect) def export_svg(self, filename, size=None): """ Saves the contents of the widget to svg file. The size of the image will be the size of the widget. @type filename: string @param filename: The path to the file where you want the chart to be saved. @type size: tuple @param size: Optional parameter to give the desired height and width of the image. """ if size is None: rect = self.get_allocation() width = rect.width height = rect.height else: width, height = size old_alloc = self.get_allocation self.get_allocation = lambda: gtk.gdk.Rectangle(0, 0, width, height) surface = cairo.SVGSurface(filename, width, height) ctx = cairo.Context(surface) context = pangocairo.CairoContext(ctx) self.draw(context) surface.finish() if size is not None: self.get_allocation = old_alloc def export_png(self, filename, size=None): """ Saves the contents of the widget to png file. The size of the image will be the size of the widget. @type filename: string @param filename: The path to the file where you want the chart to be saved. @type size: tuple @param size: Optional parameter to give the desired height and width of the image. """ if size is None: rect = self.get_allocation() width = rect.width height = rect.height else: width, height = size old_alloc = self.get_allocation self.get_allocation = lambda: gtk.gdk.Rectangle(0, 0, width, height) surface = cairo.ImageSurface(cairo.FORMAT_ARGB32, width, height) ctx = cairo.Context(surface) context = pangocairo.CairoContext(ctx) self.set_size_request(width, height) self.draw(context) surface.write_to_png(filename) if size is not None: self.get_allocation = old_alloc def set_padding(self, padding): """ Set the chart's padding. @param padding: the padding in px @type padding: int in [0, 100] (default: 16). """ self.set_property("padding", padding) self.queue_draw() def get_padding(self): """ Returns the chart's padding. @return: int in [0, 100]. """ return self.get_property("padding") class Background(ChartObject): """ The background of a chart. Properties ========== This class inherits properties from chart_object.ChartObject. Additional properties: - color (the background color, type: gtk.gdk.Color) - gradient (the background gradient, type: a pair of gtk.gdk.Color) - image (path to the background image file, type: string) Signals ======= The Background class inherits signals from chart_object.ChartObject. """ __gproperties__ = {"color": (gobject.TYPE_PYOBJECT, "background color", "The color of the backround.", gobject.PARAM_READWRITE), "gradient": (gobject.TYPE_PYOBJECT, "background gradient", "A background gardient. (first_color, second_color)", gobject.PARAM_READWRITE), "image": (gobject.TYPE_STRING, "background image file", "Path to the image file to use as background.", "", gobject.PARAM_READWRITE)} def __init__(self): ChartObject.__init__(self) self._color = gtk.gdk.color_parse("#ffffff") #the backgound is filled white by default self._gradient = None self._image = "" self._pixbuf = None def do_get_property(self, property): if property.name == "visible": return self._show elif property.name == "antialias": return self._antialias elif property.name == "gradient": return self._gradient elif property.name == "color": return self._color elif property.name == "image": return self._image else: raise AttributeError, "Property %s does not exist." % property.name def do_set_property(self, property, value): if property.name == "visible": self._show = value elif property.name == "antialias": self._antialias = value elif property.name == "gradient": self._gradient = value elif property.name == "color": self._color = value elif property.name == "image": self._image = value else: raise AttributeError, "Property %s does not exist." % property.name def _do_draw(self, context, rect): """ Do all the drawing stuff. @type context: cairo.Context @param context: The context to draw on. @type rect: gtk.gdk.Rectangle @param rect: A rectangle representing the charts area. """ if self._color != None: #set source color context.set_source_rgb(*color_gdk_to_cairo(self._color)) elif self._gradient != None: #set source gradient cs = color_gdk_to_cairo(self._gradient[0]) ce = color_gdk_to_cairo(self._gradient[1]) gradient = cairo.LinearGradient(0, 0, 0, rect.height) gradient.add_color_stop_rgb(0, cs[0], cs[1], cs[2]) gradient.add_color_stop_rgb(1, ce[0], ce[1], ce[2]) context.set_source(gradient) elif self._pixbuf: context.set_source_pixbuf(self._pixbuf, 0, 0) else: context.set_source_rgb(1, 1, 1) #fallback to white bg #create the background rectangle and fill it: context.rectangle(0, 0, rect.width, rect.height) context.fill() def set_color(self, color): """ The set_color() method can be used to change the color of the background. @type color: gtk.gdk.Color @param color: Set the background to be filles with this color. """ self.set_property("color", color) self.set_property("gradient", None) self.set_property("image", "") self.emit("appearance_changed") def get_color(self): """ Returns the background's color. @return: gtk.gdk.Color. """ return self.get_property("color") def set_gradient(self, color_start, color_end): """ Use set_gradient() to define a vertical gradient as the background. @type color_start: gtk.gdk.Color @param color_start: The starting (top) color of the gradient. @type color_end: gtk.gdk.Color @param color_end: The ending (bottom) color of the gradient. """ self.set_property("color", None) self.set_property("gradient", (color_start, color_end)) self.set_property("image", "") self.emit("appearance_changed") def get_gradient(self): """ Returns the gradient of the background or None. @return: A (gtk.gdk.Color, gtk.gdk.Color) tuple or None. """ return self.get_property("gradient") def set_image(self, filename): """ The set_image() method sets the background to be filled with an image. @type filename: string @param filename: Path to the file you want to use as background image. If the file does not exists, the background is set to white. """ try: self._pixbuf = gtk.gdk.pixbuf_new_from_file(filename) except: self._pixbuf = None self.set_property("color", None) self.set_property("gradient", None) self.set_property("image", filename) self.emit("appearance_changed") def get_image(self): return self.get_property("image") class Title(label.Label): """ The title of a chart. The title will be drawn centered at the top of the chart. Properties ========== The Title class inherits properties from label.Label. Signals ======= The Title class inherits signals from label.Label. """ def __init__(self, text=""): label.Label.__init__(self, (0, 0), text, weight=pango.WEIGHT_BOLD, anchor=label.ANCHOR_TOP_CENTER, fixed=True) def _do_draw(self, context, rect, top=-1): if top == -1: top = rect.height / 80 self._size = max(8, int(rect.height / 50.0)) self._position = rect.width / 2, top self._do_draw_label(context, rect) class Area(ChartObject): """ This is a base class for classes that represent areas, e.g. the pie_chart.PieArea class and the bar_chart.Bar class. Properties ========== The Area class inherits properties from chart_object.ChartObject. Additional properties: - name (a unique name for the area, type: string, read only) - value (the value of the area, type: float) - color (the area's color, type: gtk.gdk.Color) - label (a label for the area, type: string) - highlighted (set whether the area should be highlighted, type: boolean). Signals ======= The Area class inherits signals from chart_object.ChartObject. """ __gproperties__ = {"name": (gobject.TYPE_STRING, "area name", "A unique name for the area.", "", gobject.PARAM_READABLE), "value": (gobject.TYPE_FLOAT, "value", "The value.", 0.0, 9999999999.0, 0.0, gobject.PARAM_READWRITE), "color": (gobject.TYPE_PYOBJECT, "area color", "The color of the area.", gobject.PARAM_READWRITE), "label": (gobject.TYPE_STRING, "area label", "The label for the area.", "", gobject.PARAM_READWRITE), "highlighted": (gobject.TYPE_BOOLEAN, "area is higlighted", "Set whether the area should be higlighted.", False, gobject.PARAM_READWRITE)} def __init__(self, name, value, title=""): ChartObject.__init__(self) self._name = name self._value = value self._label = title self._color = COLOR_AUTO self._highlighted = False def do_get_property(self, property): if property.name == "visible": return self._show elif property.name == "antialias": return self._antialias elif property.name == "name": return self._name elif property.name == "value": return self._value elif property.name == "color": return self._color elif property.name == "label": return self._label elif property.name == "highlighted": return self._highlighted else: raise AttributeError, "Property %s does not exist." % property.name def do_set_property(self, property, value): if property.name == "visible": self._show = value elif property.name == "antialias": self._antialias = value elif property.name == "value": self._value = value elif property.name == "color": self._color = value elif property.name == "label": self._label = value elif property.name == "highlighted": self._highlighted = value else: raise AttributeError, "Property %s does not exist." % property.name def set_value(self, value): """ Set the value of the area. @type value: float. """ self.set_property("value", value) self.emit("appearance_changed") def get_value(self): """ Returns the current value of the area. @return: float. """ return self.get_property("value") def set_color(self, color): """ Set the color of the area. @type color: gtk.gdk.Color. """ self.set_property("color", color) self.emit("appearance_changed") def get_color(self): """ Returns the current color of the area or COLOR_AUTO. @return: gtk.gdk.Color or COLOR_AUTO. """ return self.get_property("color") def set_label(self, label): """ Set the label for the area. @param label: the new label @type label: string. """ self.set_property("label", label) self.emit("appearance_changed") def get_label(self): """ Returns the current label of the area. @return: string. """ return self.get_property("label") def set_highlighted(self, highlighted): """ Set whether the area should be highlighted. @type highlighted: boolean. """ self.set_property("highlighted", highlighted) self.emit("appearance_changed") def get_highlighted(self): """ Returns True if the area is currently highlighted. @return: boolean. """ return self.get_property("highlighted") gpxviewer-0.4.3/gpxviewer/pygtk_chart/chart_object.py0000664000175000017500000001225511752304676022442 0ustar andrewandrew#!/usr/bin/env python # # chart_object.py # # Copyright 2009 Sven Festersen # # 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 2 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. """ This module contains the ChartObject class. Author: Sven Festersen (sven@sven-festersen.de) """ import cairo import gobject class ChartObject(gobject.GObject): """ This is the base class for all things that can be drawn on a chart widget. It emits the signal 'appearance-changed' when it needs to be redrawn. Properties ========== ChartObject inherits properties from gobject.GObject. Additional properties: - visible (sets whether the object should be visible, type: boolean) - antialias (sets whether the object should be antialiased, type: boolean). Signals ======= ChartObject inherits signals from gobject.GObject, Additional signals: - appearance-changed (emitted if the object needs to be redrawn). """ __gsignals__ = {"appearance-changed": (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, [])} __gproperties__ = {"visible": (gobject.TYPE_BOOLEAN, "visibilty of the object", "Set whether to draw the object or not.", True, gobject.PARAM_READWRITE), "antialias": (gobject.TYPE_BOOLEAN, "use antialiasing", "Set whether to use antialiasing when drawing the object.", True, gobject.PARAM_READWRITE)} def __init__(self): gobject.GObject.__init__(self) self._show = True self._antialias = True def do_get_property(self, property): if property.name == "visible": return self._show elif property.name == "antialias": return self._antialias else: raise AttributeError, "Property %s does not exist." % property.name def do_set_property(self, property, value): if property.name == "visible": self._show = value elif property.name == "antialias": self._antialias = value else: raise AttributeError, "Property %s does not exist." % property.name def _do_draw(self, context, rect): """ A derived class should override this method. The drawing stuff should happen here. @type context: cairo.Context @param context: The context to draw on. @type rect: gtk.gdk.Rectangle @param rect: A rectangle representing the charts area. """ pass def draw(self, context, rect, *args): """ This method is called by the parent Chart instance. It calls _do_draw. @type context: cairo.Context @param context: The context to draw on. @type rect: gtk.gdk.Rectangle @param rect: A rectangle representing the charts area. """ res = None if self._show: if not self._antialias: context.set_antialias(cairo.ANTIALIAS_NONE) res = self._do_draw(context, rect, *args) context.set_antialias(cairo.ANTIALIAS_DEFAULT) return res def set_antialias(self, antialias): """ This method sets the antialiasing mode of the ChartObject. Antialiasing is enabled by default. @type antialias: boolean @param antialias: If False, antialiasing is disabled for this ChartObject. """ self.set_property("antialias", antialias) self.emit("appearance_changed") def get_antialias(self): """ Returns True if antialiasing is enabled for the object. @return: boolean. """ return self.get_property("antialias") def set_visible(self, visible): """ Use this method to set whether the ChartObject should be visible or not. @type visible: boolean @param visible: If False, the PlotObject won't be drawn. """ self.set_property("visible", visible) self.emit("appearance_changed") def get_visible(self): """ Returns True if the object is visble. @return: boolean. """ return self.get_property("visible") gobject.type_register(ChartObject) gpxviewer-0.4.3/gpxviewer/pygtk_chart/label.py0000664000175000017500000006423511752304676021077 0ustar andrewandrew#!/usr/bin/env python # # text.py # # Copyright 2009 Sven Festersen # # 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 2 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. """ Contains the Label class. Author: Sven Festersen (sven@sven-festersen.de) """ import cairo import gobject import gtk import math import pango import pygtk from pygtk_chart import basics from pygtk_chart.chart_object import ChartObject ANCHOR_BOTTOM_LEFT = 0 ANCHOR_TOP_LEFT = 1 ANCHOR_TOP_RIGHT = 2 ANCHOR_BOTTOM_RIGHT = 4 ANCHOR_CENTER = 5 ANCHOR_TOP_CENTER = 6 ANCHOR_BOTTOM_CENTER = 7 ANCHOR_LEFT_CENTER = 8 ANCHOR_RIGHT_CENTER = 9 UNDERLINE_NONE = pango.UNDERLINE_NONE UNDERLINE_SINGLE = pango.UNDERLINE_SINGLE UNDERLINE_DOUBLE = pango.UNDERLINE_DOUBLE UNDERLINE_LOW = pango.UNDERLINE_LOW STYLE_NORMAL = pango.STYLE_NORMAL STYLE_OBLIQUE = pango.STYLE_OBLIQUE STYLE_ITALIC = pango.STYLE_ITALIC WEIGHT_ULTRALIGHT = pango.WEIGHT_ULTRALIGHT WEIGHT_LIGHT = pango.WEIGHT_LIGHT WEIGHT_NORMAL = pango.WEIGHT_NORMAL WEIGHT_BOLD = pango.WEIGHT_BOLD WEIGHT_ULTRABOLD = pango.WEIGHT_ULTRABOLD WEIGHT_HEAVY = pango.WEIGHT_HEAVY DRAWING_INITIALIZED = False REGISTERED_LABELS = [] def begin_drawing(): global DRAWING_INITIALIZED DRAWING_INITIALIZED = True def finish_drawing(): global REGISTERED_LABELS global DRAWING_INITIALIZED REGISTERED_LABELS = [] DRAWING_INITIALIZED = False def register_label(label): if DRAWING_INITIALIZED: REGISTERED_LABELS.append(label) def get_registered_labels(): if DRAWING_INITIALIZED: return REGISTERED_LABELS return [] class Label(ChartObject): """ This class is used for drawing all the text on the chart widgets. It uses the pango layout engine. Properties ========== The Label class inherits properties from chart_object.ChartObject. Additional properties: - color (the label's color, type: gtk.gdk.Color) - text (text to display, type: string) - position (the label's position, type: pair of float) - anchor (the anchor that should be used to position the label, type: an anchor constant) - underline (sets the type of underline, type; an underline constant) - max-width (the maximum width of the label in px, type: int) - rotation (angle of rotation in degrees, type: int) - size (the size of the label's text in px, type: int) - slant (the font slant, type: a slant style constant) - weight (the font weight, type: a font weight constant) - fixed (sets whether the position of the label may be changed dynamicly or not, type: boolean) - wrap (sets whether the label's text should be wrapped if it's longer than max-width, type: boolean). Signals ======= The Label class inherits signals from chart_object.ChartObject. """ __gproperties__ = {"color": (gobject.TYPE_PYOBJECT, "label color", "The color of the label (a gtk.gdkColor)", gobject.PARAM_READWRITE), "text": (gobject.TYPE_STRING, "label text", "The text to show on the label.", "", gobject.PARAM_READWRITE), "position": (gobject.TYPE_PYOBJECT, "label position", "A pair of x,y coordinates.", gobject.PARAM_READWRITE), "anchor": (gobject.TYPE_INT, "label anchor", "The anchor of the label.", 0, 9, 0, gobject.PARAM_READWRITE), "underline": (gobject.TYPE_PYOBJECT, "underline text", "Set whether to underline the text.", gobject.PARAM_READWRITE), "max-width": (gobject.TYPE_INT, "maximum width", "The maximum width of the label.", 1, 99999, 99999, gobject.PARAM_READWRITE), "rotation": (gobject.TYPE_INT, "rotation of the label", "The angle that the label should be rotated by in degrees.", 0, 360, 0, gobject.PARAM_READWRITE), "size": (gobject.TYPE_INT, "text size", "The size of the text.", 0, 1000, 8, gobject.PARAM_READWRITE), "slant": (gobject.TYPE_PYOBJECT, "font slant", "The font slant style.", gobject.PARAM_READWRITE), "weight": (gobject.TYPE_PYOBJECT, "font weight", "The font weight.", gobject.PARAM_READWRITE), "fixed": (gobject.TYPE_BOOLEAN, "fixed", "Set whether the position of the label should be forced.", False, gobject.PARAM_READWRITE), "wrap": (gobject.TYPE_BOOLEAN, "wrap text", "Set whether text should be wrapped.", False, gobject.PARAM_READWRITE)} def __init__(self, position, text, size=None, slant=pango.STYLE_NORMAL, weight=pango.WEIGHT_NORMAL, underline=pango.UNDERLINE_NONE, anchor=ANCHOR_BOTTOM_LEFT, max_width=99999, fixed=False): ChartObject.__init__(self) self._position = position self._text = text self._size = size self._slant = slant self._weight = weight self._underline = underline self._anchor = anchor self._rotation = 0 self._color = gtk.gdk.Color() self._max_width = max_width self._fixed = fixed self._wrap = True self._real_dimensions = (0, 0) self._real_position = (0, 0) self._line_count = 1 self._context = None self._layout = None def do_get_property(self, property): if property.name == "visible": return self._show elif property.name == "antialias": return self._antialias elif property.name == "text": return self._text elif property.name == "color": return self._color elif property.name == "position": return self._position elif property.name == "anchor": return self._anchor elif property.name == "underline": return self._underline elif property.name == "max-width": return self._max_width elif property.name == "rotation": return self._rotation elif property.name == "size": return self._size elif property.name == "slant": return self._slant elif property.name == "weight": return self._weight elif property.name == "fixed": return self._fixed elif property.name == "wrap": return self._wrap else: raise AttributeError, "Property %s does not exist." % property.name def do_set_property(self, property, value): if property.name == "visible": self._show = value elif property.name == "antialias": self._antialias = value elif property.name == "text": self._text = value elif property.name == "color": self._color = value elif property.name == "position": self._position = value elif property.name == "anchor": self._anchor = value elif property.name == "underline": self._underline = value elif property.name == "max-width": self._max_width = value elif property.name == "rotation": self._rotation = value elif property.name == "size": self._size = value elif property.name == "slant": self._slant = value elif property.name == "weight": self._weight = value elif property.name == "fixed": self._fixed = value elif property.name == "wrap": self._wrap = value else: raise AttributeError, "Property %s does not exist." % property.name def _do_draw(self, context, rect): self._do_draw_label(context, rect) def _do_draw_label(self, context, rect): angle = 2 * math.pi * self._rotation / 360.0 if self._context == None: label = gtk.Label() self._context = label.create_pango_context() pango_context = self._context attrs = pango.AttrList() attrs.insert(pango.AttrWeight(self._weight, 0, len(self._text))) attrs.insert(pango.AttrStyle(self._slant, 0, len(self._text))) attrs.insert(pango.AttrUnderline(self._underline, 0, len(self._text))) if self._size != None: attrs.insert(pango.AttrSize(1000 * self._size, 0, len(self._text))) if self._layout == None: self._layout = pango.Layout(pango_context) layout = self._layout layout.set_text(self._text) layout.set_attributes(attrs) #find out where to draw the layout and calculate the maximum width width = rect.width if self._anchor in [ANCHOR_BOTTOM_LEFT, ANCHOR_TOP_LEFT, ANCHOR_LEFT_CENTER]: width = rect.width - self._position[0] elif self._anchor in [ANCHOR_BOTTOM_RIGHT, ANCHOR_TOP_RIGHT, ANCHOR_RIGHT_CENTER]: width = self._position[0] text_width, text_height = layout.get_pixel_size() width = width * math.cos(angle) width = min(width, self._max_width) if self._wrap: layout.set_wrap(pango.WRAP_WORD_CHAR) layout.set_width(int(1000 * width)) x, y = get_text_pos(layout, self._position, self._anchor, angle) if not self._fixed: #Find already drawn labels that would intersect with the current one #and adjust position to avoid intersection. text_width, text_height = layout.get_pixel_size() real_width = abs(text_width * math.cos(angle)) + abs(text_height * math.sin(angle)) real_height = abs(text_height * math.cos(angle)) + abs(text_width * math.sin(angle)) other_labels = get_registered_labels() this_rect = gtk.gdk.Rectangle(int(x), int(y), int(real_width), int(real_height)) for label in other_labels: label_rect = label.get_allocation() intersection = this_rect.intersect(label_rect) if intersection.width == 0 and intersection.height == 0: continue y_diff = 0 if label_rect.y <= y and label_rect.y + label_rect.height >= y: y_diff = y - label_rect.y + label_rect.height elif label_rect.y > y and label_rect.y < y + real_height: y_diff = label_rect.y - real_height - y y += y_diff #draw layout context.move_to(x, y) context.rotate(angle) context.set_source_rgb(*basics.color_gdk_to_cairo(self._color)) context.show_layout(layout) context.rotate(-angle) context.stroke() #calculate the real dimensions text_width, text_height = layout.get_pixel_size() real_width = abs(text_width * math.cos(angle)) + abs(text_height * math.sin(angle)) real_height = abs(text_height * math.cos(angle)) + abs(text_width * math.sin(angle)) self._real_dimensions = real_width, real_height self._real_position = x, y self._line_count = layout.get_line_count() register_label(self) def get_calculated_dimensions(self, context, rect): angle = 2 * math.pi * self._rotation / 360.0 if self._context == None: label = gtk.Label() self._context = label.create_pango_context() pango_context = self._context attrs = pango.AttrList() attrs.insert(pango.AttrWeight(self._weight, 0, len(self._text))) attrs.insert(pango.AttrStyle(self._slant, 0, len(self._text))) attrs.insert(pango.AttrUnderline(self._underline, 0, len(self._text))) if self._size != None: attrs.insert(pango.AttrSize(1000 * self._size, 0, len(self._text))) if self._layout == None: self._layout = pango.Layout(pango_context) layout = self._layout layout.set_text(self._text) layout.set_attributes(attrs) #find out where to draw the layout and calculate the maximum width width = rect.width if self._anchor in [ANCHOR_BOTTOM_LEFT, ANCHOR_TOP_LEFT, ANCHOR_LEFT_CENTER]: width = rect.width - self._position[0] elif self._anchor in [ANCHOR_BOTTOM_RIGHT, ANCHOR_TOP_RIGHT, ANCHOR_RIGHT_CENTER]: width = self._position[0] text_width, text_height = layout.get_pixel_size() width = width * math.cos(angle) width = min(width, self._max_width) if self._wrap: layout.set_wrap(pango.WRAP_WORD_CHAR) layout.set_width(int(1000 * width)) x, y = get_text_pos(layout, self._position, self._anchor, angle) if not self._fixed: #Find already drawn labels that would intersect with the current one #and adjust position to avoid intersection. text_width, text_height = layout.get_pixel_size() real_width = abs(text_width * math.cos(angle)) + abs(text_height * math.sin(angle)) real_height = abs(text_height * math.cos(angle)) + abs(text_width * math.sin(angle)) other_labels = get_registered_labels() this_rect = gtk.gdk.Rectangle(int(x), int(y), int(real_width), int(real_height)) for label in other_labels: label_rect = label.get_allocation() intersection = this_rect.intersect(label_rect) if intersection.width == 0 and intersection.height == 0: continue y_diff = 0 if label_rect.y <= y and label_rect.y + label_rect.height >= y: y_diff = y - label_rect.y + label_rect.height elif label_rect.y > y and label_rect.y < y + real_height: y_diff = label_rect.y - real_height - y y += y_diff #calculate the dimensions text_width, text_height = layout.get_pixel_size() real_width = abs(text_width * math.cos(angle)) + abs(text_height * math.sin(angle)) real_height = abs(text_height * math.cos(angle)) + abs(text_width * math.sin(angle)) return real_width, real_height def set_text(self, text): """ Use this method to set the text that should be displayed by the label. @param text: the text to display. @type text: string """ self.set_property("text", text) self.emit("appearance_changed") def get_text(self): """ Returns the text currently displayed. @return: string. """ return self.get_property("text") def set_color(self, color): """ Set the color of the label. color has to be a gtk.gdk.Color. @param color: the color of the label @type color: gtk.gdk.Color. """ self.set_property("color", color) self.emit("appearance_changed") def get_color(self): """ Returns the current color of the label. @return: gtk.gdk.Color. """ return self.get_property("color") def set_position(self, pos): """ Set the position of the label. pos has to be a x,y pair of absolute pixel coordinates on the widget. The position is not the actual position but the position of the Label's anchor point (see L{set_anchor} for details). @param pos: new position of the label @type pos: pair of (x, y). """ self.set_property("position", pos) self.emit("appearance_changed") def get_position(self): """ Returns the current position of the label. @return: pair of (x, y). """ return self.get_property("position") def set_anchor(self, anchor): """ Set the anchor point of the label. The anchor point is the a point on the label's edge that has the position you set with set_position(). anchor has to be one of the following constants: - label.ANCHOR_BOTTOM_LEFT - label.ANCHOR_TOP_LEFT - label.ANCHOR_TOP_RIGHT - label.ANCHOR_BOTTOM_RIGHT - label.ANCHOR_CENTER - label.ANCHOR_TOP_CENTER - label.ANCHOR_BOTTOM_CENTER - label.ANCHOR_LEFT_CENTER - label.ANCHOR_RIGHT_CENTER The meaning of the constants is illustrated below::: ANCHOR_TOP_LEFT ANCHOR_TOP_CENTER ANCHOR_TOP_RIGHT * * * ##################### ANCHOR_LEFT_CENTER * # * # * ANCHOR_RIGHT_CENTER ##################### * * * ANCHOR_BOTTOM_LEFT ANCHOR_BOTTOM_CENTER ANCHOR_BOTTOM_RIGHT The point in the center is of course referred to by constant label.ANCHOR_CENTER. @param anchor: the anchor point of the label @type anchor: one of the constants described above. """ self.set_property("anchor", anchor) self.emit("appearance_changed") def get_anchor(self): """ Returns the current anchor point that's used to position the label. See L{set_anchor} for details. @return: one of the anchor constants described in L{set_anchor}. """ return self.get_property("anchor") def set_underline(self, underline): """ Set the underline style of the label. underline has to be one of the following constants: - label.UNDERLINE_NONE: do not underline the text - label.UNDERLINE_SINGLE: draw a single underline (the normal underline method) - label.UNDERLINE_DOUBLE: draw a double underline - label.UNDERLINE_LOW; draw a single low underline. @param underline: the underline style @type underline: one of the constants above. """ self.set_property("underline", underline) self.emit("appearance_changed") def get_underline(self): """ Returns the current underline style. See L{set_underline} for details. @return: an underline constant (see L{set_underline}). """ return self.get_property("underline") def set_max_width(self, width): """ Set the maximum width of the label in pixels. @param width: the maximum width @type width: integer. """ self.set_property("max-width", width) self.emit("appearance_changed") def get_max_width(self): """ Returns the maximum width of the label. @return: integer. """ return self.get_property("max-width") def set_rotation(self, angle): """ Use this method to set the rotation of the label in degrees. @param angle: the rotation angle @type angle: integer in [0, 360]. """ self.set_property("rotation", angle) self.emit("appearance_changed") def get_rotation(self): """ Returns the current rotation angle. @return: integer in [0, 360]. """ return self.get_property("rotation") def set_size(self, size): """ Set the size of the text in pixels. @param size: size of the text @type size: integer. """ self.set_property("size", size) self.emit("appearance_changed") def get_size(self): """ Returns the current size of the text in pixels. @return: integer. """ return self.get_property("size") def set_slant(self, slant): """ Set the font slant. slat has to be one of the following: - label.STYLE_NORMAL - label.STYLE_OBLIQUE - label.STYLE_ITALIC @param slant: the font slant style @type slant: one of the constants above. """ self.set_property("slant", slant) self.emit("appearance_changed") def get_slant(self): """ Returns the current font slant style. See L{set_slant} for details. @return: a slant style constant. """ return self.get_property("slant") def set_weight(self, weight): """ Set the font weight. weight has to be one of the following: - label.WEIGHT_ULTRALIGHT - label.WEIGHT_LIGHT - label.WEIGHT_NORMAL - label.WEIGHT_BOLD - label.WEIGHT_ULTRABOLD - label.WEIGHT_HEAVY @param weight: the font weight @type weight: one of the constants above. """ self.set_property("weight", weight) self.emit("appearance_changed") def get_weight(self): """ Returns the current font weight. See L{set_weight} for details. @return: a font weight constant. """ return self.get_property("weight") def set_fixed(self, fixed): """ Set whether the position of the label should be forced (fixed=True) or if it should be positioned avoiding intersection with other labels. @type fixed: boolean. """ self.set_property("fixed", fixed) self.emit("appearance_changed") def get_fixed(self): """ Returns True if the label's position is forced. @return: boolean """ return self.get_property("fixed") def set_wrap(self, wrap): """ Set whether too long text should be wrapped. @type wrap: boolean. """ self.set_property("wrap", wrap) self.emit("appearance_changed") def get_wrap(self): """ Returns True if too long text should be wrapped. @return: boolean. """ return self.get_property("wrap") def get_real_dimensions(self): """ This method returns a pair (width, height) with the dimensions the label was drawn with. Call this method I{after} drawing the label. @return: a (width, height) pair. """ return self._real_dimensions def get_real_position(self): """ Returns the position of the label where it was really drawn. @return: a (x, y) pair. """ return self._real_position def get_allocation(self): """ Returns an allocation rectangle. @return: gtk.gdk.Rectangle. """ x, y = self._real_position w, h = self._real_dimensions return gtk.gdk.Rectangle(int(x), int(y), int(w), int(h)) def get_line_count(self): """ Returns the number of lines. @return: int. """ return self._line_count def get_text_pos(layout, pos, anchor, angle): """ This function calculates the position of bottom left point of the layout respecting the given anchor point. @return: (x, y) pair """ text_width_n, text_height_n = layout.get_pixel_size() text_width = text_width_n * abs(math.cos(angle)) + text_height_n * abs(math.sin(angle)) text_height = text_height_n * abs(math.cos(angle)) + text_width_n * abs(math.sin(angle)) height_delta = text_height - text_height_n x, y = pos ref = (0, -text_height) if anchor == ANCHOR_TOP_LEFT: ref = (0, 0) elif anchor == ANCHOR_TOP_RIGHT: ref = (-text_width, height_delta) elif anchor == ANCHOR_BOTTOM_RIGHT: ref = (-text_width, -text_height) elif anchor == ANCHOR_CENTER: ref = (-text_width / 2, -text_height / 2) elif anchor == ANCHOR_TOP_CENTER: ref = (-text_width / 2, 0) elif anchor == ANCHOR_BOTTOM_CENTER: ref = (-text_width / 2, -text_height) elif anchor == ANCHOR_LEFT_CENTER: ref = (0, -text_height / 2) elif anchor == ANCHOR_RIGHT_CENTER: ref = (-text_width, -text_height / 2) x += ref[0] y += ref[1] return x, y gpxviewer-0.4.3/gpxviewer/pygtk_chart/line_chart.py0000664000175000017500000023127211752304676022125 0ustar andrewandrew#!/usr/bin/env python # # lineplot.py # # Copyright 2008 Sven Festersen # # 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 2 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. """ Contains the LineChart widget. Author: Sven Festersen (sven@sven-festersen.de) """ __docformat__ = "epytext" import gobject import cairo import gtk import math import os import pygtk_chart from pygtk_chart.basics import * from pygtk_chart.chart_object import ChartObject from pygtk_chart import chart from pygtk_chart import label from pygtk_chart import COLORS, COLOR_AUTO RANGE_AUTO = 0 GRAPH_PADDING = 1 / 15.0 #a relative padding GRAPH_POINTS = 1 GRAPH_LINES = 2 GRAPH_BOTH = 3 COLOR_AUTO = 4 POSITION_AUTO = 5 POSITION_LEFT = 6 POSITION_RIGHT = 7 POSITION_BOTTOM = 6 POSITION_TOP = 7 POSITION_TOP_RIGHT = 8 POSITION_BOTTOM_RIGHT = 9 POSITION_BOTTOM_LEFT = 10 POSITION_TOP_LEFT = 11 def draw_point(context, x, y, radius, style): a = radius / 1.414 #1.414=sqrt(2) if style == pygtk_chart.POINT_STYLE_CIRCLE: context.arc(x, y, radius, 0, 2 * math.pi) context.fill() elif style == pygtk_chart.POINT_STYLE_SQUARE: context.rectangle(x - a, y- a, 2 * a, 2 * a) context.fill() elif style == pygtk_chart.POINT_STYLE_CROSS: context.move_to(x, y - a) context.rel_line_to(0, 2 * a) context.stroke() context.move_to(x - a, y) context.rel_line_to(2 * a, 0) context.stroke() elif style == pygtk_chart.POINT_STYLE_TRIANGLE_UP: a = 1.732 * radius #1.732=sqrt(3) b = a / (2 * 1.732) context.move_to(x - a / 2, y + b) context.rel_line_to(a, 0) context.rel_line_to(-a / 2, -(radius + b)) context.rel_line_to(-a / 2, radius + b) context.close_path() context.fill() elif style == pygtk_chart.POINT_STYLE_TRIANGLE_DOWN: a = 1.732 * radius #1.732=sqrt(3) b = a / (2 * 1.732) context.move_to(x - a / 2, y - b) context.rel_line_to(a, 0) context.rel_line_to(-a / 2, radius + b) context.rel_line_to(-a / 2, -(radius + b)) context.close_path() context.fill() elif style == pygtk_chart.POINT_STYLE_DIAMOND: context.move_to(x, y - a) context.rel_line_to(a, a) context.rel_line_to(-a, a) context.rel_line_to(-a, -a) context.rel_line_to(a, -a) context.fill() def draw_point_pixbuf(context, x, y, pixbuf): w = pixbuf.get_width() h = pixbuf.get_height() ax = x - w / 2 ay = y - h / 2 context.set_source_pixbuf(pixbuf, ax, ay) context.rectangle(ax, ay, w, h) context.fill() def draw_errors(context, rect, range_calc, x, y, errors, draw_x, draw_y, xaxis, yaxis, size): if (x, y) in errors: xerror, yerror = errors[(x, y)] if draw_x and xerror > 0: #rect, x, y, xaxis, yaxis left = range_calc.get_absolute_point(rect, x - xerror, y, xaxis, yaxis) right = range_calc.get_absolute_point(rect, x + xerror, y, xaxis, yaxis) context.move_to(left[0], left[1]) context.line_to(right[0], right[1]) context.stroke() context.move_to(left[0], left[1] - size) context.rel_line_to(0, 2 * size) context.stroke() context.move_to(right[0], right[1] - size) context.rel_line_to(0, 2 * size) context.stroke() if draw_y and yerror > 0: top = range_calc.get_absolute_point(rect, x, y - yerror, xaxis, yaxis) bottom = range_calc.get_absolute_point(rect, x, y + yerror, xaxis, yaxis) context.move_to(top[0], top[1]) context.line_to(bottom[0], bottom[1]) context.stroke() context.move_to(top[0] - size, top[1]) context.rel_line_to(2 * size, 0) context.stroke() context.move_to(bottom[0] - size, bottom[1]) context.rel_line_to(2 * size, 0) context.stroke() def separate_data_and_errors(old_data): data = [] errors = {} for d in old_data: if len(d) == 2: data.append(d) elif len(d) == 4: data.append((d[0], d[1])) errors[(d[0], d[1])] = (d[2], d[3]) return data, errors class RangeCalculator: """ This helper class calculates ranges. It is used by the LineChart widget internally, there is no need to create an instance yourself. """ def __init__(self): self._data_xrange = None self._data_yrange = None self._xrange = RANGE_AUTO self._yrange = RANGE_AUTO self._cached_xtics = [] self._cached_ytics = [] def add_graph(self, graph): if self._data_xrange == None: self._data_yrange = graph.get_y_range() self._data_xrange = graph.get_x_range() else: yrange = graph.get_y_range() xrange = graph.get_x_range() if xrange and yrange: xmin = min(xrange[0], self._data_xrange[0]) xmax = max(xrange[1], self._data_xrange[1]) ymin = min(yrange[0], self._data_yrange[0]) ymax = max(yrange[1], self._data_yrange[1]) self._data_xrange = (xmin, xmax) self._data_yrange = (ymin, ymax) def get_ranges(self, xaxis, yaxis): xrange = self._xrange if xrange == RANGE_AUTO: xrange = self._data_xrange if xrange[0] == xrange[1]: xrange = (xrange[0], xrange[0] + 0.1) yrange = self._yrange if yrange == RANGE_AUTO: yrange = self._data_yrange if yrange[0] == yrange[1]: yrange = (yrange[0], yrange[0] + 0.1) if xaxis.get_logarithmic(): xrange = math.log10(xrange[0]), math.log10(xrange[1]) if yaxis.get_logarithmic(): yrange = math.log10(yrange[0]), math.log10(yrange[1]) return (xrange, yrange) def set_xrange(self, xrange): self._xrange = xrange def set_yrange(self, yrange): self._yrange = yrange def get_absolute_zero(self, rect, xaxis, yaxis): xrange, yrange = self.get_ranges(xaxis, yaxis) xfactor = float(rect.width * (1 - 2 * GRAPH_PADDING)) / (xrange[1] - xrange[0]) yfactor = float(rect.height * (1 - 2 * GRAPH_PADDING)) / (yrange[1] - yrange[0]) zx = (rect.width * GRAPH_PADDING) - xrange[0] * xfactor zy = rect.height - ((rect.height * GRAPH_PADDING) - yrange[0] * yfactor) return (zx,zy) def get_absolute_point(self, rect, x, y, xaxis, yaxis): (zx, zy) = self.get_absolute_zero(rect, xaxis, yaxis) xrange, yrange = self.get_ranges(xaxis, yaxis) xfactor = float(rect.width * (1 - 2 * GRAPH_PADDING)) / (xrange[1] - xrange[0]) yfactor = float(rect.height * (1 - 2 * GRAPH_PADDING)) / (yrange[1] - yrange[0]) ax = zx + x * xfactor ay = zy - y * yfactor return (ax, ay) def prepare_tics(self, rect, xaxis, yaxis): self._cached_xtics = self._get_xtics(rect, xaxis, yaxis) self._cached_ytics = self._get_ytics(rect, xaxis, yaxis) def get_xtics(self, rect): return self._cached_xtics def get_ytics(self, rect): return self._cached_ytics def _get_xtics(self, rect, xaxis, yaxis): tics = [] (zx, zy) = self.get_absolute_zero(rect, xaxis, yaxis) (xrange, yrange) = self.get_ranges(xaxis, yaxis) delta = xrange[1] - xrange[0] exp = int(math.log10(delta)) - 1 first_n = int(xrange[0] / (10 ** exp)) last_n = int(xrange[1] / (10 ** exp)) n = last_n - first_n N = rect.width / 50.0 divide_by = int(n / N) if divide_by == 0: divide_by = 1 left = rect.width * GRAPH_PADDING right = rect.width * (1 - GRAPH_PADDING) for i in range(first_n, last_n + 1): num = i * 10 ** exp (x, y) = self.get_absolute_point(rect, num, 0, xaxis, yaxis) if i % divide_by == 0 and is_in_range(x, (left, right)): tics.append(((x, y), num)) return tics def _get_ytics(self, rect, xaxis, yaxis): tics = [] (zx, zy) = self.get_absolute_zero(rect, xaxis, yaxis) (xrange, yrange) = self.get_ranges(xaxis, yaxis) delta = yrange[1] - yrange[0] exp = int(math.log10(delta)) - 1 first_n = int(yrange[0] / (10 ** exp)) last_n = int(yrange[1] / (10 ** exp)) n = last_n - first_n N = rect.height / 50.0 divide_by = int(n / N) if divide_by == 0: divide_by = 1 top = rect.height * GRAPH_PADDING bottom = rect.height * (1 - GRAPH_PADDING) for i in range(first_n, last_n + 1): num = i * 10 ** exp (x, y) = self.get_absolute_point(rect, 0, num, xaxis, yaxis) if i % divide_by == 0 and is_in_range(y, (top, bottom)): tics.append(((x, y), num)) return tics class LineChart(chart.Chart): """ A widget that shows a line chart. The following attributes can be accessed: - LineChart.background (inherited from chart.Chart) - LineChart.title (inherited from chart.Chart) - LineChart.graphs (a dict that holds the graphs identified by their name) - LineChart.grid - LineChart.xaxis - LineChart.yaxis Properties ========== LineChart inherits properties from chart.Chart. Signals ======= The LineChart class inherits signals from chart.Chart. Additional chart: - datapoint-clicked (emitted if a datapoint is clicked) - datapoint-hovered (emitted if a datapoint is hovered with the mouse pointer) Callback signature for both signals: def callback(linechart, graph, (x, y)) """ __gsignals__ = {"datapoint-clicked": (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, (gobject.TYPE_PYOBJECT, gobject.TYPE_PYOBJECT)), "datapoint-hovered": (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, (gobject.TYPE_PYOBJECT, gobject.TYPE_PYOBJECT))} def __init__(self): chart.Chart.__init__(self) self.graphs = {} self._range_calc = RangeCalculator() self.xaxis = XAxis(self._range_calc) self.yaxis = YAxis(self._range_calc) self.grid = Grid(self._range_calc) self.legend = Legend() self._highlighted_points = [] self.xaxis.connect("appearance_changed", self._cb_appearance_changed) self.yaxis.connect("appearance_changed", self._cb_appearance_changed) self.grid.connect("appearance_changed", self._cb_appearance_changed) self.legend.connect("appearance_changed", self._cb_appearance_changed) def __iter__(self): for name, graph in self.graphs.iteritems(): yield graph def _cb_button_pressed(self, widget, event): points = chart.get_sensitive_areas(event.x, event.y) for x, y, graph in points: self.emit("datapoint-clicked", graph, (x, y)) def _cb_motion_notify(self, widget, event): self._highlighted_points = chart.get_sensitive_areas(event.x, event.y) for x, y, graph in self._highlighted_points: self.emit("datapoint-hovered", graph, (x, y)) self.queue_draw() def _do_draw_graphs(self, context, rect): """ Draw all the graphs. @type context: cairo.Context @param context: The context to draw on. @type rect: gtk.gdk.Rectangle @param rect: A rectangle representing the charts area. """ for (name, graph) in self.graphs.iteritems(): graph.draw(context, rect, self.xaxis, self.yaxis, self._highlighted_points) self._highlighted_points = [] def _do_draw_axes(self, context, rect): """ Draw x and y axis. @type context: cairo.Context @param context: The context to draw on. @type rect: gtk.gdk.Rectangle @param rect: A rectangle representing the charts area. """ self.xaxis.draw(context, rect, self.yaxis) self.yaxis.draw(context, rect, self.xaxis) def draw(self, context): """ Draw the widget. This method is called automatically. Don't call it yourself. If you want to force a redrawing of the widget, call the queue_draw() method. @type context: cairo.Context @param context: The context to draw on. """ label.begin_drawing() chart.init_sensitive_areas() rect = self.get_allocation() self._range_calc.prepare_tics(rect, self.xaxis, self.yaxis) #initial context settings: line width & font context.set_line_width(1) font = gtk.Label().style.font_desc.get_family() context.select_font_face(font,cairo.FONT_SLANT_NORMAL, \ cairo.FONT_WEIGHT_NORMAL) self.draw_basics(context, rect) data_available = False for (name, graph) in self.graphs.iteritems(): if graph.has_something_to_draw(): data_available = True break if self.graphs and data_available: self.grid.draw(context, rect, self.xaxis, self.yaxis) self._do_draw_axes(context, rect) self._do_draw_graphs(context, rect) label.finish_drawing() self.legend.draw(context, rect, self.graphs) def add_graph(self, graph): """ Add a graph object to the plot. @type graph: line_chart.Graph @param graph: The graph to add. """ if graph.get_color() == COLOR_AUTO: graph.set_color(COLORS[len(self.graphs) % len(COLORS)]) graph.set_range_calc(self._range_calc) self.graphs[graph.get_name()] = graph self._range_calc.add_graph(graph) graph.connect("appearance-changed", self._cb_appearance_changed) def remove_graph(self, name): """ Remove a graph from the plot. @type name: string @param name: The name of the graph to remove. """ del self.graphs[name] self.queue_draw() def set_xrange(self, xrange): """ Set the visible xrange. xrange has to be a pair: (xmin, xmax) or RANGE_AUTO. If you set it to RANGE_AUTO, the visible range will be calculated. @type xrange: pair of numbers @param xrange: The new xrange. """ self._range_calc.set_xrange(xrange) self.queue_draw() def get_xrange(self): return self._range_calc.get_ranges(self.xaxis, self.yaxis)[0] def set_yrange(self, yrange): """ Set the visible yrange. yrange has to be a pair: (ymin, ymax) or RANGE_AUTO. If you set it to RANGE_AUTO, the visible range will be calculated. @type yrange: pair of numbers @param yrange: The new yrange. """ self._range_calc.set_yrange(yrange) self.queue_draw() def get_yrange(self): return self._range_calc.get_ranges(self.xaxis, self.yaxis)[1] class Axis(ChartObject): """ This class represents an axis on the line chart. Properties ========== The Axis class inherits properties from chart_object.ChartObject. Additional properties: - label (a label for the axis, type: string) - show-label (sets whether the axis' label should be shown, type: boolean) - position (position of the axis, type: an axis position constant) - show-tics (sets whether tics should be shown at the axis, type: boolean) - show-tic-lables (sets whether labels should be shown at the tics, type: boolean) - tic-format-function (a function that is used to format the tic labels, default: str) - logarithmic (sets whether the axis should use a logarithmic scale, type: boolean). Signals ======= The Axis class inherits signals from chart_object.ChartObject. """ __gproperties__ = {"label": (gobject.TYPE_STRING, "axis label", "The label of the axis.", "", gobject.PARAM_READWRITE), "show-label": (gobject.TYPE_BOOLEAN, "show label", "Set whether to show the axis label.", True, gobject.PARAM_READWRITE), "position": (gobject.TYPE_INT, "axis position", "Position of the axis.", 5, 7, 5, gobject.PARAM_READWRITE), "show-tics": (gobject.TYPE_BOOLEAN, "show tics", "Set whether to draw tics.", True, gobject.PARAM_READWRITE), "show-tic-labels": (gobject.TYPE_BOOLEAN, "show tic labels", "Set whether to draw tic labels", True, gobject.PARAM_READWRITE), "tic-format-function": (gobject.TYPE_PYOBJECT, "tic format function", "This function is used to label the tics.", gobject.PARAM_READWRITE), "logarithmic": (gobject.TYPE_BOOLEAN, "logarithmic scale", "Set whether to use logarithmic scale.", False, gobject.PARAM_READWRITE)} def __init__(self, range_calc, label): ChartObject.__init__(self) self.set_property("antialias", False) self._label = label self._show_label = True self._position = POSITION_AUTO self._show_tics = True self._show_tic_labels = True self._tic_format_function = str self._logarithmic = False self._range_calc = range_calc def do_get_property(self, property): if property.name == "visible": return self._show elif property.name == "antialias": return self._antialias elif property.name == "label": return self._label elif property.name == "show-label": return self._show_label elif property.name == "position": return self._position elif property.name == "show-tics": return self._show_tics elif property.name == "show-tic-labels": return self._show_tic_labels elif property.name == "tic-format-function": return self._tic_format_function elif property.name == "logarithmic": return self._logarithmic else: raise AttributeError, "Property %s does not exist." % property.name def do_set_property(self, property, value): if property.name == "visible": self._show = value elif property.name == "antialias": self._antialias = value elif property.name == "label": self._label = value elif property.name == "show-label": self._show_label = value elif property.name == "position": self._position = value elif property.name == "show-tics": self._show_tics = value elif property.name == "show-tic-labels": self._show_tic_labels = value elif property.name == "tic-format-function": self._tic_format_function = value elif property.name == "logarithmic": self._logarithmic = value else: raise AttributeError, "Property %s does not exist." % property.name def set_label(self, label): """ Set the label of the axis. @param label: new label @type label: string. """ self.set_property("label", label) self.emit("appearance_changed") def get_label(self): """ Returns the current label of the axis. @return: string. """ return self.get_property("label") def set_show_label(self, show): """ Set whether to show the axis' label. @type show: boolean. """ self.set_property("show-label", show) self.emit("appearance_changed") def get_show_label(self): """ Returns True if the axis' label is shown. @return: boolean. """ return self.get_property("show-label") def set_position(self, pos): """ Set the position of the axis. pos hast to be one these constants: POSITION_AUTO, POSITION_BOTTOM, POSITION_LEFT, POSITION_RIGHT, POSITION_TOP. """ self.set_property("position", pos) self.emit("appearance_changed") def get_position(self): """ Returns the position of the axis. (see set_position for details). """ return self.get_property("position") def set_show_tics(self, show): """ Set whether to draw tics at the axis. @type show: boolean. """ self.set_property("show-tics", show) self.emit("appearance_changed") def get_show_tics(self): """ Returns True if tics are drawn. @return: boolean. """ return self.get_property("show-tics") def set_show_tic_labels(self, show): """ Set whether to draw tic labels. Labels are only drawn if tics are drawn. @type show: boolean. """ self.set_property("show-tic-labels", show) self.emit("appearance_changed") def get_show_tic_labels(self): """ Returns True if tic labels are shown. @return: boolean. """ return self.get_property("show-tic-labels") def set_tic_format_function(self, func): """ Use this to set the function that should be used to label the tics. The function should take a number as the only argument and return a string. Default: str @type func: function. """ self.set_property("tic-format-function", func) self.emit("appearance_changed") def get_tic_format_function(self): """ Returns the function currently used for labeling the tics. """ return self.get_property("tic-format-function") def set_logarithmic(self, log): """ Set whether the axis should use logarithmic (base 10) scale. @type log: boolean. """ self.set_property("logarithmic", log) self.emit("appearance_changed") def get_logarithmic(self): """ Returns True if the axis uses logarithmic scale. @return: boolean. """ return self.get_property("logarithmic") class XAxis(Axis): """ This class represents the xaxis. It is used by the LineChart widget internally, there is no need to create an instance yourself. Properties ========== The XAxis class inherits properties from Axis. Signals ======= The XAxis class inherits signals from Axis. """ def __init__(self, range_calc): Axis.__init__(self, range_calc, "x") def draw(self, context, rect, yaxis): """ This method is called by the parent Plot instance. It calls _do_draw. """ if self._show: if not self._antialias: context.set_antialias(cairo.ANTIALIAS_NONE) self._do_draw(context, rect, yaxis) context.set_antialias(cairo.ANTIALIAS_DEFAULT) def _do_draw_tics(self, context, rect, yaxis): if self._show_tics: tics = self._range_calc.get_xtics(rect) #calculate yaxis position (zx, zy) = self._range_calc.get_absolute_zero(rect, self, yaxis) if yaxis.get_position() == POSITION_LEFT: zx = rect.width * GRAPH_PADDING elif yaxis.get_position() == POSITION_RIGHT: zx = rect.width * (1 - GRAPH_PADDING) for ((x,y), val) in tics: if self._position == POSITION_TOP: y = rect.height * GRAPH_PADDING elif self._position == POSITION_BOTTOM: y = rect.height * (1 - GRAPH_PADDING) tic_height = rect.height / 80.0 context.move_to(x, y + tic_height / 2) context.rel_line_to(0, - tic_height) context.stroke() if self._show_tic_labels: if abs(x - zx) < 10: #the distance to the yaxis is to small => do not draw label continue pos = x, y + tic_height text = self._tic_format_function(val) tic_label = label.Label(pos, text, anchor=label.ANCHOR_TOP_CENTER, fixed=True) tic_label.draw(context, rect) def _do_draw_label(self, context, rect, pos): axis_label = label.Label(pos, self._label, anchor=label.ANCHOR_LEFT_CENTER, fixed=True) axis_label.draw(context, rect) def _do_draw(self, context, rect, yaxis): """ Draw the axis. """ (zx, zy) = self._range_calc.get_absolute_zero(rect, self, yaxis) if self._position == POSITION_BOTTOM: zy = rect.height * (1 - GRAPH_PADDING) elif self._position == POSITION_TOP: zy = rect.height * GRAPH_PADDING if rect.height * GRAPH_PADDING <= zy and rect.height * (1 - GRAPH_PADDING) >= zy: context.set_source_rgb(0, 0, 0) #draw the line: context.move_to(rect.width * GRAPH_PADDING, zy) context.line_to(rect.width * (1 - GRAPH_PADDING), zy) context.stroke() #draw arrow: context.move_to(rect.width * (1 - GRAPH_PADDING) + 3, zy) context.rel_line_to(-3, -3) context.rel_line_to(0, 6) context.close_path() context.fill() if self._show_label: self._do_draw_label(context, rect, (rect.width * (1 - GRAPH_PADDING) + 3, zy)) self._do_draw_tics(context, rect, yaxis) class YAxis(Axis): """ This class represents the yaxis. It is used by the LineChart widget internally, there is no need to create an instance yourself. Properties ========== The YAxis class inherits properties from Axis. Signals ======= The YAxis class inherits signals from Axis. """ def __init__(self, range_calc): Axis.__init__(self, range_calc, "y") def draw(self, context, rect, xaxis): """ This method is called by the parent Plot instance. It calls _do_draw. """ if self._show: if not self._antialias: context.set_antialias(cairo.ANTIALIAS_NONE) self._do_draw(context, rect, xaxis) context.set_antialias(cairo.ANTIALIAS_DEFAULT) def _do_draw_tics(self, context, rect, xaxis): if self._show_tics: tics = self._range_calc.get_ytics(rect) #calculate xaxis position (zx, zy) = self._range_calc.get_absolute_zero(rect, xaxis, self) if xaxis.get_position() == POSITION_BOTTOM: zy = rect.height * (1 - GRAPH_PADDING) elif xaxis.get_position() == POSITION_TOP: zy = rect.height * GRAPH_PADDING for ((x,y), val) in tics: if self._position == POSITION_LEFT: x = rect.width * GRAPH_PADDING elif self._position == POSITION_RIGHT: x = rect.width * (1 - GRAPH_PADDING) tic_width = rect.height / 80.0 context.move_to(x + tic_width / 2, y) context.rel_line_to(- tic_width, 0) context.stroke() if self._show_tic_labels: if abs(y - zy) < 10: #distance to xaxis is to small => do not draw label continue pos = x - tic_width, y text = self._tic_format_function(val) tic_label = label.Label(pos, text, anchor=label.ANCHOR_RIGHT_CENTER, fixed=True) tic_label.draw(context, rect) def _do_draw_label(self, context, rect, pos): axis_label = label.Label(pos, self._label, anchor=label.ANCHOR_BOTTOM_CENTER, fixed=True) axis_label.draw(context, rect) def _do_draw(self, context, rect, xaxis): (zx, zy) = self._range_calc.get_absolute_zero(rect, xaxis, self) if self._position == POSITION_LEFT: zx = rect.width * GRAPH_PADDING elif self._position == POSITION_RIGHT: zx = rect.width * (1 - GRAPH_PADDING) if rect.width * GRAPH_PADDING <= zx and rect.width * (1 - GRAPH_PADDING) >= zx: context.set_source_rgb(0, 0, 0) #draw line: context.move_to(zx, rect.height * (1 - GRAPH_PADDING)) context.line_to(zx, rect.height * GRAPH_PADDING) context.stroke() #draw arrow: context.move_to(zx, rect.height * GRAPH_PADDING - 3) context.rel_line_to(-3, 3) context.rel_line_to(6, 0) context.close_path() context.fill() if self._show_label: self._do_draw_label(context, rect, (zx, rect.height * GRAPH_PADDING - 3)) self._do_draw_tics(context, rect, xaxis) class Grid(ChartObject): """ A class representing the grid of the chart. It is used by the LineChart widget internally, there is no need to create an instance yourself. Properties ========== The Grid class inherits properties from chart_object.ChartObject. Additional properties: - show-horizontal (sets whther to show horizontal grid lines, type: boolean) - show-vertical (sets whther to show vertical grid lines, type: boolean) - color (the color of the grid lines, type: gtk.gdk.Color) - line-style-horizontal (the line style of the horizontal grid lines, type: a line style constant) - line-style-vertical (the line style of the vertical grid lines, type: a line style constant). Signals ======= The Grid class inherits signals from chart_object.ChartObject. """ __gproperties__ = {"show-horizontal": (gobject.TYPE_BOOLEAN, "show horizontal lines", "Set whether to draw horizontal lines.", True, gobject.PARAM_READWRITE), "show-vertical": (gobject.TYPE_BOOLEAN, "show vertical lines", "Set whether to draw vertical lines.", True, gobject.PARAM_READWRITE), "color": (gobject.TYPE_PYOBJECT, "grid color", "The color of the grid in (r,g,b) format. r,g,b in [0,1]", gobject.PARAM_READWRITE), "line-style-horizontal": (gobject.TYPE_INT, "horizontal line style", "Line Style for the horizontal grid lines", 0, 3, 0, gobject.PARAM_READWRITE), "line-style-vertical": (gobject.TYPE_INT, "vertical line style", "Line Style for the vertical grid lines", 0, 3, 0, gobject.PARAM_READWRITE)} def __init__(self, range_calc): ChartObject.__init__(self) self.set_property("antialias", False) self._range_calc = range_calc self._color = gtk.gdk.color_parse("#DEDEDE") self._show_h = True self._show_v = True self._line_style_h = pygtk_chart.LINE_STYLE_SOLID self._line_style_v = pygtk_chart.LINE_STYLE_SOLID def do_get_property(self, property): if property.name == "visible": return self._show elif property.name == "antialias": return self._antialias elif property.name == "show-horizontal": return self._show_h elif property.name == "show-vertical": return self._show_v elif property.name == "color": return self._color elif property.name == "line-style-horizontal": return self._line_style_h elif property.name == "line-style-vertical": return self._line_style_v else: raise AttributeError, "Property %s does not exist." % property.name def do_set_property(self, property, value): if property.name == "visible": self._show = value elif property.name == "antialias": self._antialias = value elif property.name == "show-horizontal": self._show_h = value elif property.name == "show-vertical": self._show_v = value elif property.name == "color": self._color = value elif property.name == "line-style-horizontal": self._line_style_h = value elif property.name == "line-style-vertical": self._line_style_v = value else: raise AttributeError, "Property %s does not exist." % property.name def _do_draw(self, context, rect, xaxis, yaxis): context.set_source_rgb(*color_gdk_to_cairo(self._color)) #draw horizontal lines if self._show_h: set_context_line_style(context, self._line_style_h) ytics = self._range_calc.get_ytics(rect) xa = rect.width * GRAPH_PADDING xb = rect.width * (1 - GRAPH_PADDING) for ((x, y), label) in ytics: context.move_to(xa, y) context.line_to(xb, y) context.stroke() context.set_dash([]) #draw vertical lines if self._show_v: set_context_line_style(context, self._line_style_v) xtics = self._range_calc.get_xtics(rect) ya = rect.height * GRAPH_PADDING yb = rect.height * (1 - GRAPH_PADDING) for ((x, y), label) in xtics: context.move_to(x, ya) context.line_to(x, yb) context.stroke() context.set_dash([]) def set_draw_horizontal_lines(self, draw): """ Set whether to draw horizontal grid lines. @type draw: boolean. """ self.set_property("show-horizontal", draw) self.emit("appearance_changed") def get_draw_horizontal_lines(self): """ Returns True if horizontal grid lines are drawn. @return: boolean. """ return self.get_property("show-horizontal") def set_draw_vertical_lines(self, draw): """ Set whether to draw vertical grid lines. @type draw: boolean. """ self.set_property("show-vertical", draw) self.emit("appearance_changed") def get_draw_vertical_lines(self): """ Returns True if vertical grid lines are drawn. @return: boolean. """ return self.get_property("show-vertical") def set_color(self, color): """ Set the color of the grid. @type color: gtk.gdk.Color @param color: The new color of the grid. """ self.set_property("color", color) self.emit("appearance_changed") def get_color(self): """ Returns the color of the grid. @return: gtk.gdk.Color. """ return self.get_property("color") def set_line_style_horizontal(self, style): """ Set the line style of the horizontal grid lines. style has to be one of these constants: - pygtk_chart.LINE_STYLE_SOLID (default) - pygtk_chart.LINE_STYLE_DOTTED - pygtk_chart.LINE_STYLE_DASHED - pygtk_chart.LINE_STYLE_DASHED_ASYMMETRIC. @param style: the new line style @type style: one of the constants above. """ self.set_property("line-style-horizontal", style) self.emit("appearance_changed") def get_line_style_horizontal(self): """ Returns ths current horizontal line style. @return: a line style constant. """ return self.get_property("line-style-horizontal") def set_line_style_vertical(self, style): """ Set the line style of the vertical grid lines. style has to be one of these constants: - pygtk_chart.LINE_STYLE_SOLID (default) - pygtk_chart.LINE_STYLE_DOTTED - pygtk_chart.LINE_STYLE_DASHED - pygtk_chart.LINE_STYLE_DASHED_ASYMMETRIC. @param style: the new line style @type style: one of the constants above. """ self.set_property("line-style-vertical", style) self.emit("appearance_changed") def get_line_style_vertical(self): """ Returns ths current vertical line style. @return: a line style constant. """ return self.get_property("line-style-vertical") class Graph(ChartObject): """ This class represents a graph or the data you want to plot on your LineChart widget. Properties ========== The Graph class inherits properties from chart_object.ChartObject. Additional properties: - name (a unique id for the graph, type: string, read only) - title (the graph's title, type: string) - color (the graph's color, type: gtk.gdk.Color) - type (graph type, type: a graph type constant) - point-size (radius of the datapoints in px, type: int in [1, 100]) - fill-to (set how to fill space under the graph, type: None, Graph or float) - fill-color (the color of the filling, type: gtk.gdk.Color) - fill-opacity (the opacity of the filling, type: float in [0, 1]) - show-values (sets whether y values should be shown at the datapoints, type: boolean) - show-title (sets whether ot show the graph's title, type: boolean) - line-style (the graph's line style, type: a line style constant) - point-style (the graph's datapoints' point style, type: a point style constant) - clickable (sets whether datapoints are sensitive for clicks, type: boolean) - show-xerrors (sets whether x errors should be shown if error data is available, type: boolean) - show-yerrors (sets whether y errors should be shown if error data is available, type: boolean). Signals ======= The Graph class inherits signals from chart_object.ChartObject. """ __gproperties__ = {"name": (gobject.TYPE_STRING, "graph id", "The graph's unique name.", "", gobject.PARAM_READABLE), "title": (gobject.TYPE_STRING, "graph title", "The title of the graph.", "", gobject.PARAM_READWRITE), "color": (gobject.TYPE_PYOBJECT, "graph color", "The color of the graph in (r,g,b) format. r,g,b in [0,1].", gobject.PARAM_READWRITE), "type": (gobject.TYPE_INT, "graph type", "The type of the graph.", 1, 3, 3, gobject.PARAM_READWRITE), "point-size": (gobject.TYPE_INT, "point size", "Radius of the data points.", 1, 100, 2, gobject.PARAM_READWRITE), "fill-to": (gobject.TYPE_PYOBJECT, "fill to", "Set how to fill space under the graph.", gobject.PARAM_READWRITE), "fill-color": (gobject.TYPE_PYOBJECT, "fill color", "Set which color to use when filling space under the graph.", gobject.PARAM_READWRITE), "fill-opacity": (gobject.TYPE_FLOAT, "fill opacity", "Set which opacity to use when filling space under the graph.", 0.0, 1.0, 0.3, gobject.PARAM_READWRITE), "show-values": (gobject.TYPE_BOOLEAN, "show values", "Sets whether to show the y values.", False, gobject.PARAM_READWRITE), "show-title": (gobject.TYPE_BOOLEAN, "show title", "Sets whether to show the graph's title.", True, gobject.PARAM_READWRITE), "line-style": (gobject.TYPE_INT, "line style", "The line style to use.", 0, 3, 0, gobject.PARAM_READWRITE), "point-style": (gobject.TYPE_PYOBJECT, "point style", "The graph's point style.", gobject.PARAM_READWRITE), "clickable": (gobject.TYPE_BOOLEAN, "clickable", "Sets whether datapoints should be clickable.", True, gobject.PARAM_READWRITE), "show-xerrors": (gobject.TYPE_BOOLEAN, "show xerrors", "Set whether to show x-errorbars.", True, gobject.PARAM_READWRITE), "show-yerrors": (gobject.TYPE_BOOLEAN, "show yerrors", "Set whether to show y-errorbars.", True, gobject.PARAM_READWRITE)} def __init__(self, name, title, data): """ Create a new graph instance. data should be a list of x,y pairs. If you want to provide error data for a datapoint, the tuple for that point has to be (x, y, xerror, yerror). If you want only one error, set the other to zero. You can mix datapoints with and without error data in data. @type name: string @param name: A unique name for the graph. This could be everything. It's just a name used internally for identification. You need to know this if you want to access or delete a graph from a chart. @type title: string @param title: The graphs title. This can be drawn on the chart. @type data: list (see above) @param data: This is the data you want to be visualized. For detail see description above. """ ChartObject.__init__(self) self._name = name self._title = title self._data, self._errors = separate_data_and_errors(data) self._color = COLOR_AUTO self._type = GRAPH_BOTH self._point_size = 2 self._show_value = False self._show_title = True self._fill_to = None self._fill_color = COLOR_AUTO self._fill_opacity = 0.3 self._line_style = pygtk_chart.LINE_STYLE_SOLID self._point_style = pygtk_chart.POINT_STYLE_CIRCLE self._clickable = True self._draw_xerrors = True self._draw_yerrors = True self._range_calc = None self._label = label.Label((0, 0), self._title, anchor=label.ANCHOR_LEFT_CENTER) def do_get_property(self, property): if property.name == "visible": return self._show elif property.name == "antialias": return self._antialias elif property.name == "name": return self._name elif property.name == "title": return self._title elif property.name == "color": return self._color elif property.name == "type": return self._type elif property.name == "point-size": return self._point_size elif property.name == "fill-to": return self._fill_to elif property.name == "fill-color": return self._fill_color elif property.name == "fill-opacity": return self._fill_opacity elif property.name == "show-values": return self._show_value elif property.name == "show-title": return self._show_title elif property.name == "line-style": return self._line_style elif property.name == "point-style": return self._point_style elif property.name == "clickable": return self._clickable elif property.name == "show-xerrors": return self._draw_xerrors elif property.name == "show-yerrors": return self._draw_yerrors else: raise AttributeError, "Property %s does not exist." % property.name def do_set_property(self, property, value): if property.name == "visible": self._show = value elif property.name == "antialias": self._antialias = value elif property.name == "title": self._label.set_text(value) self._title = value elif property.name == "color": self._color = value elif property.name == "type": self._type = value elif property.name == "point-size": self._point_size = value elif property.name == "fill-to": self._fill_to = value elif property.name == "fill-color": self._fill_color = value elif property.name == "fill-opacity": self._fill_opacity = value elif property.name == "show-values": self._show_value = value elif property.name == "show-title": self._show_title = value elif property.name == "line-style": self._line_style = value elif property.name == "point-style": self._point_style = value elif property.name == "clickable": self._clickable = value elif property.name == "show-xerrors": self._draw_xerrors = value elif property.name == "show-yerrors": self._draw_yerrors = value else: raise AttributeError, "Property %s does not exist." % property.name def has_something_to_draw(self): return self._data != [] def _do_draw_lines(self, context, rect, xrange, yrange, xaxis, yaxis): context.set_source_rgb(*color_gdk_to_cairo(self._color)) set_context_line_style(context, self._line_style) first_point = None last_point = None for (x, y) in self._data: if xaxis.get_logarithmic(): x = math.log10(x) if yaxis.get_logarithmic(): y = math.log10(y) if is_in_range(x, xrange) and is_in_range(y, yrange): (ax, ay) = self._range_calc.get_absolute_point(rect, x, y, xaxis, yaxis) if first_point == None: context.move_to(ax, ay) first_point = x, y else: context.line_to(ax, ay) last_point = ax, ay context.stroke() context.set_dash([]) return first_point, last_point def _do_draw_points(self, context, rect, xrange, yrange, xaxis, yaxis, highlighted_points): context.set_source_rgb(*color_gdk_to_cairo(self._color)) first_point = None last_point = None for (x, y) in self._data: if xaxis.get_logarithmic(): x = math.log10(x) if yaxis.get_logarithmic(): y = math.log10(y) if is_in_range(x, xrange) and is_in_range(y, yrange): (ax, ay) = self._range_calc.get_absolute_point(rect, x, y, xaxis, yaxis) if self._clickable: chart.add_sensitive_area(chart.AREA_CIRCLE, (ax, ay, self._point_size), (x, y, self)) if first_point == None: context.move_to(ax, ay) #draw errors draw_errors(context, rect, self._range_calc, x, y, self._errors, self._draw_xerrors, self._draw_yerrors, xaxis, yaxis, self._point_size) #draw the point if type(self._point_style) != gtk.gdk.Pixbuf: draw_point(context, ax, ay, self._point_size, self._point_style) highlighted = (x, y, self) in highlighted_points if highlighted and self._clickable: context.set_source_rgba(1, 1, 1, 0.3) draw_point(context, ax, ay, self._point_size, self._point_style) context.set_source_rgb(*color_gdk_to_cairo(self._color)) else: draw_point_pixbuf(context, ax, ay, self._point_style) last_point = ax, ay return first_point, last_point def _do_draw_values(self, context, rect, xrange, yrange, xaxis, yaxis): anchors = {} first_point = True for i, (x, y) in enumerate(self._data): if xaxis.get_logarithmic(): x = math.log10(x) if yaxis.get_logarithmic(): y = math.log10(y) if is_in_range(x, xrange) and is_in_range(y, yrange): next_point = None if i + 1 < len(self._data) and (is_in_range(self._data[i + 1][0], xrange) and is_in_range(self._data[i + 1][1], yrange)): next_point = self._data[i + 1] if first_point: if next_point != None: if next_point[1] >= y: anchors[(x, y)] = label.ANCHOR_TOP_LEFT else: anchors[(x, y)] = label.ANCHOR_BOTTOM_LEFT first_point = False else: previous_point = self._data[i - 1] if next_point != None: if previous_point[1] <= y <= next_point[1]: anchors[(x, y)] = label.ANCHOR_BOTTOM_RIGHT elif previous_point[1] > y > next_point[1]: anchors[(x, y)] = label.ANCHOR_BOTTOM_LEFT elif previous_point[1] < y and next_point[1] < y: anchors[(x, y)] = label.ANCHOR_BOTTOM_CENTER elif previous_point[1] > y and next_point[1] > y: anchors[(x, y)] = label.ANCHOR_TOP_CENTER else: if previous_point[1] >= y: anchors[(x, y)] = label.ANCHOR_TOP_RIGHT else: anchors[(x, y)] = label.ANCHOR_BOTTOM_RIGHT for x, y in self._data: if xaxis.get_logarithmic(): x = math.log10(x) if yaxis.get_logarithmic(): y = math.log10(y) if (x, y) in anchors and is_in_range(x, xrange) and is_in_range(y, yrange): (ax, ay) = self._range_calc.get_absolute_point(rect, x, y, xaxis, yaxis) value_label = label.Label((ax, ay), str(y), anchor=anchors[(x, y)]) value_label.set_color(self._color) value_label.draw(context, rect) def _do_draw_title(self, context, rect, last_point, xaxis, yaxis): """ Draws the title. @type context: cairo.Context @param context: The context to draw on. @type rect: gtk.gdk.Rectangle @param rect: A rectangle representing the charts area. @type last_point: pairs of numbers @param last_point: The absolute position of the last drawn data point. """ if last_point: x = last_point[0] + 5 y = last_point[1] self._label.set_position((x, y)) self._label.set_color(self._color) self._label.draw(context, rect) def _do_draw_fill(self, context, rect, xrange, xaxis, yaxis): if type(self._fill_to) in (int, float): data = [] for i, (x, y) in enumerate(self._data): if xaxis.get_logarithmic(): x = math.log10(x) if yaxis.get_logarithmic(): y = math.log10(y) if is_in_range(x, xrange) and not data: data.append((x, self._fill_to)) elif not is_in_range(x, xrange) and len(data) == 1: data.append((prev, self._fill_to)) break elif i == len(self._data) - 1: data.append((x, self._fill_to)) prev = x graph = Graph("none", "", data) elif type(self._fill_to) == Graph: graph = self._fill_to d = graph.get_data() range_b = d[0][0], d[-1][0] xrange = intersect_ranges(xrange, range_b) if not graph.get_visible(): return c = self._fill_color if c == COLOR_AUTO: c = self._color c = color_gdk_to_cairo(c) context.set_source_rgba(c[0], c[1], c[2], self._fill_opacity) data_a = self._data data_b = graph.get_data() first = True start_point = (0, 0) for x, y in data_a: if xaxis.get_logarithmic(): x = math.log10(x) if yaxis.get_logarithmic(): y = math.log10(y) if is_in_range(x, xrange): xa, ya = self._range_calc.get_absolute_point(rect, x, y, xaxis, yaxis) if first: context.move_to(xa, ya) start_point = xa, ya first = False else: context.line_to(xa, ya) first = True for i in range(0, len(data_b)): j = len(data_b) - i - 1 x, y = data_b[j] xa, ya = self._range_calc.get_absolute_point(rect, x, y, xaxis, yaxis) if is_in_range(x, xrange): context.line_to(xa, ya) context.line_to(*start_point) context.fill() def _do_draw(self, context, rect, xaxis, yaxis, highlighted_points): """ Draw the graph. @type context: cairo.Context @param context: The context to draw on. @type rect: gtk.gdk.Rectangle @param rect: A rectangle representing the charts area. """ (xrange, yrange) = self._range_calc.get_ranges(xaxis, yaxis) if self._type in [GRAPH_LINES, GRAPH_BOTH]: first_point, last_point = self._do_draw_lines(context, rect, xrange, yrange, xaxis, yaxis) if self._type in [GRAPH_POINTS, GRAPH_BOTH]: first_point, last_point = self._do_draw_points(context, rect, xrange, yrange, xaxis, yaxis, highlighted_points) if self._fill_to != None: self._do_draw_fill(context, rect, xrange, xaxis, yaxis) if self._show_value and self._type in [GRAPH_POINTS, GRAPH_BOTH]: self._do_draw_values(context, rect, xrange, yrange, xaxis, yaxis) if self._show_title: self._do_draw_title(context, rect, last_point, xaxis, yaxis) def get_x_range(self): """ Get the the endpoints of the x interval. @return: pair of numbers """ try: self._data.sort(lambda x, y: cmp(x[0], y[0])) return (self._data[0][0], self._data[-1][0]) except: return None def get_y_range(self): """ Get the the endpoints of the y interval. @return: pair of numbers """ try: self._data.sort(lambda x, y: cmp(x[1], y[1])) return (self._data[0][1], self._data[-1][1]) except: return None def get_name(self): """ Get the name of the graph. @return: string """ return self.get_property("name") def get_title(self): """ Returns the title of the graph. @return: string """ return self.get_property("title") def set_title(self, title): """ Set the title of the graph. @type title: string @param title: The graph's new title. """ self.set_property("title", title) self.emit("appearance_changed") def set_range_calc(self, range_calc): self._range_calc = range_calc def get_color(self): """ Returns the current color of the graph or COLOR_AUTO. @return: gtk.gdk.Color or COLOR_AUTO. """ return self.get_property("color") def set_color(self, color): """ Set the color of the graph. If set to COLOR_AUTO, the color will be choosen dynamicly. @type color: gtk.gdk.Color @param color: The new color of the graph. """ self.set_property("color", color) self.emit("appearance_changed") def get_type(self): """ Returns the type of the graph. @return: a type constant (see set_type() for details) """ return self.get_property("type") def set_type(self, type): """ Set the type of the graph to one of these: - GRAPH_POINTS: only show points - GRAPH_LINES: only draw lines - GRAPH_BOTH: draw points and lines, i.e. connect points with lines @param type: One of the constants above. """ self.set_property("type", type) self.emit("appearance_changed") def get_point_size(self): """ Returns the radius of the data points. @return: a poisitive integer """ return self.get_property("point_size") def set_point_size(self, size): """ Set the radius of the drawn points. @type size: a positive integer in [1, 100] @param size: The new radius of the points. """ self.set_property("point_size", size) self.emit("appearance_changed") def get_fill_to(self): """ The return value of this method depends on the filling under the graph. See set_fill_to() for details. """ return self.get_property("fill-to") def set_fill_to(self, fill_to): """ Use this method to specify how the space under the graph should be filled. fill_to has to be one of these: - None: dont't fill the space under the graph. - int or float: fill the space to the value specified (setting fill_to=0 means filling the space between graph and xaxis). - a Graph object: fill the space between this graph and the graph given as the argument. The color of the filling is the graph's color with 30% opacity. @type fill_to: one of the possibilities listed above. """ self.set_property("fill-to", fill_to) self.emit("appearance_changed") def get_fill_color(self): """ Returns the color that is used to fill space under the graph or COLOR_AUTO. @return: gtk.gdk.Color or COLOR_AUTO. """ return self.get_property("fill-color") def set_fill_color(self, color): """ Set which color should be used when filling the space under a graph. If color is COLOR_AUTO, the graph's color will be used. @type color: gtk.gdk.Color or COLOR_AUTO. """ self.set_property("fill-color", color) self.emit("appearance_changed") def get_fill_opacity(self): """ Returns the opacity that is used to fill space under the graph. """ return self.get_property("fill-opacity") def set_fill_opacity(self, opacity): """ Set which opacity should be used when filling the space under a graph. The default is 0.3. @type opacity: float in [0, 1]. """ self.set_property("fill-opacity", opacity) self.emit("appearance_changed") def get_show_values(self): """ Returns True if y values are shown. @return: boolean """ return self.get_property("show-values") def set_show_values(self, show): """ Set whether the y values should be shown (only if graph type is GRAPH_POINTS or GRAPH_BOTH). @type show: boolean """ self.set_property("show-values", show) self.emit("appearance_changed") def get_show_title(self): """ Returns True if the title of the graph is shown. @return: boolean. """ return self.get_property("show-title") def set_show_title(self, show): """ Set whether to show the graph's title or not. @type show: boolean. """ self.set_property("show-title", show) self.emit("appearance_changed") def add_data(self, data_list): """ Add data to the graph. data_list should be a list of x,y pairs. If you want to provide error data for a datapoint, the tuple for that point has to be (x, y, xerror, yerror). If you want only one error, set the other to zero. You can mix datapoints with and without error data in data_list. @type data_list: a list (see above). """ new_data, new_errors = separate_data_and_errors(data_list) self._data += new_data self._errors = dict(self._errors, **new_errors) self._range_calc.add_graph(self) def get_data(self): """ Returns the data of the graph. @return: a list of x, y pairs. """ return self._data def set_line_style(self, style): """ Set the line style that should be used for drawing the graph (if type is line_chart.GRAPH_LINES or line_chart.GRAPH_BOTH). style has to be one of these constants: - pygtk_chart.LINE_STYLE_SOLID (default) - pygtk_chart.LINE_STYLE_DOTTED - pygtk_chart.LINE_STYLE_DASHED - pygtk_chart.LINE_STYLE_DASHED_ASYMMETRIC. @param style: the new line style @type style: one of the line style constants above. """ self.set_property("line-style", style) self.emit("appearance_changed") def get_line_style(self): """ Returns the current line style for the graph (see L{set_line_style} for details). @return: a line style constant. """ return self.get_property("line-style") def set_point_style(self, style): """ Set the point style that should be used when drawing the graph (if type is line_chart.GRAPH_POINTS or line_chart.GRAPH_BOTH). For style you can use one of these constants: - pygtk_chart.POINT_STYLE_CIRCLE (default) - pygtk_chart.POINT_STYLE_SQUARE - pygtk_chart.POINT_STYLE_CROSS - pygtk_chart.POINT_STYLE_TRIANGLE_UP - pygtk_chart.POINT_STYLE_TRIANGLE_DOWN - pygtk_chart.POINT_STYLE_DIAMOND style can also be a gtk.gdk.Pixbuf that should be used as point. @param style: the new point style @type style: one of the cosnatnts above or gtk.gdk.Pixbuf. """ self.set_property("point-style", style) self.emit("appearance_changed") def get_point_style(self): """ Returns the current point style. See L{set_point_style} for details. @return: a point style constant or gtk.gdk.Pixbuf. """ return self.get_property("point-style") def set_clickable(self, clickable): """ Set whether the datapoints of the graph should be clickable (only if the datapoints are shown). If this is set to True, the LineChart will emit the signal 'datapoint-clicked' when a datapoint was clicked. @type clickable: boolean. """ self.set_property("clickable", clickable) self.emit("appearance_changed") def get_clickable(self): """ Returns True if the datapoints of the graph are clickable. @return: boolean. """ return self.get_property("clickable") def set_show_xerrors(self, show): """ Use this method to set whether x-errorbars should be shown if error data is available. @type show: boolean. """ self.set_property("show-xerrors", show) self.emit("appearance_changed") def get_show_xerrors(self): """ Returns True if x-errorbars should be drawn if error data is available. @return: boolean. """ return self.get_property("show-xerrors") def set_show_yerrors(self, show): """ Use this method to set whether y-errorbars should be shown if error data is available. @type show: boolean. """ self.set_property("show-yerrors", show) self.emit("appearance_changed") def get_show_yerrors(self): """ Returns True if y-errorbars should be drawn if error data is available. @return: boolean. """ return self.get_property("show-yerrors") def graph_new_from_function(func, xmin, xmax, graph_name, samples=100, do_optimize_sampling=True): """ Returns a line_chart.Graph with data created from the function y = func(x) with x in [xmin, xmax]. The id of the new graph is graph_name. The parameter samples gives the number of points that should be evaluated in [xmin, xmax] (default: 100). If do_optimize_sampling is True (default) additional points will be evaluated to smoothen the curve. @type func: a function @param func: the function to evaluate @type xmin: float @param xmin: the minimum x value to evaluate @type xmax: float @param xmax: the maximum x value to evaluate @type graph_name: string @param graph_name: a unique name for the new graph @type samples: int @param samples: number of samples @type do_optimize_sampling: boolean @param do_optimize_sampling: set whether to add additional points @return: line_chart.Graph """ delta = (xmax - xmin) / float(samples - 1) data = [] x = xmin while x <= xmax: data.append((x, func(x))) x += delta if do_optimize_sampling: data = optimize_sampling(func, data) return Graph(graph_name, "", data) def optimize_sampling(func, data): new_data = [] prev_point = None prev_slope = None for x, y in data: if prev_point != None: if (x - prev_point[0]) == 0: return data slope = (y - prev_point[1]) / (x - prev_point[0]) if prev_slope != None: if abs(slope - prev_slope) >= 0.1: nx = prev_point[0] + (x - prev_point[0]) / 2.0 ny = func(nx) new_data.append((nx, ny)) #print abs(slope - prev_slope), prev_point[0], nx, x prev_slope = slope prev_point = x, y if new_data: data += new_data data.sort(lambda x, y: cmp(x[0], y[0])) return optimize_sampling(func, data) else: return data def graph_new_from_file(filename, graph_name, x_col=0, y_col=1, xerror_col=-1, yerror_col=-1): """ Returns a line_chart.Graph with point taken from data file filename. The id of the new graph is graph_name. Data file format: The columns in the file have to be separated by tabs or one or more spaces. Everything after '#' is ignored (comment). Use the parameters x_col and y_col to control which columns to use for plotting. By default, the first column (x_col=0) is used for x values, the second (y_col=1) is used for y values. The parameters xerror_col and yerror_col should point to the column in which the x/y error values are. If you do not want to provide x or y error data, omit the paramter or set it to -1 (default). @type filename: string @param filename: path to the data file @type graph_name: string @param graph_name: a unique name for the graph @type x_col: int @param x_col: the number of the column to use for x values @type y_col: int @param y_col: the number of the column to use for y values @type xerror_col: int @param xerror_col: index of the column for x error values @type yerror_col: int @param yerror_col: index of the column for y error values @return: line_chart.Graph """ points = [] f = open(filename, "r") data = f.read() f.close() lines = data.split("\n") for line in lines: line = line.strip() #remove special characters at beginning and end #remove comments: a = line.split("#", 1) if a and a[0]: line = a[0] #get data from line: if line.find("\t") != -1: #col separator is tab d = line.split("\t") else: #col separator is one or more space #normalize to one space: while line.find(" ") != -1: line = line.replace(" ", " ") d = line.split(" ") d = filter(lambda x: x, d) d = map(lambda x: float(x), d) new_data = (d[x_col], d[y_col]) if xerror_col != -1 or yerror_col != -1: xerror = 0 yerror = 0 if xerror_col != -1: xerror = d[xerror_col] if yerror_col != -1: yerror = d[yerror_col] new_data = (d[x_col], d[y_col], xerror, yerror) points.append(new_data) return Graph(graph_name, "", points) class Legend(ChartObject): """ This class represents a legend on a line chart. Properties ========== The Legend class inherits properties from chart_object.ChartObject. Additional properties: - position (the legend's position on the chart, type: a corner position constant). Signals ======= The Legend class inherits signals from chart_object.ChartObject. """ __gproperties__ = {"position": (gobject.TYPE_INT, "legend position", "Position of the legend.", 8, 11, 8, gobject.PARAM_READWRITE)} def __init__(self): ChartObject.__init__(self) self._show = False self._position = POSITION_TOP_RIGHT def do_get_property(self, property): if property.name == "visible": return self._show elif property.name == "antialias": return self._antialias elif property.name == "position": return self._position else: raise AttributeError, "Property %s does not exist." % property.name def do_set_property(self, property, value): if property.name == "visible": self._show = value elif property.name == "antialias": self._antialias = value elif property.name == "position": self._position = value else: raise AttributeError, "Property %s does not exist." % property.name def _do_draw(self, context, rect, graphs): context.set_line_width(1) width = 0.2 * rect.width label_width = width - 12 - 20 x = rect.width - width y = 16 total_height = 0 total_width = 0 for id, graph in graphs.iteritems(): if not graph.get_visible(): continue graph_label = label.Label((x + (width - label_width), y), graph.get_title(), anchor=label.ANCHOR_TOP_LEFT) graph_label.set_max_width(label_width) rwidth, rheight = graph_label.get_calculated_dimensions(context, rect) total_height += rheight + 6 total_width = max(total_width, rwidth) total_width += 18 + 20 if self._position == POSITION_TOP_RIGHT: x = rect.width - total_width - 16 y = 16 elif self._position == POSITION_BOTTOM_RIGHT: x = rect.width - total_width - 16 y = rect.height - 16 - total_height elif self._position == POSITION_BOTTOM_LEFT: x = 16 y = rect.height - 16 - total_height elif self._position == POSITION_TOP_LEFT: x = 16 y = 16 context.set_antialias(cairo.ANTIALIAS_NONE) context.set_source_rgb(1, 1, 1) context.rectangle(x, y - 3, total_width, total_height) context.fill_preserve() context.set_source_rgb(0, 0, 0) context.stroke() context.set_antialias(cairo.ANTIALIAS_DEFAULT) for id, graph in graphs.iteritems(): if not graph.get_visible(): continue #draw the label graph_label = label.Label((x + (width - label_width), y), graph.get_title(), anchor=label.ANCHOR_TOP_LEFT) graph_label.set_max_width(label_width) graph_label.draw(context, rect) #draw line if graph.get_type() in [GRAPH_LINES, GRAPH_BOTH]: lines = graph_label.get_line_count() line_height = graph_label.get_real_dimensions()[1] / lines set_context_line_style(context, graph.get_line_style()) context.set_source_rgb(*color_gdk_to_cairo(graph.get_color())) context.move_to(x + 6, y + line_height / 2) context.rel_line_to(20, 0) context.stroke() #draw point if graph.get_type() in [GRAPH_POINTS, GRAPH_BOTH]: lines = graph_label.get_line_count() line_height = graph_label.get_real_dimensions()[1] / lines context.set_source_rgb(*color_gdk_to_cairo(graph.get_color())) if type(graph.get_point_style()) != gtk.gdk.Pixbuf: draw_point(context, x + 6 + 20, y + line_height / 2, graph.get_point_size(), graph.get_point_style()) else: draw_point_pixbuf(context, x + 6 + 20, y + line_height / 2, graph.get_point_style()) y += graph_label.get_real_dimensions()[1] + 6 def set_position(self, position): """ Set the position of the legend. position has to be one of these position constants: - line_chart.POSITION_TOP_RIGHT (default) - line_chart.POSITION_BOTTOM_RIGHT - line_chart.POSITION_BOTTOM_LEFT - line_chart.POSITION_TOP_LEFT @param position: the legend's position @type position: one of the constants above. """ self.set_property("position", position) self.emit("appearance_changed") def get_position(self): """ Returns the position of the legend. See L{set_position} for details. @return: a position constant. """ return self.get_property("position") gpxviewer-0.4.3/gpxviewer/pygtk_chart/multi_bar_chart.py0000664000175000017500000006663211752304676023162 0ustar andrewandrew# Copyright 2009 Sven Festersen # # 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 2 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. """ Contains the MultiBarChart widget. Author: Sven Festersen (sven@sven-festersen.de) """ __docformat__ = "epytext" import cairo import gtk import gobject import os import math import pygtk_chart from pygtk_chart.basics import * from pygtk_chart import bar_chart from pygtk_chart.chart_object import ChartObject from pygtk_chart import chart from pygtk_chart import label MODE_VERTICAL = 0 MODE_HORIZONTAL = 1 COLOR_AUTO = 0 COLORS = gdk_color_list_from_file(os.sep.join([os.path.dirname(__file__), "data", "tango.color"])) class Bar(bar_chart.Bar): """ This is a special version of the bar_chart.Bar class that draws the bars on a MultiBarChart widget. Properties ========== This class inherits properties from bar_chart.Bar. Signals ======= This class inherits signals from bar_chart.Bar. """ def __init__(self, name, value, title=""): bar_chart.Bar.__init__(self, name, value, title) #drawing methods def _do_draw(self, context, rect, group, bar_count, n, i, m, j, mode, group_padding, bar_padding, maximum_value, group_end, value_label_size, label_size, label_rotation, draw_labels): if mode == MODE_VERTICAL: return self._do_draw_multi_vertical(context, rect, group, bar_count, n, i, m, j, mode, group_padding, bar_padding, maximum_value, group_end, value_label_size, label_size, label_rotation, draw_labels) elif mode == MODE_HORIZONTAL: return self._do_draw_multi_horizontal(context, rect, group, bar_count, n, i, m, j, mode, group_padding, bar_padding, maximum_value, group_end, value_label_size, label_size, label_rotation, draw_labels) def _do_draw_multi_vertical(self, context, rect, group, bar_count, n, i, m, j, mode, group_padding, bar_padding, maximum_value, group_end, value_label_size, label_size, label_rotation, draw_labels): bar_width = (rect.width - (bar_count - n) * bar_padding - (n - 1) * group_padding) / bar_count bar_height = (rect.height - value_label_size - label_size) * self._value / maximum_value bar_x = group_end + j * (bar_width + bar_padding) bar_y = rect.y + rect.height - bar_height - label_size context.set_source_rgb(*color_gdk_to_cairo(self._color)) bar_chart.draw_rounded_rectangle(context, bar_x, bar_y, bar_width, bar_height, self._corner_radius) context.fill() chart.add_sensitive_area(chart.AREA_RECTANGLE, (bar_x, bar_y, bar_width, bar_height), (group, self)) if self._highlighted: context.set_source_rgba(1, 1, 1, 0.1) bar_chart.draw_rounded_rectangle(context, bar_x, bar_y, bar_width, bar_height, self._corner_radius) context.fill() if draw_labels: #draw the value label self._value_label_object.set_max_width(bar_width) self._value_label_object.set_text(str(self._value)) self._value_label_object.set_color(self._color) self._value_label_object.set_position((bar_x + bar_width / 2, bar_y - 3)) self._value_label_object.set_anchor(label.ANCHOR_BOTTOM_CENTER) self._value_label_object.draw(context, rect) context.fill() #draw label self._label_object.set_rotation(label_rotation) self._label_object.set_wrap(False) self._label_object.set_color(self._color) self._label_object.set_fixed(True) self._label_object.set_max_width(3 * bar_width) self._label_object.set_text(self._label) self._label_object.set_position((bar_x + bar_width / 2 + 5, bar_y + bar_height + 8)) self._label_object.set_anchor(label.ANCHOR_TOP_RIGHT) self._label_object.draw(context, rect) context.fill() return bar_x + bar_width def _do_draw_multi_horizontal(self, context, rect, group, bar_count, n, i, m, j, mode, group_padding, bar_padding, maximum_value, group_end, value_label_size, label_size, label_rotation, draw_labels): bar_height = (rect.height - (bar_count - n) * bar_padding - (n - 1) * group_padding) / bar_count bar_width = (rect.width - value_label_size - label_size) * self._value / maximum_value bar_x = rect.x + label_size bar_y = group_end + j * (bar_height + bar_padding) context.set_source_rgb(*color_gdk_to_cairo(self._color)) bar_chart.draw_rounded_rectangle(context, bar_x, bar_y, bar_width, bar_height, self._corner_radius) context.fill() chart.add_sensitive_area(chart.AREA_RECTANGLE, (bar_x, bar_y, bar_width, bar_height), (group, self)) if self._highlighted: context.set_source_rgba(1, 1, 1, 0.1) bar_chart.draw_rounded_rectangle(context, bar_x, bar_y, bar_width, bar_height, self._corner_radius) context.fill() if draw_labels: #draw the value label self._value_label_object.set_text(str(self._value)) self._value_label_object.set_wrap(False) self._value_label_object.set_color(self._color) self._value_label_object.set_position((bar_x + bar_width + 3, bar_y + bar_height / 2)) self._value_label_object.set_anchor(label.ANCHOR_LEFT_CENTER) self._value_label_object.draw(context, rect) context.fill() #draw label self._label_object.set_rotation(0) self._label_object.set_wrap(False) self._label_object.set_color(self._color) self._label_object.set_fixed(True) self._label_object.set_max_width(0.25 * rect.width) self._label_object.set_text(self._label) self._label_object.set_position((bar_x - 3, bar_y + bar_height / 2)) self._label_object.set_anchor(label.ANCHOR_RIGHT_CENTER) self._label_object.draw(context, rect) context.fill() return bar_y + bar_height def get_value_label_size(self, context, rect, mode, bar_count, n, group_padding, bar_padding): if mode == MODE_VERTICAL: bar_width = (rect.width - (bar_count - n) * bar_padding - (n - 1) * group_padding) / bar_count self._value_label_object.set_max_width(bar_width) self._value_label_object.set_text(str(self._value)) return self._value_label_object.get_calculated_dimensions(context, rect)[1] elif mode == MODE_HORIZONTAL: self._value_label_object.set_wrap(False) self._value_label_object.set_fixed(True) self._value_label_object.set_text(str(self._value)) return self._value_label_object.get_calculated_dimensions(context, rect)[0] def get_label_size(self, context, rect, mode, bar_count, n, group_padding, bar_padding, label_rotation): if mode == MODE_VERTICAL: bar_width = (rect.width - (bar_count - n) * bar_padding - (n - 1) * group_padding) / bar_count self._label_object.set_rotation(label_rotation) self._label_object.set_wrap(False) self._label_object.set_fixed(True) self._label_object.set_max_width(3 * bar_width) self._label_object.set_text(self._label) return self._label_object.get_calculated_dimensions(context, rect)[1] elif mode == MODE_HORIZONTAL: self._label_object.set_max_width(0.25 * rect.width) self._label_object.set_text(self._label) return self._label_object.get_calculated_dimensions(context, rect)[0] class BarGroup(ChartObject): """ This class represents a group of bars on the MultiBarChart widget. Properties ========== This class has the following properties: - name (a unique identifier for the group, type: string) - title (a title for the group, type: string) - bar-padding (the space between two bars of the group in px, type: int in [0, 100]) - bars (a list of the bars in the group, read only) - maximum-value (the maximum value of the bars in the group, read only) - bar-count (the number of bars in the group, read only). Signals ======= The BarGroup class inherits signals from chart_object.ChartObject. """ __gproperties__ = {"name": (gobject.TYPE_STRING, "group name", "A unique identifier for this group.", "", gobject.PARAM_READABLE), "title": (gobject.TYPE_STRING, "group title", "The group's title.", "", gobject.PARAM_READWRITE), "bar-padding": (gobject.TYPE_INT, "bar padding", "The space between bars in this group.", 0, 100, 2, gobject.PARAM_READWRITE), "bars": (gobject.TYPE_PYOBJECT, "bars in the group", "A list of bars in this group.", gobject.PARAM_READABLE), "maximum-value": (gobject.TYPE_FLOAT, "max value", "The maximum value of the bars in this group.", 0, 9999999, 0, gobject.PARAM_READABLE), "bar-count": (gobject.TYPE_INT, "bar count", "The number of bars in this group.", 0, 100, 0, gobject.PARAM_READWRITE)} def __init__(self, name, title=""): ChartObject.__init__(self) #private properties: self._group_label_object = label.Label((0, 0), title) #gobject properties: self._bars = [] self._name = name self._title = title self._bar_padding = 2 #gobject set_* and get_* methods def do_get_property(self, property): if property.name == "visible": return self._show elif property.name == "antialias": return self._antialias elif property.name == "name": return self._name elif property.name == "title": return self._title elif property.name == "bar-padding": return self._bar_padding elif property.name == "bars": return self._bars elif property.name == "maximum-value": return max(bar.get_value() for bar in self._bars) elif property.name == "bar-count": return len(self._bars) else: raise AttributeError, "Property %s does not exist." % property.name def do_set_property(self, property, value): if property.name == "visible": self._show = value elif property.name == "antialias": self._antialias = value elif property.name == "name": self._name = value elif property.name == "title": self._title = value elif property.name == "bar-padding": self._bar_padding = value else: raise AttributeError, "Property %s does not exist." % property.name def get_bar_count(self): """ Returns the number of bars in this group. @return: int in [0, 100]. """ return self.get_property("bar-count") def get_maximum_value(self): """ Returns the maximum value of the bars in this group. @return: float. """ return self.get_property("maximum-value") def get_bars(self): """ Returns a list of the bars in this group. @return: list of multi_bar_chart.Bar. """ return self.get_property("bars") def get_name(self): """ Returns the name (a unique identifier) of this group. @return: string. """ return self.get_property("name") def set_title(self, title): """ Set the title of the group. @param title: the new title @type title: string. """ self.set_property("title", title) self.emit("appearance_changed") def get_title(self): """ Returns the title of the group. @return: string. """ return self.get_property("title") def get_label(self): """ Alias for get_title. @return: string. """ return self.get_title() def set_bar_padding(self, padding): """ Set the distance between two bars in this group (in px). @param padding: the padding in px @type padding: int in [0, 100]. """ self.set_property("bar-padding", padding) self.emit("appearance_changed") def get_bar_padding(self): """ Returns the distance of two bars in the group (in px). @return: int in [0, 100]. """ return self.get_property("bar-padding") #drawing methods def _do_draw(self, context, rect, bar_count, n, i, mode, group_padding, maximum_value, group_end, value_label_size, label_size, label_rotation, draw_labels, rotate_label_horizontal): end = group_end for j, bar in enumerate(self._bars): end = bar.draw(context, rect, self, bar_count, n, i, len(self._bars), j, mode, group_padding, self._bar_padding, maximum_value, group_end, value_label_size, label_size, label_rotation, draw_labels) if draw_labels and mode == MODE_VERTICAL: context.set_source_rgb(0, 0, 0) group_width = end - group_end self._group_label_object.set_text(self._title) self._group_label_object.set_fixed(True) self._group_label_object.set_max_width(group_width) self._group_label_object.set_position((group_end + group_width / 2, rect.y + rect.height)) self._group_label_object.set_anchor(label.ANCHOR_BOTTOM_CENTER) self._group_label_object.draw(context, rect) context.fill() elif draw_labels and mode == MODE_HORIZONTAL: context.set_source_rgb(0, 0, 0) group_height = end - group_end if rotate_label_horizontal: self._group_label_object.set_rotation(90) offset = self.get_group_label_size(context, rect, mode, rotate_label_horizontal) #fixes postioning bug else: self._group_label_object.set_rotation(0) offset = 0 self._group_label_object.set_text(self._title) self._group_label_object.set_wrap(False) self._group_label_object.set_fixed(True) self._group_label_object.set_position((rect.x + offset, group_end + group_height / 2)) self._group_label_object.set_anchor(label.ANCHOR_LEFT_CENTER) self._group_label_object.draw(context, rect) context.fill() return end + group_padding #other methods def add_bar(self, bar): """ Add a bar to the group. @param bar: the bar to add @type bar: multi_bar_chart.Bar. """ if bar.get_color() == COLOR_AUTO: bar.set_color(COLORS[len(self._bars) % len(COLORS)]) self._bars.append(bar) self.emit("appearance_changed") def get_value_label_size(self, context, rect, mode, bar_count, n, group_padding, bar_padding): value_label_size = 0 for bar in self._bars: value_label_size = max(value_label_size, bar.get_value_label_size(context, rect, mode, bar_count, n, group_padding, bar_padding)) return value_label_size def get_label_size(self, context, rect, mode, bar_count, n, group_padding, bar_padding, label_rotation): label_size = 0 for bar in self._bars: label_size = max(label_size, bar.get_label_size(context, rect, mode, bar_count, n, group_padding, bar_padding, label_rotation)) return label_size def get_group_label_size(self, context, rect, mode, rotate_label_horizontal): self._group_label_object.set_text(self._title) if mode == MODE_VERTICAL: return self._group_label_object.get_calculated_dimensions(context, rect)[1] elif mode == MODE_HORIZONTAL: if rotate_label_horizontal: self._group_label_object.set_rotation(90) else: self._group_label_object.set_rotation(0) self._group_label_object.set_wrap(False) return self._group_label_object.get_calculated_dimensions(context, rect)[0] class MultiBarChart(bar_chart.BarChart): """ The MultiBarChart widget displays groups of bars. Usage: create multi_bar_chart.BarGroups and add multi_bar_chart.Bars. The add the bar groups to MultiBarChart. Properties ========== The MultiBarChart class inherits properties from bar_chart.BarChart (except bar-padding). Additional properties: - group-padding (the space between two bar groups in px, type: int in [0, 100], default: 16) - label-rotation (the angle (in degrees) that should be used to rotate bar labels in vertical mode, type: int in [0, 360], default: 300) - rotate-group-labels (sets whether group labels should be roteated by 90 degrees in horizontal mode, type: boolean, default: False). Signals ======= The MultiBarChart class inherits the signal 'bar-clicked' from bar_chart.BarChart. Additional signals: - group-clicked: emitted when a bar is clicked, callback signature: def group_clicked(chart, group, bar). """ __gsignals__ = {"group-clicked": (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, (gobject.TYPE_PYOBJECT, gobject.TYPE_PYOBJECT))} __gproperties__ = {"group-padding": (gobject.TYPE_INT, "group padding", "The space between two bar groups.", 0, 100, 16, gobject.PARAM_READWRITE), "label-rotation": (gobject.TYPE_INT, "label rotation", "The angle that should bar labels be rotated by in vertical mode.", 0, 360, 300, gobject.PARAM_READWRITE), "rotate-group-labels": (gobject.TYPE_BOOLEAN, "rotate group label", "Sets whether the group label should be rotated by 90 degrees in horizontal mode.", False, gobject.PARAM_READWRITE), "mode": (gobject.TYPE_INT, "mode", "The chart's mode.", 0, 1, 0, gobject.PARAM_READWRITE), "draw-labels": (gobject.TYPE_BOOLEAN, "draw labels", "Set whether to draw labels on bars.", True, gobject.PARAM_READWRITE), "enable-mouseover": (gobject.TYPE_BOOLEAN, "enable mouseover", "Set whether to enable mouseover effect.", True, gobject.PARAM_READWRITE)} def __init__(self): bar_chart.BarChart.__init__(self) #private properties: self._groups = [] #gobject properties: self._group_padding = 16 self._label_rotation = 300 self._rotate_group_label_in_horizontal_mode = False #gobject set_* and get_* methods def do_get_property(self, property): if property.name == "group-padding": return self._group_padding elif property.name == "label-rotation": return self._label_rotation elif property.name == "rotate-group-labels": return self._rotate_group_label_in_horizontal_mode elif property.name == "mode": return self._mode elif property.name == "draw-labels": return self._draw_labels elif property.name == "enable-mouseover": return self._mouseover else: raise AttributeError, "Property %s does not exist." % property.name def do_set_property(self, property, value): if property.name == "group-padding": self._group_padding = value elif property.name == "label-rotation": self._label_rotation = value elif property.name == "rotate-group-labels": self._rotate_group_label_in_horizontal_mode = value elif property.name == "mode": self._mode = value elif property.name == "draw-labels": self._draw_labels = value elif property.name == "enable-mouseover": self._mouseover = value else: raise AttributeError, "Property %s does not exist." % property.name def set_group_padding(self, padding): """ Set the amount of free space between bar groups (in px, default: 16). @param padding: the padding @type padding: int in [0, 100]. """ self.set_property("group-padding", padding) self.queue_draw() def get_group_padding(self): """ Returns the amount of free space between two bar groups (in px). @return: int in [0, 100]. """ return self.get_property("group-padding") def set_label_rotation(self, angle): """ Set the abgle (in degrees) that should be used to rotate the bar labels in vertical mode (defualt: 300 degrees). @type angle: int in [0, 360]. """ self.set_property("label-rotation", angle) self.queue_draw() def get_label_rotation(self): """ Returns the angle by which bar labels are rotated in vertical mode. @return: int in [0, 350]. """ return self.get_property("label-rotation") def set_rotate_group_labels(self, rotate): """ Set wether the groups' labels should be rotated by 90 degrees in horizontal mode (default: False). @type rotate: boolean. """ self.set_property("rotate-group-labels", rotate) self.queue_draw() def get_rotate_group_labels(self): """ Returns True if group labels should be rotated by 90 degrees in horizontal mode. @return: boolean. """ return self.get_property("rotate-group-labels") #callbacks def _cb_motion_notify(self, widget, event): if not self._mouseover: return active = chart.get_sensitive_areas(event.x, event.y) if active == []: return for group in self._groups: for bar in group.get_bars(): bar.set_highlighted((group, bar) in active) self.queue_draw() def _cb_button_pressed(self, widget, event): active = chart.get_sensitive_areas(event.x, event.y) for group, bar in active: self.emit("group-clicked", group, bar) self.emit("bar-clicked", bar) #drawing methods def _do_draw_groups(self, context, rect, maximum_value, value_label_size, label_size, bar_count): if self._groups == []: return if self._mode == MODE_VERTICAL: group_end = rect.x else: group_end = rect.y for i, group in enumerate(self._groups): group_end = group.draw(context, rect, bar_count, len(self._groups), i, self._mode, self._group_padding, maximum_value, group_end, value_label_size, label_size, self._label_rotation, self._draw_labels, self._rotate_group_label_in_horizontal_mode) def draw(self, context): """ Draw the widget. This method is called automatically. Don't call it yourself. If you want to force a redrawing of the widget, call the queue_draw() method. @type context: cairo.Context @param context: The context to draw on. """ label.begin_drawing() chart.init_sensitive_areas() rect = self.get_allocation() rect = gtk.gdk.Rectangle(0, 0, rect.width, rect.height) #transform rect to context coordinates context.set_line_width(1) rect = self.draw_basics(context, rect) maximum_value = max(group.get_maximum_value() for group in self._groups) bar_count = 0 for group in self._groups: bar_count += group.get_bar_count() value_label_size = 0 if self._draw_labels: for group in self._groups: value_label_size = max(value_label_size, group.get_value_label_size(context, rect, self._mode, bar_count, len(self._groups), self._group_padding, self._bar_padding)) label_size = 0 if self._draw_labels: for group in self._groups: label_size = max(label_size, group.get_label_size(context, rect, self._mode, bar_count, len(self._groups), self._group_padding, self._bar_padding, self._label_rotation)) label_size += 10 label_size += group.get_group_label_size(context, rect, self._mode, self._rotate_group_label_in_horizontal_mode) rect = self._do_draw_grid(context, rect, maximum_value, value_label_size, label_size) self._do_draw_groups(context, rect, maximum_value, value_label_size, label_size, bar_count) label.finish_drawing() n = len(self._groups) if self._mode == MODE_VERTICAL: minimum_width = rect.x + self._padding + bar_count * 10 + n * self._group_padding minimum_height = rect.y + self._padding + 200 elif self._mode == MODE_HORIZONTAL: minimum_width = rect.x + self._padding + 200 minimum_height = rect.y + self._padding + bar_count * 10 + n * self._group_padding self.set_size_request(minimum_width, minimum_height) #other methods def add_group(self, group): """ Add a BarGroup to the chart. @type group: multi_bar_chart.BarGroup. """ self._groups.append(group) self.queue_draw() def add_bar(self, bar): """ Alias for add_group. This method is deprecated. Use add_group instead. """ print "MultiBarChart.add_bar is deprecated. Use add_group instead." self.add_group(bar) gpxviewer-0.4.3/gpxviewer/pygtk_chart/pie_chart.py0000664000175000017500000004202411752304676021746 0ustar andrewandrew#!/usr/bin/env python # # pie_chart.py # # Copyright 2008 Sven Festersen # # 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 2 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. """ Contains the PieChart widget. Author: Sven Festersen (sven@sven-festersen.de) """ __docformat__ = "epytext" import cairo import gobject import gtk import math import os from pygtk_chart.basics import * from pygtk_chart.chart_object import ChartObject from pygtk_chart import chart from pygtk_chart import label from pygtk_chart import COLORS, COLOR_AUTO def draw_sector(context, cx, cy, radius, angle, angle_offset): context.move_to(cx, cy) context.arc(cx, cy, radius, angle_offset, angle_offset + angle) context.close_path() context.fill() class PieArea(chart.Area): """ This class represents the sector of a pie chart. Properties ========== The PieArea class inherits properties from chart.Area. Signals ======= The PieArea class inherits signals from chart.Area. """ def __init__(self, name, value, title=""): chart.Area.__init__(self, name, value, title) self._label_object = label.Label((0, 0), title) def _do_draw(self, context, rect, cx, cy, radius, angle, angle_offset, draw_label, draw_percentage, draw_value): context.set_source_rgb(*color_gdk_to_cairo(self._color)) draw_sector(context, cx, cy, radius, angle, angle_offset) if self._highlighted: context.set_source_rgba(1, 1, 1, 0.1) draw_sector(context, cx, cy, radius, angle, angle_offset) if draw_label: title = self._label title_extra = "" fraction = angle / (2 * math.pi) if draw_percentage and not draw_value: title_extra = " (%s%%)" % round(100 * fraction, 2) elif not draw_percentage and draw_value: title_extra = " (%s)" % self._value elif draw_percentage and draw_value: title_extra = " (%s, %s%%)" % (self._value, round(100 * fraction, 2)) title += title_extra label_angle = angle_offset + angle / 2 label_angle = label_angle % (2 * math.pi) x = cx + (radius + 10) * math.cos(label_angle) y = cy + (radius + 10) * math.sin(label_angle) ref = label.ANCHOR_BOTTOM_LEFT if 0 <= label_angle <= math.pi / 2: ref = label.ANCHOR_TOP_LEFT elif math.pi / 2 <= label_angle <= math.pi: ref = label.ANCHOR_TOP_RIGHT elif math.pi <= label_angle <= 1.5 * math.pi: ref = label.ANCHOR_BOTTOM_RIGHT if self._highlighted: self._label_object.set_underline(label.UNDERLINE_SINGLE) else: self._label_object.set_underline(label.UNDERLINE_NONE) self._label_object.set_color(self._color) self._label_object.set_text(title) self._label_object.set_position((x, y)) self._label_object.set_anchor(ref) self._label_object.draw(context, rect) class PieChart(chart.Chart): """ This is the pie chart class. Properties ========== The PieChart class inherits properties from chart.Chart. Additional properties: - rotate (the angle that the pie chart should be rotated by in degrees, type: int in [0, 360]) - draw-shadow (sets whther to draw a shadow under the pie chart, type: boolean) - draw-labels (sets whether to draw area labels, type: boolean) - show-percentage (sets whether to show percentage in area labels, type: boolean) - show-values (sets whether to show values in area labels, type: boolean) - enable-scroll (sets whether the pie chart can be rotated by scrolling with the mouse wheel, type: boolean) - enable-mouseover (sets whether a mouse over effect should be added to areas, type: boolean). Signals ======= The PieChart class inherits signals from chart.Chart. Additional signals: - area-clicked (emitted when an area is clicked) callback signature: def callback(piechart, area). """ __gproperties__ = {"rotate": (gobject.TYPE_INT, "rotation", "The angle to rotate the chart in degrees.", 0, 360, 0, gobject.PARAM_READWRITE), "draw-shadow": (gobject.TYPE_BOOLEAN, "draw pie shadow", "Set whether to draw pie shadow.", True, gobject.PARAM_READWRITE), "draw-labels": (gobject.TYPE_BOOLEAN, "draw area labels", "Set whether to draw area labels.", True, gobject.PARAM_READWRITE), "show-percentage": (gobject.TYPE_BOOLEAN, "show percentage", "Set whether to show percentage in the areas' labels.", False, gobject.PARAM_READWRITE), "show-values": (gobject.TYPE_BOOLEAN, "show values", "Set whether to show values in the areas' labels.", True, gobject.PARAM_READWRITE), "enable-scroll": (gobject.TYPE_BOOLEAN, "enable scroll", "If True, the pie can be rotated by scrolling with the mouse wheel.", True, gobject.PARAM_READWRITE), "enable-mouseover": (gobject.TYPE_BOOLEAN, "enable mouseover", "Set whether a mouseover effect should be visible if moving the mouse over a pie area.", True, gobject.PARAM_READWRITE)} __gsignals__ = {"area-clicked": (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, (gobject.TYPE_PYOBJECT,))} def __init__(self): chart.Chart.__init__(self) self._areas = [] self._rotate = 0 self._shadow = True self._labels = True self._percentage = False self._values = True self._enable_scroll = True self._enable_mouseover = True self.add_events(gtk.gdk.BUTTON_PRESS_MASK|gtk.gdk.SCROLL_MASK|gtk.gdk.POINTER_MOTION_MASK) self.connect("button_press_event", self._cb_button_pressed) self.connect("scroll-event", self._cb_scroll_event) self.connect("motion-notify-event", self._cb_motion_notify) def do_get_property(self, property): if property.name == "rotate": return self._rotate elif property.name == "draw-shadow": return self._shadow elif property.name == "draw-labels": return self._labels elif property.name == "show-percentage": return self._percentage elif property.name == "show-values": return self._values elif property.name == "enable-scroll": return self._enable_scroll elif property.name == "enable-mouseover": return self._enable_mouseover else: raise AttributeError, "Property %s does not exist." % property.name def do_set_property(self, property, value): if property.name == "rotate": self._rotate = value elif property.name == "draw-shadow": self._shadow = value elif property.name == "draw-labels": self._labels = value elif property.name == "show-percentage": self._percentage = value elif property.name == "show-values": self._values = value elif property.name == "enable-scroll": self._enable_scroll = value elif property.name == "enable-mouseover": self._enable_mouseover = value else: raise AttributeError, "Property %s does not exist." % property.name def _cb_appearance_changed(self, widget): self.queue_draw() def _cb_motion_notify(self, widget, event): if not self._enable_mouseover: return area = self._get_area_at_pos(event.x, event.y) for a in self._areas: a.set_property("highlighted", a == area) self.queue_draw() def _cb_button_pressed(self, widget, event): area = self._get_area_at_pos(event.x, event.y) if area: self.emit("area-clicked", area) def _get_area_at_pos(self, x, y): rect = self.get_allocation() center = rect.width / 2, rect.height / 2 x = x - center[0] y = y - center[1] #calculate angle angle = math.atan2(x, -y) angle -= math.pi / 2 angle -= 2 * math.pi * self.get_rotate() / 360.0 while angle < 0: angle += 2 * math.pi #calculate radius radius_squared = math.pow(int(0.4 * min(rect.width, rect.height)), 2) clicked_radius_squared = x*x + y*y if clicked_radius_squared <= radius_squared: #find out area that was clicked sum = 0 for area in self._areas: if area.get_visible(): sum += area.get_value() current_angle_position = 0 for area in self._areas: area_angle = 2 * math.pi * area.get_value() / sum if current_angle_position <= angle <= current_angle_position + area_angle: return area current_angle_position += area_angle return None def _cb_scroll_event(self, widget, event): if not self._enable_scroll: return if event.direction in [gtk.gdk.SCROLL_UP, gtk.gdk.SCROLL_RIGHT]: delta = 360.0 / 32 elif event.direction in [gtk.gdk.SCROLL_DOWN, gtk.gdk.SCROLL_LEFT]: delta = - 360.0 / 32 else: delta = 0 rotate = self.get_rotate() + delta rotate = rotate % 360.0 if rotate < 0: rotate += 360 self.set_rotate(rotate) def draw(self, context): """ Draw the widget. This method is called automatically. Don't call it yourself. If you want to force a redrawing of the widget, call the queue_draw() method. @type context: cairo.Context @param context: The context to draw on. """ label.begin_drawing() rect = self.get_allocation() #initial context settings: line width & font context.set_line_width(1) font = gtk.Label().style.font_desc.get_family() context.select_font_face(font, cairo.FONT_SLANT_NORMAL, \ cairo.FONT_WEIGHT_NORMAL) self.draw_basics(context, rect) self._do_draw_shadow(context, rect) self._do_draw_areas(context, rect) label.finish_drawing() def _do_draw_areas(self, context, rect): center = rect.width / 2, rect.height / 2 radius = int(0.4 * min(rect.width, rect.height)) sum = 0 for area in self._areas: if area.get_visible(): sum += area.get_value() current_angle_position = 2 * math.pi * self.get_rotate() / 360.0 for i, area in enumerate(self._areas): area_angle = 2 * math.pi * area.get_value() / sum area.draw(context, rect, center[0], center[1], radius, area_angle, current_angle_position, self._labels, self._percentage, self._values) current_angle_position += area_angle def _do_draw_shadow(self, context, rect): if not self._shadow: return center = rect.width / 2, rect.height / 2 radius = int(0.4 * min(rect.width, rect.height)) gradient = cairo.RadialGradient(center[0], center[1], radius, center[0], center[1], radius + 10) gradient.add_color_stop_rgba(0, 0, 0, 0, 0.5) gradient.add_color_stop_rgba(0.5, 0, 0, 0, 0) context.set_source(gradient) context.arc(center[0], center[1], radius + 10, 0, 2 * math.pi) context.fill() def add_area(self, area): color = area.get_color() if color == COLOR_AUTO: area.set_color(COLORS[len(self._areas) % len(COLORS)]) self._areas.append(area) area.connect("appearance_changed", self._cb_appearance_changed) def get_pie_area(self, name): """ Returns the PieArea with the id 'name' if it exists, None otherwise. @type name: string @param name: the id of a PieArea @return: a PieArea or None. """ for area in self._areas: if area.get_name() == name: return area return None def set_rotate(self, angle): """ Set the rotation angle of the PieChart in degrees. @param angle: angle in degrees 0 - 360 @type angle: integer. """ self.set_property("rotate", angle) self.queue_draw() def get_rotate(self): """ Get the current rotation angle in degrees. @return: integer. """ return self.get_property("rotate") def set_draw_shadow(self, draw): """ Set whether to draw the pie chart's shadow. @type draw: boolean. """ self.set_property("draw-shadow", draw) self.queue_draw() def get_draw_shadow(self): """ Returns True if pie chart currently has a shadow. @return: boolean. """ return self.get_property("draw-shadow") def set_draw_labels(self, draw): """ Set whether to draw the labels of the pie areas. @type draw: boolean. """ self.set_property("draw-labels", draw) self.queue_draw() def get_draw_labels(self): """ Returns True if area labels are shown. @return: boolean. """ return self.get_property("draw-labels") def set_show_percentage(self, show): """ Set whether to show the percentage an area has in its label. @type show: boolean. """ self.set_property("show-percentage", show) self.queue_draw() def get_show_percentage(self): """ Returns True if percentages are shown. @return: boolean. """ return self.get_property("show-percentage") def set_enable_scroll(self, scroll): """ Set whether the pie chart can be rotated by scrolling with the mouse wheel. @type scroll: boolean. """ self.set_property("enable-scroll", scroll) def get_enable_scroll(self): """ Returns True if the user can rotate the pie chart by scrolling. @return: boolean. """ return self.get_property("enable-scroll") def set_enable_mouseover(self, mouseover): """ Set whether a mouseover effect should be shown when the pointer enters a pie area. @type mouseover: boolean. """ self.set_property("enable-mouseover", mouseover) def get_enable_mouseover(self): """ Returns True if the mouseover effect is enabled. @return: boolean. """ return self.get_property("enable-mouseover") def set_show_values(self, show): """ Set whether the area's value should be shown in its label. @type show: boolean. """ self.set_property("show-values", show) self.queue_draw() def get_show_values(self): """ Returns True if the value of a pie area is shown in its label. @return: boolean. """ return self.get_property("show-values") gpxviewer-0.4.3/gpxviewer/utils/__init__.py0000664000175000017500000000000011752304676020354 0ustar andrewandrewgpxviewer-0.4.3/bin/gpxviewer0000775000175000017500000000246611752304676015623 0ustar andrewandrew#!/usr/bin/env python # # gpxviewer - Launcher File # # Copyright (C) 2009 Andrew Gee # # GPX Viewer 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. # # GPX Viewer is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. # See the GNU General Public License for more details. # # You should have received a copy of the GNU General Public License along # with this program. If not, see . # # If you're having any problems, don't hesitate to contact: andrew@andrewgee.org # import os.path, sys import gpxviewer sys.path.append(gpxviewer.__path__[0]) import gtk.gdk from gpxviewer.ui import MainWindow POSSIBLE_SHARE_DIRS = ["/usr/local/share/gpxviewer/","/usr/share/gpxviewer/"] gtk.gdk.threads_init() prefix = "" for share_dir in POSSIBLE_SHARE_DIRS: if os.path.exists(share_dir): prefix = share_dir if len(sys.argv) > 2: files = sys.argv[1:] elif len(sys.argv) > 1: files = [sys.argv[1]] else: files = [] gui = MainWindow(ui_dir="%sui/" % prefix,files=files) gtk.main() gpxviewer-0.4.3/gpxviewer/__init__.py0000664000175000017500000000000011752304676017214 0ustar andrewandrewgpxviewer-0.4.3/gpxviewer/gpx.py0000664000175000017500000001270211752304676016267 0ustar andrewandrew# # gpx.py - Used to hold gpx files for access by other scripts # # Copyright (C) 2009 Andrew Gee # # GPX Viewer 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. # # GPX Viewer is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. # See the GNU General Public License for more details. # # You should have received a copy of the GNU General Public License along # with this program. If not, see . # # If you're having any problems, don't hesitate to contact: andrew@andrewgee.org # from datetime import * from gpximport import * from math import sqrt, radians, sin, cos, atan2, fabs, pi, acos from os.path import basename, abspath def calculate_distance(lat1, lat2, lon1, lon2): R = 6371000 #Earth's radius =~ 6371km lat1 = radians(lat1) lat2 = radians(lat2) lon1 = radians(lon1) lon2 = radians(lon2) # Great Circle Distance Formula # arc = acos((sin(lat1) * sin(lat2)) + (cos(lat1) * cos(lat2) * cos(lon2 - lon1))) # 9.8.2011 Hadmut Danisch hadmut@danisch.de: # This formula can fail and abort with a domain exception since the inner part # of the expression can become >1.0 in rare cases due to the limited # precision of the floating point arithmetics a=(sin(lat1) * sin(lat2)) + (cos(lat1) * cos(lat2) * cos(lon2 - lon1)) if a >= 1.0: arc=0.0 elif a <= -1.0: arc=math.pi else: arc=acos(a) d = R * arc return d class GPXTrace: def __init__(self,filename): self.trace = import_gpx_trace(filename) self._cache = {} def _walk_points(self): """ Computes all measurements that require walking over all points """ maxlat = minlat = self.trace['tracks'][0]['segments'][0]['points'][0]['lat'] maxlon = minlon = self.trace['tracks'][0]['segments'][0]['points'][0]['lon'] distance = 0 seconds = 0 mspeed = 0 for track in self.trace['tracks']: for segment in track['segments']: pointp = None for point in segment['points']: #{max,min}{lat,lon} if maxlat < point['lat']: maxlat = point['lat'] elif minlat > point['lat']: minlat = point['lat'] if maxlon < point['lon']: maxlon = point['lon'] elif minlon > point['lon']: minlon = point['lon'] #distance if pointp != None: #maximum speed d = calculate_distance(point['lat'], pointp['lat'], point['lon'], pointp['lon']) t = (point['time'] - pointp['time']).microseconds + ((point['time'] - pointp['time']).seconds * 1000000) if t > 0: s = (d/t)*1000000 if s > mspeed: mspeed = s distance += d pointp = point #segment duration (pointp contains the last point in the segment) seconds += (pointp['time'] - segment['points'][0]['time']).seconds self._cache["max_lat"] = maxlat self._cache["min_lat"] = minlat self._cache["max_lon"] = maxlon self._cache["min_lon"] = minlon self._cache["distance"] = distance self._cache["duration"] = seconds self._cache["max_speed"] = mspeed def _get_cached_value(self, name): if name not in self._cache: self._walk_points() return self._cache[name] def get_points(self): tracks = [] for track in self.trace['tracks']: segments = [] for segment in track['segments']: points = [] for point in segment['points']: points.append((radians(point['lat']),radians(point['lon']))) segments.append(points) tracks.append(segments) return tracks def get_display_name(self): try: return self.trace["metadata"]["name"] except KeyError: return self.get_filename() def get_filename(self): return basename(self.trace['filename']) def get_full_path(self): return abspath(self.trace['filename']) def get_max_lat(self): return self._get_cached_value("max_lat") def get_min_lat(self): return self._get_cached_value("min_lat") def get_max_lon(self): return self._get_cached_value("max_lon") def get_min_lon(self): return self._get_cached_value("min_lon") def get_centre(self): maxlat = self.get_max_lat() minlat = self.get_min_lat() maxlon = self.get_max_lon() minlon = self.get_min_lon() return (maxlat+minlat)/2,(maxlon+minlon)/2 def get_average_speed(self): #return self._get_cached_value("distance")/self._get_cached_value("duration") # 9.8.2011 Hadmut Danisch hadmut@danisch.de: # duration can become 0 in special cases and thus cause division by zero dis = self._get_cached_value("distance") dur = self._get_cached_value("duration") if dur == 0: return 0 return dis/dur def get_distance(self): return self._get_cached_value("distance") def get_duration(self): return self._get_cached_value("duration") def get_gpxfrom(self): return self.trace['tracks'][0]['segments'][0]['points'][0]['time'] def get_gpxto(self): return self.trace['tracks'][-1]['segments'][-1]['points'][-1]['time'] def get_maximum_speed(self): return self._get_cached_value("max_speed") gpxviewer-0.4.3/gpxviewer/gpximport.py0000664000175000017500000001065711752304676017531 0ustar andrewandrew# # gpximport.py - Used to import GPX XML files into applications # # Copyright (C) 2009 Andrew Gee # # GPX Viewer 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. # # GPX Viewer is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. # See the GNU General Public License for more details. # # You should have received a copy of the GNU General Public License along # with this program. If not, see . # # If you're having any problems, don't hesitate to contact: andrew@andrewgee.org # import xml.dom.minidom as minidom from utils.iso8601 import parse_date as parse_xml_date __all__ = ["import_gpx_trace"] class ParseError(Exception): """Raised when there is a problem parsing any part of the GPX XML""" pass def fetch_metadata(node): metadata = {} for mnode in node.childNodes: if mnode.nodeName == "name": metadata['name'] = mnode.childNodes[0].nodeValue elif mnode.nodeName == "desc": try: metadata['description'] = mnode.childNodes[0].nodeValue except: metadata['description'] = "" #no description elif mnode.nodeName == "time": metadata['time'] = mnode.childNodes[0].nodeValue elif mnode.nodeName == "author": metadata['author'] = {} for anode in mnode.childNodes: if anode.nodeName == "name": metadata['author']['name'] = anode.childNodes[0].nodeValue elif anode.nodeName == "email": metadata['author']['email'] = anode.childNodes[0].nodeValue elif anode.nodeName == "link": metadata['author']['link'] = anode.childNodes[0].nodeValue elif mnode.nodeName == "copyright": metadata['copyright'] = {} if mnode.attributes["author"].value != "": metadata['copyright']['author'] = mnode.attributes["author"].value for cnode in mnode.childNodes: if cnode.nodeName == "year": metadata['copyright']['year'] = cnode.childNodes[0].nodeValue elif cnode.nodeName == "license": metadata['copyright']['license'] = cnode.childNodes[0].nodeValue elif mnode.nodeName == "link": metadata['link'] = {} if mnode.attributes["href"].value != "": metadata['link']['href'] = mnode.attributes["href"].value for lnode in mnode.childNodes: if lnode.nodeName == "text": metadata['link']['text'] = lnode.childNodes[0].nodeValue elif lnode.nodeName == "type": metadata['link']['type'] = lnode.childNodes[0].nodeValue elif mnode.nodeName == "time": metadata['time'] = parse_xml_date(mnode.childNodes[0].nodeValue) elif mnode.nodeName == "keywords": metadata['keywords'] = mnode.childNodes[0].nodeValue return metadata def fetch_track_point(tsnode): point = {} if tsnode.attributes["lat"] != "" and tsnode.attributes["lon"] != "": point['lat'] = float(tsnode.attributes["lat"].value) point['lon'] = float(tsnode.attributes["lon"].value) for tpnode in tsnode.childNodes: if tpnode.nodeName == "ele": point['ele'] = float(tpnode.childNodes[0].nodeValue) elif tpnode.nodeName == "desc": point['description'] = tpnode.childNodes[0].nodeValue elif tpnode.nodeName == "time": point['time'] = parse_xml_date(tpnode.childNodes[0].nodeValue) elif tpnode.nodeName == "name": point['name'] = tpnode.childNodes[0].nodeValue return point def fetch_track_segment(tnode): trkseg = {} trkseg['points'] = [] for tsnode in tnode.childNodes: if tsnode.nodeName == "trkpt": trkseg['points'].append(fetch_track_point(tsnode)) return trkseg def fetch_track(node): track = {} track['segments'] = [] for tnode in node.childNodes: if tnode.nodeName == "trkseg": track_segment = fetch_track_segment(tnode) if len(track_segment['points']) > 0: track['segments'].append(fetch_track_segment(tnode)) return track def import_gpx_trace(filename): doc = minidom.parse(filename) doce = doc.documentElement if doce.nodeName != "gpx": raise Exception trace = {} trace['filename'] = filename trace['tracks'] = [] try: e = doce.childNodes for node in e: if node.nodeName == "metadata": trace['metadata'] = fetch_metadata(node) elif node.nodeName == "trk": trace['tracks'].append(fetch_track(node)) except: raise Exception return trace gpxviewer-0.4.3/gpxviewer/stats.py0000664000175000017500000000403111752304676016623 0ustar andrewandrewimport gtk import pygtk_chart.bar_chart import pygtk_chart.line_chart as line_chart class _Chart: title = "" def chart(self): raise NotImplementedError def chart_window(self): window = gtk.Window() window.add(self.chart()) window.resize(800,400) window.show_all() def chart_notebook_page(self): return self.chart(),gtk.Label(self.title) class ChartNotebook(gtk.Notebook): def __init__(self, *charts): gtk.Notebook.__init__(self) for c in charts: self.append_page(*c.chart_notebook_page()) class StatBarChart(_Chart): show_data_labels = True def getBarChartData(self): raise NotImplementedError def chart(self): barchart = pygtk_chart.bar_chart.BarChart() barchart.title.set_text(self.title) barchart.grid.set_visible(True) barchart.grid.set_line_style(pygtk_chart.LINE_STYLE_DOTTED) barchart.set_draw_labels(self.show_data_labels) for bar_info in self.getBarChartData(): bar = pygtk_chart.bar_chart.Bar(*bar_info) barchart.add_bar(bar) return barchart class LineChart(_Chart): def getLineChartData(self): raise NotImplementedError def chart(self): chart = line_chart.LineChart() chart.title.set_text(self.title) data = self.getLineChartData() graph = line_chart.Graph("avg", "avg", data) graph.set_type(line_chart.GRAPH_LINES) chart.add_graph(graph) return chart class WeekStats(StatBarChart): title = 'Total Distance Cycled Per Week' def __init__(self): self._weeks = [0]*53 def addTrace(self, trace): week = trace.get_gpxfrom().isocalendar()[1] distance = trace.get_distance() self._weeks[week] += (distance/1000.0) def getBarChartData(self): wk = 1 data = [] for dist in self._weeks: if dist: data.append( (str(wk),float(int(dist)),"W%d"%wk) ) wk += 1 return data class AvgSpeedStats(LineChart): title = "Average Speed" def __init__(self): self._i = 0 self._avgspeeds = [] def addTrace(self, trace): self._avgspeeds.append( (self._i, trace.get_average_speed()) ) self._i += 1 def getLineChartData(self): return self._avgspeeds gpxviewer-0.4.3/gpxviewer/ui.py0000664000175000017500000003605111752304676016111 0ustar andrewandrew# # ui.py - GUI for GPX Viewer # # Copyright (C) 2009 Andrew Gee # # GPX Viewer 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. # # GPX Viewer is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. # See the GNU General Public License for more details. # # You should have received a copy of the GNU General Public License along # with this program. If not, see . # # If you're having any problems, don't hesitate to contact: andrew@andrewgee.org # import os import sys import glib import gtk import gobject import osmgpsmap assert osmgpsmap.__version__ >= "0.7.1" import stats from gpx import GPXTrace from utils.timezone import LocalTimezone import locale import gettext locale.setlocale(locale.LC_ALL, '') # see http://bugzilla.gnome.org/show_bug.cgi?id=344926 for why the # next two commands look repeated. #gtk.glade.bindtextdomain('gpxviewer') #gtk.glade.textdomain('gpxviewer') gettext.bindtextdomain('gpxviewer') gettext.textdomain('gpxviewer') _ = gettext.lgettext # Function used to defer translation until later, while still being recognised # by build_i18n def N_(message): return message def show_url(url): gtk.show_uri(None, url, gtk.gdk.CURRENT_TIME) ALPHA_UNSELECTED = 0.5 ALPHA_SELECTED = 0.8 LAZY_LOAD_AFTER_N_FILES = 3 class _TrackManager(gobject.GObject): NAME_IDX = 0 FILENAME_IDX = 1 __gsignals__ = { 'track-added': (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, [object, object]), 'track-removed': (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, [object, object]), } def __init__(self): gobject.GObject.__init__(self) # maps track_filename : (GPXTrace, [OsmGpsMapTrack]) self._tracks = {} # name, filename self.model = gtk.ListStore(str, str) def getOtherTracks(self, trace): tracks = [] for _trace,_tracks in self._tracks.values(): if trace != _trace: tracks += _tracks return tracks def getTraceFromModel(self, _iter): filename = self.model.get_value(_iter, self.FILENAME_IDX) return self.getTrace(filename) def deleteTraceFromModel(self, _iter): self.emit("track-removed", *self._tracks[self.model.get_value(_iter, self.FILENAME_IDX)]) self.model.remove(_iter) def getTrace(self, filename): """ Returns (trace, [OsmGpsMapTrack]) """ return self._tracks[filename] def addTrace(self, trace): filename = trace.get_full_path() if filename not in self._tracks: gpstracks = [] for track in trace.get_points(): for segment in track: gpstrack = osmgpsmap.GpsMapTrack() gpstrack.props.alpha = ALPHA_UNSELECTED for rlat,rlon in segment: gpstrack.add_point(osmgpsmap.point_new_radians(rlat, rlon)) gpstracks.append(gpstrack) self._tracks[filename] = (trace, gpstracks) self.model.append( (trace.get_display_name(), filename) ) self.emit("track-added", trace, gpstracks) def numTraces(self): return len(self._tracks) def getAllTraces(self): return [t[0] for t in self._tracks.values()] class MainWindow: def __init__(self, ui_dir, files): self.localtz = LocalTimezone() self.recent = gtk.recent_manager_get_default() self.wTree = gtk.Builder() self.wTree.set_translation_domain('gpxviewer') self.wTree.add_from_file("%sgpxviewer.ui" % ui_dir) signals = { "on_windowMain_destroy": self.quit, "on_menuitemQuit_activate": self.quit, "on_menuitemOpen_activate": self.openGPX, "on_menuitemZoomIn_activate": self.zoomMapIn, "on_buttonZoomIn_clicked": self.zoomMapIn, "on_menuitemZoomOut_activate": self.zoomMapOut, "on_buttonZoomOut_clicked": self.zoomMapOut, "on_menuitemAbout_activate": self.openAboutDialog, "on_checkmenuitemShowSidebar_toggled": self.showSidebarToggled, "on_menuitemShowStatistics_activate": self.showStatistics, "on_buttonTrackAdd_clicked": self.buttonTrackAddClicked, "on_buttonTrackDelete_clicked": self.buttonTrackDeleteClicked, "on_buttonTrackInspect_clicked": self.buttonTrackInspectClicked, } self.mainWindow = self.wTree.get_object("windowMain") self.mainWindow.set_icon_from_file("%sgpxviewer.svg" % ui_dir) self.mainWindow.set_title(_("GPX Viewer")) i = self.wTree.get_object("checkmenuitemCenter") i.connect("toggled", self.autoCenterToggled) self.autoCenter = i.get_active() self.ui_dir = ui_dir self.map = osmgpsmap.GpsMap( tile_cache=os.path.join( glib.get_user_cache_dir(), 'gpxviewer', 'tiles')) self.map.layer_add( osmgpsmap.GpsMapOsd( show_dpad=False, show_zoom=False, show_scale=True, show_coordinates=False)) self.wTree.get_object("hbox_map").pack_start(self.map, True, True) sb = self.wTree.get_object("statusbar1") #move zoom control into apple like slider self.zoomSlider = MapZoomSlider(self.map) self.zoomSlider.show_all() a = gtk.Alignment(0.5,0.5,1.0,1.0) a.set_padding(0,0,0,4) a.add(self.zoomSlider) a.show_all() sb.pack_end(a, False, False, padding=4) #animate a spinner when downloading tiles try: self.spinner = gtk.Spinner() self.spinner.props.has_tooltip = True self.spinner.connect("query-tooltip", self.onSpinnerTooltip) self.map.connect("notify::tiles-queued", self.updateTilesQueued) self.spinner.set_size_request(*gtk.icon_size_lookup(gtk.ICON_SIZE_MENU)) sb.pack_end(self.spinner, False, False) except AttributeError: self.spinner = None self.wTree.connect_signals(signals) #add open with external tool submenu items and actions programs = { 'josm':N_('JOSM Editor'), 'merkaartor':N_('Merkaartor'), } submenu_open_with = gtk.Menu() for prog,progname in programs.iteritems(): submenuitem_open_with = gtk.MenuItem(_(progname)) submenu_open_with.append(submenuitem_open_with) submenuitem_open_with.connect("activate", self.openWithExternalApp, prog) submenuitem_open_with.show() self.wTree.get_object('menuitemOpenBy').set_submenu(submenu_open_with) self.trackManager = _TrackManager() self.trackManager.connect("track-added", self.onTrackAdded) self.trackManager.connect("track-removed", self.onTrackRemoved) self.wTree.get_object("menuitemHelp").connect("activate", lambda *a: show_url("https://answers.launchpad.net/gpxviewer")) self.wTree.get_object("menuitemTranslate").connect("activate", lambda *a: show_url("https://translations.launchpad.net/gpxviewer")) self.wTree.get_object("menuitemReportProblem").connect("activate", lambda *a: show_url("https://bugs.launchpad.net/gpxviewer/+filebug")) self.tv = gtk.TreeView(self.trackManager.model) self.tv.get_selection().connect("changed", self.onSelectionChanged) self.tv.append_column( gtk.TreeViewColumn( "Track Name", gtk.CellRendererText(), text=self.trackManager.NAME_IDX ) ) self.wTree.get_object("scrolledwindow1").add(self.tv) self.sb = self.wTree.get_object("vbox_sidebar") self.hideSpinner() self.hideTrackSelector() self.lazyLoadFiles(files) self.map.show() self.mainWindow.show() def lazyLoadFiles(self, files): def do_lazy_load(_files): try: self.loadGPX( _files.pop() ) self.loadingFiles -= 1 return True except IndexError: self.loadingFiles = 0 return False self.loadingFiles = 0 if not files: return #if less than LAZY_LOAD_AFTER_N_FILES load directly, else #load on idle if len(files) < LAZY_LOAD_AFTER_N_FILES: i = 0 for filename in files: self.loadingFiles = i trace = self.loadGPX(filename) if i < LAZY_LOAD_AFTER_N_FILES: i += 1 else: #select the last loaded trace self.loadingFiles = 0 self.selectTrace(trace) break else: self.loadingFiles = len(files) gobject.timeout_add(100, do_lazy_load, files) def showSpinner(self): if self.spinner: self.spinner.show() self.spinner.start() def hideSpinner(self): if self.spinner: self.spinner.stop() self.spinner.hide() def onSpinnerTooltip(self, spinner, x, y, keyboard_mode, tooltip): tiles = self.map.props.tiles_queued if tiles: tooltip.set_text("Downloading Map") return True return False def showTrackSelector(self): self.sb.show_all() def hideTrackSelector(self): self.sb.hide_all() def onSelectionChanged(self, selection): model, _iter = selection.get_selected() if not _iter: return trace, tracks = self.trackManager.getTraceFromModel(_iter) self.selectTrace(trace) #highlight current track self.selectTracks(tracks, ALPHA_SELECTED) #dim other tracks self.selectTracks(self.trackManager.getOtherTracks(trace), ALPHA_UNSELECTED) def onTrackAdded(self, tm, trace, tracks): for t in tracks: self.map.track_add(t) self.selectTrace(trace) def onTrackRemoved(self, tm, trace, tracks): for t in tracks: self.map.track_remove(t) def updateTilesQueued(self, map_, paramspec): if self.map.props.tiles_queued > 0: self.showSpinner() else: self.hideSpinner() def showSidebarToggled(self, item): if item.get_active(): self.showTrackSelector() else: self.hideTrackSelector() def showStatistics(self, item): ws = stats.WeekStats() ss = stats.AvgSpeedStats() for t in self.trackManager.getAllTraces(): ws.addTrace(t) ss.addTrace(t) w = gtk.Window() w.add(stats.ChartNotebook(ws,ss)) w.resize(500,300) w.set_position(gtk.WIN_POS_CENTER_ON_PARENT) w.set_transient_for(self.mainWindow) w.show_all() def openAboutDialog(self,w): dialog = self.wTree.get_object("dialogAbout") self.wTree.get_object("dialogAbout").set_icon_from_file("%sgpxviewer.svg" % self.ui_dir) dialog.connect("response", lambda *a: dialog.hide()) dialog.show_all() def selectTracks(self, tracks, alpha): for t in tracks: t.props.alpha = alpha def selectTrace(self, trace): if self.loadingFiles: return self.zoom = 12 distance = trace.get_distance() maximum_speed = trace.get_maximum_speed() average_speed = trace.get_average_speed() duration = trace.get_duration() clat, clon = trace.get_centre() gpxfrom = trace.get_gpxfrom().astimezone(self.localtz) gpxto = trace.get_gpxto().astimezone(self.localtz) self.setDistanceLabel(round(distance/1000,2)) self.setMaximumSpeedLabel(maximum_speed) self.setAverageSpeedLabel(average_speed) self.setDurationLabel(int(duration/60),duration-(int(duration/60)*60)) self.setLoggingDateLabel(gpxfrom.strftime("%x")) self.setLoggingTimeLabel(gpxfrom.strftime("%X"),gpxto.strftime("%X")) self.currentFilename = trace.get_filename() self.mainWindow.set_title(_("GPX Viewer - %s") % trace.get_filename()) if self.autoCenter: self.setCentre(clat,clon) def loadGPX(self, filename): try: trace = GPXTrace(filename) self.trackManager.addTrace(trace) if self.trackManager.numTraces() > 1: self.showTrackSelector() return trace except Exception, e: self.showGPXError() return None def openGPX(self, *args): filechooser = gtk.FileChooserDialog(title=_("Choose a GPX file to Load"),action=gtk.FILE_CHOOSER_ACTION_OPEN,parent=self.mainWindow) filechooser.add_button(gtk.STOCK_CANCEL, gtk.RESPONSE_DELETE_EVENT) filechooser.add_button(gtk.STOCK_OPEN, gtk.RESPONSE_OK) filechooser.set_position(gtk.WIN_POS_CENTER_ON_PARENT) filechooser.set_select_multiple(True) response = filechooser.run() if response == gtk.RESPONSE_OK: for filename in filechooser.get_filenames(): if self.loadGPX(filename): self.recent.add_item("file://"+filename) filechooser.destroy() def showGPXError(self): message_box = gtk.MessageDialog(parent=self.mainWindow,type=gtk.MESSAGE_ERROR,buttons=gtk.BUTTONS_OK,message_format=_("You selected an invalid GPX file. \n Please try again")) message_box.run() message_box.destroy() return None def quit(self,w): gtk.main_quit() def main(self): gtk.main() def openWithExternalApp(self,w,app): if self.currentFilename: os.spawnlp(os.P_NOWAIT,app,app,self.currentFilename) def zoomMapIn(self,w): self.map.zoom_in() def zoomMapOut(self,w): self.map.zoom_out() def setCentre(self,lat,lon): self.map.set_center_and_zoom(lat,lon,self.zoom) def setDistanceLabel(self,distance="--"): self.wTree.get_object("labelDistance").set_markup(_("Distance: %.2f km") % distance) def setAverageSpeedLabel(self,average_speed="--"): self.wTree.get_object("labelAverageSpeed").set_markup(_("Average Speed: %.2f m/s") % average_speed) def setMaximumSpeedLabel(self,maximum_speed="--"): self.wTree.get_object("labelMaximumSpeed").set_markup(_("Maximum Speed: %.2f m/s") % maximum_speed) def setDurationLabel(self,minutes="--",seconds="--"): self.wTree.get_object("labelDuration").set_markup(_("Duration: %(minutes)s minutes, %(seconds)s seconds") % {"minutes": minutes, "seconds": seconds}) def setLoggingDateLabel(self,gpxdate="--"): self.wTree.get_object("labelLoggingDate").set_markup(_("Logging Date: %s") % gpxdate) def setLoggingTimeLabel(self,gpxfrom="--",gpxto="--"): self.wTree.get_object("labelLoggingTime").set_markup(_("Logging Time: %(from)s - %(to)s") % {"from": gpxfrom, "to": gpxto}) def autoCenterToggled(self, item): self.autoCenter = item.get_active() def buttonTrackAddClicked(self, *args): self.openGPX() def buttonTrackDeleteClicked(self, *args): model, _iter = self.tv.get_selection().get_selected() if _iter: self.trackManager.deleteTraceFromModel(_iter) def buttonTrackInspectClicked(self, *args): pass class MapZoomSlider(gtk.HBox): def __init__(self, _map): gtk.HBox.__init__(self) zo = gtk.EventBox() zo.add(gtk.image_new_from_stock (gtk.STOCK_ZOOM_OUT, gtk.ICON_SIZE_MENU)) zo.connect("button-press-event", self._on_zoom_out_pressed, _map) self.pack_start(zo, False, False) self.zoom = gtk.Adjustment( value=_map.props.zoom, lower=_map.props.min_zoom, upper=_map.props.max_zoom, step_incr=1, page_incr=1, page_size=0) self.zoom.connect("value-changed", self._on_zoom_slider_value_changed, _map) hs = gtk.HScale(self.zoom) hs.props.digits = 0 hs.props.draw_value = False hs.set_size_request(100,-1) hs.set_update_policy(gtk.UPDATE_DISCONTINUOUS) self.pack_start(hs, True, True) zi = gtk.EventBox() zi.add(gtk.image_new_from_stock (gtk.STOCK_ZOOM_IN, gtk.ICON_SIZE_MENU)) zi.connect("button-press-event", self._on_zoom_in_pressed, _map) self.pack_start(zi, False, False) _map.connect("notify::zoom", self._on_map_zoom_changed) def _on_zoom_in_pressed(self, box, event, _map): _map.zoom_in() def _on_zoom_out_pressed(self, box, event, _map): _map.zoom_out() def _on_zoom_slider_value_changed(self, adj, _map): zoom = adj.get_value() if zoom != _map.props.zoom: _map.set_zoom( int(zoom) ) def _on_map_zoom_changed(self, _map, paramspec): self.zoom.set_value(_map.props.zoom) gpxviewer-0.4.3/po/POTFILES.in0000664000175000017500000000006611752304676015272 0ustar andrewandrewgpxviewer/ui.py [type: gettext/glade] ui/gpxviewer.ui gpxviewer-0.4.3/po/cs.po0000664000175000017500000001114111752304676014456 0ustar andrewandrew# Czech translation for gpxviewer # Copyright (c) 2009 Rosetta Contributors and Canonical Ltd 2009 # This file is distributed under the same license as the gpxviewer package. # FIRST AUTHOR , 2009. # msgid "" msgstr "" "Project-Id-Version: gpxviewer\n" "Report-Msgid-Bugs-To: FULL NAME \n" "POT-Creation-Date: 2010-12-31 00:11+0000\n" "PO-Revision-Date: 2011-02-13 13:32+0000\n" "Last-Translator: Radek Stastny \n" "Language-Team: Czech \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "X-Launchpad-Export-Date: 2011-05-17 18:28+0000\n" "X-Generator: Launchpad (build 13053)\n" #: ../gpxviewer/ui.py:146 ../ui/gpxviewer.ui.h:10 msgid "GPX Viewer" msgstr "ProhlížeÄ GPX" #: ../gpxviewer/ui.py:189 msgid "JOSM Editor" msgstr "JOSM Editor" #: ../gpxviewer/ui.py:190 msgid "Merkaartor" msgstr "Merkaartor" #: ../gpxviewer/ui.py:363 #, python-format msgid "GPX Viewer - %s" msgstr "ProhlížeÄ souborů GPX- %s" #: ../gpxviewer/ui.py:380 msgid "Choose a GPX file to Load" msgstr "Zvolte k naÄtení GPX soubor" #: ../gpxviewer/ui.py:394 msgid "" "You selected an invalid GPX file. \n" " Please try again" msgstr "" "Vybrali jste neplatný GPX soubor. \n" " Zkuste to prosím znovu" #: ../gpxviewer/ui.py:419 #, python-format msgid "Distance: %.2f km" msgstr "Vzdálenost: %.2f km" #: ../gpxviewer/ui.py:422 #, python-format msgid "Average Speed: %.2f m/s" msgstr "PrůmÄ›rná rychlost: %.2f m/s" #: ../gpxviewer/ui.py:425 #, python-format msgid "Maximum Speed: %.2f m/s" msgstr "Maximální rychlost: %.2f m/s" #: ../gpxviewer/ui.py:428 #, python-format msgid "Duration: %(minutes)s minutes, %(seconds)s seconds" msgstr "Trvání: %(minutes)s minut, %(seconds)s sekund" #: ../gpxviewer/ui.py:431 #, python-format msgid "Logging Date: %s" msgstr "Datum záznamu: %s" #: ../gpxviewer/ui.py:434 #, python-format msgid "Logging Time: %(from)s - %(to)s" msgstr "ÄŒas záznamu: %(from)s - %(to)s" #: ../ui/gpxviewer.ui.h:1 msgid "Average Speed: -- m/s" msgstr "PrůmÄ›rná rychlost: -- m/s" #: ../ui/gpxviewer.ui.h:2 msgid "Distance: -- km" msgstr "Vzdálenost: -- km" #: ../ui/gpxviewer.ui.h:3 msgid "Duration: -- minutes, -- seconds" msgstr "Trvání: -- minut, -- sekund" #: ../ui/gpxviewer.ui.h:4 msgid "Logging Date: --" msgstr "Datum záznamu: --" #: ../ui/gpxviewer.ui.h:5 msgid "Logging Time: from -- to -- " msgstr "ÄŒas záznamu: od -- do -- " #: ../ui/gpxviewer.ui.h:6 msgid "Maximum Speed: -- m/s" msgstr "Maximální rychlost: -- m/s" #: ../ui/gpxviewer.ui.h:7 msgid "About GPX Viewer" msgstr "O ProhlížeÄi GPX souborů" #: ../ui/gpxviewer.ui.h:8 msgid "Center on Selected Track" msgstr "StÅ™ed vybrané trasy" #: ../ui/gpxviewer.ui.h:9 msgid "Copyright (C) Andrew Gee 2009" msgstr "Copyright © Andrew Gee 2009" #: ../ui/gpxviewer.ui.h:11 msgid "GPX Viewer Website" msgstr "Webové stránky prohlížeÄe GPX souborů" #: ../ui/gpxviewer.ui.h:12 msgid "" "GPX Viewer is an application that allows\n" "users to easily view GPS traces they \n" "have created in the GPX file format." msgstr "" "ProhlížeÄ GPX souborů je aplikace\n" "umožňující uživatelům snadno prohlížet\n" "GPS trasy vytvoÅ™ené ve formátu GPX." #: ../ui/gpxviewer.ui.h:15 msgid "Get Help Online..." msgstr "Získat online nápovÄ›du..." #: ../ui/gpxviewer.ui.h:16 msgid "Multiple Tracks" msgstr "Více tras" #: ../ui/gpxviewer.ui.h:17 msgid "Open with External Program..." msgstr "Otevřít pomocí programu ..." #: ../ui/gpxviewer.ui.h:18 msgid "Report a Problem..." msgstr "Nahlásit problém..." #: ../ui/gpxviewer.ui.h:19 msgid "Show Aggregate Statistics" msgstr "Zobrazit statistiku" #: ../ui/gpxviewer.ui.h:20 msgid "Show Sidebar" msgstr "Zobrazit postranní liÅ¡tu" #: ../ui/gpxviewer.ui.h:21 msgid "Translate This Application..." msgstr "PÅ™eložit tuto aplikaci..." #: ../ui/gpxviewer.ui.h:22 msgid "_File" msgstr "_Soubor" #: ../ui/gpxviewer.ui.h:23 msgid "_Help" msgstr "_NápovÄ›da" #: ../ui/gpxviewer.ui.h:24 msgid "_Tools" msgstr "Nás_troje" #: ../ui/gpxviewer.ui.h:25 msgid "_View" msgstr "_Zobrazit" #, python-format #~ msgid "Maximum Speed: %s m/s" #~ msgstr "Maximální rychlost: %s m/s" #, python-format #~ msgid "Average Speed: %s m/s" #~ msgstr "PrůmÄ›rná rychlost: %s m/s" #, python-format #~ msgid "Distance: %s km" #~ msgstr "Vzdálenost: %s km" #~ msgid "Zoom In" #~ msgstr "PÅ™iblížit" #~ msgid "Zoom Out" #~ msgstr "Oddálit" gpxviewer-0.4.3/po/de.po0000664000175000017500000001132011752304676014440 0ustar andrewandrew# German translation for gpxviewer # Copyright (c) 2009 Rosetta Contributors and Canonical Ltd 2009 # This file is distributed under the same license as the gpxviewer package. # FIRST AUTHOR , 2009. # msgid "" msgstr "" "Project-Id-Version: gpxviewer\n" "Report-Msgid-Bugs-To: FULL NAME \n" "POT-Creation-Date: 2010-12-31 00:11+0000\n" "PO-Revision-Date: 2011-03-23 18:26+0000\n" "Last-Translator: Clemens Korner \n" "Language-Team: German \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "X-Launchpad-Export-Date: 2011-05-17 18:28+0000\n" "X-Generator: Launchpad (build 13053)\n" #: ../gpxviewer/ui.py:146 ../ui/gpxviewer.ui.h:10 msgid "GPX Viewer" msgstr "GPX Viewer" #: ../gpxviewer/ui.py:189 msgid "JOSM Editor" msgstr "JOSM Editor" #: ../gpxviewer/ui.py:190 msgid "Merkaartor" msgstr "Merkaartor" #: ../gpxviewer/ui.py:363 #, python-format msgid "GPX Viewer - %s" msgstr "GPX Viewer - %s" #: ../gpxviewer/ui.py:380 msgid "Choose a GPX file to Load" msgstr "Wählen Sie eine zu ladende GPX-Datei aus" #: ../gpxviewer/ui.py:394 msgid "" "You selected an invalid GPX file. \n" " Please try again" msgstr "" "Sie haben eine ungültige GPX-Datei ausgewählt. \n" " Bitte versuchen Sie es noch einmal" #: ../gpxviewer/ui.py:419 #, python-format msgid "Distance: %.2f km" msgstr "Entfernung: %.2f km" #: ../gpxviewer/ui.py:422 #, python-format msgid "Average Speed: %.2f m/s" msgstr "Durchschnittliche Geschwindigkeit: %.2f m/s" #: ../gpxviewer/ui.py:425 #, python-format msgid "Maximum Speed: %.2f m/s" msgstr "Maximale Geschwindigkeit: %.2f m/s" #: ../gpxviewer/ui.py:428 #, python-format msgid "Duration: %(minutes)s minutes, %(seconds)s seconds" msgstr "Dauer: %(minutes)s Minuten, %(seconds)s Sekunden" #: ../gpxviewer/ui.py:431 #, python-format msgid "Logging Date: %s" msgstr "Aufnahme Datum: %s" #: ../gpxviewer/ui.py:434 #, python-format msgid "Logging Time: %(from)s - %(to)s" msgstr "Aufnahme Zeit %(from)s - %(to)s" #: ../ui/gpxviewer.ui.h:1 msgid "Average Speed: -- m/s" msgstr "Durchschnittsgeschwindigkeit: -- m/s" #: ../ui/gpxviewer.ui.h:2 msgid "Distance: -- km" msgstr "Entfernung: -- km" #: ../ui/gpxviewer.ui.h:3 msgid "Duration: -- minutes, -- seconds" msgstr "Dauer: -- Minuten, -- Sekunden" #: ../ui/gpxviewer.ui.h:4 msgid "Logging Date: --" msgstr "Aufnahme Datum: --" #: ../ui/gpxviewer.ui.h:5 msgid "Logging Time: from -- to -- " msgstr "Aufnahme Zeit: von -- bis -- " #: ../ui/gpxviewer.ui.h:6 msgid "Maximum Speed: -- m/s" msgstr "Höchstgeschwindigkeit: -- m/s" #: ../ui/gpxviewer.ui.h:7 msgid "About GPX Viewer" msgstr "Über GPX Viewer" #: ../ui/gpxviewer.ui.h:8 msgid "Center on Selected Track" msgstr "Auf ausgewählten Track zentrieren" #: ../ui/gpxviewer.ui.h:9 msgid "Copyright (C) Andrew Gee 2009" msgstr "Copyright (C) Andrew Gee 2009" #: ../ui/gpxviewer.ui.h:11 msgid "GPX Viewer Website" msgstr "GPX Viewer Webseite" #: ../ui/gpxviewer.ui.h:12 msgid "" "GPX Viewer is an application that allows\n" "users to easily view GPS traces they \n" "have created in the GPX file format." msgstr "" "GPX Viewer ist eine Anwendung die es erlaubt\n" "im GPX-Format abgelegte GPS-Protokolle\n" "anzusehen." #: ../ui/gpxviewer.ui.h:15 msgid "Get Help Online..." msgstr "Online Hilfe erhalten ..." #: ../ui/gpxviewer.ui.h:16 msgid "Multiple Tracks" msgstr "Mehrere Tracks" #: ../ui/gpxviewer.ui.h:17 msgid "Open with External Program..." msgstr "Mit externem Programm öffnen..." #: ../ui/gpxviewer.ui.h:18 msgid "Report a Problem..." msgstr "Ein Problem melden..." #: ../ui/gpxviewer.ui.h:19 msgid "Show Aggregate Statistics" msgstr "Aggregierte Statistiken anzeigen" #: ../ui/gpxviewer.ui.h:20 msgid "Show Sidebar" msgstr "Seitenleiste anzeigen" #: ../ui/gpxviewer.ui.h:21 msgid "Translate This Application..." msgstr "Diese Anwendung übersetzen …" #: ../ui/gpxviewer.ui.h:22 msgid "_File" msgstr "_Datei" #: ../ui/gpxviewer.ui.h:23 msgid "_Help" msgstr "_Hilfe" #: ../ui/gpxviewer.ui.h:24 msgid "_Tools" msgstr "_Werkzeuge" #: ../ui/gpxviewer.ui.h:25 msgid "_View" msgstr "_Ansicht" #, python-format #~ msgid "Maximum Speed: %s m/s" #~ msgstr "Höchstgeschwindigkeit: %s m/s" #, python-format #~ msgid "Average Speed: %s m/s" #~ msgstr "Durchschnittsgeschwindigkeit: %s m/s" #, python-format #~ msgid "Distance: %s km" #~ msgstr "Entfernung: %s km" #~ msgid "Zoom In" #~ msgstr "Vergrößern" #~ msgid "Zoom Out" #~ msgstr "Verkleinern" #~ msgid "Zoom _In" #~ msgstr "Ver_größern" #~ msgid "Zoom _Out" #~ msgstr "_Verkleinern" gpxviewer-0.4.3/po/el.po0000664000175000017500000001207311752304676014456 0ustar andrewandrew# Greek translation for gpxviewer # Copyright (c) 2009 Rosetta Contributors and Canonical Ltd 2009 # This file is distributed under the same license as the gpxviewer package. # FIRST AUTHOR , 2009. # msgid "" msgstr "" "Project-Id-Version: gpxviewer\n" "Report-Msgid-Bugs-To: FULL NAME \n" "POT-Creation-Date: 2010-12-31 00:11+0000\n" "PO-Revision-Date: 2010-11-25 14:06+0000\n" "Last-Translator: Ioannis Stilianakos(stilia-johny, d3vil) " "\n" "Language-Team: Greek \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "X-Launchpad-Export-Date: 2011-05-17 18:28+0000\n" "X-Generator: Launchpad (build 13053)\n" #: ../gpxviewer/ui.py:146 ../ui/gpxviewer.ui.h:10 msgid "GPX Viewer" msgstr "Απεικονιστής GPX" #: ../gpxviewer/ui.py:189 msgid "JOSM Editor" msgstr "Συντάκτης JOSM" #: ../gpxviewer/ui.py:190 msgid "Merkaartor" msgstr "Merkaartor" #: ../gpxviewer/ui.py:363 #, python-format msgid "GPX Viewer - %s" msgstr "Απεικονιστής GPX - %s" #: ../gpxviewer/ui.py:380 msgid "Choose a GPX file to Load" msgstr "Επιλέξτε αÏχείο GPX Ï€Ïος ΦόÏτωση" #: ../gpxviewer/ui.py:394 msgid "" "You selected an invalid GPX file. \n" " Please try again" msgstr "" "Επιλέξατε άκυÏο αÏχείο GPX. \n" " ΠαÏακαλώ δοκιμάστε ξανά" #: ../gpxviewer/ui.py:419 #, python-format msgid "Distance: %.2f km" msgstr "" #: ../gpxviewer/ui.py:422 #, python-format msgid "Average Speed: %.2f m/s" msgstr "" #: ../gpxviewer/ui.py:425 #, python-format msgid "Maximum Speed: %.2f m/s" msgstr "" #: ../gpxviewer/ui.py:428 #, python-format msgid "Duration: %(minutes)s minutes, %(seconds)s seconds" msgstr "ΔιάÏκεια: %(minutes)s λεπτά, %(seconds)s δευτεÏόλεπτα" #: ../gpxviewer/ui.py:431 #, python-format msgid "Logging Date: %s" msgstr "ΗμεÏομηνία Εισόδου: %s" #: ../gpxviewer/ui.py:434 #, python-format msgid "Logging Time: %(from)s - %(to)s" msgstr "ÎÏα Εισόδου: %(from)s - %(to)s" #: ../ui/gpxviewer.ui.h:1 msgid "Average Speed: -- m/s" msgstr "Μέση ΤαχÏτητα -- μ/δευτ." #: ../ui/gpxviewer.ui.h:2 msgid "Distance: -- km" msgstr "Απόσταση -- Χλμ" #: ../ui/gpxviewer.ui.h:3 msgid "Duration: -- minutes, -- seconds" msgstr "ΔιάÏκεια: -- λεπτά, -- δευτεÏόλεπτα" #: ../ui/gpxviewer.ui.h:4 msgid "Logging Date: --" msgstr "ΗμεÏομηνία Εισόδου: --" #: ../ui/gpxviewer.ui.h:5 msgid "Logging Time: from -- to -- " msgstr "ÎÏα Εισόδου: from -- to -- " #: ../ui/gpxviewer.ui.h:6 msgid "Maximum Speed: -- m/s" msgstr "Μέγιστη ΤαχÏτητα: -- μ/δευτ." #: ../ui/gpxviewer.ui.h:7 msgid "About GPX Viewer" msgstr "Σχετιά με τον Απεικονιστή GPX" #: ../ui/gpxviewer.ui.h:8 msgid "Center on Selected Track" msgstr "" #: ../ui/gpxviewer.ui.h:9 msgid "Copyright (C) Andrew Gee 2009" msgstr "ΚατωχÏÏωση Πνευματικής Ιδιοκτησίας (C) Andrew Gee 2009" #: ../ui/gpxviewer.ui.h:11 msgid "GPX Viewer Website" msgstr "Ιστοσελίδα Απεικονιστή GPX" #: ../ui/gpxviewer.ui.h:12 msgid "" "GPX Viewer is an application that allows\n" "users to easily view GPS traces they \n" "have created in the GPX file format." msgstr "" "Ο Απεικονιστής GPX είναι μια εφαÏμογή που επιτÏέπει\n" "στους χÏήστες να" #: ../ui/gpxviewer.ui.h:15 msgid "Get Help Online..." msgstr "Λήψη Online Βοήθειας..." #: ../ui/gpxviewer.ui.h:16 msgid "Multiple Tracks" msgstr "" #: ../ui/gpxviewer.ui.h:17 msgid "Open with External Program..." msgstr "Άνοιγμα με εξωτεÏικό Ï€ÏόγÏαμμα ..." #: ../ui/gpxviewer.ui.h:18 msgid "Report a Problem..." msgstr "ΑναφοÏά ΠÏοβλήματος..." #: ../ui/gpxviewer.ui.h:19 msgid "Show Aggregate Statistics" msgstr "" #: ../ui/gpxviewer.ui.h:20 msgid "Show Sidebar" msgstr "" #: ../ui/gpxviewer.ui.h:21 msgid "Translate This Application..." msgstr "ΜετάφÏαση αυτής της εφαÏμογής..." #: ../ui/gpxviewer.ui.h:22 msgid "_File" msgstr "_ΑÏχείο" #: ../ui/gpxviewer.ui.h:23 msgid "_Help" msgstr "_Βοήθεια" #: ../ui/gpxviewer.ui.h:24 msgid "_Tools" msgstr "ΕÏ_γαλεία" #: ../ui/gpxviewer.ui.h:25 msgid "_View" msgstr "Π_Ïοβολή" #, python-format #~ msgid "Maximum Speed: %s m/s" #~ msgstr "Μέγιστη ΤαχÏτητα %s μ/δευτ." #, python-format #~ msgid "Average Speed: %s m/s" #~ msgstr "Μέση ΤαχÏτητα: %s μ/δευτ." #, python-format #~ msgid "Distance: %s km" #~ msgstr "Απόσταση: %s Χλμ" #~ msgid "Zoom Out" #~ msgstr "ΣμίκÏυνση" #~ msgid "Zoom In" #~ msgstr "Μεγέθυνση" #~ msgid "Zoom _Out" #~ msgstr "ΣμίκÏ_υνση" #~ msgid "Zoom _In" #~ msgstr "Ε_στίαση" gpxviewer-0.4.3/po/en_GB.po0000664000175000017500000001111511752304676015024 0ustar andrewandrew# English (United Kingdom) translation for gpxviewer # Copyright (c) 2009 Rosetta Contributors and Canonical Ltd 2009 # This file is distributed under the same license as the gpxviewer package. # FIRST AUTHOR , 2009. # msgid "" msgstr "" "Project-Id-Version: gpxviewer\n" "Report-Msgid-Bugs-To: FULL NAME \n" "POT-Creation-Date: 2010-12-31 00:11+0000\n" "PO-Revision-Date: 2010-12-31 21:24+0000\n" "Last-Translator: Jon Trew \n" "Language-Team: English (United Kingdom) \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "X-Launchpad-Export-Date: 2011-05-17 18:28+0000\n" "X-Generator: Launchpad (build 13053)\n" #: ../gpxviewer/ui.py:146 ../ui/gpxviewer.ui.h:10 msgid "GPX Viewer" msgstr "GPX Viewer" #: ../gpxviewer/ui.py:189 msgid "JOSM Editor" msgstr "JOSM Editor" #: ../gpxviewer/ui.py:190 msgid "Merkaartor" msgstr "Merkaartor" #: ../gpxviewer/ui.py:363 #, python-format msgid "GPX Viewer - %s" msgstr "GPX Viewer - %s" #: ../gpxviewer/ui.py:380 msgid "Choose a GPX file to Load" msgstr "Choose a GPX file to Load" #: ../gpxviewer/ui.py:394 msgid "" "You selected an invalid GPX file. \n" " Please try again" msgstr "" "You selected an invalid GPX file. \n" " Please try again" #: ../gpxviewer/ui.py:419 #, python-format msgid "Distance: %.2f km" msgstr "Distance: %.2f km" #: ../gpxviewer/ui.py:422 #, python-format msgid "Average Speed: %.2f m/s" msgstr "Average Speed: %.2f m/s" #: ../gpxviewer/ui.py:425 #, python-format msgid "Maximum Speed: %.2f m/s" msgstr "Maximum Speed: %.2f m/s" #: ../gpxviewer/ui.py:428 #, python-format msgid "Duration: %(minutes)s minutes, %(seconds)s seconds" msgstr "Duration: %(minutes)s minutes, %(seconds)s seconds" #: ../gpxviewer/ui.py:431 #, python-format msgid "Logging Date: %s" msgstr "Logging Date: %s" #: ../gpxviewer/ui.py:434 #, python-format msgid "Logging Time: %(from)s - %(to)s" msgstr "Logging Time: %(from)s - %(to)s" #: ../ui/gpxviewer.ui.h:1 msgid "Average Speed: -- m/s" msgstr "Average Speed: -- m/s" #: ../ui/gpxviewer.ui.h:2 msgid "Distance: -- km" msgstr "Distance: -- km" #: ../ui/gpxviewer.ui.h:3 msgid "Duration: -- minutes, -- seconds" msgstr "Duration: -- minutes, -- seconds" #: ../ui/gpxviewer.ui.h:4 msgid "Logging Date: --" msgstr "Logging Date: --" #: ../ui/gpxviewer.ui.h:5 msgid "Logging Time: from -- to -- " msgstr "Logging Time: from -- to -- " #: ../ui/gpxviewer.ui.h:6 msgid "Maximum Speed: -- m/s" msgstr "Maximum Speed: -- m/s" #: ../ui/gpxviewer.ui.h:7 msgid "About GPX Viewer" msgstr "About GPX Viewer" #: ../ui/gpxviewer.ui.h:8 msgid "Center on Selected Track" msgstr "Centre on Selected Track" #: ../ui/gpxviewer.ui.h:9 msgid "Copyright (C) Andrew Gee 2009" msgstr "Copyright (C) Andrew Gee 2009" #: ../ui/gpxviewer.ui.h:11 msgid "GPX Viewer Website" msgstr "GPX Viewer Website" #: ../ui/gpxviewer.ui.h:12 msgid "" "GPX Viewer is an application that allows\n" "users to easily view GPS traces they \n" "have created in the GPX file format." msgstr "" "GPX Viewer is an application that allows\n" "users to easily view GPS traces they \n" "have created in the GPX file format." #: ../ui/gpxviewer.ui.h:15 msgid "Get Help Online..." msgstr "Get Help Online..." #: ../ui/gpxviewer.ui.h:16 msgid "Multiple Tracks" msgstr "Multiple Tracks" #: ../ui/gpxviewer.ui.h:17 msgid "Open with External Program..." msgstr "Open with External Program..." #: ../ui/gpxviewer.ui.h:18 msgid "Report a Problem..." msgstr "Report a Problem..." #: ../ui/gpxviewer.ui.h:19 msgid "Show Aggregate Statistics" msgstr "Show Aggregate Statistics" #: ../ui/gpxviewer.ui.h:20 msgid "Show Sidebar" msgstr "Show Sidebar" #: ../ui/gpxviewer.ui.h:21 msgid "Translate This Application..." msgstr "Translate This Application..." #: ../ui/gpxviewer.ui.h:22 msgid "_File" msgstr "_File" #: ../ui/gpxviewer.ui.h:23 msgid "_Help" msgstr "_Help" #: ../ui/gpxviewer.ui.h:24 msgid "_Tools" msgstr "_Tools" #: ../ui/gpxviewer.ui.h:25 msgid "_View" msgstr "_View" #, python-format #~ msgid "Maximum Speed: %s m/s" #~ msgstr "Maximum Speed: %s m/s" #, python-format #~ msgid "Average Speed: %s m/s" #~ msgstr "Average Speed: %s m/s" #, python-format #~ msgid "Distance: %s km" #~ msgstr "Distance: %s km" #~ msgid "Zoom Out" #~ msgstr "Zoom Out" #~ msgid "Zoom In" #~ msgstr "Zoom In" #~ msgid "Zoom _Out" #~ msgstr "Zoom _Out" #~ msgid "Zoom _In" #~ msgstr "Zoom _In" gpxviewer-0.4.3/po/es.po0000664000175000017500000001130511752304676014462 0ustar andrewandrew# Spanish translation for gpxviewer # Copyright (c) 2009 Rosetta Contributors and Canonical Ltd 2009 # This file is distributed under the same license as the gpxviewer package. # FIRST AUTHOR , 2009. # msgid "" msgstr "" "Project-Id-Version: gpxviewer\n" "Report-Msgid-Bugs-To: FULL NAME \n" "POT-Creation-Date: 2010-12-31 00:11+0000\n" "PO-Revision-Date: 2011-05-15 01:14+0000\n" "Last-Translator: Fitoschido \n" "Language-Team: Spanish \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "X-Launchpad-Export-Date: 2011-05-17 18:28+0000\n" "X-Generator: Launchpad (build 13053)\n" #: ../gpxviewer/ui.py:146 ../ui/gpxviewer.ui.h:10 msgid "GPX Viewer" msgstr "Visor GPX" #: ../gpxviewer/ui.py:189 msgid "JOSM Editor" msgstr "Editor JOSM" #: ../gpxviewer/ui.py:190 msgid "Merkaartor" msgstr "Merkaartor" #: ../gpxviewer/ui.py:363 #, python-format msgid "GPX Viewer - %s" msgstr "Visor GPX -%s" #: ../gpxviewer/ui.py:380 msgid "Choose a GPX file to Load" msgstr "Seleccione un archivo GPX para cargar" #: ../gpxviewer/ui.py:394 msgid "" "You selected an invalid GPX file. \n" " Please try again" msgstr "" "Seleccionó un archivo GPX no válido. \n" " Inténtelo de nuevo" #: ../gpxviewer/ui.py:419 #, python-format msgid "Distance: %.2f km" msgstr "Distancia: %.2f km" #: ../gpxviewer/ui.py:422 #, python-format msgid "Average Speed: %.2f m/s" msgstr "Velocidad media: %.2f m/s" #: ../gpxviewer/ui.py:425 #, python-format msgid "Maximum Speed: %.2f m/s" msgstr "Velocidad máxima: %.2f m/s" #: ../gpxviewer/ui.py:428 #, python-format msgid "Duration: %(minutes)s minutes, %(seconds)s seconds" msgstr "Duración: %(minutes)s minutos, %(seconds)s segundos" #: ../gpxviewer/ui.py:431 #, python-format msgid "Logging Date: %s" msgstr "Fecha de ingreso: %s" #: ../gpxviewer/ui.py:434 #, python-format msgid "Logging Time: %(from)s - %(to)s" msgstr "Tiempo de inicio de sesión: %(from)s - %(to)s" #: ../ui/gpxviewer.ui.h:1 msgid "Average Speed: -- m/s" msgstr "Velocidad media: -- m/s" #: ../ui/gpxviewer.ui.h:2 msgid "Distance: -- km" msgstr "Distancia: -- km" #: ../ui/gpxviewer.ui.h:3 msgid "Duration: -- minutes, -- seconds" msgstr "Duración: -- minutos, -- segundos" #: ../ui/gpxviewer.ui.h:4 msgid "Logging Date: --" msgstr " Fecha de ingreso:" #: ../ui/gpxviewer.ui.h:5 msgid "Logging Time: from -- to -- " msgstr " Tiempo de ingreso: desde .. hasta -- " #: ../ui/gpxviewer.ui.h:6 msgid "Maximum Speed: -- m/s" msgstr "Velocidad máxima: -- m/s" #: ../ui/gpxviewer.ui.h:7 msgid "About GPX Viewer" msgstr "Acerca del Visor GPX" #: ../ui/gpxviewer.ui.h:8 msgid "Center on Selected Track" msgstr "Centrar en el Track seleccionado" #: ../ui/gpxviewer.ui.h:9 msgid "Copyright (C) Andrew Gee 2009" msgstr "Copyright (C) Andrew Gee 2009" #: ../ui/gpxviewer.ui.h:11 msgid "GPX Viewer Website" msgstr "Sitio web Visor GPX" #: ../ui/gpxviewer.ui.h:12 msgid "" "GPX Viewer is an application that allows\n" "users to easily view GPS traces they \n" "have created in the GPX file format." msgstr "" "El visor GPX es una aplicación que permite\n" "al usuario visualizar rutas GPS que\n" "están en el formato de archivo GPX." #: ../ui/gpxviewer.ui.h:15 msgid "Get Help Online..." msgstr "Obtener ayuda en línea..." #: ../ui/gpxviewer.ui.h:16 msgid "Multiple Tracks" msgstr "Tracks Múltiples" #: ../ui/gpxviewer.ui.h:17 msgid "Open with External Program..." msgstr "Abrir con programa externo..." #: ../ui/gpxviewer.ui.h:18 msgid "Report a Problem..." msgstr "Informar de un problema..." #: ../ui/gpxviewer.ui.h:19 msgid "Show Aggregate Statistics" msgstr "Mostrar las estadísticas totales" #: ../ui/gpxviewer.ui.h:20 msgid "Show Sidebar" msgstr "Mostrar barra lateral" #: ../ui/gpxviewer.ui.h:21 msgid "Translate This Application..." msgstr "Traducir esta aplicación..." #: ../ui/gpxviewer.ui.h:22 msgid "_File" msgstr "_Archivo" #: ../ui/gpxviewer.ui.h:23 msgid "_Help" msgstr "Ay_uda" #: ../ui/gpxviewer.ui.h:24 msgid "_Tools" msgstr "_Herramientas" #: ../ui/gpxviewer.ui.h:25 msgid "_View" msgstr "_Ver" #, python-format #~ msgid "Maximum Speed: %s m/s" #~ msgstr "Velocidad máxima: %s m/s" #, python-format #~ msgid "Average Speed: %s m/s" #~ msgstr "Velocidad promedio: %s m/s" #, python-format #~ msgid "Distance: %s km" #~ msgstr "Distancia: %s km" #~ msgid "Zoom In" #~ msgstr "Acercar" #~ msgid "Zoom Out" #~ msgstr "Alejar" #~ msgid "Zoom _Out" #~ msgstr "_Disminuir ampliación" #~ msgid "Zoom _In" #~ msgstr "_Aumentar ampliación" gpxviewer-0.4.3/po/fr.po0000664000175000017500000001050511752304676014463 0ustar andrewandrew# French translation for gpxviewer # Copyright (c) 2010 Rosetta Contributors and Canonical Ltd 2010 # This file is distributed under the same license as the gpxviewer package. # FIRST AUTHOR , 2010. # msgid "" msgstr "" "Project-Id-Version: gpxviewer\n" "Report-Msgid-Bugs-To: FULL NAME \n" "POT-Creation-Date: 2010-12-31 00:11+0000\n" "PO-Revision-Date: 2011-05-17 17:53+0000\n" "Last-Translator: quesh \n" "Language-Team: French \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "X-Launchpad-Export-Date: 2011-05-17 18:28+0000\n" "X-Generator: Launchpad (build 13053)\n" #: ../gpxviewer/ui.py:146 ../ui/gpxviewer.ui.h:10 msgid "GPX Viewer" msgstr "GPX Viewer" #: ../gpxviewer/ui.py:189 msgid "JOSM Editor" msgstr "Éditeur JOSM" #: ../gpxviewer/ui.py:190 msgid "Merkaartor" msgstr "Merkaartor" #: ../gpxviewer/ui.py:363 #, python-format msgid "GPX Viewer - %s" msgstr "Visualiseur de GPX - %s" #: ../gpxviewer/ui.py:380 msgid "Choose a GPX file to Load" msgstr "Choisissez un fichier GPX à charger" #: ../gpxviewer/ui.py:394 msgid "" "You selected an invalid GPX file. \n" " Please try again" msgstr "Vous avez choisit un fichier GPX invalide." #: ../gpxviewer/ui.py:419 #, python-format msgid "Distance: %.2f km" msgstr "Distance : %.2f km" #: ../gpxviewer/ui.py:422 #, python-format msgid "Average Speed: %.2f m/s" msgstr "Vitesse moyenne : %.2f m/s" #: ../gpxviewer/ui.py:425 #, python-format msgid "Maximum Speed: %.2f m/s" msgstr "Vitesse maximale: %.2f m/s" #: ../gpxviewer/ui.py:428 #, python-format msgid "Duration: %(minutes)s minutes, %(seconds)s seconds" msgstr "Durée: %(minutes)s minutes, %(seconds)s secondes" #: ../gpxviewer/ui.py:431 #, python-format msgid "Logging Date: %s" msgstr "Date de Connexion : %s" #: ../gpxviewer/ui.py:434 #, python-format msgid "Logging Time: %(from)s - %(to)s" msgstr "Heure de Connexion: %(from)s - %(to)s" #: ../ui/gpxviewer.ui.h:1 msgid "Average Speed: -- m/s" msgstr "Vitesse moyenne : -- m/s" #: ../ui/gpxviewer.ui.h:2 msgid "Distance: -- km" msgstr "Distance: -- km" #: ../ui/gpxviewer.ui.h:3 msgid "Duration: -- minutes, -- seconds" msgstr "Durée: -- minutes, -- secondes" #: ../ui/gpxviewer.ui.h:4 msgid "Logging Date: --" msgstr "Date de Connexion: --" #: ../ui/gpxviewer.ui.h:5 msgid "Logging Time: from -- to -- " msgstr "Heure de Connexion: de -- à -- " #: ../ui/gpxviewer.ui.h:6 msgid "Maximum Speed: -- m/s" msgstr "Vitesse Maximale: -- m/s" #: ../ui/gpxviewer.ui.h:7 msgid "About GPX Viewer" msgstr "À propos GPX Viewer" #: ../ui/gpxviewer.ui.h:8 msgid "Center on Selected Track" msgstr "Centrer sur le chemin sélectionné" #: ../ui/gpxviewer.ui.h:9 msgid "Copyright (C) Andrew Gee 2009" msgstr "Copyright (C) Andrew Gee 2009" #: ../ui/gpxviewer.ui.h:11 msgid "GPX Viewer Website" msgstr "Site web de GPX Viewer" #: ../ui/gpxviewer.ui.h:12 msgid "" "GPX Viewer is an application that allows\n" "users to easily view GPS traces they \n" "have created in the GPX file format." msgstr "" "Le visualiseur GPX est une application qui permet\n" "aux utilisateurs de facilement voir les repères GPS \n" "qu'ils ont créées au format de fichier GPX." #: ../ui/gpxviewer.ui.h:15 msgid "Get Help Online..." msgstr "Obtenir de l'aide en ligne..." #: ../ui/gpxviewer.ui.h:16 msgid "Multiple Tracks" msgstr "Plusieurs chemins" #: ../ui/gpxviewer.ui.h:17 msgid "Open with External Program..." msgstr "Ouvrir avec un programme externe..." #: ../ui/gpxviewer.ui.h:18 msgid "Report a Problem..." msgstr "Signaler un bogue..." #: ../ui/gpxviewer.ui.h:19 msgid "Show Aggregate Statistics" msgstr "Montrer les statistiques globales" #: ../ui/gpxviewer.ui.h:20 msgid "Show Sidebar" msgstr "Afficher la barre latérale" #: ../ui/gpxviewer.ui.h:21 msgid "Translate This Application..." msgstr "Traduire cette application…" #: ../ui/gpxviewer.ui.h:22 msgid "_File" msgstr "_Fichier" #: ../ui/gpxviewer.ui.h:23 msgid "_Help" msgstr "_Aide" #: ../ui/gpxviewer.ui.h:24 msgid "_Tools" msgstr "_Outils" #: ../ui/gpxviewer.ui.h:25 msgid "_View" msgstr "_Affichage" #, python-format #~ msgid "Distance: %s km" #~ msgstr "Distance: %s km" gpxviewer-0.4.3/po/gpxviewer.pot0000664000175000017500000000616611752304676016270 0ustar andrewandrew# SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # FIRST AUTHOR , YEAR. # #, fuzzy msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2011-05-17 19:31+0100\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" "Language: \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=CHARSET\n" "Content-Transfer-Encoding: 8bit\n" #: ../gpxviewer/ui.py:146 ../ui/gpxviewer.ui.h:10 msgid "GPX Viewer" msgstr "" #: ../gpxviewer/ui.py:189 msgid "JOSM Editor" msgstr "" #: ../gpxviewer/ui.py:190 msgid "Merkaartor" msgstr "" #: ../gpxviewer/ui.py:363 #, python-format msgid "GPX Viewer - %s" msgstr "" #: ../gpxviewer/ui.py:380 msgid "Choose a GPX file to Load" msgstr "" #: ../gpxviewer/ui.py:394 msgid "" "You selected an invalid GPX file. \n" " Please try again" msgstr "" #: ../gpxviewer/ui.py:419 #, python-format msgid "Distance: %.2f km" msgstr "" #: ../gpxviewer/ui.py:422 #, python-format msgid "Average Speed: %.2f m/s" msgstr "" #: ../gpxviewer/ui.py:425 #, python-format msgid "Maximum Speed: %.2f m/s" msgstr "" #: ../gpxviewer/ui.py:428 #, python-format msgid "Duration: %(minutes)s minutes, %(seconds)s seconds" msgstr "" #: ../gpxviewer/ui.py:431 #, python-format msgid "Logging Date: %s" msgstr "" #: ../gpxviewer/ui.py:434 #, python-format msgid "Logging Time: %(from)s - %(to)s" msgstr "" #: ../ui/gpxviewer.ui.h:1 msgid "Average Speed: -- m/s" msgstr "" #: ../ui/gpxviewer.ui.h:2 msgid "Distance: -- km" msgstr "" #: ../ui/gpxviewer.ui.h:3 msgid "Duration: -- minutes, -- seconds" msgstr "" #: ../ui/gpxviewer.ui.h:4 msgid "Logging Date: --" msgstr "" #: ../ui/gpxviewer.ui.h:5 msgid "Logging Time: from -- to -- " msgstr "" #: ../ui/gpxviewer.ui.h:6 msgid "Maximum Speed: -- m/s" msgstr "" #: ../ui/gpxviewer.ui.h:7 msgid "About GPX Viewer" msgstr "" #: ../ui/gpxviewer.ui.h:8 msgid "Center on Selected Track" msgstr "" #: ../ui/gpxviewer.ui.h:9 msgid "Copyright (C) Andrew Gee 2009" msgstr "" #: ../ui/gpxviewer.ui.h:11 msgid "GPX Viewer Website" msgstr "" #: ../ui/gpxviewer.ui.h:12 msgid "" "GPX Viewer is an application that allows\n" "users to easily view GPS traces they \n" "have created in the GPX file format." msgstr "" #: ../ui/gpxviewer.ui.h:15 msgid "Get Help Online..." msgstr "" #: ../ui/gpxviewer.ui.h:16 msgid "Multiple Tracks" msgstr "" #: ../ui/gpxviewer.ui.h:17 msgid "Open with External Program..." msgstr "" #: ../ui/gpxviewer.ui.h:18 msgid "Report a Problem..." msgstr "" #: ../ui/gpxviewer.ui.h:19 msgid "Show Aggregate Statistics" msgstr "" #: ../ui/gpxviewer.ui.h:20 msgid "Show Sidebar" msgstr "" #: ../ui/gpxviewer.ui.h:21 msgid "Translate This Application..." msgstr "" #: ../ui/gpxviewer.ui.h:22 msgid "_File" msgstr "" #: ../ui/gpxviewer.ui.h:23 msgid "_Help" msgstr "" #: ../ui/gpxviewer.ui.h:24 msgid "_Tools" msgstr "" #: ../ui/gpxviewer.ui.h:25 msgid "_View" msgstr "" gpxviewer-0.4.3/po/hr.po0000664000175000017500000001027111752304676014465 0ustar andrewandrew# Croatian translation for gpxviewer # Copyright (c) 2010 Rosetta Contributors and Canonical Ltd 2010 # This file is distributed under the same license as the gpxviewer package. # FIRST AUTHOR , 2010. # msgid "" msgstr "" "Project-Id-Version: gpxviewer\n" "Report-Msgid-Bugs-To: FULL NAME \n" "POT-Creation-Date: 2010-12-31 00:11+0000\n" "PO-Revision-Date: 2010-05-05 20:00+0000\n" "Last-Translator: speedy \n" "Language-Team: Croatian \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "X-Launchpad-Export-Date: 2011-05-17 18:28+0000\n" "X-Generator: Launchpad (build 13053)\n" #: ../gpxviewer/ui.py:146 ../ui/gpxviewer.ui.h:10 msgid "GPX Viewer" msgstr "GPX Viewer" #: ../gpxviewer/ui.py:189 msgid "JOSM Editor" msgstr "" #: ../gpxviewer/ui.py:190 msgid "Merkaartor" msgstr "" #: ../gpxviewer/ui.py:363 #, python-format msgid "GPX Viewer - %s" msgstr "GPX Viewer - %s" #: ../gpxviewer/ui.py:380 msgid "Choose a GPX file to Load" msgstr "Odaberite GPX datoteku za uÄitavanje" #: ../gpxviewer/ui.py:394 msgid "" "You selected an invalid GPX file. \n" " Please try again" msgstr "" "Odabrali ste nevažeću GPX datoteku. \n" " Molimo pokuÅ¡ajte opet" #: ../gpxviewer/ui.py:419 #, python-format msgid "Distance: %.2f km" msgstr "" #: ../gpxviewer/ui.py:422 #, python-format msgid "Average Speed: %.2f m/s" msgstr "" #: ../gpxviewer/ui.py:425 #, python-format msgid "Maximum Speed: %.2f m/s" msgstr "" #: ../gpxviewer/ui.py:428 #, python-format msgid "Duration: %(minutes)s minutes, %(seconds)s seconds" msgstr "Trajanje: %(minutes)s minute, %(seconds)s sekunde" #: ../gpxviewer/ui.py:431 #, python-format msgid "Logging Date: %s" msgstr "" #: ../gpxviewer/ui.py:434 #, python-format msgid "Logging Time: %(from)s - %(to)s" msgstr "" #: ../ui/gpxviewer.ui.h:1 msgid "Average Speed: -- m/s" msgstr "ProsjeÄna brzina: -- m/s" #: ../ui/gpxviewer.ui.h:2 msgid "Distance: -- km" msgstr "Udaljenost: -- km" #: ../ui/gpxviewer.ui.h:3 msgid "Duration: -- minutes, -- seconds" msgstr "Trajanje: -- minute, -- sekunde" #: ../ui/gpxviewer.ui.h:4 msgid "Logging Date: --" msgstr "" #: ../ui/gpxviewer.ui.h:5 msgid "Logging Time: from -- to -- " msgstr "" #: ../ui/gpxviewer.ui.h:6 msgid "Maximum Speed: -- m/s" msgstr "Maksimalna brzina: -- m/s" #: ../ui/gpxviewer.ui.h:7 msgid "About GPX Viewer" msgstr "O GPX Vieweru" #: ../ui/gpxviewer.ui.h:8 msgid "Center on Selected Track" msgstr "" #: ../ui/gpxviewer.ui.h:9 msgid "Copyright (C) Andrew Gee 2009" msgstr "Copyright (C) Andrew Gee 2009" #: ../ui/gpxviewer.ui.h:11 msgid "GPX Viewer Website" msgstr "GPX Viewer Webstranica" #: ../ui/gpxviewer.ui.h:12 msgid "" "GPX Viewer is an application that allows\n" "users to easily view GPS traces they \n" "have created in the GPX file format." msgstr "" "GPX Viewer je aplikacija koja omogućava\n" "korisnicima da jednostavno pregledaju GPS tragove\n" "koje su kreirali u GPX formatu." #: ../ui/gpxviewer.ui.h:15 msgid "Get Help Online..." msgstr "Potraži pomoć na Internetu..." #: ../ui/gpxviewer.ui.h:16 msgid "Multiple Tracks" msgstr "" #: ../ui/gpxviewer.ui.h:17 msgid "Open with External Program..." msgstr "" #: ../ui/gpxviewer.ui.h:18 msgid "Report a Problem..." msgstr "Prijavi problem..." #: ../ui/gpxviewer.ui.h:19 msgid "Show Aggregate Statistics" msgstr "" #: ../ui/gpxviewer.ui.h:20 msgid "Show Sidebar" msgstr "" #: ../ui/gpxviewer.ui.h:21 msgid "Translate This Application..." msgstr "Prevedite Ovu Aplikaciju..." #: ../ui/gpxviewer.ui.h:22 msgid "_File" msgstr "_Datoteka" #: ../ui/gpxviewer.ui.h:23 msgid "_Help" msgstr "_Pomoć" #: ../ui/gpxviewer.ui.h:24 msgid "_Tools" msgstr "" #: ../ui/gpxviewer.ui.h:25 msgid "_View" msgstr "" #, python-format #~ msgid "Maximum Speed: %s m/s" #~ msgstr "Maksimalna brzina: %s m/s" #, python-format #~ msgid "Average Speed: %s m/s" #~ msgstr "ProsjeÄna brzina: %s m/s" #, python-format #~ msgid "Distance: %s km" #~ msgstr "Udaljenost: %s km" #~ msgid "Zoom In" #~ msgstr "Približi" #~ msgid "Zoom Out" #~ msgstr "Udalji" gpxviewer-0.4.3/po/hu.po0000664000175000017500000001112411752304676014466 0ustar andrewandrew# Hungarian translation for gpxviewer # Copyright (c) 2010 Rosetta Contributors and Canonical Ltd 2010 # This file is distributed under the same license as the gpxviewer package. # FIRST AUTHOR , 2010. # msgid "" msgstr "" "Project-Id-Version: gpxviewer\n" "Report-Msgid-Bugs-To: FULL NAME \n" "POT-Creation-Date: 2010-12-31 00:11+0000\n" "PO-Revision-Date: 2011-02-03 13:58+0000\n" "Last-Translator: György Balló \n" "Language-Team: Hungarian \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "X-Launchpad-Export-Date: 2011-05-17 18:28+0000\n" "X-Generator: Launchpad (build 13053)\n" #: ../gpxviewer/ui.py:146 ../ui/gpxviewer.ui.h:10 msgid "GPX Viewer" msgstr "GPX Viewer" #: ../gpxviewer/ui.py:189 msgid "JOSM Editor" msgstr "JOSM szerkesztÅ‘" #: ../gpxviewer/ui.py:190 msgid "Merkaartor" msgstr "Merkaartor" #: ../gpxviewer/ui.py:363 #, python-format msgid "GPX Viewer - %s" msgstr "GPX Viewer - %s" #: ../gpxviewer/ui.py:380 msgid "Choose a GPX file to Load" msgstr "Válasszon egy GPX fájlt a betöltéshez" #: ../gpxviewer/ui.py:394 msgid "" "You selected an invalid GPX file. \n" " Please try again" msgstr "" "A kiválasztott GPX fájl érvénytelen. \n" " Próbálja újra" #: ../gpxviewer/ui.py:419 #, python-format msgid "Distance: %.2f km" msgstr "Távolság: %.2f km" #: ../gpxviewer/ui.py:422 #, python-format msgid "Average Speed: %.2f m/s" msgstr "Ãtlagsebesség: %.2f m/s" #: ../gpxviewer/ui.py:425 #, python-format msgid "Maximum Speed: %.2f m/s" msgstr "Maximális sebesség: %.2f m/s" #: ../gpxviewer/ui.py:428 #, python-format msgid "Duration: %(minutes)s minutes, %(seconds)s seconds" msgstr "IdÅ‘tartam: %(minutes)s perc, %(seconds)s másodperc" #: ../gpxviewer/ui.py:431 #, python-format msgid "Logging Date: %s" msgstr "Rögzítés dátuma: %s" #: ../gpxviewer/ui.py:434 #, python-format msgid "Logging Time: %(from)s - %(to)s" msgstr "Rögzítés ideje: %(from)s - %(to)s" #: ../ui/gpxviewer.ui.h:1 msgid "Average Speed: -- m/s" msgstr "Ãtlagsebesség: -- m/s" #: ../ui/gpxviewer.ui.h:2 msgid "Distance: -- km" msgstr "Távolság: -- km" #: ../ui/gpxviewer.ui.h:3 msgid "Duration: -- minutes, -- seconds" msgstr "IdÅ‘tartam: -- perc, -- másodperc" #: ../ui/gpxviewer.ui.h:4 msgid "Logging Date: --" msgstr "Rögzítés dátuma: --" #: ../ui/gpxviewer.ui.h:5 msgid "Logging Time: from -- to -- " msgstr "Rögzítés ideje: from -- to -- " #: ../ui/gpxviewer.ui.h:6 msgid "Maximum Speed: -- m/s" msgstr "Maximális sebesség: -- m/s" #: ../ui/gpxviewer.ui.h:7 msgid "About GPX Viewer" msgstr "A GPX Viewer névjegye" #: ../ui/gpxviewer.ui.h:8 msgid "Center on Selected Track" msgstr "A kijelölt nyomvonal közepére" #: ../ui/gpxviewer.ui.h:9 msgid "Copyright (C) Andrew Gee 2009" msgstr "Copyright (C) Andrew Gee 2009" #: ../ui/gpxviewer.ui.h:11 msgid "GPX Viewer Website" msgstr "GPX Viewer webhelye" #: ../ui/gpxviewer.ui.h:12 msgid "" "GPX Viewer is an application that allows\n" "users to easily view GPS traces they \n" "have created in the GPX file format." msgstr "" "A GPX Viewer egy alkalmazás, amely lehetÅ‘vé teszi\n" "a felhasználóknak, hogy egyszerűen megtekintsék \n" "az általuk készített GPX fájlformátumú GPS nyomvonalaikat." #: ../ui/gpxviewer.ui.h:15 msgid "Get Help Online..." msgstr "Online segítség..." #: ../ui/gpxviewer.ui.h:16 msgid "Multiple Tracks" msgstr "Több nyomvonal" #: ../ui/gpxviewer.ui.h:17 msgid "Open with External Program..." msgstr "Megnyitás külsÅ‘ programmal..." #: ../ui/gpxviewer.ui.h:18 msgid "Report a Problem..." msgstr "Hiba jelentése..." #: ../ui/gpxviewer.ui.h:19 msgid "Show Aggregate Statistics" msgstr "Összesített statisztika megjelenítése" #: ../ui/gpxviewer.ui.h:20 msgid "Show Sidebar" msgstr "Oldalsáv megjelenítése" #: ../ui/gpxviewer.ui.h:21 msgid "Translate This Application..." msgstr "Ezen alkalmazás fordítása..." #: ../ui/gpxviewer.ui.h:22 msgid "_File" msgstr "_Fájl" #: ../ui/gpxviewer.ui.h:23 msgid "_Help" msgstr "_Súgó" #: ../ui/gpxviewer.ui.h:24 msgid "_Tools" msgstr "_Eszközök" #: ../ui/gpxviewer.ui.h:25 msgid "_View" msgstr "_Nézet" #, python-format #~ msgid "Maximum Speed: %s m/s" #~ msgstr "Maximális sebesség: %s m/s" #, python-format #~ msgid "Average Speed: %s m/s" #~ msgstr "Ãtlagsebesség: %s m/s" #, python-format #~ msgid "Distance: %s km" #~ msgstr "Távolság: %s km" gpxviewer-0.4.3/po/it.po0000664000175000017500000001122711752304676014472 0ustar andrewandrew# Italian translation for gpxviewer # Copyright (c) 2009 Rosetta Contributors and Canonical Ltd 2009 # This file is distributed under the same license as the gpxviewer package. # FIRST AUTHOR , 2009. # Diego Donati , 2009 msgid "" msgstr "" "Project-Id-Version: gpxviewer\n" "Report-Msgid-Bugs-To: FULL NAME \n" "POT-Creation-Date: 2010-12-31 00:11+0000\n" "PO-Revision-Date: 2011-02-14 16:56+0000\n" "Last-Translator: Martino Barbon \n" "Language-Team: DiegoDonatiTranslations, Italy\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "X-Launchpad-Export-Date: 2011-05-17 18:28+0000\n" "X-Generator: Launchpad (build 13053)\n" "Language: en\n" #: ../gpxviewer/ui.py:146 ../ui/gpxviewer.ui.h:10 msgid "GPX Viewer" msgstr "GPX Viewer" #: ../gpxviewer/ui.py:189 msgid "JOSM Editor" msgstr "Editor JOSM" #: ../gpxviewer/ui.py:190 msgid "Merkaartor" msgstr "Merkaartor" #: ../gpxviewer/ui.py:363 #, python-format msgid "GPX Viewer - %s" msgstr "GPX Viewer - %s" #: ../gpxviewer/ui.py:380 msgid "Choose a GPX file to Load" msgstr "Scegliere un file GPX da caricare" #: ../gpxviewer/ui.py:394 msgid "" "You selected an invalid GPX file. \n" " Please try again" msgstr "" "È stato selezionato un file GPX non valido. \n" " Riprovare ancora" #: ../gpxviewer/ui.py:419 #, python-format msgid "Distance: %.2f km" msgstr "Distanza: %.2f km" #: ../gpxviewer/ui.py:422 #, python-format msgid "Average Speed: %.2f m/s" msgstr "Velocità Media: %.2f m/s" #: ../gpxviewer/ui.py:425 #, python-format msgid "Maximum Speed: %.2f m/s" msgstr "Velocità Massima: %.2f m/s" #: ../gpxviewer/ui.py:428 #, python-format msgid "Duration: %(minutes)s minutes, %(seconds)s seconds" msgstr "Durata: %(minutes)s minuti, %(seconds)s secondi" #: ../gpxviewer/ui.py:431 #, python-format msgid "Logging Date: %s" msgstr "Data di Accesso: %s" #: ../gpxviewer/ui.py:434 #, python-format msgid "Logging Time: %(from)s - %(to)s" msgstr "Periodo di Accesso: %(from)s - %(to)s" #: ../ui/gpxviewer.ui.h:1 msgid "Average Speed: -- m/s" msgstr "Velocità media: -- m/s" #: ../ui/gpxviewer.ui.h:2 msgid "Distance: -- km" msgstr "Distanza: -- km" #: ../ui/gpxviewer.ui.h:3 msgid "Duration: -- minutes, -- seconds" msgstr "Durata: -- minuti, -- secondi" #: ../ui/gpxviewer.ui.h:4 msgid "Logging Date: --" msgstr "Data di Accesso: --" #: ../ui/gpxviewer.ui.h:5 msgid "Logging Time: from -- to -- " msgstr "Periodo di Accesso: da -- a -- " #: ../ui/gpxviewer.ui.h:6 msgid "Maximum Speed: -- m/s" msgstr "Velocità massima: -- m/s" #: ../ui/gpxviewer.ui.h:7 msgid "About GPX Viewer" msgstr "Informazioni su GPX Viewer" #: ../ui/gpxviewer.ui.h:8 msgid "Center on Selected Track" msgstr "Centra sulla Traccia Selezionata" #: ../ui/gpxviewer.ui.h:9 msgid "Copyright (C) Andrew Gee 2009" msgstr "Copyright (C) Andrew Gee 2009" #: ../ui/gpxviewer.ui.h:11 msgid "GPX Viewer Website" msgstr "Sito web di GPX Viewer" #: ../ui/gpxviewer.ui.h:12 msgid "" "GPX Viewer is an application that allows\n" "users to easily view GPS traces they \n" "have created in the GPX file format." msgstr "" "GPX Viewer è un applicazione che consente\n" "di visualizzare facilmente le tracciature GPS \n" "che gli utenti hanno creato in formato file GPX ." #: ../ui/gpxviewer.ui.h:15 msgid "Get Help Online..." msgstr "Ottieni aiuto online..." #: ../ui/gpxviewer.ui.h:16 msgid "Multiple Tracks" msgstr "Tracce Multiple" #: ../ui/gpxviewer.ui.h:17 msgid "Open with External Program..." msgstr "Apri con un Programma Esterno..." #: ../ui/gpxviewer.ui.h:18 msgid "Report a Problem..." msgstr "Segnala un problema" #: ../ui/gpxviewer.ui.h:19 msgid "Show Aggregate Statistics" msgstr "Mostra Statistiche Aggregate" #: ../ui/gpxviewer.ui.h:20 msgid "Show Sidebar" msgstr "Mostra la barra laterale" #: ../ui/gpxviewer.ui.h:21 msgid "Translate This Application..." msgstr "Traduci questa applicazione..." #: ../ui/gpxviewer.ui.h:22 msgid "_File" msgstr "_File" #: ../ui/gpxviewer.ui.h:23 msgid "_Help" msgstr "_Aiuto" #: ../ui/gpxviewer.ui.h:24 msgid "_Tools" msgstr "S_trumenti" #: ../ui/gpxviewer.ui.h:25 msgid "_View" msgstr "_Visualizza" #, python-format #~ msgid "Distance: %s km" #~ msgstr "Distanza: %s km" #, python-format #~ msgid "Average Speed: %s m/s" #~ msgstr "Velocità media: %s m/s" #, python-format #~ msgid "Maximum Speed: %s m/s" #~ msgstr "Velocità massima: %s m/s" #~ msgid "Zoom Out" #~ msgstr "Riduci Zoom" #~ msgid "Zoom In" #~ msgstr "Aumenta zoom" gpxviewer-0.4.3/po/ja.po0000664000175000017500000001120111752304676014440 0ustar andrewandrew# Japanese translation for gpxviewer # Copyright (c) 2010 Rosetta Contributors and Canonical Ltd 2010 # This file is distributed under the same license as the gpxviewer package. # FIRST AUTHOR , 2010. # msgid "" msgstr "" "Project-Id-Version: gpxviewer\n" "Report-Msgid-Bugs-To: FULL NAME \n" "POT-Creation-Date: 2010-12-31 00:11+0000\n" "PO-Revision-Date: 2010-07-03 09:25+0000\n" "Last-Translator: Hiroshi Miura \n" "Language-Team: Japanese \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "X-Launchpad-Export-Date: 2011-05-17 18:28+0000\n" "X-Generator: Launchpad (build 13053)\n" #: ../gpxviewer/ui.py:146 ../ui/gpxviewer.ui.h:10 msgid "GPX Viewer" msgstr "GPXビューア" #: ../gpxviewer/ui.py:189 msgid "JOSM Editor" msgstr "JOSMエディター" #: ../gpxviewer/ui.py:190 msgid "Merkaartor" msgstr "メルカトル・OSMエディター" #: ../gpxviewer/ui.py:363 #, python-format msgid "GPX Viewer - %s" msgstr "GPXビューア - %s" #: ../gpxviewer/ui.py:380 msgid "Choose a GPX file to Load" msgstr "読ã¿è¾¼ã‚€GPXãƒ•ã‚¡ã‚¤ãƒ«ã‚’é¸æŠžã—ã¦ãã ã•ã„。" #: ../gpxviewer/ui.py:394 msgid "" "You selected an invalid GPX file. \n" " Please try again" msgstr "" "䏿­£ãªGPXãƒ•ã‚¡ã‚¤ãƒ«ã‚’é¸æŠžã—ã¾ã—ãŸã€‚\n" " å†åº¦ã€è©¦ã¿ã¦ãã ã•ã„。" #: ../gpxviewer/ui.py:419 #, python-format msgid "Distance: %.2f km" msgstr "" #: ../gpxviewer/ui.py:422 #, python-format msgid "Average Speed: %.2f m/s" msgstr "" #: ../gpxviewer/ui.py:425 #, python-format msgid "Maximum Speed: %.2f m/s" msgstr "" #: ../gpxviewer/ui.py:428 #, python-format msgid "Duration: %(minutes)s minutes, %(seconds)s seconds" msgstr "æ‰€è¦æ™‚é–“: %(minutes)s 分, %(seconds)s ç§’" #: ../gpxviewer/ui.py:431 #, python-format msgid "Logging Date: %s" msgstr "記録日: %s" #: ../gpxviewer/ui.py:434 #, python-format msgid "Logging Time: %(from)s - %(to)s" msgstr "記録時刻: %(from)s - %(to)s" #: ../ui/gpxviewer.ui.h:1 msgid "Average Speed: -- m/s" msgstr "å¹³å‡é€Ÿåº¦: 毎秒 -- m" #: ../ui/gpxviewer.ui.h:2 msgid "Distance: -- km" msgstr "è·é›¢: -- km" #: ../ui/gpxviewer.ui.h:3 msgid "Duration: -- minutes, -- seconds" msgstr "時間: -- 分, -- ç§’" #: ../ui/gpxviewer.ui.h:4 msgid "Logging Date: --" msgstr "記録日: --" #: ../ui/gpxviewer.ui.h:5 msgid "Logging Time: from -- to -- " msgstr "記録時刻: from -- to -- " #: ../ui/gpxviewer.ui.h:6 msgid "Maximum Speed: -- m/s" msgstr "最大速度: 毎秒 -- m" #: ../ui/gpxviewer.ui.h:7 msgid "About GPX Viewer" msgstr "GPX Viewerã«ã¤ã„ã¦" #: ../ui/gpxviewer.ui.h:8 msgid "Center on Selected Track" msgstr "" #: ../ui/gpxviewer.ui.h:9 msgid "Copyright (C) Andrew Gee 2009" msgstr "Copyright (C) Andrew Gee 2009" #: ../ui/gpxviewer.ui.h:11 msgid "GPX Viewer Website" msgstr "GPX Viewer Webサイト" #: ../ui/gpxviewer.ui.h:12 msgid "" "GPX Viewer is an application that allows\n" "users to easily view GPS traces they \n" "have created in the GPX file format." msgstr "" "GPX Viewer ã¯ã€ãƒ¦ãƒ¼ã‚¶ãŒè‡ªåˆ†ã§ä½œæˆã—ãŸ\n" "GPXファイルフォーマットã®GPSトレースを\n" "容易ã«é–²è¦§ã§ãるよã†ã«ã™ã‚‹ã‚½ãƒ•トã§ã™ã€‚" #: ../ui/gpxviewer.ui.h:15 msgid "Get Help Online..." msgstr "オンラインヘルプを開ã..." #: ../ui/gpxviewer.ui.h:16 msgid "Multiple Tracks" msgstr "" #: ../ui/gpxviewer.ui.h:17 msgid "Open with External Program..." msgstr "外部プログラムã§é–‹ã..." #: ../ui/gpxviewer.ui.h:18 msgid "Report a Problem..." msgstr "å•題を報告ã™ã‚‹" #: ../ui/gpxviewer.ui.h:19 msgid "Show Aggregate Statistics" msgstr "" #: ../ui/gpxviewer.ui.h:20 msgid "Show Sidebar" msgstr "" #: ../ui/gpxviewer.ui.h:21 msgid "Translate This Application..." msgstr "ã“ã®ã‚¢ãƒ—リケーションを翻訳ã™ã‚‹..." #: ../ui/gpxviewer.ui.h:22 msgid "_File" msgstr "ファイル(_F)" #: ../ui/gpxviewer.ui.h:23 msgid "_Help" msgstr "ヘルプ(_H)" #: ../ui/gpxviewer.ui.h:24 msgid "_Tools" msgstr "ツール(_T)" #: ../ui/gpxviewer.ui.h:25 msgid "_View" msgstr "表示 (_V)" #, python-format #~ msgid "Average Speed: %s m/s" #~ msgstr "å¹³å‡é€Ÿåº¦: 毎秒 %s m" #, python-format #~ msgid "Distance: %s km" #~ msgstr "è·é›¢: %s km" #, python-format #~ msgid "Maximum Speed: %s m/s" #~ msgstr "最大速度: 毎秒 %s m" #~ msgid "Zoom In" #~ msgstr "拡大" #~ msgid "Zoom Out" #~ msgstr "縮å°" #~ msgid "Zoom _Out" #~ msgstr "縮å°è¡¨ç¤º(_O)" #~ msgid "Zoom _In" #~ msgstr "拡大表示(_I)" gpxviewer-0.4.3/po/nl.po0000664000175000017500000001031511752304676014464 0ustar andrewandrew# Dutch translation for gpxviewer # Copyright (c) 2009 Rosetta Contributors and Canonical Ltd 2009 # This file is distributed under the same license as the gpxviewer package. # FIRST AUTHOR , 2009. # msgid "" msgstr "" "Project-Id-Version: gpxviewer\n" "Report-Msgid-Bugs-To: FULL NAME \n" "POT-Creation-Date: 2010-12-31 00:11+0000\n" "PO-Revision-Date: 2010-09-19 17:11+0000\n" "Last-Translator: Dennis Groenveld \n" "Language-Team: Dutch \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "X-Launchpad-Export-Date: 2011-05-17 18:28+0000\n" "X-Generator: Launchpad (build 13053)\n" #: ../gpxviewer/ui.py:146 ../ui/gpxviewer.ui.h:10 msgid "GPX Viewer" msgstr "GPX Viewer" #: ../gpxviewer/ui.py:189 msgid "JOSM Editor" msgstr "JOSM Editor" #: ../gpxviewer/ui.py:190 msgid "Merkaartor" msgstr "" #: ../gpxviewer/ui.py:363 #, python-format msgid "GPX Viewer - %s" msgstr "GPX Viewer - %s" #: ../gpxviewer/ui.py:380 msgid "Choose a GPX file to Load" msgstr "Kies een GPX-bestand om te laden" #: ../gpxviewer/ui.py:394 msgid "" "You selected an invalid GPX file. \n" " Please try again" msgstr "" "Je hebt een ongeldig GPX-bestand gekozen. \n" " Probeer het opnieuw" #: ../gpxviewer/ui.py:419 #, python-format msgid "Distance: %.2f km" msgstr "" #: ../gpxviewer/ui.py:422 #, python-format msgid "Average Speed: %.2f m/s" msgstr "" #: ../gpxviewer/ui.py:425 #, python-format msgid "Maximum Speed: %.2f m/s" msgstr "" #: ../gpxviewer/ui.py:428 #, python-format msgid "Duration: %(minutes)s minutes, %(seconds)s seconds" msgstr "Duur: %(minutes)s minuten, %(seconds)s seconden" #: ../gpxviewer/ui.py:431 #, python-format msgid "Logging Date: %s" msgstr "" #: ../gpxviewer/ui.py:434 #, python-format msgid "Logging Time: %(from)s - %(to)s" msgstr "" #: ../ui/gpxviewer.ui.h:1 msgid "Average Speed: -- m/s" msgstr "Gemiddelde snelheid: -- m/s" #: ../ui/gpxviewer.ui.h:2 msgid "Distance: -- km" msgstr "Afstand: -- km" #: ../ui/gpxviewer.ui.h:3 msgid "Duration: -- minutes, -- seconds" msgstr "Duur: -- minuten, -- seconden" #: ../ui/gpxviewer.ui.h:4 msgid "Logging Date: --" msgstr "" #: ../ui/gpxviewer.ui.h:5 msgid "Logging Time: from -- to -- " msgstr "" #: ../ui/gpxviewer.ui.h:6 msgid "Maximum Speed: -- m/s" msgstr "Hoogste snelheid: -- m/s" #: ../ui/gpxviewer.ui.h:7 msgid "About GPX Viewer" msgstr "Over GPX Viewer" #: ../ui/gpxviewer.ui.h:8 msgid "Center on Selected Track" msgstr "" #: ../ui/gpxviewer.ui.h:9 msgid "Copyright (C) Andrew Gee 2009" msgstr "Copyright (C) Andrew Gee 2009" #: ../ui/gpxviewer.ui.h:11 msgid "GPX Viewer Website" msgstr "Website van GPX Viewer" #: ../ui/gpxviewer.ui.h:12 msgid "" "GPX Viewer is an application that allows\n" "users to easily view GPS traces they \n" "have created in the GPX file format." msgstr "" "GPX Viewer is een applicatie die het voor gebruikers\n" "mogelijk maakt GPS traces te bekijken die ze\n" "gemaakt hebben in het GPX bestandsformaat." #: ../ui/gpxviewer.ui.h:15 msgid "Get Help Online..." msgstr "Online hulp vragen..." #: ../ui/gpxviewer.ui.h:16 msgid "Multiple Tracks" msgstr "" #: ../ui/gpxviewer.ui.h:17 msgid "Open with External Program..." msgstr "" #: ../ui/gpxviewer.ui.h:18 msgid "Report a Problem..." msgstr "Een probleem melden..." #: ../ui/gpxviewer.ui.h:19 msgid "Show Aggregate Statistics" msgstr "" #: ../ui/gpxviewer.ui.h:20 msgid "Show Sidebar" msgstr "" #: ../ui/gpxviewer.ui.h:21 msgid "Translate This Application..." msgstr "Dit programma vertalen ..." #: ../ui/gpxviewer.ui.h:22 msgid "_File" msgstr "_Bestand" #: ../ui/gpxviewer.ui.h:23 msgid "_Help" msgstr "_Help" #: ../ui/gpxviewer.ui.h:24 msgid "_Tools" msgstr "" #: ../ui/gpxviewer.ui.h:25 msgid "_View" msgstr "" #, python-format #~ msgid "Maximum Speed: %s m/s" #~ msgstr "Hoogste snelheid: %s m/s" #, python-format #~ msgid "Average Speed: %s m/s" #~ msgstr "Gemiddelde snelheid: %s m/s" #, python-format #~ msgid "Distance: %s km" #~ msgstr "Afstand: %s km" #~ msgid "Zoom In" #~ msgstr "Inzoomen" #~ msgid "Zoom Out" #~ msgstr "Uitzoomen" gpxviewer-0.4.3/po/pl.po0000664000175000017500000001064011752304676014467 0ustar andrewandrew# Polish translation for gpxviewer # Copyright (c) 2009 Rosetta Contributors and Canonical Ltd 2009 # This file is distributed under the same license as the gpxviewer package. # FIRST AUTHOR , 2009. # msgid "" msgstr "" "Project-Id-Version: gpxviewer\n" "Report-Msgid-Bugs-To: FULL NAME \n" "POT-Creation-Date: 2010-12-31 00:11+0000\n" "PO-Revision-Date: 2010-10-03 14:30+0000\n" "Last-Translator: Naven \n" "Language-Team: Polish \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "X-Launchpad-Export-Date: 2011-05-17 18:28+0000\n" "X-Generator: Launchpad (build 13053)\n" #: ../gpxviewer/ui.py:146 ../ui/gpxviewer.ui.h:10 msgid "GPX Viewer" msgstr "GPX Viewer" #: ../gpxviewer/ui.py:189 msgid "JOSM Editor" msgstr "Edytor JOSM" #: ../gpxviewer/ui.py:190 msgid "Merkaartor" msgstr "Merkaartor" #: ../gpxviewer/ui.py:363 #, python-format msgid "GPX Viewer - %s" msgstr "GPX Viewer - %s" #: ../gpxviewer/ui.py:380 msgid "Choose a GPX file to Load" msgstr "Wskaż plik GPX do zaÅ‚adowania" #: ../gpxviewer/ui.py:394 msgid "" "You selected an invalid GPX file. \n" " Please try again" msgstr "" "WskazaÅ‚eÅ› nieprawidÅ‚owy plik GPX. \n" " Spróbuj ponownie" #: ../gpxviewer/ui.py:419 #, python-format msgid "Distance: %.2f km" msgstr "" #: ../gpxviewer/ui.py:422 #, python-format msgid "Average Speed: %.2f m/s" msgstr "" #: ../gpxviewer/ui.py:425 #, python-format msgid "Maximum Speed: %.2f m/s" msgstr "" #: ../gpxviewer/ui.py:428 #, python-format msgid "Duration: %(minutes)s minutes, %(seconds)s seconds" msgstr "Czas trwania: %(minutes)s minut, %(seconds)s sekund" #: ../gpxviewer/ui.py:431 #, python-format msgid "Logging Date: %s" msgstr "Data Zalogowania: %s" #: ../gpxviewer/ui.py:434 #, python-format msgid "Logging Time: %(from)s - %(to)s" msgstr "Czas Zalogowania: %(from)s - %(to)s" #: ../ui/gpxviewer.ui.h:1 msgid "Average Speed: -- m/s" msgstr "PrÄ™dkość Å›rednia: -- m/s" #: ../ui/gpxviewer.ui.h:2 msgid "Distance: -- km" msgstr "OdlegÅ‚ość: -- km" #: ../ui/gpxviewer.ui.h:3 msgid "Duration: -- minutes, -- seconds" msgstr "Czas trwania: -- minut, -- sekund" #: ../ui/gpxviewer.ui.h:4 msgid "Logging Date: --" msgstr "Data Zalogowania: --" #: ../ui/gpxviewer.ui.h:5 msgid "Logging Time: from -- to -- " msgstr "Czas Zalogowania: od -- do -- " #: ../ui/gpxviewer.ui.h:6 msgid "Maximum Speed: -- m/s" msgstr "PrÄ™dkość maksymalna: -- m/s" #: ../ui/gpxviewer.ui.h:7 msgid "About GPX Viewer" msgstr "O programie GPX Viewer" #: ../ui/gpxviewer.ui.h:8 msgid "Center on Selected Track" msgstr "" #: ../ui/gpxviewer.ui.h:9 msgid "Copyright (C) Andrew Gee 2009" msgstr "Prawa autorskie (C) Andrew Gee 2009" #: ../ui/gpxviewer.ui.h:11 msgid "GPX Viewer Website" msgstr "Strona domowa programu GPX Viewer" #: ../ui/gpxviewer.ui.h:12 msgid "" "GPX Viewer is an application that allows\n" "users to easily view GPS traces they \n" "have created in the GPX file format." msgstr "" "GPX Viewer jest aplikacjÄ…, która pozwala\n" "użytkownikom na proste przeglÄ…danie tras GPS, które \n" "utworzyli w formacie GPX." #: ../ui/gpxviewer.ui.h:15 msgid "Get Help Online..." msgstr "Uzyskaj pomoc online..." #: ../ui/gpxviewer.ui.h:16 msgid "Multiple Tracks" msgstr "" #: ../ui/gpxviewer.ui.h:17 msgid "Open with External Program..." msgstr "Otwórz za Pomoca ZewnÄ™trznego Programu..." #: ../ui/gpxviewer.ui.h:18 msgid "Report a Problem..." msgstr "ZgÅ‚oÅ› problem..." #: ../ui/gpxviewer.ui.h:19 msgid "Show Aggregate Statistics" msgstr "" #: ../ui/gpxviewer.ui.h:20 msgid "Show Sidebar" msgstr "" #: ../ui/gpxviewer.ui.h:21 msgid "Translate This Application..." msgstr "PrzetÅ‚umacz tÄ™ aplikacjÄ™..." #: ../ui/gpxviewer.ui.h:22 msgid "_File" msgstr "_Plik" #: ../ui/gpxviewer.ui.h:23 msgid "_Help" msgstr "_Pomoc" #: ../ui/gpxviewer.ui.h:24 msgid "_Tools" msgstr "_NarzÄ™dzia" #: ../ui/gpxviewer.ui.h:25 msgid "_View" msgstr "_Widok" #, python-format #~ msgid "Maximum Speed: %s m/s" #~ msgstr "PrÄ™dkość maksymalna: %s m/s" #, python-format #~ msgid "Average Speed: %s m/s" #~ msgstr "PrÄ™dkość Å›rednia: %s m/s" #, python-format #~ msgid "Distance: %s km" #~ msgstr "OdlegÅ‚ość: %s km" #~ msgid "Zoom In" #~ msgstr "Zbliż" #~ msgid "Zoom Out" #~ msgstr "Oddal" gpxviewer-0.4.3/po/pt.po0000664000175000017500000001044011752304676014475 0ustar andrewandrew# Portuguese translation for gpxviewer # Copyright (c) 2009 Rosetta Contributors and Canonical Ltd 2009 # This file is distributed under the same license as the gpxviewer package. # FIRST AUTHOR , 2009. # msgid "" msgstr "" "Project-Id-Version: gpxviewer\n" "Report-Msgid-Bugs-To: FULL NAME \n" "POT-Creation-Date: 2010-12-31 00:11+0000\n" "PO-Revision-Date: 2010-11-08 17:09+0000\n" "Last-Translator: Pedro Cunha \n" "Language-Team: Portuguese \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "X-Launchpad-Export-Date: 2011-05-17 18:28+0000\n" "X-Generator: Launchpad (build 13053)\n" #: ../gpxviewer/ui.py:146 ../ui/gpxviewer.ui.h:10 msgid "GPX Viewer" msgstr "GPX Viewer" #: ../gpxviewer/ui.py:189 msgid "JOSM Editor" msgstr "Editor JOSM" #: ../gpxviewer/ui.py:190 msgid "Merkaartor" msgstr "" #: ../gpxviewer/ui.py:363 #, python-format msgid "GPX Viewer - %s" msgstr "GPX Viewer - %s" #: ../gpxviewer/ui.py:380 msgid "Choose a GPX file to Load" msgstr "Escolha um ficheiro GPX para carregar" #: ../gpxviewer/ui.py:394 msgid "" "You selected an invalid GPX file. \n" " Please try again" msgstr "" "O ficheiro seleccionado é inválido.\n" " Tente novamente" #: ../gpxviewer/ui.py:419 #, python-format msgid "Distance: %.2f km" msgstr "" #: ../gpxviewer/ui.py:422 #, python-format msgid "Average Speed: %.2f m/s" msgstr "" #: ../gpxviewer/ui.py:425 #, python-format msgid "Maximum Speed: %.2f m/s" msgstr "" #: ../gpxviewer/ui.py:428 #, python-format msgid "Duration: %(minutes)s minutes, %(seconds)s seconds" msgstr "Duração: %(minutes)s minutos, %(seconds)s segundos" #: ../gpxviewer/ui.py:431 #, python-format msgid "Logging Date: %s" msgstr "Data de registo: %s" #: ../gpxviewer/ui.py:434 #, python-format msgid "Logging Time: %(from)s - %(to)s" msgstr "" #: ../ui/gpxviewer.ui.h:1 msgid "Average Speed: -- m/s" msgstr "Velocidade Média: -- m/s" #: ../ui/gpxviewer.ui.h:2 msgid "Distance: -- km" msgstr "Distância: -- km" #: ../ui/gpxviewer.ui.h:3 msgid "Duration: -- minutes, -- seconds" msgstr "Duração: -- minutos, -- segundos" #: ../ui/gpxviewer.ui.h:4 msgid "Logging Date: --" msgstr "Data de registo: --" #: ../ui/gpxviewer.ui.h:5 msgid "Logging Time: from -- to -- " msgstr "" #: ../ui/gpxviewer.ui.h:6 msgid "Maximum Speed: -- m/s" msgstr "Velocidade Máxima: -- m/s" #: ../ui/gpxviewer.ui.h:7 msgid "About GPX Viewer" msgstr "Sobre o GPX Viewer" #: ../ui/gpxviewer.ui.h:8 msgid "Center on Selected Track" msgstr "" #: ../ui/gpxviewer.ui.h:9 msgid "Copyright (C) Andrew Gee 2009" msgstr "Copyright (C) Andrew Gee 2009" #: ../ui/gpxviewer.ui.h:11 msgid "GPX Viewer Website" msgstr "GPX Viewer Website" #: ../ui/gpxviewer.ui.h:12 msgid "" "GPX Viewer is an application that allows\n" "users to easily view GPS traces they \n" "have created in the GPX file format." msgstr "" "O GPX Viewer é uma aplicação que permite aos\n" "utilizadores verem, de uma forma fácil, o mapeamento\n" "criado em formato GPX." #: ../ui/gpxviewer.ui.h:15 msgid "Get Help Online..." msgstr "Obtenha ajuda online..." #: ../ui/gpxviewer.ui.h:16 msgid "Multiple Tracks" msgstr "" #: ../ui/gpxviewer.ui.h:17 msgid "Open with External Program..." msgstr "Abrir com aplicação externa..." #: ../ui/gpxviewer.ui.h:18 msgid "Report a Problem..." msgstr "Reportar um problema..." #: ../ui/gpxviewer.ui.h:19 msgid "Show Aggregate Statistics" msgstr "" #: ../ui/gpxviewer.ui.h:20 msgid "Show Sidebar" msgstr "" #: ../ui/gpxviewer.ui.h:21 msgid "Translate This Application..." msgstr "Traduza esta aplicação..." #: ../ui/gpxviewer.ui.h:22 msgid "_File" msgstr "_Ficheiro" #: ../ui/gpxviewer.ui.h:23 msgid "_Help" msgstr "_Ajuda" #: ../ui/gpxviewer.ui.h:24 msgid "_Tools" msgstr "" #: ../ui/gpxviewer.ui.h:25 msgid "_View" msgstr "_Ver" #, python-format #~ msgid "Average Speed: %s m/s" #~ msgstr "Velocidade Média: %s m/s" #, python-format #~ msgid "Maximum Speed: %s m/s" #~ msgstr "Velocidade Máxima: %s m/s" #, python-format #~ msgid "Distance: %s km" #~ msgstr "Distância: %s km" #~ msgid "Zoom Out" #~ msgstr "Reduzir o Zoom" #~ msgid "Zoom In" #~ msgstr "Ampliar" gpxviewer-0.4.3/po/ru.po0000664000175000017500000001171211752304676014503 0ustar andrewandrew# Russian translation for gpxviewer # Copyright (c) 2009 Rosetta Contributors and Canonical Ltd 2009 # This file is distributed under the same license as the gpxviewer package. # FIRST AUTHOR , 2009. # msgid "" msgstr "" "Project-Id-Version: gpxviewer\n" "Report-Msgid-Bugs-To: FULL NAME \n" "POT-Creation-Date: 2010-12-31 00:11+0000\n" "PO-Revision-Date: 2010-07-03 09:24+0000\n" "Last-Translator: Andrew Gee \n" "Language-Team: Russian \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "X-Launchpad-Export-Date: 2011-05-17 18:28+0000\n" "X-Generator: Launchpad (build 13053)\n" #: ../gpxviewer/ui.py:146 ../ui/gpxviewer.ui.h:10 msgid "GPX Viewer" msgstr "GPX Viewer" #: ../gpxviewer/ui.py:189 msgid "JOSM Editor" msgstr "JOSM редактор" #: ../gpxviewer/ui.py:190 msgid "Merkaartor" msgstr "Merkaartor" #: ../gpxviewer/ui.py:363 #, python-format msgid "GPX Viewer - %s" msgstr "GPX Viewer - %s" #: ../gpxviewer/ui.py:380 msgid "Choose a GPX file to Load" msgstr "Выберите GPX файл Ð´Ð»Ñ Ð·Ð°Ð³Ñ€ÑƒÐ·ÐºÐ¸" #: ../gpxviewer/ui.py:394 msgid "" "You selected an invalid GPX file. \n" " Please try again" msgstr "" "Ð’Ñ‹ выбрали неверный GPX файл. \n" " ПожалуйÑта, попробуйте ещё раз" #: ../gpxviewer/ui.py:419 #, python-format msgid "Distance: %.2f km" msgstr "" #: ../gpxviewer/ui.py:422 #, python-format msgid "Average Speed: %.2f m/s" msgstr "" #: ../gpxviewer/ui.py:425 #, python-format msgid "Maximum Speed: %.2f m/s" msgstr "" #: ../gpxviewer/ui.py:428 #, python-format msgid "Duration: %(minutes)s minutes, %(seconds)s seconds" msgstr "ДлительноÑть: %(minutes)s минут, %(seconds)s Ñекунд" #: ../gpxviewer/ui.py:431 #, python-format msgid "Logging Date: %s" msgstr "Дата входа: %s" #: ../gpxviewer/ui.py:434 #, python-format msgid "Logging Time: %(from)s - %(to)s" msgstr "Ð’Ñ€ÐµÐ¼Ñ Ð²Ñ…Ð¾Ð´Ð°: %(from)s - %(to)s" #: ../ui/gpxviewer.ui.h:1 msgid "Average Speed: -- m/s" msgstr "СреднÑÑ ÑкороÑть: -- м/Ñ" #: ../ui/gpxviewer.ui.h:2 msgid "Distance: -- km" msgstr "РаÑÑтоÑние: -- км" #: ../ui/gpxviewer.ui.h:3 msgid "Duration: -- minutes, -- seconds" msgstr "ДлительноÑть: -- минут, -- Ñекунд" #: ../ui/gpxviewer.ui.h:4 msgid "Logging Date: --" msgstr "Дата входа: --" #: ../ui/gpxviewer.ui.h:5 msgid "Logging Time: from -- to -- " msgstr "Ð’Ñ€ÐµÐ¼Ñ Ð²Ñ…Ð¾Ð´Ð°: from -- to -- " #: ../ui/gpxviewer.ui.h:6 msgid "Maximum Speed: -- m/s" msgstr "МакÑÐ¸Ð¼Ð°Ð»ÑŒÐ½Ð°Ñ ÑкороÑть: -- м/Ñ" #: ../ui/gpxviewer.ui.h:7 msgid "About GPX Viewer" msgstr "О GPX Viewer" #: ../ui/gpxviewer.ui.h:8 msgid "Center on Selected Track" msgstr "" #: ../ui/gpxviewer.ui.h:9 msgid "Copyright (C) Andrew Gee 2009" msgstr "Copyright (C) Andrew Gee 2009" #: ../ui/gpxviewer.ui.h:11 msgid "GPX Viewer Website" msgstr "ВебÑайт GPX Viewer" #: ../ui/gpxviewer.ui.h:12 msgid "" "GPX Viewer is an application that allows\n" "users to easily view GPS traces they \n" "have created in the GPX file format." msgstr "" "GPX Viewer - приложение, которое позволÑет \n" "пользователÑм легко проÑматривать GPS треки, \n" "которые они ÑоздаютÑÑ Ð² формате GPX файлов." #: ../ui/gpxviewer.ui.h:15 msgid "Get Help Online..." msgstr "Справка в Ñети..." #: ../ui/gpxviewer.ui.h:16 msgid "Multiple Tracks" msgstr "" #: ../ui/gpxviewer.ui.h:17 msgid "Open with External Program..." msgstr "Открыть Ñ Ð¿Ð¾Ð¼Ð¾Ñ‰ÑŒÑŽ Ñторонних программ..." #: ../ui/gpxviewer.ui.h:18 msgid "Report a Problem..." msgstr "Сообщить о проблеме..." #: ../ui/gpxviewer.ui.h:19 msgid "Show Aggregate Statistics" msgstr "" #: ../ui/gpxviewer.ui.h:20 msgid "Show Sidebar" msgstr "" #: ../ui/gpxviewer.ui.h:21 msgid "Translate This Application..." msgstr "ПеревеÑти Ñто приложение..." #: ../ui/gpxviewer.ui.h:22 msgid "_File" msgstr "_Файл" #: ../ui/gpxviewer.ui.h:23 msgid "_Help" msgstr "_Справка" #: ../ui/gpxviewer.ui.h:24 msgid "_Tools" msgstr "_СервиÑ" #: ../ui/gpxviewer.ui.h:25 msgid "_View" msgstr "_Вид" #, python-format #~ msgid "Average Speed: %s m/s" #~ msgstr "СреднÑÑ ÑкороÑть: %s м/Ñ" #, python-format #~ msgid "Maximum Speed: %s m/s" #~ msgstr "МакÑÐ¸Ð¼Ð°Ð»ÑŒÐ½Ð°Ñ ÑкороÑть: %s м/Ñ" #, python-format #~ msgid "Distance: %s km" #~ msgstr "РаÑÑтоÑние: %s км" #~ msgid "Zoom In" #~ msgstr "Приблизить" #~ msgid "Zoom Out" #~ msgstr "Отдалить" #~ msgid "Zoom _In" #~ msgstr "У_величить" #~ msgid "Zoom _Out" #~ msgstr "У_меньшить" gpxviewer-0.4.3/po/sl.po0000664000175000017500000001031211752304676014466 0ustar andrewandrew# Slovenian translation for gpxviewer # Copyright (c) 2009 Rosetta Contributors and Canonical Ltd 2009 # This file is distributed under the same license as the gpxviewer package. # FIRST AUTHOR , 2009. # msgid "" msgstr "" "Project-Id-Version: gpxviewer\n" "Report-Msgid-Bugs-To: FULL NAME \n" "POT-Creation-Date: 2010-12-31 00:11+0000\n" "PO-Revision-Date: 2009-08-01 01:45+0000\n" "Last-Translator: Andrew Gee \n" "Language-Team: Slovenian \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "X-Launchpad-Export-Date: 2011-05-17 18:28+0000\n" "X-Generator: Launchpad (build 13053)\n" #: ../gpxviewer/ui.py:146 ../ui/gpxviewer.ui.h:10 msgid "GPX Viewer" msgstr "Pregledovalnik datotek GPX" #: ../gpxviewer/ui.py:189 msgid "JOSM Editor" msgstr "" #: ../gpxviewer/ui.py:190 msgid "Merkaartor" msgstr "" #: ../gpxviewer/ui.py:363 #, python-format msgid "GPX Viewer - %s" msgstr "Pregledovalnik datotek GPX - %s" #: ../gpxviewer/ui.py:380 msgid "Choose a GPX file to Load" msgstr "Izberite datoteko GPX" #: ../gpxviewer/ui.py:394 msgid "" "You selected an invalid GPX file. \n" " Please try again" msgstr "" "Izbrali ste neveljavno datoteko GPX. \n" " Prosimo, poizkusite znova" #: ../gpxviewer/ui.py:419 #, python-format msgid "Distance: %.2f km" msgstr "" #: ../gpxviewer/ui.py:422 #, python-format msgid "Average Speed: %.2f m/s" msgstr "" #: ../gpxviewer/ui.py:425 #, python-format msgid "Maximum Speed: %.2f m/s" msgstr "" #: ../gpxviewer/ui.py:428 #, python-format msgid "Duration: %(minutes)s minutes, %(seconds)s seconds" msgstr "Trajanje: %(minutes)s minut, %(seconds)s sekund" #: ../gpxviewer/ui.py:431 #, python-format msgid "Logging Date: %s" msgstr "" #: ../gpxviewer/ui.py:434 #, python-format msgid "Logging Time: %(from)s - %(to)s" msgstr "" #: ../ui/gpxviewer.ui.h:1 msgid "Average Speed: -- m/s" msgstr "PovpreÄna hitrost: -- m/s" #: ../ui/gpxviewer.ui.h:2 msgid "Distance: -- km" msgstr "Razdalja: -- km" #: ../ui/gpxviewer.ui.h:3 msgid "Duration: -- minutes, -- seconds" msgstr "Trajanje: -- minut, -- sekund" #: ../ui/gpxviewer.ui.h:4 msgid "Logging Date: --" msgstr "" #: ../ui/gpxviewer.ui.h:5 msgid "Logging Time: from -- to -- " msgstr "" #: ../ui/gpxviewer.ui.h:6 msgid "Maximum Speed: -- m/s" msgstr "NajviÅ¡ja hitrost: -- m/s" #: ../ui/gpxviewer.ui.h:7 msgid "About GPX Viewer" msgstr "O pregledovalniku datotek GPX" #: ../ui/gpxviewer.ui.h:8 msgid "Center on Selected Track" msgstr "" #: ../ui/gpxviewer.ui.h:9 msgid "Copyright (C) Andrew Gee 2009" msgstr "" #: ../ui/gpxviewer.ui.h:11 msgid "GPX Viewer Website" msgstr "DomaÄa stran pregledovalnika datotek GPX" #: ../ui/gpxviewer.ui.h:12 msgid "" "GPX Viewer is an application that allows\n" "users to easily view GPS traces they \n" "have created in the GPX file format." msgstr "" "Pregledovalnik GPX datotek je program, ki \n" "uporabnikom omogoÄa enostavno pregledovanje \n" "GPS sledi, shranjenih v datotekah GPX." #: ../ui/gpxviewer.ui.h:15 msgid "Get Help Online..." msgstr "PomoÄ na spletu..." #: ../ui/gpxviewer.ui.h:16 msgid "Multiple Tracks" msgstr "" #: ../ui/gpxviewer.ui.h:17 msgid "Open with External Program..." msgstr "" #: ../ui/gpxviewer.ui.h:18 msgid "Report a Problem..." msgstr "Prijavi težavo..." #: ../ui/gpxviewer.ui.h:19 msgid "Show Aggregate Statistics" msgstr "" #: ../ui/gpxviewer.ui.h:20 msgid "Show Sidebar" msgstr "" #: ../ui/gpxviewer.ui.h:21 msgid "Translate This Application..." msgstr "Prevedi ta program..." #: ../ui/gpxviewer.ui.h:22 msgid "_File" msgstr "_Datoteka" #: ../ui/gpxviewer.ui.h:23 msgid "_Help" msgstr "_PomoÄ" #: ../ui/gpxviewer.ui.h:24 msgid "_Tools" msgstr "" #: ../ui/gpxviewer.ui.h:25 msgid "_View" msgstr "" #, python-format #~ msgid "Maximum Speed: %s m/s" #~ msgstr "NajviÅ¡ja hitrost: %s m/s" #, python-format #~ msgid "Average Speed: %s m/s" #~ msgstr "PovpreÄna hitrost: %s m/s" #, python-format #~ msgid "Distance: %s km" #~ msgstr "Razdalja: %s km" #~ msgid "Zoom Out" #~ msgstr "PomanjÅ¡aj" #~ msgid "Zoom In" #~ msgstr "PoveÄaj" gpxviewer-0.4.3/po/sv.po0000664000175000017500000001044311752304676014505 0ustar andrewandrew# Swedish translation for gpxviewer # Copyright (c) 2009 Rosetta Contributors and Canonical Ltd 2009 # This file is distributed under the same license as the gpxviewer package. # FIRST AUTHOR , 2009. # msgid "" msgstr "" "Project-Id-Version: gpxviewer\n" "Report-Msgid-Bugs-To: FULL NAME \n" "POT-Creation-Date: 2010-12-31 00:11+0000\n" "PO-Revision-Date: 2011-04-05 12:46+0000\n" "Last-Translator: Erik SimmesgÃ¥rd \n" "Language-Team: Swedish \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "X-Launchpad-Export-Date: 2011-05-17 18:28+0000\n" "X-Generator: Launchpad (build 13053)\n" #: ../gpxviewer/ui.py:146 ../ui/gpxviewer.ui.h:10 msgid "GPX Viewer" msgstr "GPX Viewer" #: ../gpxviewer/ui.py:189 msgid "JOSM Editor" msgstr "" #: ../gpxviewer/ui.py:190 msgid "Merkaartor" msgstr "Merkaartor" #: ../gpxviewer/ui.py:363 #, python-format msgid "GPX Viewer - %s" msgstr "GPX Viewer - %s" #: ../gpxviewer/ui.py:380 msgid "Choose a GPX file to Load" msgstr "Välj en GPX-fil att ladda" #: ../gpxviewer/ui.py:394 msgid "" "You selected an invalid GPX file. \n" " Please try again" msgstr "" "Du valde en ogiltig GPX-fil. \n" " Vänligen försök igen" #: ../gpxviewer/ui.py:419 #, python-format msgid "Distance: %.2f km" msgstr "" #: ../gpxviewer/ui.py:422 #, python-format msgid "Average Speed: %.2f m/s" msgstr "Medelhastighet: %.2f m/s" #: ../gpxviewer/ui.py:425 #, python-format msgid "Maximum Speed: %.2f m/s" msgstr "Maxhastighet: %.2f m/s" #: ../gpxviewer/ui.py:428 #, python-format msgid "Duration: %(minutes)s minutes, %(seconds)s seconds" msgstr "Duration: %(minutes)s minuter, %(seconds)s sekunder" #: ../gpxviewer/ui.py:431 #, python-format msgid "Logging Date: %s" msgstr "" #: ../gpxviewer/ui.py:434 #, python-format msgid "Logging Time: %(from)s - %(to)s" msgstr "" #: ../ui/gpxviewer.ui.h:1 msgid "Average Speed: -- m/s" msgstr "Medelhastighet: -- m/s" #: ../ui/gpxviewer.ui.h:2 msgid "Distance: -- km" msgstr "Distans: -- km" #: ../ui/gpxviewer.ui.h:3 msgid "Duration: -- minutes, -- seconds" msgstr "Duration: -- minuter, -- sekunder" #: ../ui/gpxviewer.ui.h:4 msgid "Logging Date: --" msgstr "" #: ../ui/gpxviewer.ui.h:5 msgid "Logging Time: from -- to -- " msgstr "" #: ../ui/gpxviewer.ui.h:6 msgid "Maximum Speed: -- m/s" msgstr "Maximalhastighet: -- m/s" #: ../ui/gpxviewer.ui.h:7 msgid "About GPX Viewer" msgstr "Om GPX Viewer" #: ../ui/gpxviewer.ui.h:8 msgid "Center on Selected Track" msgstr "Centrera pÃ¥ valt spÃ¥r" #: ../ui/gpxviewer.ui.h:9 msgid "Copyright (C) Andrew Gee 2009" msgstr "Copyright (C) Andrew Gee 2009" #: ../ui/gpxviewer.ui.h:11 msgid "GPX Viewer Website" msgstr "GPX Viewer hemsida" #: ../ui/gpxviewer.ui.h:12 msgid "" "GPX Viewer is an application that allows\n" "users to easily view GPS traces they \n" "have created in the GPX file format." msgstr "" "GPX Viewer är en applikation som tillÃ¥ter\n" "användare att lätt se GPS-spÃ¥r dom \n" "har skapat i GPX-filformatet." #: ../ui/gpxviewer.ui.h:15 msgid "Get Help Online..." msgstr "FÃ¥ hjälp online..." #: ../ui/gpxviewer.ui.h:16 msgid "Multiple Tracks" msgstr "Multipla spÃ¥r" #: ../ui/gpxviewer.ui.h:17 msgid "Open with External Program..." msgstr "Öppna med externt program..." #: ../ui/gpxviewer.ui.h:18 msgid "Report a Problem..." msgstr "Rapportera problem..." #: ../ui/gpxviewer.ui.h:19 msgid "Show Aggregate Statistics" msgstr "Visa aggregerad statistik" #: ../ui/gpxviewer.ui.h:20 msgid "Show Sidebar" msgstr "" #: ../ui/gpxviewer.ui.h:21 msgid "Translate This Application..." msgstr "Översätt GPX Viewer..." #: ../ui/gpxviewer.ui.h:22 msgid "_File" msgstr "_Fil" #: ../ui/gpxviewer.ui.h:23 msgid "_Help" msgstr "_Hjälp" #: ../ui/gpxviewer.ui.h:24 msgid "_Tools" msgstr "" #: ../ui/gpxviewer.ui.h:25 msgid "_View" msgstr "_Visa" #, python-format #~ msgid "Maximum Speed: %s m/s" #~ msgstr "Maximalhastighet: %s m/s" #, python-format #~ msgid "Average Speed: %s m/s" #~ msgstr "Medelhastighet: %s m/s" #, python-format #~ msgid "Distance: %s km" #~ msgstr "Distans: %s km" #~ msgid "Zoom In" #~ msgstr "Zooma in" #~ msgid "Zoom Out" #~ msgstr "Zooma ut" gpxviewer-0.4.3/po/th.po0000664000175000017500000001151611752304676014472 0ustar andrewandrew# Thai translation for gpxviewer # Copyright (c) 2009 Rosetta Contributors and Canonical Ltd 2009 # This file is distributed under the same license as the gpxviewer package. # FIRST AUTHOR , 2009. # msgid "" msgstr "" "Project-Id-Version: gpxviewer\n" "Report-Msgid-Bugs-To: FULL NAME \n" "POT-Creation-Date: 2010-12-31 00:11+0000\n" "PO-Revision-Date: 2009-08-01 01:45+0000\n" "Last-Translator: Andrew Gee \n" "Language-Team: Thai \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "X-Launchpad-Export-Date: 2011-05-17 18:28+0000\n" "X-Generator: Launchpad (build 13053)\n" #: ../gpxviewer/ui.py:146 ../ui/gpxviewer.ui.h:10 msgid "GPX Viewer" msgstr "GPX Viewer" #: ../gpxviewer/ui.py:189 msgid "JOSM Editor" msgstr "" #: ../gpxviewer/ui.py:190 msgid "Merkaartor" msgstr "" #: ../gpxviewer/ui.py:363 #, python-format msgid "GPX Viewer - %s" msgstr "GPX Viewer - %s" #: ../gpxviewer/ui.py:380 msgid "Choose a GPX file to Load" msgstr "เลือà¸à¹„ฟล์ GPX เพื่อโหลด" #: ../gpxviewer/ui.py:394 msgid "" "You selected an invalid GPX file. \n" " Please try again" msgstr "" "คุณเลือà¸à¹„ฟล์ GPX ที่ไม่ถูà¸à¸•้อง\n" " โปรดลองอีà¸à¸„รั้ง" #: ../gpxviewer/ui.py:419 #, python-format msgid "Distance: %.2f km" msgstr "" #: ../gpxviewer/ui.py:422 #, python-format msgid "Average Speed: %.2f m/s" msgstr "" #: ../gpxviewer/ui.py:425 #, python-format msgid "Maximum Speed: %.2f m/s" msgstr "" #: ../gpxviewer/ui.py:428 #, python-format msgid "Duration: %(minutes)s minutes, %(seconds)s seconds" msgstr "ระยะเวลา: %(minutes)s นาที, %(seconds)s วินาที" #: ../gpxviewer/ui.py:431 #, python-format msgid "Logging Date: %s" msgstr "" #: ../gpxviewer/ui.py:434 #, python-format msgid "Logging Time: %(from)s - %(to)s" msgstr "" #: ../ui/gpxviewer.ui.h:1 msgid "Average Speed: -- m/s" msgstr "ความเร็วเฉลี่ย: -- ม./วินาที" #: ../ui/gpxviewer.ui.h:2 msgid "Distance: -- km" msgstr "ระยะทาง: -- à¸à¸¡." #: ../ui/gpxviewer.ui.h:3 msgid "Duration: -- minutes, -- seconds" msgstr "ระยะเวลา: -- นาที, -- วินาที" #: ../ui/gpxviewer.ui.h:4 msgid "Logging Date: --" msgstr "" #: ../ui/gpxviewer.ui.h:5 msgid "Logging Time: from -- to -- " msgstr "" #: ../ui/gpxviewer.ui.h:6 msgid "Maximum Speed: -- m/s" msgstr "ความเร็วสูงสุด: -- ม./วินาที" #: ../ui/gpxviewer.ui.h:7 msgid "About GPX Viewer" msgstr "เà¸à¸µà¹ˆà¸¢à¸§à¸à¸±à¸š GPX Viewer" #: ../ui/gpxviewer.ui.h:8 msgid "Center on Selected Track" msgstr "" #: ../ui/gpxviewer.ui.h:9 msgid "Copyright (C) Andrew Gee 2009" msgstr "สงวนลิขสิทธิ์ (C) Andrew Gee 2009" #: ../ui/gpxviewer.ui.h:11 msgid "GPX Viewer Website" msgstr "เว็บไซต์ GPX Viewer" #: ../ui/gpxviewer.ui.h:12 msgid "" "GPX Viewer is an application that allows\n" "users to easily view GPS traces they \n" "have created in the GPX file format." msgstr "" "GPX Viewer คือโปรà¹à¸à¸£à¸¡à¸—ี่ทำให้\n" "ผู้ใช้เข้าดู GPS traces ได้โดยง่าย \n" "ซึ่งพวà¸à¹€à¸‚าสร้างไว้ในรูปà¹à¸šà¸š GPX" #: ../ui/gpxviewer.ui.h:15 msgid "Get Help Online..." msgstr "รับความช่วยเหลือออนไลน์..." #: ../ui/gpxviewer.ui.h:16 msgid "Multiple Tracks" msgstr "" #: ../ui/gpxviewer.ui.h:17 msgid "Open with External Program..." msgstr "" #: ../ui/gpxviewer.ui.h:18 msgid "Report a Problem..." msgstr "รายงานปัà¸à¸«à¸²..." #: ../ui/gpxviewer.ui.h:19 msgid "Show Aggregate Statistics" msgstr "" #: ../ui/gpxviewer.ui.h:20 msgid "Show Sidebar" msgstr "" #: ../ui/gpxviewer.ui.h:21 msgid "Translate This Application..." msgstr "à¹à¸›à¸¥à¹‚ปรà¹à¸à¸£à¸¡à¸™à¸µà¹‰..." #: ../ui/gpxviewer.ui.h:22 msgid "_File" msgstr "à¹_ฟ้ม" #: ../ui/gpxviewer.ui.h:23 msgid "_Help" msgstr "_ช่วยเหลือ" #: ../ui/gpxviewer.ui.h:24 msgid "_Tools" msgstr "" #: ../ui/gpxviewer.ui.h:25 msgid "_View" msgstr "" #, python-format #~ msgid "Average Speed: %s m/s" #~ msgstr "ความเร็วเฉลี่ย: %s ม./วินาที" #, python-format #~ msgid "Maximum Speed: %s m/s" #~ msgstr "ความเร็วสูงสุด: %s ม./วินาที" #, python-format #~ msgid "Distance: %s km" #~ msgstr "ระยะทาง: %s à¸à¸¡." #~ msgid "Zoom In" #~ msgstr "ซูมเข้า" #~ msgid "Zoom Out" #~ msgstr "ซูมออà¸" gpxviewer-0.4.3/po/uk.po0000664000175000017500000001200411752304676014467 0ustar andrewandrew# Ukrainian translation for gpxviewer # Copyright (c) 2010 Rosetta Contributors and Canonical Ltd 2010 # This file is distributed under the same license as the gpxviewer package. # FIRST AUTHOR , 2010. # FULL NAME , 2010. # Sergiy Gavrylov , 2010. # msgid "" msgstr "" "Project-Id-Version: gpxviewer\n" "Report-Msgid-Bugs-To: FULL NAME \n" "POT-Creation-Date: 2010-12-31 00:11+0000\n" "PO-Revision-Date: 2010-07-03 09:25+0000\n" "Last-Translator: Sergiy Gavrylov \n" "Language-Team: Ukrainian \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "X-Launchpad-Export-Date: 2011-05-17 18:28+0000\n" "X-Generator: Launchpad (build 13053)\n" #: ../gpxviewer/ui.py:146 ../ui/gpxviewer.ui.h:10 msgid "GPX Viewer" msgstr "GPX Viewer" #: ../gpxviewer/ui.py:189 msgid "JOSM Editor" msgstr "Редактор JOSM" #: ../gpxviewer/ui.py:190 msgid "Merkaartor" msgstr "Merkaartor" #: ../gpxviewer/ui.py:363 #, python-format msgid "GPX Viewer - %s" msgstr "GPX Viewer - %s" #: ../gpxviewer/ui.py:380 msgid "Choose a GPX file to Load" msgstr "Виберіть GPX-файл Ð´Ð»Ñ Ð·Ð°Ð²Ð°Ð½Ñ‚Ð°Ð¶ÐµÐ½Ð½Ñ" #: ../gpxviewer/ui.py:394 msgid "" "You selected an invalid GPX file. \n" " Please try again" msgstr "" "Ви вибрали помилковий GPX-файл. \n" " Спробуйте інший" #: ../gpxviewer/ui.py:419 #, python-format msgid "Distance: %.2f km" msgstr "" #: ../gpxviewer/ui.py:422 #, python-format msgid "Average Speed: %.2f m/s" msgstr "" #: ../gpxviewer/ui.py:425 #, python-format msgid "Maximum Speed: %.2f m/s" msgstr "" #: ../gpxviewer/ui.py:428 #, python-format msgid "Duration: %(minutes)s minutes, %(seconds)s seconds" msgstr "ТриваліÑть: %(minutes)s хвилин, %(seconds)s Ñекунд" #: ../gpxviewer/ui.py:431 #, python-format msgid "Logging Date: %s" msgstr "Дата реєÑтрації: %s" #: ../gpxviewer/ui.py:434 #, python-format msgid "Logging Time: %(from)s - %(to)s" msgstr "Ð§Ð°Ñ Ñ€ÐµÑ”Ñтрації: %(from)s - %(to)s" #: ../ui/gpxviewer.ui.h:1 msgid "Average Speed: -- m/s" msgstr "Ð¡ÐµÑ€ÐµÐ´Ð½Ñ ÑˆÐ²Ð¸Ð´ÐºÑ–Ñть: -- м/Ñ" #: ../ui/gpxviewer.ui.h:2 msgid "Distance: -- km" msgstr "ВідÑтань: -- км" #: ../ui/gpxviewer.ui.h:3 msgid "Duration: -- minutes, -- seconds" msgstr "ТриваліÑть: -- хвилин, -- Ñекунд" #: ../ui/gpxviewer.ui.h:4 msgid "Logging Date: --" msgstr "Дата реєÑтрації: --" #: ../ui/gpxviewer.ui.h:5 msgid "Logging Time: from -- to -- " msgstr "Ð§Ð°Ñ Ñ€ÐµÑ”Ñтрації: від -- до -- " #: ../ui/gpxviewer.ui.h:6 msgid "Maximum Speed: -- m/s" msgstr "МакÑимальна швидкіÑть: -- м/Ñ" #: ../ui/gpxviewer.ui.h:7 msgid "About GPX Viewer" msgstr "Про GPX Viewer" #: ../ui/gpxviewer.ui.h:8 msgid "Center on Selected Track" msgstr "" #: ../ui/gpxviewer.ui.h:9 msgid "Copyright (C) Andrew Gee 2009" msgstr "Copyright (C) Andrew Gee 2009" #: ../ui/gpxviewer.ui.h:11 msgid "GPX Viewer Website" msgstr "Веб-Ñайт GPX Viewer" #: ../ui/gpxviewer.ui.h:12 msgid "" "GPX Viewer is an application that allows\n" "users to easily view GPS traces they \n" "have created in the GPX file format." msgstr "" "Програма GPX Viewer дає змогу\n" "легко переглÑдати GPS-запиÑи, \n" "Ñтворені у форматі GPX-файлів." #: ../ui/gpxviewer.ui.h:15 msgid "Get Help Online..." msgstr "Отримати онлайн довідку..." #: ../ui/gpxviewer.ui.h:16 msgid "Multiple Tracks" msgstr "" #: ../ui/gpxviewer.ui.h:17 msgid "Open with External Program..." msgstr "Відкрити у зовнішній програмі..." #: ../ui/gpxviewer.ui.h:18 msgid "Report a Problem..." msgstr "Звітувати про проблему..." #: ../ui/gpxviewer.ui.h:19 msgid "Show Aggregate Statistics" msgstr "" #: ../ui/gpxviewer.ui.h:20 msgid "Show Sidebar" msgstr "" #: ../ui/gpxviewer.ui.h:21 msgid "Translate This Application..." msgstr "ПереклаÑти цю програму..." #: ../ui/gpxviewer.ui.h:22 msgid "_File" msgstr "_Файл" #: ../ui/gpxviewer.ui.h:23 msgid "_Help" msgstr "_Довідка" #: ../ui/gpxviewer.ui.h:24 msgid "_Tools" msgstr "_ІнÑтрументи" #: ../ui/gpxviewer.ui.h:25 msgid "_View" msgstr "П_ереглÑд" #, python-format #~ msgid "Distance: %s km" #~ msgstr "ВідÑтань: %s км" #, python-format #~ msgid "Average Speed: %s m/s" #~ msgstr "Ð¡ÐµÑ€ÐµÐ´Ð½Ñ ÑˆÐ²Ð¸Ð´ÐºÑ–Ñть: %s м/Ñ" #, python-format #~ msgid "Maximum Speed: %s m/s" #~ msgstr "МакÑимальна швидкіÑть: %s м/Ñ" #~ msgid "Zoom In" #~ msgstr "Збільшити" #~ msgid "Zoom Out" #~ msgstr "Зменшити" #~ msgid "Zoom _Out" #~ msgstr "З_меншити" #~ msgid "Zoom _In" #~ msgstr "З_більшити" gpxviewer-0.4.3/po/zh_CN.po0000664000175000017500000000646611752304676015070 0ustar andrewandrew# Simplified Chinese translation for gpxviewer # Copyright (c) 2009 Rosetta Contributors and Canonical Ltd 2009 # This file is distributed under the same license as the gpxviewer package. # FIRST AUTHOR , 2009. # msgid "" msgstr "" "Project-Id-Version: gpxviewer\n" "Report-Msgid-Bugs-To: FULL NAME \n" "POT-Creation-Date: 2010-12-31 00:11+0000\n" "PO-Revision-Date: 2010-03-30 15:48+0000\n" "Last-Translator: Andrew Gee \n" "Language-Team: Simplified Chinese \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "X-Launchpad-Export-Date: 2011-05-17 18:28+0000\n" "X-Generator: Launchpad (build 13053)\n" #: ../gpxviewer/ui.py:146 ../ui/gpxviewer.ui.h:10 msgid "GPX Viewer" msgstr "GPX 查看器" #: ../gpxviewer/ui.py:189 msgid "JOSM Editor" msgstr "" #: ../gpxviewer/ui.py:190 msgid "Merkaartor" msgstr "" #: ../gpxviewer/ui.py:363 #, python-format msgid "GPX Viewer - %s" msgstr "GPX 查看器 - %s" #: ../gpxviewer/ui.py:380 msgid "Choose a GPX file to Load" msgstr "" #: ../gpxviewer/ui.py:394 msgid "" "You selected an invalid GPX file. \n" " Please try again" msgstr "" #: ../gpxviewer/ui.py:419 #, python-format msgid "Distance: %.2f km" msgstr "" #: ../gpxviewer/ui.py:422 #, python-format msgid "Average Speed: %.2f m/s" msgstr "" #: ../gpxviewer/ui.py:425 #, python-format msgid "Maximum Speed: %.2f m/s" msgstr "" #: ../gpxviewer/ui.py:428 #, python-format msgid "Duration: %(minutes)s minutes, %(seconds)s seconds" msgstr "" #: ../gpxviewer/ui.py:431 #, python-format msgid "Logging Date: %s" msgstr "" #: ../gpxviewer/ui.py:434 #, python-format msgid "Logging Time: %(from)s - %(to)s" msgstr "" #: ../ui/gpxviewer.ui.h:1 msgid "Average Speed: -- m/s" msgstr "" #: ../ui/gpxviewer.ui.h:2 msgid "Distance: -- km" msgstr "" #: ../ui/gpxviewer.ui.h:3 msgid "Duration: -- minutes, -- seconds" msgstr "" #: ../ui/gpxviewer.ui.h:4 msgid "Logging Date: --" msgstr "" #: ../ui/gpxviewer.ui.h:5 msgid "Logging Time: from -- to -- " msgstr "" #: ../ui/gpxviewer.ui.h:6 msgid "Maximum Speed: -- m/s" msgstr "" #: ../ui/gpxviewer.ui.h:7 msgid "About GPX Viewer" msgstr "关于GPX 查看器" #: ../ui/gpxviewer.ui.h:8 msgid "Center on Selected Track" msgstr "" #: ../ui/gpxviewer.ui.h:9 msgid "Copyright (C) Andrew Gee 2009" msgstr "" #: ../ui/gpxviewer.ui.h:11 msgid "GPX Viewer Website" msgstr "" #: ../ui/gpxviewer.ui.h:12 msgid "" "GPX Viewer is an application that allows\n" "users to easily view GPS traces they \n" "have created in the GPX file format." msgstr "" #: ../ui/gpxviewer.ui.h:15 msgid "Get Help Online..." msgstr "" #: ../ui/gpxviewer.ui.h:16 msgid "Multiple Tracks" msgstr "" #: ../ui/gpxviewer.ui.h:17 msgid "Open with External Program..." msgstr "" #: ../ui/gpxviewer.ui.h:18 msgid "Report a Problem..." msgstr "" #: ../ui/gpxviewer.ui.h:19 msgid "Show Aggregate Statistics" msgstr "" #: ../ui/gpxviewer.ui.h:20 msgid "Show Sidebar" msgstr "" #: ../ui/gpxviewer.ui.h:21 msgid "Translate This Application..." msgstr "" #: ../ui/gpxviewer.ui.h:22 msgid "_File" msgstr "" #: ../ui/gpxviewer.ui.h:23 msgid "_Help" msgstr "" #: ../ui/gpxviewer.ui.h:24 msgid "_Tools" msgstr "" #: ../ui/gpxviewer.ui.h:25 msgid "_View" msgstr "" gpxviewer-0.4.3/ui/gpxviewer.png0000664000175000017500000000531511752304676016244 0ustar andrewandrew‰PNG  IHDR@@ªiqÞsBIT|dˆ pHYsÅÅ 1¥wtEXtSoftwarewww.inkscape.org›î< JIDATxœÍ›yp”åÇ?ïÙd“ìæ 9HWˆ€ˆ@¨ ¡"V±ˆ¢Øjk­×jËh×^£¸ã8£.mÅb­8tÆB§¨A0H9 rÈ‘@ ä$÷&Ùd³»oÿØ$æØã}÷}¡|fv’÷y~Ï÷ù=¿}ö}ßçDQäJbµ‹ñÀ­À =ìÓ Ô úT÷ý= ì±Y÷•ôO¸°ÚÅL`)p70Ð…)Õl>Šl¡S¿GµXíb ð$ppƒ*¢CévÚ,µDÀjuÀ/€—€d5œ’À~ày›EاTHQ¬vq9°¯Ô‘0ù7ð‚Í"œ W ¬XíâD`0;ÜŠUĬVÛ,Bܲ`µ‹‹€M@œÜÊäàÕ¸)Í_Omf1c*î&¥f.1ެ!6¢à¡!í—ÆáÖw^ê¡9Y9í’œzdÀjŸÞFÂ]½%®Œ¸¶q¢FŽ?4&bÏ¢‡iI<9$=Ò9ŠÄË7xùzæsTgîÄehÈOhœêÊ.¿çþ¢‚?•Z—¤ôÝèìÀãRDëR²ué¦{šÙ%/Iõ€vs[WÐÙ$«\?ZwùÇžz}׌7/Å>ä×Ó×øÏØx€’‚WÐD6S:i#nSj1j2w†ÝxÎÉ©©þÝw­Kн”þù°8PfODûçü’Ù¯Ð|„Ž˜K´&|‡¸£.Ó8ê˜D×}zdÙûíïä››^X}wñ{†² ú[¶ÚÅÇð½ÜdÇWQ;v+ˆZ:¢kqZqÅV¡zõ8e~›™•Kˆk™Dk|ØO6z"›8:ëµuwìí8»mîs»ÙìV»8X¬’ÎèšR¡¼èÝFšO ˆê]& .“,Çu½Ñühó^’kçÈ*ç®èÍ™)ï->ðzf ¿°ÚÅlàŸ€>X‚¨%¦-·Ç€©a:^E#êè¿­:Ó]3O¶ã†î–|º“ìŠe²Ë§Ý\Ñ’xbŸÕ.jýåk_~ùå‰;Kø ˜J\ßÃÄÒñŠ…_¾ODo ".CG7í[ƒ©=',Ç5^=9åËiJ>B[|YXý´&œ6Ç8ÆÄ­È½þóáy#ƒV»x Ú`C)gó>bÏ¢Gë$4NñÌÛùaê÷ߨ88}HúºÉq`²’Êܺ.š“ŽÑWF{\ÆŽ4’êg‘Ð87è¯ QðÒ<êué_Q›¾‡ÚÌb\mJÜ`âÉŸïÍÿ ppÚð§À*4ÞÑÆ©ikùnú»~ŸåZwÉu¤VßLjÍ\’kç sqë;¹0v U9Û¸”µCÑ{@0*&|rË-GcgOû`Ú@°ÚÅ(  È·‚íË ©ÍØ-Ù^ãÕ¡sGãÖ:ñj]áV+‹œòå'ŽÛ|Ý€ƒò~‰‚ÆWeo“Õxð x\mW­ñ5ÅùO|\–×=8÷+Î,VRüªÑÙDYÞG/ö_k¬v1 ˜©DørÊÁÐF×µ™Å·÷ÿßßî„p½7ɇ•úuÕhH-1/øö™é04aÓ2ê8™£¾ÿ/"^mïSº¾ÙÜ[•Èէ횟Ð8¼Ñn>Ãt‡Ù÷QëùNcÝBð½%b-‰ßÍŸóÕ;¤VÏ‘ÞÙŒÃt·¾‹È®d¢ºRˆp™ðèºqëº|½ï¯Gçr]µ“³y€ð&u[NgZíb„– ⚃âšóý¦º0tÿëz£ÑõFÕÌ-]‰¹e‡~ð[鎢-¾T(Íÿk¾ßò”"Ò/Þ0/Ò™D¤s”Ò*ü2áÔCþy©ÍØ]¨JâšóH¼<Ý^KÈAeØDu¥* noD›:=àÆý¯cj˸ֻb™zøy ·oRCÞ/Óù!³ÂrH¿¸ˆYÿ}«L‡Jȸ°˜{7œÅa:OCê×d\XŒ¡'^rùË©%Teÿ‡ÆäC¸ ­x´=Du¥0ªa S‰kžŒ©uÜÀh²3¶ŠÝ·ýVúZHbà ¤W-$µz™•K!NµôÛžC¬ÌI#/qtÖkø»£Weoø_ãÕajGLûj3vKj¼©u¹e+É=³së„áÙ£Ã]¶–…WëÂ[‰Ã|ŽvsóyœÆzz"›pëiJ:*MGã¦5þLÈ Ó(g29e+Wº’¤ºà«w:|›&Jl‹,z"›9>ãMNN[{UÞ5^=3÷½Aþ·ÏH}:Ô\±8LçÙòÀôZÔ–HÞñ'¸îè¯å©Ñà €Êˆì]øèUm<€ÓX'·È• Àé)ë¨ÉøRmÙ8L•r‹¨€ŽØ |3÷7jJJ¯;ÌœSÓ‰ƒóž£Wß¡¦¤dœÆz97Ûnúð% {g…?ÚÍTæþK ©°ñjz¥šî²Y—Æf:€]jT^>i#¢àUC*,4^=zék‘[àû¡­j8S¾ 3kŠ‘ñê-âÛó0$Š7 Æ7M!»\ù‚f¸œ‰RMÚ,BôÀfjoÔpbÆ×¯Õ•¢†”l Ý’0°ö9x]à5œˆkÎcÙß3þôª³:jcìL“bæ6÷_ À:@Ö³@D:“¸ù‹¿ñàúz ·o"§|9Zw”ÒA1v¦K1Û`³åý°Y'¾í®ª¡sÉ)_NáöM<¸¾žEÉ:'O„šÕ Ýrdï^œ0|‡Èà”Š>  ï!·t%‹>Û½—¡õ(šˆöKtGÈ¥M»Í"TN›Eð/¨ì×bYŒ?½Ju]cgÐР؆'ŽØ#d³[½ê¹åŸëŽÈ¶JÂØô°ÆfFL Ú%öS 1@ž*˜['לÚP"ÆÎÑÁ¦âvàÛï8¿°Y„J`9 ùÅ:|“ê]~O }ÉeÀоŸ÷î´Y„¯€§ÕqÏ?•·‡6’ÈØ³÷úKnîò×õû ºUÖfÞþ¤ÌµÀ¤ÖÎEߣXÇØ‘NJÍMÓ=ø¾ùÒ`e¥ì~±¿N 4žF_\¨X'§â† ÂDàY›E( éC(ƒ¾ckwï…ë`0²*ïP¬‘S6¤ûw÷Ù,Â¥”½b&¤âÑ9ùÇÃ98£Â*oìHç/Òת€¥6‹ m¡i?ú¢ºoAÎZwS¯»üäãOÒ×ø¯™r2`³_@‰Ü²˜|ÌÂÌ}oÈ>^“viS?ïÁw£^`³õr뾦ŽÍš¶–óŸ‘dkèNäÎMû?7·ŽÿÕU?67µNîYôgó> ˜åLfléU?¾qéâmJë»æŽÎöF8(™·š c·ÐÙ„(x‰êJ%¹n¶'©~Ö‰ '^óîÏÓ6‡V’Æ5~xZlqÚ‹"zÌ›¹ÖOâZ?>ÿ?ŒµÄ¥iIEND®B`‚gpxviewer-0.4.3/ui/gpxviewer.svg0000664000175000017500000002670311752304676016263 0ustar andrewandrew image/svg+xml gpxviewer-0.4.3/ui/gpxviewer.ui0000664000175000017500000016252111752304676016100 0ustar andrewandrew GPX Viewer 800 600 True True True _File True True gtk-open True True True accelgroup1 True gtk-quit True True True accelgroup1 True _View True True gtk-zoom-in True True True accelgroup1 gtk-zoom-out True True True accelgroup1 True True Center on Selected Track True True True True Multiple Tracks True True True Show Sidebar True True Show Aggregate Statistics True True _Tools True True Open with External Program... True True False accelgroup1 True _Help True True True Get Help Online... True True Translate This Application... True True Report a Problem... True True gtk-about True True True accelgroup1 False 0 True 4 4 4 4 True True True never automatic 0 True gtk-add True True True True False 0 gtk-delete True True True True 1 False 1 False 1 True True 2 3 True 0 <b>Duration:</b> -- minutes, -- seconds True 1 2 1 2 4 True 0 <b>Maximum Speed:</b> -- m/s True 1 2 3 True 0 <b>Average Speed:</b> -- m/s True 1 2 3 True 0 <b>Distance:</b> -- km True 3 True 0 <b>Logging Date:</b> -- True 2 3 3 True 0 <b>Logging Time:</b> from -- to -- True 2 3 1 2 3 False 0 True 1 4 end 0 1 True 4 False False 2 5 About GPX Viewer False center-on-parent dialog GPX Viewer 0.4.3 Copyright (C) Andrew Gee 2009 GPX Viewer is an application that allows users to easily view GPS traces they have created in the GPX file format. http://andrewgee.org/blog/gpxviewer/ GPX Viewer Website GNU GENERAL PUBLIC LICENSE Version 3, 29 June 2007 Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/> Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The GNU General Public License is a free, copyleft license for software and other kinds of works. The licenses for most software and other practical works are designed to take away your freedom to share and change the works. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change all versions of a program--to make sure it remains free software for all its users. We, the Free Software Foundation, use the GNU General Public License for most of our software; it applies also to any other work released this way by its authors. You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for them if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs, and that you know you can do these things. To protect your rights, we need to prevent others from denying you these rights or asking you to surrender the rights. Therefore, you have certain responsibilities if you distribute copies of the software, or if you modify it: responsibilities to respect the freedom of others. For example, if you distribute copies of such a program, whether gratis or for a fee, you must pass on to the recipients the same freedoms that you received. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. Developers that use the GNU GPL protect your rights with two steps: (1) assert copyright on the software, and (2) offer you this License giving you legal permission to copy, distribute and/or modify it. For the developers' and authors' protection, the GPL clearly explains that there is no warranty for this free software. For both users' and authors' sake, the GPL requires that modified versions be marked as changed, so that their problems will not be attributed erroneously to authors of previous versions. Some devices are designed to deny users access to install or run modified versions of the software inside them, although the manufacturer can do so. This is fundamentally incompatible with the aim of protecting users' freedom to change the software. The systematic pattern of such abuse occurs in the area of products for individuals to use, which is precisely where it is most unacceptable. Therefore, we have designed this version of the GPL to prohibit the practice for those products. If such problems arise substantially in other domains, we stand ready to extend this provision to those domains in future versions of the GPL, as needed to protect the freedom of users. Finally, every program is threatened constantly by software patents. States should not allow patents to restrict development and use of software on general-purpose computers, but in those that do, we wish to avoid the special danger that patents applied to a free program could make it effectively proprietary. To prevent this, the GPL assures that patents cannot be used to render the program non-free. The precise terms and conditions for copying, distribution and modification follow. TERMS AND CONDITIONS 0. Definitions. "This License" refers to version 3 of the GNU General Public License. "Copyright" also means copyright-like laws that apply to other kinds of works, such as semiconductor masks. "The Program" refers to any copyrightable work licensed under this License. Each licensee is addressed as "you". "Licensees" and "recipients" may be individuals or organizations. To "modify" a work means to copy from or adapt all or part of the work in a fashion requiring copyright permission, other than the making of an exact copy. The resulting work is called a "modified version" of the earlier work or a work "based on" the earlier work. A "covered work" means either the unmodified Program or a work based on the Program. To "propagate" a work means to do anything with it that, without permission, would make you directly or secondarily liable for infringement under applicable copyright law, except executing it on a computer or modifying a private copy. Propagation includes copying, distribution (with or without modification), making available to the public, and in some countries other activities as well. To "convey" a work means any kind of propagation that enables other parties to make or receive copies. Mere interaction with a user through a computer network, with no transfer of a copy, is not conveying. An interactive user interface displays "Appropriate Legal Notices" to the extent that it includes a convenient and prominently visible feature that (1) displays an appropriate copyright notice, and (2) tells the user that there is no warranty for the work (except to the extent that warranties are provided), that licensees may convey the work under this License, and how to view a copy of this License. If the interface presents a list of user commands or options, such as a menu, a prominent item in the list meets this criterion. 1. Source Code. The "source code" for a work means the preferred form of the work for making modifications to it. "Object code" means any non-source form of a work. A "Standard Interface" means an interface that either is an official standard defined by a recognized standards body, or, in the case of interfaces specified for a particular programming language, one that is widely used among developers working in that language. The "System Libraries" of an executable work include anything, other than the work as a whole, that (a) is included in the normal form of packaging a Major Component, but which is not part of that Major Component, and (b) serves only to enable use of the work with that Major Component, or to implement a Standard Interface for which an implementation is available to the public in source code form. A "Major Component", in this context, means a major essential component (kernel, window system, and so on) of the specific operating system (if any) on which the executable work runs, or a compiler used to produce the work, or an object code interpreter used to run it. The "Corresponding Source" for a work in object code form means all the source code needed to generate, install, and (for an executable work) run the object code and to modify the work, including scripts to control those activities. However, it does not include the work's System Libraries, or general-purpose tools or generally available free programs which are used unmodified in performing those activities but which are not part of the work. For example, Corresponding Source includes interface definition files associated with source files for the work, and the source code for shared libraries and dynamically linked subprograms that the work is specifically designed to require, such as by intimate data communication or control flow between those subprograms and other parts of the work. The Corresponding Source need not include anything that users can regenerate automatically from other parts of the Corresponding Source. The Corresponding Source for a work in source code form is that same work. 2. Basic Permissions. All rights granted under this License are granted for the term of copyright on the Program, and are irrevocable provided the stated conditions are met. This License explicitly affirms your unlimited permission to run the unmodified Program. The output from running a covered work is covered by this License only if the output, given its content, constitutes a covered work. This License acknowledges your rights of fair use or other equivalent, as provided by copyright law. You may make, run and propagate covered works that you do not convey, without conditions so long as your license otherwise remains in force. You may convey covered works to others for the sole purpose of having them make modifications exclusively for you, or provide you with facilities for running those works, provided that you comply with the terms of this License in conveying all material for which you do not control copyright. Those thus making or running the covered works for you must do so exclusively on your behalf, under your direction and control, on terms that prohibit them from making any copies of your copyrighted material outside their relationship with you. Conveying under any other circumstances is permitted solely under the conditions stated below. Sublicensing is not allowed; section 10 makes it unnecessary. 3. Protecting Users' Legal Rights From Anti-Circumvention Law. No covered work shall be deemed part of an effective technological measure under any applicable law fulfilling obligations under article 11 of the WIPO copyright treaty adopted on 20 December 1996, or similar laws prohibiting or restricting circumvention of such measures. When you convey a covered work, you waive any legal power to forbid circumvention of technological measures to the extent such circumvention is effected by exercising rights under this License with respect to the covered work, and you disclaim any intention to limit operation or modification of the work as a means of enforcing, against the work's users, your or third parties' legal rights to forbid circumvention of technological measures. 4. Conveying Verbatim Copies. You may convey verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice; keep intact all notices stating that this License and any non-permissive terms added in accord with section 7 apply to the code; keep intact all notices of the absence of any warranty; and give all recipients a copy of this License along with the Program. You may charge any price or no price for each copy that you convey, and you may offer support or warranty protection for a fee. 5. Conveying Modified Source Versions. You may convey a work based on the Program, or the modifications to produce it from the Program, in the form of source code under the terms of section 4, provided that you also meet all of these conditions: a) The work must carry prominent notices stating that you modified it, and giving a relevant date. b) The work must carry prominent notices stating that it is released under this License and any conditions added under section 7. This requirement modifies the requirement in section 4 to "keep intact all notices". c) You must license the entire work, as a whole, under this License to anyone who comes into possession of a copy. This License will therefore apply, along with any applicable section 7 additional terms, to the whole of the work, and all its parts, regardless of how they are packaged. This License gives no permission to license the work in any other way, but it does not invalidate such permission if you have separately received it. d) If the work has interactive user interfaces, each must display Appropriate Legal Notices; however, if the Program has interactive interfaces that do not display Appropriate Legal Notices, your work need not make them do so. A compilation of a covered work with other separate and independent works, which are not by their nature extensions of the covered work, and which are not combined with it such as to form a larger program, in or on a volume of a storage or distribution medium, is called an "aggregate" if the compilation and its resulting copyright are not used to limit the access or legal rights of the compilation's users beyond what the individual works permit. Inclusion of a covered work in an aggregate does not cause this License to apply to the other parts of the aggregate. 6. Conveying Non-Source Forms. You may convey a covered work in object code form under the terms of sections 4 and 5, provided that you also convey the machine-readable Corresponding Source under the terms of this License, in one of these ways: a) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by the Corresponding Source fixed on a durable physical medium customarily used for software interchange. b) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by a written offer, valid for at least three years and valid for as long as you offer spare parts or customer support for that product model, to give anyone who possesses the object code either (1) a copy of the Corresponding Source for all the software in the product that is covered by this License, on a durable physical medium customarily used for software interchange, for a price no more than your reasonable cost of physically performing this conveying of source, or (2) access to copy the Corresponding Source from a network server at no charge. c) Convey individual copies of the object code with a copy of the written offer to provide the Corresponding Source. This alternative is allowed only occasionally and noncommercially, and only if you received the object code with such an offer, in accord with subsection 6b. d) Convey the object code by offering access from a designated place (gratis or for a charge), and offer equivalent access to the Corresponding Source in the same way through the same place at no further charge. You need not require recipients to copy the Corresponding Source along with the object code. If the place to copy the object code is a network server, the Corresponding Source may be on a different server (operated by you or a third party) that supports equivalent copying facilities, provided you maintain clear directions next to the object code saying where to find the Corresponding Source. Regardless of what server hosts the Corresponding Source, you remain obligated to ensure that it is available for as long as needed to satisfy these requirements. e) Convey the object code using peer-to-peer transmission, provided you inform other peers where the object code and Corresponding Source of the work are being offered to the general public at no charge under subsection 6d. A separable portion of the object code, whose source code is excluded from the Corresponding Source as a System Library, need not be included in conveying the object code work. A "User Product" is either (1) a "consumer product", which means any tangible personal property which is normally used for personal, family, or household purposes, or (2) anything designed or sold for incorporation into a dwelling. In determining whether a product is a consumer product, doubtful cases shall be resolved in favor of coverage. For a particular product received by a particular user, "normally used" refers to a typical or common use of that class of product, regardless of the status of the particular user or of the way in which the particular user actually uses, or expects or is expected to use, the product. A product is a consumer product regardless of whether the product has substantial commercial, industrial or non-consumer uses, unless such uses represent the only significant mode of use of the product. "Installation Information" for a User Product means any methods, procedures, authorization keys, or other information required to install and execute modified versions of a covered work in that User Product from a modified version of its Corresponding Source. The information must suffice to ensure that the continued functioning of the modified object code is in no case prevented or interfered with solely because modification has been made. If you convey an object code work under this section in, or with, or specifically for use in, a User Product, and the conveying occurs as part of a transaction in which the right of possession and use of the User Product is transferred to the recipient in perpetuity or for a fixed term (regardless of how the transaction is characterized), the Corresponding Source conveyed under this section must be accompanied by the Installation Information. But this requirement does not apply if neither you nor any third party retains the ability to install modified object code on the User Product (for example, the work has been installed in ROM). The requirement to provide Installation Information does not include a requirement to continue to provide support service, warranty, or updates for a work that has been modified or installed by the recipient, or for the User Product in which it has been modified or installed. Access to a network may be denied when the modification itself materially and adversely affects the operation of the network or violates the rules and protocols for communication across the network. Corresponding Source conveyed, and Installation Information provided, in accord with this section must be in a format that is publicly documented (and with an implementation available to the public in source code form), and must require no special password or key for unpacking, reading or copying. 7. Additional Terms. "Additional permissions" are terms that supplement the terms of this License by making exceptions from one or more of its conditions. Additional permissions that are applicable to the entire Program shall be treated as though they were included in this License, to the extent that they are valid under applicable law. If additional permissions apply only to part of the Program, that part may be used separately under those permissions, but the entire Program remains governed by this License without regard to the additional permissions. When you convey a copy of a covered work, you may at your option remove any additional permissions from that copy, or from any part of it. (Additional permissions may be written to require their own removal in certain cases when you modify the work.) You may place additional permissions on material, added by you to a covered work, for which you have or can give appropriate copyright permission. Notwithstanding any other provision of this License, for material you add to a covered work, you may (if authorized by the copyright holders of that material) supplement the terms of this License with terms: a) Disclaiming warranty or limiting liability differently from the terms of sections 15 and 16 of this License; or b) Requiring preservation of specified reasonable legal notices or author attributions in that material or in the Appropriate Legal Notices displayed by works containing it; or c) Prohibiting misrepresentation of the origin of that material, or requiring that modified versions of such material be marked in reasonable ways as different from the original version; or d) Limiting the use for publicity purposes of names of licensors or authors of the material; or e) Declining to grant rights under trademark law for use of some trade names, trademarks, or service marks; or f) Requiring indemnification of licensors and authors of that material by anyone who conveys the material (or modified versions of it) with contractual assumptions of liability to the recipient, for any liability that these contractual assumptions directly impose on those licensors and authors. All other non-permissive additional terms are considered "further restrictions" within the meaning of section 10. If the Program as you received it, or any part of it, contains a notice stating that it is governed by this License along with a term that is a further restriction, you may remove that term. If a license document contains a further restriction but permits relicensing or conveying under this License, you may add to a covered work material governed by the terms of that license document, provided that the further restriction does not survive such relicensing or conveying. If you add terms to a covered work in accord with this section, you must place, in the relevant source files, a statement of the additional terms that apply to those files, or a notice indicating where to find the applicable terms. Additional terms, permissive or non-permissive, may be stated in the form of a separately written license, or stated as exceptions; the above requirements apply either way. 8. Termination. You may not propagate or modify a covered work except as expressly provided under this License. Any attempt otherwise to propagate or modify it is void, and will automatically terminate your rights under this License (including any patent licenses granted under the third paragraph of section 11). However, if you cease all violation of this License, then your license from a particular copyright holder is reinstated (a) provisionally, unless and until the copyright holder explicitly and finally terminates your license, and (b) permanently, if the copyright holder fails to notify you of the violation by some reasonable means prior to 60 days after the cessation. Moreover, your license from a particular copyright holder is reinstated permanently if the copyright holder notifies you of the violation by some reasonable means, this is the first time you have received notice of violation of this License (for any work) from that copyright holder, and you cure the violation prior to 30 days after your receipt of the notice. Termination of your rights under this section does not terminate the licenses of parties who have received copies or rights from you under this License. If your rights have been terminated and not permanently reinstated, you do not qualify to receive new licenses for the same material under section 10. 9. Acceptance Not Required for Having Copies. You are not required to accept this License in order to receive or run a copy of the Program. Ancillary propagation of a covered work occurring solely as a consequence of using peer-to-peer transmission to receive a copy likewise does not require acceptance. However, nothing other than this License grants you permission to propagate or modify any covered work. These actions infringe copyright if you do not accept this License. Therefore, by modifying or propagating a covered work, you indicate your acceptance of this License to do so. 10. Automatic Licensing of Downstream Recipients. Each time you convey a covered work, the recipient automatically receives a license from the original licensors, to run, modify and propagate that work, subject to this License. You are not responsible for enforcing compliance by third parties with this License. An "entity transaction" is a transaction transferring control of an organization, or substantially all assets of one, or subdividing an organization, or merging organizations. If propagation of a covered work results from an entity transaction, each party to that transaction who receives a copy of the work also receives whatever licenses to the work the party's predecessor in interest had or could give under the previous paragraph, plus a right to possession of the Corresponding Source of the work from the predecessor in interest, if the predecessor has it or can get it with reasonable efforts. You may not impose any further restrictions on the exercise of the rights granted or affirmed under this License. For example, you may not impose a license fee, royalty, or other charge for exercise of rights granted under this License, and you may not initiate litigation (including a cross-claim or counterclaim in a lawsuit) alleging that any patent claim is infringed by making, using, selling, offering for sale, or importing the Program or any portion of it. 11. Patents. A "contributor" is a copyright holder who authorizes use under this License of the Program or a work on which the Program is based. The work thus licensed is called the contributor's "contributor version". A contributor's "essential patent claims" are all patent claims owned or controlled by the contributor, whether already acquired or hereafter acquired, that would be infringed by some manner, permitted by this License, of making, using, or selling its contributor version, but do not include claims that would be infringed only as a consequence of further modification of the contributor version. For purposes of this definition, "control" includes the right to grant patent sublicenses in a manner consistent with the requirements of this License. Each contributor grants you a non-exclusive, worldwide, royalty-free patent license under the contributor's essential patent claims, to make, use, sell, offer for sale, import and otherwise run, modify and propagate the contents of its contributor version. In the following three paragraphs, a "patent license" is any express agreement or commitment, however denominated, not to enforce a patent (such as an express permission to practice a patent or covenant not to sue for patent infringement). To "grant" such a patent license to a party means to make such an agreement or commitment not to enforce a patent against the party. If you convey a covered work, knowingly relying on a patent license, and the Corresponding Source of the work is not available for anyone to copy, free of charge and under the terms of this License, through a publicly available network server or other readily accessible means, then you must either (1) cause the Corresponding Source to be so available, or (2) arrange to deprive yourself of the benefit of the patent license for this particular work, or (3) arrange, in a manner consistent with the requirements of this License, to extend the patent license to downstream recipients. "Knowingly relying" means you have actual knowledge that, but for the patent license, your conveying the covered work in a country, or your recipient's use of the covered work in a country, would infringe one or more identifiable patents in that country that you have reason to believe are valid. If, pursuant to or in connection with a single transaction or arrangement, you convey, or propagate by procuring conveyance of, a covered work, and grant a patent license to some of the parties receiving the covered work authorizing them to use, propagate, modify or convey a specific copy of the covered work, then the patent license you grant is automatically extended to all recipients of the covered work and works based on it. A patent license is "discriminatory" if it does not include within the scope of its coverage, prohibits the exercise of, or is conditioned on the non-exercise of one or more of the rights that are specifically granted under this License. You may not convey a covered work if you are a party to an arrangement with a third party that is in the business of distributing software, under which you make payment to the third party based on the extent of your activity of conveying the work, and under which the third party grants, to any of the parties who would receive the covered work from you, a discriminatory patent license (a) in connection with copies of the covered work conveyed by you (or copies made from those copies), or (b) primarily for and in connection with specific products or compilations that contain the covered work, unless you entered into that arrangement, or that patent license was granted, prior to 28 March 2007. Nothing in this License shall be construed as excluding or limiting any implied license or other defenses to infringement that may otherwise be available to you under applicable patent law. 12. No Surrender of Others' Freedom. If conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot convey a covered work so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not convey it at all. For example, if you agree to terms that obligate you to collect a royalty for further conveying from those to whom you convey the Program, the only way you could satisfy both those terms and this License would be to refrain entirely from conveying the Program. 13. Use with the GNU Affero General Public License. Notwithstanding any other provision of this License, you have permission to link or combine any covered work with a work licensed under version 3 of the GNU Affero General Public License into a single combined work, and to convey the resulting work. The terms of this License will continue to apply to the part which is the covered work, but the special requirements of the GNU Affero General Public License, section 13, concerning interaction through a network will apply to the combination as such. 14. Revised Versions of this License. The Free Software Foundation may publish revised and/or new versions of the GNU General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies that a certain numbered version of the GNU General Public License "or any later version" applies to it, you have the option of following the terms and conditions either of that numbered version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of the GNU General Public License, you may choose any version ever published by the Free Software Foundation. If the Program specifies that a proxy can decide which future versions of the GNU General Public License can be used, that proxy's public statement of acceptance of a version permanently authorizes you to choose that version for the Program. Later license versions may give you additional or different permissions. However, no additional obligations are imposed on any author or copyright holder as a result of your choosing to follow a later version. 15. Disclaimer of Warranty. THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 16. Limitation of Liability. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. 17. Interpretation of Sections 15 and 16. If the disclaimer of warranty and limitation of liability provided above cannot be given local legal effect according to their terms, reviewing courts shall apply local law that most closely approximates an absolute waiver of all civil liability in connection with the Program, unless a warranty or assumption of liability accompanies a copy of the Program in return for a fee. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively state the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. <one line to give the program's name and a brief idea of what it does.> Copyright (C) <year> <name of author> This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see <http://www.gnu.org/licenses/>. Also add information on how to contact you by electronic and paper mail. If the program does terminal interaction, make it output a short notice like this when it starts in an interactive mode: <program> Copyright (C) <year> <name of author> This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, your program's commands might be different; for a GUI interface, you would use an "about box". You should also get your employer (if you work as a programmer) or school, if any, to sign a "copyright disclaimer" for the program, if necessary. For more information on this, and how to apply and follow the GNU GPL, see <http://www.gnu.org/licenses/>. The GNU General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Lesser General Public License instead of this License. But first, please read <http://www.gnu.org/philosophy/why-not-lgpl.html>. Andrew Gee <andrew@andrewgee.org> Hiroshi Miura <miurahr@linux.com> John Stowers <john.stowers@gmail.com> Many wonderful people at: http://translations.launchpad.net/gpxviewer True 2 True end False end 0 gpxviewer-0.4.3/.bzrignore0000664000175000017500000000000611752304676015073 0ustar andrewandrewbuild gpxviewer-0.4.3/.gitignore0000664000175000017500000000001511752304676015061 0ustar andrewandrew*.pyc build/ gpxviewer-0.4.3/AUTHORS0000664000175000017500000000031411752304676014143 0ustar andrewandrewAndrew Gee Hiroshi Miura John Stowers From pygtk_graph: John Dickinson Sven Festersen gpxviewer-0.4.3/CHANGELOG0000664000175000017500000000317111752304676014311 0ustar andrewandrew0.4.3: * More accurate calculations * Multiple file selection in open dialog * Fixing of cache 0.4.2: * Added MimeType to desktop file * Updated translations 0.4.1: * Ignore blank track segments * More accurate distance calculation 0.4.0: * Aggregate stats across multiple tracks * Improved multi-track sidebar with add/removal buttons * Ability to toggle sidebar * Auto centering * Update translations (New: Hungarian) * Make Spinner optional in application 0.3.1: * Quick fix for bin/gpxviewer. Hadn't updated for new multi-track 0.3.0: * Implementation of multitrack menu by John Stowers * Updated translations 0.2.1: * Translation update 0.2.0: * Change to osm-gps-map 0.7.1, requiring changes to trace loading * Migration to GtkBuilder from Glade * Open trace with external applications * Zoom slider in status bar, replaces buttons * Scale key displayed on map * Convert trace time to correct timezone * Caching for map tiles enabled * Fixes to command line arguments to handle invalid files, and show filename in window title. 0.1.5: * Patches contributed by Hiroshi Miura * Accept gpx file as cli argument * Fixed translation gettext support * Trace time shown on the interface 0.1.4: * Now shows multiple trk elements * Updated translations (New: cs, nl, zh_CN) 0.1.3: * Fixed < 0 'a' value in calculate_distance() in gpx.py 0.1.2: * Fixed blank descriptions and duplicate points (Thanks Stef Sijben) * Moved to more appropriate network category 0.1.1: * Removed Encoding key in desktop file, that is now depreciated. * Updated translations 0.1: * Initial Release of hopefully many to come :) gpxviewer-0.4.3/COPYING0000664000175000017500000010451311752304676014134 0ustar andrewandrew GNU GENERAL PUBLIC LICENSE Version 3, 29 June 2007 Copyright (C) 2007 Free Software Foundation, Inc. Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The GNU General Public License is a free, copyleft license for software and other kinds of works. The licenses for most software and other practical works are designed to take away your freedom to share and change the works. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change all versions of a program--to make sure it remains free software for all its users. We, the Free Software Foundation, use the GNU General Public License for most of our software; it applies also to any other work released this way by its authors. You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for them if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs, and that you know you can do these things. To protect your rights, we need to prevent others from denying you these rights or asking you to surrender the rights. Therefore, you have certain responsibilities if you distribute copies of the software, or if you modify it: responsibilities to respect the freedom of others. For example, if you distribute copies of such a program, whether gratis or for a fee, you must pass on to the recipients the same freedoms that you received. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. Developers that use the GNU GPL protect your rights with two steps: (1) assert copyright on the software, and (2) offer you this License giving you legal permission to copy, distribute and/or modify it. For the developers' and authors' protection, the GPL clearly explains that there is no warranty for this free software. For both users' and authors' sake, the GPL requires that modified versions be marked as changed, so that their problems will not be attributed erroneously to authors of previous versions. Some devices are designed to deny users access to install or run modified versions of the software inside them, although the manufacturer can do so. This is fundamentally incompatible with the aim of protecting users' freedom to change the software. The systematic pattern of such abuse occurs in the area of products for individuals to use, which is precisely where it is most unacceptable. Therefore, we have designed this version of the GPL to prohibit the practice for those products. If such problems arise substantially in other domains, we stand ready to extend this provision to those domains in future versions of the GPL, as needed to protect the freedom of users. Finally, every program is threatened constantly by software patents. States should not allow patents to restrict development and use of software on general-purpose computers, but in those that do, we wish to avoid the special danger that patents applied to a free program could make it effectively proprietary. To prevent this, the GPL assures that patents cannot be used to render the program non-free. The precise terms and conditions for copying, distribution and modification follow. TERMS AND CONDITIONS 0. Definitions. "This License" refers to version 3 of the GNU General Public License. "Copyright" also means copyright-like laws that apply to other kinds of works, such as semiconductor masks. "The Program" refers to any copyrightable work licensed under this License. Each licensee is addressed as "you". "Licensees" and "recipients" may be individuals or organizations. To "modify" a work means to copy from or adapt all or part of the work in a fashion requiring copyright permission, other than the making of an exact copy. The resulting work is called a "modified version" of the earlier work or a work "based on" the earlier work. A "covered work" means either the unmodified Program or a work based on the Program. To "propagate" a work means to do anything with it that, without permission, would make you directly or secondarily liable for infringement under applicable copyright law, except executing it on a computer or modifying a private copy. Propagation includes copying, distribution (with or without modification), making available to the public, and in some countries other activities as well. To "convey" a work means any kind of propagation that enables other parties to make or receive copies. Mere interaction with a user through a computer network, with no transfer of a copy, is not conveying. An interactive user interface displays "Appropriate Legal Notices" to the extent that it includes a convenient and prominently visible feature that (1) displays an appropriate copyright notice, and (2) tells the user that there is no warranty for the work (except to the extent that warranties are provided), that licensees may convey the work under this License, and how to view a copy of this License. If the interface presents a list of user commands or options, such as a menu, a prominent item in the list meets this criterion. 1. Source Code. The "source code" for a work means the preferred form of the work for making modifications to it. "Object code" means any non-source form of a work. A "Standard Interface" means an interface that either is an official standard defined by a recognized standards body, or, in the case of interfaces specified for a particular programming language, one that is widely used among developers working in that language. The "System Libraries" of an executable work include anything, other than the work as a whole, that (a) is included in the normal form of packaging a Major Component, but which is not part of that Major Component, and (b) serves only to enable use of the work with that Major Component, or to implement a Standard Interface for which an implementation is available to the public in source code form. A "Major Component", in this context, means a major essential component (kernel, window system, and so on) of the specific operating system (if any) on which the executable work runs, or a compiler used to produce the work, or an object code interpreter used to run it. The "Corresponding Source" for a work in object code form means all the source code needed to generate, install, and (for an executable work) run the object code and to modify the work, including scripts to control those activities. However, it does not include the work's System Libraries, or general-purpose tools or generally available free programs which are used unmodified in performing those activities but which are not part of the work. For example, Corresponding Source includes interface definition files associated with source files for the work, and the source code for shared libraries and dynamically linked subprograms that the work is specifically designed to require, such as by intimate data communication or control flow between those subprograms and other parts of the work. The Corresponding Source need not include anything that users can regenerate automatically from other parts of the Corresponding Source. The Corresponding Source for a work in source code form is that same work. 2. Basic Permissions. All rights granted under this License are granted for the term of copyright on the Program, and are irrevocable provided the stated conditions are met. This License explicitly affirms your unlimited permission to run the unmodified Program. The output from running a covered work is covered by this License only if the output, given its content, constitutes a covered work. This License acknowledges your rights of fair use or other equivalent, as provided by copyright law. You may make, run and propagate covered works that you do not convey, without conditions so long as your license otherwise remains in force. You may convey covered works to others for the sole purpose of having them make modifications exclusively for you, or provide you with facilities for running those works, provided that you comply with the terms of this License in conveying all material for which you do not control copyright. Those thus making or running the covered works for you must do so exclusively on your behalf, under your direction and control, on terms that prohibit them from making any copies of your copyrighted material outside their relationship with you. Conveying under any other circumstances is permitted solely under the conditions stated below. Sublicensing is not allowed; section 10 makes it unnecessary. 3. Protecting Users' Legal Rights From Anti-Circumvention Law. No covered work shall be deemed part of an effective technological measure under any applicable law fulfilling obligations under article 11 of the WIPO copyright treaty adopted on 20 December 1996, or similar laws prohibiting or restricting circumvention of such measures. When you convey a covered work, you waive any legal power to forbid circumvention of technological measures to the extent such circumvention is effected by exercising rights under this License with respect to the covered work, and you disclaim any intention to limit operation or modification of the work as a means of enforcing, against the work's users, your or third parties' legal rights to forbid circumvention of technological measures. 4. Conveying Verbatim Copies. You may convey verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice; keep intact all notices stating that this License and any non-permissive terms added in accord with section 7 apply to the code; keep intact all notices of the absence of any warranty; and give all recipients a copy of this License along with the Program. You may charge any price or no price for each copy that you convey, and you may offer support or warranty protection for a fee. 5. Conveying Modified Source Versions. You may convey a work based on the Program, or the modifications to produce it from the Program, in the form of source code under the terms of section 4, provided that you also meet all of these conditions: a) The work must carry prominent notices stating that you modified it, and giving a relevant date. b) The work must carry prominent notices stating that it is released under this License and any conditions added under section 7. This requirement modifies the requirement in section 4 to "keep intact all notices". c) You must license the entire work, as a whole, under this License to anyone who comes into possession of a copy. This License will therefore apply, along with any applicable section 7 additional terms, to the whole of the work, and all its parts, regardless of how they are packaged. This License gives no permission to license the work in any other way, but it does not invalidate such permission if you have separately received it. d) If the work has interactive user interfaces, each must display Appropriate Legal Notices; however, if the Program has interactive interfaces that do not display Appropriate Legal Notices, your work need not make them do so. A compilation of a covered work with other separate and independent works, which are not by their nature extensions of the covered work, and which are not combined with it such as to form a larger program, in or on a volume of a storage or distribution medium, is called an "aggregate" if the compilation and its resulting copyright are not used to limit the access or legal rights of the compilation's users beyond what the individual works permit. Inclusion of a covered work in an aggregate does not cause this License to apply to the other parts of the aggregate. 6. Conveying Non-Source Forms. You may convey a covered work in object code form under the terms of sections 4 and 5, provided that you also convey the machine-readable Corresponding Source under the terms of this License, in one of these ways: a) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by the Corresponding Source fixed on a durable physical medium customarily used for software interchange. b) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by a written offer, valid for at least three years and valid for as long as you offer spare parts or customer support for that product model, to give anyone who possesses the object code either (1) a copy of the Corresponding Source for all the software in the product that is covered by this License, on a durable physical medium customarily used for software interchange, for a price no more than your reasonable cost of physically performing this conveying of source, or (2) access to copy the Corresponding Source from a network server at no charge. c) Convey individual copies of the object code with a copy of the written offer to provide the Corresponding Source. This alternative is allowed only occasionally and noncommercially, and only if you received the object code with such an offer, in accord with subsection 6b. d) Convey the object code by offering access from a designated place (gratis or for a charge), and offer equivalent access to the Corresponding Source in the same way through the same place at no further charge. You need not require recipients to copy the Corresponding Source along with the object code. If the place to copy the object code is a network server, the Corresponding Source may be on a different server (operated by you or a third party) that supports equivalent copying facilities, provided you maintain clear directions next to the object code saying where to find the Corresponding Source. Regardless of what server hosts the Corresponding Source, you remain obligated to ensure that it is available for as long as needed to satisfy these requirements. e) Convey the object code using peer-to-peer transmission, provided you inform other peers where the object code and Corresponding Source of the work are being offered to the general public at no charge under subsection 6d. A separable portion of the object code, whose source code is excluded from the Corresponding Source as a System Library, need not be included in conveying the object code work. A "User Product" is either (1) a "consumer product", which means any tangible personal property which is normally used for personal, family, or household purposes, or (2) anything designed or sold for incorporation into a dwelling. In determining whether a product is a consumer product, doubtful cases shall be resolved in favor of coverage. For a particular product received by a particular user, "normally used" refers to a typical or common use of that class of product, regardless of the status of the particular user or of the way in which the particular user actually uses, or expects or is expected to use, the product. A product is a consumer product regardless of whether the product has substantial commercial, industrial or non-consumer uses, unless such uses represent the only significant mode of use of the product. "Installation Information" for a User Product means any methods, procedures, authorization keys, or other information required to install and execute modified versions of a covered work in that User Product from a modified version of its Corresponding Source. The information must suffice to ensure that the continued functioning of the modified object code is in no case prevented or interfered with solely because modification has been made. If you convey an object code work under this section in, or with, or specifically for use in, a User Product, and the conveying occurs as part of a transaction in which the right of possession and use of the User Product is transferred to the recipient in perpetuity or for a fixed term (regardless of how the transaction is characterized), the Corresponding Source conveyed under this section must be accompanied by the Installation Information. But this requirement does not apply if neither you nor any third party retains the ability to install modified object code on the User Product (for example, the work has been installed in ROM). The requirement to provide Installation Information does not include a requirement to continue to provide support service, warranty, or updates for a work that has been modified or installed by the recipient, or for the User Product in which it has been modified or installed. Access to a network may be denied when the modification itself materially and adversely affects the operation of the network or violates the rules and protocols for communication across the network. Corresponding Source conveyed, and Installation Information provided, in accord with this section must be in a format that is publicly documented (and with an implementation available to the public in source code form), and must require no special password or key for unpacking, reading or copying. 7. Additional Terms. "Additional permissions" are terms that supplement the terms of this License by making exceptions from one or more of its conditions. Additional permissions that are applicable to the entire Program shall be treated as though they were included in this License, to the extent that they are valid under applicable law. If additional permissions apply only to part of the Program, that part may be used separately under those permissions, but the entire Program remains governed by this License without regard to the additional permissions. When you convey a copy of a covered work, you may at your option remove any additional permissions from that copy, or from any part of it. (Additional permissions may be written to require their own removal in certain cases when you modify the work.) You may place additional permissions on material, added by you to a covered work, for which you have or can give appropriate copyright permission. Notwithstanding any other provision of this License, for material you add to a covered work, you may (if authorized by the copyright holders of that material) supplement the terms of this License with terms: a) Disclaiming warranty or limiting liability differently from the terms of sections 15 and 16 of this License; or b) Requiring preservation of specified reasonable legal notices or author attributions in that material or in the Appropriate Legal Notices displayed by works containing it; or c) Prohibiting misrepresentation of the origin of that material, or requiring that modified versions of such material be marked in reasonable ways as different from the original version; or d) Limiting the use for publicity purposes of names of licensors or authors of the material; or e) Declining to grant rights under trademark law for use of some trade names, trademarks, or service marks; or f) Requiring indemnification of licensors and authors of that material by anyone who conveys the material (or modified versions of it) with contractual assumptions of liability to the recipient, for any liability that these contractual assumptions directly impose on those licensors and authors. All other non-permissive additional terms are considered "further restrictions" within the meaning of section 10. If the Program as you received it, or any part of it, contains a notice stating that it is governed by this License along with a term that is a further restriction, you may remove that term. If a license document contains a further restriction but permits relicensing or conveying under this License, you may add to a covered work material governed by the terms of that license document, provided that the further restriction does not survive such relicensing or conveying. If you add terms to a covered work in accord with this section, you must place, in the relevant source files, a statement of the additional terms that apply to those files, or a notice indicating where to find the applicable terms. Additional terms, permissive or non-permissive, may be stated in the form of a separately written license, or stated as exceptions; the above requirements apply either way. 8. Termination. You may not propagate or modify a covered work except as expressly provided under this License. Any attempt otherwise to propagate or modify it is void, and will automatically terminate your rights under this License (including any patent licenses granted under the third paragraph of section 11). However, if you cease all violation of this License, then your license from a particular copyright holder is reinstated (a) provisionally, unless and until the copyright holder explicitly and finally terminates your license, and (b) permanently, if the copyright holder fails to notify you of the violation by some reasonable means prior to 60 days after the cessation. Moreover, your license from a particular copyright holder is reinstated permanently if the copyright holder notifies you of the violation by some reasonable means, this is the first time you have received notice of violation of this License (for any work) from that copyright holder, and you cure the violation prior to 30 days after your receipt of the notice. Termination of your rights under this section does not terminate the licenses of parties who have received copies or rights from you under this License. If your rights have been terminated and not permanently reinstated, you do not qualify to receive new licenses for the same material under section 10. 9. Acceptance Not Required for Having Copies. You are not required to accept this License in order to receive or run a copy of the Program. Ancillary propagation of a covered work occurring solely as a consequence of using peer-to-peer transmission to receive a copy likewise does not require acceptance. However, nothing other than this License grants you permission to propagate or modify any covered work. These actions infringe copyright if you do not accept this License. Therefore, by modifying or propagating a covered work, you indicate your acceptance of this License to do so. 10. Automatic Licensing of Downstream Recipients. Each time you convey a covered work, the recipient automatically receives a license from the original licensors, to run, modify and propagate that work, subject to this License. You are not responsible for enforcing compliance by third parties with this License. An "entity transaction" is a transaction transferring control of an organization, or substantially all assets of one, or subdividing an organization, or merging organizations. If propagation of a covered work results from an entity transaction, each party to that transaction who receives a copy of the work also receives whatever licenses to the work the party's predecessor in interest had or could give under the previous paragraph, plus a right to possession of the Corresponding Source of the work from the predecessor in interest, if the predecessor has it or can get it with reasonable efforts. You may not impose any further restrictions on the exercise of the rights granted or affirmed under this License. For example, you may not impose a license fee, royalty, or other charge for exercise of rights granted under this License, and you may not initiate litigation (including a cross-claim or counterclaim in a lawsuit) alleging that any patent claim is infringed by making, using, selling, offering for sale, or importing the Program or any portion of it. 11. Patents. A "contributor" is a copyright holder who authorizes use under this License of the Program or a work on which the Program is based. The work thus licensed is called the contributor's "contributor version". A contributor's "essential patent claims" are all patent claims owned or controlled by the contributor, whether already acquired or hereafter acquired, that would be infringed by some manner, permitted by this License, of making, using, or selling its contributor version, but do not include claims that would be infringed only as a consequence of further modification of the contributor version. For purposes of this definition, "control" includes the right to grant patent sublicenses in a manner consistent with the requirements of this License. Each contributor grants you a non-exclusive, worldwide, royalty-free patent license under the contributor's essential patent claims, to make, use, sell, offer for sale, import and otherwise run, modify and propagate the contents of its contributor version. In the following three paragraphs, a "patent license" is any express agreement or commitment, however denominated, not to enforce a patent (such as an express permission to practice a patent or covenant not to sue for patent infringement). To "grant" such a patent license to a party means to make such an agreement or commitment not to enforce a patent against the party. If you convey a covered work, knowingly relying on a patent license, and the Corresponding Source of the work is not available for anyone to copy, free of charge and under the terms of this License, through a publicly available network server or other readily accessible means, then you must either (1) cause the Corresponding Source to be so available, or (2) arrange to deprive yourself of the benefit of the patent license for this particular work, or (3) arrange, in a manner consistent with the requirements of this License, to extend the patent license to downstream recipients. "Knowingly relying" means you have actual knowledge that, but for the patent license, your conveying the covered work in a country, or your recipient's use of the covered work in a country, would infringe one or more identifiable patents in that country that you have reason to believe are valid. If, pursuant to or in connection with a single transaction or arrangement, you convey, or propagate by procuring conveyance of, a covered work, and grant a patent license to some of the parties receiving the covered work authorizing them to use, propagate, modify or convey a specific copy of the covered work, then the patent license you grant is automatically extended to all recipients of the covered work and works based on it. A patent license is "discriminatory" if it does not include within the scope of its coverage, prohibits the exercise of, or is conditioned on the non-exercise of one or more of the rights that are specifically granted under this License. You may not convey a covered work if you are a party to an arrangement with a third party that is in the business of distributing software, under which you make payment to the third party based on the extent of your activity of conveying the work, and under which the third party grants, to any of the parties who would receive the covered work from you, a discriminatory patent license (a) in connection with copies of the covered work conveyed by you (or copies made from those copies), or (b) primarily for and in connection with specific products or compilations that contain the covered work, unless you entered into that arrangement, or that patent license was granted, prior to 28 March 2007. Nothing in this License shall be construed as excluding or limiting any implied license or other defenses to infringement that may otherwise be available to you under applicable patent law. 12. No Surrender of Others' Freedom. If conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot convey a covered work so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not convey it at all. For example, if you agree to terms that obligate you to collect a royalty for further conveying from those to whom you convey the Program, the only way you could satisfy both those terms and this License would be to refrain entirely from conveying the Program. 13. Use with the GNU Affero General Public License. Notwithstanding any other provision of this License, you have permission to link or combine any covered work with a work licensed under version 3 of the GNU Affero General Public License into a single combined work, and to convey the resulting work. The terms of this License will continue to apply to the part which is the covered work, but the special requirements of the GNU Affero General Public License, section 13, concerning interaction through a network will apply to the combination as such. 14. Revised Versions of this License. The Free Software Foundation may publish revised and/or new versions of the GNU General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies that a certain numbered version of the GNU General Public License "or any later version" applies to it, you have the option of following the terms and conditions either of that numbered version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of the GNU General Public License, you may choose any version ever published by the Free Software Foundation. If the Program specifies that a proxy can decide which future versions of the GNU General Public License can be used, that proxy's public statement of acceptance of a version permanently authorizes you to choose that version for the Program. Later license versions may give you additional or different permissions. However, no additional obligations are imposed on any author or copyright holder as a result of your choosing to follow a later version. 15. Disclaimer of Warranty. THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 16. Limitation of Liability. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. 17. Interpretation of Sections 15 and 16. If the disclaimer of warranty and limitation of liability provided above cannot be given local legal effect according to their terms, reviewing courts shall apply local law that most closely approximates an absolute waiver of all civil liability in connection with the Program, unless a warranty or assumption of liability accompanies a copy of the Program in return for a fee. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively state the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . Also add information on how to contact you by electronic and paper mail. If the program does terminal interaction, make it output a short notice like this when it starts in an interactive mode: Copyright (C) This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, your program's commands might be different; for a GUI interface, you would use an "about box". You should also get your employer (if you work as a programmer) or school, if any, to sign a "copyright disclaimer" for the program, if necessary. For more information on this, and how to apply and follow the GNU GPL, see . The GNU General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Lesser General Public License instead of this License. But first, please read . gpxviewer-0.4.3/INSTALL0000664000175000017500000000016511752304676014130 0ustar andrewandrewSimply, to install, just use the setup.py script... ./setup.py install from within the right directory, of course! gpxviewer-0.4.3/README0000664000175000017500000000045511752304676013761 0ustar andrewandrew------------ GPX Viewer ------------ GPX Viewer is a simple app to look through your GPX trace files that you have from your journeys. This application processes these GPX files and then outputs useful information like Average and Maximum Speeds. You are also able to see the route on a map overlay. gpxviewer-0.4.3/gpxviewer.desktop.in0000664000175000017500000000022611752304676017115 0ustar andrewandrew[Desktop Entry] _Name=GPX Viewer Type=Application _Comment=GPS Trace Viewer Exec=gpxviewer Icon=gpxviewer Categories=Network MimeType=application/xml gpxviewer-0.4.3/main.py0000775000175000017500000000237111752304676014401 0ustar andrewandrew#!/usr/bin/env python # # main.py - Launcher for GPX Viewer # # Copyright (C) 2009 Andrew Gee # # GPX Viewer 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. # # GPX Viewer is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. # See the GNU General Public License for more details. # # You should have received a copy of the GNU General Public License along # with this program. If not, see . # # If you're having any problems, don't hesitate to contact: andrew@andrewgee.org # import sys, os.path parent_dir = os.path.dirname(os.path.abspath(__file__)) source_dir = os.path.join(parent_dir, "gpxviewer") sys.path.append(source_dir) import gtk.gdk from gpxviewer.ui import MainWindow gtk.gdk.threads_init() if len(sys.argv) > 2: files = sys.argv[1:] elif len(sys.argv) > 1: files = [sys.argv[1]] else: files = [] ui_dir = os.path.join(parent_dir, "ui/") gui = MainWindow( ui_dir=ui_dir, files=files ).main() gpxviewer-0.4.3/setup.cfg0000664000175000017500000000022011752304676014710 0ustar andrewandrew[install] prefix=/usr [build] i18n = True [build_i18n] domain = gpxviewer desktop_files = [('share/applications', ('gpxviewer.desktop.in',))] gpxviewer-0.4.3/setup.py0000775000175000017500000000204711752304676014615 0ustar andrewandrew#!/usr/bin/env python from distutils.core import setup from DistUtilsExtra.command import * setup(name="gpxviewer", version="0.4.3", author="Andrew Gee", author_email="andrew@andrewgee.org", maintainer="Andrew Gee", maintainer_email="andrew@andrewgee.org", description="GPS Trace Viewer and Analyser", long_description="GPX Viewer is a simple way to review those GPS traces you've made whilst on the move. Find out interesting stats and see a map overlay of the journey.", url="http://andrewgee.org/blog/gpxviewer", license="GNU General Public License (GPL)", platforms="linux", packages=["gpxviewer","gpxviewer.utils","gpxviewer.utils.iso8601","gpxviewer.utils.timezone","gpxviewer.pygtk_chart"], data_files=[ ('share/gpxviewer/ui/', ['ui/gpxviewer.ui','ui/gpxviewer.png','ui/gpxviewer.svg','gpxviewer/pygtk_chart/data/tango.color']), ('share/pixmaps', ['ui/gpxviewer.svg']) ], scripts = ['bin/gpxviewer'], cmdclass = { "build" : build_extra.build_extra, "build_i18n" : build_i18n.build_i18n, } ) gpxviewer-0.4.3/gpxviewer/pygtk_chart/data/0000775000175000017500000000000011752304676020345 5ustar andrewandrewgpxviewer-0.4.3/gpxviewer/utils/iso8601/0000775000175000017500000000000011752304676017366 5ustar andrewandrewgpxviewer-0.4.3/gpxviewer/utils/timezone/0000775000175000017500000000000011752304676020107 5ustar andrewandrewgpxviewer-0.4.3/gpxviewer/pygtk_chart/0000775000175000017500000000000011752304676017434 5ustar andrewandrewgpxviewer-0.4.3/gpxviewer/utils/0000775000175000017500000000000011752304676016255 5ustar andrewandrewgpxviewer-0.4.3/bin/0000775000175000017500000000000011752304676013645 5ustar andrewandrewgpxviewer-0.4.3/gpxviewer/0000775000175000017500000000000011752304676015115 5ustar andrewandrewgpxviewer-0.4.3/po/0000775000175000017500000000000011752304676013513 5ustar andrewandrewgpxviewer-0.4.3/ui/0000775000175000017500000000000011752304676013512 5ustar andrewandrewgpxviewer-0.4.3/0000775000175000017500000000000011752310370013061 5ustar andrewandrew