This commit is contained in:
yumoqing 2020-04-18 11:49:13 +08:00
parent 211fee42ec
commit 2490b46b46
4 changed files with 266 additions and 77 deletions

View File

@ -3,6 +3,7 @@ from kivy.utils import platform
from kivy.uix.button import Button, ButtonBehavior from kivy.uix.button import Button, ButtonBehavior
from kivy.uix.accordion import Accordion,AccordionItem from kivy.uix.accordion import Accordion,AccordionItem
from kivy.uix.label import Label from kivy.uix.label import Label
from kivy.event import EventDispatcher
from kivy.uix.actionbar import ActionBar,ActionView,ActionPrevious,ActionItem,ActionButton from kivy.uix.actionbar import ActionBar,ActionView,ActionPrevious,ActionItem,ActionButton
from kivy.uix.actionbar import ActionToggleButton, ActionCheck,ActionSeparator,ActionGroup from kivy.uix.actionbar import ActionToggleButton, ActionCheck,ActionSeparator,ActionGroup
@ -79,3 +80,38 @@ class PressableLabel(ButtonBehavior, Text):
def on_press(self): def on_press(self):
pass pass
class HTTPDataHandler(EventDispatcher):
def __init__(self, url,method='GET',params={},headers={}):
EventDispatcher.__init__()
self.opts = opts
self.url = url
self.method = method
self.params = params
self.headers = headers
self.hc = App.get_running_app().hc
self.register_event_type('on_sucess')
self.register_event_type('on_error')
def on_success(self,o,v):
pass
def on_error(self,o,e):
pass
def onSuccess(self,o,v):
self.dispatch('on_sucess',v)
def onError(self,o,e):
self.dispatch('on_error',e)
def handle(self,params={},headers={}):
p = self.params
p.update(params)
h = self.headers
h.update(headers)
self.hc(self.url,method=self.method,
params=p,
headers=h,
callback=self.onSuccess,
errback=self.onError)

View File

@ -5,23 +5,23 @@ from kivy.properties import ListProperty
_logcnt = 0 _logcnt = 0
class BGColorBehavior(object): class BGColorBehavior(object):
bgcolor = ListProperty([]) bgcolor = ListProperty([])
def __init__(self, bgcolor=[1,0,0,1],**kwargs): def __init__(self, bgcolor=[0,0,0,1],**kwargs):
self.bgcolor = bgcolor self.bgcolor = bgcolor
self.bind(size=self.on_bgcolor,pos=self.on_bgcolor) self.bind(size=self.onSize_bgcolor_behavior,
pos=self.onSize_bgcolor_behavior)
self.on_bgcolor()
def onSize_bgcolor_behavior(self,o,v=None):
if not hasattr(self,'rect'):
self.on_bgcolor()
else:
self.rect.pos = o.pos
self.rect.size = o.size
def on_bgcolor(self,o=None,v=None): def on_bgcolor(self,o=None,v=None):
global _logcnt
Logger.info('bgcolorBehavior: on_bgcolor(),o=%s,v=%s,logcnt=%d',\
o.text if hasattr(o,'text') else o,v,_logcnt)
_logcnt += 1
x = abs(self.size[0] - 100.0)
y = abs(self.size[1] - 100.0)
if self.size[0] < 0.0001 or self.size[1] < 0.0001:
return
if x < 0.0001 and y < 0.0001:
return
if self.canvas: if self.canvas:
with self.canvas.before: with self.canvas.before:
Color(*self.bgcolor) Color(*self.bgcolor)
Rectangle(pos=self.pos, size=self.size) self.rect = Rectangle(pos=self.pos,
size=self.size)

View File

