使用Kivy(Python)中的ScreenManager处理多个.py屏幕

jobtbby3  于 2023-01-24  发布在  Python
关注(0)|答案(2)|浏览(179)

我最近开始使用Kivy框架来创建一个应用程序,它有多个我想要合并的屏幕,这样,例如,在登录屏幕上按下一个按钮就可以打开另一个页面。我已经尝试使用ScreenManager库好几天了,但是我不能让它工作,互联网上有几个教程,但是每个人使用的变体都不一样。下面我附上了登录页面的代码,按下“登录”按钮,应打开第二个屏幕。
login.py:

from kivy.core.text import LabelBase
from kivy.lang import Builder
from kivy.core.window import Window
from kivymd.app import MDApp

Window.size = (350, 580)

kv = """
MDFloatLayout:
    md_bg_color: 0, 0, 0, 1
    Image:
        source: "img\\logo5.png"
        pos_hint: {"center_x": .5, "center_y": .85}
        size_hint: .18, .18
    MDFloatLayout:
        size_hint: .9, .07
        pos_hint: {"center_x": .5, "center_y": .68}
        canvas:
            Color:
                rgb: 250/255, 250/255, 250/255, 1
            RoundedRectangle:
                size: self.size
                pos:self.pos
                radius: [4]
        canvas.before:
            Color:
                rgb: 217/255, 217/255, 217/255, 1
            Line:
                width: 1.1
                rounded_rectangle: self.x, self.y, self.width, self.height, 4, 4, 4, 4, 100
        TextInput:
            hint_text: "Phone number, username or e-mail"
            size_hint: 1, None
            pos_hint: {"center_x": .5, "center_y": .5}
            height: self.minimum_height
            background_color: 1, 1, 1, 0
            font_size: "14sp"
            font_name: "MRoboto"
            hint_text_color: 170/255, 170/255, 170/255, 1
            padding: 13
            cursor_color: 0, 0, 0, 1
    MDFloatLayout:
        size_hint: .9, .07
        pos_hint: {"center_x": .5, "center_y": .59}
        canvas:
            Color:
                rgb: 250/255, 250/255, 250/255, 1
            RoundedRectangle:
                size: self.size
                pos:self.pos
                radius: [4]
        canvas.before:
            Color:
                rgb: 217/255, 217/255, 217/255, 1
            Line:
                width: 1.1
                rounded_rectangle: self.x, self.y, self.width, self.height, 4, 4, 4, 4, 100
        TextInput:
            hint_text: "Password"
            size_hint: 1, None
            pos_hint: {"center_x": .5, "center_y": .5}
            height: self.minimum_height
            background_color: 1, 1, 1, 0
            font_size: "14sp"
            font_name: "MRoboto"
            password: "true"
            hint_text_color: 170/255, 170/255, 170/255, 1
            padding: 13
            cursor_color: 0, 0, 0, 1
    Button:
        text: "Log in"
        color: 1, 1, 1, 1
        size_hint: .9, .07
        pos_hint: {"center_x": .5, "center_y": .43}
        background_color: 1, 1, 1, 0
        font_size: "13sp"
        font_name: "BRoboto"
        canvas.before:
            Color:
                rgb: 98/255, 170/255, 243/255, 1
            RoundedRectangle:
                size: self.size
                pos: self.pos
                radius: [4]
    MDLabel:
        text: "Don't have an account?"
        color: 172/255, 172/255, 172/255, 1
        pos_hint: {"center_x": .74, "center_y": .095}
        font_size: "13sp"
        font_name: "MRoboto"
    MDTextButton:
        text: "Sign up"
        color: 98/255, 170/255, 243/255, 1
        pos_hint: {"center_x": .685, "center_y": .095}
        font_size: "13sp"
        font_name: "MRoboto"
    MDCheckbox:
        size_hint: None, None
        size: "48dp", "48dp"
        pos_hint: {"center_x": .1, "center_x": .1}
        on_active: app.show_password(*args)
    MDLabel:
        id: password_text
        text: "Show Password"
        pos_hint: {"center_x": .7, "center_x": .43}
"""
class Login(MDApp):

    def build(self):
        return Builder.load_string(kv)

    def show_password(self, checkbox, value):
        if value:
            self.root.ids.password.password = False
            self.root.ids.password_text.text = "Hide Password"
        else:
            self.root.ids.password.password = True
            self.root.ids.password_text.text = "Show Password"

