0

我遇到的问题是应用程序在其他任何事情发生之前立即运行按钮的 on_press 命令。如果我使用 .kv 作为布局,它可以正常工作,但我希望能够使用简单的列表来管理按钮。

class AppBase(Widget):

    def Launcher(self, launchapp):
        os.system(launchapp)

    def BuildLayout(self):
        layout = GridLayout( rows=4, row_force_default = True, row_default_height = 100, col_force_default = True, col_default_width = 300 )
        with open('config.txt', 'rb') as f:
            reader = csv.reader(f, delimiter="|")
            for row in reader:
                launchbutton = Button( text = row[0], background_normal = 'tile.png', on_press = self.Launcher(row[1]) )
                layout.add_widget(launchbutton)
        return layout


class MyApp(App):

    def build(self):
        Config.set('graphics', 'width', 1920)
        Config.set('graphics', 'height', 400)
        return AppBase().BuildLayout()

if __name__ == '__main__':
    MyApp().run()
4

2 回答 2

4

您没有将回调传递给Button,您实际上是在此时执行该函数。改变这个:

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

对此:

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

现在您传入一个未命名的函数,该函数将self.Launcher在引发事件时调用,而不是将其设置为创建时on_press的返回结果。self.LauncherButton

更新:由于某种原因,on_press事件on_release实际上并没有分配给回调Button.__init__,事件本身只是注册而没有结果。(这对我来说似乎是一个错误,但我对 Kivy 还不够熟悉,无法肯定地说。)你需要明确地bind回调它才能工作:

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

请注意,回调实际上接收了一个参数,我已将其包含widget在 lambda 中。

更新 2:对不起,我应该早点发现这个问题,但我已将本地测试用例减少到一个按钮。当您在循环中执行此操作时:

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

每次调用funcs[n]()wheren in [0..9]都会返回9,而不是n预期的值。lambda 创建了一个包含x来自周围范围的闭包。但是,它的值x在循环过程中会发生变化,到最后它是9. 现在所有 lambdasfuncs都持有对9. 您可以通过将所需的值添加到 lambda 的本地范围来避免这种情况:

    funcs.append( lambda x=x: x)

这将 lambda 局部变量指向与外部范围中x的循环变量引用的相同对象。x如果我们使用不同的变量名,会发生什么更明显:

    funcs.append( lambda inner_x=x: inner_x)

但这种x=x形式在这种情况下很常见。因此,为确保每个按钮使用正确的值,您应该能够:

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

在这里,您将 to 的当前值绑定row[1]appnamelambda 的本地范围内,这样它就会Launcher在被调用时传递给它。

于 2012-09-11T16:29:41.877 回答
1

工作代码。创建具有以下结构的 CSV 文件

1|hello
2|world

另存为 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

class AppBase(Widget):
    def Launcher(self, launchapp):
        #os.system(launchapp)
        print(f"Application to be launched is {launchapp}")

    def BuildLayout(self):
        layout = GridLayout( rows = 4, row_force_default = True, row_default_height = 100, col_force_default = True, col_default_width = 300)
        with open('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

class MyApp(App):
    def build(self):
        Config.set('graphics','width', 1920)
        Config.set('graphics', 'height', 400)
        Config.set('graphics', 'maxfps', 30)
        return AppBase().BuildLayout()
if __name__ == '__main__':
    MyApp().run()
于 2021-08-14T11:39:08.783 回答