如何在Kivy应用中实现安卓、Windows等操作系统的已安装应用列表功能
在Kivy应用中实现跨平台已安装应用列表功能
嘿,这个需求挺实用的!要在Kivy里实现安卓、Windows的已安装应用列表,核心是针对不同系统调用原生接口,再把数据整合到Kivy的UI里。下面分平台给你一步步拆解:
一、核心思路
Kivy本身没有直接提供获取系统应用列表的API,所以咱们需要借助平台专属的Python库,调用系统底层接口:
- 安卓:用
pyjnius调用Android的Java API(PackageManager) - Windows:用
pywin32读取系统注册表,提取应用信息
二、安卓平台实现
1. 依赖准备
首先安装pyjnius:
pip install pyjnius
如果用Buildozer打包,记得在buildozer.spec里添加:
requirements = python3,kivy,pyjnius android.permissions = QUERY_ALL_PACKAGES
注意:Android 11及以上必须申请
QUERY_ALL_PACKAGES权限,否则只能获取与你的应用有交互的应用列表。
2. 获取应用列表的代码
from jnius import autoclass, cast from kivy.utils import platform def get_installed_apps_android(): if platform != 'android': return [] PackageManager = autoclass('android.content.pm.PackageManager') Context = autoclass('android.content.Context') PythonActivity = autoclass('org.kivy.android.PythonActivity') context = cast('android.content.Context', PythonActivity.mActivity) pm = context.getPackageManager() # 获取所有已安装应用(包括系统应用) apps = pm.getInstalledApplications(PackageManager.GET_META_DATA) app_list = [] for app in apps: app_info = { 'name': pm.getApplicationLabel(app), 'package_name': app.packageName, 'icon': pm.getApplicationIcon(app) # 这里返回的是Android的Drawable对象 } app_list.append(app_info) return app_list
3. 把安卓图标转成Kivy可用的Texture
Android的Drawable需要转换成Kivy的Texture才能显示:
from kivy.graphics.texture import Texture from PIL import Image import io def android_icon_to_texture(drawable): # 将Drawable转成Bitmap Bitmap = autoclass('android.graphics.Bitmap') bitmap = drawable.getBitmap() # 把Bitmap转成字节流 byte_array_output_stream = autoclass('java.io.ByteArrayOutputStream')() bitmap.compress(Bitmap.CompressFormat.PNG, 100, byte_array_output_stream) byte_array = byte_array_output_stream.toByteArray() # 用PIL读取字节流,再转成Kivy Texture img = Image.open(io.BytesIO(byte_array)) texture = Texture.create(size=(img.width, img.height)) texture.blit_buffer(img.tobytes(), colorfmt='rgba', bufferfmt='ubyte') return texture
三、Windows平台实现
1. 依赖准备
安装pywin32:
pip install pywin32
2. 获取应用列表的代码
Windows的已安装应用信息存在注册表中,咱们需要遍历几个关键路径:
import winreg from kivy.utils import platform import os def get_installed_apps_windows(): if platform != 'win': return [] app_list = [] # 注册表路径:系统级和用户级的已安装应用 reg_paths = [ r"SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall", r"SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall" ] for reg_path in reg_paths: try: with winreg.OpenKey(winreg.HKEY_LOCAL_MACHINE, reg_path) as key: i = 0 while True: try: subkey_name = winreg.EnumKey(key, i) with winreg.OpenKey(key, subkey_name) as subkey: # 提取应用名称 name = winreg.QueryValueEx(subkey, "DisplayName")[0] if "DisplayName" in [v[0] for v in winreg.EnumValue(subkey)] else subkey_name # 提取图标路径 icon_path = winreg.QueryValueEx(subkey, "DisplayIcon")[0] if "DisplayIcon" in [v[0] for v in winreg.EnumValue(subkey)] else None # 处理图标路径(有些会带参数,只取前面的exe路径) if icon_path: icon_path = icon_path.split(',')[0].strip('"') if not os.path.exists(icon_path): icon_path = None app_list.append({ 'name': name, 'icon_path': icon_path, 'uninstall_string': winreg.QueryValueEx(subkey, "UninstallString")[0] if "UninstallString" in [v[0] for v in winreg.EnumValue(subkey)] else None }) i += 1 except OSError: break except WindowsError: continue # 去重(有些应用会在多个注册表路径出现) unique_apps = [] seen_names = set() for app in app_list: if app['name'] not in seen_names: seen_names.add(app['name']) unique_apps.append(app) return unique_apps
3. 加载Windows应用图标到Kivy
用PIL加载图标文件,转成Texture:
from kivy.graphics.texture import Texture from PIL import Image def windows_icon_to_texture(icon_path): if not icon_path: return None try: img = Image.open(icon_path) texture = Texture.create(size=(img.width, img.height)) texture.blit_buffer(img.tobytes(), colorfmt='rgba', bufferfmt='ubyte') return texture except Exception as e: print(f"加载图标失败:{e}") return None
四、整合到Kivy应用UI
咱们用RecycleView来展示应用列表,这是Kivy里高效展示大量数据的组件:
from kivy.app import App from kivy.uix.recycleview import RecycleView from kivy.uix.recycleview.views import RecycleDataViewBehavior from kivy.uix.label import Label from kivy.uix.boxlayout import BoxLayout from kivy.properties import StringProperty, ObjectProperty from kivy.utils import platform class AppItem(RecycleDataViewBehavior, BoxLayout): name = StringProperty("") icon_texture = ObjectProperty(None) class AppRecycleView(RecycleView): def __init__(self, **kwargs): super(AppRecycleView, self).__init__(**kwargs) self.load_apps() def load_apps(self): app_data = [] if platform == 'android': apps = get_installed_apps_android() for app in apps: texture = android_icon_to_texture(app['icon']) app_data.append({ 'name': str(app['name']), 'icon_texture': texture }) elif platform == 'win': apps = get_installed_apps_windows() for app in apps: texture = windows_icon_to_texture(app['icon_path']) app_data.append({ 'name': app['name'], 'icon_texture': texture }) self.data = app_data class AppManagerApp(App): def build(self): return AppRecycleView() if __name__ == '__main__': AppManagerApp().run()
对应的KV文件(命名为appmanager.kv):
<AppItem>: orientation: 'horizontal' size_hint_y: None height: 50 Image: size_hint_x: None width: 40 texture: root.icon_texture allow_stretch: True Label: text: root.name valign: 'middle' <AppRecycleView>: viewclass: 'AppItem' RecycleBoxLayout: default_size: None, 50 default_size_hint: 1, None size_hint_y: None height: self.minimum_height orientation: 'vertical'
五、注意事项
- 跨平台判断:一定要用
kivy.utils.platform来检测当前系统,避免调用错误的平台代码。 - 打包问题:
- 安卓:用Buildozer打包,确保
buildozer.spec里正确配置了pyjnius和权限。 - Windows:用PyInstaller打包时,要把
pywin32的相关库包含进去,或者用--hidden-import win32api、--hidden-import winreg参数。
- 安卓:用Buildozer打包,确保
- 性能优化:获取应用列表可能需要一点时间,建议在子线程里执行,避免UI卡顿。
- 权限限制:安卓11+必须申请
QUERY_ALL_PACKAGES,否则只能获取部分应用;Windows读取注册表大部分普通应用信息不需要管理员权限,但系统级应用可能需要。
内容的提问来源于stack exchange,提问作者Thomas Shelby




