QPython 中文文档 下载 App

QPython 文件路径问题——FileNotFoundError、PermissionError、硬编码路径踩坑

常见路径报错

在 QPython 中操作文件时最常遇到的两个报错:

FileNotFoundError

脚本说找不到某个文件,但你明明记得文件存在。

PermissionError

脚本说没有权限读写某个路径下的文件。

根本原因

Android 采用沙盒机制——每个 App 只能直接访问自己的私有目录(内部存储)和有限的公共目录(外部存储)。直接写硬编码路径(如 /sdcard/Download/xxx.py)在很多手机上会失败,因为 Android 的存储访问框架(SAF)不让 QPython 直接越过沙盒访问其他 App 的目录。

正确的路径获取方式

方式一:os.getcwd() 看当前工作目录

import os
print(os.getcwd())  # 当前工作目录
print(os.listdir("."))  # 当前目录下有什么

这是最基础也是最重要的一步。先搞清楚脚本当前在哪个目录下运行。

方式二:QSL4A 获取外部存储路径

from androidhelper import Android
droid = Android()
print(droid.getExternalStoragePath())

返回的是 QPython 可以访问的外部存储根路径。

方式三:构造相对路径

最简单可靠的路径策略——永远用相对路径(相对于当前工作目录):

# 好
with open("my_data.csv", "r") as f:
    data = f.read()

# 不推荐
with open("/sdcard/Download/my_data.csv", "r") as f:
    data = f.read()

QEditor 保存的 .py 文件、通过 open() 创建的新文件,默认都在 QPython 的当前工作目录下,相对路径永远能找到它们。

路径问题排查清单

脚本报文件相关错误时,按这个顺序排查:

1. 确认目录存在

import os
print(f"工作目录: {os.getcwd()}")
print(f"目标目录存在: {os.path.exists('目标目录名')}")
print(f"目标文件存在: {os.path.exists('目标文件名')}")

2. 确认权限已授予

读取 / 写入文件需要存储权限。检查:

3. 确认路径格式

Android 路径用 / 分隔(和 Windows 不同):

Python 的 os.path 会自动适配平台,所以构造路径时用 os.path.join:

import os
target_path = os.path.join(os.getcwd(), "subdir", "file.txt")

4. 检查中文文件名

极少数情况下,文件包含中文字符可能导致路径解析问题。如果你怀疑是中文的问题,临时把文件改个英文名字再试。

5. 检查文件是否真的存在于磁盘

有时候 QEditor 里你觉得文件存了,但其实没存成功:

跨目录文件操作

同目录

最简单,直接用文件名:

with open("data.csv", "r") as f:
    data = f.read()

子目录

用相对路径:

import os
# 假设当前目录下有 data/ 子目录
file_path = os.path.join("data", "sensor_20240101.csv")
with open(file_path, "r") as f:
    data = f.read()

外部公共目录(Download、Documents 等)

这需要存储权限(特别是「管理所有文件」)。确认权限后:

download_path = os.path.join("/storage/emulated/0", "Download", "target.csv")
# 或用 QSL4A
from androidhelper import Android
droid = Android()
# SAF 选择文件后会返回可访问的路径

不同 Android 版本对公共目录的限制不同。Android 11+ 对 Download 等目录的限制非常严格,即使有存储权限也不一定能直接读写。

其他 App 的数据

基本不可能。Android 沙盒隔离了每个 App 的数据目录,QPython 不能直接访问微信、QQ、相机等 App 的私有数据。如果需要其他 App 里的数据:

  1. 让那个 App 导出到公共目录(如微信文件传输助手保存到 Download)
  2. 或在 QPython 里用 droid.webViewShow() 打开网页版手动下载
  3. 或通过 QSL4A 的 droid.contentQuery() 查询(某些版本支持)

路径问题快捷诊断脚本

在 REPL 里直接跑,帮你快速看清楚 QPython 能访问哪些路径:

import os
from androidhelper import Android

droid = Android()

print("=== 路径自检 ===")
print(f"1. 当前工作目录: {os.getcwd()}")
print(f"2. 工作目录内容: {os.listdir('.')[:10]}...")  # 前 10 项
print(f"3. 外部存储路径: {droid.getExternalStoragePath()}")
print(f"4. 存储总空间: {droid.getExternalStorageTotalSize()} 字节")
print(f"5. 存储剩余空间: {droid.getExternalStorageAvailableSize()} 字节")

# 测试当前目录是否可写
test_file = "test_write_permission.tmp"
try:
    with open(test_file, "w") as f:
        f.write("test")
    os.remove(test_file)
    print("6. 当前目录可写: YES")
except Exception as e:
    print(f"6. 当前目录可写: NO ({e})")

# 测试能否访问 Download 目录
try:
    dl_path = os.path.join("/storage/emulated/0", "Download")
    os.listdir(dl_path)
    print("7. Download 目录可访问: YES")
except Exception as e:
    print(f"7. Download 目录可访问: NO ({e})")

print("=== 自检完成 ===")

小结

路径问题的根因是 Android 沙盒机制——App 不能随便访问任意路径。最好的策略是永远用相对路径、永远在 QPython 的工作目录和子目录里读写文件。访问外部公共目录(Download 等)需要额外的存储权限,且受 Android 版本影响。跨 App 数据访问基本不可能——需要用导出到公共目录的方式间接获取。

全部 12 组页面 + 首页 + 图片下载到此完成。现在跑渲染校验。