Is it possible to have a folder-based structure in kivy? - python

I am trying to make an app. I have got it to work where all my files are in the same folder but it gets very messy and I would like to separate my files into separate folders of the structure: Start, Prelogin, andFirebaseLoginScreen`. So is what I am trying to do possible in kivy?
I have tried #: import name x.y.z as the pointer where x is the folder name, y is the name of kv-file and z is the class in kv-file I want to import, but I get so many weird errors when I try to do it. I have pretty much added every folder and everything to my PYTHONPATH but nothing works.
Start contains main.py and main.kv where main.kv then points to the screenmanger in ``Prelogin. Prelogin contains some files that consist of labels and text about the app and then points to the screenmanger in FirebaseLoginScreen. The FirebaseLoginScreen contains a lot of files for the login system.

Yes, I'will give you and example with this folder structure
-Project\
--main.py
--main.kv
--folder1\
----window1.py
----window1.kv
--folder2\
----window2.py
----window3.py
--folder3\
----window4.py
----window4.kv
folder\window1.py will be like
from kivy.app import App
from kivy.uix.boxlayout import BoxLayout
from kivy.lang.builder import Builder
from kivy.uix.widget import Widget
kivy.require("1.11.1")
# Builder is neccesary to work with multiple files
Builder.load_file("folder1/window1.kv")
class login(BoxLayout):
def __init__(self, **kwargs):
super().__init__(**kwargs)
class login_window(App):
def build(self):
return login()
if __name__=="__main__":
aplicacion=login_window()
aplicacion.run()
folder1\window1.kv is not necessary to specify.
main.py will be like
from kivy.app import App
from kivy.uix.boxlayout import BoxLayout
from kivy.lang.builder import Builder
from kivy.uix.widget import Widget
# Import files like
from folder1.window1 import login_window
from folder2.window2 import example2_window
from folder3.window3 import example3_window
class manager(BoxLayout):
# Add screens to main
login_widget=login_window()
example2_widget=example2_window()
example3_widget=example3_window()
def __init__(self, **kwargs):
super().__init__(**kwargs)
# Check kv file to understand these lines
self.ids.screen_login.add_widget(self.login_widget)
self.ids.screen_example2.add_widget(self.example2_widget)
self.ids.screen_example3.add_widget(self.example3_widget)
class main(App):
def build(self):
return manager()
if __name__ == "__main__":
main().run()
main.kv will be like (Which has the ScreenManager!)
#:kivy 1.11.1
<manager>:
id: main_window
ScreenManager:
id: scrn_mngr_main
Screen:
id: screen_login
name:'screen_login'
Screen:
id: screen_example2
name:'screen_example2'
Screen:
id: screen_example3
name:'screen_example3'
Now, to control the program flow yo need to add these lines in window1.py
self.parent.current='screen_login'
self.parent.current='screen_example2'
self.parent.current='screen_example3'
Adapted from 3 videos of a tutorial on Youtube

The answer is simply "yes", there's nothing special about the import parsing in kv language, or in Python when using Kivy. If you're having issues, it's because what you're attempting is wrong or insufficient, which isn't possible to debug from the amount of information you've given.
If you'd like to follow it up, post another question with an example that you think should work given your PYTHONPATH manipulations.

Related

Kivy : load_string() vs kv file

In order to understand the logic behind Kivy kv language, I'm trying to rewrite a minimal application by replacing the automatic load of a kv file by a call to Builder.load_string().
Here's my starting point (source: examples 1-2, 1-3): two files, weather.py and weather.kv :
weather.py:
from kivy.app import App
class WeatherApp(App):
pass
if __name__ == '__main__':
WeatherApp().run()
and weather.kv:
Label:
text: "Hello World"
Up to there, everything's alright
.
But if I try to load manually the kv stuff, I just get a black screen (and no error message). My code:
from kivy.app import App
from kivy.lang import Builder
KV = '''
Label
text: "Hello World"
'''
Builder.load_string(KV)
class WeatherApp(App):
pass
if __name__ == '__main__':
WeatherApp().run()
I'm obviously missing something here, but what ? Any help would be appreciated !
When you create a .kv there are basic but strict rules, among them there can only be one toplevel, the toplevel is identified because it does not have "<>", besides for the App to recognize it it must have the same name of the application in lowercase, in your case the .kv is called weather.kv and the WeatherApp app. But the above does not happen if you use Builder, in the case that the .kv has a root as it is in your case Builder.load_string() returns it so you must return it in the build method of the App:
from kivy.app import App
from kivy.lang import Builder
KV = '''
Label:
text: "Hello World"
'''
root = Builder.load_string(KV)
class WeatherApp(App):
def build(self):
return root
if __name__ == '__main__':
WeatherApp().run()

Kivy Python - Button to increment variable, without using NumericProperty

I'm a complete novice to both python and kivy having learnt python from codeacademy about a week ago and kivy from youtube tutorials.
Could someone please explain to me why the code below does not result in a screen with a label displaying n, which is incremented by the button?
Python file
import kivy
from kivy.app import App
from kivy.uix.button import Button, Label
from kivy.uix.boxlayout import BoxLayout
from kivy.properties import NumericProperty
class Example(BoxLayout):
n = 0
def n_plus(self):
self.n += 1
class ExampleApp(App):
def build(self):
return Example()
example = ExampleApp()
example.run()
KV file
<Example>:
BoxLayout:
Label:
text: str(root.n)
Button:
text: "+1"
on_press: root.n_plus()
Then could you explain why making n = NumericProperty(0) makes this work?
I'd like to run some functions on n which don't seem to work on numeric properties.
Because when you use NumericProperty()
As the official document said:
It produces events such that when an attribute of your object changes,
all properties that reference that attribute are automatically
updated.
So, in short, it creates a binding relationship between your UI(.kv) and attribute of its class(.py)
But, actually, you can modify the UI by yourself without the help from the kivy framework. I changed your example as the following:
Add an id attribute to your widget
Access the id attribute by using self.ids.your_id_in_kv_file
But it's obviously not good, since now you need to update your UIby yourself everytime you want to update your UI. But with that XXXXProperty from kivy, you just need to change the value of that attribute, you don't need to worry about the UI at all.
Another disadvantage of the solution is that, when you need to change the UI, you need to change tons of code if you modify them all by yourself...
Here is the example:
import kivy
from kivy.app import App
from kivy.uix.label import Label
from kivy.lang.builder import Builder
from kivy.uix.boxlayout import BoxLayout
Builder.load_string("""
<Example>:
BoxLayout:
Label:
id: lbl
text: "0"
Button:
text: "+1"
on_press: root.n_plus()
""")
class Example(BoxLayout):
def n_plus(self):
value = self.ids.lbl.text
self.ids.lbl.text = str(int(value) + 1)
class ExampleApp(App):
def build(self):
return Example()
if __name__ == '__main__':
ExampleApp().run()

Kivy FileChooser doubleclick

Could somebody post a small working example of a kivy Filechooser with the following simple doubleclick function: doubleclicking on a file will print out the filename?
Here is an example of that.
from kivy.app import App
from kivy.uix.filechooser import FileChooserListView
from kivy.uix.boxlayout import BoxLayout
class MyFileChooser(FileChooserListView):
def on_submit(*args):
print(args[1][0])
class MyLayout(BoxLayout):
def __init__(self,**kwargs):
super(MyLayout,self).__init__(**kwargs)
# filter added. Since windows will throw error on sys files
self.fclv = MyFileChooser(filters= [lambda folder, filename: not filename.endswith('.sys')])
self.add_widget(self.fclv)
class MyApp(App):
def build(self):
return MyLayout()
MyApp().run()
I think it is simpler than that.
FileChooser has an argument dirselect. By default it is False making it single-click. If you change dirselect to True, it works as double-click.
For example, in kivy language
BoxLayout:
FileChooserIconView:
size_hint: (0.3, 0.4)
dirselect: True
For example, in python language
FileChooserListView(size_hint_x=0.3, size_hint_y=0.4, dirselect=True)
Hope it helps somebody

How to pass arguments to build() in kivy?

I have written a code with GridLayout which need to be added with buttons in Python file. So, the add_widget() mainpulation should be done in build(). I am getting errors and couldn't get it.Someone Please help me.
In short, instead of add_btn(), I need it in build() of MineApp class.
Thanks in advance.`
main.py
from kivy.app import App
from kivy.uix.gridlayout import GridLayout
from kivy.uix.widget import Widget
from kivy.uix.button import Button
from kivy.properties import ObjectProperty
from kivy.uix.boxlayout import BoxLayout
class MainLayout(BoxLayout):
def build(self):
pass
def add_btn(self,id):
for i in range(100):
id.add_widget(Button())
class MineApp(App):
def build(self):
return MainLayout()
if __name__ == '__main__':
MineApp().run()
mine.kv file:
<MainLayout>:
orientation:'vertical'
BoxLayout:
orientation:'horizontal'
height: '30px'
size_hint_y:None
TextInput:
id: tinput
text:'10'
Button:
text:'start'
on_press:root.add_btn(grid)
Label:
id:mylabel
text:'0'
GridLayout:
id: grid
cols:10
rows:10
It took some time but think I get what you're trying to say! You can get the same effect by passing the id parameter of Grid in the .py file like so...
class MainLayout(BoxLayout):
def build(self):
for i in range(100):
self.ids.grid.add_widget(Button())
Then, you can just take your start button and...
Button:
text:'start'
on_press:root.build()
This works because "self" in .py refers to the class while in the .kv a similar wording would be 'root' (while 'self' in .kv refers to the widget!) Is this what you kind of had in mind? Let me know! I tried it out myself and had no problem running it through :)
Also, it wasn't put explicitly here that to test the code you'd need to import Builder via:
from kivy.lang import Builder
and do...
Builder.load_file("mine.kv")
as your .py and .kv don't share the same name (which you'd still need to add a "#File name: main.py" to the .kv if they did match names! Other than that, it looks good!

