bugfix
This commit is contained in:
parent
211fee42ec
commit
2490b46b46
@ -3,6 +3,7 @@ from kivy.utils import platform
|
||||
from kivy.uix.button import Button, ButtonBehavior
|
||||
from kivy.uix.accordion import Accordion,AccordionItem
|
||||
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 ActionToggleButton, ActionCheck,ActionSeparator,ActionGroup
|
||||
@ -79,3 +80,38 @@ class PressableLabel(ButtonBehavior, Text):
|
||||
def on_press(self):
|
||||
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)
|
||||
|
||||
|
@ -5,23 +5,23 @@ from kivy.properties import ListProperty
|
||||
_logcnt = 0
|
||||
class BGColorBehavior(object):
|
||||
bgcolor = ListProperty([])
|
||||
def __init__(self, bgcolor=[1,0,0,1],**kwargs):
|
||||
def __init__(self, bgcolor=[0,0,0,1],**kwargs):
|
||||
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):
|
||||
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:
|
||||
with self.canvas.before:
|
||||
Color(*self.bgcolor)
|
||||
Rectangle(pos=self.pos, size=self.size)
|
||||
self.rect = Rectangle(pos=self.pos,
|
||||
size=self.size)
|
||||
|
||||
|
@ -1,5 +1,8 @@
|
||||
import traceback
|
||||
|
||||
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.uix.boxlayout import BoxLayout
|
||||
from kivy.uix.label import Label
|
||||
@ -8,9 +11,11 @@ from kivy.uix.button import ButtonBehavior
|
||||
from kivyblocks.widgetExt import ScrollWidget
|
||||
from kivyblocks.utils import CSize
|
||||
from appPublic.dictObject import DictObject
|
||||
from appPublic.jsonConfig import getConfig
|
||||
from .baseWidget import PressableLabel
|
||||
from .color_definitions import getColors
|
||||
from .bgcolorbehavior import BGColorBehavior
|
||||
from .utils import alert,absurl
|
||||
|
||||
class EmptyBox(Label):
|
||||
def __init__(self,size_cnt=1):
|
||||
@ -80,8 +85,11 @@ class TreeNode(BoxLayout):
|
||||
self.data = data
|
||||
self.content = None
|
||||
self.children_open = False
|
||||
self.subNodes = []
|
||||
self.node_box = BoxLayout(orientation='horizontal',size_hint_y=None)
|
||||
self.nodes = []
|
||||
self.initChildren = False
|
||||
self.node_box = BoxLayout(orientation='horizontal',
|
||||
spacing=CSize(0.5),
|
||||
size_hint_y=None)
|
||||
self.node_box1 = BoxLayout(orientation='horizontal')
|
||||
n = data.get('children')
|
||||
self.hasChildren_nodes = True if not n is None else False
|
||||
@ -122,7 +130,7 @@ class TreeNode(BoxLayout):
|
||||
self.node_box.width = width
|
||||
if self.node_box1.width < width:
|
||||
self.node_box1.width = width
|
||||
for n in self.subNodes:
|
||||
for n in self.nodes:
|
||||
n.setMinWidth(width)
|
||||
|
||||
def buildContent(self):
|
||||
@ -134,28 +142,32 @@ class TreeNode(BoxLayout):
|
||||
self.node_box.height = self.content.height
|
||||
self.node_box.width = self.trigger.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.node_box.width,self.node_box.height)
|
||||
|
||||
def buildChildren(self):
|
||||
if self.initChildren == True:
|
||||
return
|
||||
|
||||
if self.data.children is None:
|
||||
logging.info('Tree : is a leaf node')
|
||||
Logger.info('Tree : is a leaf node')
|
||||
return
|
||||
|
||||
if self.data.children == []:
|
||||
self.treeObj.getUrlData(self.addChildren,self.data)
|
||||
return
|
||||
if len(self.subNodes) == 0:
|
||||
logging.info('Tree : add subnodes')
|
||||
if len(self.nodes) == 0:
|
||||
Logger.info('Tree : add subnodes')
|
||||
self.addChildren(self.data.children)
|
||||
else:
|
||||
logging.info('Tree : not need add subnodes')
|
||||
Logger.info('Tree : not need add subnodes')
|
||||
self.initChildren = True
|
||||
|
||||
def childrenSizeChange(self,tn,v):
|
||||
h = 0
|
||||
w = 0
|
||||
for n in self.subNodes:
|
||||
for n in self.nodes:
|
||||
h += n.height
|
||||
w = max(w,n.width)
|
||||
|
||||
@ -176,13 +188,72 @@ class TreeNode(BoxLayout):
|
||||
options['data'] = c
|
||||
tn = self.treeObj.NodeKlass(**options)
|
||||
tn.bind(size=self.childrenSizeChange)
|
||||
self.subNodes.append(tn)
|
||||
self.nodes.append(tn)
|
||||
self.children_box.add_widget(tn)
|
||||
self.children_box.height += tn.height
|
||||
self.children_box.width = max(self.children_box.width,tn.width)
|
||||
self.node_box1.height = self.children_box.height
|
||||
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):
|
||||
self.treeObj.unselect_row()
|
||||
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()
|
||||
# will return a None
|
||||
# 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
|
||||
{
|
||||
"url":
|
||||
"params",
|
||||
"bg_color",
|
||||
"color_level",
|
||||
"color",
|
||||
"checkbox",
|
||||
"multplecheck",
|
||||
"idField",
|
||||
@ -230,14 +299,14 @@ class Tree(BGColorBehavior, ScrollWidget):
|
||||
|
||||
def unselect_row(self):
|
||||
if self.selected_node:
|
||||
logging.info('selected node unselected')
|
||||
Logger.info('selected node unselected')
|
||||
self.selected_node.unselected()
|
||||
self.selected_node = None
|
||||
|
||||
def onSize(self,o,v=None):
|
||||
if not self.initflag:
|
||||
self.initflag = True
|
||||
self.buildTree(self)
|
||||
self.buildTree()
|
||||
for n in self.nodes:
|
||||
n.setMinWidth(self.width)
|
||||
|
||||
@ -246,13 +315,23 @@ class Tree(BGColorBehavior, ScrollWidget):
|
||||
|
||||
def getUrlData(self,callback,kv=None):
|
||||
hc = App.get_running_app().hc
|
||||
params = self.options.get(params,{}).copy()
|
||||
if not data is None:
|
||||
for k,v in kv:
|
||||
params = self.options.get('params',{}).copy()
|
||||
if not kv is None:
|
||||
for k,v in kv.items():
|
||||
if k in params.keys():
|
||||
params[k] = v
|
||||
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):
|
||||
if not hasattr(self,'NodeKlass'):
|
||||
@ -260,16 +339,18 @@ class Tree(BGColorBehavior, ScrollWidget):
|
||||
|
||||
if self.options.url:
|
||||
return self.getUrlData(self.dataLoaded,kv)
|
||||
data = self.options.data
|
||||
logging.info("Tree : buildTree,data=%s",data)
|
||||
self.dataLoaded(data)
|
||||
data = self.options.data or []
|
||||
Logger.info("Tree : buildTree,data=%s",data)
|
||||
self.dataLoaded(None,data)
|
||||
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.addNodes()
|
||||
|
||||
def addNodes(self):
|
||||
Logger.info("Tree: addNodes()")
|
||||
for c in self.data:
|
||||
options = {}
|
||||
options['tree'] = self
|
||||
@ -278,7 +359,22 @@ class Tree(BGColorBehavior, ScrollWidget):
|
||||
w = self.NodeKlass(**options)
|
||||
self.nodes.append(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):
|
||||
def __init__(self,level=0,**options):
|
||||
@ -309,22 +405,21 @@ class TextTreeNode(TreeNode):
|
||||
|
||||
def onPress(self,o,v=None):
|
||||
if self.hasChildren_nodes:
|
||||
v = True if not self.children_open else False
|
||||
self.toggleChildren(self)
|
||||
self.trigger.on_press()
|
||||
return
|
||||
logging.info('select the leaf node')
|
||||
Logger.info('select the leaf node')
|
||||
self.treeObj.select_row(self)
|
||||
|
||||
def selected(self):
|
||||
logging.info('content selected ........')
|
||||
Logger.info('content selected ........')
|
||||
color, bgcolor = getColors(self.treeObj.color_level,
|
||||
selected=True)
|
||||
self.content.bgcolor = bgcolor
|
||||
self.content.color = color
|
||||
|
||||
def unselected(self):
|
||||
logging.info('content unselected ........')
|
||||
Logger.info('content unselected ........')
|
||||
color, bgcolor = getColors(self.treeObj.color_level,
|
||||
selected=False)
|
||||
self.content.bgcolor = bgcolor
|
||||
@ -336,13 +431,45 @@ class TextTree(Tree):
|
||||
super().__init__(**options)
|
||||
self.register_event_type('on_press')
|
||||
|
||||
|
||||
def onPress(self,o,v=None):
|
||||
if self.selectNode:
|
||||
self.selectNode.unselected()
|
||||
self.selectNode = o
|
||||
o.selected()
|
||||
self.dispatch(on_press,o,o.data)
|
||||
def select_row(self, node):
|
||||
super().select_row(node)
|
||||
self.dispatch('on_press',node.data)
|
||||
|
||||
def on_press(self,o,v):
|
||||
def on_press(self,o,v=None):
|
||||
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
|
||||
|
@ -1,5 +1,6 @@
|
||||
import sys
|
||||
import re
|
||||
from kivy.logger import Logger
|
||||
from kivy.uix.image import Image
|
||||
from kivy.uix.boxlayout import BoxLayout
|
||||
from kivy.uix.textinput import TextInput
|
||||
@ -25,14 +26,14 @@ class BoolInput(Switch):
|
||||
a.active = kw.get('value')
|
||||
|
||||
super().__init__(**a)
|
||||
self.register_event_type('on_changed')
|
||||
self.bind(active=on_active)
|
||||
self.bind(change=self.on_change)
|
||||
|
||||
def on_change(self,t,v):
|
||||
def on_changed(self,v=None):
|
||||
pass
|
||||
|
||||
def on_active(self,t,v):
|
||||
change = True
|
||||
self.dispatch('on_changed',v)
|
||||
|
||||
def getValue(self):
|
||||
return self.active
|
||||
@ -41,43 +42,53 @@ class BoolInput(Switch):
|
||||
self.active = v
|
||||
|
||||
class StrInput(TextInput):
|
||||
change = BooleanProperty(False)
|
||||
def __init__(self,**kv):
|
||||
if kv is None:
|
||||
kv = {}
|
||||
a = {
|
||||
# "allow_copy":True,
|
||||
"font_size":CSize(1),
|
||||
"allow_copy":True,
|
||||
"font_size":1,
|
||||
"multiline":False,
|
||||
"halign":"left",
|
||||
"hint_text":"",
|
||||
"size_hint_y":None,
|
||||
"height":CSize(2)
|
||||
"width":20,
|
||||
"height":2.5
|
||||
}
|
||||
if kv.get('tip'):
|
||||
a['hint_text'] = kv['tip']
|
||||
a.update(kv)
|
||||
a['multiline'] = False
|
||||
super(StrInput,self).__init__(**a)
|
||||
self.bind(focus=self.on_focus)
|
||||
self.bind(text=self.on_text)
|
||||
self.bind(change=self.on_change)
|
||||
a['width'] = CSize(kv.get('width',20))
|
||||
a['height'] = CSize(kv.get('height',2.5))
|
||||
a['font_size'] = CSize(kv.get('font_size',1))
|
||||
|
||||
def on_change(self,t,v):
|
||||
if v:
|
||||
pass
|
||||
super(StrInput,self).__init__(**a)
|
||||
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
|
||||
|
||||
def on_text(self,t,v):
|
||||
self.change = True
|
||||
def checkChange(self,o,v=None):
|
||||
v = self.getValue()
|
||||
if v != self.old_value:
|
||||
self.dispatch('on_changed',v)
|
||||
|
||||
def on_focus(self,t,v):
|
||||
self.change = False
|
||||
if v:
|
||||
self.old_value = self.getValue()
|
||||
else:
|
||||
self.checkChange(None)
|
||||
|
||||
def getValue(self):
|
||||
return self.text
|
||||
|
||||
def setValue(self,v):
|
||||
self.text = v
|
||||
if v is None:
|
||||
v = ''
|
||||
self.text = str(v)
|
||||
|
||||
class Password(StrInput):
|
||||
def __init__(self, **kw):
|
||||
@ -97,6 +108,10 @@ class IntegerInput(StrInput):
|
||||
s = re.sub(pat, '', substring)
|
||||
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):
|
||||
pat = re.compile('[^0-9]')
|
||||
def filter(self,substring):
|
||||
@ -167,18 +182,17 @@ class MyDropDown(DropDown):
|
||||
for d in data:
|
||||
dd = (d[self.valueField],d[self.textField])
|
||||
b = Button(text=d[self.textField],font_size=CSize(1),
|
||||
size_hint_y=None,
|
||||
height=CSize(1.8))
|
||||
size_hint=(None,None),
|
||||
width=CSize(10),
|
||||
height=CSize(2.5))
|
||||
setattr(b,'kw_data',dd)
|
||||
b.bind(on_release=lambda btn: self.select(btn.kw_data))
|
||||
self.add_widget(b)
|
||||
#print(dd)
|
||||
|
||||
def setDataByUrl(self,url,params={}):
|
||||
def x(obj,resp):
|
||||
if resp.status_code == 200:
|
||||
d = resp.json()
|
||||
self.setData(d)
|
||||
def x(obj,d):
|
||||
self.setData(d)
|
||||
|
||||
app = App.get_running_app()
|
||||
app.hc.get(url,params=params,callback=x)
|
||||
@ -191,8 +205,11 @@ class MyDropDown(DropDown):
|
||||
|
||||
class SelectInput(BoxLayout):
|
||||
def __init__(self,**kw):
|
||||
super(SelectInput,self).__init__(orientation='horizontal', size_hint_y=None,height=CSize(1.8))
|
||||
self.tinp = StrInput()
|
||||
super(SelectInput,self).__init__(orientation='horizontal',\
|
||||
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
|
||||
newkw = {}
|
||||
newkw.update(kw)
|
||||
@ -205,17 +222,26 @@ class SelectInput(BoxLayout):
|
||||
self.si_data = ''
|
||||
self.text = ''
|
||||
self.add_widget(self.tinp)
|
||||
self.old_value = self.si_data
|
||||
self.tinp.bind(focus=self.showDropdown)
|
||||
self.register_event_type('on_changed')
|
||||
|
||||
def on_changed(self,v=None):
|
||||
pass
|
||||
|
||||
def showDropdown(self,instance,yn):
|
||||
# if self.collide_point(*touch.pos):
|
||||
if yn:
|
||||
self.tinp.focus = False
|
||||
self.dropdown.showme(self)
|
||||
self.old_value = self.getValue()
|
||||
|
||||
def setData(self,d):
|
||||
self.tinp.si_data = d[0]
|
||||
self.tinp.text = d[1]
|
||||
v = self.getValue()
|
||||
if v != self.old_value:
|
||||
self.dispatch('on_changed',v)
|
||||
|
||||
def setValue(self,v):
|
||||
self.tinp.si_value = v
|
||||
|
Loading…
Reference in New Issue
Block a user