[Kivy] Need to render updated recycler view - Printable Version +- Python Forum (https://python-forum.io) +-- Forum: Python Coding (https://python-forum.io/forum-7.html) +--- Forum: GUI (https://python-forum.io/forum-10.html) +--- Thread: [Kivy] Need to render updated recycler view (/thread-14275.html) Pages:
1
2
|
Need to render updated recycler view - test - Nov-22-2018 hello, i'm trying to learn kivy. I need to update one part of the screen based on interaction in another part. Specifically, i need to get on_press input from a recycleView on left half of a boxLayout and update the recycleView on the right side of the boxLayout. It isnt working. So far my code: import openpyxl from kivy.app import App from kivy.lang import Builder from kivy.uix.recycleview import RecycleView from kivy.uix.recycleview.views import RecycleDataViewBehavior from kivy.uix.label import Label from kivy.properties import BooleanProperty from kivy.uix.recycleboxlayout import RecycleBoxLayout from kivy.uix.behaviors import FocusBehavior from kivy.uix.recycleview.layout import LayoutSelectionBehavior from kivy.uix.boxlayout import BoxLayout Builder.load_string(''' <SelectableLabel>: # Draw a background to indicate selection canvas.before: Color: rgba: (.0, 0.5, .1, .3) if self.selected else (0, 0, 0, 1) Rectangle: pos: self.pos size: self.size <SelectableLabel2>: # Draw a background to indicate selection canvas.before: Color: rgba: (.0, 0.1, .5, .3) if self.selected else (0, 0, 0, 1) Rectangle: pos: self.pos size: self.size <RootWid>: orientation: 'vertical' ActionBar: pos_hint: {'top':1} ActionView: use_separator: True ActionPrevious: title: 'StuCheck' with_previous: False ActionOverflow: ActionButton: text: 'Populate list' on_press: QuestionsList().populate() ActionButton: text: 'Sort list' on_press: root.sort() ActionButton: text: 'Clear list' on_press: root.clear() ActionGroup: text: 'More' ActionButton: text: 'Insert new item' on_press: root.insert(new_item_input.text) ActionButton: text: 'Update first item' on_press: root.update(update_item_input.text) ActionButton: text: 'Remove first item' on_press: root.remove() BoxLayout: orientation: 'horizontal' RV: QuestionsList: <RV>: viewclass: 'SelectableLabel' SelectableRecycleBoxLayout: default_size: None, dp(56) default_size_hint: 1, None size_hint_y: None height: self.minimum_height orientation: 'vertical' multiselect: False touch_multiselect: True <QuestionsList>: viewclass: 'SelectableLabel2' SelectableRecycleBoxLayout: default_size: None, dp(56) default_size_hint: 1, None size_hint_y: None height: self.minimum_height orientation: 'vertical' multiselect: False touch_multiselect: True ''') class SelectableRecycleBoxLayout(FocusBehavior, LayoutSelectionBehavior, RecycleBoxLayout): ''' Adds selection and focus behaviour to the view. ''' class RootWid(BoxLayout): pass class SelectableLabel(RecycleDataViewBehavior, Label): ''' Add selection support to the Label ''' index = None selected = BooleanProperty(False) selectable = BooleanProperty(True) def refresh_view_attrs(self, rv, index, data): ''' Catch and handle the view changes ''' self.index = index return super(SelectableLabel, self).refresh_view_attrs( rv, index, data) def on_touch_down(self, touch): ''' Add selection on touch down ''' if super(SelectableLabel, self).on_touch_down(touch): return True if self.collide_point(*touch.pos) and self.selectable: return self.parent.select_with_touch(self.index, touch) def apply_selection(self, rv, index, is_selected): ''' Respond to the selection of items in the view. ''' self.selected = is_selected if is_selected: QuestionsList().populate(rv.data[index]['text']) print("selection changed to {0}".format(rv.data[index])) else: print("selection removed for {0}".format(rv.data[index])) class SelectableLabel2(RecycleDataViewBehavior, Label): ''' Add selection support to the Label ''' index = None selected = BooleanProperty(False) selectable = BooleanProperty(True) def refresh_view_attrs(self, rv, index, data): ''' Catch and handle the view changes ''' self.index = index print (data, 'refreshed') return super(SelectableLabel2, self).refresh_view_attrs( rv, index, data) def on_touch_down(self, touch): ''' Add selection on touch down ''' if super(SelectableLabel, self).on_touch_down(touch): return True if self.collide_point(*touch.pos) and self.selectable: return self.parent.select_with_touch(self.index, touch) def apply_selection(self, rv, index, is_selected): ''' Respond to the selection of items in the view. ''' self.selected = is_selected if is_selected: print("selection changed to {0}".format(rv.data[index])) else: print("selection removed for {0}".format(rv.data[index])) class QuestionsList(RecycleView): def __init__(self, **kwargs): super(QuestionsList, self).__init__(**kwargs) def clear(self): self.data = [] def populate(self, subject): wb = openpyxl.load_workbook('sWL.xlsx') sheet = wb[subject] self.data = [] for row in range (2, sheet.max_row+1): question = sheet.cell(row=row, column=1).value self.data.append({'text' : question}) RootWid() class RV(RecycleView): def __init__(self, **kwargs): super(RV, self).__init__(**kwargs) subjects = [some list] self.data = [] for subject in subjects: self.data.append({'text':subject}) class TestApp(App): def build(self): return RootWid() if __name__ == '__main__': TestApp().run()Please advice. Thanks in advance :) RE: Need to render updated recycler view - test - Nov-22-2018 if there is a better way of doing this, kindly let me know :) RE: Need to render updated recycler view - test - Nov-23-2018 hi! refined the code a little, although output is the same. Tried to make the second RV a dynamic class in a widget and then clear the widget and update it everytime a button is clicked on the left. Kindly advice. import openpyxl from kivy.app import App from kivy.lang import Builder from kivy.uix.recycleview import RecycleView from kivy.uix.recycleview.views import RecycleDataViewBehavior from kivy.uix.label import Label from kivy.properties import BooleanProperty from kivy.uix.recycleboxlayout import RecycleBoxLayout from kivy.uix.behaviors import FocusBehavior from kivy.uix.recycleview.layout import LayoutSelectionBehavior from kivy.uix.boxlayout import BoxLayout from kivy.uix.widget import Widget from kivy.factory import Factory Builder.load_string(''' <SelectableLabel>: # Draw a background to indicate selection canvas.before: Color: rgba: (.0, 0.5, .1, .3) if self.selected else (0, 0, 0, 1) Rectangle: pos: self.pos size: self.size <RootWid>: orientation: 'vertical' ActionBar: pos_hint: {'top':1} ActionView: use_separator: True ActionPrevious: title: 'StuCheck' with_previous: False ActionOverflow: ActionButton: text: 'Populate list' on_press: QuestionsList().populate() ActionButton: text: 'Sort list' on_press: root.sort() ActionButton: text: 'Clear list' on_press: root.clear() ActionGroup: text: 'More' ActionButton: text: 'Insert new item' on_press: root.insert(new_item_input.text) ActionButton: text: 'Update first item' on_press: root.update(update_item_input.text) ActionButton: text: 'Remove first item' on_press: root.remove() BoxLayout: orientation: 'horizontal' RV: Middle_c: Questions_list: <Middle_c>: orientation: 'horizontal' <Questions_list@RecycleView>: data: [{'text':'123'}] viewclass: 'SelectableLabel' SelectableRecycleBoxLayout: default_size: None, dp(56) default_size_hint: 1, None size_hint_y: None height: self.minimum_height orientation: 'vertical' multiselect: False touch_multiselect: True <RV>: data: self.data viewclass: 'SelectableLabel' SelectableRecycleBoxLayout: default_size: None, dp(56) default_size_hint: 1, None size_hint_y: None height: self.minimum_height orientation: 'vertical' multiselect: False touch_multiselect: True ''') class RootWid(BoxLayout): pass class Middle_c(BoxLayout): uq = Factory.Questions_list() def update_questions(self, subject): uq = Factory.Questions_list() self.clear_widgets() uq.data = self.populate(subject) def populate(self, subject): wb = openpyxl.load_workbook('sWL.xlsx') sheet = wb[subject] self.data = [] for row in range (2, sheet.max_row+1): question = sheet.cell(row=row, column=1).value self.data.append({'text' : question}) return self.data class RV(RecycleView): def __init__(self, **kwargs): super(RV, self).__init__(**kwargs) subjects = [some list here] self.data = [] for subject in subjects: self.data.append({'text':subject}) class SelectableRecycleBoxLayout(FocusBehavior, LayoutSelectionBehavior, RecycleBoxLayout): ''' Adds selection and focus behaviour to the view. ''' class SelectableLabel(RecycleDataViewBehavior, Label): ''' Add selection support to the Label ''' index = None selected = BooleanProperty(False) selectable = BooleanProperty(True) def refresh_view_attrs(self, rv, index, data): ''' Catch and handle the view changes ''' self.index = index return super(SelectableLabel, self).refresh_view_attrs( rv, index, data) def on_touch_down(self, touch): ''' Add selection on touch down ''' if super(SelectableLabel, self).on_touch_down(touch): Middle_c().update_questions(RV().data[self.index]['text']) return True if self.collide_point(*touch.pos) and self.selectable: Middle_c().update_questions(RV().data[self.index]['text']) return self.parent.select_with_touch(self.index, touch) def apply_selection(self, rv, index, is_selected): ''' Respond to the selection of items in the view. ''' self.selected = is_selected if is_selected: Middle_c().update_questions(RV().data[self.index]['text']) print("selection changed to {0}".format(rv.data[index])) else: print("selection removed for {0}".format(rv.data[index])) class TestApp(App): def build(self): return RootWid() if __name__ == '__main__': TestApp().run() RE: Need to render updated recycler view - test - Nov-23-2018 realised i could achieve the same effect using a combination of scroll view and generating button widgets on the go. here's the mod code. But it still doesnt work. I dont know why. There is some error with the binding of the on_release call. I'm attaching the traceback as well. Someone please give me a hint, this problem has been bugging me for a long time now.. from kivy.app import App from kivy.lang import Builder from kivy.uix.widget import Widget from kivy.uix.button import Button from kivy.uix.boxlayout import BoxLayout import openpyxl root = Builder.load_string(''' <RootWid>: orientation: 'vertical' ActionBar: pos_hint: {'top':1} ActionView: use_separator: True ActionPrevious: title: 'StuCheck' with_previous: False ActionOverflow: ActionButton: text: 'Populate list' ActionButton: text: 'Sort list' ActionButton: text: 'Clear list' BoxLayout: orientation: 'horizontal' Widget1: Widget2: <Widget1>: orientation: 'vertical' <Widget2>: orientation: 'vertical' ''') class RootWid(BoxLayout): pass class Widget1(BoxLayout): def __init__(self, **kwargs): super(Widget1, self).__init__(**kwargs) subjects = [Subjects go here] for subject in subjects: btn = Button(text=subject, on_release=self.populate(subject)) Widget1.add_widget(self, btn) def populate(self, subject): wb = openpyxl.load_workbook('sWL.xlsx') sheet = wb[subject] data = [] for row in range (2, sheet.max_row+1): question = sheet.cell(row=row, column=1).value data.append(question) return self.make_buttons(data) def make_buttons(self, data_list): for question in data_list: btn = Button(text=question) Widget2.add_widget(self, btn) class Widget2(BoxLayout): pass class TestApp(App): def build(self): return RootWid() if __name__ == '__main__': TestApp().run() It works if i remove the on_release call, but it makes the whole app useless...Kindle advice. RE: Need to render updated recycler view - j.crater - Nov-23-2018 You already found where the error happens, when code runs after the mentioned line is removed. Whose "populate()" method are you attempting to call on line #44? RE: Need to render updated recycler view - test - Nov-24-2018 i tried moving the populate method around. first i kept it in widget2, cand called it as: on_release=Widget2().populate()It didnt work. Then i moved it to Widget1 and tried: on_release=self.populate()It behaves the same way in terms of output. Am i making the call the wrong way? Kindly advice. RE: Need to render updated recycler view - j.crater - Nov-24-2018 What I asked was intended more in the way of "does your widget class even provide the populate method?" You'll need to do research on that. But I suspect the class does not have the populate method and that's why error message says "None is not callable". RE: Need to render updated recycler view - test - Nov-24-2018 ^_^ The call needed a lambda function, and i was passing it a normal function! That was the error. Thank you sir :D But then running my code puts everything into the same side. I'll attach picture of the output. I want it to come on the right side and i want it to be scrollable. Here is my semi-fiexed code and its output. from kivy.app import App from kivy.lang import Builder from kivy.uix.widget import Widget from kivy.uix.button import Button from kivy.uix.boxlayout import BoxLayout import openpyxl root = Builder.load_string(''' <RootWid>: orientation: 'vertical' ActionBar: pos_hint: {'top':1} ActionView: use_separator: True ActionPrevious: title: 'StuCheck' with_previous: False ActionOverflow: ActionButton: text: 'Populate list' ActionButton: text: 'Sort list' ActionButton: text: 'Clear list' BoxLayout: orientation: 'horizontal' ScrollView: Widget1: ScrollView: Widget2: <Widget1>: orientation: 'vertical' <Widget2>: orientation: 'vertical' ''') class RootWid(BoxLayout): pass class Widget1(BoxLayout): def __init__(self, **kwargs): super(Widget1, self).__init__(**kwargs) subjects = ['Anatomy', 'Biochemistry', 'Physiology', 'Pharmacology', 'Pathology', 'Microbiology', 'FMT', 'Ophthalmology', 'ENT', 'PSM', 'OG', 'Surgery', 'Internal_Medicine', 'Paediatrics', 'Anaesthesiology', 'Radiology', 'Dermatology', 'Orthopaedics', 'Psychiatry'] for subject in subjects: btn = Button(text=subject, on_release= lambda x: self.populate(subject)) Widget1.add_widget(self, btn) def populate(self, subject): wb = openpyxl.load_workbook('sWL.xlsx') sheet = wb[subject] data = [] for row in range (2, sheet.max_row+1): question = sheet.cell(row=row, column=1).value data.append(question) return self.make_buttons(data) def make_buttons(self, data_list): self.clear_widgets() for question in data_list: btn = Button(text=question) Widget2().add_widget(self, btn) class Widget2(BoxLayout): pass class TestApp(App): def build(self): return RootWid() if __name__ == '__main__': TestApp().run() RE: Need to render updated recycler view - j.crater - Nov-24-2018 Glad you found the source of error! I get an error (no file found) when following the ling to images. Can you check whether the uploading was successful and retry if necessary? RE: Need to render updated recycler view - test - Nov-25-2018 These are files on my computer. i hope this works.. https://drive.google.com/open?id=1s_5nzFWZcGwHupVTGbbvnkwVy6gafyWK https://drive.google.com/open?id=1QpOjd1BHP2T04p6xh_cor1aKp4PiDxrt i uploaded them to drive. |