I would like to import optional GUI elements defined in separate Mod1.py
/Mod2.py
/etc files and add/remove these dynamically from the main GUI. The separate files that define these optional GUI elements, contain kv
strings. In my use case, these GUI elements can be unloaded/reloaded multiple times. I discovered that if I have identically named classes in the Modx.py
files, this creates cross-talk between the modules because Builder.load_string
works cumulatively. The background to this discovery is here - Importing multiple modules containing identically named class in python
Kivy Builder documentation suggests that a given kv
string can be selectively unloaded later if a pseudo filename is supplied e.g.
Builder.load_string("""<kv string>""", filename="myrule.kv")
and later to unload -
Builder.unload_file("myrule.kv")
However when I try this, it appears to work only the first time a module is unloaded and another one is loaded. After that the optional GUI elements no longer appear when reloaded. The following example demonstrates this.
from kivy.app import App
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.floatlayout import FloatLayout
from kivy.uix.button import Button
from kivy.lang import Builder
import importlib
Builder.load_string('''
<MainWidget>:
orientation: 'vertical'
BoxLayout:
Button:
text: "Load Mod 1"
on_press:
root.load_module(self.text)
Button:
text: "Load Mod 2"
on_press:
root.load_module(self.text)
Button:
text: "Unload all"
on_press:
dock.clear_widgets()
FloatLayout:
id: dock
''')
class MainWidget(BoxLayout):
def load_module(self, hint):
self.ids.dock.clear_widgets()
Builder.unload_file("foo.kv")
if "1" in hint:
self.module = importlib.import_module("Mod1").Module()
if "2" in hint:
self.module = importlib.import_module("Mod2").Module()
self.ids.dock.add_widget(self.module)
class MyApp(App):
def build(self):
return MainWidget()
if __name__ == '__main__':
MyApp().run()
Mod1.py
from kivy.uix.floatlayout import FloatLayout
from kivy.lang import Builder
Builder.load_string('''
<Module>:
size_hint: None, None
size: self.parent.size if self.parent else self.size
pos: self.parent.pos if self.parent else self.pos
Button:
size_hint: None, None
width: self.parent.width / 3
height: self.parent.height
pos: self.parent.pos
text: "Mod 1"
on_press: print(root); print([x for x in dir(root) if 'method' in str(x)])
''', filename="foo.kv")
class Module(FloatLayout):
def __init__(self, **kwargs):
super(FloatLayout, self).__init__(**kwargs)
def dummymethod1(self):
pass
Mod2.py
from kivy.uix.floatlayout import FloatLayout
from kivy.lang import Builder
Builder.load_string('''
<Module>:
size_hint: None, None
size: self.parent.size if self.parent else self.size
pos: self.parent.pos if self.parent else self.pos
Button:
size_hint: None, None
width: self.parent.width / 3
height: self.parent.height
pos: (self.parent.x + self.parent.width / 2) , self.parent.y
text: "Mod 2"
on_press: print(root); print([x for x in dir(root) if 'method' in str(x)])
''', filename="foo.kv")
class Module(FloatLayout):
def __init__(self, **kwargs):
super(FloatLayout, self).__init__(**kwargs)
def dummymethod2(self):
pass
I would like to know if there is a way to make this work properly. Perhaps I am missing something about the way Kivy builder functions?
I would like to import optional GUI elements defined in separate Mod1.py
/Mod2.py
/etc files and add/remove these dynamically from the main GUI. The separate files that define these optional GUI elements, contain kv
strings. In my use case, these GUI elements can be unloaded/reloaded multiple times. I discovered that if I have identically named classes in the Modx.py
files, this creates cross-talk between the modules because Builder.load_string
works cumulatively. The background to this discovery is here - Importing multiple modules containing identically named class in python
Kivy Builder documentation suggests that a given kv
string can be selectively unloaded later if a pseudo filename is supplied e.g.
Builder.load_string("""<kv string>""", filename="myrule.kv")
and later to unload -
Builder.unload_file("myrule.kv")
However when I try this, it appears to work only the first time a module is unloaded and another one is loaded. After that the optional GUI elements no longer appear when reloaded. The following example demonstrates this.
from kivy.app import App
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.floatlayout import FloatLayout
from kivy.uix.button import Button
from kivy.lang import Builder
import importlib
Builder.load_string('''
<MainWidget>:
orientation: 'vertical'
BoxLayout:
Button:
text: "Load Mod 1"
on_press:
root.load_module(self.text)
Button:
text: "Load Mod 2"
on_press:
root.load_module(self.text)
Button:
text: "Unload all"
on_press:
dock.clear_widgets()
FloatLayout:
id: dock
''')
class MainWidget(BoxLayout):
def load_module(self, hint):
self.ids.dock.clear_widgets()
Builder.unload_file("foo.kv")
if "1" in hint:
self.module = importlib.import_module("Mod1").Module()
if "2" in hint:
self.module = importlib.import_module("Mod2").Module()
self.ids.dock.add_widget(self.module)
class MyApp(App):
def build(self):
return MainWidget()
if __name__ == '__main__':
MyApp().run()
Mod1.py
from kivy.uix.floatlayout import FloatLayout
from kivy.lang import Builder
Builder.load_string('''
<Module>:
size_hint: None, None
size: self.parent.size if self.parent else self.size
pos: self.parent.pos if self.parent else self.pos
Button:
size_hint: None, None
width: self.parent.width / 3
height: self.parent.height
pos: self.parent.pos
text: "Mod 1"
on_press: print(root); print([x for x in dir(root) if 'method' in str(x)])
''', filename="foo.kv")
class Module(FloatLayout):
def __init__(self, **kwargs):
super(FloatLayout, self).__init__(**kwargs)
def dummymethod1(self):
pass
Mod2.py
from kivy.uix.floatlayout import FloatLayout
from kivy.lang import Builder
Builder.load_string('''
<Module>:
size_hint: None, None
size: self.parent.size if self.parent else self.size
pos: self.parent.pos if self.parent else self.pos
Button:
size_hint: None, None
width: self.parent.width / 3
height: self.parent.height
pos: (self.parent.x + self.parent.width / 2) , self.parent.y
text: "Mod 2"
on_press: print(root); print([x for x in dir(root) if 'method' in str(x)])
''', filename="foo.kv")
class Module(FloatLayout):
def __init__(self, **kwargs):
super(FloatLayout, self).__init__(**kwargs)
def dummymethod2(self):
pass
I would like to know if there is a way to make this work properly. Perhaps I am missing something about the way Kivy builder functions?
Share Improve this question asked yesterday Eugene BEugene B 617 bronze badges 1 |1 Answer
Reset to default 2I think the importlib
will not import a module if it has already been loaded. In that case, you can use importlib.reload()
. Try modifying your MainWidget
class to do that. Something like:
class MainWidget(BoxLayout):
def __init__(self):
self.current_module1 = None
self.current_module2 = None
super(MainWidget, self).__init__()
def load_module(self, hint):
self.ids.dock.clear_widgets()
Builder.unload_file("foo.kv")
if "1" in hint:
if self.current_module1:
self.current_module1 = importlib.reload(self.current_module1)
else:
self.current_module1 = importlib.import_module("Mod1")
self.module = self.current_module1.Module()
if "2" in hint:
if self.current_module2:
self.current_module2 = importlib.reload(self.current_module2)
else:
self.current_module2 = importlib.import_module("Mod2")
self.module = self.current_module2.Module()
self.ids.dock.add_widget(self.module)
open(), read(), close()
. OR maybe you needremove_widget
to remove previous content and later useadd_widget
to add new content. – furas Commented yesterday