I am building a dynamic user interface using Python and Kivy. Because I need to add and remove widgets dynamically I want to use a separate class to handle adding and removing widgets from a GridLayout. I called this class LayoutManager.
The GridLayout is defined in my kv-File (id: "config_box_layout"). Inside my root widget class in my python code I am referencing the GridLayout via id. This is working properly. This reference is passed to the LayoutManager in the constructor. I tried passing it via ObjectProperty or GridLayout.
The problem is that I always end up with this kind of error if I try to remove widgets from the Layout:
'kivy.properties.ObjectProperty' object has no attribute 'remove_widget'
If I try to remove a widget in the save-method inside my Tab class using config_box_layout.remove_widget(some newly created Label) it's working properly.
I think the problem is that Kivy and all the kv-widgets are weakly-referenced and handling those references over to other classes seem to be not the intended use case.
I try to seperate classes to avoid doing all the coding stuff in one big fat main Layout class.
Looking forward to any help! :)
main.py
import kivy
from util.layoutManager import LayoutManager
from kivy.app import App
kivy.require('1.11.1')
from kivy.uix.label import Label
from kivy.uix.tabbedpanel import TabbedPanel
from kivy.properties import ObjectProperty
from kivy.uix.popup import Popup
from kivy.uix.floatlayout import FloatLayout
from kivy.uix.dropdown import DropDown
from kivy.uix.button import Button
from functools import partial
class ChooseDirectoryDialog(FloatLayout):
add = ObjectProperty(None)
cancel = ObjectProperty(None)
save = ObjectProperty(None)
class Tab(TabbedPanel):
config_add_src = ObjectProperty()
ingest_layout = ObjectProperty()
configManager = ConfigManager()
config_box_layout = ObjectProperty()
layoutManager = LayoutManager(config_box_layout)
def add_src(self):
content = ChooseDirectoryDialog(cancel=self.cancel, save=self.save)
self._popup = Popup(title="Add Source", content=content,
size_hint=(0.9, 0.9))
self._popup.open()
def cancel(self):
self._popup.dismiss()
def delete_source(self, description, widget):
self.configManager.delete_source(description)
self.remove_source_from_ui(description)
def remove_source_from_ui(self, description):
self.layoutManager.remove_configuration(description)
def save(self, srcpath, destpath, description):
desc_label = Label(text=description)
desc_label.size_hint_y = None
desc_label.height = 30
self.config_box_layout.add_widget(desc_label)
srcpath_label = Label(text=srcpath)
srcpath_label.size_hint_y = None
srcpath_label.height = 30
self.config_box_layout.add_widget(srcpath_label)
destpath_label = Label(text=destpath)
destpath_label.size_hint_y = None
destpath_label.height = 30
self.config_box_layout.add_widget(destpath_label)
deleteButton = Button(text="Quelle löschen")
deleteButton.size_hint_y = None
deleteButton.height = 30
deleteButton.bind(on_press=partial(self.delete_source, description))
self.config_box_layout.add_widget(deleteButton)
self.layoutManager.add_configuration(description,
desc_label, srcpath_label, destpath_label, deleteButton)
self.configManager.add_source(description, srcpath, destpath)
self._popup.dismiss()
def copyToDestination(self, srcpath, destpath):
pass
class AutoIngest(App):
def build(self):
return Tab()
if __name__ == '__main__':
#Builder.load_file('autoingest.kv')
AutoIngest().run()
autoingest.kv
#:kivy 1.11.1
<Tab>:
do_default_tab: False
config_add_button: add_button
config_box_layout: config_box
TabbedPanelItem:
text: 'Konfiguration'
TabbedPanel:
tab_width: 200
do_default_tab: False
TabbedPanelItem:
text: 'Quellen verwalten'
StackLayout:
orientation: "lr-tb"
padding: 10
Button:
size_hint: .2, .1
id: add_button
text: 'Quelle hinzufügen'
on_press: root.add_src()
GridLayout:
id: config_box
cols: 4
Label:
size_hint_y: None
height: 30
text: "Bezeichnung"
Label:
size_hint_y: None
height: 30
text: "Quell-Pfad"
Label:
size_hint_y: None
height: 30
text: "Ziel-Pfad"
Label:
size_hint_y: None
height: 30
text: "Aktionen"
<ChooseDirectoryDialog>:
text_input: text_input
BoxLayout:
size: root.size
pos: root.pos
orientation: "vertical"
Label:
size_hint_y: None
height: 30
text: "Bezeichnung"
TextInput:
id: text_input
size_hint_y: None
height: 30
multiline: False
Label:
size_hint_y: None
height: 30
text: "Quellverzeichnis auswählen"
FileChooserListView:
id: source_chooser
Label:
size_hint_y: None
height: 30
text: "Zielverzeichnis auswählen"
FileChooserListView:
id: destination_chooser
BoxLayout:
size_hint_y: None
height: 30
Button:
text: "Cancel"
on_release: root.cancel()
Button:
text: "Add"
on_release: root.save(source_chooser.path, destination_chooser.path, text_input.text)
layoutManager.py
from kivy.uix.gridlayout import GridLayout
from kivy.properties import ObjectProperty
class LayoutManager:
#I alrey tried to pass the GridLayout itself. This didn't work either.
def __init__(self, configlayout: ObjectProperty):
self.configurations = {}
self.configlayout = configlayout
def remove_configuration(self, description):
widgets = self.configurations.get(description)
for x in widgets:
self.configlayout.remove_widget(x)
def add_configuration(self, description, *widgets):
self.configurations[description] = {'widgets': widgets}
I believe the problem is that the Tab instance ids have not been setup when your lines:
config_box_layout = ObjectProperty()
layoutManager = LayoutManager(config_box_layout)
are executed, so config_box_layout is not yet set.
You can force the creation of the LayoutManager to delay until the ids are created using kivy.clock in the build() method of the App:
class AutoIngest(App):
def build(self):
Clock.schedule_once(self.setup_layoutManager)
return Tab()
def setup_layoutManager(self, dt):
self.root.layoutManager = LayoutManager(self.root.config_box_layout)
Also, the line:
layoutManager = LayoutManager(config_box_layout)
can be removed from the Tab class.
Once that is working you will discover issues in your remove_configuration() method of LayoutManager.
Related
I am having trouble understanding how to inherit the functionality of one class into another with Kivy. I understand the error message ('LoadDialog_Directory' object has no attribute 'manager'), but I'm just not grasping exactly how to fix it. I believe I need to do something with the function below, but ultimately, I do not know.
def __init__(self, **kwargs):
super().__init__(**kwargs)
The goal of this script is to be able to select a specific driver as the path for the filechooser. I choose this method vs. others because most were using Kivy 1.11.1, this version has a lot of deprecated functions that do not work with 2.0.
.py
from kivy.app import App
from kivy.uix.screenmanager import ScreenManager, Screen
from kivy.lang.builder import Builder
from kivy.uix.spinner import SpinnerOption
from kivy.uix.popup import Popup
from kivy.uix.button import Button
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.floatlayout import FloatLayout
from kivy.properties import ObjectProperty
from kivy.utils import platform
from kivy.properties import StringProperty
import os
import string
class WindowManager(ScreenManager):
pass
class MyOption(SpinnerOption):
pass
class LoadDialog_Directory(FloatLayout):
load = ObjectProperty(None)
cancel = ObjectProperty(None)
def dir_driver(self):
x = self.manager.get_screen("first")
return str(x.ids.drive_dir.text)
class FirstWindow(Screen):
def get_drives(self):
drives = ['%s:' % d for d in string.ascii_uppercase if os.path.exists('%s:' % d)]
return drives
def dismiss_popup(self):
self._popup.dismiss()
def show_load_directory(self):
content = LoadDialog_Directory(load=self.directroy_path, cancel=self.dismiss_popup)
self._popup = Popup(title="Load file", content=content, size_hint=(0.9, 0.9))
self._popup.open()
def directroy_path(self, path, filename):
self.ids.text_input_directory.text = str(filename[0])
self.dismiss_popup()
kv_main = Builder.load_file('main.kv')
#
class MyApp(App):
def build(self):
return kv_main
if __name__ == '__main__':
MyApp().run()
main.kv
#:include first.kv
WindowManager:
FirstWindow:
<LoadDialog_Directory>:
BoxLayout:
size: root.size
pos: root.pos
orientation: "vertical"
FileChooserListView:
id: filechooser
dirselect: True
path: root.dir_driver()
BoxLayout:
size_hint_y: None
height: 30
Button:
text: "Cancel"
on_release: root.cancel()
Button:
text: "Load"
on_release: root.load(filechooser.path, filechooser.selection)
first.kv
<FirstWindow>
name: 'first'
GridLayout:
cols: 1
BoxLayout:
orientation: "horizontal"
TextInput:
BoxLayout:
orientation: "horizontal"
Spinner:
id: drive_dir
text: "Root Drive"
halign: 'center'
option_cls: "MyOption"
values: root.get_drives()
Button:
text: "Set Result Directory"
on_release: root.show_load_directory()
TextInput:
id: text_input_directory
disabled: True
text: text_input_directory.text
BoxLayout:
size_hint: (0.01, 1)
orientation: "horizontal"
TextInput:
Side Note: The reason for the extra blank TextInput is because the Spinner will not function (show drivers) if it is taking too much of the App.
After a few hours of trail and error I finally got it to work, but I have no idea why it works. Here is what changed:
New variable in class LoadDialog_Directory(FloatLayout) - input_pth
Called input_pth into my content variable within class FirstWindow(Screen) function show_load_directory(self)
Set my filechooser.path to root.input_pth in the main.kv file.
I do not understand how the input_pth variable within content is able to reference the class LoadDialog_Directory(FloatLayout) without having to pass something like:
def __init__(self, input_pth, **kwargs):
super(LoadDialog_Directory, self).__init__()
self.input_pth = input_pth
.py
from kivy.app import App
from kivy.uix.screenmanager import ScreenManager, Screen
from kivy.lang.builder import Builder
from kivy.uix.spinner import SpinnerOption
from kivy.uix.popup import Popup
from kivy.uix.button import Button
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.floatlayout import FloatLayout
from kivy.properties import ObjectProperty
from kivy.utils import platform
from kivy.properties import StringProperty
import os
import string
class WindowManager(ScreenManager):
pass
class MyOption(SpinnerOption):
pass
class LoadDialog_Directory(FloatLayout):
input_pth = StringProperty()
load = ObjectProperty()
cancel = ObjectProperty()
class FirstWindow(Screen):
def drive_list(self):
drives = ['%s:' % d for d in string.ascii_uppercase if os.path.exists('%s:' % d)]
return drives
def dismiss_popup(self):
self._popup.dismiss()
def show_load_directory(self):
content = LoadDialog_Directory(load=self.directroy_path, cancel=self.dismiss_popup, input_pth=self.ids.drive_dir.text)
self._popup = Popup(title="Load file", content=content, size_hint=(0.9, 0.9))
self._popup.open()
def directroy_path(self, path, filename):
self.ids.text_input_directory.text = str(filename[0])
self.dismiss_popup()
kv_main = Builder.load_file('main.kv')
#
class MyApp(App):
def build(self):
return kv_main
if __name__ == '__main__':
MyApp().run()
main.kv
#:include first.kv
WindowManager:
FirstWindow:
<LoadDialog_Directory>:
BoxLayout:
size: root.size
pos: root.pos
orientation: "vertical"
FileChooserListView:
id: filechooser
dirselect: True
path: root.input_pth
BoxLayout:
size_hint_y: None
height: 30
Button:
text: "Cancel"
on_release: root.cancel()
Button:
text: "Load"
on_release: root.load(filechooser.path, filechooser.selection)
first.kv
<FirstWindow>
name: 'first'
GridLayout:
cols: 1
BoxLayout:
orientation: "horizontal"
TextInput:
BoxLayout:
orientation: "horizontal"
Spinner:
id: drive_dir
halign: 'center'
option_cls: "MyOption"
values: root.drive_list()
Button:
text: "Set Result Directory"
on_release: root.show_load_directory()
TextInput:
id: text_input_directory
disabled: True
text: text_input_directory.text
BoxLayout:
size_hint: (0.01, 1)
orientation: "horizontal"
TextInput:
Is there a way to reference a custom widget from the py file?
I made a widget in kv, but I want to reference it from py, then add it again to another widget in kv.
I tried doing this using id but I got an error (KeyError: 'words_entry').
This is what I tried:
from kivy.app import App
from kivy.uix.screenmanager import ScreenManager, Screen
from kivy.lang import Builder
from kivy.properties import ObjectProperty
from kivy.uix.textinput import TextInput
import os
class GetCount(Screen):
count_input = ObjectProperty(None)
def next(self):
# Setup next screen
text_inputs = [self.ids.words_entry for i in range(int(self.count_input.text))]
for text_input in text_inputs:
self.manager.ids.get_input.ids.grid.add_widget(text_input)
# Switch to next screen
self.manager.current = "get_input"
class GetInput(Screen):
pass
kv_file = Builder.load_string("""
ScreenManager:
GetCount:
name: "get_count"
id: get_count
GetInput:
name: "get_input"
id: get_input
<WordEntry#TextInput>:
id: words_entry
multiline: False
size_hint: (self.width, None)
<GetCount>:
count_input: count_input
FloatLayout:
Label:
text: "count"
size_hint: 1, 0.05
pos_hint: {"top":0.9}
TextInput:
id: count_input
size_hint: 0.8, 0.05
pos_hint: {"top":0.7, "x":0.1}
multiline: False
Button:
text: "Next"
on_release: root.next()
size_hint: 0.8, 0.05
pos_hint: {"top":0.5, "x":0.1}
<GetInput>:
ScrollView:
GridLayout:
size_hint_y: None
height: self.minimum_height
id: grid
cols: 1
""")
class MainApp(App):
def build(self):
return kv_file
if __name__ == "__main__":
app = MainApp()
app.run()
In this code, I want to add WordEntry to the GridLayout in GetInput from py (the reason is that I need to add multiple depending on the user's input).
You can use Factory to create an instance of a class that has been defined in kv. So your GetCount class can be:
from kivy.factory import Factory
class GetCount(Screen):
count_input = ObjectProperty(None)
def next(self):
# Setup next screen
for _ in range(int(self.count_input.text)):
new_word_entry = Factory.WordEntry()
self.manager.ids.get_input.ids.grid.add_widget(new_word_entry)
# Switch to next screen
self.manager.current = "get_input"
I have worked mostly with Tkinter before and I used the following code to store path of file I selected. After exploring Kivy, it doesn't seem like Kivy have a simple function like this. So I tried using the filechoser instead
path = filedialog.askopenfilename(initialdir="/", title="Select file")
Main code is very simple
class checker_ui(GridLayout):
def findreport(self,path):
pass
class Checker(App):
def build(self):
return checker_ui()
if __name__ == '__main__':
Checker().run()
Then I created a .kv file just to see what filechooser looks like. The script would just open and freeze.
<checker_ui>:
BoxLayout:
FileChooserIconLayout:
id:filechooser
on_selection:root.select(*args)
Here is the output
I search all over the place and did not see anyone with a similar issue. Is there an alternative I can try to mimic the tkinter behaviour or am I stuck with this issue? I'm on a Windows machine and normal kivy widget works, ie: button,label etc.
so I wrote a lot more code than you (based heavily on the kivy example # https://kivy.org/docs/api-kivy.uix.filechooser.html#usage-example)
from kivy.app import App
from kivy.uix.floatlayout import FloatLayout
from kivy.factory import Factory
from kivy.properties import ObjectProperty
from kivy.uix.popup import Popup
from kivy.lang import Builder
Builder.load_string("""
#:kivy 1.1.0
<Root>:
text_input: text_input
BoxLayout:
orientation: 'vertical'
BoxLayout:
size_hint_y: None
height: 30
Button:
text: 'Load'
on_release: root.show_load()
Button:
text: 'Save'
on_release: root.show_save()
BoxLayout:
TextInput:
id: text_input
text: ''
RstDocument:
text: text_input.text
show_errors: True
<LoadDialog>:
BoxLayout:
size: root.size
pos: root.pos
orientation: "vertical"
FileChooserListView:
id: filechooser
BoxLayout:
size_hint_y: None
height: 30
Button:
text: "Cancel"
on_release: root.cancel()
Button:
text: "Load"
on_release: root.load(filechooser.path, filechooser.selection)
<SaveDialog>:
text_input: text_input
BoxLayout:
size: root.size
pos: root.pos
orientation: "vertical"
FileChooserListView:
id: filechooser
on_selection: text_input.text = self.selection and self.selection[0] or ''
TextInput:
id: text_input
size_hint_y: None
height: 30
multiline: False
BoxLayout:
size_hint_y: None
height: 30
Button:
text: "Cancel"
on_release: root.cancel()
Button:
text: "Save"
on_release: root.save(filechooser.path, text_input.text)""")
import os
class LoadDialog(FloatLayout):
load = ObjectProperty(None)
cancel = ObjectProperty(None)
class SaveDialog(FloatLayout):
save = ObjectProperty(None)
text_input = ObjectProperty(None)
cancel = ObjectProperty(None)
class Root(FloatLayout):
loadfile = ObjectProperty(None)
savefile = ObjectProperty(None)
text_input = ObjectProperty(None)
def dismiss_popup(self):
self._popup.dismiss()
def show_load(self):
content = LoadDialog(load=self.load, cancel=self.dismiss_popup)
self._popup = Popup(title="Load file", content=content,
size_hint=(0.9, 0.9))
self._popup.open()
def show_save(self):
content = SaveDialog(save=self.save, cancel=self.dismiss_popup)
self._popup = Popup(title="Save file", content=content,
size_hint=(0.9, 0.9))
self._popup.open()
def load(self, path, filename):
with open(os.path.join(path, filename[0])) as stream:
self.text_input.text = stream.read()
self.dismiss_popup()
def save(self, path, filename):
with open(os.path.join(path, filename), 'w') as stream:
stream.write(self.text_input.text)
self.dismiss_popup()
class Editor(App):
def build(self):
return Root()
if __name__ == '__main__':
Editor().run()
Solution
Replace FileChooserIconLayout with FileChooserIconView
Add a BoxLayout and make FileChooserIconView a children of the BoxLayout
Example
main.py
from kivy.app import App
from kivy.uix.gridlayout import GridLayout
class Checker_ui(GridLayout):
def findreport(self, path):
print("path=", path)
class Checker(App):
def build(self):
return Checker_ui()
if __name__ == "__main__":
Checker().run()
checker.kv
#:kivy 1.10.0
<Checker_ui>:
BoxLayout:
size: root.size
pos: root.pos
orientation: "vertical"
FileChooserIconView:
id: filechooser
on_selection: root.select(*args)
Output
Hey if anyone wants to open all types of file just use(mp4,jpg,pdf,txt,docs...etc)
import webbrowser
webbrowser.open(filename[0])
I am attempting to dynamically add and remove widgets from a GridLayout with Python-Kivy, and I am running into an issue with weak references. When the Screen containing the GridLayout is initialized, I am placing a Label inside of the GridLayout that contains text notifying the user that the container has no items (technically, it has the Label in there so it is not necessarily empty). Then I have a Button that allows the user to add individual GridLayout widgets to the GridLayout that contain a Label, a TextInput and a CheckBox, as well as a Button that allows the user to remove those individual GridLayout widgets that they created. If all of these widgets (dynamically added by the user) are removed, then the original Label is added back to the GridLayout.
When I attempt to construct this logic in Python to pair with Kivy, I run into an issue where the original Label never seems to be fully removed from the stack.
I was under the impression that self.ids.widget_list.remove_widget(self.ids.empty) would remove the Label widget with id empty, however this is not the case. It is clear that the widget still exists, when I call print(self.ids):
{'widget_list': <WeakProxy to <kivy.uix.gridlayout.GridLayout object at 0x0451F030>>, 'empty': <WeakProxy to <kivy.uix.gridlayout.GridLayout object at 0x0451F9D0>>}
Any help is much appreciated.
EDIT
When checking for i in self.layouts: print(i.children) every time the remove() method is called, shows that the references to the added widgets are never being completely removed. This may be where my issues resides, but unsure how to resolve it.
Python
import kivy
from kivy.app import App
from kivy.lang import Builder
from kivy.uix.gridlayout import GridLayout
from kivy.uix.button import Button
from kivy.uix.textinput import TextInput
from kivy.uix.scrollview import ScrollView
from kivy.uix.label import Label
from kivy.uix.checkbox import CheckBox
from kivy.uix.popup import Popup
from kivy.uix.screenmanager import ScreenManager, Screen
#Load kv file
Builder.load_file('test.kv')
#First Screen
class Screen1(Screen):
layouts = []
def remove(self):
for i in self.layouts:
if i.children[0].active:
self.ids.widget_list.remove_widget(i)
if len(self.layouts)==0:
layout = GridLayout(rows=1, id=empty)
layout.add_widget(Label(text='Nothing Here'))
self.ids.widget_list.add_widget(layout)
else:
self.update_hints()
def add(self):
if len(self.ids.widget_list.children)<5:
print(self.ids)
self.ids.widget_list.remove_widget(self.ids.empty)
print(self.ids)
layout = GridLayout(cols=3)
layout.add_widget(Label(text='Test ' + str(len(self.ids.widget_list.children)+1)))
layout.add_widget(TextInput())
layout.add_widget(CheckBox())
self.ids.widget_list.add_widget(layout)
self.layouts.append(layout)
self.update_hints()
else:
layout = GridLayout(cols=1)
layout.add_widget(Label(text='Only five allowed at once.\nRemove at least one to add another.'))
button = Button(text='Acknowledge'); layout.add_widget(button)
popup = Popup(content=layout, title='Limit Reached', size_hint=(.5,.5), auto_dismiss=False)
button.bind(on_release=popup.dismiss)
popup.open()
def update_hints(self):
for i in self.layouts:
i.children[1].hint_text = 'Ex. ' + str(round(100/len(self.ids.widget_list.children),2)) + '%'
#Initialize Screens and Start App
class MyScreenManager(ScreenManager):
pass
#Main application
class SampleApp(App):
def build(self):
self.sm = MyScreenManager()
return self.sm
if __name__ == '__main__':
SampleApp().run()
kv
<MyScreenManager>:
Screen1:
name: 'screen1'
<Screen1>:
BoxLayout:
orientation: 'vertical'
GridLayout:
cols: 3
padding: 20
spacing: 20
size_hint: 1, .1
Label:
text: 'List of Widgets'
underline: True
Label:
text: 'Percentage'
underline: True
Label:
text: 'Remove (Y/N)'
underline: True
ScrollView:
size_hint: 1, .5
do_scroll_x: False
padding: 20
spacing: 20
GridLayout:
id: widget_list
cols: 1
spacing: 5
GridLayout:
id: empty
rows: 1
Label:
text: 'Nothing Here'
GridLayout:
cols: 3
padding: 20
spacing: 20
size_hint: 1, .2
Button:
text: 'Add Widget'
on_release: root.add()
Label:
text: ''
Button:
text: 'Remove Widget'
on_release: root.remove()
Ok, I seem to have found a simpler means of implementation that avoids specifying any id for the initial Label (the same one that shows if self.layouts==[]):
Python
import kivy
from kivy.app import App
from kivy.lang import Builder
from kivy.uix.gridlayout import GridLayout
from kivy.uix.button import Button
from kivy.uix.textinput import TextInput
from kivy.uix.scrollview import ScrollView
from kivy.uix.label import Label
from kivy.uix.checkbox import CheckBox
from kivy.uix.popup import Popup
from kivy.uix.screenmanager import ScreenManager, Screen
#Load kv file
Builder.load_file('test.kv')
#First Screen
class Screen1(Screen):
count = 0
layouts = []
def remove(self):
for i in self.layouts:
if i.children[0].active:
self.ids.widget_list.remove_widget(i)
self.layouts = [i for i in self.layouts if not i.children[0].active]
if self.layouts!=[]:
self.update_hints()
else:
layout = GridLayout(rows=1)
layout.add_widget(Label(text='Nothing Here'))
self.ids.widget_list.add_widget(layout)
def add(self):
if self.layouts==[]:
self.ids.widget_list.clear_widgets()
if len(self.ids.widget_list.children)<5:
self.count+=1
layout = GridLayout(cols=3)
layout.add_widget(Label(text='Test ' + str(self.count)))
layout.add_widget(TextInput())
layout.add_widget(CheckBox())
self.ids.widget_list.add_widget(layout)
self.layouts.append(layout)
self.update_hints()
else:
layout = GridLayout(cols=1)
layout.add_widget(Label(text='Only five allowed at once.\nRemove at least one to add another.'))
button = Button(text='Acknowledge'); layout.add_widget(button)
popup = Popup(content=layout, title='Limit Reached', size_hint=(.5,.5), auto_dismiss=False)
button.bind(on_release=popup.dismiss)
popup.open()
def update_hints(self):
for i in self.layouts:
i.children[1].hint_text = 'Ex. ' + str(round(100/len(self.ids.widget_list.children),2)) + '%'
#Initialize Screens and Start App
class MyScreenManager(ScreenManager):
pass
#Main application
class SampleApp(App):
def build(self):
self.sm = MyScreenManager()
return self.sm
if __name__ == '__main__':
SampleApp().run()
kv
<MyScreenManager>:
Screen1:
name: 'screen1'
<Screen1>:
BoxLayout:
orientation: 'vertical'
GridLayout:
cols: 3
padding: 20
spacing: 20
size_hint: 1, .1
Label:
text: 'List of Widgets'
underline: True
Label:
text: 'Percentage'
underline: True
Label:
text: 'Remove (Y/N)'
underline: True
ScrollView:
size_hint: 1, .5
do_scroll_x: False
padding: 20
spacing: 20
GridLayout:
id: widget_list
cols: 1
spacing: 5
GridLayout:
rows: 1
Label:
text: 'Nothing Here'
GridLayout:
cols: 3
padding: 20
spacing: 20
size_hint: 1, .2
Button:
text: 'Add Widget'
on_release: root.add()
Label:
text: ''
Button:
text: 'Remove Widget'
on_release: root.remove()
I am a new user to kivy.
I need to link a checkbox from the .kv interface so that it have an impact on the underlying python application.
I tried the following code for my application:
from kivy.app import App
from kivy.uix.floatlayout import FloatLayout
from kivy.factory import Factory
from kivy.properties import ObjectProperty
from kivy.uix.popup import Popup
from kivy.uix.checkbox import CheckBox
import os
class LoadDialog(FloatLayout):
load = ObjectProperty(None)
cancel = ObjectProperty(None)
class Root(FloatLayout):
loadfile = ObjectProperty(None)
checkbox = CheckBox()
def dismiss_popup(self):
self._popup.dismiss()
def show_load(self):
content = LoadDialog(load=self.load, cancel=self.dismiss_popup)
self._popup = Popup(title="Load file", content=content, size_hint=(0.9, 0.9))
self._popup.open()
def load(self, path, filename):
print 'user chose: %s' % os.path.join(path, filename[0])
print self.checkbox.active
self.dismiss_popup()
def activate_checkbox(self):
print 'activate checkbox'
self.checkbox._toggle_active()
class Chooser(App):
pass
Factory.register('Root', cls=Root)
Factory.register('LoadDialog', cls=LoadDialog)
if __name__ == '__main__':
Chooser().run()
with the following chooser.kv file:
#:kivy 1.1.0
Root:
BoxLayout:
orientation: 'vertical'
BoxLayout:
size_hint_y: 90
height: 30
Button:
text: 'Load'
on_release: root.show_load()
BoxLayout:
size_hint_y: 90
height: 30
CheckBox:
center: self.parent.center
on_active: root.activate_checkbox
Label:
font_size: 20
center_x: self.parent.width / 4
top: self.parent.top - 50
text: 'Faster'
<LoadDialog>:
BoxLayout:
size: root.size
pos: root.pos
orientation: "vertical"
FileChooserListView:
id: filechooser
BoxLayout:
size_hint_y: None
height: 30
Button:
text: "Cancel"
on_release: root.cancel()
Button:
text: "Load"
on_release: root.load(filechooser.path, filechooser.selection)
Unfortunately it was to no avail: it seems that the state of the CheckBox user interface element have no effect whatsoever on the state of the checkbox inside the Root Class.
Is there any simple way to link the two?
on_active: root.activate_checkbox
This doesn't do anything, you want root.activate_checkbox().