if __name__ == "__main__":
    LabelBase.register(name="BRoboto", fn_regular="font\\Roboto-Bold.ttf")
    LabelBase.register(name="MRoboto", fn_regular="font\\Roboto-Medium.ttf")
    Login().run()

list.py:

from kivy.core.window import Window
from kivy.lang import Builder
from kivy.factory import Factory
from kivy.uix.image import Image
from kivymd.app import MDApp
from kivymd.uix.list import IRightBodyTouch, ILeftBody
from kivymd.uix.selectioncontrol import MDCheckbox

Window.size = (350, 580)

kv = """
<ListItemWithCheckbox@OneLineAvatarIconListItem>:
    MyAvatar:
        source: "data/logo/kivy-icon-128.png"
    MyCheckbox:

<Lists@BoxLayout>
    name: "lists"
    orientation: "vertical"

    MDTopAppBar:
        title:"Hide the story to:"
        md_bg_color: app.theme_cls.primary_color
        elevation: 3

    ScrollView:

        MDList:
            id: scroll
"""

Builder.load_string(kv)

class MyCheckbox(IRightBodyTouch, MDCheckbox):
    pass

class MyAvatar(ILeftBody, Image):
    pass

class Users(MDApp):
    def __init__(self, **kwargs):
        super().__init__(**kwargs)

    def build(self):
        self.title = "Liste"
        self.theme_cls.primary_palette = "Teal"
        self.theme_cls.theme_style = "Dark"
        list = Factory.Lists()
        for i in range(30):
            list.ids.scroll.add_widget(Factory.ListItemWithCheckbox(text="Item %d" % i))
        self.root = list

if __name__ == "__main__":
    Users().run()

在我的测试中,我尝试在login.py代码和从头创建的第三个新文件中示例化ScreenManager。

yhqotfr8

yhqotfr81#

让我来给你介绍一下Kivy屏幕的基本知识。下面是一个有3个屏幕的示例应用程序。每个屏幕都包含按钮:

  • 按名称转到指定屏幕
  • 转到下一个定义的屏幕
  • 转到上一个定义屏幕
  • 调用当前屏幕中定义的方法
  • 调用其他屏幕中定义的方法
  • 调用屏幕管理器中定义的方法
  • 调用应用程序中定义的方法

screenssample.kv文件的顶部有一个屏幕层次结构。类MyScreens是一个屏幕管理器,是三个屏幕的父级:MyScreen1MyScreen2MyScreen3。要转到另一个屏幕,您必须将其屏幕名称分配给管理器的当前属性manager.current
main.py

from kivy.app import App
from kivy.uix.screenmanager import ScreenManager
from kivy.uix.screenmanager import Screen

# hierarhy:
#   ScreensSample (App)
#   |- MyScreens (ScreenManager)
#      |- MyScreen1 (Screen)
#      |- MyScreen2 (Screen)
#      |- MyScreen3 (Screen)

class MyScreens(ScreenManager):
    def screen_manager_method(self):
        print('Hello from screen manager')

class MyScreen1(Screen):
    def screen_method(self):
        print('Hello from screen 1')

class MyScreen2(Screen):
    def screen_method(self):
        print('Hello from screen 2')

class MyScreen3(Screen):
    def screen_method(self):
        print('Hello from screen 3')

class ScreensSample(App):
    def app_method(self):
        print('Hello from app')

ScreensSample().run()

screenssample.kv

MyScreens:
    MyScreen1:
    MyScreen2:
    MyScreen3:

<MyScreen1>:
    name: 'screen_one'

    BoxLayout:
        orientation: 'vertical'

        Label:
            text: f'I am {root.name}'
        Button:
            text: 'Go to screen 2'
            on_release: root.manager.current = 'screen_two'
        Button:
            text: 'Go to next screen'
            on_release: root.manager.current = root.manager.next()
        Button:
            text: 'Go to previous screen'
            on_release: root.manager.current = root.manager.previous()
        Button:
            text: 'This screen method'
            on_release: root.screen_method()
        Button:
            text: 'Other screen method (from screen 2)'
            on_release: root.manager.get_screen('screen_two').screen_method()
        Button:
            text: 'Screen manager method'
            on_release: root.manager.screen_manager_method()
        Button:
            text: 'App method'
            on_release: app.app_method()

