curl -fsSL https://Gist.GitHubUserContent.com/GoToLoop/246a31d437aaa8c6eadb7f7186544e0f/raw/thonny-installer.bash -o thonny-installer.bash && chmod +x thonny-installer.bash && ./thonny-installer.bash
Last active
October 11, 2025 03:06
-
-
Save GoToLoop/246a31d437aaa8c6eadb7f7186544e0f to your computer and use it in GitHub Desktop.
Bash script to install Thonny + py5mode on Linux
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #!/usr/bin/env bash | |
| #=============================================================================== | |
| # NAME: Thonny + py5mode Linux Installer | |
| # REQUIREMENT: Python 3.10+, pip, internet connection | |
| # USAGE: ./thonny-installer.bash | |
| # NOTES: Add exec flag 1st: chmod +x ./thonny-installer.bash | |
| # DESCRIPTION: Installs both Thonny IDE + py5mode plugin + py5 full package | |
| # GIST_REPO: https://Gist.GitHub.com/GoToLoop/246a31d437aaa8c6eadb7f7186544e0f | |
| # SUPPORT: https://GitHub.com/py5coding/thonny-py5mode/issues | |
| # AUTHOR: GoToLoop ( https://Discourse.Processing.org/c/28 ) | |
| # VERSION: 1.2.6 | |
| # CREATED: 2025-Sep-02 | |
| # UPDATED: 2025-Sep-10 | |
| # LICENSE: MIT | |
| #=============================================================================== | |
| set -euo pipefail | |
| echo | |
| VERSION=1.2.6 # Version to echo on console for flag --version | |
| VENV_DIR=""; PYTHON="" # Target venv & python respectively | |
| PYTHON_DEFAULT=python # Default Python binary name | |
| ARGS=("$@"); LEN=${#ARGS[@]} # Array of all user's passed args and its length | |
| # Function to parse individual command-line arguments: | |
| parse_argument() { | |
| local key=$1; local val=$2 | |
| case $key in | |
| # Summary of available flags: | |
| --help|-h|'/h'|'/?') | |
| echo -e "Usage: $0 [OPTIONS] [VENV_DIR] [PYTHON]\n" | |
| echo "Options:" | |
| echo " --venv, -v, /v Path to the virtual environment directory" | |
| echo " --python, -p, /p Path or name of an existing Python binary" | |
| echo " --version, -V, /V Show current script version and exit" | |
| echo " --help, -h, /h, /? Show this help message and exit" | |
| exit 0;; | |
| --version|-V|'/V') echo $VERSION; exit 0;; # Display version and exit | |
| --venv|-v|'/v') VENV_DIR=$val;; # Venv target installation folder | |
| --python|-p|'/p') PYTHON=$val;; # Python binary path or name | |
| # Positional fallback: | |
| *) | |
| if [ -z "$VENV_DIR" ]; then | |
| VENV_DIR=$key | |
| elif [ -z "$PYTHON" ]; then | |
| PYTHON=$key | |
| fi | |
| esac | |
| } | |
| # Parse user arguments (named or positional): | |
| for (( i=0; i<LEN; ++i )); do | |
| key=${ARGS[i]}; val="" | |
| if [[ $key == *=* ]]; then # Check if current arg contains `=` | |
| val=${key#*=}; key=${key%%=*} # Handle --flag=value or -f=value | |
| elif (( i + 1 < LEN )); then # Check if it hasn't already reached last arg | |
| val=${ARGS[++i]} # Next arg is the value if key doesn't contain `=` | |
| fi | |
| parse_argument "$key" "$val" | |
| done | |
| # Check if pypi.org pip repo is remotely reachable: | |
| if ! ping -4c 1 pypi.org &> /dev/null; then | |
| echo -e "β οΈ Warning: pypi.org is unreachable! Package install may fail.\n" | |
| fi | |
| # Search the system for existing Python binaries if none provided by argument: | |
| if [ -z "$PYTHON" ]; then | |
| # Get system-wide Python commands from $PATH: | |
| mapfile -t SYSTEM_PYTHONS < <( | |
| compgen -c | grep -E '(^|[-_])python([0-9\.]*)?$' | sort -V | uniq | |
| ) | |
| # Get pyenv-installed Python binaries: | |
| mapfile -t PYENV_PYTHONS < <( | |
| find "$HOME/.pyenv/versions" -type f -executable \ | |
| -regex '.*/bin/python[0-9\.]*$' 2>/dev/null | |
| ) | |
| # Combine, deduplicate and validate: | |
| PYTHON_CANDIDATES=("${SYSTEM_PYTHONS[@]}" "${PYENV_PYTHONS[@]}") | |
| VALID_PYTHONS=() # Initialize array to store valid Python versions | |
| for candidate in "${PYTHON_CANDIDATES[@]}"; do # Validate each candidate | |
| command -v "$candidate" &> /dev/null && VALID_PYTHONS+=("$candidate") | |
| done | |
| # List available Python versions and their index: | |
| if [ ${#VALID_PYTHONS[@]} -gt 0 ]; then # if any valid Python versions found | |
| echo "Available Python interpreters: π" | |
| for i in "${!VALID_PYTHONS[@]}"; do | |
| echo " [$i] ${VALID_PYTHONS[$i]}" | |
| done | |
| msg="\nπ You can enter a full path, a Python name," | |
| echo -e "$msg or just an index number from the list above.\n" | |
| PYTHON_DEFAULT=${VALID_PYTHONS[0]} # Pick 1st valid Python version found | |
| fi | |
| fi | |
| while [ -z "$PYTHON" ]; do # Prompt only if not set via user args... | |
| # Ask for Python executable (default: index [0] python): | |
| read -rei "$PYTHON_DEFAULT" -p "π Python executable path or index: " PYTHON | |
| done | |
| [[ $PYTHON == ~* ]] && PYTHON=${PYTHON/#\~/$HOME} # Expand tilde ~ | |
| # If input is a valid index, convert it to the actual path: | |
| [[ $PYTHON =~ ^[0-9]+$ ]] && ((PYTHON >= 0 && | |
| PYTHON < ${#VALID_PYTHONS[@]})) && PYTHON=${VALID_PYTHONS[$PYTHON]} | |
| echo -e "β Chosen Python for creating venv: $PYTHON\n" | |
| # Validate the final Python path: | |
| if ! (command -v "$PYTHON" &> /dev/null && "$PYTHON" -V &> /dev/null); then | |
| echo "β Invalid Python path: $PYTHON"; exit 1 | |
| fi | |
| while [ -z "$VENV_DIR" ]; do # Prompt only if not set via user args... | |
| # Ask for venv target folder (default: ~/Apps/Thonny/): | |
| read -rei "$HOME/Apps/Thonny" -p "π Virtualenv target folder: " VENV_DIR | |
| done | |
| [[ $VENV_DIR == ~* ]] && VENV_DIR=${VENV_DIR/#\~/$HOME} # Expand tilde ~ | |
| [ "$VENV_DIR" != / ] && VENV_DIR=${VENV_DIR%/} # Remove trailing / | |
| # Extract the parent directory of the virtual environment target: | |
| VENV_DIR_PARENT=$(dirname "$VENV_DIR") | |
| # Define a regex that matches either the root (/) or the user's home directory: | |
| REGEX="^(/|${HOME})$" | |
| # Block installation if the target path or its parent is either / or $HOME | |
| # This prevents accidental installs into critical system or personal folders: | |
| if [[ $VENV_DIR =~ $REGEX || $VENV_DIR_PARENT =~ $REGEX ]]; then | |
| echo -e "\nβ Direct home/root subfolder not allowed: $VENV_DIR" | |
| exit 1 | |
| fi | |
| # Check if target already exists: | |
| if [ -e "$VENV_DIR" ]; then | |
| echo | |
| read -rp "β οΈ Warning: '$VENV_DIR' already exists. Overwrite? [y/N] " YES | |
| [[ ${YES:0:1} =~ ^[YySsOo]$ ]] || { echo "β Aborting..."; exit 1; } | |
| rm -rf "$VENV_DIR" # Delete target folder before creating venv there | |
| fi | |
| # Create the virtual environment: | |
| echo -e "\nπ Creating virtualenv in '$VENV_DIR' using '$PYTHON'..." | |
| "$PYTHON" -m venv --copies "$VENV_DIR" | |
| # Upgrade venv's pip and install Thonny + py5: | |
| echo -e "π¦ Installing thonny + thonny-py5mode...\n" | |
| VENV_BIN="$VENV_DIR/bin"; VENV_PIP="$VENV_BIN/pip" | |
| "$VENV_PIP" install -U pip; echo | |
| "$VENV_PIP" install thonny thonny-py5mode[extras]; echo | |
| # Also, add package pip-review for easily update everything inside venv. | |
| # To use it, while venv is active, type in: pip-review -aC | |
| "$VENV_PIP" install pip-review; echo | |
| # Locate Thonny icon inside the virtual environment's site-packages folder, | |
| # regardless of the specific Python version (e.g., python3.10, python3.13). | |
| # This finds the first matching "thonny.png" file and stores its full path: | |
| ICON_PATH=$(find "$VENV_DIR/lib" -type f \ | |
| -path "*/site-packages/thonny/res/thonny.png" | head -n 1) | |
| # Check if previous icon search resulted in an empty string: | |
| if [ -z "$ICON_PATH" ]; then | |
| echo "πΆ Warning: Thonny icon not found!" | |
| ICON_PATH="/usr/share/icons/hicolor/256x256/apps/org.thonny.Thonny.png" | |
| [ -f $ICON_PATH ] || ICON_PATH=thonny # Try 'thonny' as last recourse. | |
| # If not empty, locally register the found Thonny's icon: | |
| elif command -v xdg-icon-resource &> /dev/null; then | |
| echo -e "πΌοΈ Registering Thonny icon as 'thonny' from: $ICON_PATH\n" | |
| xdg-icon-resource install --size 256 --novendor "$ICON_PATH" thonny | |
| ICON_PATH=thonny # Change icon to the locally registered name 'thonny'. | |
| fi | |
| # Build KDE's ".directory" icon for Thonny's env folder: | |
| cat > "$VENV_DIR/.directory" <<EOF | |
| [Desktop Entry] | |
| Icon=$ICON_PATH | |
| EOF | |
| # Make sure "Desktop" folder exists: | |
| DESKTOP_DIR="$HOME/Desktop" | |
| mkdir -p "$DESKTOP_DIR" | |
| # Build "Thonny.desktop" launcher: | |
| THONNY_LAUNCHER="$DESKTOP_DIR/Thonny.desktop"; THONNY_EXE="$VENV_BIN/thonny" | |
| cat > "$THONNY_LAUNCHER" <<EOF | |
| [Desktop Entry] | |
| Type=Application | |
| Name=Thonny | |
| GenericName=Python IDE | |
| Comment=Run Thonny IDE in isolated environment | |
| Exec=$THONNY_EXE %F | |
| Icon=$ICON_PATH | |
| Terminal=false | |
| StartupWMClass=Thonny | |
| Categories=Development;IDE | |
| Keywords=programming;education | |
| MimeType=text/x-python | |
| Actions=Edit | |
| [Desktop Action Edit] | |
| Exec=$THONNY_EXE %F | |
| Name=Edit with Thonny | |
| EOF | |
| # Make the "Thonny.desktop" launcher file executable: | |
| chmod +x "$THONNY_LAUNCHER" | |
| if command -v gio &> /dev/null; then | |
| gio set "$THONNY_LAUNCHER" metadata::trusted true # Make the launcher trusted | |
| fi | |
| # And also make a copy of "Thonny.desktop" inside Thonny's env folder: | |
| cp -p "$THONNY_LAUNCHER" "$VENV_DIR" | |
| # Build "run-thonny" CLI launcher, using relative path to find Thonny's Python: | |
| THONNY_CLI_RUN="$VENV_DIR/run-thonny" | |
| # Quoting 'EOF' prevents variable expansion within the heredoc operator `<<`. | |
| # Those variables will expand only when "run-thonny" script runs: | |
| cat > "$THONNY_CLI_RUN" <<'EOF' | |
| #!/usr/bin/env bash | |
| # Get the absolute path to the directory containing this running script: | |
| SCRIPT_FOLDER="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" | |
| # Construct the path to the Python executable inside the virtual environment: | |
| PYTHON_EXE="$SCRIPT_FOLDER/bin/python" | |
| # Define the folder where Thonny's log file will be stored: | |
| LOG_FOLDER="$SCRIPT_FOLDER/.thonny" | |
| # Define the full path to the log file: | |
| LOG_FILE="$LOG_FOLDER/thonny.log" | |
| # Create the log directory if it doesn't already exist: | |
| mkdir -p "$LOG_FOLDER" | |
| # Launch Thonny module in the background using the virtual environment's Python. | |
| # Redirect both stdout and stderr to the log file. | |
| # `nohup` allows the process to continue running after the terminal is closed. | |
| # `disown` detaches the process from the shell's job control table: | |
| nohup "$PYTHON_EXE" -m thonny > "$LOG_FILE" 2>&1 & disown | |
| EOF | |
| # Make the "run-thonny" CLI launcher file executable: | |
| chmod +x "$THONNY_CLI_RUN" | |
| # List all installed packages: | |
| "$VENV_PIP" list; echo | |
| # Show further details of the 3 main installed packages: | |
| "$VENV_PIP" show thonny thonny-py5mode py5; echo | |
| echo -e "β Setup complete! You can now launch Thonny from your desktop.\n" | |
| # Check default shell and show the corresponding instruction to activate venv: | |
| case "$SHELL" in | |
| */fish) | |
| echo "π You're using Fish. Run the command below to activate venv:" | |
| echo "source $VENV_BIN/activate.fish";; | |
| *) | |
| echo "π‘ Using Bash or a compatible shell. Run this to activate venv:" | |
| echo "source $VENV_BIN/activate" | |
| esac |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment