我遇到的问题是应用程序在其他任何事情发生之前立即运行按钮的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()
答案 0 :(得分:3)
你没有将回调传递给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
,而不是在{{1}时将其设置为self.Launcher
的返回结果已创建。
更新:由于某种原因,Button
和on_press
事件实际上并未分配给on_release
中的回调,事件本身只是注册为否结果。 (这对我来说似乎是一个错误,但我对Kivy不太熟悉,可以肯定地说。)你需要明确地Button.__init__
回调它才能起作用:
bind
请注意,回调实际上会收到一个参数,我将其作为launchbutton = Button( text = row[0], background_normal = 'tile.png' )
launchbutton.bind( on_press = lambda widget: self.Launcher( row[1] ) )
包含在lambda中。
更新2:我应该早点抓住这个,抱歉,但我已将本地测试用例缩减为一个按钮。当你在循环中执行此操作时:
widget
对funcs = []
for x in xrange(10):
funcs.append( lambda: x)
的每次通话,其中funcs[n]()
将返回n in [0..9]
,而不是预期的9
值。 lambda创建了一个闭包,其中包含来自周围范围的n
。但是,x
的值会在循环过程中发生变化,最后变为x
。现在9
中的所有lambdas都持有对funcs
的引用。您可以通过将您想要的值添加到lambda的本地范围来避免这种情况:
9
这将lambda局部变量 funcs.append( lambda x=x: x)
指向外部作用域中循环变量x
所引用的同一对象。如果我们使用不同的变量名,会发生什么更明显:
x
但 funcs.append( lambda inner_x=x: inner_x)
形式在这种情况下很常见。因此,为了确保每个按钮使用正确的值,您应该能够:
x=x
在这里,您将lambda的本地范围中的launchbutton.bind( on_press = lambda widget, appname=row[1]: self.Launcher( appname ) )
的当前值绑定到row[1]
,这样就可以在调用它时传递给appname
。