I want to build an app with Kivy in Python but I got some errors that I tried to solve many times but I can't.
I want to open a camera screen firstly. In screen, we will see our webcam screen and there will 2 buttons at the bottom (Play and Capture). While I pressing Play, webcam will be on and if I press Capture button, I want to take snapshot. I built the working code until here.
After pressing Capture button and taking snapshot, I want to change the screen in order to go 2nd screen and it will just show a basic Hello World sentence in black background.
Here it is my code, where is my fault? Can you fix it?
from kivy.app import App
from kivy.lang import Builder
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.screenmanager import ScreenManager, Screen
import time
class CheckScreen(Screen):
pass
class ScreenManagement(ScreenManager):
pass
Builder.load_string("""
ScreenManagement:
CheckScreen:
<CameraClick#ScreenManager>:
orientation: 'vertical'
Camera:
id: camera
resolution: (640, 480)
play: False
ToggleButton:
text: 'Play'
on_press: camera.play = not camera.play
size_hint_y: None
height: '48dp'
Button:
text: 'Capture'
size_hint_y: None
height: '48dp'
on_press:
root.capture()
root.manager.transition.direction = 'left'
root.manager.current = 'check'
<CheckScreen>:
name: "check"
Button:
text: "Next Screen"
font_size: 50
""")
class CameraClick(BoxLayout):
def capture(self):
camera = self.ids['camera']
timestr = time.strftime("%Y%m%d_%H%M%S")
camera.export_to_png("IMG_{}.png".format(timestr))
print("Captured")
return CheckScreen
class TestCamera(App):
def build(self):
return CameraClick()
TestCamera().run()
When I'm running this code, it shows;
AttributeError: 'CameraClick' object has no attribute 'manager'
There were a couple problems with your code. As John Anderson mentioned in the comments, you had one class inheriting from different classes in your py/kv files. You also had 2 screen managers - you should only need 1. I believe your CameraClick class should really only inherit Screen from kivy, not ScreenManager or BoxLayout. You can define a layout inside of the CameraClick class itself.
Your error was coming from the fact that you were trying to get the 'manager' attribute of your CameraClick class which you hadn't defined anywhere. To reference a screen manager (which it looks like is what you were trying to do) you must give the screen manager an 'id' and reference it that way (as you have with your Camera).
Another good practice you should use when working with a ScreenManager is to give your screens and id and name when you're adding the screens to the ScreenManager widget. e.g. instead of
ScreenManagement:
CheckScreen:
Do
ScreenManagement:
CheckScreen:
name: "check_screen"
id: check_screen
Another good practice is to load your kv code into a variable, and return that in your main app's build method.
And so forth. I've changed your code a bit and I think it should do what you expect now. Take the time to review it and compare to yours so you can see the differences, and let me know if it's not what you're looking for.
from kivy.app import App
from kivy.lang import Builder
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.screenmanager import ScreenManager, Screen
import time
class CheckScreen(Screen):
pass
class CameraClickScreen(Screen):
def capture(self):
camera = self.ids['camera']
timestr = time.strftime("%Y%m%d_%H%M%S")
camera.export_to_png("IMG_{}.png".format(timestr))
GUI = Builder.load_string("""
GridLayout:
cols: 1
ScreenManager:
id: screen_manager
CameraClickScreen:
name: "camera_click_screen"
id: camera_click_screen
CheckScreen:
name: "check_screen"
id: check_screen
<CameraClickScreen>:
orientation: 'vertical'
GridLayout:
cols: 1
Camera:
id: camera
resolution: (640, 480)
play: False
ToggleButton:
text: 'Play'
on_press: camera.play = not camera.play
size_hint_y: None
height: '48dp'
Button:
text: 'Capture'
size_hint_y: None
height: '48dp'
on_press:
root.capture()
# root refers to <CameraClickScreen>
# app refers to TestCamera, app.root refers to the GridLayout: at the top
app.root.ids['screen_manager'].transition.direction = 'left'
app.root.ids['screen_manager'].current = 'check_screen'
<CheckScreen>:
Button:
text: "Next Screen"
font_size: 50
""")
class TestCamera(App):
def build(self):
return GUI
TestCamera().run()
Their is no problem with the code. To run the code on Android, you have to give it permission. Here is the code; you can add it on the top of your code:
from android.permissions import request_permissions, Permission
request_permissions([Permission.CAMERA,Permission.WRITE_EXTERNAL_STORAGE,Permission.READ_EXTERNAL_STORAGE])
NOTE: After importing the Android module, the code may give an “import error” while compiling on a desktop, but it will build and run successfully on Android.
Also, don't forget to change the code in the buildozer.spec file:
android.permissions = CAMERA, WRITE_EXTERNAL_STORAGE, READ_EXTERNAL_STORAGE
Related
I'm writing a python script with a kv script outside it for formatting.
I have 2 basic screens thus far. I can load 1 screen into a preview but things fall apart when I try 2 screens.
I get this error:
kivy.uix.screenmanager.ScreenManagerException: ScreenManager accepts only Screen widget.
Here's my python code:
from kivy.app import App
from kivy.lang import Builder
from kivy.uix.screenmanager import Screen, ScreenManager
Builder.load_file("design.kv")
class Intro(Screen):
def go_settings(self):
self.manager.current = "settings"
pass
class Settings(Screen):
print("In settings")
pass
class RootWidget(ScreenManager):
pass
class MainApp(App):
def build(self):
return RootWidget()
if __name__ == "__main__":
MainApp().run()
My kv code, titled "design.kv":
<Intro>:
GridLayout:
cols: 1
Label:
text: "Intro"
Button:
text: "Settings"
on_press: root.go_settings()
<Settings>:
GridLayout:
cols: 1
Label:
text: "Settings"
<RootWidget>:
Intro:
name: "intro"
Settings: #will work if I comment this line and below
name: "settings"
Not sure what's going on. Thanks for the help!
Alright, I think I got it.
I need to change the names of
Intro
and
Settings
to
IntroScreen
and
SettingsScreen
It seemed to work for me. Never thought the name I'd give would matter.
I made a kivy application where everything is done within this class :
class MyGame(Widget):
Consequently, my kv file look like this
<MyGame>:
GridLayout:
rows: 1
size: root.width, root.height
canvas.before:
Color:
rgba: 1, 1, 1, 1
Rectangle:
size: self.size
FloatLayout:
Button:
id: question
font_size: 20
size_hint: 0.77, 0.1
pos_hint: {"x": 0.09, "y":0.85}
on_press: root.start_round()
SmoothButton:
But now that my game is almost done I feel the need to include 2 screens:
One is for the game menu (to let the user select a game mode)
The other one is for the game itself
I have tried to include this in my main.py
from kivy.uix.screenmanager import ScreenManager, Screen
from kivy.lang import Builder
class MenuScreen(Screen):
pass
class GameScreen(Screen):
pass
class WindowManager(ScreenManager):
pass
kv = Builder.load_file('my.kv')
class MyGame(Widget):
...
and my class that inherits from App returns kv in build
Here is my kv file: WindowManager: MenuScreen: GameScreen:
<MenuScreen>:
name: 'Menu'
Button:
<GameScreen>:
name: 'Game'
<MyGame>:
As you can see I have tried to nest MyGame(Widget) within the GameScreen(Nest) but when I run my code, a screen opens but the screen is black
Does anyone know how I can solve my issue ? This is pretty much my first project in kivy so I have many basics things to learn yet. Thanks
<MenuScreen>:
name: 'Menu'
Button:
<GameScreen>:
name: 'Game'
<MyGame>:
This syntax is wrong, you don't want the <> around MyGame. I don't know if this is the cause of your issue.
I want set the focus in textinput. at the begining the focus set correctly but when i go to the next screen and come back to initial screen the focus dont set correctly.
This is a app with a rfid lector, I want to read a code and select enter or exit
main.py
import kivy
kivy.require('1.10.0') # replace with your current kivy version !
from kivy.app import App
from kivy.core.window import Window
from kivy.uix.screenmanager import ScreenManager, Screen
from kivy.lang import Builder
class MyScreenManager(ScreenManager):
def __init__(self,**kwargs):
super(MyScreenManager,self).__init__()
class Menu1(Screen):
def __init__(self, **kwargs):
super(Menu1, self).__init__()
class Menu2(Screen):
def __init__(self, **kwargs):
super(Menu2, self).__init__()
Builder.load_file("main.kv")
class Fichaje(App):
def build(self):
return MyScreenManager()
if __name__ == '__main__':
Fichaje().run()
main.kv
#:kivy 1.10.0
#:import WipeTransition kivy.uix.screenmanager.WipeTransition
<MyScreenManager>:
#transition: WipeTransition()
Menu1:
id: menu1
Menu2:
id: menu2
<Menu1>:
name: "screen1"
BoxLayout:
orientation: 'vertical'
TextInput:
id: input1
size_hint_y: .1
multiline: False
focus: True
on_text_validate:
root.manager.current = "screen2"
BoxLayout:
<Menu2>:
name: "screen2"
BoxLayout:
Button:
text:"Entrada"
on_press:
root.manager.current = "screen1"
Button:
text:"Salida"
on_press:
root.manager.current = "screen1"
No error messages but the focus is not in the right site,
Thanks
I change the code to simplify the error
In the example, there is not an attempt to change the focus. But I assume this was tried but it lost focus again.
The reason the text input looses focus again, is because it gets focused before the mouse or tap is released. The on_press method, is followed by on_release where the text input looses focus again.
To fix this you can just set the focus in the on_release method instead.
The quickest is to only add one line of code to the kv file and change on_press to on_release.
root.manager.get_screen("screen1").ids["input1"].focus
This line can be different by using object property in screen1, for example. Or if you cannot use the on_release method, maybe use clock to schedule a focus in some amount of time, and if the touch is still down, reschedule it.
But here is the quick fix.
<Menu2>:
name: "screen2"
BoxLayout:
Button:
text:"Entrada"
on_release:
root.manager.current = "screen1"
root.manager.get_screen("screen1").ids["input1"].focus = True
it is a simple program but i can not find a way to make it work. i just want to add a widget in boxlayout2 when the user presses the button (and has not writed anything in textinput) which is located in boxlayout1 .The widget do not display in screen.What should i do?
main.py
from kivy.app import App
from kivy.uix.button import Button
from kivy.uix.boxlayout import BoxLayout
class BoxLayout1(BoxLayout):
def Search(self):
if self.ids.textinput.text!='':
BoxLayout2()
class BoxLayout2(BoxLayout):
def Print(self):
self.add_widget(Button(text='hello'))
class TestApp(App):
pass
TestApp().run()
and here is my kivy code
test.kv
<BoxLayout1>:
BoxLayout:
Label:
text:'Hello'
TextInput:
id: textinput
Button:
text: 'write'
on_press: root.Search()
BoxLayout:
orientation: 'vertical'
BoxLayout1:
BoxLayout2:
I see the presentation layout i want but the button is nowhere to be found.
To make it clear let's follow the stream of the app you've written.
it creates a BoxLayout and puts BoxLayout1 and BoxLayout2 in it, the second one doesn't have any content. When you click on write, the app checks the content of the text box and if valid, calls the constructor of BoxLayout2! Now at this point it creates an instance of this class, but does not keep it's reference so it will be discarded immediately. Now what you want is to call a function of a currently existing instance, not to create another one. Here's the code:
python:
from kivy.app import App
from kivy.uix.button import Button
from kivy.uix.boxlayout import BoxLayout
from kivy.lang import Builder
class BoxLayout1(BoxLayout):
def Search(self):
if self.ids.textinput.text!='':
self.parent.ids.bxl2.addButton()
# BoxLayout2()
class BoxLayout2(BoxLayout):
def addButton(self):
button=Button(text='hello')
self.add_widget(button)
kivy language:
<BoxLayout1>:
BoxLayout:
Label:
text:'Hello'
TextInput:
id: textinput
Button:
text: 'write'
on_press: root.Search()
BoxLayout:
orientation: 'vertical'
BoxLayout1:
BoxLayout2:
id:bxl2
I am new to kivy and try to grasp the concept behind the kivy language. I try to have a button that changes its background picture on click. With my current code, I get no Errors, but the button doesn't do anything if I try to click it...
This is my code:
<ScatterTextWidget>:
orientation: 'vertical'
my_picture: 'picture.png'
Button:
id: b1
canvas.after:
Rectangle:
id: m_r
source: root.my_picture
pos: self.pos
size: self.size
on_release: root.nextPicture()
.py:
from kivy.app import App
from kivy.uix.button import Button
from kivy.uix.floatlayout import FloatLayout
class ScatterTextWidget(FloatLayout):
def nextPicture(self):
self.ids.my_picture = 'newPicture.png'
self.canvas.ask_update()
return
class GuiApp(App):
def build(self):
return ScatterTextWidget()
if __name__ == "__main__":
GuiApp().run()
What do I have to do to make my button show the new Picture?
Just change this line
self.ids.my_picture = 'newPicture.png'
to
self.my_picture = 'newPicture.png'
You are accessing a property not an id.