A.Piquer
A.Piquer

Reputation: 424

How to create a dynamic kivy app with checkboxes?

I'm doing a project for my university using kivy and python. I want to do a main screen where you can select some fields, and then the next screen will depend on the fields you have selected in the main screen. Doing dynamic with kv language is difficult to me because I don't know too much about programming with kivy, so i have decided to do like python programming but it doesn't work. Here you have the code of my program:

from kivy.app import App
from kivy.lang import Builder
from kivy.uix.screenmanager import ScreenManager, Screen
from kivy.uix.textinput import TextInput
from kivy.uix.label import Label
from kivy.uix.gridlayout import GridLayout
from kivy.uix.button import Button

Builder.load_string('''
<Root>:
    MainScreen:
        name: 'main'
    AnotherScreen:
        name: 'another'

<MainScreen>:
    GridLayout:
        cols: 2
        Label:
            text: "Select Subjects"
            font_size: 15
        Label:
            text: " "
        CheckBox:
            on_active:root.ping('ch1')
        Label:
            text: "Termotecnia"
        CheckBox:
            on_active:root.ping('ch2')
        Label:
            text: "Control"
        CheckBox:
            on_active:root.ping('ch3')
        Label:
            text: "Termotcnia"
        CheckBox:
            on_active:root.ping('ch4')
        Label:
            text: "Control"
        Button:
            text: "Exit"
            background_color: .7, .7, 6, 1
            on_release:root.parent.current='another'
        Button:
            text: "Run"
            font_size: 24
            background_color: .7, .7, 1, 1
            on_release: root.parent.current='another'


''')


class MainScreen(Screen):
    def __init__(self, **kw):
        super(MainScreen, self).__init__(**kw)
        self.a = App.get_running_app()
        self.e={}
    def ping(self,n):
        if self.a.big_dict[n]=='False':
            self.a.big_dict[n]='True'
        else: 
            self.a.big_dict[n]='False'
        print self.a.big_dict
        self.e=self.a.big_dict

class AnotherScreen(GridLayout):
    def __init__(self, **kw):
        super(AnotherScreen, self).__init__(**kw)
        t=[] 
        self.i=MainScreen()
        self.cols=2
        self.add_widget(Button(text='Assignatura',background_color=[0,1,1,1]))
        self.add_widget(Button(background_color=[0,1,1,1],text='Escriu Grup'))
        for k in self.i.e:
            if self.i.e[k]=='True':
                self.add_widget(Label(text=k))
                self.k=TextInput(multiline=False)
                t.append(self.k.text)
                self.add_widget(self.k)
            else:
                pass
        b1=Button(text='Exit',background_color=[0,1,0,1])
        self.add_widget(b1)
        b2=Button(text='Run',background_color=[0,1,0,1])
        self.add_widget(b2)
        b1.bind(on_press=exit)


class Root(ScreenManager):
    pass
class SimpleKivy(App):
    big_dict={'ch1':'False','ch2':'False','ch3':'False','ch4':'False'}
    def build(self):
        return Root()
SimpleKivy().run()

This code doesn't work because ScreenManager only works with a Screen, not with a GridLayout (first problem). The big_dict save the data of the checkboxes, but when I try to import to another class, it doesn't work (another problem). If anyone knows the answer to these problems it would be very helpful, because I know how to do with e.g. 13 fields always, but I want to do with a preselection, because there are too many fields (in this program only 4, but there are 14).

Upvotes: 1

Views: 2000

Answers (1)

zeeMonkeez
zeeMonkeez

Reputation: 5157

Where to start... First, let's look at SimpleKivy. You probably want to make big_dict a DictProperty, instead of a class attribute:

class SimpleKivy(App):
    big_dict = DictProperty({'ch1':False,'ch2':False,'ch3':False,'ch4':False})

I've also changed the values to booleans, because that will be more useful later.

Then, let's add another rule for AnotherScreen to the kv string:

<AnotherScreen>:
    GridLayout:
        id: container
        cols: 2

This way, we can refer to the actual layout by its id.

We also change the definition of ping:

def ping(self, n, value):
    self.a.big_dict[n] = value

When we then also change the CheckBoxes in the kv string to

CheckBox:
    on_active:root.ping('ch1', self.active)

the state of the checkbox will be directly transferred to the dictionary in the app instance. (Alternatively, if you don't need ping for other things, this could be simplified to on_active: app.big_dict['ch3'] = self.active)

Finally, the definition of AnotherScreen:

class AnotherScreen(Screen):
    def on_pre_enter(self, *args):
        t=[] 
        a = App.get_running_app()
        self.ids.container.add_widget(Button(text='Assignatura',background_color=[0,1,1,1]))
        self.ids.container.add_widget(Button(background_color=[0,1,1,1],text='Escriu Grup'))
        for k,v in a.big_dict.iteritems():
            if v:
                self.ids.container.add_widget(Label(text=k))
                self.k=TextInput(multiline=False)
                t.append(self.k.text)
                self.ids.container.add_widget(self.k)
        b1=Button(text='Exit',background_color=[0,1,0,1])
        self.ids.container.add_widget(b1)
        b2=Button(text='Run',background_color=[0,1,0,1])
        self.ids.container.add_widget(b2)
        b1.bind(on_press=exit)

This is called before the screen is entered, will get the dictionary directly from the app instance (so no need to refer to MainScreen), and iterate over said dictionary.

Upvotes: 1

Related Questions