Skip to content

Commit d0444f1

Browse files
Add file manager app
1 parent 82451db commit d0444f1

File tree

5 files changed

+216
-4
lines changed

5 files changed

+216
-4
lines changed
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
{
2+
"name": "File Manager",
3+
"publisher": "MicroPythonOS",
4+
"short_description": "Manage files",
5+
"long_description": "Traverse around the filesystem and manage files and folders you find..",
6+
"icon_url": "https://apps.micropythonos.com/apps/com.micropythonos.explorer/icons/com.micropythonos.filemanager.0.1_64x64.png",
7+
"download_url": "https://apps.micropythonos.com/apps/com.micropythonos.fileexplorer/mpks/com.micropythonos.filemanager.0.1.mpk",
8+
"fullname": "com.micropythonos.filemanager",
9+
"version": "0.0.1",
10+
"category": "development",
11+
"activities": [
12+
{
13+
"entrypoint": "assets/file_manager.py",
14+
"classname": "FileManager",
15+
"intent_filters": [
16+
{
17+
"action": "main",
18+
"category": "launcher"
19+
}
20+
]
21+
}
22+
]
23+
}
24+
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
from mpos.apps import Activity
2+
import mpos.ui
3+
4+
class FileManager(Activity):
5+
6+
# Widgets:
7+
file_explorer = None
8+
9+
def onCreate(self):
10+
#lv.log_register_print_cb(self.log_callback)
11+
screen = lv.obj()
12+
self.file_explorer = lv.file_explorer(screen)
13+
#self.file_explorer.set_root_path("M:data/images/")
14+
#self.file_explorer.explorer_open_dir('/')
15+
#self.file_explorer.explorer_open_dir('M:data/images/')
16+
#self.file_explorer.explorer_open_dir('M:/')
17+
self.file_explorer.explorer_open_dir('M:/')
18+
#self.file_explorer.explorer_open_dir('M:data/images/')
19+
#self.file_explorer.explorer_open_dir('P:.') # POSIX works, fs_driver doesn't because it doesn't have dir_open, dir_read, dir_close
20+
#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
21+
#file_explorer.explorer_open_dir('S:/')
22+
#self.file_explorer.set_size(lv.pct(100), lv.pct(100))
23+
#file_explorer.set_mode(lv.FILE_EXPLORER.MODE.DEFAULT) # Default browsing mode
24+
#file_explorer.set_sort(lv.FILE_EXPLORER.SORT.NAME_ASC) # Sort by name, ascending
25+
self.file_explorer.align(lv.ALIGN.CENTER, 0, 0)
26+
# Attach event callback
27+
self.file_explorer.add_event_cb(self.file_explorer_event_cb, lv.EVENT.ALL, None)
28+
self.setContentView(screen)
29+
30+
def file_explorer_event_cb(self, event):
31+
event_code = event.get_code()
32+
# Ignore:
33+
# =======
34+
# 2: PRESSING
35+
# 19: HIT_TEST
36+
# COVER_CHECK
37+
# 24: REFR_EXT_DRAW_SIZE
38+
# DRAW_MAIN
39+
# DRAW_MAIN_BEGIN
40+
# DRAW_MAIN_END
41+
# DRAW_POST
42+
# DRAW_POST_BEGIN
43+
# DRAW_POST_END
44+
# GET_SELF_SIZE
45+
# 47 STYLE CHANGED
46+
if event_code not in [2,19,23,24,25,26,27,28,29,30,47,49]:
47+
name = mpos.ui.get_event_name(event_code)
48+
print(f"file_explorer_event_cb {event_code} with name {name}")
49+
if event_code == lv.EVENT.VALUE_CHANGED:
50+
path = self.file_explorer.explorer_get_current_path()
51+
file = self.file_explorer.explorer_get_selected_file_name()
52+
print(f"Selected: {path}{file}")
53+
54+
# Custom log callback to capture FPS
55+
def log_callback(self, level, log_str):
56+
# Convert log_str to string if it's a bytes object
57+
log_str = log_str.decode() if isinstance(log_str, bytes) else log_str
58+
print(f"Level: {level}, Log: {log_str}")
Lines changed: 132 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,132 @@
1+
# Original author: mhepp(https://forum.lvgl.io/u/mhepp/summary)
2+
# Copyright (c) 2024 - 2025 Kevin G. Schlosser
3+
# Added directory support, upstreamed at https://github.com/lvgl-micropython/lvgl_micropython/issues/398
4+
5+
6+
import lvgl as lv
7+
import struct
8+
9+
10+
def _fs_open_cb(drv, path, mode):
11+
12+
if mode == lv.FS_MODE.WR:
13+
p_mode = 'wb'
14+
elif mode == lv.FS_MODE.RD:
15+
p_mode = 'rb'
16+
elif mode == lv.FS_MODE.WR | lv.FS_MODE.RD:
17+
p_mode = 'rb+'
18+
else:
19+
raise RuntimeError("fs_open_callback() - open mode error, %s is invalid mode" % mode)
20+
21+
try:
22+
f = open(path, p_mode)
23+
24+
except OSError as e:
25+
raise RuntimeError("fs_open_callback(%s) exception: %s" % (path, e))
26+
27+
return {'file' : f, 'path': path}
28+
29+
30+
def _fs_close_cb(drv, fs_file):
31+
try:
32+
fs_file.__cast__()['file'].close()
33+
except OSError as e:
34+
raise RuntimeError("fs_close_callback(%s) exception: %s" % (fs_file.__cast__()['path'], e))
35+
36+
return lv.FS_RES.OK
37+
38+
39+
def _fs_read_cb(drv, fs_file, buf, btr, br):
40+
try:
41+
tmp_data = fs_file.__cast__()['file'].read(btr)
42+
buf.__dereference__(btr)[0:len(tmp_data)] = tmp_data
43+
br.__dereference__(4)[0:4] = struct.pack("<L", len(tmp_data))
44+
except OSError as e:
45+
raise RuntimeError("fs_read_callback(%s) exception %s" % (fs_file.__cast__()['path'], e))
46+
47+
return lv.FS_RES.OK
48+
49+
50+
def _fs_seek_cb(drv, fs_file, pos, whence):
51+
try:
52+
fs_file.__cast__()['file'].seek(pos, whence)
53+
except OSError as e:
54+
raise RuntimeError("fs_seek_callback(%s) exception %s" % (fs_file.__cast__()['path'], e))
55+
56+
return lv.FS_RES.OK
57+
58+
59+
def _fs_tell_cb(drv, fs_file, pos):
60+
try:
61+
tpos = fs_file.__cast__()['file'].tell()
62+
pos.__dereference__(4)[0:4] = struct.pack("<L", tpos)
63+
except OSError as e:
64+
raise RuntimeError("fs_tell_callback(%s) exception %s" % (fs_file.__cast__()['path'], e))
65+
66+
return lv.FS_RES.OK
67+
68+
69+
def _fs_write_cb(drv, fs_file, buf, btw, bw):
70+
try:
71+
wr = fs_file.__cast__()['file'].write(buf.__dereference__(btw)[0:btw])
72+
bw.__dereference__(4)[0:4] = struct.pack("<L", wr)
73+
except OSError as e:
74+
raise RuntimeError("fs_write_callback(%s) exception %s" % (fs_file.__cast__()['path'], e))
75+
76+
return lv.FS_RES.OK
77+
78+
def _fs_dir_open_cb(drv, path):
79+
#print(f"_fs_dir_open_cb for path '{path}'")
80+
try:
81+
import os # for ilistdir()
82+
return {'iterator' : os.ilistdir(path)}
83+
except Exception as e:
84+
print(f"_fs_dir_open_cb exception: {e}")
85+
return None
86+
87+
def _fs_dir_read_cb(drv, lv_fs_dir_t, buf, btr):
88+
try:
89+
iterator = lv_fs_dir_t.__cast__()['iterator']
90+
nextfile = iterator.__next__()
91+
#print(f"nextfile: {nextfile}")
92+
filename = nextfile[0]
93+
entry_type = nextfile[1] # Type field
94+
if entry_type == 0x4000:
95+
#print(f"{filename} is a directory")
96+
filename = f"/{filename}"
97+
# Convert filename to bytes with null terminator
98+
tmp_data_bytes = filename.encode() + b'\x00'
99+
buf.__dereference__(btr)[0:len(tmp_data_bytes)] = tmp_data_bytes
100+
return lv.FS_RES.OK
101+
except StopIteration:
102+
# Clear buffer and return FS_ERR when iteration ends
103+
buf.__dereference__(btr)[0:1] = b'\x00' # Empty string (null byte)
104+
return lv.FS_RES.NOT_EX # Next entry "does not exist"
105+
except Exception as e:
106+
print(f"_fs_dir_read_cb exception: {e}")
107+
return lv.FS_RES.UNKNOWN
108+
109+
def _fs_dir_close_cb(drv, lv_fs_dir_t):
110+
#print(f"_fs_dir_close_cb called")
111+
# No need to cleanup the iterator so nothing to do
112+
return lv.FS_RES.OK
113+
114+
def fs_register(fs_drv, letter, cache_size=500):
115+
116+
fs_drv.init()
117+
fs_drv.letter = ord(letter)
118+
fs_drv.open_cb = _fs_open_cb
119+
fs_drv.read_cb = _fs_read_cb
120+
fs_drv.write_cb = _fs_write_cb
121+
fs_drv.seek_cb = _fs_seek_cb
122+
fs_drv.tell_cb = _fs_tell_cb
123+
fs_drv.close_cb = _fs_close_cb
124+
fs_drv.dir_open_cb = _fs_dir_open_cb
125+
fs_drv.dir_read_cb = _fs_dir_read_cb
126+
#fs_drv.dir_close_cb = _fs_dir_close_cb
127+
128+
if cache_size >= 0:
129+
fs_drv.cache_size = cache_size
130+
131+
fs_drv.register()
132+

internal_filesystem/lib/mpos/ui/__init__.py

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,3 @@
1-
#import utime # for time profiling
2-
31
import lvgl as lv
42
import mpos.apps
53
import mpos.battery_voltage

internal_filesystem/main.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,9 @@
33
import lvgl as lv
44

55
# Allow LVGL M:/path/to/file or M:relative/path/to/file to work for image set_src etc
6-
import fs_driver
6+
import mpos.fs_driver
77
fs_drv = lv.fs_drv_t()
8-
fs_driver.fs_register(fs_drv, 'M')
8+
mpos.fs_driver.fs_register(fs_drv, 'M')
99

1010
import mpos.apps
1111
import mpos.config

0 commit comments

Comments
 (0)