This commit is contained in:
yumoqing 2020-11-12 14:28:46 +08:00
parent 1f5a8d93de
commit 9333cd9bd4
2 changed files with 567 additions and 571 deletions

View File

@ -1,164 +1,167 @@
"""
BoxViewer options:
{
"dataloader":{
"url",
"method",
"params"
"filter":{
}
}
"boxwidth",
"boxheight",
"viewer"
"toolbar":{
}
}
"""
from traceback import print_exc
from functools import partial
from kivy.app import App
from kivy.factory import Factory
from kivy.utils import platform
from kivy.uix.boxlayout import BoxLayout
from .responsivelayout import VResponsiveLayout
from .toolbar import Toolbar
from .paging import Paging, RelatedLoader
from .utils import CSize
class BoxViewer(BoxLayout):
def __init__(self, **options):
self.toolbar = None
self.parenturl = None
self.dataloader = None
self.initflag = False
self.selected_box = None
remind = ['toolbar',
'dataloader',
'orientation',
'viewer',
'boxwidth',
'boxheight']
kwargs = {k:v for k,v in options.items() if k not in remind }
BoxLayout.__init__(self, orientation='vertical', **kwargs)
self.selected_data = None
self.options = options
self.box_width = CSize(options['boxwidth'])
self.box_height = CSize(options['boxheight'])
self.viewContainer = VResponsiveLayout(cols=2,box_width=self.box_width)
if options.get('toolbar'):
self.toolbar = Toolbar(options['toolbar'])
lopts = options['dataloader'].copy()
self.dataloader = RelatedLoader(target=self,**lopts['options'])
self.dataloader.bind(on_deletepage=self.deleteWidgets)
self.dataloader.bind(on_pageloaded=self.addPageWidgets)
self.dataloader.bind(on_newbegin=self.deleteAllWidgets)
self.params = self.options['dataloader']['options'].get('params',{})
if self.toolbar:
self.add_widget(self.toolbar)
if self.dataloader.widget:
self.add_widget(self.dataloader.widget)
self.dataloader.bind(on_submit=self.getParams)
self.add_widget(self.viewContainer)
self.register_event_type('on_selected')
self.viewContainer.bind(size=self.resetCols,
pos=self.resetCols)
self.viewContainer.bind(on_scroll_stop=self.on_scroll_stop)
def getParams(self,o,p):
self.params = p
def deleteAllWidgets(self,o):
self.viewContainer.clear_widgets()
def addPageWidgets(self,o,data):
widgets = []
recs = data['data']
dir = data['dir']
idx = 0
if dir == 'up':
recs.reverse()
idx = -1
for r in recs:
self.showObject(widgets, r, index=idx)
self.dataloader.bufferObjects(widgets)
x = self.dataloader.getLocater()
self.locater(x)
def deleteWidgets(self,o,data):
for w in data:
self.viewContainer.remove_widget(w)
def on_selected(self, o, v=None):
pass
def locater(self, pos):
self.viewContainer.scroll_y = pos
def resetCols(self,o=None, v=None):
h = 0
if self.toolbar:
h += self.toolbar.height
if self.dataloader.widget:
h += self.dataloader.widget.height
self.viewContainer.height = self.height - h
self.viewContainer.setCols()
if not self.initflag:
self.dataloader.loadPage(1)
self.initflag = True
def showObject(self, holders, rec,index=0):
def doit(self,holders,idx,o,w):
w.bind(on_press=self.select_record)
self.viewContainer.add_widget(w,index=idx)
holders.append(w)
def doerr(o,e):
print_exc()
print('showObject(),error=',e)
raise e
options = self.options['viewer'].copy()
options['options']['record'] = rec
options['options']['ancestor'] = self
options['options']['size_hint'] = None,None
options['options']['width'] = self.box_width
options['options']['height'] = self.box_height
blocks = Factory.Blocks()
blocks.bind(on_built=partial(doit,self,holders,index))
blocks.bind(on_failed=doerr)
blocks.widgetBuild(options, ancestor=self)
def on_scroll_stop(self,o,v=None):
if o.scroll_y <= 0.001:
self.dataloader.loadNextPage()
if o.scroll_y >= 0.999:
self.dataloader.loadPreviousPage()
def select_record(self,o,v=None):
if self.selected_box:
self.selected_box.unselected()
o.selected()
self.selected_box = o
self.selected_data = o.getRecord()
d = {
"target":self.selected_box,
"page_rows":1,
"page":self.selected_data['__posInSet__'],
"dataurl":self.options['dataloader']['options']['dataurl'],
"params":self.params
}
self.dispatch('on_selected',d)
def getData(self):
d = {
"caller":self,
"page_rows":1,
"page":self.selected_data['__posInSet__'],
"dataurl":self.options['dataloader']['options']['dataurl'],
"params":self.params
}
return d
"""
BoxViewer options:
{
"dataloader":{
"url",
"method",
"params"
"filter":{
}
}
"boxwidth",
"boxheight",
"viewer"
"toolbar":{
}
}
"""
from traceback import print_exc
from functools import partial
from kivy.app import App
from kivy.factory import Factory
from kivy.utils import platform
from kivy.uix.boxlayout import BoxLayout
from .responsivelayout import VResponsiveLayout
from .toolbar import Toolbar
from .paging import Paging, RelatedLoader
from .utils import CSize
class BoxViewer(BoxLayout):
def __init__(self, **options):
self.toolbar = None
self.parenturl = None
self.dataloader = None
self.initflag = False
self.selected_box = None
remind = ['toolbar',
'dataloader',
'orientation',
'viewer',
'boxwidth',
'boxheight']
kwargs = {k:v for k,v in options.items() if k not in remind }
BoxLayout.__init__(self, orientation='vertical', **kwargs)
self.selected_data = None
self.options = options
self.box_width = CSize(options['boxwidth'])
self.box_height = CSize(options['boxheight'])
self.viewContainer = VResponsiveLayout(cols=2,box_width=self.box_width)
if options.get('toolbar'):
self.toolbar = Toolbar(options['toolbar'])
lopts = options['dataloader'].copy()
if lopts.get('options'):
lopts = lopts.get('options')
self.dataloader = RelatedLoader(target=self,**lopts)
self.dataloader.bind(on_deletepage=self.deleteWidgets)
self.dataloader.bind(on_pageloaded=self.addPageWidgets)
self.dataloader.bind(on_newbegin=self.deleteAllWidgets)
self.params = self.options['dataloader']['options'].get('params',{})
if self.toolbar:
self.add_widget(self.toolbar)
if self.dataloader.widget:
self.add_widget(self.dataloader.widget)
self.dataloader.bind(on_submit=self.getParams)
self.add_widget(self.viewContainer)
self.register_event_type('on_selected')
self.viewContainer.bind(size=self.resetCols,
pos=self.resetCols)
self.viewContainer.bind(on_scroll_stop=self.on_scroll_stop)
def getParams(self,o,p):
self.params = p
def deleteAllWidgets(self,o):
self.viewContainer.clear_widgets()
def addPageWidgets(self,o,data):
widgets = []
recs = data['data']
page = data['page']
dir = data['dir']
idx = 0
if dir == 'up':
recs.reverse()
idx = -1
for r in recs:
self.showObject(widgets, r, index=idx)
self.dataloader.bufferObjects(page, widgets)
x = self.dataloader.getLocater()
self.locater(x)
def deleteWidgets(self,o,data):
for w in data:
self.viewContainer.remove_widget(w)
def on_selected(self, o, v=None):
pass
def locater(self, pos):
self.viewContainer.scroll_y = pos
def resetCols(self,o=None, v=None):
h = 0
if self.toolbar:
h += self.toolbar.height
if self.dataloader.widget:
h += self.dataloader.widget.height
self.viewContainer.height = self.height - h
self.viewContainer.setCols()
if not self.initflag:
self.dataloader.loadPage(1)
self.initflag = True
def showObject(self, holders, rec,index=0):
def doit(self,holders,idx,o,w):
w.bind(on_press=self.select_record)
self.viewContainer.add_widget(w,index=idx)
holders.append(w)
def doerr(o,e):
print_exc()
print('showObject(),error=',e)
raise e
options = self.options['viewer'].copy()
options['options']['record'] = rec
options['options']['ancestor'] = self
options['options']['size_hint'] = None,None
options['options']['width'] = self.box_width
options['options']['height'] = self.box_height
blocks = Factory.Blocks()
blocks.bind(on_built=partial(doit,self,holders,index))
blocks.bind(on_failed=doerr)
blocks.widgetBuild(options, ancestor=self)
def on_scroll_stop(self,o,v=None):
if o.scroll_y <= 0.001:
self.dataloader.loadNextPage()
if o.scroll_y >= 0.999:
self.dataloader.loadPreviousPage()
def select_record(self,o,v=None):
if self.selected_box:
self.selected_box.unselected()
o.selected()
self.selected_box = o
self.selected_data = o.getRecord()
d = {
"target":self.selected_box,
"page_rows":1,
"page":self.selected_data['__posInSet__'],
"dataurl":self.options['dataloader']['options']['dataurl'],
"params":self.params
}
self.dispatch('on_selected',d)
def getData(self):
d = {
"caller":self,
"page_rows":1,
"page":self.selected_data['__posInSet__'],
"dataurl":self.options['dataloader']['options']['dataurl'],
"params":self.params
}
return d

