It's about two, somehow connected problems. On the one hand it is about creating a proper subclass including a "change_DF"-method for pandas’ DataFrame (DF) object. (Prob1)
On the other hand it is about using the on_{property} and ObjectProperty from kivy the right way, so it detects changes in the mentioned DF. (Prob2)
Regarding Prob1 there are a few tutorials, e.g. from pandas itself:
https://pandas.pydata.org/pandas-docs/stable/development/extending.html#extending-subclassing-pandas
and some very rich of details, like:
http://devanla.com/case-for-inheriting-from-pandas-dataframe.html
Subclassing a Pandas DataFrame, updates? (@stackoverflow)
Unfortunately, content beyond my knowledge at this point (will have to study this class/super/property/*args/**kwargs/etc. stuff in detail... feel free to teach me in context of the given code piece)...!
Regarding Prob2 I feel a bit stuck with Prob1, because it seems to be a matter of what kivy is supposed to compare when checking if any «relevant» changes have been made to the "binded_kDF"... (well, seems as if I lack additionally knowledge about the principles of Kivy’s powerful EventDispatcher... even more to study and learn, obviously)
The piece of code in question, ready to run with Py3.7.4:
from kivy.app import App
import pandas as pd
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.gridlayout import GridLayout
from kivy.uix.label import Label
from kivy.uix.button import Button
from kivy.properties import ObjectProperty
class KivyDataFrame(pd.DataFrame):
@property
def _constructor(self):
return KivyDataFrame
def __init__(self, **kwargs):
super().__init__(**kwargs)
def change_DF(self, data):
# self.data = data # [Problem1] not how it works...
self.update(pd.DataFrame(data)) # ...and this one only works given (same structure with) same col-names
class KivySheet(GridLayout):
binded_kDF = ObjectProperty()
def __init__(self, **kwargs):
super().__init__(**kwargs)
self.cols=2
self.rows=2
self.counter = 1
def bind_kDF(self, kDF):
self.binded_kDF = kDF
def create_Sheet(self, *args):
print(self.binded_kDF)
for row in self.binded_kDF.to_numpy():
for value in row:
self.add_widget(Label(text=str(value)))
def update_Sheet(self, *args):
self.clear_widgets()
self.create_Sheet()
def on_binded_kDF(self, *args): # [Problem2] not called automatically when pressing Button (calling it beyond initiation; counter=2)
print("counter:", self.counter)
self.update_Sheet()
self.counter+=1
class AppFrame(BoxLayout):
def __init__(self, **kwargs):
super().__init__(**kwargs)
self.orientation="vertical"
self.kDF = KivyDataFrame(data={'col1': [1, 2], 'col2': [3, 4]})
self.kSh = KivySheet()
self.kSh.bind_kDF(self.kDF)
self.add_widget(self.kSh)
self.add_widget(Button(text="«change kDF»", on_press=lambda u:self.change_kDF()))
def change_kDF(self):
self.kDF.change_DF(data={'col1': [5, 6], 'col2': [7, 8]}) # change col-names and it doesn't work anymore (see Problem 1)
# self.kSh.update_Sheet() # necessary to have the wanted changing-effect (why Problem 2 has to be solved)
class MySheetApp(App):
def build(self):
return AppFrame()
if __name__ == '__main__':
MySheetApp().run()
For both problems I out-commented a line which didn't work (1) or is a workaround (2).
De-comment the latter and you will see the expected result (incl. “counter: 2”) I'm looking for.
Error/warning messages which I've recieved regarding Prob1 (using self.data=data):
UserWarning: Pandas doesn't allow columns to be created via a new attribute name - see https://pandas.pydata.org/pandas-docs/stable/indexing.html#attribute-access
...and regarding Prob2:
[WARNING] [Property ] Value comparison failed for with "init() takes 1 positional argument but 2 were given". Consider setting force_dispatch to True to avoid this.