简体   繁体   English

在Python中手动更新Kivy RecycleView

[英]Manually updating Kivy RecycleView in Python

RecycleView isn't updating its table when data is changed from outside the RecycleView class. 从RecycleView类外部更改数据时,RecycleView不会更新其表。 As a summary I'm trying to create a simple stock portfolio manager. 作为总结,我试图创建一个简单的股票投资组合经理。

I have a custom RecycleView class which I call RecycleViewPortfolio that inherits from RecycleView. 我有一个自定义的RecycleView类,我将其称为RecycleViewPortfolio,该类继承自RecycleView。 From my .kv file I have three buttons connected to functions populate_1, populate_2 and populate_3 within my RecycleViewPortfolio class. 在我的.kv文件中,我有三个按钮连接到RecycleViewPortfolio类中的函数populate_1,populate_2和populate_3。 Whenever I press a button and call any of the populate functions, the RecycleView behaves as expected. 每当我按下一个按钮并调用任何填充函数时,RecycleView的行为就会像预期的那样。

However whenever I change the RecycleView data from outside the RecycleViewPortfolio class the table doesn't update. 但是,每当我从RecycleViewPortfolio类外部更改RecycleView数据时,表都不会更新。 For example I have setup a global variable which I have imported to both my .py file and .kv file. 例如,我设置了一个全局变量,该变量已导入到我的.py文件和.kv文件中。 I would like to be able to update the table whenever this data in this global variable is changed. 每当此全局变量中的此数据更改时,我希望能够更新表。

I have tried looking at the documentation from Kivy which mentions different functions that are suppose to solve this issue. 我尝试查看Kivy的文档,其中提到了应该解决此问题的不同功能。 But I guess Im clueless to how to apply these methods. 但是我想我对如何应用这些方法一无所知。

import StackOverflow.globalvariables as GlobalVariables
from kivy.app import App
from kivy.uix.popup import Popup
from kivy.uix.button import Button
from kivy.uix.gridlayout import GridLayout
from kivy.uix.behaviors import FocusBehavior
from kivy.uix.recycleview import RecycleView
from kivy.uix.recyclegridlayout import RecycleGridLayout
from kivy.uix.recycleview.views import RecycleDataViewBehavior
from kivy.uix.recycleview.layout import LayoutSelectionBehavior
from kivy.properties import BooleanProperty, ListProperty, ObjectProperty


class AddPopup(Popup):
    """Popup for adding asset"""
    asset_name = ObjectProperty
    asset_price = ObjectProperty
    asset_amount = ObjectProperty
    currency = ObjectProperty
    asset_class = ObjectProperty
    wrapped_button = ObjectProperty()

    def __init__(self, *args, **kwargs):
        super(AddPopup, self).__init__(*args, **kwargs)

    def open(self, correct=True):
        super(AddPopup, self).open(correct)

    def save_asset(self):
        # Make sure no input is empty
        if self.asset_name.text.strip() and self.asset_price.text.strip()\
                and self.asset_amount.text.strip() and self.currency.text.strip()\
                and self.asset_class.text.strip():

            GlobalVariables.rv_data_global = [{'text': self.asset_name.text.strip()},
                                              {'text': self.asset_amount.text.strip()},
                                              {'text': self.asset_price.text.strip()}]
            self.dismiss()

class RecycleViewPortfolio(RecycleView):

    def __init__(self, **kwargs):
        super(RecycleViewPortfolio, self).__init__(**kwargs)
        self.populate_2()

    def populate_1(self):
        root = App.get_running_app().root
        root.add_popup.open(True)
        self.data = GlobalVariables.rv_data_global

    def populate_2(self):
        self.data = [{'text': str(x)} for x in range(0, 6)]

    def populate_3(self):
        self.data = [{'text': str(x)} for x in range(6, 12)]

class PortfolioRoot(GridLayout):
    """root to all screens"""
    add_popup = ObjectProperty(None)
    list = ListProperty([])

    def __init__(self, **kwargs):
        super(PortfolioRoot, self).__init__(**kwargs)
        self.add_popup = AddPopup()

    def test_set_data(self):
        GlobalVariables.rv_data_global = [{'text': str(x)} for x in range(12, 18)]



