Poupou Net
Poupou Net

Reputation: 11

kivy button event in For loop

I am looking to create buttons in a for loop. my problem is on the on_press property. I read stuff about closures (if that's the problem). But I did not really understand.

a little help.thx sorry for english.

the .py code:

import kivy
import webbrowser


from kivy.app import App
from kivy.uix.button import Label, Button
from kivy.uix.boxlayout import BoxLayout

word_list = ['one', 'two', 'three', 'four']


class LoopButton(BoxLayout):
    def aff(self):
        for w in word_list:
            url = 'https://fr.wiktionary.org/wiki/' + w
            button = Button(text= w, on_press= webbrowser.open(url))
            self.ids.grid.add_widget(button)


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


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

and the .kv

    #:kivy 1.10

<LoopButton>
    Label:
        text: 'TEST'
    Button:
        id: 'affiche'
        text: 'afficher'
        on_press: root.aff()
    GridLayout:
        id: grid
        cols: 2

Upvotes: 0

Views: 2660

Answers (2)

ikolim
ikolim

Reputation: 16011

Problem

on_press=webbrowser.open(url) will open the webbrowser when instantiating/creating Button widget.

Solution

Create a on_press callback method and create the url in the callback method

Snippet

class LoopButton(BoxLayout):
    def aff(self):
        for w in word_list:
            button = Button(text=w, on_press=self.open_webbrowser)
            self.ids.grid.add_widget(button)

    def open_webbrowser(self, instance):
        url = 'https://fr.wiktionary.org/wiki/' + instance.text
        webbrowser.open(url)

Event dispatcher » bind()

In general, property callbacks are called with 2 arguments (the object and the property’s new value) and event callbacks with one argument (the object).

Output

Img01 Img02

Upvotes: 1

eyllanesc
eyllanesc

Reputation: 243897

on_press wants you to pass a callback, and you are passing the evaluated function to it, so there are 3 possible solutions.

  1. Use functools.partial:

    import kivy
    import webbrowser
    from functools import partial
    ...
    
    class LoopButton(BoxLayout):
        def aff(self):
            for w in word_list:
                url = 'https://fr.wiktionary.org/wiki/' + w
                button = Button(text= w, on_press=partial(webbrowser.open, url))
                ...
    
  2. Use lambda:

    button = Button(text= w, on_press= lambda *args, url=url: webbrowser.open(url))
    
  3. Create a callback:

    class LoopButton(BoxLayout):
        def aff(self):
            for w in word_list:
                button = Button(text= w, on_press=self.callback)
                self.ids.grid.add_widget(button)
    
        def callback(self, instance):
            url = 'https://fr.wiktionary.org/wiki/' + instance.text
            webbrowser.open(url)
    

Upvotes: 1

Related Questions