'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, \
|
from .widgetExt.inputext import FloatInput,IntegerInput, \
|
||||||
StrInput,SelectInput, BoolInput, AmountInput
|
StrInput,SelectInput, BoolInput, AmountInput
|
||||||
from .widgetExt.messager import Messager
|
from .widgetExt.messager import Messager
|
||||||
|
from .charts.bar import Bar
|
||||||
|
|
||||||
if platform == 'android':
|
if platform == 'android':
|
||||||
from .widgetExt.phonebutton import PhoneButton
|
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):
|
def toArrayColor(color):
|
||||||
if isinstance(color,str):
|
if isinstance(color,str):
|
||||||
sc = color
|
color = get_color_from_hex(color)
|
||||||
return get_color_from_hex(sc)
|
if len(color) == 3:
|
||||||
|
color.append(1)
|
||||||
return color
|
return color
|
||||||
|
|
||||||
def distance(color1,color2):
|
def distance(color1,color2):
|
||||||
@ -18,6 +19,7 @@ def distance(color1,color2):
|
|||||||
math.pow(abs(color2[1] - color1[1]),2) + \
|
math.pow(abs(color2[1] - color1[1]),2) + \
|
||||||
math.pow(abs(color2[2] - color1[2]),2)
|
math.pow(abs(color2[2] - color1[2]),2)
|
||||||
d = math.sqrt(x)
|
d = math.sqrt(x)
|
||||||
|
return d
|
||||||
|
|
||||||
def reverseColor(color):
|
def reverseColor(color):
|
||||||
center = [0.5,0.5,0.5]
|
center = [0.5,0.5,0.5]
|
||||||
@ -28,8 +30,8 @@ def reverseColor(color):
|
|||||||
point= [0,0,0]
|
point= [0,0,0]
|
||||||
for i in range(3):
|
for i in range(3):
|
||||||
m = color[i] - center[i]
|
m = color[i] - center[i]
|
||||||
m1 = m * d1 / d
|
m1 = abs(m) * d1 / d
|
||||||
point[i] = color[i] - m1 if m < 0? color[i] + m1
|
point[i] = color[i] - m1 if m < 0 else color[i] + m1
|
||||||
return point
|
return point
|
||||||
|
|
||||||
#DIVIDE
|
#DIVIDE
|
||||||
@ -50,121 +52,3 @@ def divide(point1,point2,divide_cnt):
|
|||||||
ps.append(p)
|
ps.append(p)
|
||||||
ps.append(point2)
|
ps.append(point2)
|
||||||
return ps
|
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