View File

@ -1,407 +1,400 @@
import time
from kivy.uix.gridlayout import GridLayout
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.scrollview import ScrollView
from kivy.uix.label import Label
from kivy.uix.button import ButtonBehavior
from kivy.clock import Clock
from kivy.properties import BooleanProperty
from kivy.properties import ListProperty
from kivy.graphics import Color, Rectangle
from kivy.app import App
from kivy.factory import Factory
from appPublic.dictObject import DictObject
from appPublic.timecost import TimeCost
from appPublic.uniqueID import getID
from .utils import CSize, setSizeOptions, loading, loaded, absurl, alert
from .baseWidget import Text
from .widgetExt import ScrollWidget
from .paging import Paging, RelatedLoader
from .ready import WidgetReady
from .toolbar import Toolbar
from .i18n import I18nText
class BLabel(ButtonBehavior, Text):
def __init__(self, **kw):
ButtonBehavior.__init__(self)
Text.__init__(self,**kw)
class Cell(BoxLayout):
def __init__(self,row,desc):
"""
desc:{
width,
datatype:fff
value:
format:
on_press:
}
"""
self.desc = desc
self.row = row
super().__init__(size_hint=(None,None),
width = self.desc['width'],
height = self.row.part.datagrid.rowHeight()
)
if not self.row.header and self.desc.get('viewer'):
viewer = self.desc.get('viewer')
blocks = Factory.Blocks()
if isinstance(viewer,str):
l = self.desc.copy()
l['row'] = self.row
viewer = blocks.eval(viewer,l)
if isinstance(viewer,dict):
print('viewer = ', viewer)
w = blocks.widgetBuild(viewer,ancestor=self.row.part.datagrid)
self.add_widget(w)
return
if desc['header']:
bl = I18nText(otext=str(desc['value']),
font_size=CSize(1),
halign='left'
)
else:
bl = BLabel(text = str(desc['value']),
font_size=CSize(1),
halign='left'
)
self.add_widget(bl)
bl.bind(on_press=self.cell_press)
def cell_press(self,obj):
self.row.selected()
class Row(GridLayout):
def __init__(self,part, rowdesc,header=False,data=None):
"""
rowdesc=[
{
width,
name
value
on_press
}
"""
self.part = part
self.header = header
self.row_data = data
self.row_id = None
self.linewidth = 1
self.rowdesc = rowdesc
super().__init__(cols=len(self.rowdesc),spacing=self.linewidth)
# Clock.schedule_once(self.init,0)
self.init(0)
def init(self,t):
w = 0
h = 0
for c in self.rowdesc:
c['header'] = self.header
cell = Cell(self,c)
self.add_widget(cell)
w += cell.width
if cell.height > h:
h = cell.height
self.size_hint = None,None
self.width = w + self.linewidth * 2 * len(self.rowdesc)
self.height = h #self.part.datagrid.rowHeight()
def selected(self):
if not hasattr(self,'row_data'):
return # header
print('row selected',self.row_id, self.row_data)
self.part.datagrid.row_selected = True
self.part.datagrid.select_rowid = self.row_id
self.part.datagrid.dispatch('on_selected',self)
class Header(WidgetReady, ScrollWidget):
def __init__(self,part,**kw):
self.part = part
ScrollWidget.__init__(self,**kw)
WidgetReady.__init__(self)
self.init(1)
self.bind(on_scroll_stop=self.part.datagrid.on_scrollstop)
def init(self,t):
rd = [ f.copy() for f in self.part.rowdesc ]
[ f.update({'value':self.part.fields[i]['label']}) for i,f in enumerate(rd) ]
self.header = Row(self.part,rd,header=True)
self.add_widget(self.header)
self.height = self.header.height
class Body(ScrollWidget):
def __init__(self,part,**kw):
self.part = part
ScrollWidget.__init__(self,**kw)
self.idRow = {}
self.bind(on_scroll_stop=self.part.datagrid.on_scrollstop)
def addRow(self,id, data,index=0):
rd = [ f.copy() for f in self.part.rowdesc ]
[ f.update({'value':data.get(f['name'])}) for f in rd ]
row = Row(self.part,rd,data=data)
row.row_id = id
self.add_widget(row,index=index)
self.idRow[id] = row
def clearRows(self):
self.idRow = {}
self.clear_widgets()
def delRowById(self,id):
row = self.idRow[id]
self.remove_widget(row)
del self.idRow[id]
def getRowData(self,rowid):
return self.idRow[rowid].row_data
def getRowHeight(self):
return self.part.datagrid.rowHeight()
class DataGridPart(WidgetReady, BoxLayout):
def __init__(self,dg, freeze_flag, fields):
self.datagrid = dg
self.fields = fields
self.freeze_flag = freeze_flag
self.fields_width = None
BoxLayout.__init__(self, orientation='vertical')
WidgetReady.__init__(self)
# Clock.schedule_once(self.init,0.1)
self.init(0)
def setWidth(self):
if self.freeze_flag:
self.size_hint_x = None
self.width = self.getFieldsWidth()
def getFieldsWidth(self):
if not self.fields_width:
width = 0
for f in self.rowdesc:
width += f['width']
self.fields_width = width
return self.fields_width
def init(self,t):
rd = []
for f in self.fields:
r = f.copy()
r['width'] = CSize(f.get('width',10))
rd.append(r)
self.rowdesc = rd
self.setWidth()
kw = {}
if self.freeze_flag:
kw['width'] = self.fields_width
kw['size_hint'] = (None,None)
else:
kw['size_hint'] = (1,None)
kw['height'] = self.datagrid.rowHeight()
if not self.datagrid.noheader:
self.header = Header(self,**kw)
self.add_widget(self.header)
self.body = Body(self)
self.add_widget(self.body)
if not self.freeze_flag:
self.body.bind(pos=self.datagrid.bodyOnSize,
size=self.datagrid.bodyOnSize)
def clearRows(self):
return self.body.clearRows()
def addRow(self,id, data):
self.body.addRow(id, data)
class DataGrid(WidgetReady, BoxLayout):
row_selected = BooleanProperty(False)
def __init__(self,**options):
kw = DictObject()
kw = setSizeOptions(options,kw)
kw.orientation = 'vertical'
WidgetReady.__init__(self)
BoxLayout.__init__(self,**kw)
self.parenturl = options.get('parenturl',None)
self.options = options
self.noheader = options.get('noheader',False)
self.header_bgcolor = options.get('header_bgcolor',[0.29,0.29,0.29,1])
self.body_bgcolor = options.get('body_bgcolor',[0.25,0.25,0.25,1])
self.color = options.get('color',[0.91,0.91,0.91,1])
self.widget_ids = {}
self.row_height = None
self.selected_rowid = None
self.dataUrl = self.options.get('dataurl')
self.show_rows = 0
self.toolbar = None
self.freeze_part = None
self.normal_part = None
self.page_rows = self.options.get('page_rows', 60)
self.params = self.options.get('params',{})
self.total_cnt = 0
self.max_row = 0
self.curpage = 0
self.loading = False
self.freeze_fields = self.getPartFields(freeze_flag=True)
self.normal_fields = self.getPartFields(freeze_flag=False)
self.createDataGridPart()
self.createToolbar()
if self.toolbar:
self.add_widget(self.toolbar)
b = BoxLayout(orientation='horizontal')
if self.freeze_part:
b.add_widget(self.freeze_part)
if self.normal_part:
b.add_widget(self.normal_part)
self.add_widget(b)
if self.options.get('paging',False):
self.loader = Paging(adder=self.addRow,
clearer=self.clearRows,
dataurl=self.dataUrl,
target=self,
params=self.params,
method=self.options.get('method','GET')
)
self.add_widget(self.loader.widget)
else:
self.loader = RelatedLoader(adder=self.addRow,
remover=self.delRow,
locater=self.setScrollPosition,
page_rows=self.page_rows,
dataurl=self.dataUrl,
target=self,
params=self.params,
method=self.options.get('method','GET')
)
self.on_sizeTask = None
self.register_event_type('on_selected')
self.register_event_type('on_scrollstop')
def setScrollPosition(self,pos):
self.normal_part.body.scroll_y = pos
if self.freeze_part:
self.freeze_part.body.scroll_y = pos
def on_scrollstop(self,o,v=None):
if not self.noheader and o == self.normal_part.header:
self.normal_part.body.scroll_x = o.scroll_x
return
if o == self.normal_part.body:
if not self.noheader:
self.normal_part.header.scroll_x = o.scroll_x
if self.freeze_part:
self.freeze_part.body.scroll_y = o.scroll_y
if self.freeze_part and o == self.freeze_part.body:
self.normal_part.body.scroll_y = o.scroll_y
if self.options.get('paging'):
return
if o.scroll_y <= 0.001:
self.loader.loadNextPage()
if o.scroll_y >= 0.999:
self.loader.loadPreviousPage()
def getData(self):
if not self.row_selected:
return None
return self._getRowData(self.select_rowid)
def _getRowData(self, rowid):
d = {}
if self.freeze_part:
d.update(self.freeze_part.body.getRowData(rowid))
d.update(self.normal_part.body.getRowData(rowid))
print('getData() return=',d)
return DictObject(**d)
def bodyOnSize(self,o,s):
if self.on_sizeTask is not None:
self.on_sizeTask.cancel()
self.on_sizeTask = Clock.schedule_once(self.calculateShowRows,0.3)
def rowHeight(self):
if not self.row_height:
self.row_height = CSize(self.options.get('row_height',1.8))
return self.row_height
def calculateShowRows(self,t):
print('body height=',self.normal_part.body.height
,'row_height=',self.rowHeight()
)
self.show_rows = int(self.normal_part.body.height/self.rowHeight())
self.loader.setPageRows(self.show_rows)
def getShowRows(self):
if self.show_rows == 0:
return 60
return self.show_rows
def init(self,t):
if self.options.get('dataurl'):
self.loader.loadPage(1)
else:
data = self.options.get('data',[])
if len(data) > 0:
loading(self)
for d in data:
self.addRow()
loaded(self)
def clearRows(self):
if self.freeze_part:
self.freeze_part.body.clearRows()
self.normal_part.body.clearRows()
def addRow(self,data, **kw):
id = getID()
if self.freeze_part:
self.freeze_part.body.addRow(id, data, **kw)
self.normal_part.body.addRow(id, data, **kw)
return id
def setData(self,data):
for d in data:
self.addRow(d)
def delRow(self,id,**kw):
if self.freeze_part:
self.freeze_part.body.delRowById(id)
self.normal_part.body.delRowById(id)
def createToolbar(self):
if 'toolbar' in self.options.keys():
tb = self.options['toolbar']
self.toolbar = Toolbar(ancestor=self,**tb)
def on_selected(self,row):
self.selected_row = row
def createDataGridPart(self):
self.freeze_part = None
self.normal_part = None
if self.freeze_fields:
self.freeze_part = DataGridPart(self,True, self.freeze_fields)
if self.normal_fields:
self.normal_part = DataGridPart(self, False, self.normal_fields)
def getPartFields(self,freeze_flag:bool=False) -> list:
fs = []
for f in self.options['fields']:
if freeze_flag:
if f.get('freeze',False):
fs.append(f)
else:
if not f.get('freeze',False):
fs.append(f)
return fs
def on_ready(self,o,v=None):
print('***********onRadey*************')
import time
from kivy.uix.gridlayout import GridLayout
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.scrollview import ScrollView
from kivy.uix.label import Label
from kivy.uix.button import ButtonBehavior
from kivy.clock import Clock
from kivy.properties import BooleanProperty
from kivy.properties import ListProperty
from kivy.graphics import Color, Rectangle
from kivy.app import App
from kivy.factory import Factory
from appPublic.dictObject import DictObject
from appPublic.timecost import TimeCost
from appPublic.uniqueID import getID
from .utils import CSize, setSizeOptions, loading, loaded, absurl, alert
from .baseWidget import Text
from .widgetExt import ScrollWidget
from .paging import Paging, RelatedLoader
from .ready import WidgetReady
from .toolbar import Toolbar
from .i18n import I18nText
class BLabel(ButtonBehavior, Text):
def __init__(self, **kw):
ButtonBehavior.__init__(self)
Text.__init__(self,**kw)
class Cell(BoxLayout):
def __init__(self,row,desc):
"""
desc:{
width,
datatype:fff
value:
format:
on_press:
}
"""
self.desc = desc
self.row = row
super().__init__(size_hint=(None,None),
width = self.desc['width'],
height = self.row.part.datagrid.rowHeight()
)
if not self.row.header and self.desc.get('viewer'):
viewer = self.desc.get('viewer')
blocks = Factory.Blocks()
if isinstance(viewer,str):
l = self.desc.copy()
l['row'] = self.row
viewer = blocks.eval(viewer,l)
if isinstance(viewer,dict):
print('viewer = ', viewer)
w = blocks.widgetBuild(viewer,ancestor=self.row.part.datagrid)
self.add_widget(w)
return
if desc['header']:
bl = I18nText(otext=str(desc['value']),
font_size=CSize(1),
halign='left'
)
else:
bl = BLabel(text = str(desc['value']),
font_size=CSize(1),
halign='left'
)
self.add_widget(bl)
bl.bind(on_press=self.cell_press)
def cell_press(self,obj):
self.row.selected()
class Row(GridLayout):
def __init__(self,part, rowdesc,header=False,data=None):
"""
rowdesc=[
{
width,
name
value
on_press
}
"""
self.part = part
self.header = header
self.row_data = data
self.row_id = None
self.linewidth = 1
self.rowdesc = rowdesc
super().__init__(cols=len(self.rowdesc),spacing=self.linewidth)
# Clock.schedule_once(self.init,0)
self.init(0)
def init(self,t):
w = 0
h = 0
for c in self.rowdesc:
c['header'] = self.header
cell = Cell(self,c)
self.add_widget(cell)
w += cell.width
if cell.height > h:
h = cell.height
self.size_hint = None,None
self.width = w + self.linewidth * 2 * len(self.rowdesc)
self.height = h #self.part.datagrid.rowHeight()
def selected(self):
if not hasattr(self,'row_data'):
return # header
print('row selected',self.row_id, self.row_data)
self.part.datagrid.row_selected = True
self.part.datagrid.select_rowid = self.row_id
self.part.datagrid.dispatch('on_selected',self)
class Header(WidgetReady, ScrollWidget):
def __init__(self,part,**kw):
self.part = part
ScrollWidget.__init__(self,**kw)
WidgetReady.__init__(self)
self.init(1)
self.bind(on_scroll_stop=self.part.datagrid.on_scrollstop)
def init(self,t):
rd = [ f.copy() for f in self.part.rowdesc ]
[ f.update({'value':self.part.fields[i]['label']}) for i,f in enumerate(rd) ]
self.header = Row(self.part,rd,header=True)
self.add_widget(self.header)
self.height = self.header.height
class Body(ScrollWidget):
def __init__(self,part,**kw):
self.part = part
ScrollWidget.__init__(self,**kw)
self.idRow = {}
self.bind(on_scroll_stop=self.part.datagrid.on_scrollstop)
def addRow(self,id, data,index=0):
rd = [ f.copy() for f in self.part.rowdesc ]
[ f.update({'value':data.get(f['name'])}) for f in rd ]
row = Row(self.part,rd,data=data)
row.row_id = id
self.add_widget(row,index=index)
self.idRow[id] = row
return row
def clearRows(self):
self.idRow = {}
self.clear_widgets()
def delRowById(self,id):
row = self.idRow[id]
self.remove_widget(row)
del self.idRow[id]
def getRowData(self,rowid):
return self.idRow[rowid].row_data
def getRowHeight(self):
return self.part.datagrid.rowHeight()
class DataGridPart(WidgetReady, BoxLayout):
def __init__(self,dg, freeze_flag, fields):
self.datagrid = dg
self.fields = fields
self.freeze_flag = freeze_flag
self.fields_width = None
BoxLayout.__init__(self, orientation='vertical')
WidgetReady.__init__(self)
# Clock.schedule_once(self.init,0.1)
self.init(0)
def setWidth(self):
if self.freeze_flag:
self.size_hint_x = None
self.width = self.getFieldsWidth()
def getFieldsWidth(self):
if not self.fields_width:
width = 0
for f in self.rowdesc:
width += f['width']
self.fields_width = width
return self.fields_width
def init(self,t):
rd = []
for f in self.fields:
r = f.copy()
r['width'] = CSize(f.get('width',10))
rd.append(r)
self.rowdesc = rd
self.setWidth()
kw = {}
if self.freeze_flag:
kw['width'] = self.fields_width
kw['size_hint'] = (None,None)
else:
kw['size_hint'] = (1,None)
kw['height'] = self.datagrid.rowHeight()
if not self.datagrid.noheader:
self.header = Header(self,**kw)
self.add_widget(self.header)
self.body = Body(self)
self.add_widget(self.body)
if not self.freeze_flag:
self.body.bind(pos=self.datagrid.bodyOnSize,
size=self.datagrid.bodyOnSize)
def clearRows(self):
return self.body.clearRows()
def addRow(self,id, data):
return self.body.addRow(id, data)
class DataGrid(WidgetReady, BoxLayout):
row_selected = BooleanProperty(False)
def __init__(self,**options):
kw = DictObject()
kw = setSizeOptions(options,kw)
kw.orientation = 'vertical'
WidgetReady.__init__(self)
BoxLayout.__init__(self,**kw)
self.parenturl = options.get('parenturl',None)
self.options = options
self.noheader = options.get('noheader',False)
self.header_bgcolor = options.get('header_bgcolor',[0.29,0.29,0.29,1])
self.body_bgcolor = options.get('body_bgcolor',[0.25,0.25,0.25,1])
self.color = options.get('color',[0.91,0.91,0.91,1])
self.widget_ids = {}
self.row_height = None
self.selected_rowid = None
self.dataUrl = self.options.get('dataurl')
self.show_rows = 0
self.toolbar = None
self.freeze_part = None
self.normal_part = None
self.page_rows = self.options.get('page_rows', 60)
self.params = self.options.get('params',{})
self.total_cnt = 0
self.max_row = 0
self.curpage = 0
self.loading = False
self.freeze_fields = self.getPartFields(freeze_flag=True)
self.normal_fields = self.getPartFields(freeze_flag=False)
self.createDataGridPart()
self.createToolbar()
if self.toolbar:
self.add_widget(self.toolbar)
b = BoxLayout(orientation='horizontal')
if self.freeze_part:
b.add_widget(self.freeze_part)
if self.normal_part:
b.add_widget(self.normal_part)
self.add_widget(b)
ldr_desc = options.get('dataloader')
if not ldr_desc:
raise Exception('DataGrid need a DataLoader')
self.dataloader = RelatedLoader(target=self, **ldr_desc)
self.dataloader.bind(on_deletepage=self.delete_page)
self.dataloader.bind(on_pageloaded=self.add_page)
self.dataloader.bind(on_newbegin=self.clearRows)
self.on_sizeTask = None
self.register_event_type('on_selected')
self.register_event_type('on_scrollstop')
def locater(self,pos):
self.normal_part.body.scroll_y = pos
if self.freeze_part:
self.freeze_part.body.scroll_y = pos
def on_scrollstop(self,o,v=None):
if not self.noheader and o == self.normal_part.header:
self.normal_part.body.scroll_x = o.scroll_x
return
if o == self.normal_part.body:
if not self.noheader:
self.normal_part.header.scroll_x = o.scroll_x
if self.freeze_part:
self.freeze_part.body.scroll_y = o.scroll_y
if self.freeze_part and o == self.freeze_part.body:
self.normal_part.body.scroll_y = o.scroll_y
if o.scroll_y <= 0.001:
self.loader.loadNextPage()
if o.scroll_y >= 0.999:
self.loader.loadPreviousPage()
def getData(self):
if not self.row_selected:
return None
return self._getRowData(self.select_rowid)
def _getRowData(self, rowid):
d = {}
if self.freeze_part:
d.update(self.freeze_part.body.getRowData(rowid))
d.update(self.normal_part.body.getRowData(rowid))
print('getData() return=',d)
return DictObject(**d)
def bodyOnSize(self,o,s):
if self.on_sizeTask is not None:
self.on_sizeTask.cancel()
self.on_sizeTask = Clock.schedule_once(self.calculateShowRows,0.3)
def rowHeight(self):
if not self.row_height:
self.row_height = CSize(self.options.get('row_height',1.8))
return self.row_height
def calculateShowRows(self,t):
print('body height=',self.normal_part.body.height
,'row_height=',self.rowHeight()
)
self.show_rows = int(self.normal_part.body.height/self.rowHeight())
self.loader.setPageRows(self.show_rows)
def getShowRows(self):
if self.show_rows == 0:
return 60
return self.show_rows
def clearRows(self):
if self.freeze_part:
self.freeze_part.body.clearRows()
self.normal_part.body.clearRows()
def add_page(self,o,data):
ids = []
recs = data['data']
page = data['page']
dir = data['dir']
idx = 0
if dir == 'up':
recs.reverse()
idx = -1
for r in recs:
ids.append(self.addRow(r,index=idx))
self.dataloader.bufferObjects(page,ids)
x = self.dataloader.getLocater()
self.locater(x)
def delete_page(self,o,data):
for id in data:
self.delRow(id)
def addRow(self,data, **kw):
id = getID()
f_row = None
if self.freeze_part:
self.freeze_part.body.addRow(id, data, **kw)
self.normal_part.body.addRow(id, data, **kw)
return id
def delRow(self,id,**kw):
if self.freeze_part:
self.freeze_part.body.delRowById(id)
self.normal_part.body.delRowById(id)
def createToolbar(self):
if 'toolbar' in self.options.keys():
tb = self.options['toolbar']
self.toolbar = Toolbar(ancestor=self,**tb)
def on_selected(self,row):
self.selected_row = row
def createDataGridPart(self):
self.freeze_part = None
self.normal_part = None
if self.freeze_fields:
self.freeze_part = DataGridPart(self,True, self.freeze_fields)
if self.normal_fields:
self.normal_part = DataGridPart(self, False, self.normal_fields)
def getPartFields(self,freeze_flag:bool=False) -> list:
fs = []
for f in self.options['fields']:
if freeze_flag:
if f.get('freeze',False):
fs.append(f)
else:
if not f.get('freeze',False):
fs.append(f)
return fs
def on_ready(self,o,v=None):
print('***********onRadey*************')
Factory.register('DataGrid',DataGrid)