Not set focus when come back screen manager with kivy and python - python

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

Related

Kivy: Previous screen still visible behind current (next) screen

I am making a simple GUI with kivy. made twi screens but facing a problem.
PROBLEM: Previous is still visible behind the current screen.
check screenshots:
First Screen..
enter image description here
second screen where first screen is still visible..
enter image description here
main.py code
from kivy.app import App
from kivy.lang import Builder
from kivy.uix.screenmanager import ScreenManager, Screen
Builder.load_file("design.kv")
class LoginScreen(Screen):
def sign_up(self):
self.manager.current = "signup_screen"
class SignUpScreen(LoginScreen):
pass
class RootWidget(ScreenManager):
pass
class MainApp(App):
def build(self):
return RootWidget()
if __name__ == '__main__':
MainApp().run()
Here, design.kv
<LoginScreen>:
GridLayout:
cols:1
GridLayout:
cols:1
Label:
text: "User Login"
TextInput:
hint_text:"Username"
TextInput:
hint_text:"Password"
Button:
text:"Login"
GridLayout:
cols:2
Button:
text:"Forgot Password?"
Button:
text:"Sign up"
on_press:root.sign_up()
<SignUpScreen>:
GridLayout:
cols:1
Label:
text:"Welcome to Another Dimension, Bitch!"
TextInput:
hint_text:"New Username"
TextInput:
hint_text:"New Password"
GridLayout:
cols:2
Button:
text:"Submit"
Button:
text:"Cancel"
<RootWidget>:
LoginScreen:
name:"login_screen"
SignUpScreen:
name:"signup_screen"
The problem here is that you inherited your SignUpScreen from your LoginScreen, which is why the widgets of the login screen is still in the signup one. The simple solution here is to instead inherit your SignUpScreen from the Screen class:
class SignUpScreen(Screen):
pass

Kivy On pressing a Button, a New Button is created which takes to Second Screen

I have a code where there are two Screens: "Main" & "Second", when you press a button in the Main Screen a new button is created in the same screen. I need the new button created to take me to the Second Screen. Please Help as I need it for a bigger project soon.
Normal Code:
from kivy.app import App
from kivy.uix.button import Button
from kivy.uix.label import Label
from kivy.uix.gridlayout import GridLayout
from kivy.lang import Builder
from kivy.uix.widget import Widget
from kivy.uix.screenmanager import Screen,ScreenManager
class Main(Screen):
pass
class Second(Screen):
pass
class Manager(ScreenManager):
pass
kv=Builder.load_file("test1.kv")
movie=Manager()
movie.add_widget(Main(name="main"))
movie.add_widget(Second(name="second"))
class Movie(App):
def build(self):
return movie
Movie().run()
Kv Code:
#: import Button kivy.uix.button.Button
<Main>:
name: "main"
GridLayout:
id: GL
cols: 1
size_hint: (.5,.5)
pos_hint: {"center_x":.5,"center_y":.5}
Button:
text: "Press Me"
on_press:
Btn=Button(text="Second",size_hint=(.5,.5),pos_hint= {"center_x":.5,"center_y":.5})
GL.add_widget(Btn)
#Btn.bind(on_press= ????)
<Second>:
name: "second"
Button:
text: "Go Back!"
size_hint: (.5,.5)
pos_hint: {"center_x":.5,"center_y":.5}
on_press:
app.root.current="main"
The simplest way is to just define a method in the Main class that does the Screen change:
class Main(Screen):
def switch(self, button_instance):
self.manager.current = 'second'
Then reference that method in the kv:
Button:
text: "Press Me"
on_press:
Btn=Button(text="Second",size_hint=(.5,.5),pos_hint= {"center_x":.5,"center_y":.5}, on_press=root.switch)
GL.add_widget(Btn)
A more complex approach that doesn't require any additional methods works by using the python setattr() method to set the current Screen of the ScreenManager:
Button:
text: "Press Me"
on_press:
Btn=Button(text="Second",size_hint=(.5,.5),pos_hint= {"center_x":.5,"center_y":.5})
Btn.bind(on_press=lambda ins: partial(setattr, root.manager, 'current', 'second')())
GL.add_widget(Btn)
The lambda is required to avoid passing the Button instance to the setattr() method. Also, an import is required for partial in the kv:
#: import partial functools.partial

How to correctly remove a widget in Kivy if not added dynamically

