![](/img/trans.png)
[英]Python > Kivy > How to change the data in a RecycleView object
[英]Python Kivy RecycleView data model change
我正在嘗試在 Kivy RecycleView 上實現一個“表”。 軟件啟動后,RecycleView 會填充一個字典列表,例如:
{
'full_name': 'name surname',
'hours': [{}, {}, {}, ...],
'datetime': datetime(),
'upn': 'name.surname@domain.com'
}
使用這段代碼:
tbl = []
for user in self.users:
filtered = time_tables.filter_by_user(user.user_principal_name)
if len(filtered) == 0:
continue
tbl_row = {
'full_name': user.full_name,
'hours': [{'hours': timedelta(seconds=0), 'id': None} for i in range(days_in_month(timestamp))],
'datetime': timestamp.timetuple(),
'upn': user.user_principal_name
}
for item in filtered:
tbl_row['hours'][(item.Date + timedelta(hours=5)).day - 1] = {
'hours': item.get_working_hours(),
'id': item.Id
}
tbl.append(tbl_row)
self.ids.UserList.data.clear()
self.ids.UserList.data = tbl
這在第一次運行時按預期工作,但是,當我更改時間戳值時,例如將days_in_month
結果從 31 更改為 30 時,RecycleView 變得一團糟,有些行有 30 天,而其他行有 31 天......背景數據是正確的並且每個tbl_row
對象都具有相同的長度。
有誰可以指出我錯過了什么? 我需要刷新data_model
嗎? 如果是,如何?
提前感謝阿萊西奧
- - - - - - - - - 編輯 - - - - - - - - - -
我嘗試添加一個最小可重現的例子......
KV 文件:
<UserListViewItem@BoxLayout>
full_name: ''
hours: []
upn: ''
datetime: ()
size_hint: 1, 1
orientation: 'horizontal'
Label:
id: lblFullName
text: root.full_name
size_hint: .2, .5
size_hint_max_x: dp(240)
<PyTimbratureUI>:
BoxLayout:
orientation: 'horizontal'
size_hint: 1, .2
size_hint_max_y: dp(60)
Button:
size_hint: .2, 1
text: '<< Precedente'
on_release: root.prev_month()
Label:
id: lblCurrentMonth
size_hint: .6, 1
text: 'Placeholder'
Button:
size_hint: .2, 1
text: 'Successivo >>'
on_release: root.next_month()
RecycleView:
orientation: 'vertical'
viewclass: 'UserListViewItem'
id: UserList
size_hint: 1, 1
RecycleBoxLayout:
height: self.minimum_height
default_size: None, 45
default_size_hint: 1, None
size_hint_y: None
spacing: 5
orientation: 'vertical'
Python腳本:
def days_in_month(dt: datetime):
leap = 0
if dt.year % 400 == 0:
leap = 1
elif dt.year % 100 == 0:
leap = 0
elif dt.year % 4 == 0:
leap = 1
if dt.month == 2:
return 28 + leap
list = [1, 3, 5, 7, 8, 10, 12]
if dt.month in list:
return 31
return 30
def timedelta_to_string(data: timedelta or float):
if isinstance(data, timedelta):
hours = int(data.total_seconds() / 3600)
minutes = int(int(data.total_seconds() / 60) - (hours * 60))
else:
hours = int(data)
minutes = int(int(data * 60) - (hours * 60))
return '%02d:%02d' % (hours, minutes)
class UserListViewItem(BoxLayout, RecycleDataViewBehavior):
def __init__(self, **kwargs):
super(UserListViewItem, self).__init__(**kwargs)
Clock.schedule_once(self.finish_init)
self.labels = []
def get_overtime(self, dt):
if isinstance(dt, timedelta) and dt > timedelta(hours=8):
return dt - timedelta(hours=8)
if isinstance(dt, int) or isinstance(dt, float):
if dt > 8:
return dt - 8
return timedelta(hours=0)
def finish_init(self, dt):
current_datetime = datetime(self.datetime[0], self.datetime[1], 1)
for i in range(days_in_month(current_datetime)):
label = HourLabel(ore='0', straordinario='0')
self.labels.append(label)
self.add_widget(label)
i = 0
for hour_obj in self.hours:
hour = hour_obj['hours']
current_datetime = datetime(self.datetime[0], self.datetime[1], i+1)
self.labels[i].ore = timedelta_to_string(hour)
self.labels[i].user = self.upn
self.labels[i].record_id = hour_obj['id'] if hour_obj['id'] is not None else ''
if isinstance(hour, timedelta):
self.labels[i].straordinario = timedelta_to_string(self.get_overtime(hour))
else:
self.labels[i].straordinario = timedelta_to_string(0)
self.labels[i].color = self.get_label_color(current_datetime, hour)
i += 1
print(self.hours)
class PyTimbratureUI(BoxLayout):
def __init__(self, **kwargs):
super(PyTimbratureUI, self).__init__(**kwargs)
Clock.schedule_once(self.on_start, 1)
self.current_date = datetime.now()
self.shp = None
self.users = []
self.start_data_update()
def next_month(self):
self.current_date += timedelta(days=days_in_month(self.current_date))
self.start_data_update()
def prev_month(self):
self.current_date -= timedelta(days=days_in_month(self.current_date))
self.start_data_update()
def start_data_update(self):
self.ids.UserList.data = []
self.ids.lblCurrentMonth.text = self.current_date.strftime('%m/%Y')
#self.downloadThread = Thread(target=self.load_data, args=[datetime(2021, 12, 1)])
self.downloadThread = Thread(target=self.load_data, args=[self.current_date])
self.downloadThread.start()
@staticmethod
def get_from_month_start(lst: SharepointList, timestmap: datetime = datetime.now()):
first_day = timestmap.strftime('%Y-%m-01')
last_day = timestmap.strftime('%Y-%m-') + str(days_in_month(timestmap))
query = lst.q().on_list_field('Data').greater_equal(first_day)
query = query.chain('and').less_equal(last_day)
return lst.get_items(query=query, expand_fields=True)
def load_data(self, timestamp: datetime = datetime.now()):
self.shp = SharepointManger()
self.shp.connect()
self.users = self.shp.get_users()
#timbrature = self.shp.get_list('Timbrature')
#time_tables = Timetable.from_sharepoint_items(self.get_from_month_start(timbrature, timestamp))
self.users = [{'full_name': 'Test1', 'user_principal_name': 'test1@example.com'},
{'full_name': 'Test2', 'user_principal_name': 'test2@example.com'},
{'full_name': 'Test3', 'user_principal_name': 'test3@example.com'},
{'full_name': 'Test4', 'user_principal_name': 'test4@example.com'},
]
tbl = []
for user in self.users:
tbl_row = {
'full_name': user['full_name'],
'hours': [{'hours': timedelta(seconds=0), 'id': None} for i in range(days_in_month(timestamp))],
'datetime': timestamp.timetuple(),
'upn': user['user_principal_name']
}
self.ids.UserList.data.clear()
self.ids.UserList.data = tbl
Clock.schedule_once(lambda x: self.create_headers(days_in_month(timestamp), x, tbl))
def find_class_in_childrem(self, collection, classtype):
for child in collection:
if isinstance(child, classtype):
return True
return False
def create_headers(self, count, dt, tbl):
userlist = self.ids.UserList
while self.find_class_in_childrem(self.ids.lytTableHeader.children, TableHdr):
for widget in self.ids.lytTableHeader.children:
if isinstance(widget, TableHdr):
self.ids.lytTableHeader.remove_widget(widget)
for i in range(count):
hdrLabel = TableHdr(text=str(i+1), size_hint=(0.03, 1))
self.ids.lytTableHeader.add_widget(hdrLabel)
userlist.data.clear()
userlist.data = tbl
userlist.refresh_from_data()
def on_start(self, t):
print(self.ids)
class PyTimbratureApp(App):
def __init__(self, **kwargs):
super(PyTimbratureApp, self).__init__(**kwargs)
self.ui = None
def build(self):
self.ui = PyTimbratureUI()
return self.ui
深入挖掘似乎問題與文檔中報告的緩存小部件有關https://kivy.org/doc/stable/api-kivy.uix.recycleview.html
似乎還沒有清除緩存的類實例的方法。
解決方法是在主窗口類中完全刪除 RecycleView 跟蹤它,並在每次我需要更新列數時創建一個新的 istance。
class PyTimbratureUI(BoxLayout):
def __init__(self, **kwargs):
....
self.tt = None # This keep track of the instantiated RecycleView
....
def update_data(self, count, dt, tbl):
.....
# At the begin the class is accessed through its id, then using the
# object property tt
if self.tt is not None:
self.remove_widget(self.tt)
else:
self.remove_widget(self.ids.UserList)
self.tt = TimeTableRV()
self.tt.data = tbl
self.add_widget(self.tt)
希望這對其他人有幫助...
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.