<MyScreen2>:
    name: 'screen_two'

    BoxLayout:
        orientation: 'vertical'

        Label:
            text: f'I am {root.name}'
        Button:
            text: 'Go to screen 3'
            on_release: root.manager.current = 'screen_three'
        Button:
            text: 'Go to next screen'
            on_release: root.manager.current = root.manager.next()
        Button:
            text: 'Go to previous screen'
            on_release: root.manager.current = root.manager.previous()
        Button:
            text: 'This screen method'
            on_release: root.screen_method()
        Button:
            text: 'Other screen method (from screen 1)'
            on_release: root.manager.get_screen('screen_one').screen_method()
        Button:
            text: 'Screen manager method'
            on_release: root.manager.screen_manager_method()
        Button:
            text: 'App method'
            on_release: app.app_method()

<MyScreen3>:
    name: 'screen_three'

    BoxLayout:
        orientation: 'vertical'

        Label:
            text: f'I am {root.name}'
        Button:
            text: 'Go to screen 1'
            on_release: root.manager.current = 'screen_one'
        Button:
            text: 'Go to next screen'
            on_release: root.manager.current = root.manager.next()
        Button:
            text: 'Go to previous screen'
            on_release: root.manager.current = root.manager.previous()
        Button:
            text: 'This screen method'
            on_release: root.screen_method()
        Button:
            text: 'Other screen method (from screen 1)'
            on_release: root.manager.get_screen('screen_one').screen_method()
        Button:
            text: 'Screen manager method'
            on_release: root.manager.screen_manager_method()
        Button:
            text: 'App method'
            on_release: app.app_method()

我希望这个示例容易理解,但是即使基于这个简单的py和kv文件,您也可能会注意到,将所有screen放在一个kv文件中,将所有screen类放在一个py文件中,将产生难以开发和维护的巨大文件。
下面是相同的例子,但是每个屏幕都是在单独的kv和py文件中定义的。我认为这使得代码更可读,更容易开发/维护。
main.py

from kivy.app import App
from kivy.uix.screenmanager import ScreenManager
import screen1, screen2, screen3

# hierarhy:
#   ScreensSample (App)
#   |- MyScreens (ScreenManager)
#      |- MyScreen1 (Screen)
#      |- MyScreen2 (Screen)
#      |- MyScreen3 (Screen)

class MyScreens(ScreenManager):
    def screen_manager_method(self):
        print('Hello from screen manager')

class ScreensSample(App):
    def app_method(self):
        print('Hello from app')

ScreensSample().run()

screenssample.kv

#:include screen1.kv
#:include screen2.kv
#:include screen3.kv

MyScreens:
    MyScreen1:
    MyScreen2:
    MyScreen3:

screen1.py

from kivy.app import App
from kivy.uix.screenmanager import Screen

class MyScreen1(Screen):
    def screen_method(self):
        print('Hello from screen 1')
        # self.manager.get_screen('screen_two').screen_method()  # sample how to call other screen method from here
        # self.manager.screen_manager_method()  # sample how to call screen manager method from here
        # App.get_running_app().app_method()  # sample hot to call app method from here

screen1.kv

<MyScreen1>:
    name: 'screen_one'

    BoxLayout:
        orientation: 'vertical'

        Label:
            text: f'I am {root.name}'
        Button:
            text: 'Go to screen 2'
            on_release: root.manager.current = 'screen_two'
        Button:
            text: 'Go to next screen'
            on_release: root.manager.current = root.manager.next()
        Button:
            text: 'Go to previous screen'
            on_release: root.manager.current = root.manager.previous()
        Button:
            text: 'This screen method'
            on_release: root.screen_method()
        Button:
            text: 'Other screen method (from screen 2)'
            on_release: root.manager.get_screen('screen_two').screen_method()
        Button:
            text: 'Screen manager method'
            on_release: root.manager.screen_manager_method()
        Button:
            text: 'App method'
            on_release: app.app_method()

