调用两次的方法:一次有参数,一次没有参数;如何防止这种行为?
methods called twice: once with and once without parameters; how to prevent this behaviour?
我尝试实现排序操作方法以显示不同种类的数据。
代码似乎有效,它做了我期望的事情,
但是class中的方法“sort_by_columnName”(例如:sort_by_customer、sort_by_order等)被调用:“Order_DataSource”两次 - 一次有参数,一次没有参数,
我正在考虑 - 这段代码出了什么问题以及如何防止这种行为?
python 3.8
import abc
from abc import ABCMeta
from typing import List
# kivy 2.0
from kivy.app import App
from kivy.core.window import Window
from kivy.event import EventDispatcher
from kivy.lang import Builder
from kivy.properties import ListProperty
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.button import Button
from kivy.uix.gridlayout import GridLayout
from kivy.uix.label import Label
kv = '''
#: import cust_rgba kivy.utils.get_color_from_hex
<Concept_Datagrid>:
id: gridview
orientation: 'vertical'
canvas.before:
Color:
rgba: cust_rgba('#8a8e8a')
Rectangle:
pos: self.pos
size: self.size
<Custom_HeaderCell>:
BoxLayout:
orientation: 'vertical'
canvas.before:
Color:
rgba: cust_rgba('#0b0f0a')
Line:
width: 1
rectangle: (self.x, self.y, self.width, self.height)
Label:
size_hint: 1, 0.5
id: header_label
color: [0, 0, 0, 1]
canvas.before:
Color:
rgba: cust_rgba('#eaeaea')
Rectangle:
pos: self.pos
size: self.size
BoxLayout:
id: sort_box
orientation: 'horizontal'
size_hint: 1, 0.5
Button:
id: sort_asc_btn
background_color: cust_rgba('#3aeaea')
text: 'sort Asc'
on_release: root.sort_by(sort_id=self.text, sort_value=header_label.text)
Button:
id: sort_desc_btn
background_color: cust_rgba('#3aeaea')
text: 'sort Desc'
on_release: root.sort_by(sort_id=self.text, sort_value=header_label.text)
'''
class Base_DataSource(metaclass=ABCMeta):
@classmethod
def __subclasshook__(cls, subclass):
return (
hasattr(subclass, 'source_data') and callable(subclass.source_data) and
hasattr(subclass, 'table_keys') and callable(subclass.table_keys) and
hasattr(subclass, 'sort_methods') and callable(subclass.sort_methods) or
NotImplemented
)
pass
@abc.abstractmethod
def source_data(self):
# returns data as List[dict]
raise NotImplementedError
@abc.abstractmethod
def table_keys(self):
# returns table's columns name
raise NotImplementedError
@abc.abstractmethod
def sort_methods(self):
# returns datasource column specific sorting methods name
raise NotImplementedError
def sort_dummy(self):
# special method for sorting: nothing to do but important to be here;"
pass
class Order_DataSource(EventDispatcher, Base_DataSource):
data: List[dict] = ListProperty()
def __init__(self):
super(Order_DataSource, self).__init__()
self.data = self.source_data()
pass
@staticmethod
def source_data() -> List[dict]:
return list([
{'id': 1, 'customer': 'otto b', 'order_group': 'og_1', 'destination': 'DE_BER'},
{'id': 2, 'customer': 'boris a', 'order_group': 'og_2', 'destination': 'UK_LD'},
{'id': 3, 'customer': 'francine b', 'order_group': 'og_1', 'destination': 'FR_PAR'},
{'id': 4, 'customer': 'franz w', 'order_group': 'og_1', 'destination': 'AT_W01'},
{'id': 5, 'customer': 'cleopatra a', 'order_group': 'og_5', 'destination': 'EGY_KAI'},
{'id': 6, 'customer': 'jorge c', 'order_group': 'og_1', 'destination': 'SP_MAD'},
{'id': 7, 'customer': 'aras l', 'order_group': 'og_1', 'destination': 'LT_VIL'},
{'id': 8, 'customer': 'uri m', 'order_group': 'og_8', 'destination': 'RUS_MOC'},
{'id': 9, 'customer': 'joseph s', 'order_group': 'og_1', 'destination': 'DE_BAV'},
{'id': 10, 'customer': 'julie g', 'order_group': 'og_2', 'destination': 'CH_URI'},
{'id': 11, 'customer': 'cindy l', 'order_group': 'og_3', 'destination': 'US_NY'},
{'id': 12, 'customer': 'jair b', 'order_group': 'og_4', 'destination': 'BR_BRA'},
{'id': 13, 'customer': 'akiko a', 'order_group': 'og_5', 'destination': 'JAP_HON'},
{'id': 14, 'customer': 'lana s', 'order_group': 'og_1', 'destination': 'SLO_LUB'},
{'id': 15, 'customer': 'adanna u', 'order_group': 'og_7', 'destination': 'NIG_UL'},
{'id': 16, 'customer': 'ljudmila c', 'order_group': 'og_8', 'destination': 'RUS_PET'},
])
def table_keys(self) -> List[str]:
dict_keys = []
for key_, value_ in self.source_data()[0].items():
dict_keys.append(key_)
return dict_keys
def sort_methods(self) -> dict:
return {'id': None, 'customer': self.sort_by_customer,
'order_group': self.sort_by_order, 'destination': self.sort_by_destination}
def sort_by_id(self, *args) -> List[dict]:
print(f'{self.sort_by_id.__name__} with args: {args}')
if args:
the_data = self.source_data()
if args[0]['sort_id'] in 'sort Asc':
sorted_data = sorted(the_data, key=lambda sCol: sCol['id'], reverse=False)
else:
sorted_data = sorted(the_data, key=lambda sCol: sCol['id'], reverse=True)
return sorted_data
pass
def sort_by_customer(self, *args) -> List[dict]:
print(f'{self.sort_by_customer.__name__} with args: {args}')
if args:
the_data = self.source_data()
if args[0]['sort_id'] in 'sort Asc':
sorted_data = sorted(the_data, key=lambda sCol: sCol['customer'], reverse=False)
else:
sorted_data = sorted(the_data, key=lambda sCol: sCol['customer'], reverse=True)
return sorted_data
pass
def sort_by_order(self, *args) -> List[dict]:
print(f'{self.sort_by_order.__name__} with args: {args}')
if args:
the_data = self.source_data()
if args[0]['sort_id'] in 'sort Asc':
sorted_data = sorted(the_data, key=lambda sCol: sCol['order_group'], reverse=False)
else:
sorted_data = sorted(the_data, key=lambda sCol: sCol['order_group'], reverse=True)
return sorted_data
pass
def sort_by_destination(self, *args) -> List[dict]:
print(f'{self.sort_by_destination.__name__} with args: {args}')
if args:
the_data = self.source_data()
if args[0]['sort_id'] in 'sort Asc':
sorted_data = sorted(the_data, key=lambda sCol: sCol['destination'], reverse=False)
else:
sorted_data = sorted(the_data, key=lambda sCol: sCol['destination'], reverse=True)
return sorted_data
pass
pass
class Custom_HeaderCell(BoxLayout):
def __init__(self, **kwargs):
super().__init__()
self.datagrid = kwargs['master']
self.datasource = kwargs['source']
self.cell_text = kwargs['cell_text']
self.ids.header_label.text = self.cell_text
if kwargs['sort_method'] is None:
self.ids.sort_box.remove_widget(self.ids.sort_asc_btn)
self.ids.sort_box.remove_widget(self.ids.sort_desc_btn)
self.ids.sort_box.add_widget(Button(text='no sort'))
self.wanted_method = self.datasource.sort_dummy
else:
if kwargs['sort_method'] not in ['None', None]:
for method in dir(kwargs['sort_method'].__self__):
if method in kwargs['sort_method'].__name__:
self.wanted_method = kwargs['sort_method']
self.ids.sort_asc_btn.on_release = self.wanted_method
self.ids.sort_desc_btn.on_release = self.wanted_method
def sort_by(self, **kwargs):
print(f'Custom_HeaderCell.sort_by kwargs {kwargs}')
if callable(self.wanted_method):
self.datagrid.data_view.data = self.wanted_method(kwargs)
pass
class Concept_Datagrid(BoxLayout):
def __init__(self, datasource):
super(Concept_Datagrid, self).__init__()
self.datasource = datasource
self.add_widget(Header(self, list(self.datasource.table_keys())))
self.data_view = DataView(self.datasource)
self.add_widget(self.data_view)
pass
class Header(BoxLayout):
size_hint = 1, None
def __init__(self, dataGrid, columns, **kwargs):
super(Header, self).__init__(**kwargs)
self.height = self.size[1] * 2 / len(columns) # this is not what I really want!
the_header = HeaderView(dataGrid, columns)
self.add_widget(the_header)
pass
class HeaderView(GridLayout):
def __init__(self, dataGrid, columns: list, **kwargs):
super().__init__(**kwargs)
self.rows = 1
self.columns = columns
for column_name in self.columns:
if dataGrid.datasource.sort_methods()[column_name]:
sortMethod = dataGrid.datasource.sort_methods()[column_name]
else:
sortMethod = None
self.add_widget(Custom_HeaderCell(
cell_text=str(column_name),
sort_method=sortMethod,
source=dataGrid.datasource,
master=dataGrid)
)
pass
class DataView(GridLayout):
data = ListProperty()
def __init__(self, datasource: Order_DataSource):
super(DataView, self).__init__()
self.data = datasource.source_data()
self.cols = len(datasource.table_keys()) # set number of columns to GridLayout,
pass
def _update_view(self):
if self.children:
self.clear_widgets()
for entity in self.data:
for item in entity.values():
lbl = Label()
lbl.text = str(item)
self.add_widget(lbl)
def on_data(self, *args):
self._update_view()
pass
class Test_App(App):
Window.size = (800, 600)
title = str('minimal datagrid app')
def build(self):
Builder.load_string(kv)
source = Order_DataSource()
concept_test = Concept_Datagrid(source)
return concept_test
pass
pass
if __name__ == '__main__':
Test_App().run()
pass
您似乎指定了两次 on_release
操作,因此指定的方法被调用了两次。在 Custom_HeaderCell
的 __init__()
方法中,您有:
self.ids.sort_asc_btn.on_release = self.wanted_method
self.ids.sort_desc_btn.on_release = self.wanted_method
并且在您的 kv
中您有:
Button:
id: sort_asc_btn
background_color: cust_rgba('#3aeaea')
text: 'sort Asc'
on_release: root.sort_by(sort_id=self.text, sort_value=header_label.text)
Button:
id: sort_desc_btn
background_color: cust_rgba('#3aeaea')
text: 'sort Desc'
on_release: root.sort_by(sort_id=self.text, sort_value=header_label.text)
因此您设置了 on_release
两次,这导致它被调用了两次。
我尝试实现排序操作方法以显示不同种类的数据。 代码似乎有效,它做了我期望的事情,
但是class中的方法“sort_by_columnName”(例如:sort_by_customer、sort_by_order等)被调用:“Order_DataSource”两次 - 一次有参数,一次没有参数,
我正在考虑 - 这段代码出了什么问题以及如何防止这种行为?
python 3.8
import abc
from abc import ABCMeta
from typing import List
# kivy 2.0
from kivy.app import App
from kivy.core.window import Window
from kivy.event import EventDispatcher
from kivy.lang import Builder
from kivy.properties import ListProperty
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.button import Button
from kivy.uix.gridlayout import GridLayout
from kivy.uix.label import Label
kv = '''
#: import cust_rgba kivy.utils.get_color_from_hex
<Concept_Datagrid>:
id: gridview
orientation: 'vertical'
canvas.before:
Color:
rgba: cust_rgba('#8a8e8a')
Rectangle:
pos: self.pos
size: self.size
<Custom_HeaderCell>:
BoxLayout:
orientation: 'vertical'
canvas.before:
Color:
rgba: cust_rgba('#0b0f0a')
Line:
width: 1
rectangle: (self.x, self.y, self.width, self.height)
Label:
size_hint: 1, 0.5
id: header_label
color: [0, 0, 0, 1]
canvas.before:
Color:
rgba: cust_rgba('#eaeaea')
Rectangle:
pos: self.pos
size: self.size
BoxLayout:
id: sort_box
orientation: 'horizontal'
size_hint: 1, 0.5
Button:
id: sort_asc_btn
background_color: cust_rgba('#3aeaea')
text: 'sort Asc'
on_release: root.sort_by(sort_id=self.text, sort_value=header_label.text)
Button:
id: sort_desc_btn
background_color: cust_rgba('#3aeaea')
text: 'sort Desc'
on_release: root.sort_by(sort_id=self.text, sort_value=header_label.text)
'''
class Base_DataSource(metaclass=ABCMeta):
@classmethod
def __subclasshook__(cls, subclass):
return (
hasattr(subclass, 'source_data') and callable(subclass.source_data) and
hasattr(subclass, 'table_keys') and callable(subclass.table_keys) and
hasattr(subclass, 'sort_methods') and callable(subclass.sort_methods) or
NotImplemented
)
pass
@abc.abstractmethod
def source_data(self):
# returns data as List[dict]
raise NotImplementedError
@abc.abstractmethod
def table_keys(self):
# returns table's columns name
raise NotImplementedError
@abc.abstractmethod
def sort_methods(self):
# returns datasource column specific sorting methods name
raise NotImplementedError
def sort_dummy(self):
# special method for sorting: nothing to do but important to be here;"
pass
class Order_DataSource(EventDispatcher, Base_DataSource):
data: List[dict] = ListProperty()
def __init__(self):
super(Order_DataSource, self).__init__()
self.data = self.source_data()
pass
@staticmethod
def source_data() -> List[dict]:
return list([
{'id': 1, 'customer': 'otto b', 'order_group': 'og_1', 'destination': 'DE_BER'},
{'id': 2, 'customer': 'boris a', 'order_group': 'og_2', 'destination': 'UK_LD'},
{'id': 3, 'customer': 'francine b', 'order_group': 'og_1', 'destination': 'FR_PAR'},
{'id': 4, 'customer': 'franz w', 'order_group': 'og_1', 'destination': 'AT_W01'},
{'id': 5, 'customer': 'cleopatra a', 'order_group': 'og_5', 'destination': 'EGY_KAI'},
{'id': 6, 'customer': 'jorge c', 'order_group': 'og_1', 'destination': 'SP_MAD'},
{'id': 7, 'customer': 'aras l', 'order_group': 'og_1', 'destination': 'LT_VIL'},
{'id': 8, 'customer': 'uri m', 'order_group': 'og_8', 'destination': 'RUS_MOC'},
{'id': 9, 'customer': 'joseph s', 'order_group': 'og_1', 'destination': 'DE_BAV'},
{'id': 10, 'customer': 'julie g', 'order_group': 'og_2', 'destination': 'CH_URI'},
{'id': 11, 'customer': 'cindy l', 'order_group': 'og_3', 'destination': 'US_NY'},
{'id': 12, 'customer': 'jair b', 'order_group': 'og_4', 'destination': 'BR_BRA'},
{'id': 13, 'customer': 'akiko a', 'order_group': 'og_5', 'destination': 'JAP_HON'},
{'id': 14, 'customer': 'lana s', 'order_group': 'og_1', 'destination': 'SLO_LUB'},
{'id': 15, 'customer': 'adanna u', 'order_group': 'og_7', 'destination': 'NIG_UL'},
{'id': 16, 'customer': 'ljudmila c', 'order_group': 'og_8', 'destination': 'RUS_PET'},
])
def table_keys(self) -> List[str]:
dict_keys = []
for key_, value_ in self.source_data()[0].items():
dict_keys.append(key_)
return dict_keys
def sort_methods(self) -> dict:
return {'id': None, 'customer': self.sort_by_customer,
'order_group': self.sort_by_order, 'destination': self.sort_by_destination}
def sort_by_id(self, *args) -> List[dict]:
print(f'{self.sort_by_id.__name__} with args: {args}')
if args:
the_data = self.source_data()
if args[0]['sort_id'] in 'sort Asc':
sorted_data = sorted(the_data, key=lambda sCol: sCol['id'], reverse=False)
else:
sorted_data = sorted(the_data, key=lambda sCol: sCol['id'], reverse=True)
return sorted_data
pass
def sort_by_customer(self, *args) -> List[dict]:
print(f'{self.sort_by_customer.__name__} with args: {args}')
if args:
the_data = self.source_data()
if args[0]['sort_id'] in 'sort Asc':
sorted_data = sorted(the_data, key=lambda sCol: sCol['customer'], reverse=False)
else:
sorted_data = sorted(the_data, key=lambda sCol: sCol['customer'], reverse=True)
return sorted_data
pass
def sort_by_order(self, *args) -> List[dict]:
print(f'{self.sort_by_order.__name__} with args: {args}')
if args:
the_data = self.source_data()
if args[0]['sort_id'] in 'sort Asc':
sorted_data = sorted(the_data, key=lambda sCol: sCol['order_group'], reverse=False)
else:
sorted_data = sorted(the_data, key=lambda sCol: sCol['order_group'], reverse=True)
return sorted_data
pass
def sort_by_destination(self, *args) -> List[dict]:
print(f'{self.sort_by_destination.__name__} with args: {args}')
if args:
the_data = self.source_data()
if args[0]['sort_id'] in 'sort Asc':
sorted_data = sorted(the_data, key=lambda sCol: sCol['destination'], reverse=False)
else:
sorted_data = sorted(the_data, key=lambda sCol: sCol['destination'], reverse=True)
return sorted_data
pass
pass
class Custom_HeaderCell(BoxLayout):
def __init__(self, **kwargs):
super().__init__()
self.datagrid = kwargs['master']
self.datasource = kwargs['source']
self.cell_text = kwargs['cell_text']
self.ids.header_label.text = self.cell_text
if kwargs['sort_method'] is None:
self.ids.sort_box.remove_widget(self.ids.sort_asc_btn)
self.ids.sort_box.remove_widget(self.ids.sort_desc_btn)
self.ids.sort_box.add_widget(Button(text='no sort'))
self.wanted_method = self.datasource.sort_dummy
else:
if kwargs['sort_method'] not in ['None', None]:
for method in dir(kwargs['sort_method'].__self__):
if method in kwargs['sort_method'].__name__:
self.wanted_method = kwargs['sort_method']
self.ids.sort_asc_btn.on_release = self.wanted_method
self.ids.sort_desc_btn.on_release = self.wanted_method
def sort_by(self, **kwargs):
print(f'Custom_HeaderCell.sort_by kwargs {kwargs}')
if callable(self.wanted_method):
self.datagrid.data_view.data = self.wanted_method(kwargs)
pass
class Concept_Datagrid(BoxLayout):
def __init__(self, datasource):
super(Concept_Datagrid, self).__init__()
self.datasource = datasource
self.add_widget(Header(self, list(self.datasource.table_keys())))
self.data_view = DataView(self.datasource)
self.add_widget(self.data_view)
pass
class Header(BoxLayout):
size_hint = 1, None
def __init__(self, dataGrid, columns, **kwargs):
super(Header, self).__init__(**kwargs)
self.height = self.size[1] * 2 / len(columns) # this is not what I really want!
the_header = HeaderView(dataGrid, columns)
self.add_widget(the_header)
pass
class HeaderView(GridLayout):
def __init__(self, dataGrid, columns: list, **kwargs):
super().__init__(**kwargs)
self.rows = 1
self.columns = columns
for column_name in self.columns:
if dataGrid.datasource.sort_methods()[column_name]:
sortMethod = dataGrid.datasource.sort_methods()[column_name]
else:
sortMethod = None
self.add_widget(Custom_HeaderCell(
cell_text=str(column_name),
sort_method=sortMethod,
source=dataGrid.datasource,
master=dataGrid)
)
pass
class DataView(GridLayout):
data = ListProperty()
def __init__(self, datasource: Order_DataSource):
super(DataView, self).__init__()
self.data = datasource.source_data()
self.cols = len(datasource.table_keys()) # set number of columns to GridLayout,
pass
def _update_view(self):
if self.children:
self.clear_widgets()
for entity in self.data:
for item in entity.values():
lbl = Label()
lbl.text = str(item)
self.add_widget(lbl)
def on_data(self, *args):
self._update_view()
pass
class Test_App(App):
Window.size = (800, 600)
title = str('minimal datagrid app')
def build(self):
Builder.load_string(kv)
source = Order_DataSource()
concept_test = Concept_Datagrid(source)
return concept_test
pass
pass
if __name__ == '__main__':
Test_App().run()
pass
您似乎指定了两次 on_release
操作,因此指定的方法被调用了两次。在 Custom_HeaderCell
的 __init__()
方法中,您有:
self.ids.sort_asc_btn.on_release = self.wanted_method
self.ids.sort_desc_btn.on_release = self.wanted_method
并且在您的 kv
中您有:
Button:
id: sort_asc_btn
background_color: cust_rgba('#3aeaea')
text: 'sort Asc'
on_release: root.sort_by(sort_id=self.text, sort_value=header_label.text)
Button:
id: sort_desc_btn
background_color: cust_rgba('#3aeaea')
text: 'sort Desc'
on_release: root.sort_by(sort_id=self.text, sort_value=header_label.text)
因此您设置了 on_release
两次,这导致它被调用了两次。