Search code examples
pythonpython-3.xkivypython-3.7kivy-language

Kivy - Objects change size and position when switching from Widget to Screen


I'm attempting to convert a Widget into a Screen so that I can use Kivy's ScreenManager. However, changing the classes to Screens causes the objects within them to become massively resized and displaced.

user.py

"""User-end software for signup/account data."""
# Import required Kivy modules
from kivy.app import App
# from kivy.uix.widget import Widget
from kivy.uix.screenmanager import Screen, ScreenManager
from kivy.properties import StringProperty

# Import regex
import re


# Define ScreenManager
sm = ScreenManager()


class LoginScreen(Screen):
    """Class for login screen contents."""

    email = StringProperty()  # variable for email from text box
    password = StringProperty()  # variable for password from text box

    def login(self):  # check credentials and login if valid
        """Actions for when Login button is pressed."""
        if not re.fullmatch(r"[^@]+@[^@]+\.[^@]+", self.email):
            self.ids.valid_login.valid_color = (1, 0, 0, 1)  # show if invalid
        else:
            self.ids.valid_login.valid_color = (0, 0, 0, 0)  # hide if valid
            if self.password != "":  # Make sure password isnt empty
                None  # Search DB for email & check password

    def goto_signup(self):
        """Switch to Signup Screen."""
        sm.current = 'signup'


class SignupScreen(Screen):
    """Class for signup screen contents."""

    email = StringProperty()  # variable for email from text box
    password = StringProperty()  # variable for password from text box

    def signup(self):  # check credentials and login if valid
        """Actions for when Signup button is pressed."""
        if not re.fullmatch(r"[^@]+@[^@]+\.[^@]+", self.email):
            self.ids.valid_login.valid_color = (1, 0, 0, 1)  # show if invalid
        else:
            self.ids.valid_login.valid_color = (0, 0, 0, 0)  # hide if valid

    def goto_login(self):
        """Switch to Signup Screen."""
        sm.current = 'login'


class UserApp(App):
    """Main app."""

    def build(self):
        """Build app."""
        sm.add_widget(LoginScreen(name='login'))
        sm.add_widget(SignupScreen(name='signup'))

        self.icon = 'graphics/window_icon.png'
        self.title = 'Offbox Insurance'
        return sm


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

user.kv

#:kivy 1.11.1

<SignupScreen>:
    email: email_input.text
    password: password_input.text
    Widget:
        id: valid_login
        valid_color:(0, 0, 0, 0)
        canvas:
            Color:
                rgba: self.valid_color
            Rectangle:  # show/hide this object
                size:  root.width * 5 / 7 + 6, 46
                pos: root.width * 1 / 7 - 3, root.top - 259
    Label:
        font_size: 20
        center_x: root.width / 2
        top: root.top + 20
        text: "Offbox Insurance"
    Label:
        font_size: 64
        center_x: root.width / 2
        top: root.top - 30
        text: "Sign up"
    Label:
        font_size: 20
        center_x: root.width / 2
        top: root.top - 140
        text: "Email"
    TextInput:
        id: email_input
        font_size: 24
        height: 40
        width: root.width * 5 / 7
        center_x: root.width / 2
        top: root.top - 216
        multiline: False
    Label:
        font_size: 20
        center_x: root.width / 2
        top: root.top - 240
        text: "Password"
    TextInput:
        id: password_input
        font_size: 24
        height: 40
        width: root.width * 5 / 7
        center_x: root.width / 2
        top: root.top - 316
        multiline: False
        password: True
    Button:
        font_size: 20
        height: 50
        center_x: root.width / 2
        top: root.top - 380
        text: "Sign up"
        background_normal: 'graphics/button_up.png'
        background_down: 'graphics/button_down.png'
        on_press: root.login()
    Label:
        font_size: 16
        center_x: root.width / 2
        top: root.height / 12 + 75
        text: "Already have an account?"
    Button:
        font_size: 16
        height: 36
        center_x: root.width / 2
        top: root.height / 12 + 5
        text: "Log in"
        background_normal: 'graphics/button_up.png'
        background_down: 'graphics/button_down.png'
        on_press: root.goto_login()

