Piggy: call camera app using Intent, use same class

Also add fallback to class name.
This commit is contained in:
Thomas Farstrike
2025-06-03 11:34:10 +02:00
parent 3935b0018e
commit 8a0eac09b8
4 changed files with 230 additions and 567 deletions
@@ -1,306 +0,0 @@
# This code grabs images from the camera in RGB565 format (2 bytes per pixel)
# and sends that to the QR decoder if QR decoding is enabled.
# The QR decoder then converts the RGB565 to grayscale, as that's what quirc operates on.
# It would be slightly more efficient to capture the images from the camera in L8/grayscale format,
# or in YUV format and discarding the U and V planes, but then the image will be gray (not great UX)
# and the performance impact of converting RGB565 to grayscale is probably minimal anyway.
import lvgl as lv
import time
try:
import webcam
except Exception as e:
print(f"Info: could not import webcam module: {e}")
import mpos.apps
import mpos.ui
# screens:
main_screen = None
keepliveqrdecoding = False
width = 240
height = 240
cam = None
# Variable to hold the current memoryview to prevent garbage collection
current_cam_buffer = None
image_dsc = None
image = None
qr_label = None
scanqr_callback = None
use_webcam = False
qr_button = None
snap_button = None
capture_timer = None
status_label = None
status_label_cont = None
status_label_text = "No camera found."
status_label_text_searching = "Searching QR codes...\n\nHold still and make them big!\n10cm for simple QR codes,\n20cm for complex."
status_label_text_found = "Decoding QR..."
def print_qr_buffer(buffer):
try:
# Try to decode buffer as a UTF-8 string
result = buffer.decode('utf-8')
# Check if the string is printable (ASCII printable characters)
if all(32 <= ord(c) <= 126 for c in result):
return result
except Exception as e:
pass
# If not a valid string or not printable, convert to hex
hex_str = ' '.join([f'{b:02x}' for b in buffer])
return hex_str.lower()
# Byte-Order-Mark is added sometimes
def remove_bom(buffer):
bom = b'\xEF\xBB\xBF'
if buffer.startswith(bom):
return buffer[3:]
return buffer
def qrdecode_one():
global status_label, status_label_text, scanqr_callback
try:
import qrdecode
result = qrdecode.qrdecode_rgb565(current_cam_buffer, width, height)
#result = bytearray("INSERT_QR_HERE", "utf-8")
if not result:
status_label.set_text(status_label_text_searching)
else:
stop_qr_decoding()
result = remove_bom(result)
result = print_qr_buffer(result)
print(f"QR decoding found: {result}")
if scanqr_callback:
scanqr_callback(True,result)
mpos.ui.back_screen()
else:
status_label.set_text(result) # in the future, the status_label text should be copy-paste-able
except ValueError as e:
print("QR ValueError: ", e)
status_label.set_text(status_label_text_searching)
except TypeError as e:
print("QR TypeError: ", e)
status_label.set_text(status_label_text_found)
except Exception as e:
print("QR got other error: ", e)
def close_button_click(e):
print("Close button clicked")
mpos.ui.back_screen()
def snap_button_click(e):
print("Picture taken!")
import os
try:
os.mkdir("data")
except OSError:
pass
try:
os.mkdir("data/com.example.camtest")
except OSError:
pass
if current_cam_buffer is not None:
filename="data/com.example.camtest/capture.raw"
try:
with open(filename, 'wb') as f:
f.write(current_cam_buffer)
print(f"Successfully wrote current_cam_buffer to {filename}")
except OSError as e:
print(f"Error writing to file: {e}")
def start_qr_decoding():
global qr_label, keepliveqrdecoding, status_label_cont, status_label
print("Activating live QR decoding...")
keepliveqrdecoding = True
qr_label.set_text(lv.SYMBOL.EYE_CLOSE)
status_label_cont.remove_flag(lv.obj.FLAG.HIDDEN)
status_label.set_text(status_label_text_searching)
def stop_qr_decoding():
global qr_label, keepliveqrdecoding
print("Deactivating live QR decoding...")
keepliveqrdecoding = False
qr_label.set_text(lv.SYMBOL.EYE_OPEN)
status_label_text = status_label.get_text()
if status_label_text == status_label_text_searching or status_label_text == status_label_text_found: # if it found a QR code, then leave it
status_label_cont.add_flag(lv.obj.FLAG.HIDDEN)
def qr_button_click(e):
global keepliveqrdecoding
if not keepliveqrdecoding:
start_qr_decoding()
else:
stop_qr_decoding()
def try_capture(event):
#print("capturing camera frame")
global current_cam_buffer, image_dsc, image, use_webcam, cam
try:
if use_webcam:
current_cam_buffer = webcam.capture_frame(cam, "rgb565")
elif cam.frame_available():
current_cam_buffer = cam.capture()
if current_cam_buffer and len(current_cam_buffer):
image_dsc.data = current_cam_buffer
#image.invalidate() # does not work so do this:
image.set_src(image_dsc)
if not use_webcam:
cam.free_buffer() # Free the old buffer
if keepliveqrdecoding:
qrdecode_one()
except Exception as e:
print(f"Camera capture exception: {e}")
def build_ui():
global image, image_dsc,qr_label, status_label, cam, use_webcam, qr_button, snap_button, status_label_cont, main_screen
main_screen = lv.obj()
main_screen.set_style_pad_all(0, 0)
main_screen.set_style_border_width(0, 0)
main_screen.set_size(lv.pct(100), lv.pct(100))
main_screen.set_scrollbar_mode(lv.SCROLLBAR_MODE.OFF)
close_button = lv.button(main_screen)
close_button.set_size(60,60)
close_button.align(lv.ALIGN.TOP_RIGHT, 0, 0)
close_label = lv.label(close_button)
close_label.set_text(lv.SYMBOL.CLOSE)
close_label.center()
close_button.add_event_cb(close_button_click,lv.EVENT.CLICKED,None)
snap_button = lv.button(main_screen)
snap_button.set_size(60, 60)
snap_button.align(lv.ALIGN.RIGHT_MID, 0, 0)
snap_button.add_flag(lv.obj.FLAG.HIDDEN)
snap_label = lv.label(snap_button)
snap_label.set_text(lv.SYMBOL.OK)
snap_label.center()
snap_button.add_event_cb(snap_button_click,lv.EVENT.CLICKED,None)
qr_button = lv.button(main_screen)
qr_button.set_size(60, 60)
qr_button.add_flag(lv.obj.FLAG.HIDDEN)
qr_button.align(lv.ALIGN.BOTTOM_RIGHT, 0, 0)
qr_label = lv.label(qr_button)
qr_label.set_text(lv.SYMBOL.EYE_OPEN)
qr_label.center()
qr_button.add_event_cb(qr_button_click,lv.EVENT.CLICKED,None)
# Initialize LVGL image widget
image = lv.image(main_screen)
image.align(lv.ALIGN.LEFT_MID, 0, 0)
# Create image descriptor once
image_dsc = lv.image_dsc_t({
"header": {
"magic": lv.IMAGE_HEADER_MAGIC,
"w": width,
"h": height,
"stride": width * 2,
"cf": lv.COLOR_FORMAT.RGB565
#"cf": lv.COLOR_FORMAT.L8
},
'data_size': width * height * 2,
'data': None # Will be updated per frame
})
image.set_src(image_dsc)
status_label_cont = lv.obj(main_screen)
status_label_cont.set_size(lv.pct(66),lv.pct(60))
status_label_cont.align(lv.ALIGN.LEFT_MID, lv.pct(5), 0)
status_label_cont.set_style_bg_color(lv.color_white(), 0)
status_label_cont.set_style_bg_opa(66, 0)
status_label_cont.set_style_border_width(0, 0)
status_label = lv.label(status_label_cont)
status_label.set_text("No camera found.")
status_label.set_long_mode(lv.label.LONG.WRAP)
status_label.set_style_text_color(lv.color_white(), 0)
status_label.set_width(lv.pct(100))
status_label.center()
def init_cam():
try:
from camera import Camera, GrabMode, PixelFormat, FrameSize, GainCeiling
cam = Camera(
data_pins=[12,13,15,11,14,10,7,2],
vsync_pin=6,
href_pin=4,
sda_pin=21,
scl_pin=16,
pclk_pin=9,
xclk_pin=8,
xclk_freq=20000000,
powerdown_pin=-1,
reset_pin=-1,
pixel_format=PixelFormat.RGB565,
#pixel_format=PixelFormat.GRAYSCALE,
frame_size=FrameSize.R240X240,
grab_mode=GrabMode.LATEST
)
#cam.init() automatically done when creating the Camera()
#cam.reconfigure(frame_size=FrameSize.HVGA)
#frame_size=FrameSize.HVGA, # 480x320
#frame_size=FrameSize.QVGA, # 320x240
#frame_size=FrameSize.QQVGA # 160x120
cam.set_vflip(True)
return cam
except Exception as e:
print(f"init_cam exception: {e}")
return None
def check_running(timer):
if lv.screen_active() != main_screen:
print("camtest.py backgrounded, cleaning up...")
check_running_timer.delete()
if capture_timer:
capture_timer.delete()
if use_webcam:
webcam.deinit(cam)
elif cam:
cam.deinit()
print("camtest.py cleanup done.")
def init(scanqr_cb=None):
global scanqr_callback, cam, use_webcam, check_running_timer, status_label_cont, capture_timer, main_screen
scanqr_callback = scanqr_cb
build_ui()
cam = init_cam()
if cam:
image.set_rotation(900) # internal camera is rotated 90 degrees
else:
print("camtest.py: no internal camera found, trying webcam on /dev/video0")
try:
cam = webcam.init("/dev/video0")
use_webcam = True
except Exception as e:
print(f"camtest.py: webcam exception: {e}")
if cam:
print("Camera initialized, continuing...")
check_running_timer = lv.timer_create(check_running, 500, None)
capture_timer = lv.timer_create(try_capture, 100, None)
status_label_cont.add_flag(lv.obj.FLAG.HIDDEN)
if scanqr_callback:
start_qr_decoding()
else:
qr_button.remove_flag(lv.obj.FLAG.HIDDEN)
snap_button.remove_flag(lv.obj.FLAG.HIDDEN)
return main_screen
else:
print("No camera found, stopping camtest.py")
if scanqr_callback:
scanqr_callback(False,"")
return None
if __name__ == '__main__':
print("camera started as __main__")
mpos.ui.load_screen(init())
@@ -0,0 +1 @@
/home/user/sources/PiggyOS/internal_filesystem/apps/com.lightningpiggy.displaywallet/assets/captureqr.py
@@ -6,225 +6,221 @@
# and the performance impact of converting RGB565 to grayscale is probably minimal anyway.
import lvgl as lv
import time
try:
import webcam
except Exception as e:
print(f"Info: could not import webcam module: {e}")
from mpos.apps import Activity
import mpos.apps
import mpos.ui
# screens:
main_screen = None
keepliveqrdecoding = False
width = 240
height = 240
cam = None
# Variable to hold the current memoryview to prevent garbage collection
current_cam_buffer = None
image_dsc = None
image = None
qr_label = None
scanqr_callback = None
use_webcam = False
qr_button = None
snap_button = None
capture_timer = None
status_label = None
status_label_cont = None
status_label_text = "No camera found."
status_label_text_searching = "Searching QR codes...\n\nHold still and make them big!\n10cm for simple QR codes,\n20cm for complex."
status_label_text_found = "Decoding QR..."
def print_qr_buffer(buffer):
try:
# Try to decode buffer as a UTF-8 string
result = buffer.decode('utf-8')
# Check if the string is printable (ASCII printable characters)
if all(32 <= ord(c) <= 126 for c in result):
return result
except Exception as e:
pass
# If not a valid string or not printable, convert to hex
hex_str = ' '.join([f'{b:02x}' for b in buffer])
return hex_str.lower()
class CameraActivity(Activity):
# Byte-Order-Mark is added sometimes
def remove_bom(buffer):
bom = b'\xEF\xBB\xBF'
if buffer.startswith(bom):
return buffer[3:]
return buffer
def __init__(self):
self.cam = None
self.current_cam_buffer = None # Variable to hold the current memoryview to prevent garbage collection
self.image_dsc = None
self.image = None
self.qr_label = None
self.scanqr_callback = None
self.use_webcam = False
self.qr_button = None
self.snap_button = None
self.capture_timer = None
self.status_label = None
self.status_label_cont = None
self.keepliveqrdecoding = False
def qrdecode_one():
global status_label, status_label_text, scanqr_callback
try:
import qrdecode
result = qrdecode.qrdecode_rgb565(current_cam_buffer, width, height)
#result = bytearray("INSERT_QR_HERE", "utf-8")
if not result:
status_label.set_text(status_label_text_searching)
def onCreate(self):
self.scanqr_callback = self.getIntent().extras.get("scanqr_callback")
main_screen = lv.obj()
main_screen.set_style_pad_all(0, 0)
main_screen.set_style_border_width(0, 0)
main_screen.set_size(lv.pct(100), lv.pct(100))
main_screen.set_scrollbar_mode(lv.SCROLLBAR_MODE.OFF)
close_button = lv.button(main_screen)
close_button.set_size(60,60)
close_button.align(lv.ALIGN.TOP_RIGHT, 0, 0)
close_label = lv.label(close_button)
close_label.set_text(lv.SYMBOL.CLOSE)
close_label.center()
close_button.add_event_cb(lambda e: self.finish(),lv.EVENT.CLICKED,None)
self.snap_button = lv.button(main_screen)
self.snap_button.set_size(60, 60)
self.snap_button.align(lv.ALIGN.RIGHT_MID, 0, 0)
self.snap_button.add_flag(lv.obj.FLAG.HIDDEN)
self.snap_button.add_event_cb(self.snap_button_click,lv.EVENT.CLICKED,None)
snap_label = lv.label(self.snap_button)
snap_label.set_text(lv.SYMBOL.OK)
snap_label.center()
self.qr_button = lv.button(main_screen)
self.qr_button.set_size(60, 60)
self.qr_button.add_flag(lv.obj.FLAG.HIDDEN)
self.qr_button.align(lv.ALIGN.BOTTOM_RIGHT, 0, 0)
self.qr_button.add_event_cb(self.qr_button_click,lv.EVENT.CLICKED,None)
self.qr_label = lv.label(self.qr_button)
self.qr_label.set_text(lv.SYMBOL.EYE_OPEN)
self.qr_label.center()
# Initialize LVGL image widget
self.image = lv.image(main_screen)
self.image.align(lv.ALIGN.LEFT_MID, 0, 0)
# Create image descriptor once
self.image_dsc = lv.image_dsc_t({
"header": {
"magic": lv.IMAGE_HEADER_MAGIC,
"w": width,
"h": height,
"stride": width * 2,
"cf": lv.COLOR_FORMAT.RGB565
#"cf": lv.COLOR_FORMAT.L8
},
'data_size': width * height * 2,
'data': None # Will be updated per frame
})
self.image.set_src(self.image_dsc)
self.status_label_cont = lv.obj(main_screen)
self.status_label_cont.set_size(lv.pct(66),lv.pct(60))
self.status_label_cont.align(lv.ALIGN.LEFT_MID, lv.pct(5), 0)
self.status_label_cont.set_style_bg_color(lv.color_white(), 0)
self.status_label_cont.set_style_bg_opa(66, 0)
self.status_label_cont.set_style_border_width(0, 0)
self.status_label = lv.label(self.status_label_cont)
self.status_label.set_text("No camera found.")
self.status_label.set_long_mode(lv.label.LONG.WRAP)
self.status_label.set_style_text_color(lv.color_white(), 0)
self.status_label.set_width(lv.pct(100))
self.status_label.center()
self.setContentView(main_screen)
def onResume(self, screen):
self.cam = init_internal_cam()
if self.cam:
self.image.set_rotation(900) # internal camera is rotated 90 degrees
else:
stop_qr_decoding()
result = remove_bom(result)
result = print_qr_buffer(result)
print(f"QR decoding found: {result}")
if scanqr_callback:
scanqr_callback(True,result)
mpos.ui.back_screen()
print("camtest.py: no internal camera found, trying webcam on /dev/video0")
try:
self.cam = webcam.init("/dev/video0")
self.use_webcam = True
except Exception as e:
print(f"camtest.py: webcam exception: {e}")
if self.cam:
print("Camera initialized, continuing...")
self.capture_timer = lv.timer_create(self.try_capture, 100, None)
self.status_label_cont.add_flag(lv.obj.FLAG.HIDDEN)
if self.scanqr_callback:
self.start_qr_decoding()
else:
status_label.set_text(result) # in the future, the status_label text should be copy-paste-able
except ValueError as e:
print("QR ValueError: ", e)
status_label.set_text(status_label_text_searching)
except TypeError as e:
print("QR TypeError: ", e)
status_label.set_text(status_label_text_found)
except Exception as e:
print("QR got other error: ", e)
self.qr_button.remove_flag(lv.obj.FLAG.HIDDEN)
self.snap_button.remove_flag(lv.obj.FLAG.HIDDEN)
else:
print("No camera found, stopping camtest.py")
if self.scanqr_callback:
self.scanqr_callback(False,"")
def onStop(self, screen):
print("camtest.py backgrounded, cleaning up...")
if self.capture_timer:
self.capture_timer.delete()
if self.use_webcam:
webcam.deinit(self.cam)
elif self.cam:
self.cam.deinit()
print("camtest.py cleanup done.")
def close_button_click(e):
print("Close button clicked")
mpos.ui.back_screen()
def snap_button_click(e):
print("Picture taken!")
import os
try:
os.mkdir("data")
except OSError:
pass
try:
os.mkdir("data/com.example.camtest")
except OSError:
pass
if current_cam_buffer is not None:
filename="data/com.example.camtest/capture.raw"
def qrdecode_one(self):
try:
with open(filename, 'wb') as f:
f.write(current_cam_buffer)
print(f"Successfully wrote current_cam_buffer to {filename}")
except OSError as e:
print(f"Error writing to file: {e}")
import qrdecode
result = qrdecode.qrdecode_rgb565(self.current_cam_buffer, width, height)
#result = bytearray("INSERT_QR_HERE", "utf-8")
if not result:
self.status_label.set_text(status_label_text_searching)
else:
self.stop_qr_decoding()
result = remove_bom(result)
result = print_qr_buffer(result)
print(f"QR decoding found: {result}")
if self.scanqr_callback:
self.scanqr_callback(True,result)
self.finish()
else:
self.status_label.set_text(result) # in the future, the status_label text should be copy-paste-able
except ValueError as e:
print("QR ValueError: ", e)
self.status_label.set_text(status_label_text_searching)
except TypeError as e:
print("QR TypeError: ", e)
self.status_label.set_text(status_label_text_found)
except Exception as e:
print("QR got other error: ", e)
def snap_button_click(self, e):
print("Picture taken!")
import os
try:
os.mkdir("data")
except OSError:
pass
try:
os.mkdir("data/com.example.camtest")
except OSError:
pass
if self.current_cam_buffer is not None:
filename="data/com.example.camtest/capture.raw"
try:
with open(filename, 'wb') as f:
f.write(self.current_cam_buffer)
print(f"Successfully wrote current_cam_buffer to {filename}")
except OSError as e:
print(f"Error writing to file: {e}")
def start_qr_decoding(self):
print("Activating live QR decoding...")
self.keepliveqrdecoding = True
self.qr_label.set_text(lv.SYMBOL.EYE_CLOSE)
self.status_label_cont.remove_flag(lv.obj.FLAG.HIDDEN)
self.status_label.set_text(status_label_text_searching)
def stop_qr_decoding(self):
print("Deactivating live QR decoding...")
self.keepliveqrdecoding = False
self.qr_label.set_text(lv.SYMBOL.EYE_OPEN)
status_label_text = self.status_label.get_text()
if status_label_text == status_label_text_searching or status_label_text == status_label_text_found: # if it found a QR code, leave it
self.status_label_cont.add_flag(lv.obj.FLAG.HIDDEN)
def qr_button_click(self, e):
if not self.keepliveqrdecoding:
self.start_qr_decoding()
else:
self.stop_qr_decoding()
def try_capture(self, event):
#print("capturing camera frame")
try:
if self.use_webcam:
self.current_cam_buffer = webcam.capture_frame(self.cam, "rgb565")
elif self.cam.frame_available():
self.current_cam_buffer = self.cam.capture()
if self.current_cam_buffer and len(self.current_cam_buffer):
self.image_dsc.data = self.current_cam_buffer
#image.invalidate() # does not work so do this:
self.image.set_src(self.image_dsc)
if not self.use_webcam:
self.cam.free_buffer() # Free the old buffer
if self.keepliveqrdecoding:
self.qrdecode_one()
except Exception as e:
print(f"Camera capture exception: {e}")
def start_qr_decoding():
global qr_label, keepliveqrdecoding, status_label_cont, status_label
print("Activating live QR decoding...")
keepliveqrdecoding = True
qr_label.set_text(lv.SYMBOL.EYE_CLOSE)
status_label_cont.remove_flag(lv.obj.FLAG.HIDDEN)
status_label.set_text(status_label_text_searching)
def stop_qr_decoding():
global qr_label, keepliveqrdecoding
print("Deactivating live QR decoding...")
keepliveqrdecoding = False
qr_label.set_text(lv.SYMBOL.EYE_OPEN)
status_label_text = status_label.get_text()
if status_label_text == status_label_text_searching or status_label_text == status_label_text_found: # if it found a QR code, then leave it
status_label_cont.add_flag(lv.obj.FLAG.HIDDEN)
def qr_button_click(e):
global keepliveqrdecoding
if not keepliveqrdecoding:
start_qr_decoding()
else:
stop_qr_decoding()
def try_capture(event):
#print("capturing camera frame")
global current_cam_buffer, image_dsc, image, use_webcam, cam
try:
if use_webcam:
current_cam_buffer = webcam.capture_frame(cam, "rgb565")
elif cam.frame_available():
current_cam_buffer = cam.capture()
if current_cam_buffer and len(current_cam_buffer):
image_dsc.data = current_cam_buffer
#image.invalidate() # does not work so do this:
image.set_src(image_dsc)
if not use_webcam:
cam.free_buffer() # Free the old buffer
if keepliveqrdecoding:
qrdecode_one()
except Exception as e:
print(f"Camera capture exception: {e}")
def build_ui():
global image, image_dsc,qr_label, status_label, cam, use_webcam, qr_button, snap_button, status_label_cont, main_screen
main_screen = lv.obj()
main_screen.set_style_pad_all(0, 0)
main_screen.set_style_border_width(0, 0)
main_screen.set_size(lv.pct(100), lv.pct(100))
main_screen.set_scrollbar_mode(lv.SCROLLBAR_MODE.OFF)
close_button = lv.button(main_screen)
close_button.set_size(60,60)
close_button.align(lv.ALIGN.TOP_RIGHT, 0, 0)
close_label = lv.label(close_button)
close_label.set_text(lv.SYMBOL.CLOSE)
close_label.center()
close_button.add_event_cb(close_button_click,lv.EVENT.CLICKED,None)
snap_button = lv.button(main_screen)
snap_button.set_size(60, 60)
snap_button.align(lv.ALIGN.RIGHT_MID, 0, 0)
snap_button.add_flag(lv.obj.FLAG.HIDDEN)
snap_label = lv.label(snap_button)
snap_label.set_text(lv.SYMBOL.OK)
snap_label.center()
snap_button.add_event_cb(snap_button_click,lv.EVENT.CLICKED,None)
qr_button = lv.button(main_screen)
qr_button.set_size(60, 60)
qr_button.add_flag(lv.obj.FLAG.HIDDEN)
qr_button.align(lv.ALIGN.BOTTOM_RIGHT, 0, 0)
qr_label = lv.label(qr_button)
qr_label.set_text(lv.SYMBOL.EYE_OPEN)
qr_label.center()
qr_button.add_event_cb(qr_button_click,lv.EVENT.CLICKED,None)
# Initialize LVGL image widget
image = lv.image(main_screen)
image.align(lv.ALIGN.LEFT_MID, 0, 0)
# Create image descriptor once
image_dsc = lv.image_dsc_t({
"header": {
"magic": lv.IMAGE_HEADER_MAGIC,
"w": width,
"h": height,
"stride": width * 2,
"cf": lv.COLOR_FORMAT.RGB565
#"cf": lv.COLOR_FORMAT.L8
},
'data_size': width * height * 2,
'data': None # Will be updated per frame
})
image.set_src(image_dsc)
status_label_cont = lv.obj(main_screen)
status_label_cont.set_size(lv.pct(66),lv.pct(60))
status_label_cont.align(lv.ALIGN.LEFT_MID, lv.pct(5), 0)
status_label_cont.set_style_bg_color(lv.color_white(), 0)
status_label_cont.set_style_bg_opa(66, 0)
status_label_cont.set_style_border_width(0, 0)
status_label = lv.label(status_label_cont)
status_label.set_text("No camera found.")
status_label.set_long_mode(lv.label.LONG.WRAP)
status_label.set_style_text_color(lv.color_white(), 0)
status_label.set_width(lv.pct(100))
status_label.center()
def init_cam():
# Non-class functions:
def init_internal_cam():
try:
from camera import Camera, GrabMode, PixelFormat, FrameSize, GainCeiling
cam = Camera(
@@ -254,53 +250,22 @@ def init_cam():
print(f"init_cam exception: {e}")
return None
def print_qr_buffer(buffer):
try:
# Try to decode buffer as a UTF-8 string
result = buffer.decode('utf-8')
# Check if the string is printable (ASCII printable characters)
if all(32 <= ord(c) <= 126 for c in result):
return result
except Exception as e:
pass
# If not a valid string or not printable, convert to hex
hex_str = ' '.join([f'{b:02x}' for b in buffer])
return hex_str.lower()
def check_running(timer):
if lv.screen_active() != main_screen:
print("camtest.py backgrounded, cleaning up...")
check_running_timer.delete()
if capture_timer:
capture_timer.delete()
if use_webcam:
webcam.deinit(cam)
elif cam:
cam.deinit()
print("camtest.py cleanup done.")
def init(scanqr_cb=None):
global scanqr_callback, cam, use_webcam, check_running_timer, status_label_cont, capture_timer, main_screen
scanqr_callback = scanqr_cb
build_ui()
cam = init_cam()
if cam:
image.set_rotation(900) # internal camera is rotated 90 degrees
else:
print("camtest.py: no internal camera found, trying webcam on /dev/video0")
try:
cam = webcam.init("/dev/video0")
use_webcam = True
except Exception as e:
print(f"camtest.py: webcam exception: {e}")
if cam:
print("Camera initialized, continuing...")
check_running_timer = lv.timer_create(check_running, 500, None)
capture_timer = lv.timer_create(try_capture, 100, None)
status_label_cont.add_flag(lv.obj.FLAG.HIDDEN)
if scanqr_callback:
start_qr_decoding()
else:
qr_button.remove_flag(lv.obj.FLAG.HIDDEN)
snap_button.remove_flag(lv.obj.FLAG.HIDDEN)
return main_screen
else:
print("No camera found, stopping camtest.py")
if scanqr_callback:
scanqr_callback(False,"")
return None
if __name__ == '__main__':
print("camera started as __main__")
mpos.ui.load_screen(init())
# Byte-Order-Mark is added sometimes
def remove_bom(buffer):
bom = b'\xEF\xBB\xBF'
if buffer.startswith(bom):
return buffer[3:]
return buffer
@@ -3,7 +3,7 @@ import mpos.config
import mpos.ui
from wallet import LNBitsWallet, NWCWallet
from captureqr import CameraActivity
class MainActivity(Activity):
@@ -328,10 +328,7 @@ class SettingActivity(Activity):
def cambutton_cb(self, event):
print("cambutton clicked!")
import captureqr
qr_scanner_screen = captureqr.init(self.gotqr_callback)
if qr_scanner_screen:
mpos.ui.load_screen(qr_scanner_screen)
self.startActivity(Intent(activity_class=CameraActivity).putExtra("scanqr_callback", self.gotqr_callback))
def save_setting(self, setting):
if setting["key"] == "wallet_type" and self.radio_container:
+17 -11
View File
@@ -7,7 +7,6 @@ import uos
import _thread
import traceback
import mpos.apps
import mpos.info
import mpos.ui
@@ -41,16 +40,23 @@ def execute_script(script_source, is_file, cwd=None):
compiled_script = compile(script_source, compile_name, 'exec')
exec(compiled_script, script_globals)
# Introspect globals
classes = {k: v for k, v in script_globals.items() if isinstance(v, type)}
functions = {k: v for k, v in script_globals.items() if callable(v) and not isinstance(v, type)}
variables = {k: v for k, v in script_globals.items() if not callable(v)}
print("Classes:", classes.keys())
print("Functions:", functions.keys())
print("Variables:", variables.keys())
MainActivity = script_globals.get("MainActivity")
if MainActivity:
loaded_activity = MainActivity()
loaded_activity.onCreate() # Call lifecycle method
#classes = {k: v for k, v in script_globals.items() if isinstance(v, type)}
#functions = {k: v for k, v in script_globals.items() if callable(v) and not isinstance(v, type)}
#variables = {k: v for k, v in script_globals.items() if not callable(v)}
#print("Classes:", classes.keys())
#print("Functions:", functions.keys())
#print("Variables:", variables.keys())
main_activity = script_globals.get("MainActivity")
if not main_activity: # Fallback to taking the first non-generic Activity class, but that's slower
for k, v in script_globals.items():
if k != "Activity" and isinstance(v, type):
main_activity = v # first one is the 'Activity' from which it inherits so take the second one
break
print(f"Got main_activity: {main_activity}")
if main_activity:
Activity.startActivity(None, Intent(activity_class=main_activity))
else:
print("Warning: could not find main_activity")
except Exception as e:
print(f"Thread {thread_id}: exception during execution:")
# Print stack trace with exception type, value, and traceback