i'm working on a qr code scanner using kivy zbarcam, I managed to make it work but the problem is the camera initializes right away and does not release the camera after leaving the screen. I tried zbarcam.stop() and root.ids.zbarcam_id.ids.xcamera.play=False but what it does is just unscheduling and doesn't really release the use of camera. I tried to build this in buildozer but it's so slow since the camera is using a lot of memory even when not in use.
Can somebody know what is the workaround on this?
This is my code:
from kivy.lang.builder import Builder
from kivy.uix.screenmanager import Screen
from kivy.clock import Clock
from kivymd.app import MDApp
class QRScreen(Screen):
pass
class LoginScreen(Screen):
pass
class ScannerScreen(Screen):
def __init__(self, **kwargs):
super().__init__(**kwargs)
Clock.schedule_once(self._after_init)
# self.ids.zbarcam_id.ids.xcamera.play=True
def _after_init(self, dt):
"""
Binds `ZBarCam.on_symbols()` event.
"""
zbarcam = self.ids.zbarcam_id
zbarcam.bind(symbols=self.on_symbols)
def on_symbols(self, zbarcam, symbols):
"""
Loads the first symbol data to the `QRFoundScreen.data_property`.
"""
# going from symbols found to no symbols found state would also
# trigger `on_symbols`
if not symbols:
return
symbol = symbols[0]
data = symbol.data.decode('utf8')
print(data)
self.manager.get_screen('qr').ids.data.text= data
self.manager.transition.direction = 'left'
self.manager.current = 'qr'
def on_leave(self):
zbarcam = self.ids.zbarcam_id
zbarcam.stop()
class DemoApp(MDApp):
def build(self):
# screen =Screen()
self.title='Demeter'
self.theme_cls.theme_style = "Dark"
self.theme_cls.primary_palette = "DeepPurple"
self.help = Builder.load_file('main.kv')
return self.help
DemoApp().run()
kv file:
#:import ZBarCam kivy_garden.zbarcam.ZBarCam
ScreenManager:
LoginScreen:
ScannerScreen:
QRScreen:
<LoginScreen>:
name: "login"
MDFlatButton:
text:'release'
on_press:
root.manager.current = 'scanner'
root.manager.transition.direction = "right"
<ScannerScreen>:
name: 'scanner'
ZBarCam:
id: zbarcam_id
play: False
MDFlatButton:
id: iyu
text:'release'
on_press:
root.ids.zbarcam_id.ids.xcamera.play=False
MDFlatButton:
id: ads
text:'replay'
pos_hint: {"center_x": 0.5, "center_y": 0.95}
on_press:
root.ids.zbarcam_id.ids.xcamera.play=True
<QRScreen>:
name: 'qr'
MDIconButton:
icon:'arrow-left'
pos_hint: {'center_x':0.1,'center_y':0.95}
on_press:
root.manager.current = 'scanner'
root.manager.transition.direction = "right"
MDLabel:
id:data
text: "asdgasda"
pos_hint: {'center_y':0.5}
theme_text_color: "Custom"
text_color: 0, 1, 0, 1
I was having the same problem and finally found the solution (based on this)
In my case I initate ZBarCam in the .py file instead of .kv (see example).
To actually close the camera, you can call the following:zbarcam.ids['xcamera']._camera._device.release() in your on_leave() function.
If you initiate/manage ZBarCam in the .kv, I think that the equivalent would be something like: root.ids.zbarcam.ids.xcamera._camera._device.release()
If compiled with buildzoner for Android, I have to use self.zbarcam.ids['xcamera']._camera = None instead
I have the same error, I already install garden, kivy-garden, kivy, kivymd, pyzbar, and all the requriments in the buildozer.spec file, but when I run it, the log said ModuleNotFoundError: No module named 'kivy_garden.zbarcam', I have being trying a lot of solutions in other requriments, but any of that works.
Related
I state that I am not an expert in Python and I started very recently with Kivy! I would like to know if it is possible, and if it makes sense, to add widgets such as buttons or labels while the app is running. For example a button that each time it is pressed adds a new button to a screen. I don't know if I've been clear enough.
This example illustrates the process by creating a new Button each time another Button is pressed, you can also delete (remove) the created buttons.
from kivy.app import App
from kivy.lang import Builder
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.button import Button
class CustomBox(BoxLayout):
def add_buttons(self, *args):
"""Method for creating new Button."""
i = len(self.ids.inner_box.children) # Just to distinguish the buttons from one another.
btn = Button(
text = f"Button {i+1}",
size_hint_y = None,
height = "64dp",
)
self.ids.inner_box.add_widget(btn) # Referencing container by its 'id'.
Builder.load_string("""
<CustomBox>:
orientation: "vertical"
spacing: dp(2)
Button:
size_hint_y: 0.5
text: "Add Button"
color: 0, 1, 0, 1
on_release: root.add_buttons()
ScrollView: # To see and add all the buttons in progress.
BoxLayout: # Button's container.
id: inner_box
orientation: "vertical"
spacing: dp(5)
padding: dp(5)
size_hint_y: None # Grow vertically.
height: self.minimum_height # Take as much height as needed.
Button:
size_hint_y: 0.5
text: "Delete Button"
color: 1, 0, 0, 1
on_release: inner_box.remove_widget(inner_box.children[0]) if inner_box.children else None # Here '0' means last added widget.
""")
class MainApp(App):
def build(self):
return CustomBox()
if __name__ == '__main__':
MainApp().run()
from kivy.app import App
from kivy.uix.button import Button
from kivy.uix.boxlayout import BoxLayout
class BeginnerApp(App):
def build(self):
root = BoxLayout(orientation='vertical')
self.count = 0
a = root.add_widget(Button(text='add button', on_press=self.add_button))
b = root.add_widget(Button(text='remove button', on_press=self.delete_button))
return root
def add_button(self, *args):
self.count += 1
self.root.add_widget(Button(text=str(self.count)))
def delete_button(self, *args):
if self.count > 0:
self.root.remove_widget(self.root.children[0])
self.count -= 1
else:
pass
if __name__ == '__main__':
app = BeginnerApp()
app.run()
Note 1: when you bind a method, specify its name without brackets. Otherwise, binding does not work normally.
on_press=self.add_button
on_press=self.delete_button
Note 2: to add widgets in layout you can use method "add_widget()".
self.root.add_widget(Button())
For delete widget you can use method "remove_widget()". To delete a widget in layout, you need to specify this widget. This can be done through the "children" method. The last widget in the "children" method is numbered "0". So you can delete the last widget in layout.
self.root.remove_widget(self.root.children[0])
Note 3: When declaring methods, don't forget *args.
def add_button(self, *args):
Every android app I build on kivy with buildozer restarts itself after being in the background. Even if I slide down the notifications when on the app the app restarts.
I tried finding documentation about this issue but didn't find anything.
from kivy.app import App
from kivy.lang import Builder
from kivy.uix.boxlayout import BoxLayout
import time
Builder.load_string('''
<CameraClick>:
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()
''')
class CameraClick(BoxLayout):
def capture(self):
'''
Function to capture the images and give them the names
according to their captured time and date.
'''
camera = self.ids['camera']
timestr = time.strftime("%Y%m%d_%H%M%S")
camera.export_to_png("IMG_{}.png".format(timestr))
print("Captured")
class TestCamera(App):
def build(self):
return CameraClick()
TestCamera().run()
This is the app I'm trying to run, with a default buildozer.spec only adding opencv for the camera.
from kivy.app import App
from kivy.lang import Builder
from kivy.uix.boxlayout import BoxLayout
import time
Builder.load_string('''
<CameraClick>:
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()
''')
class CameraClick(BoxLayout):
def capture(self):
'''
Function to capture the images and give them the names
according to their captured time and date.
'''
camera = self.ids['camera']
timestr = time.strftime("%Y%m%d_%H%M%S")
camera.export_to_png("IMG_{}.png".format(timestr))
print("Captured")
class TestCamera(App):
def build(self):
return CameraClick()
def on_pause(self):
return True
TestCamera().run()
Changing the on_pause method solves it.
Framework: I am talking about an Android app written in Python 2.7 and packaged with Builtdozer
I have a Builder inside the app with a button
Builder.load_string('''
FloatLayout:
orientation: 'horizontal'
Button:
text: str(root.name)
font_size: '20sp'
pos_hint: {'x':.0, 'y':.3}
size_hint: .4, .8
''')
I want to create a function, change_name, that, if I press the above button, opens the android keyboard to accept an user raw_input
The raw_input provided by the user has to replace the text of the above button.
What I thought is:
1) Create a variable name = StringProperty('Me')
2) Create a function:
def change_name(self):
self.name = raw_input()
3) Call the function inside my button with a on_release
Builder.load_string('''
FloatLayout:
orientation: 'horizontal'
Button:
text: str(root.name)
font_size: '20sp'
pos_hint: {'x':.0, 'y':.3}
size_hint: .4, .8
on_release: root.change_name()
''')
It is correct? Because actually, running the app on Ubuntu, I am trying to click on the button but the app does not ask for an input (it seems blocked).
As a consequence I believe it will not work also on Android.
Could you please help me understanding where am I wrong?
raw_input allows to get input from stdin (terminal). On Android you will not have the terminal available. In addition, raw_input is blocking, this causes the main event loop of your app to be freeze and will cause your app to stop responding.
You shouldn't use raw_input but Kivy's own methods.
On the other hand, you want to make your button editable (as if it were a TextInput). You can create your own custom Button class or use WindowBase.request_keyboard() to request the keyboard manually. However, you can do a little trick by hiding a TextInput and use it to enter the text:
from kivy.app import App
from kivy.lang import Builder
from kivy.uix.floatlayout import FloatLayout
from kivy.uix.textinput import TextInput
kv_text= ('''
<MyWidget>:
FloatLayout:
orientation: 'horizontal'
Button:
text: 'Hello'
font_size: '20sp'
pos_hint: {'x':.0, 'y':.3}
size_hint: .4, .8
on_release: root.change_name(self)
Button:
text: 'World'
font_size: '20sp'
pos_hint: {'x':0.6, 'y':.3}
size_hint: .4, 0.8
on_release: root.change_name(self)
''')
class MyWidget(FloatLayout):
def __init__(self, **kwargs):
super(MyWidget, self).__init__(**kwargs)
self.hide_input = TextInput(size_hint=(None, None),
size = (0, 0),
multiline = False)
self.hide_input_bind = None
def change_name(self, instance):
if self.hide_input_bind:
self.hide_input.unbind_uid('text', self.hide_input_bind)
self.hide_input.text = instance.text
self.hide_input.focus = True
self.hide_input_bind = self.hide_input.fbind('text', self._update_text, instance)
def _update_text(self, button, instance, value):
button.text = value
class MyKivyApp(App):
def build(self):
return MyWidget()
def main():
Builder.load_string(kv_text)
app = MyKivyApp()
app.run()
if __name__ == '__main__':
main()
App working on Android (Kivy Launcher):
im still a begginer with kivy, when i launch my kivy app on windows i get a black winodw and the window fits the whole laptop screen. how can i fix this and how can i resize the window?
my python code
# File name FaceRecognition.py
import kivy
from kivy.app import App
from kivy.uix.floatlayout import FloatLayout
from kivy.uix.screenmanager import ScreenManager, Screen
from kivy.uix.button import Button
from kivy.uix.widget import Widget
from kivy.uix.label import Label
from kivy.lang import Builder
# Define each screen
class ScreenOne(Screen):
pass
class ScreenTwo(Screen):
pass
class ScreenManagement(ScreenManager):
pass
Builder.load_file("facerecognition.kv")
class FaceRecognitionApp(App):
def build(self):
return ScreenManagement()
if __name__=="__main__":
FaceRecognitionApp().run()
my kv file
# File name FaceRecognition.kv
#: kivy 1.9.1
ScreenManagement:
id: screen_management
ScreenOne:
ScreenTwo:
<MyButton#Button>:
color: .8,.9,0,1 # yellow color
font_size: 32 # font size
size_hint: .2,.1
<ScreenOne>:
name: "screen1"
id: screen_one
FloatLayout:
Label:
text:"Hello\n Welcome to my App\n"
font_size:40
color: 0,0,0,1
MyButton:
text: 'Next'
pos_hint:{'right':1, 'y':0}
on_press:root.manager.current="screen2"
<ScreenOne>:
name: "screen2"
id: screen_two
FloatLayout:
Label:
text:"Please insert your Name\n Please insert your Password\n"
font_size:40
color: 0,0,0,1
MyButton:
text: 'Next'
pos_hint:{'right':1, 'y':0}
on_press:root.manager.current="screen1"
first of all you are defining <ScreenOne> twice in your kv file, second one should be <ScreenTwo>
you are defining a root widget by returning it in your build method. you are also defining it in your kv file by not putting <> around it. You need to get rid of one definition. I just put it in <> in the kv file.
your labels color is black, hence black on black, you won't see anything. I changed it to white color: 1,1,1,1
the window does not fill up the whole screen on my Laptop. I think this is not a code issue.
Here is the kv file with all the changes I mentioned in the list. It works on my pc.
# File name FaceRecognition.kv
#: kivy 1.9.1
<ScreenManagement>:
id: screen_management
ScreenOne:
ScreenTwo:
<MyButton#Button>:
color: .8,.9,0,1 # yellow color
font_size: 32 # font size
size_hint: .2,.1
<ScreenOne>:
name: "screen1"
id: screen_one
FloatLayout:
Label:
text:"Hello\n Welcome to my App\n"
font_size:40
color: 1,1,1,1
MyButton:
text: 'Next'
pos_hint:{'right':1, 'y':0}
on_press:root.manager.current="screen2"
<ScreenTwo>:
name: "screen2"
id: screen_two
FloatLayout:
Label:
text:"Please insert your Name\n Please insert your Password\n"
font_size:40
color: 1,1,1,1
MyButton:
text: 'Next'
pos_hint:{'right':1, 'y':0}
on_press:root.manager.current="screen1"
I have created a two button code for a simple Kivy app, but on clicking the button, it is unable to run a method.
When I click on 'Check in' button, it says AttributeError: 'Button' object has no attribute 'login'
Here is my code. I want to print a message on clicking button.
from kivy.app import App
from kivy.lang import Builder
button1 = '''
BoxLayout:
Button:
text: 'Check In'
height: "40dp"
on_press: login()
size_hint: None, None
pos_hint: {'center_x': .1, 'center_y': .1}
canvas.before:
PushMatrix
Rotate:
angle: 0
origin: self.center
canvas.after:
PopMatrix
Button:
text: 'SOS'
size_hint: None, None
pos_hint: {'center_x': .1, 'center_y': .1}
canvas.before:
PushMatrix
Rotate:
angle: 0
origin: self.center
canvas.after:
PopMatrix
'''
class RotationApp(App):
def build(self):
return Builder.load_string(button1)
def login(self):
print("Click the goddamn button")
RotationApp().run()
The button doesn't have a reference to the login method - as far as it's concerned that's just some random name that could have come from anywhere.
Since login is a method of your app class, you need a reference to the running app, which luckily kv language already provides with the app keyword. That means you should replace login with app.login.
You can also get a reference to the app in pure python via App.get_running_app(), which is in fact what the app keyword does behind the scenes.