Kivy: how to instantiate a dynamic classes in python

I'm having a hard time trying to figure out how to instantiate a dynamic class I created using kv lang on my python code, consider the following code:
My test.kv file looks like this:
<MyPopup#Popup>:
title:'hello'
size_hint:(1, .6)
GridLayout:
id:root_grid
cols:2
padding:['8dp', '4dp','8dp','4dp']
spacing:'8dp'
Label:
text:'some text here'
Button:
text:'Ok'
on_press:do_something()
<MyGrid>:
rows:1
Button:
text:'Show Popup'
on_press:root.pop.show()
Then in my test.py:
from kivy.app import App
from kivy.uix.gridlayout import GridLayout
from kivy.uix.floatlayout import FloatLayout
from kivy.uix.popup import Popup
from kivy.factory import Factory
class MyGrid(GridLayout):
pop = Factory.MyPopup()
pass
class Test(App):
def build(self):
return MyGrid()
if __name__=='__main__':
Test().run()
The I get the following error: raise FactoryException('Unkown class <%s>' % name) kivy.factory.FactoryException: Unkown class
Can somebody please explain me how to properly do it, what am I missing? If you need any more information please let me know. Thanks.
Your call to the Factory takes place before the kv file is loaded, therefore the class you want does not yet exist.
Unless there is some reason to need a class level attribute, set self.pop in the __init__ of MyGrid instead.
You could also just include a Python class declaration. I generally prefer to do this for anything that I want to interact with from python, though opinions vary.

Categories