Note
DISCLAIMER: This guide is unofficial and not affiliated with Qt. Please refer to the Official Documentation about the compiling process for Android.
Taking Qt for Python to Android
Qt for Python v6.8
Important
Compiling PySide6 to Android is a very hard topic. To take yourself away from some pain, read the WHOLE
guide
in all of its details, because I already went through a lot of pain and took some notes for you :)
PySide6 == 6.8.0
- General
- Downloading the Android wheels
- Setup
- Building the Android APK
- Errors and solutions
- RuntimeError
- C-Compiler can not create executables
- python package architecture mismatch
- DeadObjectException
- Module not found error
- DEBUGGING ::: IMPORTANT :::
- Building the Wheels (LEGACY)
Warning
Before proceeding, make sure your app is compatible with either Python3.10.x
or Python3.11.x
Android devices can have one of the four architectures: armv7
, aarch64
, x86_64
, i686
.
You should compile your application for all four of these. This involves using the official pyside6-android-deploy
tool, which will automatically set everything up.
Since Qt Version 6.8
Qt published their own Android wheels, which you need for building. I STRONGLY
recommend you to download them, instead of compiling your own.
However, if you want to compile them by yourself, you can skip to the LEGACY part.
Here's the link to their public archive: https://download.qt.io/official_releases/QtForPython/pyside6/
And here are the links for every release:
Note
Only the aarch64
and x86_64
versions are available at the moment.
I also compile my own wheels, which you can download in the GitHub releases, although there's no guarantee for them to work!
When building the .apk you need the Android SDK and NDK. You can install them manually and skip this section, but to make your life a little bit easier, I recommend using Qt's own tool for that purpose.
Dependencies:
Although you do not need all of them, I recommend installing them:
sudo pacman -Syu base-devel android-tools android-udev clang jdk17-openjdk llvm openssl cmake wget git
cd ~/
git clone https://code.qt.io/pyside/pyside-setup
cd pyside-setup
python3 -m venv venv
source venv/bin/activate
pip install -r requirements.txt
pip install -r tools/cross_compile_android/requirements.txt
python tools/cross_compile_android/main.py --download-only --verbose
Important
Make sure to use the --verbose flag, otherwise it won't show you the License Agreement for the SDK and you won't be able to install the build tools.
After you are finished with this your Android SDK and NDK are in the following directories:
Android SDK: ~/.pyside6_android_deploy/android-sdk
Android NDK: ~/.pyside6_android_deploy/android-ndk/android-ndk-r26b/
Note
Although you do not need this for a base PySide6 application, I still recommend you to read through the following part, because it contains very important things for your Android App.
Buildozer is the tool which generates the .apk file using P4A (Python for Android) as backend. The buildozer.spec file is a configuration file which is generated on Buildozer's first run. It's used to configure the behaviour of your application. For example whether your App should run in portrait or landscape mode and other things. Here's a list of the most important options:
requirements
: The list of all python packages used in your app See Buildozer.specicon.filename
: The Icon of your App as a .png or .jpg. Please have a look at Google's Adaptive Iconstitle
: Your App nameversion
: The version of your application e.g, 1.1android.permissions:
All permissions your App needs. Have a look at Android Permissionspackage.name
: The name of your package, which is the output of buildozerpackage.domain
: The unique identifier of your app inside the Android app systemorientation
: The orientation of your app:portrait
->:||
orlandscape
->:===
If your project uses the 'requests' library or any other library dependent on 'charset_normalizer', ensure to specify
the version charset-normalizer==2.1.1
in your requirements, otherwise there will be an architecture
mismatch.
Unfortunately the pyside6-android-deploy
script starts the build proces immediately, without giving
you the option to manually review the buildozer.spec file, which is the reason why we need to do
a little modification, but don't worry it's easy:
- Go into your virtual environment to the PySide6 folder (e.g: venv/lib/python3.11/site-packages/PySide6/scripts/)
- Edit the
android_deploy.py
file. - Search for the line:
logging.info("[DEPLOY] Running buildozer deployment")
- Above this line write this:
input("Modify your buildozer.spec now")
When you start building the apk with pyside6-android-deploy
the build process will stop at some point,
and then you can make adjustments to the buildozer.spec
file. After you are done, just press enter
the build process, and it will go on using your own options in the requirements.
Important
Make sure your script is named main.py
Go into your source directory and type the following:
`pyside6-android-deploy --wheel-pyside=<your .whl file for the architecture> --wheel-shiboken=<your .whl file for your architecture> --name=main --ndk-path ~/.pyside6_android_deploy/android-ndk/android-ndk-r26b --sdk-path ~/.pyside6_android_deploy/android-sdk/
- --wheel-pyside= Here comes your PySide6 wheel which you've downloaded or compiled
- --wheel-shiboken= Here comes your Shiboken wheel which you've downloaded or compiled
- --name= The name of your application
- --ndk-path= The path to your Android NDK (See Setup)
- --sdk-path= The path to your Android SDK (See Setup)
The script will start configuring buildozer and buildozer will start the build process. At the end you will have a .apk file for the specified Android architecture.
I generally recommend you to use ADB / Fastboot to install, and debug your Android application. Once you understood how it works, it makes your life a lot easier...
-
Go into your system information and tap a few times on your build number
-
Go (or search) into the developer settings
-
Enable USB-Debugging (Or Debugging over W-Lan, but this is a little bit advanced)
-
Install the android-tools on your system:
- On Arch Linux:
sudo pacman -S android-tools
- On Ubuntu:
sudo apt install android-tools-adb android-tools-fastboot
- On Windows:
Imagine using Windows lol
- On Arch Linux:
-
Type:
adb devices
-
On your device there should be a popup asking for your permission. Click on confirm.
-
Type:
adb devices
once again and confirm, that you see your device there.
The two magical commands
Install your apk: adb install <path_to_apk_file>
Debug your app: adb logcat --regex "<package.domain>
After your apk was installed, you will see it in your system apps. Click on your app, scroll down and at the very last line it should say something like:
Version 2.1.6
com.digibites.accubattery
The first line is your App version and the second line is your package domain.
After executing the logcat command, you can start your App and you should see a lot
of debug messages. In case your app crashes, you can see what went wrong, although the crash
report isn't always very helpful...
Note
These instructions are based on my Pixel 7 Pro running Android 14. The steps for you might be different, but in general they are all very similar on all Android devices. If you are stuck somewhere, XDA, Google and StackOverflow are your best friends :D
- RuntimeError: "You are including a lot of QML files from a local venv..." This error is related to your virtual environment. Make sure your virtual environment is NOT inside your projects' folder. It doesn't matter where it is, but must not be in your projects' folder. For example, if you have your main.py in a folder named my_project, then your virtual environment can't be in this folder!
Create a new virtual environment in a different location and delete your old one with `rm -rf venv .venv` (or whatever you've called it)
This is more an issue from Qt and will hopefully be fixed in a later Qt release, so that you don't need to do this anymore. I know it's confusing.
-
c compiler can not create executables Don't worry, this issue most of the time comes because you are using a too high API level. Just go into the path, where it says the C compiler wouldn't be able to create executables and then look inside this directory. You'll find files ending like
androidclang33-
(or something like that). The highest number which is there is the highest number you can select. -
blah blash blah is for x86_64 architecture not aarch64
Solution: Search online if the pip package has a verified aarch64 version. If it doesn't prepare for some months of work building the recipes lmao -
DeadObjectException (Couldn't insert ... into...) If you see this, you can make yourself some coffee, turn on UK-Drill and try to find the error the next few months :) This error can be literally anything but most of the time it's because you are trying to access a resource that doesn't exist. In my case it was a
logging.debug
function which made the whole application crash, because I couldn't access thelog.log
file for some reason. So, if you see the DeadObjectException I recommend youSTRONGLY
to go to Debugging to find and fix the issue. -
No module named <your_module> Sometimes it's not enough to just specify the module name. For example, if you want to use
httpx
you also need to listhttpx
,httpcore
,idna
,certifi
,h11
,sniffio
in the requirements, until it works.
Whenever you get such an error, I recommend you to look into the module and its dependencies and include all the dependencies and the dependencies of the dependencies. It will be some trial and error, but once you have got it working, it will be clear.
Important
THIS SECTION IS VERY IMPORTANT
Your App WILL CRASH on it's first run (except if you are god), so what you do is:
Write this into your Python application:
import http.client
import json
def send_error_log(message):
url = "<your_pc's_ip>:8000" # Don't forget to place the IP of the device your server runs on
endpoint = "/error-log/"
data = json.dumps({"message": message})
headers = {"Content-type": "application/json"}
conn = http.client.HTTPConnection(url)
conn.request("POST", endpoint, data, headers)
This function does not need any dependencies and will 100% work no matter what's wrong with your App. You place
it on top of your function, and then you always use the send_error_log()
call with your message.
For example, you have different functions in your app, and it crashes you log every new line in your Python application
until you can see where it exactly crashed. I know it's a lot of pain, but this is your only option to find the error!
Server Code
from fastapi import FastAPI, HTTPException
from pydantic import BaseModel
class ErrorLog(BaseModel):
message: str
app = FastAPI()
@app.post("/error-log/")
def receive_error_log(error_log: ErrorLog):
print(f"Received error: {error_log.message}")
return {"detail": "Error log received"}
if __name__ == "__main__":
import uvicorn
uvicorn.run(app, host="0.0.0.0", port=8000)
Run this on your PC or Phone, and it will receive the error logs from your Android Application
Requirements: pip install pydantic fastapi uvicorn
Important
I AGAIN want to remind you of using Java version 17! Java 11 is supported by Gradle and even recommended, but not supported by Qt, and version 21 is supported by Qt, but not by the currently used Gradle version. This may change in the future and I will update it accordingly.
If you see Issues in this guide, or you have something to improve it, feel free to open PRs and Issues. I'll respond to them and try to help you as much as possible. (Only for Arch Linux).
Maybe someone of you can make an alternative readme for other distros. Would be nice :)
I appreciate every star on this repo, as it shows me that my work is useful for people, and it keeps me motivated :)
Note
Please note, that this won't be maintained as often and that the following part is NOT perfect and may contain some issues. This is made to get you into the right direction, but shouldn't be seen as a perfect tutorial or guide!
- Log in to your Qt account at Qt
- Download the Qt Online Installer and execute it
- Select "Desktop" and "Android" and install Qt (Should be around ~1.3 Gigabyte downloading)
sudo pacman -Syu base-devel android-tools android-udev clang jdk17-openjdk llvm openssl cmake wget p7zip git
Note
If you want to compile your App using Python3.10 you need to install Python3.10 with shared libraries. You only need this if you want Python3.10
Explanation:
This is the source directory of PySide6. Just execute the bash stuff below. It will install the needed stuff. Execute this one time and REMEMBER where you've cloned this to, as we need this later :)
python -m venv venv # Needed, trust me...
source venv/bin/activate
git clone https://code.qt.io/pyside/pyside-setup
wget https://download.qt.io/development_releases/prebuilt/libclang/libclang-release_140-based-linux-Rhel8.2-gcc9.2-x86_64.7z
7z x libclang-release_140-based-linux-Rhel8.2-gcc9.2-x86_64.7z
export LLVM_INSTALL_DIR=$PWD/libclang
if [ -n "$ZSH_VERSION" ]; then
echo "export LLVM_INSTALL_DIR=$PWD/libclang" >> ~/.zshrc
elif [ -n "$BASH_VERSION" ]; then
echo "export LLVM_INSTALL_DIR=$PWD/libclang" >> ~/.bashrc
fi
cd pyside-setup
pip install -r requirements.txt
pip install -r tools/cross_compile_android/requirements.txt
pip install pyside6
cd
Explanation:
We are going inside the pyside-setup
folder which you've cloned. This folder is the source code of PySide6. The tool for
compiling the wheels is located in tools/cross_compile_android/main.py
.
There are 4 Android architectures:
- aarch64
- armv7a
- x86_64
- i686
You want to build your Application for all of these architectures, so that your App runs on any Android Device including the oldest phone from 11 years ago and a new Android Tablet from 2024 :)
Note
If you want to use Python3.10 instead of Python3.11 modify the main.py script. You'll find a line saying: "PYTHON_VERSION = 3.11". Change this to 3.10. (It's line 21)
Note
If you want to use a different NDK version than r26b, go into tools/cross_compile_android/android_utilities.py
and
change the value in the NDK version to your preferred one (at the top of the script)
Quick Information: You only need to build the wheels once, and you can use them for all your projects. So this is a one time step!
The basic command for building the wheels is the following:
python tools/cross_compile_android/main.py --plat-name= --qt-install-path= --api-level 34 --verbose
Note
If you want to save some time when cloning the Cpython repository, modify the main.py script. Search for: if not cpython_dir.exists():
Under it, you see the Repo.clone_from() call. Add the following argument to it: depth=1
This will only clone the latest commits and branch from the Python repository, otherwise you'll clone a lot of unnecessary data.
Warning
DO NOT REMOVE THE --verbose
flag! The installer redirects all output from the SDK / NDK installation to the logs.
Android will prompt you to accept the license, and you need to type y
and proceed. If you don't use the --verbose line,
you simply won't see this and the script is stuck forever!
--plat-name = Here comes your Android architecture. e.g, aarch64 or armv7a
--qt-install-path = Here comes your Qt installation path. e.g, "/home/$USER/Qt/6.8.0" or "/opt/Qt/6.8.0"
--api-level = Here comes your target android API level. I recommend 34.
Now, execute this command for all 4 Android architectures.
Your Wheels should be in the dist
folder at the end.
If you get any errors, try to use the --clean-cache all
argument first.