首页 > 其他分享 >把 PySide6 移植到安卓上去!

把 PySide6 移植到安卓上去!

时间:2023-07-28 14:35:59浏览次数:45  
标签:name plat -- python PySide6 path android 到安卓 移植

官方教程在此: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装不上)

然后等它自己下一些依赖(记得开代理),不出意外就行啦

标签:name,plat,--,python,PySide6,path,android,到安卓,移植
From: https://www.cnblogs.com/Ctrl-cCtrl-v/p/17583640.html

相关文章

  • Anaconda-用conda创建python虚拟环境及移植到内网
    conda可以理解为一个工具,也是一个可执行命令,其核心功能是包管理和环境管理。包管理与pip的使用方法类似,环境管理则是允许用户方便滴安装不同版本的python环境并在不同环境之间快速地切换。conda的设计理念conda将几乎所有的工具、第三方包都当作package进行管理,甚至包括python......
  • 可移植性(兼容性)测试指南
    可移植性测试可移植性是指应用程序能够安装到不同的环境中,在不同的环境中使用,甚至可以移动到不同的环境中。当然,前两者对所有系统都很重要。就PC软件而言,鉴于操作系统、共存和互操作应用程序、硬件、带宽可用性等方面的快速变化,能够移动和适应新环境也是至关重要的。在计算机领......
  • ARM平台移植ZLMediaKit
    ZLMediaKit是一套高性能的流媒体服务框架,目前支持rtmp、rtsp、hls、http-flv等流媒体协议,支持linux、macos、windows三大PC平台和ios、android两大移动端平台。host主机:ubuntu18.04移植平台:rk3568交叉编译链版本:gccversion9.3.0https://github.com/ZLMediaKit/ZLMediaKit1,......
  • STM32:rtthread_f1移植
    本文开始移植rtthread的代码到正点原子的板子上;参考资料为野火的教程,需要搭配野火教程使用;使用源码是作为pack包放在arm-keil官网下载的nano3.0.3版本;nano版本精简方便解构;gittee上的master版本组件又多又杂不利于初学;本来想用3.1.5版本源码的,但是移植过程会有代码报错又莫名其......
  • 永磁同步电机pmsm无感foc驱动代码,启动为高频注入,平滑切入观测器高速控制,代码全部手写
    永磁同步电机pmsm无感foc驱动代码,启动为高频注入,平滑切入观测器高速控制,代码全部手写开源,可以移植到各类mcu上。附赠高频注入仿真模型ID:69100646985514964......
  • C#winform软件移植上linux的秘密,用GTK开发System.Windows.Forms
    国产系统大势所趋,如果你公司的winform界面软件需要在linux上运行,如果软件是用C#开发的,现在我有一个好的快速解决方案。世界第一的微软的MicrosoftVisualStudio,确实好用,C#开发起来确实效率高,不过微软的开发语言开发的软件的界面都是跟windows系统绑定的,现在.netcore已......
  • stm32移植FreeRTOS(手动)
    使用软件版本1.cubemxv5.3.02.stm32芯片包:Keil.STM32F4xx_DFP.2.16.0/Keil.STM32F1xx_DFP.2.4.03.FreeRTOS版本:FreeRTOSv202212.004.ARM编译器版本AC5,注意:AC6编译器使用ARMClang编译,本教程将不再适用移植步骤1.FreeRTOSsource文件夹下的如下文件拷贝到keil工程2.在keil......
  • 移植SDL到JZ2440显示BMP图片
    写这类教程的目的是,熟悉Linux基本操作和嵌入式开发流程,希望对你有所帮助. 前面我们讲过系统起来后开机LOGO的制作,韦老师第3期讲了如何显示jpeg图片,那么怎么显示bmp图片?这次我们借助libSDL来实现,我们先移植SDL到Ubuntu,体验它的威力后再移植到开发板。一、移植SDL到Ubun......
  • Pyside6-QtCharts+psutil实战-绘制一个CPU监测工具
    今天是实战篇章,我们结合可以快速提升我们开发效率的工具一起开实战一波实时读取系统CPU使用情况的折线图。使用的开发工具QtDesigner来开发UI界面。十分便捷。使用起来也算比较的简单了,虽然也存在不少的BUG。对所需要的控件进行拖拽式,就OK。后续会出一个简单的视频录制。第二步,......
  • Oracle将用户权限移植到另一个用户上
    问题描述:往往有些需求,A用户依赖于B用户创建,A用户想要获取B用户的权限,oracle没找到有命令可以直接继承,只能写一些语句来代替 1.查询用户下的权限有哪些SETPAGESIZE100SETLINESIZE200COLUMNownerFORMATA20COLUMNtable_nameFORMATA30COLUMNprivilegeFORMATA30......