I'm making a simple basic facts maths app in Kivy. I'm using ScreenManager to switch between different screens. When I switch between my screen, I'm getting an annoying visual glitch with my labels at the top of each page.
I've tried setting IDs to each label but that didn't do anything, I'm pretty new to kivy so have no idea what I'm doing.
The Program is in 2 files:
main.kv
Manager:
MainMenu:
GameMode:
DifficultyScreen:
Game:
<MainMenu>:
name: "menu"
GridLayout:
rows: 2
Label:
text: "More On Maths"
id: home_title
Button:
text: "Play"
on_release:
app.root.current = "gamemode_screen"
<GameMode>:
name: "gamemode_screen"
GridLayout:
rows: 5
Label:
text: "Select Game Mode"
id: gamemode_select
Button:
text: "Addition"
on_press: root.btn_add()
on_release:
app.root.current = "difficulty_screen"
main.py
import kivy
from kivy.app import App
from kivy.uix.screenmanager import ScreenManager, Screen
class MainMenu(Screen):
pass
class GameMode(Screen):
pass
class DifficultyScreen(Screen):
pass
class Game(Screen):
pass
class Manager(ScreenManager):
pass
class Main(App):
def build(self):
return
if __name__ == "__main__":
Main().run()
Anyone encountered this before? Any idea how to fix it?
Thanks
Related
I have an issue with an kv file and can't solve it. In the app, I use different screens and every screen should have the same menu bar. The app is provided for Android, but in the future I want to use it on Windows too. For Android, the menu bar should be at the bottom of the screen. For Windows, I want to place the menu bar at the top of the screens and there is my issue: the menu bar at the top doesn't work.
I've created a simple example code, to reproduce the issue.
Here is the a simple Python code. There is only one function to switch the screens:
import kivy
kivy.require('1.10.1')
from kivy.app import App
from kivy.uix.button import Button
from kivy.uix.screenmanager import ScreenManager, Screen
class MenuBar( Button ):
pass
class Screen1( Screen ):
pass
class Screen2( Screen ):
pass
class ScreenManager( ScreenManager ):
pass
class TestscreenApp( App ):
def switch_screen( self, screen ):
self.root.current = screen
if __name__ == '__main__':
TestscreenApp().run()
And here is the kv file. There are 2 screens and a menu bar. I want to reuse the menu bar on different screens.
As you can see, the menu bar on the first screen is on the bottom of the screen. On the second screen, I placed the menu bar at the top and nothing will be displayed :-(
If I place the menu bar on the second screen at the bottom, it works. If I place the menu bar on the first screen at the top, this also display me nothing.
#:import FadeTransition kivy.uix.screenmanager.FadeTransition
#:import SlideTransition kivy.uix.screenmanager.SlideTransition
ScreenManager:
id: screen_manager
Screen1:
name: "screen_1"
screen_manager: "screen_manager"
Screen2:
name: "screen_2"
screen_manager: "screen_manager"
<Screen1>:
name: "screen_1"
GridLayout:
rows: 2
GridLayout:
cols: 2
Button:
text: 'Test 1'
font_size: 50
Button:
text: 'Test 2'
font_size: 50
MenuBar:
<Screen2>:
name: "screen_2"
GridLayout:
rows: 2
MenuBar:
Button:
text: 'Test 3'
font_size: 50
on_release:
app.switch_screen( "screen_1" )
<MenuBar>:
name: "menu_bar_top"
GridLayout:
cols: 2
size: root.width, root.height
Button:
text: 'Menu 1'
font_size: 20
on_release:
app.switch_screen( "screen_1" )
Button:
text: 'Menu 2'
font_size: 20
on_release:
app.switch_screen( "screen_2" )
I tried many things and used the search, but I can't find anything similar. I also tried to use BoxLayout instead of GridLayout, but I can't find a solution.
What I am doing wrong? Can anyone explain me this strange behavior?
The problem is that you are defining the MenuBar as extending Button, then using it as a widget container. A Button is not intended to be used as a container. A better choice would be one of the Layout classes. Try changing:
class MenuBar( Button ):
pass
to:
class MenuBar( RelativeLayout ):
pass
You made my day! That is a solution for my problem.
I did not think to change my Python code. All the time I've changed my kv file to fix the issue.
Thanks
I am trying to make an app out of different .py files. But I don't know how to add them together, I have one main file, and one login file with plans to add a lot more, but with these I'm experimenting right now. They are pretty basic for now until I figure out this "bonding" between them and then I will start adding some more complex stuff. I tried couple of things and they didn't work, but I left them in code for you to see (I tried to make the app to start with MainWindow, and on press of the first button it goes to login page*). Here's the code and please help me.
*Right now when I press the button it gives me this error: OSError: exception: access violation writing 0x0000000080006010
this is main.py:
from kivy.lang import Builder
from kivy.app import App
import login
from kivy.uix.screenmanager import Screen
kv = Builder.load_string('''
<MainWindow>:
GridLayout:
cols:1
GridLayout:
rows:5
Button:
text:"NOVA ROBA"
on_release:
root.call_login()
Button:
text:"KUPCI"
Button:
text:"PRODATO"
Button:
text: "AGRONOMI"
Button:
text: "STANJE U MAGACINU"
''')
class MainWindow(Screen):
def call_login(self):
login.app().run()
pass
class main_app(App):
def build(self):
return MainWindow()
if __name__ == '__main__':
main_app().run()
this is login.py:
from kivy.lang import Builder
from kivy.uix.screenmanager import Screen
from kivy.app import App
class Login(Screen, App):
def build(self):
return
pass
kv = Builder.load_string('''
<Login>:
name:"login"
GridLayout:
rows:2
GridLayout:
cols:2
Label:
text:"Password: "
TextInput:
id:passwd
multiline: False
Button:
text: "Submit"
on_release:
passwd.text = ""
''')
class app(App):
def build(self):
return Login()
if __name__ == "__main__":
app().run()
You are creating 2 apps, which is not needed. Instead of inheriting from both Screen and App in the Loginscreen, inherit only from Screen. Then create a ScreenManager in your main.py's build method and then add the imported loginscreen as a widget, to switch to the new screen, use self.manager.current = "login" in the call_login method of MainWindow
class app(App):
def build(self):
sm = ScreenManager()
sm.add_widget(MainWindow())
sm.add_widget(Login())
return sm
I am expecting the following kivy app to switch screens when I press and then release the button, but nothing happens and there's no error on the terminal. When I run the app, the GirisEkrani screen show up, and then when I press and release the button in GirisEkrani, the next screen (GirisEkrani2) should show up. Do you how to make that work?
from kivy.app import App
from kivy.lang import Builder
from kivy.uix.screenmanager import ScreenManager, Screen
Builder.load_string("""
<ekranApp>
GirisEkrani:
id: ge
Button:
text: "İleri"
on_release: ge.manager.current = ge.manager.next()
GirisEkrani2:
Button:
id: ge2
text: "Ileri 2"
on_release: ge2.manager.current = ge2.manager.next()
KontrolEkrani:
id: ke
Button:
text: "Geri"
on_release: ke.manager.current = ke.manager.previous()
""")
class GirisEkrani(Screen):
pass
class GirisEkrani2(Screen):
pass
class KontrolEkrani(Screen):
pass
class ekranApp(App, ScreenManager):
def build(self):
#root = ScreenManager()
#root.add_widget(GirisEkrani(name = "giris_ekrani"))
#root.add_widget(GirisEkrani2(name = "giris_ekrani2"))
#root.add_widget(KontrolEkrani(name = "kontrol_ekrani"))
return self
if __name__ == "__main__":
ekranApp().run()
While people seem to advocate the use of .kv files as opposed to using pure Python, I find it very frustrating to have no errors showing up when something doesn't work.
The build() method of an App is expected to return a Widget which will be the root of the Widget tree for your App, but yours returns the App itself. I suspect that will cause problems. However, your code is not working because you are not setting the names of the Screens. Here is a corrected version of your kv:
Builder.load_string("""
<ekranApp>:
GirisEkrani:
id: ge
name: "giris_ekrani"
Button:
text: "İleri"
on_release: ge.manager.current = ge.manager.next()
GirisEkrani2:
id: ge2
name: "giris_ekrani2"
Button:
text: "Ileri 2"
on_release: ge2.manager.current = ge2.manager.next()
KontrolEkrani:
id: ke
name: "kontrol_ekrani"
Button:
text: "Geri"
on_release: ke.manager.current = ke.manager.previous()
""")
Also, your id for GirisEkrani2 is actually set on the Button.
i have a simple test program:
main.py
from kivy.app import App
from kivy.uix.button import Button
from kivy.uix.screenmanager import Screen
from kivy.clock import mainthread
class TestScreen(Screen):
#mainthread
def on_pre_enter(self): #Is loaded before kv buttons etc? how make it work
pass
#mainthread
def on_enter(self): #Load after kv buttons etc?
button = Button(text="Work?")
#how now add it to display?
#how control where display it? on end or begin is just about on_pre and on?
class TestApp(App):
pass
if __name__ == '__main__':
TestApp().run()
And test.kv file
#:import NoTransition kivy.uix.screenmanager.NoTransition
<TestScreen>:
name:'try'
GridLayout:
id:'test'
cols:2
Button:
text:'Test'
on_press:app.root.current='Main'
ScreenManager:
transition: NoTransition()
Screen:
name: 'Main'
GridLayout:
cols:1
Button:
text:'1'
Button:
text:'2'
Button:
text:'Test'
on_press:root.current='try'
TestScreen:
Is simple to control kv and python widgets(but i dont know how but is more easy to writes widgets etc in kv file, but still need create some in python for automatic content) or better just create all in python withou kv file? I wanna make somehting like this: App with left menu always displayed and on the right side another screen with dynamic content based on screen(clicked from menu) is maybe another simple solution for this. Anyone can explain me step by step? :)
AttributeError
The solution to AttributeError, please replace "id: 'test'" with "id: test" in test.kv file.
Dynamic Content
It is possible to display screen with dynamic content based on clicked from menu. But remember to remove the widgets that were added when exiting the screen (TestScreen/SettingsScreen). If you do not remove the widgets, you will get duplicates/multiples of each widget added each time you enter the screen (TestScreen/SettingsScreen). I recommend using on_pre_enter and on_leave methods. Please refer to the example below for details.
Example
main.py
from kivy.app import App
from kivy.uix.screenmanager import ScreenManager, Screen
from kivy.uix.button import Button
class MyScreenManager(ScreenManager):
pass
class MenuScreen(Screen):
pass
class SettingsScreen(Screen):
def on_pre_enter(self, *args):
self.ids.test.add_widget(Button(text="Work?"))
def on_leave(self, *args):
self.ids.test.remove_widget(self.ids.test.children[0])
class TestApp(App):
title = "Add & Remove Widgets Dynamically"
def build(self):
return MyScreenManager()
if __name__ == "__main__":
TestApp().run()
test.kv
#:kivy 1.10.0
#:import NoTransition kivy.uix.screenmanager.NoTransition
<MyScreenManager>:
transition: NoTransition()
MenuScreen:
SettingsScreen:
<MenuScreen>:
name: 'menu'
GridLayout:
cols: 1
Button:
text: '1'
Button:
text: '2'
Button:
text: 'Test'
on_press: root.manager.current = 'settings'
<SettingsScreen>:
name:'settings'
GridLayout:
id: test
cols: 2
Button:
text: 'Test'
on_press: root.manager.current = 'menu'
Output
I have been fiddling with the Kivy language and have done some searching, but haven't found a solution that works for my situation. I assume I am missing a little fundamental thing here.
I am trying to call a function within a screen from a button press and I want to facilitate this with a kv file. I simplified the code and left out some formatting in kv file, such as layout and button size, etc.
The 'entered' variable is showing up on the screen as "Not Entered", but when I press the button, the label does not change and the function is not entered, nothing happens.
Python:
import kivy
kivy.require('1.9.1')
from kivy.app import App
from kivy.uix.screenmanager import Screenmanager, Screen
from kivy.properties import StringProperty
class ScreenManager(ScreenManager):
pass
class StartMenu(Screen):
pass
class MyScreen(Screen):
entered = StringProperty()
entered = "Not Entered"
def my_function(self, *args):
self.entered = "Entered"
class MyApp(App):
def build(self):
return ScreenManager()
if __name__ == "__main__":
MyApp().run()
Kivy: my.kv
#:kivy 1.9.1
<ScreenManager>:
StartMenu:
MyScreen:
<StartMenu>:
name: 'StartMenu'
Button:
on_release:
root.manager.current = 'MyScreen'
<MyScreen>:
name: 'MyScreen'
Label:
text: root.entered
Button:
on_release:
root.my_function()
Thanks for your time!
The problem is here:
class MyScreen(Screen):
entered = StringProperty()
entered = "Not Entered"
entered gets overwritten immediately (and is a standard class property, losing all the event magic). Instead initialize it as entered = StringProperty("Not Entered"), or in the kv file as
<MyScreen>:
entered: "Not Entered"
Btw, to make your example work, there should be some kind of layout:
#:kivy 1.9.1
<ScreenManager>:
StartMenu:
MyScreen:
<StartMenu>:
name: 'StartMenu'
Button:
on_release:
root.manager.current = 'MyScreen'
<MyScreen>:
entered:"Not Entered"
name: 'MyScreen'
GridLayout:
cols: 2
Label:
text: root.entered
Button:
on_release:
root.my_function()