Skip to content

Instantly share code, notes, and snippets.

@roflsunriz
Last active March 14, 2025 03:38
Show Gist options
  • Save roflsunriz/095dd7369862fce5becd5f93a39957a8 to your computer and use it in GitHub Desktop.
Save roflsunriz/095dd7369862fce5becd5f93a39957a8 to your computer and use it in GitHub Desktop.
WSAInstallAPK.py : GUI Program that performs installing APK for WSA (Windows Android Subsystem)
import sys
import os
import json
import subprocess
import threading
from PyQt6.QtWidgets import (QApplication, QMainWindow, QWidget, QVBoxLayout,
QHBoxLayout, QLabel, QLineEdit, QPushButton,
QTextEdit, QRadioButton, QFrame, QMessageBox,
QFileDialog)
from PyQt6.QtCore import Qt, QPoint, QRectF
from PyQt6.QtGui import QPalette, QColor, QPainter, QPainterPath
import pkg_resources
import subprocess
import sys
def install_required_packages():
required = {'PyQt6'}
installed = {pkg.key for pkg in pkg_resources.working_set}
missing = required - installed
if missing:
python = sys.executable
subprocess.check_call([python, '-m', 'pip', 'install', *missing], stdout=subprocess.DEVNULL)
# 必要なパッケージをインストール
install_required_packages()
# 言語用の辞書の定義
LANGUAGES = {
"ja": {
"title": "Windows Android Subsystem 用 GUI実行ツール のじゃ",
"adb_folder": "adbフォルダの場所:",
"apk_path": "インストールするAPKパス:",
"log_label": "ログ:",
"run": "実行",
"exit": "終了",
"browse": "参照",
"language": "言語選択:",
},
"en": {
"title": "Windows Android Subsystem GUI 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}",
},
"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}",
}
}
# 設定ファイルのパスを変更
CONFIG_DIR = os.path.join(os.getcwd(), "misc", "config")
SETTINGS_FILE = os.path.join(CONFIG_DIR, "wsainstallapk_config.json")
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)
return defaults
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.append(message + "\n")
def run_commands(adb_path, apk_path, log_widget, run_button, language):
"""コマンドを実行するのじゃ"""
trans = LOG_MESSAGES[language]
try:
run_button.setEnabled(False)
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.setEnabled(True)
return
connect_cmd = ["adb", "connect", "127.0.0.1:58526"]
append_log(log_widget, trans["command_execute"].format(cmd=" ".join(connect_cmd)))
result = subprocess.run(connect_cmd, cwd=adb_path, 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.setEnabled(True)
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=adb_path, 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.setEnabled(True)
def select_apk(entry_apk):
"""APKファイル選択用のダイアログを開くのじゃ"""
filepath, _ = QFileDialog.getOpenFileName(
None,
"APKファイルを選択するのじゃ",
"",
"APKファイル (*.apk)"
)
if filepath:
entry_apk.setText(filepath)
def select_adb_folder(entry_adb):
"""adbフォルダー選択用のダイアログを開くのじゃ"""
folder = QFileDialog.getExistingDirectory(
None,
"adbフォルダを選択するのじゃ",
""
)
if folder:
entry_adb.setText(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):
lang = language_var.get()
root.setWindowTitle(LANGUAGES[lang]["title"])
lbl_adb.setText(LANGUAGES[lang]["adb_folder"])
lbl_apk.setText(LANGUAGES[lang]["apk_path"])
lbl_log.setText(LANGUAGES[lang]["log_label"])
btn_run.setText(LANGUAGES[lang]["run"])
btn_exit.setText(LANGUAGES[lang]["exit"])
btn_browse_apk.setText(LANGUAGES[lang]["browse"])
btn_browse_adb.setText(LANGUAGES[lang]["browse"])
lbl_lang.setText(LANGUAGES[lang]["language"])
class CustomTitleBar(QWidget):
def __init__(self, parent=None):
super().__init__(parent)
self.parent = parent
layout = QHBoxLayout()
layout.setContentsMargins(0, 0, 0, 0)
self.title = QLabel(LANGUAGES["ja"]["title"])
self.title.setStyleSheet("color: #333; font-weight: bold;")
layout.addWidget(self.title)
layout.addStretch()
self.minimize_button = QPushButton("-")
self.close_button = QPushButton("×")
for button in (self.minimize_button, self.close_button):
button.setFixedSize(45, 30)
button.setStyleSheet("""
QPushButton {
background-color: transparent;
border: none;
color: #555;
}
QPushButton:hover {
background-color: rgba(0, 0, 0, 0.1);
}
""")
self.minimize_button.clicked.connect(self.parent.showMinimized)
self.close_button.clicked.connect(self.parent.close)
layout.addWidget(self.minimize_button)
layout.addWidget(self.close_button)
self.setLayout(layout)
def mousePressEvent(self, event):
if event.button() == Qt.MouseButton.LeftButton:
self.parent.drag_position = event.globalPosition().toPoint() - self.parent.frameGeometry().topLeft()
event.accept()
def mouseMoveEvent(self, event):
if event.buttons() == Qt.MouseButton.LeftButton:
self.parent.move(event.globalPosition().toPoint() - self.parent.drag_position)
event.accept()
class MainWindow(QMainWindow):
def __init__(self):
super().__init__()
self.drag_position = QPoint()
self.setWindowFlags(Qt.WindowType.FramelessWindowHint)
self.setAttribute(Qt.WidgetAttribute.WA_TranslucentBackground)
self.current_language = "ja" # 現在の言語を保持
# メインウィジェットの設定
main_widget = QWidget()
self.setCentralWidget(main_widget)
# メインレイアウト
layout = QVBoxLayout(main_widget)
layout.setContentsMargins(0, 0, 0, 0)
# カスタムタイトルバーの追加
self.title_bar = CustomTitleBar(self)
layout.addWidget(self.title_bar)
# コンテンツエリア
content = QWidget()
content.setObjectName("contentArea")
content_layout = QVBoxLayout(content)
# 言語選択
lang_frame = QFrame()
lang_layout = QHBoxLayout(lang_frame)
lang_label = QLabel(LANGUAGES["ja"]["language"])
self.ja_radio = QRadioButton("日本語")
self.en_radio = QRadioButton("English")
self.ja_radio.setChecked(True)
# 言語切り替えイベントの設定
self.ja_radio.toggled.connect(lambda: self.change_language("ja"))
self.en_radio.toggled.connect(lambda: self.change_language("en"))
lang_layout.addWidget(lang_label)
lang_layout.addWidget(self.ja_radio)
lang_layout.addWidget(self.en_radio)
content_layout.addWidget(lang_frame)
# 入力フィールド
self.adb_folder_edit = QLineEdit()
self.apk_path_edit = QLineEdit()
for label_text, line_edit, button_text in [
(LANGUAGES["ja"]["adb_folder"], self.adb_folder_edit, LANGUAGES["ja"]["browse"]),
(LANGUAGES["ja"]["apk_path"], self.apk_path_edit, LANGUAGES["ja"]["browse"])
]:
frame = QFrame()
frame_layout = QHBoxLayout(frame)
label = QLabel(label_text)
browse_button = QPushButton(button_text)
frame_layout.addWidget(label)
frame_layout.addWidget(line_edit)
frame_layout.addWidget(browse_button)
content_layout.addWidget(frame)
# ブラウズボタンのクリックイベントを設定
if line_edit == self.adb_folder_edit:
browse_button.clicked.connect(lambda: select_adb_folder(self.adb_folder_edit))
else:
browse_button.clicked.connect(lambda: select_apk(self.apk_path_edit))
# 設定の読み込み
settings = load_settings()
self.adb_folder_edit.setText(settings["adb_folder"])
self.apk_path_edit.setText(settings["apk_path"])
# ログエリア
log_label = QLabel(LANGUAGES["ja"]["log_label"])
self.log_text = QTextEdit()
self.log_text.setReadOnly(True)
content_layout.addWidget(log_label)
content_layout.addWidget(self.log_text)
# ボタンエリア
button_frame = QFrame()
button_layout = QHBoxLayout(button_frame)
self.run_button = QPushButton(LANGUAGES["ja"]["run"])
self.exit_button = QPushButton(LANGUAGES["ja"]["exit"])
# 実行ボタンのクリックイベントを設定
self.run_button.clicked.connect(self.execute_commands)
self.exit_button.clicked.connect(self.close_application)
button_layout.addWidget(self.run_button)
button_layout.addWidget(self.exit_button)
content_layout.addWidget(button_frame)
layout.addWidget(content)
# スタイル設定
self.setStyleSheet("""
QMainWindow {
background: transparent;
}
#contentArea {
background: rgba(240, 249, 255, 0.95);
border-radius: 10px;
border: 1px solid rgba(0, 120, 212, 0.3);
padding: 20px;
}
QFrame {
background: transparent;
}
QLabel {
color: #333333;
font-size: 12px;
}
QRadioButton {
color: #333333;
font-size: 12px;
spacing: 5px;
}
QRadioButton::indicator {
width: 13px;
height: 13px;
}
QRadioButton::indicator:checked {
background-color: #0078D4;
border: 2px solid #FFFFFF;
border-radius: 7px;
}
QRadioButton::indicator:unchecked {
background-color: #FFFFFF;
border: 2px solid #999999;
border-radius: 7px;
}
QPushButton {
background: qlineargradient(x1:0, y1:0, x2:0, y2:1,
stop:0 #f0f0f0, stop:1 #e5e5e5);
border: 1px solid #ccc;
border-radius: 4px;
padding: 5px 15px;
color: #333;
font-size: 12px;
}
QPushButton:hover {
background: qlineargradient(x1:0, y1:0, x2:0, y2:1,
stop:0 #e5e5e5, stop:1 #d5d5d5);
}
QLineEdit {
border: 1px solid #aaa;
border-radius: 4px;
padding: 5px;
background: rgba(255, 255, 255, 0.8);
color: #333333;
font-size: 12px;
}
QTextEdit {
border: 1px solid #aaa;
border-radius: 4px;
background: rgba(255, 255, 255, 0.8);
color: #333333;
font-size: 12px;
padding: 5px;
}
""")
self.setMinimumSize(800, 600)
self.setWindowTitle(LANGUAGES["ja"]["title"])
def paintEvent(self, event):
painter = QPainter(self)
painter.setRenderHint(QPainter.RenderHint.Antialiasing)
path = QPainterPath()
rect = QRectF(self.rect()) # QRectをQRectFに変換
path.addRoundedRect(rect, 10, 10)
painter.fillPath(path, QColor(255, 255, 255, 245))
def execute_commands(self):
"""実行ボタンが押されたときの処理なのじゃ"""
adb_path = self.adb_folder_edit.text()
apk_path = self.apk_path_edit.text()
# 別スレッドで実行
threading.Thread(target=run_commands, args=(
adb_path,
apk_path,
self.log_text,
self.run_button,
self.current_language
)).start()
def close_application(self):
"""終了ボタンが押されたときの処理なのじゃ"""
# 設定を保存
save_settings(self.adb_folder_edit.text(), self.apk_path_edit.text())
self.close()
def change_language(self, lang):
"""言語を切り替えるのじゃ"""
if not self.ja_radio.isChecked() and not self.en_radio.isChecked():
return
self.current_language = lang
# ウィンドウタイトルの更新
self.setWindowTitle(LANGUAGES[lang]["title"])
self.title_bar.title.setText(LANGUAGES[lang]["title"])
# 各ラベルとボタンのテキストを更新
for frame in self.findChildren(QFrame):
for child in frame.children():
if isinstance(child, QLabel):
if "adb_folder" in LANGUAGES[lang].values() and child.text() in LANGUAGES["ja"].values():
child.setText(LANGUAGES[lang]["adb_folder"])
elif "apk_path" in LANGUAGES[lang].values() and child.text() in LANGUAGES["ja"].values():
child.setText(LANGUAGES[lang]["apk_path"])
elif "log_label" in LANGUAGES[lang].values() and child.text() in LANGUAGES["ja"].values():
child.setText(LANGUAGES[lang]["log_label"])
elif "language" in LANGUAGES[lang].values() and child.text() in LANGUAGES["ja"].values():
child.setText(LANGUAGES[lang]["language"])
elif isinstance(child, QPushButton):
if child.text() == LANGUAGES["ja"]["browse"]:
child.setText(LANGUAGES[lang]["browse"])
elif child.text() == LANGUAGES["ja"]["run"]:
child.setText(LANGUAGES[lang]["run"])
elif child.text() == LANGUAGES["ja"]["exit"]:
child.setText(LANGUAGES[lang]["exit"])
def main():
app = QApplication(sys.argv)
window = MainWindow()
window.show()
sys.exit(app.exec())
if __name__ == "__main__":
main()
@roflsunriz
Copy link
Author

roflsunriz commented Feb 1, 2025

How to use:

  1. Install latest python.
  2. double click .pyw file.
  3. Specify platform-tools folder.
  4. Specify APK file.
  5. Run. This installs APK.

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