From 2536eb7c973a15ef3b0ed6753641e77c1d6bb087 Mon Sep 17 00:00:00 2001 From: yumoqing Date: Tue, 28 Dec 2021 16:33:39 +0800 Subject: [PATCH] bugfix --- __pycache__/version.cpython-39.pyc | Bin 134 -> 0 bytes build/lib/kivycharts/__init__.py | 0 build/lib/kivycharts/bar.py | 85 -- build/lib/kivycharts/chart.py | 79 -- build/lib/kivycharts/geometry.py | 81 -- build/lib/kivycharts/graph/__init__.py | 6 - build/lib/kivycharts/graph/_version.py | 1 - build/lib/kivycharts/graph/arc.py | 13 - build/lib/kivycharts/graph/base.py | 988 --------------------- build/lib/kivycharts/graph/beziercurve.py | 14 - build/lib/kivycharts/graph/circle.py | 10 - build/lib/kivycharts/graph/compoundpath.py | 7 - build/lib/kivycharts/graph/displayable.py | 5 - build/lib/kivycharts/graph/droplet.py | 11 - build/lib/kivycharts/graph/ellipse.py | 11 - build/lib/kivycharts/graph/heart.py | 11 - build/lib/kivycharts/graph/image.py | 12 - build/lib/kivycharts/graph/line.py | 12 - build/lib/kivycharts/graph/lsogon.py | 10 - build/lib/kivycharts/graph/path.py | 14 - build/lib/kivycharts/graph/plot.py | 795 ----------------- build/lib/kivycharts/graph/polygon.py | 10 - build/lib/kivycharts/graph/polyline.py | 10 - build/lib/kivycharts/graph/rectangle.py | 12 - build/lib/kivycharts/graph/rose.py | 12 - build/lib/kivycharts/graph/sector.py | 14 - build/lib/kivycharts/graph/star.py | 13 - build/lib/kivycharts/graph/text.py | 9 - build/lib/kivycharts/graph/touchoid.py | 12 - build/lib/kivycharts/line.py | 130 --- build/lib/kivycharts/pie.py | 66 -- dist/kivycharts-0.0.5-py3.9.egg | Bin 71528 -> 0 bytes kivycharts.egg-info/PKG-INFO | 14 - kivycharts.egg-info/SOURCES.txt | 37 - kivycharts.egg-info/dependency_links.txt | 1 - kivycharts.egg-info/requires.txt | 10 - kivycharts.egg-info/top_level.txt | 1 - 37 files changed, 2516 deletions(-) delete mode 100644 __pycache__/version.cpython-39.pyc delete mode 100644 build/lib/kivycharts/__init__.py delete mode 100644 build/lib/kivycharts/bar.py delete mode 100644 build/lib/kivycharts/chart.py delete mode 100644 build/lib/kivycharts/geometry.py delete mode 100644 build/lib/kivycharts/graph/__init__.py delete mode 100644 build/lib/kivycharts/graph/_version.py delete mode 100644 build/lib/kivycharts/graph/arc.py delete mode 100644 build/lib/kivycharts/graph/base.py delete mode 100644 build/lib/kivycharts/graph/beziercurve.py delete mode 100644 build/lib/kivycharts/graph/circle.py delete mode 100644 build/lib/kivycharts/graph/compoundpath.py delete mode 100644 build/lib/kivycharts/graph/displayable.py delete mode 100644 build/lib/kivycharts/graph/droplet.py delete mode 100644 build/lib/kivycharts/graph/ellipse.py delete mode 100644 build/lib/kivycharts/graph/heart.py delete mode 100644 build/lib/kivycharts/graph/image.py delete mode 100644 build/lib/kivycharts/graph/line.py delete mode 100644 build/lib/kivycharts/graph/lsogon.py delete mode 100644 build/lib/kivycharts/graph/path.py delete mode 100644 build/lib/kivycharts/graph/plot.py delete mode 100644 build/lib/kivycharts/graph/polygon.py delete mode 100644 build/lib/kivycharts/graph/polyline.py delete mode 100644 build/lib/kivycharts/graph/rectangle.py delete mode 100644 build/lib/kivycharts/graph/rose.py delete mode 100644 build/lib/kivycharts/graph/sector.py delete mode 100644 build/lib/kivycharts/graph/star.py delete mode 100644 build/lib/kivycharts/graph/text.py delete mode 100644 build/lib/kivycharts/graph/touchoid.py delete mode 100644 build/lib/kivycharts/line.py delete mode 100644 build/lib/kivycharts/pie.py delete mode 100644 dist/kivycharts-0.0.5-py3.9.egg delete mode 100644 kivycharts.egg-info/PKG-INFO delete mode 100644 kivycharts.egg-info/SOURCES.txt delete mode 100644 kivycharts.egg-info/dependency_links.txt delete mode 100644 kivycharts.egg-info/requires.txt delete mode 100644 kivycharts.egg-info/top_level.txt diff --git a/__pycache__/version.cpython-39.pyc b/__pycache__/version.cpython-39.pyc deleted file mode 100644 index 0d7f8e6cd4e163f365f1d605bceb918507226da3..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 134 zcmYe~<>g`kf>JlJL}4KP7{oyaj6jY95EpX*i4=w?h7`tN22G|aRs%f)JySnT##`)V zsYS(^`FSfDikN_Ez{D>p7ps_p%9!lTvdZL)#G;ZIs3N_B%3B;Zx%nxjIjMFavp)kd F0|2}?9Krwq diff --git a/build/lib/kivycharts/__init__.py b/build/lib/kivycharts/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/build/lib/kivycharts/bar.py b/build/lib/kivycharts/bar.py deleted file mode 100644 index 01f1b33..0000000 --- a/build/lib/kivycharts/bar.py +++ /dev/null @@ -1,85 +0,0 @@ -from kivy.uix.widget import Widget -from kivy.graphics import Color, Rectangle -from kivyblocks.colorcalc import * -from kivyblocks.charts.chart import Chart,ChartPart - -class BarPart(ChartPart): - def __init__(self,widget,pos,size,color,data_offset): - self.widget = widget - self.pos = pos - self.size = size - self.color = color - self.data_offset = data_offset - - def collide_point(self,x,y): - minx = self.widget.pos[0] + self.pos[0] - maxx = minx + self.size[0] - miny = self.widget.pos[1] + self.pos[1] - maxy = miny + self.size[1] - if minx <= x and x <= maxx and \ - miny <= y and y <= maxy: - return True - return False - - def draw(self): - with self.widget.canvas: - Color(*self.color) - Rectangle(pos=(self.widget.pos[0] + self.pos[0], - self.widget.pos[1] + self.pos[1]), - size = self.size) - - def mark(self): - rcolor = reverseColor(self.color) - rcolor.append(0.8) - with self.widget.canvas.after: - Color(*reverseColor(self.color)) - Rectangle(pos=(self.widget.pos[0] + self.pos[0], - self.widget.pos[1] + self.pos[1]), - size = self.size) - -class Bar(Chart): - """ - a BAR class - """ - def __init__(self,**options): - """ - options={ - width, - height, - title, - keyField, - valueField, - color1: - color2: - data=[ - { - name, - value, - },{ - } - ] - } - """ - self.data = None - super().__init__(**options) - - def data2part(self): - data = self.data - kvs = [ [i[self.options['keyField']],i[self.options['valueField']]] for i in data ] - m = max([i[1] for i in kvs ]) - cnt = len(kvs) - points = divide([0,0],[self.width,0],cnt) - color1='8833ee' - color2='ed8234' - colors = divideColor(color1,color2,cnt-1) - for i in range(cnt): - h = self.height * kvs[i][1] / m - c = colors[i] - part = BarPart(self, - points[i], - (points[i+1][0] - points[i][0],h), - colors[i], - i - ) - self.chartparts.append(part) - diff --git a/build/lib/kivycharts/chart.py b/build/lib/kivycharts/chart.py deleted file mode 100644 index f7aef87..0000000 --- a/build/lib/kivycharts/chart.py +++ /dev/null @@ -1,79 +0,0 @@ -from kivy.app import App -from kivy.graphics import Color, Rectangle -from kivy.uix.widget import Widget -from kivyblocks.colorcalc import toArrayColor - -class ChartPart(object): - def __init__(self,pos,width,height): - self.pos = pos - self.width = width - self.height = height - - def collide_point(self,x,y): - raise NotImplementedError - - def draw(self): - raise NotImplementedError - - def mark(self): - raise NotImplementedError - -class Chart(Widget): - def __init__(self,**options): - super().__init__() - self.options = options - self.bg_color = toArrayColor(self.options.get('bg_color',[0.3,0.3,0.3,1])) - self.markpart = None - self.bind(size=self.onSize,pos=self.onSize) - self.register_event_type("on_press") - - def unmark(self): - self.canvas.after.clear() - - def getData(self): - url = self.options.get('url') - if url: - hc = App.get_running_app().hc - params = self.options.get('params',{}) - d = hc.get(url,parms=param) - self.data = d.get('data',[]) - else: - self.data = self.options.get('data',[]) - self.chartparts = [] - - def on_touch_down(self,touch): - if touch.is_mouse_scrolling: - return False - if not self.collide_point(touch.x,touch.y): - return False - self.markpart = None - self.unmark() - for part in self.chartparts: - if part.collide_point(touch.x,touch.y): - self.markPart(part) - self.dispatch('on_press',self,self.data[part.data_offset]) - return super(Chart, self).on_touch_down(touch) - - def on_press(self,o,data): - print('data=',data) - - def draw(self): - with self.canvas.before: - Color(*self.bg_color) - Rectangle(pos=self.pos,size=self.size) - - self.canvas.clear() - for part in self.chartparts: - part.draw() - - def markPart(self,part): - self.markpart = part - part.mark() - - def onSize(self,o,v): - self.build() - - def build(self): - self.getData() - self.data2part() - self.draw() diff --git a/build/lib/kivycharts/geometry.py b/build/lib/kivycharts/geometry.py deleted file mode 100644 index 166d640..0000000 --- a/build/lib/kivycharts/geometry.py +++ /dev/null @@ -1,81 +0,0 @@ -import math -import numpy as np - -class Point(object): - def __init__(self, x=0, y=0): - super().__init__() - self.x = x - self.y = y - -class Line(object): # 直线由两个点组成 - def __init__(self, p1, p2): - if isinstance(p1,list) or isinstance(p1,tuple): - self.p1 = Point(*p1) - else: - self.p1 = p1 - if isinstance(p2,list) or isinstance(p2,tuple): - self.p2 = Point(*p2) - else: - self.p2 = p2 - - def vector(self): - return self.p1.x - self.p2.x,self.p1.y - self.p2.y - - def lenght(self): - return math.sqrt(pow((self.p1.x - self.p2.x), 2) - + pow((self.p1.y - self.p2.y), 2)) - - def get_cross_angle(self, l): - # 向量a - arr_a = np.array(self.vector()) - # 向量b - arr_b = np.array(l.vector()) - cos_value = float(arr_a.dot(arr_b)) / (np.sqrt(arr_a.dot(arr_a)) \ - * np.sqrt(arr_b.dot(arr_b))) - # 注意转成浮点数运算 - return np.arccos(cos_value) * (180 / np.pi) - # 两个向量的夹角的角度, - # 余弦值:cos_value, np.cos(para), - # 其中para是弧度,不是角度 - -class EllipseUtils(object): - def __init__(self,pos,size): - self.pos = Point(pos) - self.size = size - self.slides = [] - super().__init__() - - def split(self,data,colors): - self.slides = [] - kvs = [ [i[self.options['keyField']],i[self.options['valueField']]] for i in data ] - total = sum([i[1] for i in kvs ]) - start_degree = 0 - cnt = len(kvs) - for i in range(cnt): - degree = start_degree + 360 * kvs[i][1] / total - self.slides.append((degress,colors[i])) - start_degree = degree - - def isInside(self,a,b,x,y): - if a>b: - return x**x / a**a + y**y/b**b <= 1 - return x**x / b**b + y**y / a**a <= 1 - - def collide_point(self,x,y): - a = float(self.size[0] / 2) - b = float(self.size[1] / 2) - x = x - self.pos[0] - y = y - self.pos[1] - if not self.isInside(a,b,x,y): - return -1 - start_degress = 0 - l = Line((a,b),(a,b*2)) - l1 = Line((a,b),(x,y)) - degress = l.get_cross_angle(l1) - if x < a: - degress = 360 - degress - for i in range(len(self.slides)): - if start_degress <= degress and degress < self.slides[i][0]: - return i - start_degress += self.slides[i][0] - return -1 diff --git a/build/lib/kivycharts/graph/__init__.py b/build/lib/kivycharts/graph/__init__.py deleted file mode 100644 index f6b27fc..0000000 --- a/build/lib/kivycharts/graph/__init__.py +++ /dev/null @@ -1,6 +0,0 @@ -from .base import Graph -from .plot import Plot, MeshLinePlot, MeshStemPlot, LinePlot, \ - SmoothLinePlot, ContourPlot, ScatterPlot, PointPlot, \ - BarPlot, HBar, VBar - - diff --git a/build/lib/kivycharts/graph/_version.py b/build/lib/kivycharts/graph/_version.py deleted file mode 100644 index df9144c..0000000 --- a/build/lib/kivycharts/graph/_version.py +++ /dev/null @@ -1 +0,0 @@ -__version__ = '0.1.1' diff --git a/build/lib/kivycharts/graph/arc.py b/build/lib/kivycharts/graph/arc.py deleted file mode 100644 index 715d9ec..0000000 --- a/build/lib/kivycharts/graph/arc.py +++ /dev/null @@ -1,13 +0,0 @@ -# arc.py -""" -名称 类型 默认值 描述 -opts Object 配置项,继承得到的配置项参见 zrender.Displayable。 -opts.shape Object 形状属性。 -opts.shape.cx number 0 圆心横坐标。 -opts.shape.cy number 0 圆心纵坐标。 -opts.shape.r number 0 半径。 -opts.shape.startAngle number 0 起始弧度。 -opts.shape.endAngle number Math.PI * 2 终止弧度。 -opts.shape.clockwise boolean true 顺时针方向。 -""" - diff --git a/build/lib/kivycharts/graph/base.py b/build/lib/kivycharts/graph/base.py deleted file mode 100644 index edb6d48..0000000 --- a/build/lib/kivycharts/graph/base.py +++ /dev/null @@ -1,988 +0,0 @@ -''' -Graph -====== - -The :class:`Graph` widget is a widget for displaying plots. It supports -drawing multiple plot with different colors on the Graph. It also supports -axes titles, ticks, labeled ticks, grids and a log or linear representation on -both the x and y axis, independently. - -To display a plot. First create a graph which will function as a "canvas" for -the plots. Then create plot objects e.g. MeshLinePlot and add them to the -graph. - -To create a graph with x-axis between 0-100, y-axis between -1 to 1, x and y -labels of and X and Y, respectively, x major and minor ticks every 25, 5 units, -respectively, y major ticks every 1 units, full x and y grids and with -a red line plot containing a sin wave on this range:: - - from kivy_garden.graph import Graph, MeshLinePlot - graph = Graph(xlabel='X', ylabel='Y', x_ticks_minor=5, - x_ticks_major=25, y_ticks_major=1, - y_grid_label=True, x_grid_label=True, padding=5, - x_grid=True, y_grid=True, xmin=-0, xmax=100, ymin=-1, ymax=1) - plot = MeshLinePlot(color=[1, 0, 0, 1]) - plot.points = [(x, sin(x / 10.)) for x in range(0, 101)] - graph.add_plot(plot) - -The MeshLinePlot plot is a particular plot which draws a set of points using -a mesh object. The points are given as a list of tuples, with each tuple -being a (x, y) coordinate in the graph's units. - -You can create different types of plots other than MeshLinePlot by inheriting -from the Plot class and implementing the required functions. The Graph object -provides a "canvas" to which a Plot's instructions are added. The plot object -is responsible for updating these instructions to show within the bounding -box of the graph the proper plot. The Graph notifies the Plot when it needs -to be redrawn due to changes. See the MeshLinePlot class for how it is done. - -The current availables plots are: - - * `MeshStemPlot` - * `MeshLinePlot` - * `SmoothLinePlot` - require Kivy 1.8.1 - -.. note:: - - The graph uses a stencil view to clip the plots to the graph display area. - As with the stencil graphics instructions, you cannot stack more than 8 - stencil-aware widgets. - -''' - -from kivy.uix.widget import Widget -from kivy.uix.label import Label -from kivy.uix.stencilview import StencilView -from kivy.properties import NumericProperty, BooleanProperty,\ - BoundedNumericProperty, StringProperty, ListProperty, ObjectProperty,\ - DictProperty, AliasProperty -from kivy.clock import Clock -from kivy.graphics import Mesh, Color, Rectangle, Point -from kivy.graphics import Fbo -from kivy.graphics.texture import Texture -from kivy.event import EventDispatcher -from kivy.lang import Builder -from kivy.logger import Logger -from kivy import metrics -from math import log10, floor, ceil -from decimal import Decimal -from itertools import chain -try: - import numpy as np -except ImportError as e: - np = None - -from ._version import __version__ - - -def identity(x): - return x - - -def exp10(x): - return 10 ** x - - -Builder.load_string(""" -: - canvas.before: - PushMatrix - Rotate: - angle: root.angle - axis: 0, 0, 1 - origin: root.center - canvas.after: - PopMatrix -""") - - -class GraphRotatedLabel(Label): - angle = NumericProperty(0) - - -class Axis(EventDispatcher): - pass - - -class XAxis(Axis): - pass - - -class YAxis(Axis): - pass - - -class Graph(Widget): - '''Graph class, see module documentation for more information. - ''' - - # triggers a full reload of graphics - _trigger = ObjectProperty(None) - # triggers only a repositioning of objects due to size/pos updates - _trigger_size = ObjectProperty(None) - # triggers only a update of colors, e.g. tick_color - _trigger_color = ObjectProperty(None) - # holds widget with the x-axis label - _xlabel = ObjectProperty(None) - # holds widget with the y-axis label - _ylabel = ObjectProperty(None) - # holds all the x-axis tick mark labels - _x_grid_label = ListProperty([]) - # holds all the y-axis tick mark labels - _y_grid_label = ListProperty([]) - # the mesh drawing all the ticks/grids - _mesh_ticks = ObjectProperty(None) - # the mesh which draws the surrounding rectangle - _mesh_rect = ObjectProperty(None) - # a list of locations of major and minor ticks. The values are not - # but is in the axis min - max range - _ticks_majorx = ListProperty([]) - _ticks_minorx = ListProperty([]) - _ticks_majory = ListProperty([]) - _ticks_minory = ListProperty([]) - - tick_color = ListProperty([.25, .25, .25, 1]) - '''Color of the grid/ticks, default to 1/4. grey. - ''' - - background_color = ListProperty([0, 0, 0, 0]) - '''Color of the background, defaults to transparent - ''' - - border_color = ListProperty([1, 1, 1, 1]) - '''Color of the border, defaults to white - ''' - - x_grid_texts = ListProperty(None) - label_options = DictProperty() - '''Label options that will be passed to `:class:`kivy.uix.Label`. - ''' - - _with_stencilbuffer = BooleanProperty(True) - '''Whether :class:`Graph`'s FBO should use FrameBuffer (True) or not - (False). - - .. warning:: This property is internal and so should be used with care. - It can break some other graphic instructions used by the :class:`Graph`, - for example you can have problems when drawing :class:`SmoothLinePlot` - plots, so use it only when you know what exactly you are doing. - - :data:`_with_stencilbuffer` is a :class:`~kivy.properties.BooleanProperty`, - defaults to True. - ''' - - def __init__(self, **kwargs): - super(Graph, self).__init__(**kwargs) - - with self.canvas: - self._fbo = Fbo( - size=self.size, with_stencilbuffer=self._with_stencilbuffer) - - with self._fbo: - self._background_color = Color(*self.background_color) - self._background_rect = Rectangle(size=self.size) - self._mesh_ticks_color = Color(*self.tick_color) - self._mesh_ticks = Mesh(mode='lines') - self._mesh_rect_color = Color(*self.border_color) - self._mesh_rect = Mesh(mode='line_strip') - - with self.canvas: - Color(1, 1, 1) - self._fbo_rect = Rectangle( - size=self.size, texture=self._fbo.texture) - - mesh = self._mesh_rect - mesh.vertices = [0] * (5 * 4) - mesh.indices = range(5) - - self._plot_area = StencilView() - self.add_widget(self._plot_area) - - t = self._trigger = Clock.create_trigger(self._redraw_all) - ts = self._trigger_size = Clock.create_trigger(self._redraw_size) - tc = self._trigger_color = Clock.create_trigger(self._update_colors) - - self.bind(center=ts, padding=ts, precision=ts, plots=ts, x_grid=ts, - y_grid=ts, draw_border=ts) - self.bind(xmin=t, xmax=t, xlog=t, x_ticks_major=t, x_ticks_minor=t, - xlabel=t, x_grid_label=t, ymin=t, ymax=t, ylog=t, - y_ticks_major=t, y_ticks_minor=t, ylabel=t, y_grid_label=t, - font_size=t, label_options=t, x_ticks_angle=t) - self.bind(tick_color=tc, background_color=tc, border_color=tc) - self._trigger() - - def add_widget(self, widget): - if widget is self._plot_area: - canvas = self.canvas - self.canvas = self._fbo - super(Graph, self).add_widget(widget) - if widget is self._plot_area: - self.canvas = canvas - - def remove_widget(self, widget): - if widget is self._plot_area: - canvas = self.canvas - self.canvas = self._fbo - super(Graph, self).remove_widget(widget) - if widget is self._plot_area: - self.canvas = canvas - - def _get_ticks(self, major, minor, log, s_min, s_max): - if major and s_max > s_min: - if log: - s_min = log10(s_min) - s_max = log10(s_max) - # count the decades in min - max. This is in actual decades, - # not logs. - n_decades = floor(s_max - s_min) - # for the fractional part of the last decade, we need to - # convert the log value, x, to 10**x but need to handle - # differently if the last incomplete decade has a decade - # boundary in it - if floor(s_min + n_decades) != floor(s_max): - n_decades += 1 - (10 ** (s_min + n_decades + 1) - 10 ** - s_max) / 10 ** floor(s_max + 1) - else: - n_decades += ((10 ** s_max - 10 ** (s_min + n_decades)) / - 10 ** floor(s_max + 1)) - # this might be larger than what is needed, but we delete - # excess later - n_ticks_major = n_decades / float(major) - n_ticks = int(floor(n_ticks_major * (minor if minor >= - 1. else 1.0))) + 2 - # in decade multiples, e.g. 0.1 of the decade, the distance - # between ticks - decade_dist = major / float(minor if minor else 1.0) - - points_minor = [0] * n_ticks - points_major = [0] * n_ticks - k = 0 # position in points major - k2 = 0 # position in points minor - # because each decade is missing 0.1 of the decade, if a tick - # falls in < min_pos skip it - min_pos = 0.1 - 0.00001 * decade_dist - s_min_low = floor(s_min) - # first real tick location. value is in fractions of decades - # from the start we have to use decimals here, otherwise - # floating point inaccuracies results in bad values - start_dec = ceil((10 ** Decimal(s_min - s_min_low - 1)) / - Decimal(decade_dist)) * decade_dist - count_min = (0 if not minor else - floor(start_dec / decade_dist) % minor) - start_dec += s_min_low - count = 0 # number of ticks we currently have passed start - while True: - # this is the current position in decade that we are. - # e.g. -0.9 means that we're at 0.1 of the 10**ceil(-0.9) - # decade - pos_dec = start_dec + decade_dist * count - pos_dec_low = floor(pos_dec) - diff = pos_dec - pos_dec_low - zero = abs(diff) < 0.001 * decade_dist - if zero: - # the same value as pos_dec but in log scale - pos_log = pos_dec_low - else: - pos_log = log10((pos_dec - pos_dec_low - ) * 10 ** ceil(pos_dec)) - if pos_log > s_max: - break - count += 1 - if zero or diff >= min_pos: - if minor and not count_min % minor: - points_major[k] = pos_log - k += 1 - else: - points_minor[k2] = pos_log - k2 += 1 - count_min += 1 - else: - # distance between each tick - tick_dist = major / float(minor if minor else 1.0) - n_ticks = int(floor((s_max - s_min) / tick_dist) + 1) - points_major = [0] * int(floor((s_max - s_min) / float(major)) - + 1) - points_minor = [0] * (n_ticks - len(points_major) + 1) - k = 0 # position in points major - k2 = 0 # position in points minor - for m in range(0, n_ticks): - if minor and m % minor: - if k2 >= len(points_minor): - continue - points_minor[k2] = m * tick_dist + s_min - k2 += 1 - else: - points_major[k] = m * tick_dist + s_min - k += 1 - del points_major[k:] - del points_minor[k2:] - else: - points_major = [] - points_minor = [] - return points_major, points_minor - - def _update_labels(self): - xlabel = self._xlabel - ylabel = self._ylabel - x = self.x - y = self.y - width = self.width - height = self.height - padding = self.padding - x_next = padding + x - y_next = padding + y - xextent = width + x - yextent = height + y - ymin = self.ymin - ymax = self.ymax - xmin = self.xmin - precision = self.precision - x_overlap = False - y_overlap = False - # set up x and y axis labels - if xlabel: - xlabel.text = self.xlabel - xlabel.texture_update() - xlabel.size = xlabel.texture_size - xlabel.pos = int( - x + width / 2. - xlabel.width / 2.), int(padding + y) - y_next += padding + xlabel.height - if ylabel: - ylabel.text = self.ylabel - ylabel.texture_update() - ylabel.size = ylabel.texture_size - ylabel.x = padding + x - (ylabel.width / 2. - ylabel.height / 2.) - x_next += padding + ylabel.height - xpoints = self._ticks_majorx - xlabels = self._x_grid_label - xlabel_grid = self.x_grid_label - ylabel_grid = self.y_grid_label - ypoints = self._ticks_majory - ylabels = self._y_grid_label - # now x and y tick mark labels - if len(ylabels) and ylabel_grid: - # horizontal size of the largest tick label, to have enough room - funcexp = exp10 if self.ylog else identity - funclog = log10 if self.ylog else identity - ylabels[0].text = precision % funcexp(ypoints[0]) - ylabels[0].texture_update() - y1 = ylabels[0].texture_size - y_start = y_next + (padding + y1[1] if len(xlabels) and xlabel_grid - else 0) + \ - (padding + y1[1] if not y_next else 0) - yextent = y + height - padding - y1[1] / 2. - - ymin = funclog(ymin) - ratio = (yextent - y_start) / float(funclog(ymax) - ymin) - y_start -= y1[1] / 2. - y1 = y1[0] - for k in range(len(ylabels)): - ylabels[k].text = precision % funcexp(ypoints[k]) - ylabels[k].texture_update() - ylabels[k].size = ylabels[k].texture_size - y1 = max(y1, ylabels[k].texture_size[0]) - ylabels[k].pos = ( - int(x_next), - int(y_start + (ypoints[k] - ymin) * ratio)) - if len(ylabels) > 1 and ylabels[0].top > ylabels[1].y: - y_overlap = True - else: - x_next += y1 + padding - if len(xlabels) and xlabel_grid: - funcexp = exp10 if self.xlog else identity - funclog = log10 if self.xlog else identity - # find the distance from the end that'll fit the last tick label - if self.x_grid_texts: - xlabels[0].text = self.x_grid_texts[int(xpoints[-1])] - else: - xlabels[0].text = precision % funcexp(xpoints[-1]) - xlabels[0].texture_update() - xextent = x + width - xlabels[0].texture_size[0] / 2. - padding - # find the distance from the start that'll fit the first tick label - if not x_next: - if self.x_grid_texts: - xlabels[0].text = self.x_grid_texts[int(xpoints[0])] - else: - xlabels[0].text = precision % funcexp(xpoints[0]) - xlabels[0].texture_update() - x_next = padding + xlabels[0].texture_size[0] / 2. - xmin = funclog(xmin) - ratio = (xextent - x_next) / float(funclog(self.xmax) - xmin) - right = -1 - for k in range(len(xlabels)): - if self.x_grid_texts: - xlabels[k].text = self.x_grid_texts[int(xpoints[k])] - else: - xlabels[k].text = precision % funcexp(xpoints[k]) - # update the size so we can center the labels on ticks - xlabels[k].texture_update() - xlabels[k].size = xlabels[k].texture_size - half_ts = xlabels[k].texture_size[0] / 2. - xlabels[k].pos = ( - int(x_next + (xpoints[k] - xmin) * ratio - half_ts), - int(y_next)) - if xlabels[k].x < right: - x_overlap = True - break - right = xlabels[k].right - if not x_overlap: - y_next += padding + xlabels[0].texture_size[1] - # now re-center the x and y axis labels - if xlabel: - xlabel.x = int( - x_next + (xextent - x_next) / 2. - xlabel.width / 2.) - if ylabel: - ylabel.y = int( - y_next + (yextent - y_next) / 2. - ylabel.height / 2.) - ylabel.angle = 90 - if x_overlap: - for k in range(len(xlabels)): - xlabels[k].text = '' - if y_overlap: - for k in range(len(ylabels)): - ylabels[k].text = '' - return x_next - x, y_next - y, xextent - x, yextent - y - - def _update_ticks(self, size): - # re-compute the positions of the bounding rectangle - mesh = self._mesh_rect - vert = mesh.vertices - if self.draw_border: - s0, s1, s2, s3 = size - vert[0] = s0 - vert[1] = s1 - vert[4] = s2 - vert[5] = s1 - vert[8] = s2 - vert[9] = s3 - vert[12] = s0 - vert[13] = s3 - vert[16] = s0 - vert[17] = s1 - else: - vert[0:18] = [0 for k in range(18)] - mesh.vertices = vert - # re-compute the positions of the x/y axis ticks - mesh = self._mesh_ticks - vert = mesh.vertices - start = 0 - xpoints = self._ticks_majorx - ypoints = self._ticks_majory - xpoints2 = self._ticks_minorx - ypoints2 = self._ticks_minory - ylog = self.ylog - xlog = self.xlog - xmin = self.xmin - xmax = self.xmax - if xlog: - xmin = log10(xmin) - xmax = log10(xmax) - ymin = self.ymin - ymax = self.ymax - if ylog: - ymin = log10(ymin) - ymax = log10(ymax) - if len(xpoints): - top = size[3] if self.x_grid else metrics.dp(12) + size[1] - ratio = (size[2] - size[0]) / float(xmax - xmin) - for k in range(start, len(xpoints) + start): - vert[k * 8] = size[0] + (xpoints[k - start] - xmin) * ratio - vert[k * 8 + 1] = size[1] - vert[k * 8 + 4] = vert[k * 8] - vert[k * 8 + 5] = top - start += len(xpoints) - if len(xpoints2): - top = metrics.dp(8) + size[1] - ratio = (size[2] - size[0]) / float(xmax - xmin) - for k in range(start, len(xpoints2) + start): - vert[k * 8] = size[0] + (xpoints2[k - start] - xmin) * ratio - vert[k * 8 + 1] = size[1] - vert[k * 8 + 4] = vert[k * 8] - vert[k * 8 + 5] = top - start += len(xpoints2) - if len(ypoints): - top = size[2] if self.y_grid else metrics.dp(12) + size[0] - ratio = (size[3] - size[1]) / float(ymax - ymin) - for k in range(start, len(ypoints) + start): - vert[k * 8 + 1] = size[1] + (ypoints[k - start] - ymin) * ratio - vert[k * 8 + 5] = vert[k * 8 + 1] - vert[k * 8] = size[0] - vert[k * 8 + 4] = top - start += len(ypoints) - if len(ypoints2): - top = metrics.dp(8) + size[0] - ratio = (size[3] - size[1]) / float(ymax - ymin) - for k in range(start, len(ypoints2) + start): - vert[k * 8 + 1] = size[1] + ( - ypoints2[k - start] - ymin) * ratio - vert[k * 8 + 5] = vert[k * 8 + 1] - vert[k * 8] = size[0] - vert[k * 8 + 4] = top - mesh.vertices = vert - - x_axis = ListProperty([None]) - y_axis = ListProperty([None]) - - def get_x_axis(self, axis=0): - if axis == 0: - return self.xlog, self.xmin, self.xmax - info = self.x_axis[axis] - return info["log"], info["min"], info["max"] - - def get_y_axis(self, axis=0): - if axis == 0: - return self.ylog, self.ymin, self.ymax - info = self.y_axis[axis] - return info["log"], info["min"], info["max"] - - def add_x_axis(self, xmin, xmax, xlog=False): - data = { - "log": xlog, - "min": xmin, - "max": xmax - } - self.x_axis.append(data) - return data - - def add_y_axis(self, ymin, ymax, ylog=False): - data = { - "log": ylog, - "min": ymin, - "max": ymax - } - self.y_axis.append(data) - return data - - def _update_plots(self, size): - for plot in self.plots: - xlog, xmin, xmax = self.get_x_axis(plot.x_axis) - ylog, ymin, ymax = self.get_y_axis(plot.y_axis) - plot._update(xlog, xmin, xmax, ylog, ymin, ymax, size) - - def _update_colors(self, *args): - self._mesh_ticks_color.rgba = tuple(self.tick_color) - self._background_color.rgba = tuple(self.background_color) - self._mesh_rect_color.rgba = tuple(self.border_color) - - def _redraw_all(self, *args): - # add/remove all the required labels - xpoints_major, xpoints_minor = self._redraw_x(*args) - ypoints_major, ypoints_minor = self._redraw_y(*args) - - mesh = self._mesh_ticks - n_points = (len(xpoints_major) + len(xpoints_minor) + - len(ypoints_major) + len(ypoints_minor)) - mesh.vertices = [0] * (n_points * 8) - mesh.indices = [k for k in range(n_points * 2)] - self._redraw_size() - - def _redraw_x(self, *args): - font_size = self.font_size - if self.xlabel: - xlabel = self._xlabel - if not xlabel: - xlabel = Label() - self.add_widget(xlabel) - self._xlabel = xlabel - - xlabel.font_size = font_size - for k, v in self.label_options.items(): - setattr(xlabel, k, v) - - else: - xlabel = self._xlabel - if xlabel: - self.remove_widget(xlabel) - self._xlabel = None - grids = self._x_grid_label - xpoints_major, xpoints_minor = self._get_ticks(self.x_ticks_major, - self.x_ticks_minor, - self.xlog, self.xmin, - self.xmax) - self._ticks_majorx = xpoints_major - self._ticks_minorx = xpoints_minor - - if not self.x_grid_label: - n_labels = 0 - else: - n_labels = len(xpoints_major) - - for k in range(n_labels, len(grids)): - self.remove_widget(grids[k]) - del grids[n_labels:] - - grid_len = len(grids) - grids.extend([None] * (n_labels - len(grids))) - for k in range(grid_len, n_labels): - grids[k] = GraphRotatedLabel( - font_size=font_size, angle=self.x_ticks_angle, - **self.label_options) - self.add_widget(grids[k]) - return xpoints_major, xpoints_minor - - def _redraw_y(self, *args): - font_size = self.font_size - if self.ylabel: - ylabel = self._ylabel - if not ylabel: - ylabel = GraphRotatedLabel() - self.add_widget(ylabel) - self._ylabel = ylabel - - ylabel.font_size = font_size - for k, v in self.label_options.items(): - setattr(ylabel, k, v) - else: - ylabel = self._ylabel - if ylabel: - self.remove_widget(ylabel) - self._ylabel = None - grids = self._y_grid_label - ypoints_major, ypoints_minor = self._get_ticks(self.y_ticks_major, - self.y_ticks_minor, - self.ylog, self.ymin, - self.ymax) - self._ticks_majory = ypoints_major - self._ticks_minory = ypoints_minor - - if not self.y_grid_label: - n_labels = 0 - else: - n_labels = len(ypoints_major) - - for k in range(n_labels, len(grids)): - self.remove_widget(grids[k]) - del grids[n_labels:] - - grid_len = len(grids) - grids.extend([None] * (n_labels - len(grids))) - for k in range(grid_len, n_labels): - grids[k] = Label(font_size=font_size, **self.label_options) - self.add_widget(grids[k]) - return ypoints_major, ypoints_minor - - def _redraw_size(self, *args): - # size a 4-tuple describing the bounding box in which we can draw - # graphs, it's (x0, y0, x1, y1), which correspond with the bottom left - # and top right corner locations, respectively - self._clear_buffer() - size = self._update_labels() - self.view_pos = self._plot_area.pos = (size[0], size[1]) - self.view_size = self._plot_area.size = ( - size[2] - size[0], size[3] - size[1]) - - if self.size[0] and self.size[1]: - self._fbo.size = self.size - else: - self._fbo.size = 1, 1 # gl errors otherwise - self._fbo_rect.texture = self._fbo.texture - self._fbo_rect.size = self.size - self._fbo_rect.pos = self.pos - self._background_rect.size = self.size - self._update_ticks(size) - self._update_plots(size) - - def _clear_buffer(self, *largs): - fbo = self._fbo - fbo.bind() - fbo.clear_buffer() - fbo.release() - - def add_plot(self, plot): - '''Add a new plot to this graph. - - :Parameters: - `plot`: - Plot to add to this graph. - - >>> graph = Graph() - >>> plot = MeshLinePlot(mode='line_strip', color=[1, 0, 0, 1]) - >>> plot.points = [(x / 10., sin(x / 50.)) for x in range(-0, 101)] - >>> graph.add_plot(plot) - ''' - if plot in self.plots: - return - add = self._plot_area.canvas.add - for instr in plot.get_drawings(): - add(instr) - plot.bind(on_clear_plot=self._clear_buffer) - self.plots.append(plot) - - def remove_plot(self, plot): - '''Remove a plot from this graph. - - :Parameters: - `plot`: - Plot to remove from this graph. - - >>> graph = Graph() - >>> plot = MeshLinePlot(mode='line_strip', color=[1, 0, 0, 1]) - >>> plot.points = [(x / 10., sin(x / 50.)) for x in range(-0, 101)] - >>> graph.add_plot(plot) - >>> graph.remove_plot(plot) - ''' - if plot not in self.plots: - return - remove = self._plot_area.canvas.remove - for instr in plot.get_drawings(): - remove(instr) - plot.unbind(on_clear_plot=self._clear_buffer) - self.plots.remove(plot) - self._clear_buffer() - - def collide_plot(self, x, y): - '''Determine if the given coordinates fall inside the plot area. Use - `x, y = self.to_widget(x, y, relative=True)` to first convert into - widget coordinates if it's in window coordinates because it's assumed - to be given in local widget coordinates, relative to the graph's pos. - - :Parameters: - `x, y`: - The coordinates to test. - ''' - adj_x, adj_y = x - self._plot_area.pos[0], y - self._plot_area.pos[1] - return 0 <= adj_x <= self._plot_area.size[0] \ - and 0 <= adj_y <= self._plot_area.size[1] - - def to_data(self, x, y): - '''Convert widget coords to data coords. Use - `x, y = self.to_widget(x, y, relative=True)` to first convert into - widget coordinates if it's in window coordinates because it's assumed - to be given in local widget coordinates, relative to the graph's pos. - - :Parameters: - `x, y`: - The coordinates to convert. - - If the graph has multiple axes, use :class:`Plot.unproject` instead. - ''' - adj_x = float(x - self._plot_area.pos[0]) - adj_y = float(y - self._plot_area.pos[1]) - norm_x = adj_x / self._plot_area.size[0] - norm_y = adj_y / self._plot_area.size[1] - if self.xlog: - xmin, xmax = log10(self.xmin), log10(self.xmax) - conv_x = 10.**(norm_x * (xmax - xmin) + xmin) - else: - conv_x = norm_x * (self.xmax - self.xmin) + self.xmin - if self.ylog: - ymin, ymax = log10(self.ymin), log10(self.ymax) - conv_y = 10.**(norm_y * (ymax - ymin) + ymin) - else: - conv_y = norm_y * (self.ymax - self.ymin) + self.ymin - return [conv_x, conv_y] - - xmin = NumericProperty(0.) - '''The x-axis minimum value. - - If :data:`xlog` is True, xmin must be larger than zero. - - :data:`xmin` is a :class:`~kivy.properties.NumericProperty`, defaults to 0. - ''' - - xmax = NumericProperty(100.) - '''The x-axis maximum value, larger than xmin. - - :data:`xmax` is a :class:`~kivy.properties.NumericProperty`, defaults to 0. - ''' - - xlog = BooleanProperty(False) - '''Determines whether the x-axis should be displayed logarithmically (True) - or linearly (False). - - :data:`xlog` is a :class:`~kivy.properties.BooleanProperty`, defaults - to False. - ''' - - x_ticks_major = BoundedNumericProperty(0, min=0) - '''Distance between major tick marks on the x-axis. - - Determines the distance between the major tick marks. Major tick marks - start from min and re-occur at every ticks_major until :data:`xmax`. - If :data:`xmax` doesn't overlap with a integer multiple of ticks_major, - no tick will occur at :data:`xmax`. Zero indicates no tick marks. - - If :data:`xlog` is true, then this indicates the distance between ticks - in multiples of current decade. E.g. if :data:`xmin` is 0.1 and - ticks_major is 0.1, it means there will be a tick at every 10th of the - decade, i.e. 0.1 ... 0.9, 1, 2... If it is 0.3, the ticks will occur at - 0.1, 0.3, 0.6, 0.9, 2, 5, 8, 10. You'll notice that it went from 8 to 10 - instead of to 20, that's so that we can say 0.5 and have ticks at every - half decade, e.g. 0.1, 0.5, 1, 5, 10, 50... Similarly, if ticks_major is - 1.5, there will be ticks at 0.1, 5, 100, 5,000... Also notice, that there's - always a major tick at the start. Finally, if e.g. :data:`xmin` is 0.6 - and this 0.5 there will be ticks at 0.6, 1, 5... - - :data:`x_ticks_major` is a - :class:`~kivy.properties.BoundedNumericProperty`, defaults to 0. - ''' - - x_ticks_minor = BoundedNumericProperty(0, min=0) - '''The number of sub-intervals that divide x_ticks_major. - - Determines the number of sub-intervals into which ticks_major is divided, - if non-zero. The actual number of minor ticks between the major ticks is - ticks_minor - 1. Only used if ticks_major is non-zero. If there's no major - tick at xmax then the number of minor ticks after the last major - tick will be however many ticks fit until xmax. - - If self.xlog is true, then this indicates the number of intervals the - distance between major ticks is divided. The result is the number of - multiples of decades between ticks. I.e. if ticks_minor is 10, then if - ticks_major is 1, there will be ticks at 0.1, 0.2...0.9, 1, 2, 3... If - ticks_major is 0.3, ticks will occur at 0.1, 0.12, 0.15, 0.18... Finally, - as is common, if ticks major is 1, and ticks minor is 5, there will be - ticks at 0.1, 0.2, 0.4... 0.8, 1, 2... - - :data:`x_ticks_minor` is a - :class:`~kivy.properties.BoundedNumericProperty`, defaults to 0. - ''' - - x_grid = BooleanProperty(False) - '''Determines whether the x-axis has tick marks or a full grid. - - If :data:`x_ticks_major` is non-zero, then if x_grid is False tick marks - will be displayed at every major tick. If x_grid is True, instead of ticks, - a vertical line will be displayed at every major tick. - - :data:`x_grid` is a :class:`~kivy.properties.BooleanProperty`, defaults - to False. - ''' - - x_grid_label = BooleanProperty(False) - '''Whether labels should be displayed beneath each major tick. If true, - each major tick will have a label containing the axis value. - - :data:`x_grid_label` is a :class:`~kivy.properties.BooleanProperty`, - defaults to False. - ''' - - xlabel = StringProperty('') - '''The label for the x-axis. If not empty it is displayed in the center of - the axis. - - :data:`xlabel` is a :class:`~kivy.properties.StringProperty`, - defaults to ''. - ''' - - ymin = NumericProperty(0.) - '''The y-axis minimum value. - - If :data:`ylog` is True, ymin must be larger than zero. - - :data:`ymin` is a :class:`~kivy.properties.NumericProperty`, defaults to 0. - ''' - - ymax = NumericProperty(100.) - '''The y-axis maximum value, larger than ymin. - - :data:`ymax` is a :class:`~kivy.properties.NumericProperty`, defaults to 0. - ''' - - ylog = BooleanProperty(False) - '''Determines whether the y-axis should be displayed logarithmically (True) - or linearly (False). - - :data:`ylog` is a :class:`~kivy.properties.BooleanProperty`, defaults - to False. - ''' - - y_ticks_major = BoundedNumericProperty(0, min=0) - '''Distance between major tick marks. See :data:`x_ticks_major`. - - :data:`y_ticks_major` is a - :class:`~kivy.properties.BoundedNumericProperty`, defaults to 0. - ''' - - y_ticks_minor = BoundedNumericProperty(0, min=0) - '''The number of sub-intervals that divide ticks_major. - See :data:`x_ticks_minor`. - - :data:`y_ticks_minor` is a - :class:`~kivy.properties.BoundedNumericProperty`, defaults to 0. - ''' - - y_grid = BooleanProperty(False) - '''Determines whether the y-axis has tick marks or a full grid. See - :data:`x_grid`. - - :data:`y_grid` is a :class:`~kivy.properties.BooleanProperty`, defaults - to False. - ''' - - y_grid_label = BooleanProperty(False) - '''Whether labels should be displayed beneath each major tick. If true, - each major tick will have a label containing the axis value. - - :data:`y_grid_label` is a :class:`~kivy.properties.BooleanProperty`, - defaults to False. - ''' - - ylabel = StringProperty('') - '''The label for the y-axis. If not empty it is displayed in the center of - the axis. - - :data:`ylabel` is a :class:`~kivy.properties.StringProperty`, - defaults to ''. - ''' - - padding = NumericProperty('5dp') - '''Padding distances between the labels, axes titles and graph, as - well between the widget and the objects near the boundaries. - - :data:`padding` is a :class:`~kivy.properties.NumericProperty`, defaults - to 5dp. - ''' - - font_size = NumericProperty('15sp') - '''Font size of the labels. - - :data:`font_size` is a :class:`~kivy.properties.NumericProperty`, defaults - to 15sp. - ''' - - x_ticks_angle = NumericProperty(0) - '''Rotate angle of the x-axis tick marks. - - :data:`x_ticks_angle` is a :class:`~kivy.properties.NumericProperty`, - defaults to 0. - ''' - - precision = StringProperty('%g') - '''Determines the numerical precision of the tick mark labels. This value - governs how the numbers are converted into string representation. Accepted - values are those listed in Python's manual in the - "String Formatting Operations" section. - - :data:`precision` is a :class:`~kivy.properties.StringProperty`, defaults - to '%g'. - ''' - - draw_border = BooleanProperty(True) - '''Whether a border is drawn around the canvas of the graph where the - plots are displayed. - - :data:`draw_border` is a :class:`~kivy.properties.BooleanProperty`, - defaults to True. - ''' - - plots = ListProperty([]) - '''Holds a list of all the plots in the graph. To add and remove plots - from the graph use :data:`add_plot` and :data:`add_plot`. Do not add - directly edit this list. - - :data:`plots` is a :class:`~kivy.properties.ListProperty`, - defaults to []. - ''' - - view_size = ObjectProperty((0, 0)) - '''The size of the graph viewing area - the area where the plots are - displayed, excluding labels etc. - ''' - - view_pos = ObjectProperty((0, 0)) - '''The pos of the graph viewing area - the area where the plots are - displayed, excluding labels etc. It is relative to the graph's pos. - ''' - - diff --git a/build/lib/kivycharts/graph/beziercurve.py b/build/lib/kivycharts/graph/beziercurve.py deleted file mode 100644 index 1535558..0000000 --- a/build/lib/kivycharts/graph/beziercurve.py +++ /dev/null @@ -1,14 +0,0 @@ -# briercurve.py -""" -名称 类型 默认值 描述 -opts Object 配置项,继承得到的配置项参见 zrender.Displayable。 -opts.shape Object 形状属性。 -opts.shape.x1 number 0 起始点横坐标。 -opts.shape.y1 number 0 起始点纵坐标。 -opts.shape.x2 number 0 终止点横坐标。 -opts.shape.y2 number 0 终止点纵坐标。 -opts.shape.cpx1 number 0 控制点横坐标。 -opts.shape.cpy1 number 0 控制点纵坐标。 -opts.shape.percent number 1 已显示的百分比,用于绘制动画。 -""" - diff --git a/build/lib/kivycharts/graph/circle.py b/build/lib/kivycharts/graph/circle.py deleted file mode 100644 index dc15c8c..0000000 --- a/build/lib/kivycharts/graph/circle.py +++ /dev/null @@ -1,10 +0,0 @@ -# circle.py -""" -名称 类型 默认值 描述 -opts Object 配置项,继承得到的配置项参见 zrender.Displayable。 -opts.shape Object 形状属性。 -opts.shape.cx number 0 圆心横坐标。 -opts.shape.cy number 0 圆心纵坐标。 -opts.shape.r number 0 半径。 -""" - diff --git a/build/lib/kivycharts/graph/compoundpath.py b/build/lib/kivycharts/graph/compoundpath.py deleted file mode 100644 index 7f72431..0000000 --- a/build/lib/kivycharts/graph/compoundpath.py +++ /dev/null @@ -1,7 +0,0 @@ -# compoundpath.py -""" -名称 类型 默认值 描述 -opts Object 配置项,继承得到的配置项参见 zrender.Displayable。 -opts.shape.paths Path[] null 路径数组。 -""" - diff --git a/build/lib/kivycharts/graph/displayable.py b/build/lib/kivycharts/graph/displayable.py deleted file mode 100644 index 1f40182..0000000 --- a/build/lib/kivycharts/graph/displayable.py +++ /dev/null @@ -1,5 +0,0 @@ -# displayable.py -""" -base class for all the plot class -""" - diff --git a/build/lib/kivycharts/graph/droplet.py b/build/lib/kivycharts/graph/droplet.py deleted file mode 100644 index 30c98f8..0000000 --- a/build/lib/kivycharts/graph/droplet.py +++ /dev/null @@ -1,11 +0,0 @@ -# droplet.py -""" -名称 类型 默认值 描述 -opts Object 配置项,继承得到的配置项参见 zrender.Displayable。 -opts.shape Object 形状属性。 -opts.shape.cx number 0 圆心横坐标。 -opts.shape.cy number 0 圆心纵坐标。 -opts.shape.width number 0 宽度。 -opts.shape.height number 0 高度。 -""" - diff --git a/build/lib/kivycharts/graph/ellipse.py b/build/lib/kivycharts/graph/ellipse.py deleted file mode 100644 index 50c3d72..0000000 --- a/build/lib/kivycharts/graph/ellipse.py +++ /dev/null @@ -1,11 +0,0 @@ -# ellipse.py -""" -名称 类型 默认值 描述 -opts Object 配置项,继承得到的配置项参见 zrender.Displayable。 -opts.shape Object 形状属性。 -opts.shape.cx number 0 圆心横坐标。 -opts.shape.cy number 0 圆心纵坐标。 -opts.shape.rx number 0 横向半径。 -opts.shape.ry number 0 纵向半径。 -""" - diff --git a/build/lib/kivycharts/graph/heart.py b/build/lib/kivycharts/graph/heart.py deleted file mode 100644 index df14fb7..0000000 --- a/build/lib/kivycharts/graph/heart.py +++ /dev/null @@ -1,11 +0,0 @@ -# heart.py -""" -名称 类型 默认值 描述 -opts Object 配置项,继承得到的配置项参见 zrender.Displayable。 -opts.shape Object 形状属性。 -opts.shape.cx number 0 圆心横坐标。 -opts.shape.cy number 0 圆心纵坐标。 -opts.shape.width number 0 宽度。 -opts.shape.height number 0 高度。 -""" - diff --git a/build/lib/kivycharts/graph/image.py b/build/lib/kivycharts/graph/image.py deleted file mode 100644 index 6afc08f..0000000 --- a/build/lib/kivycharts/graph/image.py +++ /dev/null @@ -1,12 +0,0 @@ -# image.py -""" -名称 类型 默认值 描述 -opts Object 配置项,继承得到的配置项参见 zrender.Displayable。 -opts.style Object 样式。 -opts.style.image string|HTMLImageElement|HTMLCanvasElement 图片的内容,可以是图片的 URL,也可以是 dataURI。 -opts.style.x number 图片左上角相对于父节点的横坐标。 -opts.style.y number 图片左上角相对于父节点的纵坐标。 -opts.style.width number 图片宽度。 -opts.style.height number 图片高度。 -""" - diff --git a/build/lib/kivycharts/graph/line.py b/build/lib/kivycharts/graph/line.py deleted file mode 100644 index 6f74758..0000000 --- a/build/lib/kivycharts/graph/line.py +++ /dev/null @@ -1,12 +0,0 @@ -# line.py -""" -名称 类型 默认值 描述 -opts Object 配置项,继承得到的配置项参见 zrender.Displayable。 -opts.shape Object 形状属性。 -opts.shape.x1 number 0 起始点横坐标。 -opts.shape.y1 number 0 起始点纵坐标。 -opts.shape.x2 number 0 终止点横坐标。 -opts.shape.y2 number 0 终止点纵坐标。 -opts.shape.percent number 1 已显示的百分比,用于绘制动画。 -""" - diff --git a/build/lib/kivycharts/graph/lsogon.py b/build/lib/kivycharts/graph/lsogon.py deleted file mode 100644 index 0dca14d..0000000 --- a/build/lib/kivycharts/graph/lsogon.py +++ /dev/null @@ -1,10 +0,0 @@ -# lsogon.py -""" -opts Object 配置项,继承得到的配置项参见 zrender.Displayable。 -opts.shape Object 形状属性。 -opts.shape.x number 0 圆心横坐标。 -opts.shape.y number 0 圆心纵坐标。 -opts.shape.r number 0 半径。 -opts.shape.n number 0 边数。 -""" - diff --git a/build/lib/kivycharts/graph/path.py b/build/lib/kivycharts/graph/path.py deleted file mode 100644 index e2453a1..0000000 --- a/build/lib/kivycharts/graph/path.py +++ /dev/null @@ -1,14 +0,0 @@ -# path.py -""" -名称 类型 默认值 描述 -opts Object 配置项,继承得到的配置项参见 zrender.Displayable。 -""" - - -""" -path.extend -opts Object 配置项,继承得到的配置项参见 zrender.Displayable。 -opts.type string 类型,自定义的名称。 -opts.init Function 初始化时调用的函数。 -opts.buildPath Function 如何构建 Path 的函数,在绘制时候会调用。 -""" diff --git a/build/lib/kivycharts/graph/plot.py b/build/lib/kivycharts/graph/plot.py deleted file mode 100644 index 62d5121..0000000 --- a/build/lib/kivycharts/graph/plot.py +++ /dev/null @@ -1,795 +0,0 @@ - -from kivy.uix.widget import Widget -from kivy.uix.label import Label -from kivy.uix.stencilview import StencilView -from kivy.properties import NumericProperty, BooleanProperty,\ - BoundedNumericProperty, StringProperty, ListProperty, ObjectProperty,\ - DictProperty, AliasProperty -from kivy.clock import Clock -from kivy.graphics import Mesh, Color, Rectangle, Point -from kivy.graphics import Fbo -from kivy.graphics.texture import Texture -from kivy.event import EventDispatcher -from kivy.lang import Builder -from kivy.logger import Logger -from kivy import metrics -from math import log10, floor, ceil -from decimal import Decimal -from itertools import chain -try: - import numpy as np -except ImportError as e: - np = None - -class Plot(EventDispatcher): - '''Plot class, see module documentation for more information. - - :Events: - `on_clear_plot` - Fired before a plot updates the display and lets the fbo know that - it should clear the old drawings. - - ..versionadded:: 0.4 - ''' - - __events__ = ('on_clear_plot', ) - - # most recent values of the params used to draw the plot - params = DictProperty({'xlog': False, 'xmin': 0, 'xmax': 100, - 'ylog': False, 'ymin': 0, 'ymax': 100, - 'size': (0, 0, 0, 0)}) - - color = ListProperty([1, 1, 1, 1]) - '''Color of the plot. - ''' - - points = ListProperty([]) - '''List of (x, y) points to be displayed in the plot. - - The elements of points are 2-tuples, (x, y). The points are displayed - based on the mode setting. - - :data:`points` is a :class:`~kivy.properties.ListProperty`, defaults to - []. - ''' - - x_axis = NumericProperty(0) - '''Index of the X axis to use, defaults to 0 - ''' - - y_axis = NumericProperty(0) - '''Index of the Y axis to use, defaults to 0 - ''' - - def __init__(self, **kwargs): - super(Plot, self).__init__(**kwargs) - self.ask_draw = Clock.create_trigger(self.draw) - self.bind(params=self.ask_draw, points=self.ask_draw) - self._drawings = self.create_drawings() - - def funcx(self): - """Return a function that convert or not the X value according to plot - prameters""" - return log10 if self.params["xlog"] else lambda x: x - - def funcy(self): - """Return a function that convert or not the Y value according to plot - prameters""" - return log10 if self.params["ylog"] else lambda y: y - - def x_px(self): - """Return a function that convert the X value of the graph to the - pixel coordinate on the plot, according to the plot settings and axis - settings. It's relative to the graph pos. - """ - funcx = self.funcx() - params = self.params - size = params["size"] - xmin = funcx(params["xmin"]) - xmax = funcx(params["xmax"]) - ratiox = (size[2] - size[0]) / float(xmax - xmin) - return lambda x: (funcx(x) - xmin) * ratiox + size[0] - - def y_px(self): - """Return a function that convert the Y value of the graph to the - pixel coordinate on the plot, according to the plot settings and axis - settings. The returned value is relative to the graph pos. - """ - funcy = self.funcy() - params = self.params - size = params["size"] - ymin = funcy(params["ymin"]) - ymax = funcy(params["ymax"]) - ratioy = (size[3] - size[1]) / float(ymax - ymin) - return lambda y: (funcy(y) - ymin) * ratioy + size[1] - - def unproject(self, x, y): - """Return a function that unproject a pixel to a X/Y value on the plot - (works only for linear, not log yet). `x`, `y`, is relative to the - graph pos, so the graph's pos needs to be subtracted from x, y before - passing it in. - """ - params = self.params - size = params["size"] - xmin = params["xmin"] - xmax = params["xmax"] - ymin = params["ymin"] - ymax = params["ymax"] - ratiox = (size[2] - size[0]) / float(xmax - xmin) - ratioy = (size[3] - size[1]) / float(ymax - ymin) - x0 = (x - size[0]) / ratiox + xmin - y0 = (y - size[1]) / ratioy + ymin - return x0, y0 - - def get_px_bounds(self): - """Returns a dict containing the pixels bounds from the plot parameters. - The returned values are relative to the graph pos. - """ - params = self.params - x_px = self.x_px() - y_px = self.y_px() - return { - "xmin": x_px(params["xmin"]), - "xmax": x_px(params["xmax"]), - "ymin": y_px(params["ymin"]), - "ymax": y_px(params["ymax"]), - } - - def update(self, xlog, xmin, xmax, ylog, ymin, ymax, size): - '''Called by graph whenever any of the parameters - change. The plot should be recalculated then. - log, min, max indicate the axis settings. - size a 4-tuple describing the bounding box in which we can draw - graphs, it's (x0, y0, x1, y1), which correspond with the bottom left - and top right corner locations, respectively. - ''' - self.params.update({ - 'xlog': xlog, 'xmin': xmin, 'xmax': xmax, 'ylog': ylog, - 'ymin': ymin, 'ymax': ymax, 'size': size}) - - def get_group(self): - '''returns a string which is unique and is the group name given to all - the instructions returned by _get_drawings. Graph uses this to remove - these instructions when needed. - ''' - return '' - - def get_drawings(self): - '''returns a list of canvas instructions that will be added to the - graph's canvas. - ''' - if isinstance(self._drawings, (tuple, list)): - return self._drawings - return [] - - def create_drawings(self): - '''called once to create all the canvas instructions needed for the - plot - ''' - pass - - def draw(self, *largs): - '''draw the plot according to the params. It dispatches on_clear_plot - so derived classes should call super before updating. - ''' - self.dispatch('on_clear_plot') - - def iterate_points(self): - '''Iterate on all the points adjusted to the graph settings - ''' - x_px = self.x_px() - y_px = self.y_px() - for x, y in self.points: - yield x_px(x), y_px(y) - - def on_clear_plot(self, *largs): - pass - - # compatibility layer - _update = update - _get_drawings = get_drawings - _params = params - - -class MeshLinePlot(Plot): - '''MeshLinePlot class which displays a set of points similar to a mesh. - ''' - - def _set_mode(self, value): - if hasattr(self, '_mesh'): - self._mesh.mode = value - - mode = AliasProperty(lambda self: self._mesh.mode, _set_mode) - '''VBO Mode used for drawing the points. Can be one of: 'points', - 'line_strip', 'line_loop', 'lines', 'triangle_strip', 'triangle_fan'. - See :class:`~kivy.graphics.Mesh` for more details. - - Defaults to 'line_strip'. - ''' - - def create_drawings(self): - self._color = Color(*self.color) - self._mesh = Mesh(mode='line_strip') - self.bind( - color=lambda instr, value: setattr(self._color, "rgba", value)) - return [self._color, self._mesh] - - def draw(self, *args): - super(MeshLinePlot, self).draw(*args) - self.plot_mesh() - - def plot_mesh(self): - points = [p for p in self.iterate_points()] - mesh, vert, _ = self.set_mesh_size(len(points)) - for k, (x, y) in enumerate(points): - vert[k * 4] = x - vert[k * 4 + 1] = y - mesh.vertices = vert - - def set_mesh_size(self, size): - mesh = self._mesh - vert = mesh.vertices - ind = mesh.indices - diff = size - len(vert) // 4 - if diff < 0: - del vert[4 * size:] - del ind[size:] - elif diff > 0: - ind.extend(range(len(ind), len(ind) + diff)) - vert.extend([0] * (diff * 4)) - mesh.vertices = vert - return mesh, vert, ind - - -class MeshStemPlot(MeshLinePlot): - '''MeshStemPlot uses the MeshLinePlot class to draw a stem plot. The data - provided is graphed from origin to the data point. - ''' - - def plot_mesh(self): - points = [p for p in self.iterate_points()] - mesh, vert, _ = self.set_mesh_size(len(points) * 2) - y0 = self.y_px()(0) - for k, (x, y) in enumerate(self.iterate_points()): - vert[k * 8] = x - vert[k * 8 + 1] = y0 - vert[k * 8 + 4] = x - vert[k * 8 + 5] = y - mesh.vertices = vert - - -class LinePlot(Plot): - """LinePlot draws using a standard Line object. - """ - - line_width = NumericProperty(1) - - def create_drawings(self): - from kivy.graphics import Line, RenderContext - - self._grc = RenderContext( - use_parent_modelview=True, - use_parent_projection=True) - with self._grc: - self._gcolor = Color(*self.color) - self._gline = Line( - points=[], cap='none', - width=self.line_width, joint='round') - - return [self._grc] - - def draw(self, *args): - super(LinePlot, self).draw(*args) - # flatten the list - points = [] - for x, y in self.iterate_points(): - points += [x, y] - self._gline.points = points - - def on_line_width(self, *largs): - if hasattr(self, "_gline"): - self._gline.width = self.line_width - - -class SmoothLinePlot(Plot): - '''Smooth Plot class, see module documentation for more information. - This plot use a specific Fragment shader for a custom anti aliasing. - ''' - - SMOOTH_FS = ''' - $HEADER$ - - void main(void) { - float edgewidth = 0.015625 * 64.; - float t = texture2D(texture0, tex_coord0).r; - float e = smoothstep(0., edgewidth, t); - gl_FragColor = frag_color * vec4(1, 1, 1, e); - } - ''' - - # XXX This gradient data is a 64x1 RGB image, and - # values goes from 0 -> 255 -> 0. - GRADIENT_DATA = ( - b"\x00\x00\x00\x07\x07\x07\x0f\x0f\x0f\x17\x17\x17\x1f\x1f\x1f" - b"'''///777???GGGOOOWWW___gggooowww\x7f\x7f\x7f\x87\x87\x87" - b"\x8f\x8f\x8f\x97\x97\x97\x9f\x9f\x9f\xa7\xa7\xa7\xaf\xaf\xaf" - b"\xb7\xb7\xb7\xbf\xbf\xbf\xc7\xc7\xc7\xcf\xcf\xcf\xd7\xd7\xd7" - b"\xdf\xdf\xdf\xe7\xe7\xe7\xef\xef\xef\xf7\xf7\xf7\xff\xff\xff" - b"\xf6\xf6\xf6\xee\xee\xee\xe6\xe6\xe6\xde\xde\xde\xd5\xd5\xd5" - b"\xcd\xcd\xcd\xc5\xc5\xc5\xbd\xbd\xbd\xb4\xb4\xb4\xac\xac\xac" - b"\xa4\xa4\xa4\x9c\x9c\x9c\x94\x94\x94\x8b\x8b\x8b\x83\x83\x83" - b"{{{sssjjjbbbZZZRRRJJJAAA999111))) \x18\x18\x18\x10\x10\x10" - b"\x08\x08\x08\x00\x00\x00") - - def create_drawings(self): - from kivy.graphics import Line, RenderContext - - # very first time, create a texture for the shader - if not hasattr(SmoothLinePlot, '_texture'): - tex = Texture.create(size=(1, 64), colorfmt='rgb') - tex.add_reload_observer(SmoothLinePlot._smooth_reload_observer) - SmoothLinePlot._texture = tex - SmoothLinePlot._smooth_reload_observer(tex) - - self._grc = RenderContext( - fs=SmoothLinePlot.SMOOTH_FS, - use_parent_modelview=True, - use_parent_projection=True) - with self._grc: - self._gcolor = Color(*self.color) - self._gline = Line( - points=[], cap='none', width=2., - texture=SmoothLinePlot._texture) - - return [self._grc] - - @staticmethod - def _smooth_reload_observer(texture): - texture.blit_buffer(SmoothLinePlot.GRADIENT_DATA, colorfmt="rgb") - - def draw(self, *args): - super(SmoothLinePlot, self).draw(*args) - # flatten the list - points = [] - for x, y in self.iterate_points(): - points += [x, y] - self._gline.points = points - - -class ContourPlot(Plot): - """ - ContourPlot visualizes 3 dimensional data as an intensity map image. - The user must first specify 'xrange' and 'yrange' (tuples of min,max) and - then 'data', the intensity values. - `data`, is a MxN matrix, where the first dimension of size M specifies the - `y` values, and the second dimension of size N specifies the `x` values. - Axis Y and X values are assumed to be linearly spaced values from - xrange/yrange and the dimensions of 'data', `MxN`, respectively. - The color values are automatically scaled to the min and max z range of the - data set. - """ - _image = ObjectProperty(None) - data = ObjectProperty(None, force_dispatch=True) - xrange = ListProperty([0, 100]) - yrange = ListProperty([0, 100]) - - def __init__(self, **kwargs): - super(ContourPlot, self).__init__(**kwargs) - self.bind(data=self.ask_draw, xrange=self.ask_draw, - yrange=self.ask_draw) - - def create_drawings(self): - self._image = Rectangle() - self._color = Color([1, 1, 1, 1]) - self.bind( - color=lambda instr, value: setattr(self._color, 'rgba', value)) - return [self._color, self._image] - - def draw(self, *args): - super(ContourPlot, self).draw(*args) - data = self.data - xdim, ydim = data.shape - - # Find the minimum and maximum z values - zmax = data.max() - zmin = data.min() - rgb_scale_factor = 1.0 / (zmax - zmin) * 255 - # Scale the z values into RGB data - buf = np.array(data, dtype=float, copy=True) - np.subtract(buf, zmin, out=buf) - np.multiply(buf, rgb_scale_factor, out=buf) - # Duplicate into 3 dimensions (RGB) and convert to byte array - buf = np.asarray(buf, dtype=np.uint8) - buf = np.expand_dims(buf, axis=2) - buf = np.concatenate((buf, buf, buf), axis=2) - buf = np.reshape(buf, (xdim, ydim, 3)) - - charbuf = bytearray(np.reshape(buf, (buf.size))) - self._texture = Texture.create(size=(xdim, ydim), colorfmt='rgb') - self._texture.blit_buffer(charbuf, colorfmt='rgb', bufferfmt='ubyte') - image = self._image - image.texture = self._texture - - x_px = self.x_px() - y_px = self.y_px() - bl = x_px(self.xrange[0]), y_px(self.yrange[0]) - tr = x_px(self.xrange[1]), y_px(self.yrange[1]) - image.pos = bl - w = tr[0] - bl[0] - h = tr[1] - bl[1] - image.size = (w, h) - - -class BarPlot(Plot): - '''BarPlot class which displays a bar graph. - ''' - - bar_width = NumericProperty(1) - bar_spacing = NumericProperty(1.) - graph = ObjectProperty(allownone=True) - - def __init__(self, *ar, **kw): - super(BarPlot, self).__init__(*ar, **kw) - self.bind(bar_width=self.ask_draw) - self.bind(points=self.update_bar_width) - self.bind(graph=self.update_bar_width) - - def update_bar_width(self, *ar): - if not self.graph: - return - if len(self.points) < 2: - return - if self.graph.xmax == self.graph.xmin: - return - - point_width = ( - len(self.points) * - float(abs(self.graph.xmax) + abs(self.graph.xmin)) / - float(abs(max(self.points)[0]) + abs(min(self.points)[0]))) - - if not self.points: - self.bar_width = 1 - else: - self.bar_width = ( - (self.graph.width - self.graph.padding) / - point_width * self.bar_spacing) - - def create_drawings(self): - self._color = Color(*self.color) - self._mesh = Mesh() - self.bind( - color=lambda instr, value: setattr(self._color, 'rgba', value)) - return [self._color, self._mesh] - - def draw(self, *args): - super(BarPlot, self).draw(*args) - points = self.points - - # The mesh only supports (2^16) - 1 indices, so... - if len(points) * 6 > 65535: - Logger.error( - "BarPlot: cannot support more than 10922 points. " - "Ignoring extra points.") - points = points[:10922] - - point_len = len(points) - mesh = self._mesh - mesh.mode = 'triangles' - vert = mesh.vertices - ind = mesh.indices - diff = len(points) * 6 - len(vert) // 4 - if diff < 0: - del vert[24 * point_len:] - del ind[point_len:] - elif diff > 0: - ind.extend(range(len(ind), len(ind) + diff)) - vert.extend([0] * (diff * 4)) - - bounds = self.get_px_bounds() - x_px = self.x_px() - y_px = self.y_px() - ymin = y_px(0) - - bar_width = self.bar_width - if bar_width < 0: - bar_width = x_px(bar_width) - bounds["xmin"] - - for k in range(point_len): - p = points[k] - x1 = x_px(p[0]) - x2 = x1 + bar_width - y1 = ymin - y2 = y_px(p[1]) - - idx = k * 24 - # first triangle - vert[idx] = x1 - vert[idx + 1] = y2 - vert[idx + 4] = x1 - vert[idx + 5] = y1 - vert[idx + 8] = x2 - vert[idx + 9] = y1 - # second triangle - vert[idx + 12] = x1 - vert[idx + 13] = y2 - vert[idx + 16] = x2 - vert[idx + 17] = y2 - vert[idx + 20] = x2 - vert[idx + 21] = y1 - mesh.vertices = vert - - def _unbind_graph(self, graph): - graph.unbind(width=self.update_bar_width, - xmin=self.update_bar_width, - ymin=self.update_bar_width) - - def bind_to_graph(self, graph): - old_graph = self.graph - - if old_graph: - # unbind from the old one - self._unbind_graph(old_graph) - - # bind to the new one - self.graph = graph - graph.bind(width=self.update_bar_width, - xmin=self.update_bar_width, - ymin=self.update_bar_width) - - def unbind_from_graph(self): - if self.graph: - self._unbind_graph(self.graph) - - -class HBar(MeshLinePlot): - '''HBar draw horizontal bar on all the Y points provided - ''' - - def plot_mesh(self, *args): - points = self.points - mesh, vert, ind = self.set_mesh_size(len(points) * 2) - mesh.mode = "lines" - - bounds = self.get_px_bounds() - px_xmin = bounds["xmin"] - px_xmax = bounds["xmax"] - y_px = self.y_px() - for k, y in enumerate(points): - y = y_px(y) - vert[k * 8] = px_xmin - vert[k * 8 + 1] = y - vert[k * 8 + 4] = px_xmax - vert[k * 8 + 5] = y - mesh.vertices = vert - - -class VBar(MeshLinePlot): - '''VBar draw vertical bar on all the X points provided - ''' - - def plot_mesh(self, *args): - points = self.points - mesh, vert, ind = self.set_mesh_size(len(points) * 2) - mesh.mode = "lines" - - bounds = self.get_px_bounds() - px_ymin = bounds["ymin"] - px_ymax = bounds["ymax"] - x_px = self.x_px() - for k, x in enumerate(points): - x = x_px(x) - vert[k * 8] = x - vert[k * 8 + 1] = px_ymin - vert[k * 8 + 4] = x - vert[k * 8 + 5] = px_ymax - mesh.vertices = vert - - -class ScatterPlot(Plot): - """ - ScatterPlot draws using a standard Point object. - The pointsize can be controlled with :attr:`point_size`. - - >>> plot = ScatterPlot(color=[1, 0, 0, 1], point_size=5) - """ - - point_size = NumericProperty(1) - """The point size of the scatter points. Defaults to 1. - """ - - def create_drawings(self): - from kivy.graphics import Point, RenderContext - - self._points_context = RenderContext( - use_parent_modelview=True, - use_parent_projection=True) - with self._points_context: - self._gcolor = Color(*self.color) - self._gpts = Point(points=[], pointsize=self.point_size) - - return [self._points_context] - - def draw(self, *args): - super(ScatterPlot, self).draw(*args) - # flatten the list - self._gpts.points = list(chain(*self.iterate_points())) - - def on_point_size(self, *largs): - if hasattr(self, "_gpts"): - self._gpts.pointsize = self.point_size - - -class PointPlot(Plot): - '''Displays a set of points. - ''' - - point_size = NumericProperty(1) - ''' - Defaults to 1. - ''' - - _color = None - - _point = None - - def __init__(self, **kwargs): - super(PointPlot, self).__init__(**kwargs) - - def update_size(*largs): - if self._point: - self._point.pointsize = self.point_size - self.fbind('point_size', update_size) - - def update_color(*largs): - if self._color: - self._color.rgba = self.color - self.fbind('color', update_color) - - def create_drawings(self): - self._color = Color(*self.color) - self._point = Point(pointsize=self.point_size) - return [self._color, self._point] - - def draw(self, *args): - super(PointPlot, self).draw(*args) - self._point.points = [v for p in self.iterate_points() for v in p] - - -if __name__ == '__main__': - import itertools - from math import sin, cos, pi - from random import randrange - from kivy.utils import get_color_from_hex as rgb - from kivy.uix.boxlayout import BoxLayout - from kivy.app import App - - class TestApp(App): - - def build(self): - b = BoxLayout(orientation='vertical') - # example of a custom theme - colors = itertools.cycle([ - rgb('7dac9f'), rgb('dc7062'), rgb('66a8d4'), rgb('e5b060')]) - graph_theme = { - 'label_options': { - 'color': rgb('444444'), # color of tick labels and titles - 'bold': True}, - 'background_color': rgb('f8f8f2'), # canvas background color - 'tick_color': rgb('808080'), # ticks and grid - 'border_color': rgb('808080')} # border drawn around each graph - - graph = Graph( - xlabel='Cheese', - ylabel='Apples', - x_ticks_minor=5, - x_ticks_major=25, - y_ticks_major=1, - y_grid_label=True, - x_grid_label=True, - padding=5, - xlog=False, - ylog=False, - x_grid=True, - y_grid=True, - xmin=-50, - xmax=50, - ymin=-1, - ymax=1, - **graph_theme) - - plot = SmoothLinePlot(color=next(colors)) - plot.points = [(x / 10., sin(x / 50.)) for x in range(-500, 501)] - # for efficiency, the x range matches xmin, xmax - graph.add_plot(plot) - - plot = MeshLinePlot(color=next(colors)) - plot.points = [(x / 10., cos(x / 50.)) for x in range(-500, 501)] - graph.add_plot(plot) - self.plot = plot # this is the moving graph, so keep a reference - - plot = MeshStemPlot(color=next(colors)) - graph.add_plot(plot) - plot.points = [(x, x / 50.) for x in range(-50, 51)] - - plot = BarPlot(color=next(colors), bar_spacing=.72) - graph.add_plot(plot) - plot.bind_to_graph(graph) - plot.points = [(x, .1 + randrange(10) / 10.) for x in range(-50, 1)] - - Clock.schedule_interval(self.update_points, 1 / 60.) - - graph2 = Graph( - xlabel='Position (m)', - ylabel='Time (s)', - x_ticks_minor=0, - x_ticks_major=1, - y_ticks_major=10, - y_grid_label=True, - x_grid_label=True, - padding=5, - xlog=False, - ylog=False, - xmin=0, - ymin=0, - **graph_theme) - b.add_widget(graph) - - if np is not None: - (xbounds, ybounds, data) = self.make_contour_data() - # This is required to fit the graph to the data extents - graph2.xmin, graph2.xmax = xbounds - graph2.ymin, graph2.ymax = ybounds - - plot = ContourPlot() - plot.data = data - plot.xrange = xbounds - plot.yrange = ybounds - plot.color = [1, 0.7, 0.2, 1] - graph2.add_plot(plot) - - b.add_widget(graph2) - self.contourplot = plot - - Clock.schedule_interval(self.update_contour, 1 / 60.) - - # Test the scatter plot - plot = ScatterPlot(color=next(colors), point_size=5) - graph.add_plot(plot) - plot.points = [(x, .1 + randrange(10) / 10.) for x in range(-50, 1)] - return b - - def make_contour_data(self, ts=0): - omega = 2 * pi / 30 - k = (2 * pi) / 2.0 - - ts = sin(ts * 2) + 1.5 # emperically determined 'pretty' values - npoints = 100 - data = np.ones((npoints, npoints)) - - position = [ii * 0.1 for ii in range(npoints)] - time = [(ii % 100) * 0.6 for ii in range(npoints)] - - for ii, t in enumerate(time): - for jj, x in enumerate(position): - data[ii, jj] = sin( - k * x + omega * t) + sin(-k * x + omega * t) / ts - return (0, max(position)), (0, max(time)), data - - def update_points(self, *args): - self.plot.points = [ - (x / 10., cos(Clock.get_time() + x / 50.)) - for x in range(-500, 501)] - - def update_contour(self, *args): - _, _, self.contourplot.data[:] = self.make_contour_data( - Clock.get_time()) - # this does not trigger an update, because we replace the - # values of the arry and do not change the object. - # However, we cannot do "...data = make_contour_data()" as - # kivy will try to check for the identity of the new and - # old values. In numpy, 'nd1 == nd2' leads to an error - # (you have to use np.all). Ideally, property should be patched - # for this. - self.contourplot.ask_draw() - - TestApp().run() diff --git a/build/lib/kivycharts/graph/polygon.py b/build/lib/kivycharts/graph/polygon.py deleted file mode 100644 index 33df3f5..0000000 --- a/build/lib/kivycharts/graph/polygon.py +++ /dev/null @@ -1,10 +0,0 @@ -# polygon.py -""" -名称 类型 默认值 描述 -opts Object 配置项,继承得到的配置项参见 zrender.Displayable。 -opts.shape Object 形状属性。 -opts.shape.points number[][] 0 每个元素是一个横纵坐标的数组。 -opts.shape.smooth number|string 0 圆滑程度,取值范围为 0 到 1 之间的数字,0 表示不圆滑;也可以是特殊字符串 'spline' 表示用 Catmull-Rom spline 插值算法,否则默认用贝塞尔曲线插值算法。 -opts.shape.smoothConstraint number[][] 0 将计算出来的控制点约束在一个包围盒内。比如 [[0, 0], [100, 100]],这个包围盒会与整个折线的包围盒做一个并集用来约束控制点。 -""" - diff --git a/build/lib/kivycharts/graph/polyline.py b/build/lib/kivycharts/graph/polyline.py deleted file mode 100644 index c72f638..0000000 --- a/build/lib/kivycharts/graph/polyline.py +++ /dev/null @@ -1,10 +0,0 @@ -# polyline.py -""" -名称 类型 默认值 描述 -opts Object 配置项,继承得到的配置项参见 zrender.Displayable。 -opts.shape Object 形状属性。 -opts.shape.points number[][] 0 每个元素是一个横纵坐标的数组。 -opts.shape.smooth number|string 0 圆滑程度,取值范围为 0 到 1 之间的数字,0 表示不圆滑;也可以是特殊字符串 'spline' 表示用 Catmull-Rom spline 插值算法,否则默认用贝塞尔曲线插值算法。 -opts.shape.smoothConstraint number[][] 0 将计算出来的控制点约束在一个包围盒内。比如 [[0, 0], [100, 100]],这个包围盒会与整个折线的包围盒做一个并集用来约束控制点。 -""" - diff --git a/build/lib/kivycharts/graph/rectangle.py b/build/lib/kivycharts/graph/rectangle.py deleted file mode 100644 index 36e3083..0000000 --- a/build/lib/kivycharts/graph/rectangle.py +++ /dev/null @@ -1,12 +0,0 @@ -# rectangle.py -""" -名称 类型 默认值 描述 -opts Object 配置项,继承得到的配置项参见 zrender.Displayable。 -opts.shape Object 形状属性。 -opts.shape.r number|number[] 0 用于创建圆角矩形。左上、右上、右下、左下角的半径依次为 r1、 r2、 r3、 r4。r 缩写为 1 相当于 [1, 1, 1, 1];r 缩写为 [1] 相当于 [1, 1, 1, 1];r 缩写为 [1, 2] 相当于 [1, 2, 1, 2];r 缩写为 [1, 2, 3] 相当于 [1, 2, 3, 2]。 -opts.shape.x number 0 左上角的横坐标。 -opts.shape.y number 0 左上角的纵坐标。 -opts.shape.width number 0 宽度。 -opts.shape.height number 0 高度。 -""" - diff --git a/build/lib/kivycharts/graph/rose.py b/build/lib/kivycharts/graph/rose.py deleted file mode 100644 index b573447..0000000 --- a/build/lib/kivycharts/graph/rose.py +++ /dev/null @@ -1,12 +0,0 @@ -# rose.py -""" -名称 类型 默认值 描述 -opts Object 配置项,继承得到的配置项参见 zrender.Displayable。 -opts.shape Object 形状属性。 -opts.shape.cx number 0 圆心横坐标。 -opts.shape.cy number 0 圆心纵坐标。 -opts.shape.r number[] [] 半径。 -opts.shape.k number 0 玫瑰线参数,参见 wiki。 -opts.shape.n number 1 花瓣数。 -""" - diff --git a/build/lib/kivycharts/graph/sector.py b/build/lib/kivycharts/graph/sector.py deleted file mode 100644 index e91f888..0000000 --- a/build/lib/kivycharts/graph/sector.py +++ /dev/null @@ -1,14 +0,0 @@ -# sector.py -""" -名称 类型 默认值 描述 -opts Object 配置项,继承得到的配置项参见 zrender.Displayable。 -opts.shape Object 形状属性。 -opts.shape.cx number 0 圆心横坐标。 -opts.shape.cy number 0 圆心纵坐标。 -opts.shape.r number 0 半径。 -opts.shape.r0 number 0 内半径。 -opts.shape.startAngle number 0 起始弧度。 -opts.shape.endAngle number Math.PI * 2 终止弧度。 -opts.shape.clockwise boolean true 顺时针方向。 -""" - diff --git a/build/lib/kivycharts/graph/star.py b/build/lib/kivycharts/graph/star.py deleted file mode 100644 index e788dd4..0000000 --- a/build/lib/kivycharts/graph/star.py +++ /dev/null @@ -1,13 +0,0 @@ -# star.py -""" - -名称 类型 默认值 描述 -opts Object 配置项,继承得到的配置项参见 zrender.Displayable。 -opts.shape Object 形状属性。 -opts.shape.cx number 0 圆心横坐标。 -opts.shape.cy number 0 圆心纵坐标。 -opts.shape.n number 3 瓣数,如 n 等于 5 时,是我们熟悉的五角星。 -opts.shape.r number 0 半径。 -opts.shape.r0 number 0 内半径。 -""" - diff --git a/build/lib/kivycharts/graph/text.py b/build/lib/kivycharts/graph/text.py deleted file mode 100644 index 9c24a40..0000000 --- a/build/lib/kivycharts/graph/text.py +++ /dev/null @@ -1,9 +0,0 @@ -# text.py -""" -名称 类型 默认值 描述 -opts Object 配置项,继承得到的配置项参见 zrender.Displayable。 -?text -font_size -? -""" - diff --git a/build/lib/kivycharts/graph/touchoid.py b/build/lib/kivycharts/graph/touchoid.py deleted file mode 100644 index 405cd90..0000000 --- a/build/lib/kivycharts/graph/touchoid.py +++ /dev/null @@ -1,12 +0,0 @@ -# touchoid.py -""" -名称 类型 默认值 描述 -opts Object 配置项,继承得到的配置项参见 zrender.Displayable。 -opts.shape Object 形状属性。 -opts.shape.cx number 0 圆心横坐标。 -opts.shape.cy number 0 圆心纵坐标。 -opts.shape.r number 0 半径。 -opts.shape.r0 number 0 内半径。 -opts.shape.d number 0 内外旋轮曲线参数,参见 wiki。 -opts.shape.n location 'out' out 或 in,表示曲线在内部还是外部。 -""" diff --git a/build/lib/kivycharts/line.py b/build/lib/kivycharts/line.py deleted file mode 100644 index 2007b2c..0000000 --- a/build/lib/kivycharts/line.py +++ /dev/null @@ -1,130 +0,0 @@ -from kivy.properties import DictProperty, ListProperty, \ - StringProperty, NumericProperty -from kivy.factory import Factory -from kivy.utils import get_color_from_hex as rgb - -from kivyblocks.scrollpanel import ScrollPanel -from kivyblocks.utils import SUPER, CSize -from kivyblocks.threadcall import HttpClient -from kivyblocks.baseWidget import VBox - -from .graph import Graph -from .graph import Plot, LinePlot, SmoothLinePlot - -build_plots = { - 'fold-line':LinePlot, - 'smooth-line':SmoothLinePlot -} - -class LineChart(VBox): - """ - series = [ - { - yfield:xxxx, - charttype:smooth-line, fold-line, ... - color: - } - ] - """ - dataurl = StringProperty(None) - params = DictProperty({}) - method = StringProperty('get') - xlabel = StringProperty(None) - ylabel = StringProperty(None) - xfield = StringProperty(None) - x_ticks_angle = NumericProperty(45) - series = ListProperty(None) - data = ListProperty(None) - def __init__(self, **kw): - self.graph = None - SUPER(LineChart, self, kw) - - def on_dataurl(self, o, url=None): - if not self.dataurl: - return - hc = HttpClient() - x = hc(self.dataurl, - method=self.method, - params=self.params) - self.data = x['rows'] - self.url_call = True - - def on_params(self, o, params=None): - if not self.url_call: - return - self.on_dataurl(None, None) - - def build_plot(self, serie): - type = serie.get('charttype', 'smooth-line') - plotKlass = build_plots.get(type) - p = plotKlass(color=serie['color']) - return p - - def on_data(self, o, data=None): - graph_theme = { - 'label_options': { - 'color': rgb('444444'), # color of tick labels and titles - 'bold': True}, - 'background_color': rgb('f8f8f2'), # canvas background color - 'tick_color': rgb('808080'), # ticks and grid - 'border_color': rgb('808080')} # border drawn around each graph - - xcnt = len(self.data) - ymin, ymax = 9999999999999999, 0 - xlabel_text = [ r.get(self.xfield) for r in self.data ] - xlabel_text.insert(0,'0') - for s in self.series: - points = [ (i, r.get(s['yfield'])) \ - for i,r in enumerate(self.data) ] - min1 = min([p[1] for p in points]) - max1 = max([p[1] for p in points]) - if max1 >ymax: - ymax = max1 - if min1 < ymin: - ymin = min1 - s['points'] = points - yadd = int((ymax - ymin) / 8) - ymin = int(ymin - yadd) - ymax = int(ymax + yadd) - y_ticks_major = int((ymax-ymin)/4) - x_ticks_major = int(xcnt/10) - xlabel = self.xlabel or self.xfield - ylabel = self.ylabel - - if not self.graph: - self.graph = Graph( - xlabel = self.xlabel or self.xfield, - ylabel = self.ylabel, - y_ticks_major = int((ymax-ymin)/4), - x_ticks_major = int(xcnt/10), - ymin = ymin, - ymax = ymax, - xmin = 0, - xmax = xcnt, - y_grid_label=True, - x_grid_label=True, - xlog = False, - ylog = False, - x_grid = True, - y_grid = True, - padding = 5, - **graph_theme) - self.add_widget(self.graph) - else: - self.graph.xlabel = xlabel - self.graph.ylabel = ylabel - self.graph.ymin = ymin - self.graph.ymax = ymax - self.graph.xmin = 0 - self.graph.xmax = xmax - self.graph.y_ticks_major = y_ticks_major - self.graph.x_ticks_major = x_ticks_major - - for s in self.series: - s['plot'] = self.build_plot(s) - s['plot'].points = s['points'] - self.graph.add_plot(s['plot']) - self.graph.x_grid_texts = xlabel_text - self.graph.x_ticks_angle = self.x_ticks_angle - -Factory.register('LineChart', LineChart) diff --git a/build/lib/kivycharts/pie.py b/build/lib/kivycharts/pie.py deleted file mode 100644 index fc11ce8..0000000 --- a/build/lib/kivycharts/pie.py +++ /dev/null @@ -1,66 +0,0 @@ - -import math -import numpy as np -from kivy.uix.widget import Widget -from kivyblocks.charts.geometry import EllipseUtils - -class PiePart(ChartPart): - def collide_point(self,x,y): - if not self.isInSideEllipse(x,y): - return False - return self.isInSidePart(x,y) - - def isInSideEllipse(self,x,y): - a = self.width / 2 - b = self.height / 2 - v = x ** x / a ** a + y**y / b ** b - if v <= 1: - return True - return False - -class Pie(Widget): - def __init__(self,**options): - """ - options={ - width, - height, - title, - keyField, - valueField, - data=[ - { - name, - value, - },{ - } - ] - } - """ - self.options = options - self.initflag = False - super().__init__() - self.bind(size=self.onSize,pos=self.onSize) - - def data2pie(self): - data = self.options.get('data',[]) - kvs = [ [i[self.options['keyField']],i[self.options['valueField']]] for i in data ] - total = sum([i[1] for i in kvs ]) - start_degree = 0 - cnt = len(kvs) - color1='8833ee' - color2='ed8234' - colors = divideColor(color1,color2,cnt-1) - self.canvas.clear() - for i in range(cnt): - degree = start_degree + 360 * kvs[i][1] / total - with self.canvas: - Color(*colors[i]) - Ellipse(pos=self.pos, - size=self.size, - angle_start=start_degree, - angle_end= degree) - start_degree = degree - - def onSize(self,o,v): - self.data2pie() - diff --git a/dist/kivycharts-0.0.5-py3.9.egg b/dist/kivycharts-0.0.5-py3.9.egg deleted file mode 100644 index 29af3d877d73d5131fa35f17ade76dd59310afec..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 71528 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 U009vM{y_oDInrRjYzpN60K%qLCjbBd diff --git a/kivycharts.egg-info/PKG-INFO b/kivycharts.egg-info/PKG-INFO deleted file mode 100644 index e80ffd3..0000000 --- a/kivycharts.egg-info/PKG-INFO +++ /dev/null @@ -1,14 +0,0 @@ -Metadata-Version: 1.1 -Name: kivycharts -Version: 0.0.5 -Summary: kivy charts is a tool to build kivy data plot -Home-page: UNKNOWN -Author: yumoqing -Author-email: yumoqing@icloud.com -License: UNKNOWN -Description: UNKNOWN -Platform: any -Classifier: Development Status :: 3 - Alpha -Classifier: Operating System :: OS Independent -Classifier: Programming Language :: Python :: 3.5 -Classifier: Topic :: Software Development :: Libraries :: Python Modules diff --git a/kivycharts.egg-info/SOURCES.txt b/kivycharts.egg-info/SOURCES.txt deleted file mode 100644 index 0297f40..0000000 --- a/kivycharts.egg-info/SOURCES.txt +++ /dev/null @@ -1,37 +0,0 @@ -README.md -setup.py -kivycharts/__init__.py -kivycharts/bar.py -kivycharts/chart.py -kivycharts/geometry.py -kivycharts/line.py -kivycharts/pie.py -kivycharts.egg-info/PKG-INFO -kivycharts.egg-info/SOURCES.txt -kivycharts.egg-info/dependency_links.txt -kivycharts.egg-info/requires.txt -kivycharts.egg-info/top_level.txt -kivycharts/graph/__init__.py -kivycharts/graph/_version.py -kivycharts/graph/arc.py -kivycharts/graph/base.py -kivycharts/graph/beziercurve.py -kivycharts/graph/circle.py -kivycharts/graph/compoundpath.py -kivycharts/graph/displayable.py -kivycharts/graph/droplet.py -kivycharts/graph/ellipse.py -kivycharts/graph/heart.py -kivycharts/graph/image.py -kivycharts/graph/line.py -kivycharts/graph/lsogon.py -kivycharts/graph/path.py -kivycharts/graph/plot.py -kivycharts/graph/polygon.py -kivycharts/graph/polyline.py -kivycharts/graph/rectangle.py -kivycharts/graph/rose.py -kivycharts/graph/sector.py -kivycharts/graph/star.py -kivycharts/graph/text.py -kivycharts/graph/touchoid.py \ No newline at end of file diff --git a/kivycharts.egg-info/dependency_links.txt b/kivycharts.egg-info/dependency_links.txt deleted file mode 100644 index 8b13789..0000000 --- a/kivycharts.egg-info/dependency_links.txt +++ /dev/null @@ -1 +0,0 @@ - diff --git a/kivycharts.egg-info/requires.txt b/kivycharts.egg-info/requires.txt deleted file mode 100644 index 612fb27..0000000 --- a/kivycharts.egg-info/requires.txt +++ /dev/null @@ -1,10 +0,0 @@ -jinja2 -kivy -ffpyplayer -qrcode -pillow -requests -plyer -python-osc -bs4 -lxml diff --git a/kivycharts.egg-info/top_level.txt b/kivycharts.egg-info/top_level.txt deleted file mode 100644 index cdf55ae..0000000 --- a/kivycharts.egg-info/top_level.txt +++ /dev/null @@ -1 +0,0 @@ -kivycharts