Gostaria de importar elementos opcionais da GUI definidos em arquivos .py separados e adicioná-los/remover dinamicamente da GUI principal. Os elementos opcionais são definidos em um arquivo de configuração de texto e, portanto, os nomes dos arquivos não são conhecidos antecipadamente. Minha ideia original era impor um formato comum - a classe principal seria nomeada de forma idêntica em todos os arquivos de módulo. Descobri que a implementação mais simples disso se comporta de forma inesperada. Embora os objetos importados pareçam ter nomes distintos, há, de fato, diafonia. Isso demonstra o problema e uma solução alternativa que encontrei no SE (comentada):
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()
if "1" in hint:
self.module = importlib.import_module("Mod1").Module()#PROBLEMATIC
#module = importlib.import_module("Mod1")
#class_ = getattr(module, "Module1")
#self.module = class_()
if "2" in hint:
self.module = importlib.import_module("Mod2").Module()#PROBLEMATIC
#module = importlib.import_module("Mod2")
#class_ = getattr(module, "Module2")
#self.module = class_()
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('''
#<Module1>:
<Module>: #PROBLEMATIC
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)])
''')
#class Module1(FloatLayout):
class Module(FloatLayout): #PROBLEMATIC
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('''
#<Module2>:
<Module>: #PROBLEMATIC
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)])
''')
#class Module2(FloatLayout):
class Module(FloatLayout): #PROBLEMATIC
def __init__(self, **kwargs):
super(FloatLayout, self).__init__(**kwargs)
def dummymethod2(self):
pass
Parece que, embora os módulos tenham nomes distintos Mod1
e Mod2
, a classe principal ter o mesmo nome é um problema. Uma vez que ambos os módulos foram importados pelo menos uma vez, ambos aparecem na GUI, independentemente de qual deles está realmente ativado.
Embora minha solução alternativa funcione para evitar esse comportamento indesejado, gostaria de saber o que o causa. Isso tem a ver com como a importação funciona no Python em geral ou com um problema específico do Kivy? Existe outra maneira de lidar com isso que permitiria classes com nomes idênticos em módulos distintos?
Acho que o problema está nas suas
kv
strings. Cada vez que você importaMod1
ouMod2
, suas respectivaskv
strings são carregadas. Quando uma regra é carregada para uma classe (como<module>
), essa regra é salva emBuilder
. Se outra regra for carregada para o mesmo nome de classe, ela substitui a regra carregada anteriormente. Se você pressionar qualquer umButton
no Módulo carregado, ele exibirá as informações do módulo carregado mais recentemente. Pode haver outros problemas associados ao carregamentokv
de strings várias vezes.Eu sugeriria remover as
kv
strings dos módulos e adicionar uma regra<Module>
no script principal do Python para que ele seja carregado apenas uma vez, assim:Eu também mudei:
para:
e removeu a linha:
do
load_module()
método.