繁体   English   中英

在小部件 (Kivy) 中存储切换按钮状态

[英]Store toggle button states in the widget (Kivy)

我有一个回收视图,其中包含带有两个按钮的 BoxLayouts,每个 BoxLayout 共享一个组。 我想要 function 的按钮作为 StackOverflow 上的上下投票按钮。 但是,目前的问题是按钮的 state 没有存储在 BoxLayout 小部件中,这导致它们在不应该出现在屏幕上的小部件上出现多次。

这里有一个类似的问题: How can I keep my Kivy ToggleButton state when scrolling using a RecycleView?

但是我无法在我的代码中实现相同的逻辑,此外,如果选择了一个按钮,则不能选择另一个按钮(就像不能同时对 StackOverflow 上的问题进行投票一样)并且能够根本没有选择任何按钮。

我在下面包含了一个最小的示例,其中一些行从 .kv 文件中被注释掉,因此它仍然可以运行但仍然存在问题。 (代码与上面链接的问题尽可能相似,因为我的尝试偏离了方向并且没有奏效)

任何帮助将不胜感激,我不认为我离得很远,我会继续尝试不同的事情

主程序

from random import randint

from kivy.app import App
from kivy.uix.label import Label
from kivy.clock import Clock
from kivy.graphics import Color, Rectangle
from kivy.uix.recycleview import RecycleView
from kivy.uix.boxlayout import BoxLayout
from kivy.properties import StringProperty, ListProperty

class NewPostGrid(BoxLayout):
    votes_ = StringProperty()
    message_id_ = StringProperty()
    text_ = StringProperty()
    group_ = StringProperty()
    _size = ListProperty()
    vote_state_up = StringProperty()
    vote_state_down = StringProperty()

    #def update_message_size(self, message_id, texture_size): # Updates the '_size' value in rv_data_list based on the texture_size
    #    App.get_running_app().rv_data_list[int(message_id)] = {**App.get_running_app().rv_data_list[int(message_id)], '_size':[0, texture_size[1]]}
    #    #print(App.get_running_app().rv_data_list)

class SizeLabel(Label):
    def __init__(self, *args, **kwargs):  
        Label.__init__(self, *args, **kwargs)  
        Clock.schedule_once(lambda dt: self.initialize_widget(), 0.002)

    def initialize_widget(self):
        self.canvas.before.add(Color(1, 1, 1, 0))  
        self.canvas.before.add(Rectangle(pos=self.pos, size=self.size))  
        self.text_size = self.size
        self.text = ''

class RV(RecycleView):
    message_id_num = 0

    def __init__(self, **kwargs):
        super().__init__(**kwargs)
        App.get_running_app().rv_data_list = []

    def generate_post(self): # This is only to test posts with different line height
        e = ['Test post ID: ', str(self.message_id_num)]
        for i in range(randint(1, 8)): e.append('\n')
        e.append('end of post')
        return "".join(e)

    def add(self):
        l = len(App.get_running_app().rv_data_list)
        text = self.generate_post()
        sl = SizeLabel(text=text)
        sl.texture_update()
        print(sl.text)
        App.get_running_app().rv_data_list.extend([{'message_id_': str(self.message_id_num),
                                                    'text_': text, '_size': sl.texture_size,
                                                    'group_': str(self.message_id_num), 'votes_': str(20), 'vote_state_up': 'down', 'vote_state_down': 'normal'}])
        self.message_id_num = self.message_id_num + 1

    def adjust_vote_state(self, id_):
        for d in self.data:
            if d['text_'] == id_.text_:
                d['state'] == id_.ids.button_up.state
                id_.state = id_.ids.button_up.state