class SelectableRecycleGridLayout(FocusBehavior, LayoutSelectionBehavior, # View Behavior
                                  RecycleGridLayout):
    ''' Adds selection and focus behaviour to the view. '''


class SelectableButton(RecycleDataViewBehavior, Button): # Data Behavior
    ''' Add selection support to the Label '''
    index = None
    selected = BooleanProperty(True)
    selectable = BooleanProperty(True)

    def refresh_view_attrs(self, rv, index, data):
        ''' Catch and handle the view changes '''
        self.index = index
        return super(SelectableButton, self).refresh_view_attrs(
            rv, index, data)

    def on_touch_down(self, touch):
        ''' Add selection on touch down '''
        if super(SelectableButton, 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


class PortfolioApp(App):
    """App object"""

    def __init__(self, **kwargs):
        super(PortfolioApp, self).__init__(**kwargs)

    def build(self):
        return PortfolioRoot()

PortfolioApp().run()

.kv file .kv文件

    #:kivy 1.10.0
#:import GlobalVariables StackOverflow.globalvariables

<SelectableButton>:
    # Draw a background to indicate selection
    canvas.before:
        Color:
            rgba: (0, 0.517, 0.705, 1) if self.selected else (0, 0.517, 0.705, 1)
        Rectangle:
            pos: self.pos
            size: self.size
    background_color: [1, 0, 0, 1]  if self.selected else [1, 1, 1, 1]  # dark red else dark grey
    on_release:
        print("Pressed")

<WrappedLabel@Label>:
    size_hint_y: None
    height: self.texture_size[1] + (self.texture_size[1]/2)
    markup: True

<RecycleViewPortfolio@RecycleView>:
    viewclass: 'SelectableButton'
    target_id: None
#    id: rv_data_list
    data: GlobalVariables.rv_data_global
    SelectableRecycleGridLayout:
        cols: 3
        key_selection: 'selectable'
        default_size: None, dp(26)
        default_size_hint: 1, None
        size_hint_y: None
        height: self.minimum_height
        multiselect: True
        touch_multiselect: True

<PortfolioRoot>:
    BoxLayout:
        list: rv_data_list
        size: root.size
        orientation: 'vertical'
        WrappedLabel:
            text: "[b] Portfolio Manager [/b]"
            font_size: min(root.height, root.width) / 10
        GridLayout:
            size_hint_y: None
            height: root.height * 0.1
            cols: 4
            rows: 1
            #  Settings
            padding: root.width * 0.001, root.height * 0.001
            spacing: min(root.width, root.height) * 0.001

            Button:
                text: "Add"
                background_color: [1, 1, 1, 1]
                on_release:
                    rv_data_list.populate_1()
                    print("Add")

            Button:
                text: "Change"
                background_color: [1, 1, 1, 1]
                on_release:
                    rv_data_list.populate_2()
                    print("Change")

            Button:
                text: "Remove"
                background_color: [1, 1, 1, 1]
                on_release:
                    rv_data_list.populate_3()
                    print("Remove")

            Button:
                text: "Test"
                background_color: [1, 1, 1, 1]
                on_release:
                    root.test_set_data()
                    print("Test set data")

        RecycleViewPortfolio:
            id: rv_data_list

<AddPopup>:
    size_hint: 0.8, 0.8
    title: "Add Asset"
    title_size: root.height * 0.05
    auto_dismiss: False
    asset_name: asset_name
    asset_price: asset_price
    asset_amount: asset_amount
    currency: currency
    asset_class:asset_class
    wrapped_button: wrapped_button
    BoxLayout:
        orientation: 'vertical'
        GridLayout:
            rows: 5
            cols: 2
            padding: root.width * 0.02, root.height * 0.02
            spacing: min(root.width, root.height) * 0.02

            Label:
                id: asset_name_label
                text: "Asset name"
                halign: "center"
                font_size: root.height/25
                text_size: self.width, None
                center_y: .5
            TextInput:
                id: asset_name
                text: "Asset name"
                halign: "center"
                font_size: root.height/25
                text_size: self.width, None
                center_y: .5
            Label:
                id: asset_price_label
                text: "Asset price"
                halign: "center"
                font_size: root.height/25
                text_size: self.width, None
                center_y: .5
            TextInput:
                id: asset_price
                text: "asset"
                halign: "center"
                font_size: root.height/25
                text_size: self.width, None
                center_y: .5
            Label:
                id: asset_amount_label
                text: "Asset amount"
                halign: "center"
                font_size: root.height/25
                text_size: self.width, None
                center_y: .5
            TextInput:
                id: asset_amount
                text: "Asset amount"
                halign: "center"
                font_size: root.height/25
                text_size: self.width, None
                center_y: .5
            Label:
                id: currency_label
                text: "Asset currency"
                halign: "center"
                font_size: root.height/25
                text_size: self.width, None
                center_y: .5
            TextInput:
                id: currency
                text: "currency"
                halign: "center"
                font_size: root.height/25
                text_size: self.width, None
                center_y: .5
            Label:
                id: asset_class_label
                text: "Asset class"
                halign: "center"
                font_size: root.height/25
                text_size: self.width, None
                center_y: .5
            TextInput:
                id: asset_class
                text: "Asset class"
                halign: "center"
                font_size: root.height/25
                text_size: self.width, None
                center_y: .5
        Button:
            id: wrapped_button
            text: "Save"
            size_hint: 1, None
            height: root.height / 8
            on_release: root.save_asset()
        Button:
            id: wrapped_button
            text: "close"
            size_hint: 1, None
            height: root.height / 8
            on_release: root.dismiss()

globalvariables.py globalvariables.py

# global variables
rv_data_global = []

I expect to be able to create a popup window where I add information which is stored in a global variable and after changes are made I call for the RecycleView to be updated. 我希望能够创建一个弹出窗口,在其中添加存储在全局变量中的信息,并在进行更改后调用RecycleView进行更新。

Edit: Added a working example 编辑:添加了一个工作示例

This example shows how Im able to use the buttons "Change" and "Remove" in order to populate the RecycleView as expected. 本示例说明了我如何能够使用按钮“更改”和“删除”来按预期填充RecycleView。 However when the add button is pressed and the popup window appears and the save button is pressed the RecycleView doesn't update. 但是,当按下添加按钮并出现弹出窗口并按下保存按钮时,RecycleView不会更新。 If the add button is pressed again and directly closed the RecyleView gets updated and shows the correct information. 如果再次按下添加按钮并直接关闭,则RecyleView会更新并显示正确的信息。

The same goes for the "Test" buttons where I call a function which changes the global variable. “测试”按钮也是如此,在这里我调用一个更改全局变量的函数。 From there I have no idea of how to update the view since Im no longer working underneath the RecycleView class. 从那里我不知道如何更新视图,因为Im不再在RecycleView类下工作。

TLDR; TLDR; Method for manually updating the RecycleView after data has been changed. 更改数据后手动更新RecycleView的方法。

I found the answers to one of my questions. 我找到了我的一个问题的答案。 By adding: 通过增加:

self.ids.rv_data_list.data = GlobalVariables.rv_data_global
self.ids.rv_data_list.refresh_from_data()

to the test_set_data function, I am now able to refresh the data as I requested. 到test_set_data函数,我现在能够根据需要刷新数据。 Hence the magic was the refresh_from_data() method. 因此,魔术就是refresh_from_data()方法。

Through the App.get_running_app() I was able to access the refresh_from_data() command from the popup class. 通过App.get_running_app(),我可以从弹出类中访问refresh_from_data()命令。

root = App.get_running_app().root
root.ids.rv_data_list.data = GlobalVariables.rv_data_global
root.ids.rv_data_list.refresh_from_data()

I seem to have solved my own issues here. 我似乎已经在这里解决了自己的问题。 But if anyone has a better or cleaner solution, please let me know. 但是,如果有人有更好或更干净的解决方案,请告诉我。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM