You've already forked MicroPythonOS
mirror of
https://github.com/m5stack/MicroPythonOS.git
synced 2026-05-20 11:51:27 -07:00
Add file manager app
This commit is contained in:
@@ -0,0 +1,24 @@
|
||||
{
|
||||
"name": "File Manager",
|
||||
"publisher": "MicroPythonOS",
|
||||
"short_description": "Manage files",
|
||||
"long_description": "Traverse around the filesystem and manage files and folders you find..",
|
||||
"icon_url": "https://apps.micropythonos.com/apps/com.micropythonos.explorer/icons/com.micropythonos.filemanager.0.1_64x64.png",
|
||||
"download_url": "https://apps.micropythonos.com/apps/com.micropythonos.fileexplorer/mpks/com.micropythonos.filemanager.0.1.mpk",
|
||||
"fullname": "com.micropythonos.filemanager",
|
||||
"version": "0.0.1",
|
||||
"category": "development",
|
||||
"activities": [
|
||||
{
|
||||
"entrypoint": "assets/file_manager.py",
|
||||
"classname": "FileManager",
|
||||
"intent_filters": [
|
||||
{
|
||||
"action": "main",
|
||||
"category": "launcher"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -0,0 +1,58 @@
|
||||
from mpos.apps import Activity
|
||||
import mpos.ui
|
||||
|
||||
class FileManager(Activity):
|
||||
|
||||
# Widgets:
|
||||
file_explorer = None
|
||||
|
||||
def onCreate(self):
|
||||
#lv.log_register_print_cb(self.log_callback)
|
||||
screen = lv.obj()
|
||||
self.file_explorer = lv.file_explorer(screen)
|
||||
#self.file_explorer.set_root_path("M:data/images/")
|
||||
#self.file_explorer.explorer_open_dir('/')
|
||||
#self.file_explorer.explorer_open_dir('M:data/images/')
|
||||
#self.file_explorer.explorer_open_dir('M:/')
|
||||
self.file_explorer.explorer_open_dir('M:/')
|
||||
#self.file_explorer.explorer_open_dir('M:data/images/')
|
||||
#self.file_explorer.explorer_open_dir('P:.') # POSIX works, fs_driver doesn't because it doesn't have dir_open, dir_read, dir_close
|
||||
#self.file_explorer.explorer_open_dir('P:/tmp') # POSIX works, fs_driver doesn't because it doesn't have dir_open, dir_read, dir_close
|
||||
#file_explorer.explorer_open_dir('S:/')
|
||||
#self.file_explorer.set_size(lv.pct(100), lv.pct(100))
|
||||
#file_explorer.set_mode(lv.FILE_EXPLORER.MODE.DEFAULT) # Default browsing mode
|
||||
#file_explorer.set_sort(lv.FILE_EXPLORER.SORT.NAME_ASC) # Sort by name, ascending
|
||||
self.file_explorer.align(lv.ALIGN.CENTER, 0, 0)
|
||||
# Attach event callback
|
||||
self.file_explorer.add_event_cb(self.file_explorer_event_cb, lv.EVENT.ALL, None)
|
||||
self.setContentView(screen)
|
||||
|
||||
def file_explorer_event_cb(self, event):
|
||||
event_code = event.get_code()
|
||||
# Ignore:
|
||||
# =======
|
||||
# 2: PRESSING
|
||||
# 19: HIT_TEST
|
||||
# COVER_CHECK
|
||||
# 24: REFR_EXT_DRAW_SIZE
|
||||
# DRAW_MAIN
|
||||
# DRAW_MAIN_BEGIN
|
||||
# DRAW_MAIN_END
|
||||
# DRAW_POST
|
||||
# DRAW_POST_BEGIN
|
||||
# DRAW_POST_END
|
||||
# GET_SELF_SIZE
|
||||
# 47 STYLE CHANGED
|
||||
if event_code not in [2,19,23,24,25,26,27,28,29,30,47,49]:
|
||||
name = mpos.ui.get_event_name(event_code)
|
||||
print(f"file_explorer_event_cb {event_code} with name {name}")
|
||||
if event_code == lv.EVENT.VALUE_CHANGED:
|
||||
path = self.file_explorer.explorer_get_current_path()
|
||||
file = self.file_explorer.explorer_get_selected_file_name()
|
||||
print(f"Selected: {path}{file}")
|
||||
|
||||
# Custom log callback to capture FPS
|
||||
def log_callback(self, level, log_str):
|
||||
# Convert log_str to string if it's a bytes object
|
||||
log_str = log_str.decode() if isinstance(log_str, bytes) else log_str
|
||||
print(f"Level: {level}, Log: {log_str}")
|
||||
@@ -0,0 +1,132 @@
|
||||
# Original author: mhepp(https://forum.lvgl.io/u/mhepp/summary)
|
||||
# Copyright (c) 2024 - 2025 Kevin G. Schlosser
|
||||
# Added directory support, upstreamed at https://github.com/lvgl-micropython/lvgl_micropython/issues/398
|
||||
|
||||
|
||||
import lvgl as lv
|
||||
import struct
|
||||
|
||||
|
||||
def _fs_open_cb(drv, path, mode):
|
||||
|
||||
if mode == lv.FS_MODE.WR:
|
||||
p_mode = 'wb'
|
||||
elif mode == lv.FS_MODE.RD:
|
||||
p_mode = 'rb'
|
||||
elif mode == lv.FS_MODE.WR | lv.FS_MODE.RD:
|
||||
p_mode = 'rb+'
|
||||
else:
|
||||
raise RuntimeError("fs_open_callback() - open mode error, %s is invalid mode" % mode)
|
||||
|
||||
try:
|
||||
f = open(path, p_mode)
|
||||
|
||||
except OSError as e:
|
||||
raise RuntimeError("fs_open_callback(%s) exception: %s" % (path, e))
|
||||
|
||||
return {'file' : f, 'path': path}
|
||||
|
||||
|
||||
def _fs_close_cb(drv, fs_file):
|
||||
try:
|
||||
fs_file.__cast__()['file'].close()
|
||||
except OSError as e:
|
||||
raise RuntimeError("fs_close_callback(%s) exception: %s" % (fs_file.__cast__()['path'], e))
|
||||
|
||||
return lv.FS_RES.OK
|
||||
|
||||
|
||||
def _fs_read_cb(drv, fs_file, buf, btr, br):
|
||||
try:
|
||||
tmp_data = fs_file.__cast__()['file'].read(btr)
|
||||
buf.__dereference__(btr)[0:len(tmp_data)] = tmp_data
|
||||
br.__dereference__(4)[0:4] = struct.pack("<L", len(tmp_data))
|
||||
except OSError as e:
|
||||
raise RuntimeError("fs_read_callback(%s) exception %s" % (fs_file.__cast__()['path'], e))
|
||||
|
||||
return lv.FS_RES.OK
|
||||
|
||||
|
||||
def _fs_seek_cb(drv, fs_file, pos, whence):
|
||||
try:
|
||||
fs_file.__cast__()['file'].seek(pos, whence)
|
||||
except OSError as e:
|
||||
raise RuntimeError("fs_seek_callback(%s) exception %s" % (fs_file.__cast__()['path'], e))
|
||||
|
||||
return lv.FS_RES.OK
|
||||
|
||||
|
||||
def _fs_tell_cb(drv, fs_file, pos):
|
||||
try:
|
||||
tpos = fs_file.__cast__()['file'].tell()
|
||||
pos.__dereference__(4)[0:4] = struct.pack("<L", tpos)
|
||||
except OSError as e:
|
||||
raise RuntimeError("fs_tell_callback(%s) exception %s" % (fs_file.__cast__()['path'], e))
|
||||
|
||||
return lv.FS_RES.OK
|
||||
|
||||
|
||||
def _fs_write_cb(drv, fs_file, buf, btw, bw):
|
||||
try:
|
||||
wr = fs_file.__cast__()['file'].write(buf.__dereference__(btw)[0:btw])
|
||||
bw.__dereference__(4)[0:4] = struct.pack("<L", wr)
|
||||
except OSError as e:
|
||||
raise RuntimeError("fs_write_callback(%s) exception %s" % (fs_file.__cast__()['path'], e))
|
||||
|
||||
return lv.FS_RES.OK
|
||||
|
||||
def _fs_dir_open_cb(drv, path):
|
||||
#print(f"_fs_dir_open_cb for path '{path}'")
|
||||
try:
|
||||
import os # for ilistdir()
|
||||
return {'iterator' : os.ilistdir(path)}
|
||||
except Exception as e:
|
||||
print(f"_fs_dir_open_cb exception: {e}")
|
||||
return None
|
||||
|
||||
def _fs_dir_read_cb(drv, lv_fs_dir_t, buf, btr):
|
||||
try:
|
||||
iterator = lv_fs_dir_t.__cast__()['iterator']
|
||||
nextfile = iterator.__next__()
|
||||
#print(f"nextfile: {nextfile}")
|
||||
filename = nextfile[0]
|
||||
entry_type = nextfile[1] # Type field
|
||||
if entry_type == 0x4000:
|
||||
#print(f"{filename} is a directory")
|
||||
filename = f"/{filename}"
|
||||
# Convert filename to bytes with null terminator
|
||||
tmp_data_bytes = filename.encode() + b'\x00'
|
||||
buf.__dereference__(btr)[0:len(tmp_data_bytes)] = tmp_data_bytes
|
||||
return lv.FS_RES.OK
|
||||
except StopIteration:
|
||||
# Clear buffer and return FS_ERR when iteration ends
|
||||
buf.__dereference__(btr)[0:1] = b'\x00' # Empty string (null byte)
|
||||
return lv.FS_RES.NOT_EX # Next entry "does not exist"
|
||||
except Exception as e:
|
||||
print(f"_fs_dir_read_cb exception: {e}")
|
||||
return lv.FS_RES.UNKNOWN
|
||||
|
||||
def _fs_dir_close_cb(drv, lv_fs_dir_t):
|
||||
#print(f"_fs_dir_close_cb called")
|
||||
# No need to cleanup the iterator so nothing to do
|
||||
return lv.FS_RES.OK
|
||||
|
||||
def fs_register(fs_drv, letter, cache_size=500):
|
||||
|
||||
fs_drv.init()
|
||||
fs_drv.letter = ord(letter)
|
||||
fs_drv.open_cb = _fs_open_cb
|
||||
fs_drv.read_cb = _fs_read_cb
|
||||
fs_drv.write_cb = _fs_write_cb
|
||||
fs_drv.seek_cb = _fs_seek_cb
|
||||
fs_drv.tell_cb = _fs_tell_cb
|
||||
fs_drv.close_cb = _fs_close_cb
|
||||
fs_drv.dir_open_cb = _fs_dir_open_cb
|
||||
fs_drv.dir_read_cb = _fs_dir_read_cb
|
||||
#fs_drv.dir_close_cb = _fs_dir_close_cb
|
||||
|
||||
if cache_size >= 0:
|
||||
fs_drv.cache_size = cache_size
|
||||
|
||||
fs_drv.register()
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
#import utime # for time profiling
|
||||
|
||||
import lvgl as lv
|
||||
import mpos.apps
|
||||
import mpos.battery_voltage
|
||||
|
||||
@@ -3,9 +3,9 @@ import _thread
|
||||
import lvgl as lv
|
||||
|
||||
# Allow LVGL M:/path/to/file or M:relative/path/to/file to work for image set_src etc
|
||||
import fs_driver
|
||||
import mpos.fs_driver
|
||||
fs_drv = lv.fs_drv_t()
|
||||
fs_driver.fs_register(fs_drv, 'M')
|
||||
mpos.fs_driver.fs_register(fs_drv, 'M')
|
||||
|
||||
import mpos.apps
|
||||
import mpos.config
|
||||
|
||||
Reference in New Issue
Block a user