screen2.py

from kivy.uix.screenmanager import Screen

class MyScreen2(Screen):
    def screen_method(self):
        print('Hello from screen 2')

screen2.kv

<MyScreen2>:
    name: 'screen_two'

    BoxLayout:
        orientation: 'vertical'

        Label:
            text: f'I am {root.name}'
        Button:
            text: 'Go to screen 3'
            on_release: root.manager.current = 'screen_three'
        Button:
            text: 'Go to next screen'
            on_release: root.manager.current = root.manager.next()
        Button:
            text: 'Go to previous screen'
            on_release: root.manager.current = root.manager.previous()
        Button:
            text: 'This screen method'
            on_release: root.screen_method()
        Button:
            text: 'Other screen method (from screen 1)'
            on_release: root.manager.get_screen('screen_one').screen_method()
        Button:
            text: 'Screen manager method'
            on_release: root.manager.screen_manager_method()
        Button:
            text: 'App method'
            on_release: app.app_method()

screen3.py

from kivy.uix.screenmanager import Screen

class MyScreen3(Screen):
    def screen_method(self):
        print('Hello from screen 3')

screen3.kv

<MyScreen3>:
    name: 'screen_three'

    BoxLayout:
        orientation: 'vertical'

        Label:
            text: f'I am {root.name}'
        Button:
            text: 'Go to screen 1'
            on_release: root.manager.current = 'screen_one'
        Button:
            text: 'Go to next screen'
            on_release: root.manager.current = root.manager.next()
        Button:
            text: 'Go to previous screen'
            on_release: root.manager.current = root.manager.previous()
        Button:
            text: 'This screen method'
            on_release: root.screen_method()
        Button:
            text: 'Other screen method (from screen 1)'
            on_release: root.manager.get_screen('screen_one').screen_method()
        Button:
            text: 'Screen manager method'
            on_release: root.manager.screen_manager_method()
        Button:
            text: 'App method'
            on_release: app.app_method()

此外,在screen1.py中,我还介绍了如何访问与kv文件相同的方法,而不是Python的screen方法。正如您可能注意到的,如何从kv文件和Python代码访问方法(或属性)存在细微差异,例如:

kv: root.manager.get_screen('screen_two').screen_method()
python: self.manager.get_screen('screen_two').screen_method()

kv: root.manager.screen_manager_method()
python: self.manager.screen_manager_method()

kv: app.app_method()
python: App.get_running_app().app_method()
xdnvmnnf

xdnvmnnf2#

我更新了你的代码,所以所有的东西都在login.py中。它使用屏幕管理器在屏幕之间切换。你也可以把list类放在一个list.py文件中,然后把它使用的导入移到那个文件中。

from kivy.lang import Builder
from kivy.core.window import Window
from kivymd.app import MDApp
from kivymd.uix.screen import MDScreen
from kivymd.uix.screenmanager import MDScreenManager
from kivy.factory import Factory
from kivy.uix.image import Image
from kivymd import app
from kivymd.uix.list import IRightBodyTouch, ILeftBody
from kivymd.uix.selectioncontrol import MDCheckbox

Window.size = (350, 580)