<LoginScreen>:
    email: email_input.text
    password: password_input.text
    Widget:
        id: valid_login
        valid_color:(0, 0, 0, 0)
        canvas:
            Color:
                rgba: self.valid_color
            Rectangle:  # show/hide this object
                size:  root.width * 5 / 7 + 6, 46
                pos: root.width * 1 / 7 - 3, root.top - 259
    Label:
        font_size: 20
        center_x: root.width / 2
        top: root.top + 20
        text: "Offbox Insurance"
    Label:
        font_size: 64
        center_x: root.width / 2
        top: root.top - 30
        text: "Log in"
    Label:
        font_size: 20
        center_x: root.width / 2
        top: root.top - 140
        text: "Email"
    TextInput:
        id: email_input
        font_size: 24
        height: 40
        width: root.width * 5 / 7
        center_x: root.width / 2
        top: root.top - 216
        multiline: False
    Label:
        font_size: 20
        center_x: root.width / 2
        top: root.top - 240
        text: "Password"
    TextInput:
        id: password_input
        font_size: 24
        height: 40
        width: root.width * 5 / 7
        center_x: root.width / 2
        top: root.top - 316
        multiline: False
        password: True
    Button:
        font_size: 20
        height: 50
        center_x: root.width / 2
        top: root.top - 380
        text: "Log in"
        background_normal: 'graphics/button_up.png'
        background_down: 'graphics/button_down.png'
        on_press: root.login()
    Label:
        font_size: 16
        center_x: root.width / 2
        top: root.height / 12 + 75
        text: "Don't have an account?"
    Button:
        font_size: 16
        height: 36
        center_x: root.width / 2
        top: root.height / 12 + 5
        text: "Sign up"
        background_normal: 'graphics/button_up.png'
        background_down: 'graphics/button_down.png'
        on_press: root.goto_signup()

I want objects in each screen to be sized and scaled relative to the screen they're in, and each screen should be the same size as the window. This way, the window can be resized and the objects inside will still display properly, while still allowing for a smooth transition between screens.


Solution

  • Here is a re-write of your <LoginScreen> rule. It uses BoxLayout (convenient for arranging Widgets vertically or horizontally) and pos_hint to position the child Widgets. In cases where you set the size of a Widget inside a Layout, you normally need to set size_hint to None, since it will usually over-ride any size setting. Here is the modified version of your rule:

    <LoginScreen>:
        email: email_input.text
        password: password_input.text
        BoxLayout:
            # just using canvas to show extents of the BoxLayout
            canvas.before:
                Color:
                    rgba: 1,0,0,1
                Rectangle:
                    size: self.size
                    pos: self.pos
            orientation: 'vertical'
            size_hint: None, None
            size: self.minimum_size
            pos_hint: {'center_x': 0.5, 'top': 1.0}
    
            Widget:
                # not sure what is the purpose of this Widget
                id: valid_login
                valid_color:(0, 0, 0, 0)
                canvas:
                    Color:
                        rgba: self.valid_color
                    Rectangle:  # show/hide this object
                        size:  root.width * 5 / 7 + 6, 46
                        pos: root.width * 1 / 7 - 3, root.top - 259
            Label:
                size_hint: None, None
                size: self.texture_size
                font_size: 20
                pos_hint: {'center_x': 0.5}
                text: "Offbox Insurance"
            Label:
                size_hint: None, None
                size: self.texture_size
                font_size: 64
                pos_hint: {'center_x': 0.5}
                text: "Log in"
            Label:
                size_hint: None, None
                size: self.texture_size
                font_size: 20
                pos_hint: {'center_x': 0.5}
                text: "Email"
            TextInput:
                id: email_input
                font_size: 24
                size_hint: None, None
                height: 40
                width: root.width * 5 / 7
                pos_hint: {'center_x': 0.5}
                multiline: False
            Label:
                size_hint: None, None
                size: self.texture_size
                font_size: 20
                pos_hint: {'center_x': 0.5}
                text: "Password"
            TextInput:
                id: password_input
                font_size: 24
                size_hint: None, None
                height: 40
                width: root.width * 5 / 7
                pos_hint: {'center_x': 0.5}
                multiline: False
                password: True
            Button:
                size_hint: None, None
                size: self.texture_size
                font_size: 20
                height: 50
                pos_hint: {'center_x': 0.5}
                text: "Log in"
                background_normal: 'graphics/button_up.png'
                background_down: 'graphics/button_down.png'
                on_press: root.login()
            Label:
                size_hint: None, None
                size: self.texture_size
                font_size: 16
                pos_hint: {'center_x': 0.5}
                text: "Don't have an account?"
            Button:
                size_hint: None, None
                size: self.texture_size
                font_size: 16
                height: 36
                pos_hint: {'center_x': 0.5}
                text: "Sign up"
                background_normal: 'graphics/button_up.png'
                background_down: 'graphics/button_down.png'
                on_press: root.goto_signup()
    

    See the size_hint documentation and the pos_hint documentation.