bugfix
This commit is contained in:
parent
b4474b5e21
commit
78e07f8625
@ -1,3 +1,4 @@
|
|||||||
|
from kivy.logger import Logger
|
||||||
from kivy.uix.camera import Camera
|
from kivy.uix.camera import Camera
|
||||||
from kivy.properties import BooleanProperty, NumericProperty
|
from kivy.properties import BooleanProperty, NumericProperty
|
||||||
from kivy.uix.button import Button
|
from kivy.uix.button import Button
|
||||||
@ -61,10 +62,12 @@ class CustomCamera(Camera):
|
|||||||
|
|
||||||
class QrReader(Camera):
|
class QrReader(Camera):
|
||||||
def __init__(self, **kw):
|
def __init__(self, **kw):
|
||||||
|
Logger.info('QrReader:Initialed...........')
|
||||||
super(QrReader, self).__init__(**kw)
|
super(QrReader, self).__init__(**kw)
|
||||||
self.qr_reader = cv2.QRCodeDetector()
|
self.qr_reader = cv2.QRCodeDetector()
|
||||||
self.register_event_type('on_data')
|
self.register_event_type('on_data')
|
||||||
self.qr_result = None
|
self.qr_result = None
|
||||||
|
Logger.info('QrReader:Initialed')
|
||||||
|
|
||||||
def getValue(self):
|
def getValue(self):
|
||||||
return {
|
return {
|
||||||
|
@ -222,7 +222,7 @@ class MarkdownBody(VBox):
|
|||||||
|
|
||||||
def build_source(self,source_desc):
|
def build_source(self,source_desc):
|
||||||
w = MarkdownBody(md_obj=self.md_obj,
|
w = MarkdownBody(md_obj=self.md_obj,
|
||||||
csscls=md_obj.second_css, size_hint_y=None)
|
csscls=self.md_obj.second_css, size_hint_y=None)
|
||||||
w.show_mdtext(source_desc)
|
w.show_mdtext(source_desc)
|
||||||
self.add_widget(w)
|
self.add_widget(w)
|
||||||
w.resize()
|
w.resize()
|
||||||
@ -396,18 +396,18 @@ description file format
|
|||||||
first_css = StringProperty("default")
|
first_css = StringProperty("default")
|
||||||
second_css = StringProperty("default")
|
second_css = StringProperty("default")
|
||||||
def __init__(self, **kw):
|
def __init__(self, **kw):
|
||||||
ScrollView.__init__(self)
|
super(Markdown, self).__init__(**kw)
|
||||||
self.root_body = MarkdownBody(md_obj=self,
|
self.root_body = MarkdownBody(md_obj=self,
|
||||||
csscls=self.first_css,
|
csscls=self.first_css,
|
||||||
size_hint_y=None
|
size_hint_y=None
|
||||||
)
|
)
|
||||||
self.root_body.bind(minimum_height=self.root_body.setter('height'))
|
self.root_body.bind(minimum_height=self.root_body.setter('height'))
|
||||||
self.add_widget(self.root_body)
|
self.add_widget(self.root_body)
|
||||||
|
self.source = kw.get('source')
|
||||||
if self.source:
|
if self.source:
|
||||||
self.load_text()
|
self.load_text()
|
||||||
self.bind(size=self.root_body.resize)
|
self.bind(size=self.root_body.resize)
|
||||||
self.bind(source=self.load_text)
|
self.bind(source=self.load_text)
|
||||||
# Clock.schedule_interval(self.check_parent_window, 1)
|
|
||||||
|
|
||||||
def check_parent_window(self, *args):
|
def check_parent_window(self, *args):
|
||||||
pw = self.get_parent_window()
|
pw = self.get_parent_window()
|
||||||
|
@ -300,14 +300,17 @@ sub-widget's description file format
|
|||||||
self.right_menu_showed = False
|
self.right_menu_showed = False
|
||||||
print('dismiss fired, right_menu_showed=',self.right_menu_showed)
|
print('dismiss fired, right_menu_showed=',self.right_menu_showed)
|
||||||
|
|
||||||
print('right fired')
|
print('right menu fired')
|
||||||
if len(self.sub_widgets) < 1:
|
if len(self.sub_widgets) < 1:
|
||||||
|
print('no sub_widgets')
|
||||||
return
|
return
|
||||||
if self.right_menu_showed:
|
if self.right_menu_showed:
|
||||||
|
print('right menu showed, not thing done')
|
||||||
return
|
return
|
||||||
|
|
||||||
w = self.sub_widgets[-1]
|
w = self.sub_widgets[-1]
|
||||||
if not hasattr(w, 'menu_widget'):
|
if not hasattr(w, 'menu_widget'):
|
||||||
|
print('this sub widget has not menu_widget')
|
||||||
return True
|
return True
|
||||||
mc = MenuContainer()
|
mc = MenuContainer()
|
||||||
mc.add_widget(w.menu_widget)
|
mc.add_widget(w.menu_widget)
|
||||||
|
@ -1,17 +1,20 @@
|
|||||||
import numpy as np
|
import numpy as np
|
||||||
import cv2
|
import cv2
|
||||||
from kivy.app import App
|
from kivy.app import App
|
||||||
|
from kivy.properties import BooleanProperty
|
||||||
from kivy.core.window import Window
|
from kivy.core.window import Window
|
||||||
from kivy.graphics.texture import Texture
|
from kivy.graphics.texture import Texture
|
||||||
from kivy.uix.image import Image
|
from kivy.uix.image import Image
|
||||||
from kivy.clock import Clock
|
from kivy.clock import Clock
|
||||||
|
|
||||||
class QRCodeReader(Image):
|
class QRCodeReader(Image):
|
||||||
|
opened = BooleanProperty(False)
|
||||||
def __init__(self, **kwargs):
|
def __init__(self, **kwargs):
|
||||||
super().__init__(**kwargs)
|
super().__init__(**kwargs)
|
||||||
self.register_event_type('on_data')
|
self.register_event_type('on_data')
|
||||||
self.opened = False
|
|
||||||
self.task = None
|
self.task = None
|
||||||
|
if self.opened:
|
||||||
|
self.open()
|
||||||
|
|
||||||
def open(self):
|
def open(self):
|
||||||
self.opened = True
|
self.opened = True
|
||||||
|
15
kivyblocks/xcamera/__init__.py
Normal file
15
kivyblocks/xcamera/__init__.py
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
"""
|
||||||
|
Exposes `XCamera` directly in `xcamera` rather than `xcamera.xcamera`.
|
||||||
|
Also note this may break `pip` since all imports within `xcamera.py` would be
|
||||||
|
required at setup time. This is because `version.py` (same directory) is used
|
||||||
|
by the `setup.py` file.
|
||||||
|
Hence we're not exposing `XCamera` if `pip` is detected.
|
||||||
|
"""
|
||||||
|
import os
|
||||||
|
|
||||||
|
project_dir = os.path.abspath(
|
||||||
|
os.path.join(__file__, os.pardir, os.pardir, os.pardir, os.pardir))
|
||||||
|
using_pip = os.path.basename(project_dir).startswith('pip-')
|
||||||
|
# only exposes `XCamera` if not within `pip` ongoing install
|
||||||
|
if not using_pip:
|
||||||
|
from .xcamera import XCamera # noqa
|
85
kivyblocks/xcamera/android_api.py
Normal file
85
kivyblocks/xcamera/android_api.py
Normal file
@ -0,0 +1,85 @@
|
|||||||
|
from kivy.logger import Logger
|
||||||
|
|
||||||
|
from jnius import JavaException, PythonJavaClass, autoclass, java_method
|
||||||
|
|
||||||
|
Camera = autoclass('android.hardware.Camera')
|
||||||
|
AndroidActivityInfo = autoclass('android.content.pm.ActivityInfo')
|
||||||
|
AndroidPythonActivity = autoclass('org.kivy.android.PythonActivity')
|
||||||
|
PORTRAIT = AndroidActivityInfo.SCREEN_ORIENTATION_PORTRAIT
|
||||||
|
LANDSCAPE = AndroidActivityInfo.SCREEN_ORIENTATION_LANDSCAPE
|
||||||
|
|
||||||
|
|
||||||
|
class ShutterCallback(PythonJavaClass):
|
||||||
|
__javainterfaces__ = ('android.hardware.Camera$ShutterCallback', )
|
||||||
|
|
||||||
|
@java_method('()V')
|
||||||
|
def onShutter(self):
|
||||||
|
# apparently, it is enough to have an empty shutter callback to play
|
||||||
|
# the standard shutter sound. If you pass None instead of shutter_cb
|
||||||
|
# below, the standard sound doesn't play O_o
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class PictureCallback(PythonJavaClass):
|
||||||
|
__javainterfaces__ = ('android.hardware.Camera$PictureCallback', )
|
||||||
|
|
||||||
|
def __init__(self, filename, on_success):
|
||||||
|
super(PictureCallback, self).__init__()
|
||||||
|
self.filename = filename
|
||||||
|
self.on_success = on_success
|
||||||
|
|
||||||
|
@java_method('([BLandroid/hardware/Camera;)V')
|
||||||
|
def onPictureTaken(self, data, camera):
|
||||||
|
s = data.tostring()
|
||||||
|
with open(self.filename, 'wb') as f:
|
||||||
|
f.write(s)
|
||||||
|
Logger.info('xcamera: picture saved to %s', self.filename)
|
||||||
|
camera.startPreview()
|
||||||
|
self.on_success(self.filename)
|
||||||
|
|
||||||
|
|
||||||
|
class AutoFocusCallback(PythonJavaClass):
|
||||||
|
__javainterfaces__ = ('android.hardware.Camera$AutoFocusCallback', )
|
||||||
|
|
||||||
|
def __init__(self, filename, on_success):
|
||||||
|
super(AutoFocusCallback, self).__init__()
|
||||||
|
self.filename = filename
|
||||||
|
self.on_success = on_success
|
||||||
|
|
||||||
|
@java_method('(ZLandroid/hardware/Camera;)V')
|
||||||
|
def onAutoFocus(self, success, camera):
|
||||||
|
if success:
|
||||||
|
Logger.info('xcamera: autofocus succeeded, taking picture...')
|
||||||
|
shutter_cb = ShutterCallback()
|
||||||
|
picture_cb = PictureCallback(self.filename, self.on_success)
|
||||||
|
camera.takePicture(shutter_cb, None, picture_cb)
|
||||||
|
else:
|
||||||
|
Logger.info('xcamera: autofocus failed')
|
||||||
|
|
||||||
|
|
||||||
|
def take_picture(camera_widget, filename, on_success):
|
||||||
|
# to call the android API, we need access to the underlying
|
||||||
|
# android.hardware.Camera instance. However, there is no official way to
|
||||||
|
# retrieve it from the camera widget, so we need to dig into internal
|
||||||
|
# attributes :-( This works at least on kivy 1.9.1, but it might break any
|
||||||
|
# time soon.
|
||||||
|
camera = camera_widget._camera._android_camera
|
||||||
|
params = camera.getParameters()
|
||||||
|
params.setFocusMode("auto")
|
||||||
|
camera.setParameters(params)
|
||||||
|
cb = AutoFocusCallback(filename, on_success)
|
||||||
|
Logger.info('xcamera: starting autofocus...')
|
||||||
|
try:
|
||||||
|
camera.autoFocus(cb)
|
||||||
|
except JavaException as e:
|
||||||
|
Logger.info('Error when calling autofocus: {}'.format(e))
|
||||||
|
|
||||||
|
|
||||||
|
def set_orientation(value):
|
||||||
|
previous = get_orientation()
|
||||||
|
AndroidPythonActivity.mActivity.setRequestedOrientation(value)
|
||||||
|
return previous
|
||||||
|
|
||||||
|
|
||||||
|
def get_orientation():
|
||||||
|
return AndroidPythonActivity.mActivity.getRequestedOrientation()
|
BIN
kivyblocks/xcamera/data/icons.ttf
Normal file
BIN
kivyblocks/xcamera/data/icons.ttf
Normal file
Binary file not shown.
BIN
kivyblocks/xcamera/data/shutter.wav
Normal file
BIN
kivyblocks/xcamera/data/shutter.wav
Normal file
Binary file not shown.
43
kivyblocks/xcamera/main.py
Normal file
43
kivyblocks/xcamera/main.py
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
from kivy.app import App
|
||||||
|
from kivy.lang import Builder
|
||||||
|
|
||||||
|
kv = """
|
||||||
|
#:import XCamera kivy_garden.xcamera.XCamera
|
||||||
|
|
||||||
|
FloatLayout:
|
||||||
|
orientation: 'vertical'
|
||||||
|
|
||||||
|
XCamera:
|
||||||
|
id: xcamera
|
||||||
|
on_picture_taken: app.picture_taken(*args)
|
||||||
|
|
||||||
|
BoxLayout:
|
||||||
|
orientation: 'horizontal'
|
||||||
|
size_hint: 1, None
|
||||||
|
height: sp(50)
|
||||||
|
|
||||||
|
Button:
|
||||||
|
text: 'Set landscape'
|
||||||
|
on_release: xcamera.force_landscape()
|
||||||
|
|
||||||
|
Button:
|
||||||
|
text: 'Restore orientation'
|
||||||
|
on_release: xcamera.restore_orientation()
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
class CameraApp(App):
|
||||||
|
def build(self):
|
||||||
|
return Builder.load_string(kv)
|
||||||
|
|
||||||
|
def picture_taken(self, obj, filename):
|
||||||
|
print('Picture taken and saved to {}'.format(filename))
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
CameraApp().run()
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
main()
|
37
kivyblocks/xcamera/platform_api.py
Normal file
37
kivyblocks/xcamera/platform_api.py
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
from kivy.utils import platform
|
||||||
|
|
||||||
|
|
||||||
|
def play_shutter():
|
||||||
|
# bah, apparently we need to delay the import of kivy.core.audio, lese
|
||||||
|
# kivy cannot find a camera provider, at lease on linux. Maybe a
|
||||||
|
# gstreamer/pygame issue?
|
||||||
|
from kivy.core.audio import SoundLoader
|
||||||
|
sound = SoundLoader.load("data/shutter.wav")
|
||||||
|
sound.play()
|
||||||
|
|
||||||
|
|
||||||
|
if platform == 'android':
|
||||||
|
from .android_api import (
|
||||||
|
LANDSCAPE, PORTRAIT, take_picture, set_orientation, get_orientation)
|
||||||
|
|
||||||
|
else:
|
||||||
|
|
||||||
|
# generic fallback for taking pictures. Probably not the best quality,
|
||||||
|
# they are meant mostly for testing
|
||||||
|
LANDSCAPE = 'landscape'
|
||||||
|
PORTRAIT = 'portrait'
|
||||||
|
|
||||||
|
def take_picture(camera_widget, filename, on_success):
|
||||||
|
camera_widget.texture.save(filename, flipped=False)
|
||||||
|
play_shutter()
|
||||||
|
on_success(filename)
|
||||||
|
|
||||||
|
def set_orientation(value):
|
||||||
|
previous = get_orientation()
|
||||||
|
print('FAKE orientation set to {}'.format(value))
|
||||||
|
get_orientation.value = value
|
||||||
|
return previous
|
||||||
|
|
||||||
|
def get_orientation():
|
||||||
|
return get_orientation.value
|
||||||
|
get_orientation.value = PORTRAIT
|
1
kivyblocks/xcamera/version.py
Normal file
1
kivyblocks/xcamera/version.py
Normal file
@ -0,0 +1 @@
|
|||||||
|
__version__ = '2020.0613'
|
41
kivyblocks/xcamera/xcamera.kv
Normal file
41
kivyblocks/xcamera/xcamera.kv
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
#:import xcamera kivy_garden.xcamera.xcamera
|
||||||
|
|
||||||
|
<XCameraIconButton>
|
||||||
|
icon_color: (0, 0, 0, 1)
|
||||||
|
_down_color: xcamera.darker(self.icon_color)
|
||||||
|
icon_size: dp(50)
|
||||||
|
|
||||||
|
canvas.before:
|
||||||
|
Color:
|
||||||
|
rgba: self.icon_color if self.state == 'normal' else self._down_color
|
||||||
|
Ellipse:
|
||||||
|
pos: self.pos
|
||||||
|
size: self.size
|
||||||
|
|
||||||
|
size_hint: None, None
|
||||||
|
size: self.icon_size, self.icon_size
|
||||||
|
font_size: self.icon_size/2
|
||||||
|
|
||||||
|
|
||||||
|
<XCamera>:
|
||||||
|
# \ue800 corresponds to the camera icon in the font
|
||||||
|
icon: u"[font=data/icons.ttf]\ue800[/font]"
|
||||||
|
icon_color: (0.13, 0.58, 0.95, 0.8)
|
||||||
|
icon_size: dp(70)
|
||||||
|
|
||||||
|
id: camera
|
||||||
|
resolution: 640, 480 # 1920, 1080
|
||||||
|
allow_stretch: True
|
||||||
|
|
||||||
|
# Shoot button
|
||||||
|
XCameraIconButton:
|
||||||
|
id: shoot_button
|
||||||
|
markup: True
|
||||||
|
text: root.icon
|
||||||
|
icon_color: root.icon_color
|
||||||
|
icon_size: root.icon_size
|
||||||
|
on_release: root.shoot()
|
||||||
|
|
||||||
|
# position
|
||||||
|
right: root.width - dp(10)
|
||||||
|
center_y: root.center_y
|
116
kivyblocks/xcamera/xcamera.py
Normal file
116
kivyblocks/xcamera/xcamera.py
Normal file
@ -0,0 +1,116 @@
|
|||||||
|
import datetime
|
||||||
|
import os
|
||||||
|
|
||||||
|
from kivy.clock import mainthread
|
||||||
|
from kivy.lang import Builder
|
||||||
|
from kivy.properties import ObjectProperty
|
||||||
|
from kivy.resources import resource_add_path
|
||||||
|
from kivy.uix.behaviors import ButtonBehavior
|
||||||
|
from kivy.uix.camera import Camera
|
||||||
|
from kivy.uix.label import Label
|
||||||
|
from kivy.utils import platform
|
||||||
|
|
||||||
|
from .platform_api import LANDSCAPE, set_orientation, take_picture
|
||||||
|
|
||||||
|
ROOT = os.path.dirname(os.path.abspath(__file__))
|
||||||
|
resource_add_path(ROOT)
|
||||||
|
|
||||||
|
|
||||||
|
def darker(color, factor=0.5):
|
||||||
|
r, g, b, a = color
|
||||||
|
r *= factor
|
||||||
|
g *= factor
|
||||||
|
b *= factor
|
||||||
|
return r, g, b, a
|
||||||
|
|
||||||
|
|
||||||
|
def get_filename():
|
||||||
|
return datetime.datetime.now().strftime('%Y-%m-%d %H.%M.%S.jpg')
|
||||||
|
|
||||||
|
|
||||||
|
def is_android():
|
||||||
|
return platform == 'android'
|
||||||
|
|
||||||
|
|
||||||
|
def check_camera_permission():
|
||||||
|
"""
|
||||||
|
Android runtime `CAMERA` permission check.
|
||||||
|
"""
|
||||||
|
if not is_android():
|
||||||
|
return True
|
||||||
|
from android.permissions import Permission, check_permission
|
||||||
|
permission = Permission.CAMERA
|
||||||
|
return check_permission(permission)
|
||||||
|
|
||||||
|
|
||||||
|
def check_request_camera_permission(callback=None):
|
||||||
|
"""
|
||||||
|
Android runtime `CAMERA` permission check & request.
|
||||||
|
"""
|
||||||
|
had_permission = check_camera_permission()
|
||||||
|
if not had_permission:
|
||||||
|
from android.permissions import Permission, request_permissions
|
||||||
|
permissions = [Permission.CAMERA]
|
||||||
|
request_permissions(permissions, callback)
|
||||||
|
return had_permission
|
||||||
|
|
||||||
|
|
||||||
|
class XCameraIconButton(ButtonBehavior, Label):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class XCamera(Camera):
|
||||||
|
directory = ObjectProperty(None)
|
||||||
|
_previous_orientation = None
|
||||||
|
__events__ = ('on_picture_taken', 'on_camera_ready')
|
||||||
|
|
||||||
|
def __init__(self, **kwargs):
|
||||||
|
Builder.load_file(os.path.join(ROOT, "xcamera.kv"))
|
||||||
|
super().__init__(**kwargs)
|
||||||
|
|
||||||
|
def _on_index(self, *largs):
|
||||||
|
"""
|
||||||
|
Overrides `kivy.uix.camera.Camera._on_index()` to make sure
|
||||||
|
`camera.open()` is not called unless Android `CAMERA` permission is
|
||||||
|
granted, refs #5.
|
||||||
|
"""
|
||||||
|
@mainthread
|
||||||
|
def on_permissions_callback(permissions, grant_results):
|
||||||
|
"""
|
||||||
|
On camera permission callback calls parent `_on_index()` method.
|
||||||
|
"""
|
||||||
|
if all(grant_results):
|
||||||
|
self._on_index_dispatch(*largs)
|
||||||
|
if check_request_camera_permission(callback=on_permissions_callback):
|
||||||
|
self._on_index_dispatch(*largs)
|
||||||
|
|
||||||
|
def _on_index_dispatch(self, *largs):
|
||||||
|
super()._on_index(*largs)
|
||||||
|
self.dispatch('on_camera_ready')
|
||||||
|
|
||||||
|
def on_picture_taken(self, filename):
|
||||||
|
"""
|
||||||
|
This event is fired every time a picture has been taken.
|
||||||
|
"""
|
||||||
|
pass
|
||||||
|
|
||||||
|
def on_camera_ready(self):
|
||||||
|
"""
|
||||||
|
Fired when the camera is ready.
|
||||||
|
"""
|
||||||
|
pass
|
||||||
|
|
||||||
|
def shoot(self):
|
||||||
|
def on_success(filename):
|
||||||
|
self.dispatch('on_picture_taken', filename)
|
||||||
|
filename = get_filename()
|
||||||
|
if self.directory:
|
||||||
|
filename = os.path.join(self.directory, filename)
|
||||||
|
take_picture(self, filename, on_success)
|
||||||
|
|
||||||
|
def force_landscape(self):
|
||||||
|
self._previous_orientation = set_orientation(LANDSCAPE)
|
||||||
|
|
||||||
|
def restore_orientation(self):
|
||||||
|
if self._previous_orientation is not None:
|
||||||
|
set_orientation(self._previous_orientation)
|
Loading…
Reference in New Issue
Block a user