kv = """
WindowManager:
    id: manager
    LoginScreen:
        id: login_screen
    ListScreen:
        id: list_screen

<LoginScreen>:
    name: 'login_screen'
    MDFloatLayout:
        md_bg_color: 0, 0, 0, 1
        Image:
            source: "img_logo5.png"
            pos_hint: {"center_x": .5, "center_y": .85}
            size_hint: .18, .18
        MDFloatLayout:
            size_hint: .9, .07
            pos_hint: {"center_x": .5, "center_y": .68}
            canvas:
                Color:
                    rgb: 250/255, 250/255, 250/255, 1
                RoundedRectangle:
                    size: self.size
                    pos:self.pos
                    radius: [4]
            canvas.before:
                Color:
                    rgb: 217/255, 217/255, 217/255, 1
                Line:
                    width: 1.1
                    rounded_rectangle: self.x, self.y, self.width, self.height, 4, 4, 4, 4, 100
            TextInput:
                hint_text: "Phone number, username or e-mail"
                size_hint: 1, None
                pos_hint: {"center_x": .5, "center_y": .5}
                height: self.minimum_height
                background_color: 1, 1, 1, 0
                font_size: "14sp"
                # font_name: "MRoboto"
                hint_text_color: 170/255, 170/255, 170/255, 1
                padding: 13
                cursor_color: 0, 0, 0, 1
        MDFloatLayout:
            size_hint: .9, .07
            pos_hint: {"center_x": .5, "center_y": .59}
            canvas:
                Color:
                    rgb: 250/255, 250/255, 250/255, 1
                RoundedRectangle:
                    size: self.size
                    pos:self.pos
                    radius: [4]
            canvas.before:
                Color:
                    rgb: 217/255, 217/255, 217/255, 1
                Line:
                    width: 1.1
                    rounded_rectangle: self.x, self.y, self.width, self.height, 4, 4, 4, 4, 100
            TextInput:
                hint_text: "Password"
                size_hint: 1, None
                pos_hint: {"center_x": .5, "center_y": .5}
                height: self.minimum_height
                background_color: 1, 1, 1, 0
                font_size: "14sp"
                # font_name: "MRoboto"
                password: "true"
                hint_text_color: 170/255, 170/255, 170/255, 1
                padding: 13
                cursor_color: 0, 0, 0, 1
        Button:
            text: "Log in"
            color: 1, 1, 1, 1
            size_hint: .9, .07
            pos_hint: {"center_x": .5, "center_y": .43}
            background_color: 1, 1, 1, 0
            font_size: "13sp"
            # font_name: "BRoboto"
            canvas.before:
                Color:
                    rgb: 98/255, 170/255, 243/255, 1
                RoundedRectangle:
                    size: self.size
                    pos: self.pos
                    radius: [4]
            on_release: root.manager.current = 'list_screen'
        MDLabel:
            text: "Don't have an account?"
            color: 172/255, 172/255, 172/255, 1
            pos_hint: {"center_x": .74, "center_y": .095}
            font_size: "13sp"
            # font_name: "MRoboto"
        MDTextButton:
            text: "Sign up"
            color: 98/255, 170/255, 243/255, 1
            pos_hint: {"center_x": .685, "center_y": .095}
            font_size: "13sp"
            # font_name: "MRoboto"
        MDCheckbox:
            size_hint: None, None
            size: "48dp", "48dp"
            pos_hint: {"center_x": .1, "center_x": .1}
            on_active: app.show_password(*args)
        MDLabel:
            id: password_text
            text: "Show Password"
            pos_hint: {"center_x": .7, "center_x": .43}
            
<ListItemWithCheckbox@OneLineAvatarIconListItem>:
    MyAvatar:
        source: "data/logo/kivy-icon-128.png"
    MyCheckbox:
    
<ListScreen>
    name: 'list_screen'
    BoxLayout
        name: "lists"
        orientation: "vertical"
    
        MDTopAppBar:
            title:"Hide the story to:"
            md_bg_color: app.theme_cls.primary_color
            elevation: 3
    
        ScrollView:
    
            MDList:
                id: scroll
"""

class WindowManager(MDScreenManager):
    """ Window Manager """
    pass

class ListItemWithCheckbox:
    pass

class LoginScreen(MDScreen):
    pass

class Login(MDApp):

    def build(self):
        return Builder.load_string(kv)

    def show_password(self, checkbox, value):
        if value:
            self.root.ids.password.password = False
            self.root.ids.password_text.text = "Hide Password"
        else:
            self.root.ids.password.password = True
            self.root.ids.password_text.text = "Show Password"

class MyCheckbox(IRightBodyTouch, MDCheckbox):
    pass

class MyAvatar(ILeftBody, Image):
    pass

class ListItemWithCheckbox:
    pass

class ListScreen(MDScreen):
    def __init__(self, **kwargs):
        super().__init__(**kwargs)

    def on_enter(self):
        app.title = "Liste"
        # my_list = Factory.Lists()
        for i in range(30):
            self.ids.scroll.add_widget(Factory.ListItemWithCheckbox(text="Item %d" % i))
        # self.root = list

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

相关问题