@ -1,5 +1,8 @@
import traceback
from kivy.app import App from kivy.app import App
from kivy.logger import logging from kivy.core.window import Window
from kivy.logger import Logger
from kivy.graphics import Color, Rectangle, Triangle from kivy.graphics import Color, Rectangle, Triangle
from kivy.uix.boxlayout import BoxLayout from kivy.uix.boxlayout import BoxLayout
from kivy.uix.label import Label from kivy.uix.label import Label
@ -8,9 +11,11 @@ from kivy.uix.button import ButtonBehavior
from kivyblocks.widgetExt import ScrollWidget from kivyblocks.widgetExt import ScrollWidget
from kivyblocks.utils import CSize from kivyblocks.utils import CSize
from appPublic.dictObject import DictObject from appPublic.dictObject import DictObject
from appPublic.jsonConfig import getConfig
from .baseWidget import PressableLabel from .baseWidget import PressableLabel
from .color_definitions import getColors from .color_definitions import getColors
from .bgcolorbehavior import BGColorBehavior from .bgcolorbehavior import BGColorBehavior
from .utils import alert,absurl
class EmptyBox(Label): class EmptyBox(Label):
def __init__(self,size_cnt=1): def __init__(self,size_cnt=1):
@ -80,8 +85,11 @@ class TreeNode(BoxLayout):
self.data = data self.data = data
self.content = None self.content = None
self.children_open = False self.children_open = False
self.subNodes = [] self.nodes = []
self.node_box = BoxLayout(orientation='horizontal',size_hint_y=None) self.initChildren = False
self.node_box = BoxLayout(orientation='horizontal',
spacing=CSize(0.5),
size_hint_y=None)
self.node_box1 = BoxLayout(orientation='horizontal') self.node_box1 = BoxLayout(orientation='horizontal')
n = data.get('children') n = data.get('children')
self.hasChildren_nodes = True if not n is None else False self.hasChildren_nodes = True if not n is None else False
@ -122,7 +130,7 @@ class TreeNode(BoxLayout):
self.node_box.width = width self.node_box.width = width
if self.node_box1.width < width: if self.node_box1.width < width:
self.node_box1.width = width self.node_box1.width = width
for n in self.subNodes: for n in self.nodes:
n.setMinWidth(width) n.setMinWidth(width)
def buildContent(self): def buildContent(self):
@ -134,28 +142,32 @@ class TreeNode(BoxLayout):
self.node_box.height = self.content.height self.node_box.height = self.content.height
self.node_box.width = self.trigger.width + \ self.node_box.width = self.trigger.width + \
self.content.width self.content.width
logging.info('Tree : content=(%d,%d),box=(%d,%d)', \ Logger.info('Tree : content=(%d,%d),box=(%d,%d)', \
self.content.width,self.content.height, self.content.width,self.content.height,
self.node_box.width,self.node_box.height) self.node_box.width,self.node_box.height)
def buildChildren(self): def buildChildren(self):
if self.initChildren == True:
return
if self.data.children is None: if self.data.children is None:
logging.info('Tree : is a leaf node') Logger.info('Tree : is a leaf node')
return return
if self.data.children == []: if self.data.children == []:
self.treeObj.getUrlData(self.addChildren,self.data) self.treeObj.getUrlData(self.addChildren,self.data)
return return
if len(self.subNodes) == 0: if len(self.nodes) == 0:
logging.info('Tree : add subnodes') Logger.info('Tree : add subnodes')
self.addChildren(self.data.children) self.addChildren(self.data.children)
else: else:
logging.info('Tree : not need add subnodes') Logger.info('Tree : not need add subnodes')
self.initChildren = True
def childrenSizeChange(self,tn,v): def childrenSizeChange(self,tn,v):
h = 0 h = 0
w = 0 w = 0
for n in self.subNodes: for n in self.nodes:
h += n.height h += n.height
w = max(w,n.width) w = max(w,n.width)
@ -176,13 +188,72 @@ class TreeNode(BoxLayout):
options['data'] = c options['data'] = c
tn = self.treeObj.NodeKlass(**options) tn = self.treeObj.NodeKlass(**options)
tn.bind(size=self.childrenSizeChange) tn.bind(size=self.childrenSizeChange)
self.subNodes.append(tn) self.nodes.append(tn)
self.children_box.add_widget(tn) self.children_box.add_widget(tn)
self.children_box.height += tn.height self.children_box.height += tn.height
self.children_box.width = max(self.children_box.width,tn.width) self.children_box.width = max(self.children_box.width,tn.width)
self.node_box1.height = self.children_box.height self.node_box1.height = self.children_box.height
self.node_box1.width = self.trigger.width + self.children_box.width self.node_box1.width = self.trigger.width + self.children_box.width
def addNodeByData(self,data):
options = {}
options['tree'] = self.treeObj
options['parentNode'] = self
if isinstance(data,dict):
options['data'] = DictObject(**data)
else:
options['data'] = data
w = self.treeObj.NodeKlass(**options)
self.nodes.append(w)
self.children_box.add_widget(w)
self.childrenSizeChange(None,None)
def addNode(self,node):
node.parentNode = self
self.nodes.append(node)
self.children_box.add_widget(node)
self.childrenSizeChange(None,None)
def deleteNode(self,node):
self.children_box.remove_widget(node)
self.nodes = [ i for i in self.nodes if i != node ]
self.childrenSizeChange(None,None)
def delete(self):
parentNode = self.parentNode if self.parentNode else self.treeObj
parentNode.deleteNode(self)
def expand(self):
if self.children_open:
return
self.toggleChildren(None)
def collapse(self):
if not self.children_open:
return
self.toggleChildren(None)
def expandall(self):
if not self.children_open:
self.toggleChildren(None)
for n in self.nodes:
n.expandall()
def collapseall(self):
if self.children_open:
self.toggleChildren(None)
for n in self.nodes:
n.collapseall(None)
def moveNode(self,node,newParent=None):
old_parent = node.parent
if old_parent == None:
old_parent = node.treeObj
old_parent.deleteNode(node)
if newParent == None:
newParent = node.treeObj
newParent.addNode(node)
def toggleChildren(self,o): def toggleChildren(self,o):
self.treeObj.unselect_row() self.treeObj.unselect_row()
self.children_open = True if not self.children_open else False self.children_open = True if not self.children_open else False
@ -195,16 +266,14 @@ class TreeNode(BoxLayout):
# when a widget remove from its parent, the get_parent_window() # when a widget remove from its parent, the get_parent_window()
# will return a None # will return a None
# w1 = self.children_box.get_parent_window() # w1 = self.children_box.get_parent_window()
# logging.info('Tree :get_parent_window() return=%s',str(type(w1))) # Logger.info('Tree :get_parent_window() return=%s',str(type(w1)))
""" """
tree options tree options
{ {
"url": "url":
"params", "params",
"bg_color",
"color_level", "color_level",
"color",
"checkbox", "checkbox",
"multplecheck", "multplecheck",
"idField", "idField",
@ -230,14 +299,14 @@ class Tree(BGColorBehavior, ScrollWidget):
def unselect_row(self): def unselect_row(self):
if self.selected_node: if self.selected_node:
logging.info('selected node unselected') Logger.info('selected node unselected')
self.selected_node.unselected() self.selected_node.unselected()
self.selected_node = None self.selected_node = None
def onSize(self,o,v=None): def onSize(self,o,v=None):
if not self.initflag: if not self.initflag:
self.initflag = True self.initflag = True
self.buildTree(self) self.buildTree()
for n in self.nodes: for n in self.nodes:
n.setMinWidth(self.width) n.setMinWidth(self.width)
@ -246,13 +315,23 @@ class Tree(BGColorBehavior, ScrollWidget):
def getUrlData(self,callback,kv=None): def getUrlData(self,callback,kv=None):
hc = App.get_running_app().hc hc = App.get_running_app().hc
params = self.options.get(params,{}).copy() params = self.options.get('params',{}).copy()
if not data is None: if not kv is None:
for k,v in kv: for k,v in kv.items():
if k in params.keys(): if k in params.keys():
params[k] = v params[k] = v
params['id'] = kv[self.options.idField] params['id'] = kv[self.options.idField]
hc.get(self.url,params=params,callback=callback) config = getConfig()
url = absurl(self.options.url,None)
Logger.info('Tree: getUrlData(),url=%s',url)
hc.get(url,params=params,
callback=callback,
errback=self.showError)
def showError(self,o,e):
traceback.print_exc()
Logger.info('Tree: showError() o=%s,e=%s',o,e)
alert(e,title='error')
def buildTree(self,kv=None): def buildTree(self,kv=None):
if not hasattr(self,'NodeKlass'): if not hasattr(self,'NodeKlass'):
@ -260,16 +339,18 @@ class Tree(BGColorBehavior, ScrollWidget):
if self.options.url: if self.options.url:
return self.getUrlData(self.dataLoaded,kv) return self.getUrlData(self.dataLoaded,kv)
data = self.options.data data = self.options.data or []
logging.info("Tree : buildTree,data=%s",data) Logger.info("Tree : buildTree,data=%s",data)
self.dataLoaded(data) self.dataLoaded(None,data)
self.color, self.bgcolor = getColors(self.color_level) self.color, self.bgcolor = getColors(self.color_level)
def dataLoaded(self,d): def dataLoaded(self,o,d):
Logger.info("Tree: dataLoaded,d=%s",d)
self.data = d self.data = d
self.addNodes() self.addNodes()
def addNodes(self): def addNodes(self):
Logger.info("Tree: addNodes()")
for c in self.data: for c in self.data:
options = {} options = {}
options['tree'] = self options['tree'] = self
@ -278,7 +359,22 @@ class Tree(BGColorBehavior, ScrollWidget):
w = self.NodeKlass(**options) w = self.NodeKlass(**options)
self.nodes.append(w) self.nodes.append(w)
self.add_widget(w) self.add_widget(w)
logging.info('Tree : node=%s',type(w)) Logger.info('Tree : node=%s',type(w))
def addNode(self,data,parentNode=None):
options = {}
options['tree'] = self
options['parentNode'] = None
options['data'] = DictObject(**data)
w = self.NodeKlass(**options)
self.nodes.append(w)
self.add_widget(w)
def deleteNode(self,node):
if self.selected_node == node:
self.selected_node = None
self.remove_widget(node)
self.nodes = [ i for i in self.nodes if i != node ]
class TextContent(PressableLabel): class TextContent(PressableLabel):
def __init__(self,level=0,**options): def __init__(self,level=0,**options):
@ -309,22 +405,21 @@ class TextTreeNode(TreeNode):
def onPress(self,o,v=None): def onPress(self,o,v=None):
if self.hasChildren_nodes: if self.hasChildren_nodes:
v = True if not self.children_open else False
self.toggleChildren(self) self.toggleChildren(self)
self.trigger.on_press() self.trigger.on_press()
return return
logging.info('select the leaf node') Logger.info('select the leaf node')
self.treeObj.select_row(self) self.treeObj.select_row(self)
def selected(self): def selected(self):
logging.info('content selected ........') Logger.info('content selected ........')
color, bgcolor = getColors(self.treeObj.color_level, color, bgcolor = getColors(self.treeObj.color_level,
selected=True) selected=True)
self.content.bgcolor = bgcolor self.content.bgcolor = bgcolor
self.content.color = color self.content.color = color
def unselected(self): def unselected(self):
logging.info('content unselected ........') Logger.info('content unselected ........')
color, bgcolor = getColors(self.treeObj.color_level, color, bgcolor = getColors(self.treeObj.color_level,
selected=False) selected=False)
self.content.bgcolor = bgcolor self.content.bgcolor = bgcolor
@ -336,13 +431,45 @@ class TextTree(Tree):
super().__init__(**options) super().__init__(**options)
self.register_event_type('on_press') self.register_event_type('on_press')
def select_row(self, node):
super().select_row(node)
self.dispatch('on_press',node.data)
def onPress(self,o,v=None): def on_press(self,o,v=None):
if self.selectNode:
self.selectNode.unselected()
self.selectNode = o
o.selected()
self.dispatch(on_press,o,o.data)
def on_press(self,o,v):
print('TextTree():on_press(),o=',o,'v=',v) print('TextTree():on_press(),o=',o,'v=',v)
class PopupMenu(BoxLayout):
def __init__(self,target,menudesc,**opts):
self.target = target
self.menudesc = menudesc
BoxLayout.__init__(self, size_hint=(0.5,0.5))
self.menu_tree = TextTree(**menudesc)
self.add_widget(self.menu_tree)
self.menu_tree.bind(on_press=self.onMenuItemTouch)
self.register_event_type('on_press')
def on_press(self,o,v=None):
Logger.info('PopupMenu: on_press fired')
def onMenuItemTouch(self,o,d=None,v=None):
Logger.info('MenuTree: on_press fired,o=%s,d=%s,v=%s',o,d,v)
data = {
'target':self.target,
'menudata':d
}
self.dispatch('on_press',data)
self.dismiss()
def open(self):
Window.add_widget(self)
self.center = Window.center
def dismiss(self):
Window.remove_widget(self)
def on_touch_down(self,touch):
if not self.collide_point(*touch.pos):
self.dismiss()
return False
super().on_touch_down(touch)
return True

View File

@ -1,5 +1,6 @@
import sys import sys
import re import re
from kivy.logger import Logger
from kivy.uix.image import Image from kivy.uix.image import Image
from kivy.uix.boxlayout import BoxLayout from kivy.uix.boxlayout import BoxLayout
from kivy.uix.textinput import TextInput from kivy.uix.textinput import TextInput
@ -25,14 +26,14 @@ class BoolInput(Switch):
a.active = kw.get('value') a.active = kw.get('value')
super().__init__(**a) super().__init__(**a)
self.register_event_type('on_changed')
self.bind(active=on_active) self.bind(active=on_active)
self.bind(change=self.on_change)
def on_change(self,t,v): def on_changed(self,v=None):
pass pass
def on_active(self,t,v): def on_active(self,t,v):
change = True self.dispatch('on_changed',v)
def getValue(self): def getValue(self):
return self.active return self.active
@ -41,43 +42,53 @@ class BoolInput(Switch):
self.active = v self.active = v
class StrInput(TextInput): class StrInput(TextInput):
change = BooleanProperty(False)
def __init__(self,**kv): def __init__(self,**kv):
if kv is None: if kv is None:
kv = {} kv = {}
a = { a = {
# "allow_copy":True, "allow_copy":True,
"font_size":CSize(1), "font_size":1,
"multiline":False, "multiline":False,
"halign":"left", "halign":"left",
"hint_text":"", "hint_text":"",
"size_hint_y":None, "size_hint_y":None,
"height":CSize(2) "width":20,
"height":2.5
} }
if kv.get('tip'): if kv.get('tip'):
a['hint_text'] = kv['tip'] a['hint_text'] = kv['tip']
a.update(kv) a.update(kv)
a['multiline'] = False a['width'] = CSize(kv.get('width',20))
super(StrInput,self).__init__(**a) a['height'] = CSize(kv.get('height',2.5))
self.bind(focus=self.on_focus) a['font_size'] = CSize(kv.get('font_size',1))
self.bind(text=self.on_text)
self.bind(change=self.on_change)
def on_change(self,t,v): super(StrInput,self).__init__(**a)
if v: self.old_value = None
self.register_event_type('on_changed')
self.bind(focus=self.on_focus)
self.bind(on_text_validate=self.checkChange)
def on_changed(self,v=None):
pass pass
def on_text(self,t,v): def checkChange(self,o,v=None):
self.change = True v = self.getValue()
if v != self.old_value:
self.dispatch('on_changed',v)
def on_focus(self,t,v): def on_focus(self,t,v):
self.change = False if v:
self.old_value = self.getValue()
else:
self.checkChange(None)
def getValue(self): def getValue(self):
return self.text return self.text
def setValue(self,v): def setValue(self,v):
self.text = v if v is None:
v = ''
self.text = str(v)
class Password(StrInput): class Password(StrInput):
def __init__(self, **kw): def __init__(self, **kw):
@ -97,6 +108,10 @@ class IntegerInput(StrInput):
s = re.sub(pat, '', substring) s = re.sub(pat, '', substring)
return StrInput.insert_text(self,s, from_undo=from_undo) return StrInput.insert_text(self,s, from_undo=from_undo)
def on_focus(self,t,v):
super().on_focus(t,v)
self.cursor = (0,len(self.text))
class FloatInput(IntegerInput): class FloatInput(IntegerInput):
pat = re.compile('[^0-9]') pat = re.compile('[^0-9]')
def filter(self,substring): def filter(self,substring):
@ -167,17 +182,16 @@ class MyDropDown(DropDown):
for d in data: for d in data:
dd = (d[self.valueField],d[self.textField]) dd = (d[self.valueField],d[self.textField])
b = Button(text=d[self.textField],font_size=CSize(1), b = Button(text=d[self.textField],font_size=CSize(1),
size_hint_y=None, size_hint=(None,None),
height=CSize(1.8)) width=CSize(10),
height=CSize(2.5))
setattr(b,'kw_data',dd) setattr(b,'kw_data',dd)
b.bind(on_release=lambda btn: self.select(btn.kw_data)) b.bind(on_release=lambda btn: self.select(btn.kw_data))
self.add_widget(b) self.add_widget(b)
#print(dd) #print(dd)
def setDataByUrl(self,url,params={}): def setDataByUrl(self,url,params={}):
def x(obj,resp): def x(obj,d):
if resp.status_code == 200:
d = resp.json()
self.setData(d) self.setData(d)
app = App.get_running_app() app = App.get_running_app()
@ -191,8 +205,11 @@ class MyDropDown(DropDown):
class SelectInput(BoxLayout): class SelectInput(BoxLayout):
def __init__(self,**kw): def __init__(self,**kw):
super(SelectInput,self).__init__(orientation='horizontal', size_hint_y=None,height=CSize(1.8)) super(SelectInput,self).__init__(orientation='horizontal',\
self.tinp = StrInput() size_hint=(None,None),
width=CSize(kw.get('width',10)),
height=CSize(kw.get('height',2.5)))
self.tinp = StrInput(size_hint_y=None,height=kw.get('height',2.5))
self.tinp.readonly = True self.tinp.readonly = True
newkw = {} newkw = {}
newkw.update(kw) newkw.update(kw)
@ -205,17 +222,26 @@ class SelectInput(BoxLayout):
self.si_data = '' self.si_data = ''
self.text = '' self.text = ''
self.add_widget(self.tinp) self.add_widget(self.tinp)
self.old_value = self.si_data
self.tinp.bind(focus=self.showDropdown) self.tinp.bind(focus=self.showDropdown)
self.register_event_type('on_changed')
def on_changed(self,v=None):
pass
def showDropdown(self,instance,yn): def showDropdown(self,instance,yn):
# if self.collide_point(*touch.pos): # if self.collide_point(*touch.pos):
if yn: if yn:
self.tinp.focus = False self.tinp.focus = False
self.dropdown.showme(self) self.dropdown.showme(self)
self.old_value = self.getValue()
def setData(self,d): def setData(self,d):
self.tinp.si_data = d[0] self.tinp.si_data = d[0]
self.tinp.text = d[1] self.tinp.text = d[1]
v = self.getValue()
if v != self.old_value:
self.dispatch('on_changed',v)
def setValue(self,v): def setValue(self,v):
self.tinp.si_value = v self.tinp.si_value = v