This commit is contained in:
yumoqing 2021-04-23 14:53:33 +08:00
parent b4474b5e21
commit 78e07f8625
13 changed files with 353 additions and 6 deletions

View File

@ -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 {

View File

@ -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()

View File

@ -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)

View File

@ -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

View 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

View 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()

Binary file not shown.

Binary file not shown.

View 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()

View 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

View File

@ -0,0 +1 @@
__version__ = '2020.0613'

View 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

View 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)