I see there are a lot of answers for a case similar to mine: if I add a widget in code with the ".add_widget()" function, I can later remove this widget with the ".remove_widget()" function.
My case is a little bit different: I'm trying to remove a button in a Screen that is added in the configuration file (this has not a separated kv file because I'm still doing some research to well understand the mechanics under Kivy).
I'm trying to remove the button with id "setBtn" inside the "MenuScreen" screen.
I tried several ways to do this, but no one worked for me.
I'm not even sure that there is a solution to remove a widget that has not been dynamically added to a parent widget...
I leave you my code, if anyone could help me I'd be very grateful.
Thank you.
from kivy.app import App
from kivy.lang import Builder
from kivy.uix.screenmanager import ScreenManager, Screen
# Create both screens. Please note the root.manager.current: this is how
# you can control the ScreenManager from kv. Each screen has by default a
# property manager that gives you the instance of the ScreenManager used.
Builder.load_string("""
<MenuScreen>:
BoxLayout:
Button:
id: 'setBtn'
text: 'Goto settings'
on_press: root.manager.current = 'settings'
Button:
text: 'Quit'
on_press: root.remove_settings()
<SettingsScreen>:
BoxLayout:
Button:
text: 'My settings button'
Button:
text: 'Back to menu'
on_press: root.manager.current = 'menu'
""")
# Declare both screens
class MenuScreen(Screen):
def remove_settings(self):
sm.remove_widget(settings)
self.remove_widget(self.ids.setBtn)
print 'Screen "settings" removed succesfully'
class SettingsScreen(Screen):
pass
# Create the screen manager
sm = ScreenManager()
menu = MenuScreen(name='menu')
settings=SettingsScreen(name='settings')
sm.add_widget(menu)
sm.add_widget(settings)
class TestApp(App):
def build(self):
return sm
def exit(self):
self.exit()
if __name__ == '__main__':
TestApp().run()
You have 2 errors:
The ids are not strings.
If you want to remove a widget you must use it through your parent, in your case if you want to remove the "setBtn" then you must use the BoxLayout.
Considering the above, the solution is:
# ...
Builder.load_string(
"""
&ltMenuScreen>:
BoxLayout:
id: box_layout
Button:
id: setBtn
text: 'Goto settings'
on_press: root.manager.current = 'settings'
Button:
text: 'Quit'
on_press: root.remove_settings()
&ltSettingsScreen>:
BoxLayout:
Button:
text: 'My settings button'
Button:
text: 'Back to menu'
on_press: root.manager.current = 'menu'
"""
)
# Declare both screens
class MenuScreen(Screen):
def remove_settings(self):
self.ids.box_layout.remove_widget(self.ids.setBtn)
print('Screen "settings" removed succesfully')
# ...

Python & Kivy: Camera and Changing Screen

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

.kv file doesn't seem to work

So I'm just working with some example Kivy file code, and I came across this code which allows the user to switch between screens:
from kivy.app import App
from kivy.lang import Builder
from kivy.uix.screenmanager import ScreenManager, Screen
from kivy.lang import Builder
from kivy.uix.screenmanager import ScreenManager, Screen
Builder.load_string("""
<MenuScreen>:
BoxLayout:
Button:
text: 'Build Scenario'
on_press: root.manager.current = 'settings'
Button:
text: 'Run Existing Scenerio'
<SettingsScreen>:
BoxLayout:
Button:
text: 'Run Existing Scenerio'
Button:
text: 'Back to menu'
on_press: root.manager.current = 'menu'
""")
# Declare both screens
class MenuScreen(Screen):
pass
class SettingsScreen(Screen):
pass
# Create the screen manager
sm = ScreenManager()
sm.add_widget(MenuScreen(name='menu'))
sm.add_widget(SettingsScreen(name='settings'))
class TestApp(App):
def build(self):
return sm
if __name__ == '__main__':
TestApp().run()
I wondered to myself if it would be possible to put the code given in the Builder.load_string() method into a separate .kv file. So I did just that. I commented the Builder part out (I admit I don't know what its role is) and copied the string into a .kv file which looks like this:
# the file name is test.kv
#:kivy 1.0.9
<MenuScreen>:
BoxLayout:
Button:
text: 'Build Scenario'
on_press: root.manager.current = 'settings'
Button:
text: 'Run Existing Scenerio'
<SettingsScreen>:
BoxLayout:
Button:
text: 'Run Existing Scenerio'
Button:
text: 'Back to menu'
on_press: root.manager.current = 'menu'
Unfortunately when I run the code now I just get a black screen. Can anyone tell me what's wrong? Thanks!
The code creates the screenmanager (sm) in the main body of the python file. When the kv is loaded from a file this only happens later, so none of the kv rules are applied to sm. It was okay before because the load_string happens before it is instantiated.
For this reason, instantiating widgets this way is bad practice, and the sm = ScreenManager(... etc should be moved to the build method. This is run after the kv file is loaded, so everything should work.

Categories

Resources