Skip to content Skip to sidebar Skip to footer

On_press In Kivy Keeps Running At Start Up Instead

I'm having the problem where the application is running the on_press command of a button immediately before anything else happens. If I us a .kv for the layout it works fine, but I

Solution 1:

You're not passing a callback into Button, you're actually executing the function at that point. Change this:

launchbutton = Button( text =row[0], background_normal ='tile.png', 
    on_press = self.Launcher(row[1]) 
)

To this:

launchbutton = Button( text = row[0], background_normal = 'tile.png', 
    on_press = lambda: self.Launcher(row[1])
)

Now you're passing in an unnamed function that will call self.Launcher when an on_press event is raised, rather than setting it to the return result of self.Launcher when the Button is created.

Update: for some reason, on_press and on_release events aren't actually assigned to callbacks in Button.__init__, the events themselves are just registered with no outcome. (This seems a bug to me, but I'm not familiar enough with Kivy to say for certain.) You need to explicitly bind the callback for it to work:

launchbutton = Button( text = row[0], background_normal = 'tile.png' )
launchbutton.bind( on_press = lambda widget: self.Launcher( row[1] ) )

Note that the callback actually receives an argument, which I've included as widget in the lambda.

Update 2: I should have caught this earlier, sorry, but I had reduced my local test case down to one button. When you do this in a loop:

funcs = []
for x in xrange(10):
    funcs.append( lambda: x)

Every call to funcs[n]() where n in [0..9] will return 9, and not the value of n as expected. The lambda has created a closure which includes x from the surrounding scope. However, the value of that x changes over the course of the loop, and by the end it is 9. Now all lambdas in funcs are holding a reference to 9. You can avoid this by adding the value you want to the lambda's local scope:

    funcs.append( lambda x=x: x)

This points the lambda local variable x at the same object as is referred to by the loop variable x in the outer scope. It's more obvious what happens if we use different variable names:

    funcs.append( lambda inner_x=x: inner_x)

But the x=x form is very common in this case. So, to ensure that each button uses the correct value, you should be able to do:

launchbutton.bind( on_press = lambda widget, appname=row[1]: self.Launcher( appname ) )

Here, you bind the current value of row[1] to appname in the lambda's local scope, so that's what it will pass to Launcher when it's called.

Solution 2:

Working Code. create a CSV file with the following structure

1|hello
2|world

save as test.csv

from kivy.app import App
from kivy.uix.widget import Widget
from kivy.uix.gridlayout import GridLayout
from kivy.uix.button import Button
from kivy.config import Config
import csv
import os

classAppBase(Widget):
    defLauncher(self, launchapp):
        #os.system(launchapp)print(f"Application to be launched is {launchapp}")

    defBuildLayout(self):
        layout = GridLayout( rows = 4, row_force_default = True, row_default_height = 100, col_force_default = True, col_default_width = 300)
        withopen('test.csv', 'r') as f:
            reader = csv.reader(f, delimiter='|')
            for row in reader:
                print(row)
                launchbutton = Button(text = row[0])


                launchbutton.bind( on_press = lambda widget, appname=row[1]: self.Launcher( appname ) )
                layout.add_widget(launchbutton)
        return layout

classMyApp(App):
    defbuild(self):
        Config.set('graphics','width', 1920)
        Config.set('graphics', 'height', 400)
        Config.set('graphics', 'maxfps', 30)
        return AppBase().BuildLayout()
if __name__ == '__main__':
    MyApp().run()

Post a Comment for "On_press In Kivy Keeps Running At Start Up Instead"