class DemoApp(App):
    # One post format = {'message_id':0, 'text':'post_test_here','_size':[0,0], '_group':str(0), '_score':20}
    # Text fromat string = [font=Nunito-Bold.ttf][color=161616]Someone:[/color][/font]\n
    rv_data_list = ListProperty()

    def up_vote(self, button, mode): # Not part of the problem
        if button.state == 'down':
            if mode == 'all':
                print("+1 upvote for message index:" + str(button.parent.parent.message_id) + ' in all posts')
            else:
                print("+1 upvote for message index:" + str(button.parent.parent.message_id) + ' in top posts')
    
    def down_vote(self, button, mode): # Not part of the problem
        if button.state == 'down':
            if mode == 'all':
                print("-1 upvote for message index:" + str(button.parent.parent.message_id) + ' in all posts')
            else:
                print("-1 upvote for message index:" + str(button.parent.parent.message_id) + ' in top posts')





if __name__ == '__main__':
    DemoApp().run()

演示.kv

<NewPostGrid>:
    spacing: "6dp"
    message_id: root.message_id_
    BoxLayout:
        id: voting_menu
        orientation: "vertical"
        spacing: "2dp"
        size_hint: .2, None
        height: label.height
        ToggleButton:
            id: button_up
            #on_state: app.up_vote(self, 'all')
            group: root.group_
            #state: root.vote_state_up
            text: "UP"
            color: (1,1,1,1) if self.state=='normal' else (.8,0,0,1)
            font_size: "10dp"
            size_hint: 1, .3
            background_color: .2, .2, .2, 0
            #on_release: app.root.ids.rv.adjust_vote_state(root)
            canvas.before:
                Color:
                    rgba: (.1,.1,.1,1)
                RoundedRectangle:
                    pos: self.pos
                    size: self.size
                    radius: [6,]
            canvas:
                Color:
                    rgba: (.2,.2,.2,1)
                Line:
                    width: 1.4
                    rounded_rectangle:(self.x,self.y,self.width,self.height, 5)
        
        Label:
            id: vote_count
            text: root.votes_
            size_hint: 1, .4
            multiline: False

        ToggleButton:
            id: button_down
            #on_state: app.down_vote(self, 'all')
            group: root.group_
            #state: root.vote_state_down
            text: "DOWN"
            color: (1,1,1,1) if self.state=='normal' else (.8,0,0,1)
            font_size: "10dp"
            size_hint: 1, .3
            background_color: .2, .2, .2, 0
            canvas.before:
                Color:
                    rgba: (.1,.1,.1,1)
                RoundedRectangle:
                    pos: self.pos
                    size: self.size
                    radius: [6,]
            canvas:
                Color:
                    rgba: (.2,.2,.2,1)
                Line:
                    width: 1.4
                    rounded_rectangle:(self.x,self.y,self.width,self.height, 5)
    Label:
        id: label
        text: root.text_
        padding: "10dp", "12dp"
        size_hint: .9, None
        height: self.texture_size[1]
        font_size: "12dp"
        text_size: self.width, None
        color: 0,0,0,1
        multiline: True
        markup: True
        
        #on_texture_size: root.update_message_size(root.message_id, self.texture_size)

        pos: self.x, self.y

        canvas.before:
            Color:
                rgba: (0.8, 0.8, 0.8, 1)
            RoundedRectangle:
                size: self.texture_size
                radius: [5, 5, 5, 5]
                pos: self.x, self.y
        canvas:
            Color:
                rgba:0.8,0,0,1
            Line:
                width: 1.4
                rounded_rectangle:(self.x,self.y,self.width,self.height, 5)
BoxLayout:
    orientation: 'vertical'
    Button:
        size_hint_y: None
        height: 48
        text: 'Add widget to RV list'
        on_release: rv.add()
    
    RV:                          # A Reycleview
        id: rv
        viewclass: 'NewPostGrid'  # The view class is TwoButtons, defined above.
        data: app.rv_data_list  # the data is a list of dicts defined below in the RV class.
        scroll_type: ['bars', 'content']
        bar_width: 2
        RecycleBoxLayout:        
            # This layout is used to hold the Recycle widgets
            # default_size: None, dp(48)   # This sets the height of the BoxLayout that holds a TwoButtons instance.
            key_size: '_size'  # key for the datalist
            default_size_hint: 1, None
            size_hint_y: None
            spacing: '20dp'
            height: self.minimum_height   # To scroll you need to set the layout height.
            orientation: 'vertical'
            padding: ['10dp', '20dp']

