把 PySide6 移植到安卓上去!
官方教程在此:https://www.qt.io/blog/taking-qt-for-python-to-android
寥寥几句,其实不少坑。凭回忆写的,可能不是很全(无招胜有招)
仅支持 Linux 环境
QT 环境安装
从 http://mirrors.ustc.edu.cn/qtproject/official_releases/online_installers/ 下载在线安装器。
新版本的安装器(4.0.1-1 后)支持 --mirror
命令行参数。在命令行中执行安装器,添加 --mirror https://mirrors.ustc.edu.cn/qtproject
参数。例如 Windows 下执行当前目录的安装器的命令为 .\qt-unified-windows-x86-online.exe --mirror https://mirrors.ustc.edu.cn/qtproject
;
有亿点大,装这些就行了(2.22G):
然后要安装 llvm
安装安卓开发环境
安装JDK 11或更高版本
下载 Command line tools only
挂一个 我的下载链接
然新建一个 bash 脚本,只需通过授予脚本执行权限(chmod +x)并将下载的.zip文件的路径作为CLI参数传递即可运行该脚本。
安装
#!/bin/bash
shopt -s extglob
if [ -z "$1" ]
then
echo "Supply path to commandlinetools-linux-*_latest.zip"
exit 1
fi
android_sdk=$(pwd)/android_sdk
mkdir -p $android_sdk
unzip $1 -d $android_sdk
latest=$android_sdk/cmdline-tools/latest
mkdir -p $latest
mv $android_sdk/cmdline-tools/!(latest) $latest
$latest/bin/sdkmanager "ndk;25.2.9519653" "build-tools;33.0.2" "platforms;android-31" "platform-tools"
cp -avr $android_sdk/cmdline-tools/latest $android_sdk/tools
将Android SDK和NDK路径添加到以下环境变量中,
export ANDROID_SDK_ROOT=“android_sdk”
export ANDROID_NDK_ROOT=“android_sdk/ndk/25.2.9519653”
交叉编译Qt for Python wheels for Android
如果你懒的编译也可以用我编译好的
先 clone 一份 pyside-setup 或者直接 clone 我修改过的
装一下依赖
cd pyside-setup/
pip install –r tools/cross_compile_android/requirements.txt
pip install –r requirements.txt
然后跑
python tools/cross_compile_android/main.py --plat-name=aarch64 --ndk-path=$ANDROID_NDK_ROOT --qt-install-path=第一步的QT安装位置 --sdk-path=$ANDROID_SDK_ROOT -v
排错小妙招
因为官方的编译目录使用 tempfile.TemporaryDirectory()
创建,导致整个编译目录跑完就没了(是的,日志也没了)
如果你刚开始没多久就崩了,又找不到日志,可以使用我修改过的 main.py
(不会删日志)
修改后
# Copyright (C) 2023 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
import sys, os
import logging
import argparse
import tempfile
import subprocess
import stat
import warnings
from dataclasses import dataclass
from typing import List
from pathlib import Path
from git import Repo, RemoteProgress
from tqdm import tqdm
from jinja2 import Environment, FileSystemLoader
# Note: Does not work with PyEnv. Your Host Python should contain openssl.
PYTHON_VERSION = "3.10"
@dataclass
class PlatformData:
plat_name: str
api_level: str
android_abi: str
qt_plat_name: str
gcc_march: str
plat_bits: str
def occp_exists():
'''
check if '--only-cross-compile-python' exists in command line arguments
'''
return "-occp" in sys.argv or "--only-cross-compile-python" in sys.argv
class CloneProgress(RemoteProgress):
def __init__(self):
super().__init__()
self.pbar = tqdm()
def update(self, op_code, cur_count, max_count=None, message=""):
self.pbar.total = max_count
self.pbar.n = cur_count
self.pbar.refresh()
def run_command(command: List[str], cwd: str = None, ignore_fail: bool = False,
dry_run: bool = False):
if dry_run:
print(" ".join(command))
return
ex = subprocess.call(command, cwd=cwd)
if ex != 0 and not ignore_fail:
sys.exit(ex)
if __name__ == "__main__":
parser = argparse.ArgumentParser(
description="This tool cross builds cpython for android and uses that Python to cross build"
"android Qt for Python wheels",
formatter_class=argparse.RawTextHelpFormatter,
)
parser.add_argument("-p", "--plat-name", type=str, required=True,
choices=["aarch64", "armv7a", "i686", "x86_64"],
help="Android target platform name")
parser.add_argument("-v", "--verbose", help="run in verbose mode", action="store_const",
dest="loglevel", const=logging.INFO)
parser.add_argument("--api-level", type=str, default="31", help="Android API level to use")
parser.add_argument("--ndk-path", type=str, required=True,
help="Path to Android NDK (Preferred 25b)")
# sdk path is needed to compile all the Qt Java Acitivity files into Qt6AndroidBindings.jar
parser.add_argument("--sdk-path", type=str, required=True,
help="Path to Android SDK")
parser.add_argument("--qt-install-path", type=str, required=not occp_exists(),
help="Qt installation path eg: /home/Qt/6.5.0")
parser.add_argument("-occp", "--only-cross-compile-python", action="store_true",
help="Only cross compiles Python for the specified Android platform")
parser.add_argument("-apic", "--android-python-install-path", type=str, default=None,
required=occp_exists(),
help='''
Points to the installation path of Python for the specific Android
platform. If the path given does not exist, then Python for android
is cross compiled for the specific platform and installed into this
path as <path>/Python-'plat_name'/_install.
If this path is not given, then Python for android is cross-compiled
into a temportary directory, which is deleted when the Qt for Python
android wheels are created.
''')
parser.add_argument("--dry-run", action="store_true", help="show the commands to be run")
args = parser.parse_args()
logging.basicConfig(level=args.loglevel)
pyside_setup_dir = Path(__file__).parents[2].resolve()
qt_install_path = args.qt_install_path
ndk_path = args.ndk_path
sdk_path = args.sdk_path
only_py_cross_compile = args.only_cross_compile_python
python_path = args.android_python_install_path
# the same android platforms are named differently in CMake, Cpython and Qt.
# Hence, we need to distinguish them
qt_plat_name = None
android_abi = None
gcc_march = None
plat_bits = None
dry_run = args.dry_run
# python path is valid, if Python for android installation exists in python_path
valid_python_path = True
if python_path and Path(python_path).exists():
expected_dirs = ["lib", "include"]
for expected_dir in expected_dirs:
if not (Path(python_path) / expected_dir).is_dir():
valid_python_path = False
warnings.warn(
"Given target Python, given through --android-python-install-path does not"
"contain Python. New Python for android will be cross compiled and installed"
"in this directory"
)
break
templates_path = Path(__file__).parent / "templates"
plat_name = args.plat_name
api_level = args.api_level
# for armv7a the API level dependent binaries like clang are named
# armv7a-linux-androideabi27-clang, as opposed to other platforms which
# are named like x86_64-linux-android27-clang
platform_data = None
if plat_name == "armv7a":
platform_data = PlatformData("armv7a", f"eabi{api_level}", "armeabi-v7a", "armv7", "armv7",
"32")
elif plat_name == "aarch64":
platform_data = PlatformData("aarch64", api_level, "arm64-v8a", "arm64_v8a", "armv8-a", "64")
elif plat_name == "i686":
platform_data = PlatformData("i686", api_level, "x86", "x86", "i686", "32")
else: # plat_name is x86_64
platform_data = PlatformData("x86_64", api_level, "x86_64", "x86_64", "x86-64", "64")
# clone cpython and checkout 3.10
# with tempfile.TemporaryDirectory() as temp_dir:
environment = Environment(loader=FileSystemLoader(templates_path))
temp_dir = Path('/tmp/下载/build_dir')
logging.info(f"temp dir created at {temp_dir}")
if not python_path or not valid_python_path:
cpython_dir = temp_dir / "cpython"
python_ccompile_script = cpython_dir / "cross_compile.sh"
logging.info(f"cloning cpython {PYTHON_VERSION}")
if not os.path.exists(temp_dir):
Repo.clone_from(
"https://github.com/python/cpython.git",
cpython_dir,
progress=CloneProgress(),
branch=PYTHON_VERSION,
)
if not python_path:
android_py_install_path_prefix = temp_dir
else:
android_py_install_path_prefix = python_path
# use jinja2 to create cross_compile.sh script
template = environment.get_template("cross_compile.tmpl.sh")
content = template.render(
plat_name=platform_data.plat_name,
ndk_path=ndk_path,
api_level=platform_data.api_level,
android_py_install_path_prefix=android_py_install_path_prefix,
)
logging.info(f"Writing Python cross compile script into {python_ccompile_script}")
with open(python_ccompile_script, mode="w", encoding="utf-8") as ccompile_script:
ccompile_script.write(content)
# give run permission to cross compile script
python_ccompile_script.chmod(python_ccompile_script.stat().st_mode | stat.S_IEXEC)
# run the cross compile script
logging.info(f"Running Python cross-compile for platform {platform_data.plat_name}")
run_command(["./cross_compile.sh"], cwd=cpython_dir, dry_run=dry_run)
python_path = (f"{android_py_install_path_prefix}/Python-{platform_data.plat_name}-linux-android/"
"_install")
# run patchelf to change the SONAME of libpython from libpython3.x.so.1.0 to
# libpython3.x.so, to match with python_for_android's Python library. Otherwise,
# the Qfp binaries won't be able to link to Python
run_command(["patchelf", "--set-soname", f"libpython{PYTHON_VERSION}.so",
f"libpython{PYTHON_VERSION}.so.1.0"], cwd=Path(python_path) / "lib")
logging.info(
f"Cross compile Python for Android platform {platform_data.plat_name}. "
f"Final installation in "
f"{python_path}"
)
if only_py_cross_compile:
sys.exit(0)
qfp_toolchain = temp_dir / f"toolchain_{platform_data.plat_name}.cmake"
template = environment.get_template("toolchain_default.tmpl.cmake")
content = template.render(
ndk_path=ndk_path,
sdk_path=sdk_path,
api_level=platform_data.api_level,
qt_install_path=qt_install_path,
plat_name=platform_data.plat_name,
android_abi=platform_data.android_abi,
qt_plat_name=platform_data.qt_plat_name,
gcc_march=platform_data.gcc_march,
plat_bits=platform_data.plat_bits,
python_version=PYTHON_VERSION,
target_python_path=python_path
)
logging.info(f"Writing Qt for Python toolchain file into"
f"{qfp_toolchain}")
with open(qfp_toolchain, mode="w", encoding="utf-8") as ccompile_script:
ccompile_script.write(content)
# give run permission to cross compile script
qfp_toolchain.chmod(qfp_toolchain.stat().st_mode | stat.S_IEXEC)
# run the cross compile script
logging.info(f"Running Qt for Python cross-compile for platform {platform_data.plat_name}")
qfp_ccompile_cmd = [sys.executable, "setup.py", "bdist_wheel", "--parallel=9",
"--ignore-git", "--standalone", "--limited-api=yes",
f"--cmake-toolchain-file={str(qfp_toolchain.resolve())}",
f"--qt-host-path={qt_install_path}/gcc_64",
f"--plat-name=android_{platform_data.plat_name}",
f"--python-target-path={python_path}",
(f"--qt-target-path={qt_install_path}/"
f"android_{platform_data.qt_plat_name}"),
"--no-qt-tools", "--build-docs"]
run_command(qfp_ccompile_cmd, cwd=pyside_setup_dir, dry_run=dry_run)
如果你用了我的程序,日志保存在 /tmp/build_dir/config.log
解决 source does not exist 问题(文档未生成)
打开文档生成,在文件结尾改一下
qfp_ccompile_cmd = [sys.executable, "setup.py", "bdist_wheel", "--parallel=9",
"--ignore-git", "--standalone", "--limited-api=yes",
f"--cmake-toolchain-file={str(qfp_toolchain.resolve())}",
f"--qt-host-path={qt_install_path}/gcc_64",
f"--plat-name=android_{platform_data.plat_name}",
f"--python-target-path={python_path}",
(f"--qt-target-path={qt_install_path}/"
f"android_{platform_data.qt_plat_name}"),
"--no-qt-tools","--build-docs"]
装一下 sphinx
pip install sphinx
pip install sphinx_design
pip install sphinx_copybutton myst_parser
invalid command 'egg_info'
这个不知道是不是我电脑的问题
打开 setuptools
编辑:
(实际上还是有零零散散不少问题,不知道是不是我环境的问题,有问题来问我吧)
这一步跑完之后你会得到两个文件 PySide6-6.5.2-6.5.2-cp37-abi3-android_aarch64.whl
shiboken6-6.5.2-6.5.2-cp37-abi3-android_aarch64.whl
,并且你应该可以直接使用 pyside6-android-deploy
命令了
生成 APK
这是最简单的一步啦,建一个文件夹,把你要测试的文件( main.py
)、PySide6-6.5.2-6.5.2-cp37-abi3-android_aarch64.whl
shiboken6-6.5.2-6.5.2-cp37-abi3-android_aarch64.whl
放在同文件夹下,直接跑 pyside6-android-deploy --wheel-pyside=PySide6-6.5.2-6.5.2-cp37-abi3-android_aarch64.whl --wheel-shiboken=shiboken6-6.5.2-6.5.2-cp37-abi3-android_aarch64.whl --name=test
如果你遇到了这样的错误
应该是 sh
库的锅,
装 pip install sh==1.14.2
然后装我修改过的 python_for_android
(把 sh 的依赖删了因为 1.14.3
装不上)
然后等它自己下一些依赖(记得开代理),不出意外就行啦
把 PySide6 移植到安卓上去!的更多相关文章
- cocos2d移植到安卓引入第三方so文件时候编译会删除解决方式
在游戏中对接支付的SDK的时候引入支付的so文件的时候在编译的时候总是被删除,后来经过查找资料自己整理出了一个解决方式 方案例如以下 在项目导入安卓中之后.在相应的jni目录中创建一个prebuilt ...
- 移植Iperf到android 用来学习linux移植到安卓的例子
Iperf移植记录 1.生成arm编译需要的头文件config.h ./configure --host=arm如果需要make clean make distclean2.增加Android.mk文 ...
- 单片机modebus RTU通信实现,採用C语言,可适用于单片机,VC,安卓等
当前使用的是STM32+ucos_ii编写的,能够移植到安卓以及VC .NET等方便移植使用,採用modebus poll測试过. 仅仅须要改动响应的通信接口就可以,方便多串口使用 //modebus ...
- Facebook 开源安卓版 React Native,开发者可将相同代码用于网页和 iOS 应用开发
转自:http://mt.sohu.com/20150915/n421177212.shtml Facebook 创建了React Java 库,这样,Facebook 的工程团队就可以用相同的代码给 ...
- Cocos2d-x 3.x部署到安卓
一.前期准备 下载下列软件: Python2.7 (https://www.python.org/downloads/) Cocos2d-x 3.x (http://www.cocos2d-x.org ...
- Android学习——移植tr069程序到Android平台
原创作品,转载请注明出处,严禁非法转载.如有错误,请留言! email:40879506@qq.com 声明:本系列涉及的开源程序代码学习和研究,严禁用于商业目的. 如有任何问题,欢迎和我交流.(企鹅 ...
- cocos2d-x 3.0 WIN7+VS2012 安卓平台搭建
***************************************转载请注明出处:http://blog.csdn.net/lttree************************** ...
- QML 从无到有 2 (移动适配)
随着项目深入,需要移植到安卓上,问题来了,QML安卓适配! 幸好PC端程序和手机屏幕长宽比例相似.虽然单位像素,尺寸不同,通过比例缩放,可以实现组件PC和安卓通用代码. 第一步:定义全局的转换函数(3 ...
- Surprise团队项目总结
Surprise团队项目总结 项目实现情况 实现人人模式:2个用户在同一台电脑上进行切磋下棋,即实现五子棋游戏的基本功能 实现人机模式:初级模式已经实现,可以进行人机交互,但是还没达到智能判断下棋点 ...
- 【使用Unity开发Windows Phone上的2D游戏】(1)千里之行始于足下
写在前面的 其实这个名字起得不太欠当,Unity本身是很强大的工具,可以部署到很多个平台,而不仅仅是可以开发Windows Phone上的游戏. 只不过本人是Windows Phone 应用开发出身, ...
随机推荐
- 腾讯元宝登顶App Store免费榜榜首!国产AI APP混战升级
最近,国产AI APP市场热闹非凡,竞争愈发激烈.其中,腾讯元宝的表现格外亮眼,它在短短一周内,借助微信的强大助力,一路逆袭,成功登上苹果App Store免费榜榜首.这一变化不仅展现了腾讯在AI领域 ...
- mysql : 第5章 数据库的安全性
-- 创建用户CREATE USER utest@localhost IDENTIFIED BY 'temp';-- 查看所有用户SELECT * FROM mysql.user;-- 查看表级权限S ...
- win32绘图:绘制直线 矩形 圆形 曲线等
查看代码 #include <Windows.h> //画点 void PaintSetPixel(HDC hdc) { for (short i = 0; i < 20; i++) ...
- Manus的开源复刻OpenManus初探
OpenManus介绍 Manus需要邀请码才能体验,目前大部分人都体验不到. 有几个大佬花3个小时就复现了一个简单的原型OpenManus,让我们体验体验吧!! 截至目前,该项目已经获得了25.9k ...
- vue2中如何使用组合式API和vueuse工具包
vue2中如何使用组合式API和vueuse工具包 1. 安装 @vue/composition-api 依赖包 yarn add @vue/composition-api # 或 npm insta ...
- go 结构体根据某个字段进行排序
前言 在任何编程语言中,关乎到数据的排序都会有对应的策略,我们来看下 Golang 是怎样对数据进行排序,以及我们如何优化处理使用 go 排序 go 可以针对任何对象排序,虽然很多情况下是一个 sli ...
- Java SE 24 新增特性
Java SE 24 新增特性 作者:Grey 原文地址: 博客园:Java SE 24 新增特性 CSDN:Java SE 24 新增特性 源码 源仓库: Github:java_new_featu ...
- 关于IPMP
国际项目经理资质认证(International Project Manager Professional,简称IPMP)是国际项目管理协会(International Project Managem ...
- 冒泡排序--java进阶day06
1.冒泡排序 https://kdocs.cn/l/ciMkwngvaWfz?linkname=150996835 我们会发现上图排序的样子非常像之前打印的倒三角,所以,冒泡排序也需要使用循环嵌套 2 ...
- 【Linux】3.4 用户管理
用户管理 1. 基本用户管理 Linux系统是一个多用户多任务的分时操作系统,任何一个要使用系统资源的用户,都必须首先向系统管理员申请一个账号,然后以这个账号的身份进入系统. 用户的账号一方面可以帮助 ...