简体   繁体   中英

Python Kivy - Call methods across different class

I am new to coding, a few months with Python and trying to wrap my head around Kivy. I think there is a simple solution to this but I am struggling when it comes to calling a method in one class from another. Not even sure if this is possible, my OOP wouldn't be very strong!!

Would appreciate if someone could explain this to me. I've looked online but still struggling to understand what I need to do.

i have a simple code that had a label and 3 toggle buttons, the label text changes to show how many toggle buttons are pressed. Below is the original code.

What I am trying to do create the toggle buttons using a loop so that the number of toggle buttons can be easily altered. i have achieved this but when I try and bind the method to the toggle button the code fails with. I also tried defining a method within the "Tbtn" class to call the Main.Counter() but this didn't work either.

The line

    self.bind(on_state = Main.counter())

in the init of the toggle button is where i am going wrong I think.

Any help and even an explanation would be great. Not the first time I have been stuck on this!! Thanks

Original Code:

from kivy.app import App
from kivy.uix.togglebutton import ToggleButton
from kivy.uix.boxlayout import BoxLayout
from kivy.properties import NumericProperty

class Tbtn(ToggleButton):
    pass

class Header_Box(BoxLayout):
    pass

class Counter(BoxLayout):
    pass

class Main(BoxLayout):
    count = NumericProperty()

    def counter(self,widget):
        toggles = []
        for child in self.ids.Seat_Box.children:
            if isinstance(child, ToggleButton):
                if child.state == 'down':
                    toggles.append(child.text)
        self.count = len(toggles)
        print(self.count)


class TestApp(App):
    def build(self):
        return Main()

TestApp().run()

The KV file:

<Main>:
    name: "main"
    BoxLayout:

        orientation: "vertical"
        Header_Box:
            Label:
                text: str(root.count)

        Counter:
            id: Seat_Box            
            Tbtn:
                id: btn1
                on_state: root.counter(self)
            Tbtn:
                id: btn2
                on_state: root.counter(self)
            Tbtn:
                id: btn2
                on_state: root.counter(self)

Code with for Loop:

from kivy.app import App
from kivy.uix.togglebutton import ToggleButton
from kivy.uix.boxlayout import BoxLayout
from kivy.properties import NumericProperty

class Tbtn(ToggleButton):
    def __init__(self, **kwargs):
        super().__init__(**kwargs)

        self.bind(on_state = Main().counter())

class Header_Box(BoxLayout):
    pass

class Counter(BoxLayout):
    def __init__(self, **kwargs):
        super().__init__(**kwargs)

        for x in range(3):
            btn = Tbtn()
            self.add_widget(btn)

class Main(BoxLayout):
    count = NumericProperty()

    def counter(self,widget):
        toggles = []
        for child in self.ids.Seat_Box.children:
            if isinstance(child, ToggleButton):
                if child.state == 'down':
                    toggles.append(child.text)
        self.count = len(toggles)
        print(self.count)


class TestApp(App):
    def build(self):
        return Main()

TestApp().run()

KV file:

<Main>:
    name: "main"
    BoxLayout:

        orientation: "vertical"
        Header_Box:
            Label:
                text: str(root.count)

        Counter:
            id: Seat_Box      

Firstly, Remove self.bind(on_state = Main().counter()) .

I suggest you to solve this in.kv side.

Way 1-.kv side: Add this below your.kv file:

<Tbtn>:
    on_state: app.get_running_app().root.counter(self)

Way 2-.py side: Add this in Tbtn class.

def on_release(self):
    App.get_running_app().root.counter(self)

Although the other answer already solved your issue, the following made me post this one.

Any help and even an explanation would be great...

Technically the following line,

self.bind(on_state = Main().counter())

is wrong for various reasons. Let's try to figure this out.

The method on_state is kind of generic one not a default event (like on_press etc.). That's why bind(on_state = some_callback) won't work.

Again you did Main().counter() which actually creates a new instance of Main (which may or may not be related to the root , and here it's of course not) and assigned to its method.

It seems you want to just access one of Main widget's (which happens to be the root widget here) method.

Since you used kvlang , this could be done more efficiently as follows,

<Tbtn>:
    on_state: app.root.counter()

You can find more about this in the kvlang doc .

Now in .py you just define the class along with some other changes,

class Tbtn(ToggleButton):
    pass
.
.
.
class Main(BoxLayout):
    count = NumericProperty()

    def counter(self): # Pass no extra args as you haven't done in 'on_state' method.
        toggles = []
        .
        .
        .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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