How to use different . Kv files in a Kivy application?

Asked

Viewed 634 times

7

I am trying to create a game using Kivy in Python language and I would like to know how I can use different files .kv to be used in the application as levels for the game. Example:

main.py
game.kv
fases/
   fase1.kv
   fase2.kv
   fase3.kv
   ...

It is possible to split my file .kv in several others as in the example above ? If yes, how can I do it ?

2 answers

5


It is possible yes.

In Python code, remember to load the files using Builder:

from kivy.lang import Builder

Builder.load_file('caminho_para_arquivo_kivy.kv')

In the main Kv file (main.Kv), you can import other Kv files.

<main_kv>: 

    cols: 1

    AnchorLayout: 
        anchor_x: 'left'
        anchor_y: 'center'

        OutroKv: 
            size_hint: [None, None] 
            size: [app.x, app.y]

In the other Kv file:

<OutroKv@BoxLayout>: 
    Button: 
        text: 'Outro KV'

This way you can better modulate the graphical interface of your application.

  • 2

    You can add at the end of the answer a small example of how the file would look .py and the file .kv please ?

5

Because of your need it appears that you need not only to load the next phase, but also to close/download the previous one, because in case you don’t, it will take up memory in your project, so to solve this as I said in the chat on 29/01:

I understand little of this, but from what I understand it is not enough to "load" with Builder.load_file and "download" with Builder.unload_file and of course, after downloading, run in the "widget" that is loading the widget.clear_widgets()? Of course there can probably be one or the other thing, like "events", but then just plan well the code to remove at the right time

I did not formulate the answer because I had not had time to downgrade Python3.8 to 3.7 (the kivy still does not have support for 3.8), now done the tests I will formalize the answer, in the example self.palco refers ai Main layout of the application, assuming that the fase1 is already loaded and you want to carry the fase2 should look like this:

# Remove a fase antiga
self.palco.clear_widgets()

# carrega e adiciona a fase2
self.palco.add_widget(Builder.load_file('fases/fase2.kv'))

# DESCARREGA e a fase1 (que já foi removida com o clear_widgets)
Builder.unload_file('fases/fase1.kv')

So basically we had to use:

To test created the following example:

# -*- coding: latin-1 -*-

from kivy.app import App
from kivy.lang import Builder
from kivy.uix.button import Button
from kivy.uix.label import Label
from kivy.uix.popup import Popup
from kivy.uix.gridlayout import GridLayout

class GameApp(App):
    palco = None
    fase_atual = 1
    total_fases = 3


    def run(self):
        super().run()


    def on_start(self):
        self.palco = self.root.ids['palco']
        self.palco.add_widget(Builder.load_file(f'fases/fase{self.fase_atual}.kv'))


    def alerta(self, texto):
        layout = GridLayout(cols=1, padding=10)
        popup = Popup(title='Jogo', auto_dismiss=False, content=layout)

        label = Label(text=texto)
        layout.add_widget(label)

        fechar = Button(text="Continuar")
        fechar.bind(on_press = popup.dismiss)
        layout.add_widget(fechar)

        popup.open()


    def carregar_fase(self, novo):
        print('Removendo fases')

        self.palco.clear_widgets()

        print(f'Carrengado o fase {novo}')

        self.palco.add_widget(Builder.load_file(f'fases/fase{novo}.kv'))

        anterior = self.fase_atual

        self.fase_atual = novo

        print(f'Decarrengado o fase {anterior}')

        Builder.unload_file(f'fases/fase{anterior}.kv')


    def voltar_fase(self):
        proximo = self.fase_atual - 1

        if proximo < 1:
            self.alerta('Não há fases anteriores')
        else:
            self.carregar_fase(proximo)


    def proxima_fase(self):
        proximo = self.fase_atual + 1

        if proximo > self.total_fases:
            self.alerta('Não há próximas fases')
        else:
            self.carregar_fase(proximo)


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

And in the game.kv added:

GridLayout:
    cols: 1
    BoxLayout:
        id: palco
    Button:
        text: "Fase anterior"
        on_release:
            app.voltar_fase()
    Button:
        text: "Próximo fase"
        on_release:
            app.proxima_fase()

In the example the GridLayout is the layout of the whole program, buttons and current phase of the game, the "buttons" (Earlier stage and Next map) which are only for testing maps. Already the BoxLayout should receive the widgets that represent the current phase, it is the "stage".

The alert window, def alerta(self, texto), does not influence the functioning, it is merely to understand that there was a problem when trying to access a phase outside the "range" (in the example the range is 3 phases).

An important detail, to use the "native kivy" property called self.root.ids you need to wait for the screen to load the main components related to the App, soon the self.root.ids won’t work if used in the methods:

  • def __init__
  • def build
  • def run (replacement in inheritance)

That’s why I used the def on_load(self), so it is possible to get the ids and in the case of the example the "stage" (BoxLayout) to receive the phases.

  • It was very well explained William thank you very much for everything! I ended up realizing that in that code that I had made of example, it was giving an error because in the file . Kv I set the widget name to "<>" this way: <MyWidget>. Seeing this code of yours and removing the larger and smaller signal it worked. You know why ? Thanks again for the patience and effort in helping me S2

  • 1

    @Jeanextreme002 is worth a question on the subject. PS: Just to tell you, without <> it would be for a specific instance in your case the "app"... already with <> would be for any instance, it would be like a "pattern", which can be of appearance and events, if I understood right: https://kivy.org/doc/stable/guide/lang.html#Rule-context

Browser other questions tagged

You are not signed in. Login or sign up in order to post.