'bugfix'
This commit is contained in:
parent
192ceb7c68
commit
1cf4c0a421
@ -54,6 +54,7 @@ from .widgetExt.jsoncodeinput import JsonCodeInput
|
||||
from .widgetExt.inputext import FloatInput,IntegerInput, \
|
||||
StrInput,SelectInput, BoolInput, AmountInput
|
||||
from .widgetExt.messager import Messager
|
||||
from .charts.bar import Bar
|
||||
|
||||
if platform == 'android':
|
||||
from .widgetExt.phonebutton import PhoneButton
|
||||
|
85
kivyblocks/charts/bar.py
Normal file
85
kivyblocks/charts/bar.py
Normal file
@ -0,0 +1,85 @@
|
||||
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)
|
||||
|
79
kivyblocks/charts/chart.py
Normal file
79
kivyblocks/charts/chart.py
Normal file
@ -0,0 +1,79 @@
|
||||
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()
|
81
kivyblocks/charts/geometry.py
Normal file
81
kivyblocks/charts/geometry.py
Normal file
@ -0,0 +1,81 @@
|
||||
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
|
66
kivyblocks/charts/pie.py
Normal file
66
kivyblocks/charts/pie.py
Normal file
@ -0,0 +1,66 @@
|
||||
|
||||
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()
|
||||
|
@ -7,8 +7,9 @@ import math
|
||||
|
||||
def toArrayColor(color):
|
||||
if isinstance(color,str):
|
||||
sc = color
|
||||
return get_color_from_hex(sc)
|
||||
color = get_color_from_hex(color)
|
||||
if len(color) == 3:
|
||||
color.append(1)
|
||||
return color
|
||||
|
||||
def distance(color1,color2):
|
||||
@ -18,6 +19,7 @@ def distance(color1,color2):
|
||||
math.pow(abs(color2[1] - color1[1]),2) + \
|
||||
math.pow(abs(color2[2] - color1[2]),2)
|
||||
d = math.sqrt(x)
|
||||
return d
|
||||
|
||||
def reverseColor(color):
|
||||
center = [0.5,0.5,0.5]
|
||||
@ -28,8 +30,8 @@ def reverseColor(color):
|
||||
point= [0,0,0]
|
||||
for i in range(3):
|
||||
m = color[i] - center[i]
|
||||
m1 = m * d1 / d
|
||||
point[i] = color[i] - m1 if m < 0? color[i] + m1
|
||||
m1 = abs(m) * d1 / d
|
||||
point[i] = color[i] - m1 if m < 0 else color[i] + m1
|
||||
return point
|
||||
|
||||
#DIVIDE
|
||||
@ -50,121 +52,3 @@ def divide(point1,point2,divide_cnt):
|
||||
ps.append(p)
|
||||
ps.append(point2)
|
||||
return ps
|
||||
|
||||
class Bar(Widget):
|
||||
"""
|
||||
a BAR class
|
||||
"""
|
||||
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 data2bar(self):
|
||||
data = self.options.get('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)
|
||||
self.canvas.clear()
|
||||
for i in range(cnt):
|
||||
h = self.height * kvs[i][1] / m
|
||||
with self.canvas:
|
||||
Color(*colors[i])
|
||||
Rectangle(pos=points[i],
|
||||
size=(points[i+1][0] - points[i][0],h))
|
||||
|
||||
def onSize(self,o,v):
|
||||
self.build()
|
||||
|
||||
def build(self):
|
||||
self.initflag = True
|
||||
self.data2bar()
|
||||
|
||||
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()
|
||||
|
||||
class MyApp(App):
|
||||
def build(self):
|
||||
d = {
|
||||
"keyField":"name",
|
||||
"valueField":"value",
|
||||
"data":[
|
||||
{'name':'you','value':102.0},
|
||||
{'name':'me','value':42.0},
|
||||
{'name':'she','value':92.0},
|
||||
{'name':'she','value':52.0},
|
||||
{'name':'she','value':42.0},
|
||||
{'name':'she','value':82.0},
|
||||
{'name':'she','value':17.0},
|
||||
{'name':'he','value':77.0}
|
||||
]
|
||||
}
|
||||
|
||||
return Pie(**d)
|
||||
|
||||
if __name__ == '__main__':
|
||||
MyApp().run()
|
||||
|
Loading…
Reference in New Issue
Block a user