1

I have written a python kivy application for displaying measurements. While searching stuff on kivy I stumbled on the the kivy-md (Material Design for kivy) project. I find the UI very good looking. That is why I want to inject my app into a screen of the kivy-md project.

My project is inside a folder kivy-playground which contains the kivymd files in a folder kivymd, the main.py (for starting the kivy md application), the main.kv file (for the layout of the kivy md application) and a playground.py which contains the kivy application for displaying measurements. I am using Python 3.7 with the latest version of kivy.

Goal: I want to inject the Application view from playground.py into the main application which is started by the main.py such that the view of the playground.py is displayed on the screen page2 (see main.kv) of the main application. I am totally lost how this can be achieved and I would be happy if someone could show me on this toy example how this can be achieved. It is not necessarily important that the playground.py stays as it is. If the problem can be solved by small changes in the playground.py file then this would be also a valid solution.

I tried to cook up a minimal working example. Here are the files

# main.py
# -*- coding: utf-8 -*-

import os

from kivy.app import App
from kivy.core.window import Window
from kivy.lang import Builder
from kivymd.theming import ThemeManager


class MainApp(App):

    theme_cls = ThemeManager()

    def __init__(self, **kwargs):
        super(MainApp, self).__init__(**kwargs)
        Window.bind(on_close=self.on_stop)

    def build(self):
        main_widget = Builder.load_file(
            os.path.join(os.path.dirname(__file__), "./main.kv")
        )
        self.theme_cls.theme_style = 'Dark'
        return main_widget

    def close_app(self, *largs):
        super(MainApp, self).stop(*largs)

    def on_pause(self):
        return True


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

here is the main.kv

# main.kv
#:kivy 1.10.1
#:import Toolbar kivymd.toolbar.Toolbar
#:import MDNavigationDrawer kivymd.navigationdrawer.MDNavigationDrawer
#:import NavigationLayout kivymd.navigationdrawer.NavigationLayout
#:import NavigationDrawerToolbar kivymd.navigationdrawer.NavigationDrawerToolbar

NavigationLayout:
    id: nav_layout
    MDNavigationDrawer:
        id: nav_drawer
        NavigationDrawerToolbar:
        NavigationDrawerIconButton:
            icon: 'checkbox-blank-circle'
            text: "Page1"
            on_release: app.root.ids.scr_mngr.current = 'page1'
        NavigationDrawerIconButton:
            icon: 'checkbox-blank-circle'
            text: "Page2"
            on_release: app.root.ids.scr_mngr.current = 'page2'
    BoxLayout:
        orientation: 'vertical'
        halign: "center"
        Toolbar:
            id: toolbar
            md_bg_color: app.theme_cls.primary_color
            background_palette: 'Primary'
            background_hue: '500'
            right_action_items: [['dots-vertical', lambda x: app.root.toggle_nav_drawer()]]
        ScreenManager:
            id: scr_mngr
            Screen:
                name: 'page1'
                Label:
                    text: "page1"
            Screen:
                name: 'page2'
                Label:
                    text: "Page 2"

and here is the playground.py which I want to inject into the screen page2 of the main application.

from kivy.app import App
from kivy.uix.label import Label


class Playground(App):

    def build(self):
        hello_world_label = Label(text="Hello World!")
        return hello_world_label


if __name__ == "__main__":
    Playground().run()
New2Coding
  • 181
  • 13
  • 1
    I suspect that trying to have two Apps running in the same window would cause lots of problems. It would be much easier to just insert the root widget from your `Playground` App into the original App. – John Anderson Sep 10 '18 at 15:19
  • It does not have to be with a second app, but I don't know how to change the playground.py such that it can be injected into `page2` of the `main.kv`. – New2Coding Sep 10 '18 at 15:24

2 Answers2

3

If you can change Playground.py to something like this:

from kivy.app import App
from kivy.uix.label import Label

def getPlaygroundRoot():
    hello_world_label = Label(text="Hello World!")
    return hello_world_label


class PlayGround(FloatLayout):
    def __init__(self, **kwargs):
        super(PlayGround, self).__init__(**kwargs)
        self.add_widget(getPlaygroundRoot())

class Playground(App):

    def build(self):
        return getPlaygroundRoot()


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

Then, in your main.py, you can add:

from playGround import getPlaygroundRoot

And then use add_widget(getPlaygroundRoot()) to add the Playground root to some container in the MainApp.

Or, if you want to use Playground in your .kv file, you can add #:import playground playGround to your .kv file, and then add:

        Screen:
            name: 'page2'
            Label:
                text: "Page 2"
                pos_hint: {'center_x': 0.5, 'y': 0.8}
                size_hint: (1.0, 0.2)
            PlayGround:
                pos_hint: {'center_x': 0.5, 'y': 0.1}
                size_hint: (1.0, 0.2)

to add it to your page2.

John Anderson
  • 35,991
  • 4
  • 13
  • 36
  • Thank you for your answer! I will give this a try and accept the answer if it worked out. – New2Coding Sep 10 '18 at 20:25
  • Thank you! It worked like a charm! It took me some time to figure out how to refactor the application but your example was key in understanding how to implement it. – New2Coding Sep 11 '18 at 07:55
1
  • If there is a kv file, playground.kv for playground.py then use include <file> in main.kv
  • Add import statement, from playground import PlayGround in main.py

Kivy Lang Directives » include <file>

include <file>

Syntax:

#:include [force] <file>

Includes an external kivy file. This allows you to split complex widgets into their own files. If the include is forced, the file will first be unloaded and then reloaded again.

Snippets

playground.py

from kivy.app import App
from kivy.uix.label import Label


class PlayGround(Label):
    pass


class Playground(App):

    def build(self):
        return PlayGround()


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

playground.kv

#:kivy 1.11.0

<PlayGround>:
    text: "Hello World!"

min.py

# main.py
# -*- coding: utf-8 -*-

import os

from kivy.app import App
from kivy.core.window import Window
from kivy.lang import Builder
from kivymd.theming import ThemeManager
from playground import PlayGround


class MainApp(App):

main.kv

# main.kv
#:kivy 1.10.1
#:import Toolbar kivymd.toolbar.Toolbar
#:import MDNavigationDrawer kivymd.navigationdrawer.MDNavigationDrawer
#:import NavigationLayout kivymd.navigationdrawer.NavigationLayout
#:import NavigationDrawerToolbar kivymd.navigationdrawer.NavigationDrawerToolbar

#:include playground.kv

NavigationLayout:
    ...
        ScreenManager:
            id: scr_mngr
            Screen:
                name: 'page1'
                Label:
                    text: "page1"
            Screen:
                name: 'page2'
                Label:
                    text: "Page 2"
                PlayGround:
ikolim
  • 15,721
  • 2
  • 19
  • 29
  • Thank you for your answer. The problem is, that my whole code is in python. I am not using any kv file for the `playground.py`. – New2Coding Sep 11 '18 at 06:44