Skip to content

Instantly share code, notes, and snippets.

@roflsunriz
Last active May 3, 2026 08:05
Show Gist options
  • Select an option

  • Save roflsunriz/095dd7369862fce5becd5f93a39957a8 to your computer and use it in GitHub Desktop.

Select an option

Save roflsunriz/095dd7369862fce5becd5f93a39957a8 to your computer and use it in GitHub Desktop.
WSAInstallAPK.pyw : GUI Program that performs installing APK for WSA (Windows Android Subsystem)
import tkinter as tk
from tkinter import scrolledtext, filedialog
import subprocess
import threading
import os
import json
import shutil
# 言語用の辞書の定義
LANGUAGES = {
"ja": {
"title": "Windows Android Subsystem 用 GUI APKインストールツール",
"adb_folder": "adbフォルダの場所:",
"apk_path": "インストールするAPKパス:",
"log_label": "ログ:",
"run": "実行",
"exit": "終了",
"browse": "参照",
"language": "言語選択:",
},
"en": {
"title": "Windows Android Subsystem GUI APK Install Tool",
"adb_folder": "adb Folder Path:",
"apk_path": "APK Path:",
"log_label": "Log:",
"run": "Run",
"exit": "Exit",
"browse": "Browse",
"language": "Language:",
}
}
LOG_MESSAGES = {
"ja": {
"going_to_dir": "指定ディレクトリ: {adb_path} に移動中...",
"error_adb": "エラー: adbパスが存在しません",
"command_execute": "コマンド実行: {cmd}",
"error": "エラー: {error}",
"install_apk": "インストールするAPK: {apk_path}",
"error_apk": "エラー: APKファイルが存在しません",
"exception": "例外発生: {e}",
"adb_in_path": "環境変数PATHからadbを使用しています ({adb_path})",
},
"en": {
"going_to_dir": "Changing current directory to: {adb_path} ...",
"error_adb": "Error: adb folder does not exist!",
"command_execute": "Executing command: {cmd}",
"error": "Error: {error}",
"install_apk": "Installing APK: {apk_path}",
"error_apk": "Error: APK file does not exist!",
"exception": "Exception occurred: {e}",
"adb_in_path": "Using adb from PATH environment variable ({adb_path})",
}
}
# 設定ファイルのパスを変更
SETTINGS_FILE = os.path.join(os.environ["USERPROFILE"], "mini-tools", "WSAInstallAPK", "settings.json")
CONFIG_DIR = os.path.dirname(SETTINGS_FILE)
def load_settings():
"""設定ファイルから設定値を読み込み"""
defaults = {"adb_folder": r"C:\platform-tools", "apk_path": ""}
# 設定ディレクトリが存在しない場合は作成
if not os.path.exists(CONFIG_DIR):
os.makedirs(CONFIG_DIR)
if os.path.isfile(SETTINGS_FILE):
try:
with open(SETTINGS_FILE, "r", encoding="utf-8") as f:
data = json.load(f)
defaults.update(data)
except Exception as e:
print("設定読み込み失敗:", e)
# Check if adb is in PATH
adb_in_path = shutil.which("adb") is not None
return defaults, adb_in_path
def save_settings(adb_folder, apk_path):
"""現在の設定値を設定ファイルに保存"""
data = {"adb_folder": adb_folder, "apk_path": apk_path}
try:
with open(SETTINGS_FILE, "w", encoding="utf-8") as f:
json.dump(data, f, ensure_ascii=False, indent=4)
except Exception as e:
print("設定保存失敗:", e)
def append_log(log_widget, message):
log_widget.configure(state='normal')
log_widget.insert(tk.END, message + "\n")
log_widget.see(tk.END)
log_widget.configure(state='disabled')
def run_commands(adb_path, apk_path, log_widget, run_button, language_var, adb_in_path):
lang = language_var.get()
trans = LOG_MESSAGES[lang]
try:
run_button.config(state=tk.DISABLED)
if adb_in_path:
adb_exe = shutil.which("adb")
append_log(log_widget, trans["adb_in_path"].format(adb_path=adb_exe))
else:
append_log(log_widget, trans["going_to_dir"].format(adb_path=adb_path))
if not os.path.isdir(adb_path):
append_log(log_widget, trans["error_adb"])
run_button.config(state=tk.NORMAL)
return
connect_cmd = ["adb", "connect", "127.0.0.1:58526"]
append_log(log_widget, trans["command_execute"].format(cmd=" ".join(connect_cmd)))
cwd = None if adb_in_path else adb_path
result = subprocess.run(connect_cmd, cwd=cwd, capture_output=True, text=True)
append_log(log_widget, result.stdout)
if result.stderr:
append_log(log_widget, trans["error"].format(error=result.stderr))
append_log(log_widget, trans["install_apk"].format(apk_path=apk_path))
if not os.path.isfile(apk_path):
append_log(log_widget, trans["error_apk"])
run_button.config(state=tk.NORMAL)
return
install_cmd = ["adb", "install", apk_path]
append_log(log_widget, trans["command_execute"].format(cmd=" ".join(install_cmd)))
result = subprocess.run(install_cmd, cwd=cwd, capture_output=True, text=True)
append_log(log_widget, result.stdout)
if result.stderr:
append_log(log_widget, trans["error"].format(error=result.stderr))
except Exception as e:
append_log(log_widget, trans["exception"].format(e=str(e)))
finally:
run_button.config(state=tk.NORMAL)
def select_apk(entry_apk, trans):
filepath = filedialog.askopenfilename(filetypes=[("APK files", "*.apk")])
if filepath:
entry_apk.delete(0, tk.END)
entry_apk.insert(0, filepath)
def select_adb_folder(entry_adb):
"""adbフォルダー選択用のダイアログを開く"""
folder = filedialog.askdirectory()
if folder:
entry_adb.delete(0, tk.END)
entry_adb.insert(0, folder)
def update_language(root, lbl_adb, lbl_apk, lbl_log, btn_run, btn_exit, btn_browse_apk, btn_browse_adb, lbl_lang, language_var, log_text, adb_in_path):
lang = language_var.get()
root.title(LANGUAGES[lang]["title"])
lbl_adb.config(text=LANGUAGES[lang]["adb_folder"])
lbl_apk.config(text=LANGUAGES[lang]["apk_path"])
lbl_log.config(text=LANGUAGES[lang]["log_label"])
btn_run.config(text=LANGUAGES[lang]["run"])
btn_exit.config(text=LANGUAGES[lang]["exit"])
btn_browse_apk.config(text=LANGUAGES[lang]["browse"])
btn_browse_adb.config(text=LANGUAGES[lang]["browse"])
lbl_lang.config(text=LANGUAGES[lang]["language"])
if adb_in_path:
adb_exe = shutil.which("adb")
append_log(log_text, LOG_MESSAGES[lang]["adb_in_path"].format(adb_path=adb_exe))
def main():
root = tk.Tk()
# ここで language_var を生成する
language_var = tk.StringVar(root, value="ja")
settings, adb_in_path = load_settings()
root.geometry("600x400")
root.title(LANGUAGES[language_var.get()]["title"])
frame = tk.Frame(root)
frame.pack(padx=10, pady=10, fill=tk.X)
# 言語選択フレームの追加
lang_frame = tk.Frame(root)
lang_frame.pack(padx=10, pady=5, fill=tk.X)
lbl_lang = tk.Label(lang_frame, text=LANGUAGES[language_var.get()]["language"])
lbl_lang.grid(row=0, column=0, sticky=tk.W)
tk.Radiobutton(lang_frame, text="日本語", variable=language_var, value="ja",
command=lambda: update_language(root, lbl_adb, lbl_apk, lbl_log, btn_run, btn_exit, btn_browse_apk, btn_browse_adb, lbl_lang, language_var, log_text, adb_in_path)).grid(row=0, column=1)
tk.Radiobutton(lang_frame, text="English", variable=language_var, value="en",
command=lambda: update_language(root, lbl_adb, lbl_apk, lbl_log, btn_run, btn_exit, btn_browse_apk, btn_browse_adb, lbl_lang, language_var, log_text, adb_in_path)).grid(row=0, column=2)
# adbフォルダ指定
lbl_adb = tk.Label(frame, text=LANGUAGES[language_var.get()]["adb_folder"])
lbl_adb.grid(row=0, column=0, sticky=tk.W)
entry_adb = tk.Entry(frame, width=50)
entry_adb.grid(row=0, column=1, padx=5, pady=5)
entry_adb.insert(0, settings["adb_folder"])
btn_browse_adb = tk.Button(frame, text=LANGUAGES[language_var.get()]["browse"],
command=lambda: select_adb_folder(entry_adb))
btn_browse_adb.grid(row=0, column=2, padx=5, pady=5)
# adbがPATHにある場合、platform-toolsの入力欄とボタンを無効化
if adb_in_path:
entry_adb.config(state='disabled')
btn_browse_adb.config(state='disabled')
# apk指定
lbl_apk = tk.Label(frame, text=LANGUAGES[language_var.get()]["apk_path"])
lbl_apk.grid(row=1, column=0, sticky=tk.W)
entry_apk = tk.Entry(frame, width=50)
entry_apk.grid(row=1, column=1, padx=5, pady=5)
entry_apk.insert(0, settings["apk_path"])
btn_browse_apk = tk.Button(frame, text=LANGUAGES[language_var.get()]["browse"],
command=lambda: select_apk(entry_apk, LANGUAGES[language_var.get()]))
btn_browse_apk.grid(row=1, column=2, padx=5, pady=5)
# ログエリア
lbl_log = tk.Label(root, text=LANGUAGES[language_var.get()]["log_label"])
lbl_log.pack(anchor=tk.W, padx=10)
log_text = scrolledtext.ScrolledText(root, state='disabled', wrap=tk.WORD, height=15)
log_text.pack(padx=10, pady=5, fill=tk.BOTH, expand=True)
# adbがPATHにある場合はログに出力
if adb_in_path:
adb_exe = shutil.which("adb")
trans = LOG_MESSAGES[language_var.get()]
append_log(log_text, trans["adb_in_path"].format(adb_path=adb_exe))
button_frame = tk.Frame(root)
button_frame.pack(pady=5)
btn_run = tk.Button(button_frame, text=LANGUAGES[language_var.get()]["run"], width=10,
command=lambda: threading.Thread(target=run_commands, args=(
entry_adb.get(), entry_apk.get(), log_text, btn_run, language_var, adb_in_path)).start())
btn_run.grid(row=0, column=0, padx=5)
# 終了時に設定を保存するラッパー関数に変更
def exit_program():
save_settings(entry_adb.get(), entry_apk.get())
root.destroy()
btn_exit = tk.Button(button_frame, text=LANGUAGES[language_var.get()]["exit"], width=10, command=exit_program)
btn_exit.grid(row=0, column=1, padx=5)
root.mainloop()
if __name__ == "__main__":
main()
@roflsunriz

roflsunriz commented Feb 1, 2025 •

Copy link
Copy Markdown
Author

How to use:

  1. Install Python 3.12 via winget
    winget install Python.Python.3.12 --source winget
  2. Reload terminal to apply new environment variables.
  3. Click .pyw file to execute, or type these in powershell (file should be in Download folder):
    Set-Location $env:USERPROFILE\Downloads
    python .\WSAInstallAPK.pyw
  4. Specify platform-tools folder. If you didn't install platform-tools yet, install them with this:
    winget install Google.PlatformTools --source winget
    then re-launch this script.
  5. Specify APK file.
  6. Click "Run" to Install.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment