# 导入Kivy的App类,它是所有kivy应用的基类
from kivy.app import App
# kivy内置了丰富的控件widget 如 按钮button 复选框checkbox 标签label 输入框textinput 滚动容器scrollable container等
from kivy.uix.button import Button
# 引入BoxLayout 布局
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.screenmanager import ScreenManager, Screen
class IndexPage(BoxLayout):
# 初始化
def __init__(self, **kwargs):
super().__init__(**kwargs)
# 添加一个按钮
self.join = Button(text="Hello World")
# 将按钮添加到页面控件中
self.add_widget(self.join)
# 从App类中继承了kivy应用组基本的方法,如创建窗口、设置窗口的大小和位置等
class TestApp(App):
def build(self):
self.sm = ScreenManager()
#build()方法返回的控件,在kivy中称之为“根控件” root widget
# kivy将自动缩放根控件,让它填满整个窗口
screen = Screen(name = 'index')
screen.add_widget(IndexPage)
self.sm.add_widget(screen)
return self.sm
# 当.py文件被直接运行时, if__name__ == '__main__' 之下的代码块将被运行
# 当.py文件以模块的形式被导入时,if __name__ == '__main__' 之下的代码块不被运行
if __name__ == '__main__':
TestApp().run() #启动应用程序
TypeError: unbound method EventDispatcher.bind() needs an argument
如图报错,是因为第二十八行的代码中应该做这样修改
screen.add_widget(IndexPage())
即可正常运行。
kivy中 screen.add_widget(IndexPage()) 为什么不可以用 screen.add_widget(IndexPage)
在Kivy框架中,screen.add_widget(IndexPage())
和 screen.add_widget(IndexPage)
之间的主要区别在于它们如何引用和实例化IndexPage
类。
1. screen.add_widget(IndexPage())
当你使用IndexPage()
时,你实际上是在调用IndexPage
类的构造函数(也称为__init__
方法),从而创建了一个IndexPage
的实例。这个实例是一个Kivy小部件(Widget)对象,可以被添加到其他小部件中,比如屏幕(Screen)或布局(Layout)。因此,screen.add_widget(IndexPage())
是正确的用法,因为它将一个IndexPage
的实例作为子小部件添加到了screen
上。
2. screen.add_widget(IndexPage)
然而,当你只写IndexPage
而不加括号时,你并没有创建IndexPage
的实例,而是引用了IndexPage
类本身。在Kivy中,你不能直接将一个类作为小部件添加到另一个小部件中;你必须添加类的实例。因此,screen.add_widget(IndexPage)
会导致一个错误,因为add_widget
方法期望一个Widget
的实例作为参数,而不是一个类。
总结
IndexPage()
创建了一个IndexPage
的实例。IndexPage
引用了IndexPage
类本身,而不是其实例。
在Kivy(以及大多数面向对象的编程语言)中,类是用于创建对象的蓝图或模板。对象(或实例)是类的一个具体实现,包含类的所有属性和方法,以及可能的额外数据。因此,当你想要使用某个类的功能时,你需要先创建该类的一个实例。
另一方面,在使用纯py文件时,onpress要和bind()方法相搭配才能实现页面转换功能以及current的赋值。
# 导入Kivy的App类,它是所有kivy应用的基类
from kivy.app import App
# kivy内置了丰富的控件widget 如 按钮button 复选框checkbox 标签label 输入框textinput 滚动容器scrollable container等
from kivy.uix.button import Button
# 引入BoxLayout 布局
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.floatlayout import FloatLayout
from kivy.uix.screenmanager import ScreenManager, Screen
class IndexPage(BoxLayout):
# 初始化
def __init__(self, **kwargs):
super().__init__(**kwargs)
# 添加一个按钮
self.join = Button(text="Hello World")
self.join.bind(on_press=self.go2)
# 将按钮添加到页面控件中
self.add_widget(self.join)
# 从App类中继承了kivy应用组基本的方法,如创建窗口、设置窗口的大小和位置等
def go2(self, instance):
sm = App.get_running_app().root
sm.current='menu'
class MenuPage(FloatLayout):
def __init__(self, **kwargs):
super().__init__(**kwargs)
class TestApp(App):
def build(self):
self.sm = ScreenManager()
#build()方法返回的控件,在kivy中称之为“根控件” root widget
# kivy将自动缩放根控件,让它填满整个窗口
screen = Screen(name = 'index')
screen.add_widget(IndexPage())
self.sm.add_widget(screen)
screen = Screen(name ='menu')
screen.add_widget(MenuPage())
self.sm.add_widget(screen)
return self.sm
# 当.py文件被直接运行时, if__name__ == '__main__' 之下的代码块将被运行
# 当.py文件以模块的形式被导入时,if __name__ == '__main__' 之下的代码块不被运行
if __name__ == '__main__':
TestApp().run() #启动应用程序
def go2(self, instance):函数中为什么必须要加个instance?

TypeError: go2() takes 1 positional argument but 2 were given
在Kivy的go2函数中,instance
参数是on_press
事件自动传递的,代表触发事件的按钮实例。尽管在这个特定的例子中,你可能不需要直接使用这个instance
参数来执行屏幕切换,但将其包含在函数定义中是一种好的编程习惯,特别是当你打算在未来扩展按钮的行为时。
然而,对于简单的屏幕切换逻辑,instance
参数确实不是必需的。你完全可以忽略它,或者如果你确信不会用到它,甚至可以从函数签名中去掉它。但是,保持它在那里通常不会造成任何问题,而且它提供了一种灵活性,允许你在未来需要时轻松地引用触发事件的特定按钮。
如果你打算在按钮的回调函数中执行与按钮本身相关的操作(比如改变按钮的文本、颜色或禁用按钮),那么instance
参数就变得非常有用了
def go2(self, instance):
instance.text = "Going to Screen Two..." # 更新按钮文本
sm = App.get_running_app().root
sm.current = 'screen_two'
在这个例子中,instance
参数允许你访问并修改触发事件的按钮的属性。
详细解析 sm = App.get_running_app().root
在Kivy框架中,sm = App.get_running_app().root
这行代码是获取当前正在运行的Kivy应用程序的根控件的一种方式。为了更好地理解这行代码,我们可以将其分解为几个部分进行解析。
1. App.get_running_app()
App
是Kivy中所有应用程序的基类。当你定义自己的应用程序类时,你通常会继承自这个类。get_running_app()
是一个类方法,用于获取当前正在运行的Kivy应用程序的实例。这个方法会遍历所有已创建的应用程序实例,并返回最顶层的那个(即用户最近启动的那个)。
2. .root
.root
是App
实例的一个属性,它指向应用程序的根控件。在Kivy应用程序中,根控件是整个应用程序的入口点,所有其他控件都是这个根控件的子控件或子控件的子控件,以此类推。- 通常,在
App
类的build
方法中,你会创建并返回一个控件作为根控件。这个控件可以是任何类型的控件,但最常见的是布局控件(如BoxLayout
、GridLayout
等)或ScreenManager
控件(用于管理多个屏幕)。
综合解析
因此,sm = App.get_running_app().root
这行代码的作用是:
- 调用
App.get_running_app()
获取当前正在运行的Kivy应用程序的实例。 - 通过
.root
属性访问这个应用程序实例的根控件。 - 将根控件的引用赋值给变量
sm
(在这个上下文中,sm
很可能是ScreenManager
的一个实例,但这不是强制的,它取决于你在build
方法中返回了什么作为根控件)。
一旦你有了根控件的引用,你就可以通过它来访问或修改应用程序中的其他控件了。在多屏应用程序中,根控件通常是ScreenManager
,因为它允许你管理多个Screen
控件并在它们之间切换。
示例
假设你有一个名为MyApp
的应用程序类,它继承自App
,并在其build
方法中创建了一个ScreenManager
作为根控件:
from kivy.app import App
from kivy.uix.screenmanager import ScreenManager
class MyApp(App):
def build(self):
# 创建一个ScreenManager实例
sm = ScreenManager()
# ...(可能添加了一些Screen到sm中)
# 返回ScreenManager实例作为根控件
return sm
# ...(MyApp类的其他部分和应用程序的启动代码)
在这个例子中,App.get_running_app().root
将返回你在build
方法中创建的ScreenManager
实例。因此,你可以通过sm = App.get_running_app().root
来获取这个ScreenManager
的引用,并使用它来切换屏幕或访问其他屏幕控件。
因为关于相关传参,这里on_press又需要再py文件进行函数的设置,才能实现页面的转变,倒不如kivy人家原先自带的、正统的screen使用方法了。
然后再py文件中添加了代码
print(dir(MenuPage()))
['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__events__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__metaclass__', '__module__', '__ne__', '__new__', '__proxy_getter', '__proxy_setter', '__pyx_vtable__', '__reduce__', '__reduce_ex__', '__repr__', '__self__', '__setattr__', '__setstate__', '__sizeof__', '__str__', '__subclasshook__', '_apply_transform', '_context', '_disabled_count', '_disabled_value', '_kwargs_applied_init', '_proxy_ref', '_trigger_layout', '_walk', '_walk_reverse', 'add_widget', 'apply_class_lang_rules', 'apply_property', 'bind', 'canvas', 'center', 'center_x', 'center_y', 'children', 'clear_widgets', 'cls', 'collide_point', 'collide_widget', 'create_property', 'dec_disabled', 'disabled', 'dispatch', 'dispatch_children', 'dispatch_generic', 'do_layout', 'events', 'export_as_image', 'export_to_png', 'fbind', 'funbind', 'get_center_x', 'get_center_y', 'get_disabled', 'get_parent_window', 'get_property_observers', 'get_right', 'get_root_window', 'get_top', 'get_window_matrix', 'getter', 'go1', 'height', 'ids', 'inc_disabled', 'is_event_type', 'layout_hint_with_bounds', 'on_kv_post', 'on_opacity', 'on_touch_down', 'on_touch_move', 'on_touch_up', 'opacity', 'parent', 'pos', 'pos_hint', 'properties', 'property', 'proxy_ref', 'register_event_type', 'remove_widget', 'right', 'set_center_x', 'set_center_y', 'set_disabled', 'set_right', 'set_top', 'setter', 'size', 'size_hint', 'size_hint_max', 'size_hint_max_x', 'size_hint_max_y', 'size_hint_min', 'size_hint_min_x', 'size_hint_min_y', 'size_hint_x', 'size_hint_y', 'to_local', 'to_parent', 'to_widget', 'to_window', 'top', 'uid', 'unbind', 'unbind_uid', 'unregister_event_types', 'walk', 'walk_reverse', 'width', 'x', 'y']
main.py
# 导入Kivy的App类,它是所有kivy应用的基类
from kivy.app import App
from kivy.properties import ObjectProperty
# kivy内置了丰富的控件widget 如 按钮button 复选框checkbox 标签label 输入框textinput 滚动容器scrollable container等
from kivy.uix.button import Button
# 引入BoxLayout 布局
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.floatlayout import FloatLayout
from kivy.uix.screenmanager import ScreenManager, Screen
class IndexPage(BoxLayout):
# 初始化
def __init__(self, **kwargs):
super().__init__(**kwargs)
# 添加一个按钮
self.join = Button(text="Hello World")
self.join.bind(on_press=self.go2)
# 将按钮添加到页面控件中
self.add_widget(self.join)
# 从App类中继承了kivy应用组基本的方法,如创建窗口、设置窗口的大小和位置等
def go2(self, instance):
sm = App.get_running_app().root
sm.current='menu'
class MenuPage(FloatLayout):
def __init__(self, **kwargs):
super().__init__(**kwargs)
def go1(self, instance):
sm = App.get_running_app().root
sm.current = instance
class TestApp(App):
def build(self):
self.sm = ScreenManager()
#build()方法返回的控件,在kivy中称之为“根控件” root widget
# kivy将自动缩放根控件,让它填满整个窗口
screen = Screen(name = 'index')
screen.add_widget(IndexPage())
self.sm.add_widget(screen)
screen = Screen(name ='menu')
screen.add_widget (MenuPage())
self.sm.add_widget(screen)
return self.sm
# 当.py文件被直接运行时, if__name__ == '__main__' 之下的代码块将被运行
# 当.py文件以模块的形式被导入时,if __name__ == '__main__' 之下的代码块不被运行
if __name__ == '__main__':
TestApp().run() #启动应用程序
test.py
<MenuPage>
Button:
text: 'homepage'
size_hint: .2, .15
pos_hint: {'x':0.2, 'y':.6}
on_press: root.go1('index')
屏幕推荐方法使用kivy学习笔记-ScreenManager实现GUI界面切换_kivy 切换界面并开启相关定时器-优快云博客
Screen Manager — Kivy 2.3.0 documentation
扩展案例
如图效果,有3个屏幕,点击右下方页码后跳转到对性的屏幕。
screen.kv
<BottomPageButton@ToggleButton>:
group:'page'
background_normal: 'black'
color: (0,0,0,1)
border:(3,3,3,3)
on_press: root.change_page(self.text)
<PageBox@BoxLayout>:
id: bottom_box
orientation: 'horizontal'
padding: 2
spacing: 4
size: root.width, 60
BottomPageButton:
id: b1
text: '1'
state: 'down'
BottomPageButton:
id: b2
text: '2'
BottomPageButton:
id: b3
text: '3'
<A1Screen>:
Widget:
BoxLayout:
orientation: 'horizontal'
x: 0
top: root.top
Label:
size_hint: None, None
pos_hint: {'left': 1,'top':1}
size: 50, 50
text:"PAGE 1"
PageBox:
<A2Screen>:
Button:
size_hint: None, None
pos_hint: {'right': 1,'top':1}
size: 150, 50
text:"Return"
on_press:root.manager.current="1"
Label:
text:"PAGE 2"
size_hint: None, None
pos_hint: {'left': 1,'top':1}
size: 150, 50
Widget:
PageBox
<A3Screen>:
Button:
size_hint: None, None
pos_hint: {'right': 1,'top':1}
size: 150, 50
text:"Return"
on_press:root.manager.current="1"
Label:
text:"PAGE 3"
size_hint: None, None
pos_hint: {'left': 1,'top':1}
size: 150, 50
Widget:
PageBox
main.py
from kivy.app import App
from kivy.graphics import Color, Rectangle
from kivy.uix.behaviors import ToggleButtonBehavior
from kivy.uix.boxlayout import BoxLayout
from kivy.properties import ObjectProperty
from kivy.uix.label import Label
from kivy.factory import Factory
from kivy.uix.image import Image
from kivy.uix.screenmanager import ScreenManager, Screen
from kivy.uix.togglebutton import ToggleButton
from kivy.uix.button import Button
class PageBox(BoxLayout):
def __init__(self, **kwargs):
super().__init__(**kwargs)
class BottomPageButton(ToggleButton):
def change_page(self, instance):
sm = App.get_running_app().root
sm.current = instance
print(self.ids)
#self.ids.instance.status = 'down'
def __init__(self, **kwargs):
super(BottomPageButton, self).__init__(**kwargs)
class A1Screen(Screen):
pass
class A2Screen(Screen):
pass
class A3screen(Screen):
pass
class ScreenApp(App):
def build(self):
sm = ScreenManager()
A1 = A1Screen(name="1")
A2 = A2Screen(name="2")
A3 = A3screen(name='3')
sm.add_widget(A1)
sm.add_widget(A2)
sm.add_widget(A3)
return sm
if __name__ =='__main__':
ScreenApp().run()