我认为在更改ToggleButton state时调整您的demo.kv以修改data将起作用。 这是我认为可行的demo.kv的修改版本:

<NewPostGrid>:
    spacing: "6dp"
    message_id: root.message_id_
    BoxLayout:
        id: voting_menu
        orientation: "vertical"
        spacing: "2dp"
        size_hint: .2, None
        height: label.height
        ToggleButton:
            id: button_up
            state: root.vote_state_up
            #on_state: app.up_vote(self, 'all')
            on_state:
                root.vote_state_up = self.state
                app.rv_data_list[int(root.message_id_)]['vote_state_up'] = self.state
            group: root.group_
            #state: root.vote_state_up
            text: "UP"
            color: (1,1,1,1) if self.state=='normal' else (.8,0,0,1)
            font_size: "10dp"
            size_hint: 1, .3
            background_color: .2, .2, .2, 0
            #on_release: app.root.ids.rv.adjust_vote_state(root)
            canvas.before:
                Color:
                    rgba: (.1,.1,.1,1)
                RoundedRectangle:
                    pos: self.pos
                    size: self.size
                    radius: [6,]
            canvas:
                Color:
                    rgba: (.2,.2,.2,1)
                Line:
                    width: 1.4
                    rounded_rectangle:(self.x,self.y,self.width,self.height, 5)
        
        Label:
            id: vote_count
            # text: root.votes_
            text: button_up.state + ' ' + button_down.state
            size_hint: 1, .4
            multiline: False

        ToggleButton:
            id: button_down
            state: root.vote_state_down
            #on_state: app.down_vote(self, 'all')
            on_state:
                root.vote_state_down = self.state
                app.rv_data_list[int(root.message_id_)]['vote_state_down'] = self.state
            group: root.group_
            #state: root.vote_state_down
            text: "DOWN"
            color: (1,1,1,1) if self.state=='normal' else (.8,0,0,1)
            font_size: "10dp"
            size_hint: 1, .3
            background_color: .2, .2, .2, 0
            canvas.before:
                Color:
                    rgba: (.1,.1,.1,1)
                RoundedRectangle:
                    pos: self.pos
                    size: self.size
                    radius: [6,]
            canvas:
                Color:
                    rgba: (.2,.2,.2,1)
                Line:
                    width: 1.4
                    rounded_rectangle:(self.x,self.y,self.width,self.height, 5)
    Label:
        id: label
        text: root.text_
        padding: "10dp", "12dp"
        size_hint: .9, None
        height: self.texture_size[1]
        font_size: "12dp"
        text_size: self.width, None
        color: 0,0,0,1
        multiline: True
        markup: True
        
        #on_texture_size: root.update_message_size(root.message_id, self.texture_size)

        pos: self.x, self.y

        canvas.before:
            Color:
                rgba: (0.8, 0.8, 0.8, 1)
            RoundedRectangle:
                size: self.texture_size
                radius: [5, 5, 5, 5]
                pos: self.x, self.y
        canvas:
            Color:
                rgba:0.8,0,0,1
            Line:
                width: 1.4
                rounded_rectangle:(self.x,self.y,self.width,self.height, 5)
BoxLayout:
    orientation: 'vertical'
    Button:
        size_hint_y: None
        height: 48
        text: 'Add widget to RV list'
        on_release: rv.add()
    
    RV:                          # A Reycleview
        id: rv
        viewclass: 'NewPostGrid'  # The view class is TwoButtons, defined above.
        data: app.rv_data_list  # the data is a list of dicts defined below in the RV class.
        scroll_type: ['bars', 'content']
        bar_width: 2
        RecycleBoxLayout:        
            # This layout is used to hold the Recycle widgets
            # default_size: None, dp(48)   # This sets the height of the BoxLayout that holds a TwoButtons instance.
            key_size: '_size'  # key for the datalist
            default_size_hint: 1, None
            size_hint_y: None
            spacing: '20dp'
            height: self.minimum_height   # To scroll you need to set the layout height.
            orientation: 'vertical'
            padding: ['10dp', '20dp']

暂无
暂无

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

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