Python兴趣编程百例:教你开发一个拖拽式安卓APK安装工具

发布于:2024-05-31 ⋅ 阅读:(148) ⋅ 点赞:(0)

在进行安卓手机APP测试时,我们经常需要将APP的安装包通过adb装进手机中,这个过程有些繁琐和枯燥。为了提高工作效率,我想是否可以开发一个桌面小工具,让我们能够在界面上通过拖拽安装的方式实现这个过程?答案当然是可以的,本文我将带你使用Python + PyQt + ADB手把手的开发一个安卓APK安卓小工具。

一、需求分析


  1. 创建一个GUI应用程序来安装APK文件。
  2. 显示设备列表,允许用户选择要安装APK文件的设备。
  3. 允许用户通过拖放APK文件或通过选择文件对话框来选择要安装的APK文件。
  4. 在后台线程中执行APK安装操作,并在安装完成时显示提示对话框。
  5. 显示安装状态和错误信息。

二、方案设计


基于以上需求,可以设计以下方案来实现这个APK安装器应用程序:

  1. 使用PyQt5模块创建一个GUI应用程序窗口,并设置窗口的标题、大小和样式。
  2. 在窗口中创建一个QListWidget部件来显示设备列表,并使用QVBoxLayout布局将其添加到窗口中。
  3. 创建QLabel部件来显示相关文本,如"Device List:"和"Drag and drop APK here:"。
  4. 创建一个QPushButton部件作为安装APK的按钮,并将其连接到相应的槽函数。
  5. 创建一个QLabel部件用于显示安装状态信息。
  6. 创建一个自定义的WorkerThread类,继承自QThread,用于在后台线程中执行APK安装操作。该类应包含一个安装完成的信号。
  7. 在WorkerThread类中,实现run函数来执行APK安装操作,并在安装完成时发射安装完成的信号。
  8. 创建一个MainWindow类,继承自QMainWindow,作为主窗口类。在构造函数中,创建各个部件,并设置其样式和布局。
  9. 在MainWindow类中,实现相应的槽函数,如check_adb函数用于检查ADB是否已安装和设置了环境变量,populate_device_list函数用于获取设备列表,install_apk函数用于安装APK文件,以及其他辅助函数。
  10. 将相应的信号与槽函数连接起来,例如将安装按钮的clicked信号连接到install_apk_clicked槽函数。
  11. 重写dragEnterEvent和dropEvent函数,以支持拖放APK文件的操作。
  12. 实现show_info_dialog和show_error_dialog函数,用于显示安装完成和错误的提示对话框。
  13. 在应用程序的主程序中,创建一个QApplication实例,创建MainWindow实例并显示窗口,最后启动应用程序事件循环。

 三、完整代码示例


import sys
from PyQt5.QtWidgets import QApplication, QMainWindow, QListWidget, QLabel, QVBoxLayout, QWidget, QPushButton, \
    QFileDialog, QMessageBox
from PyQt5.QtCore import pyqtSlot, QThread, pyqtSignal
import subprocess

class WorkerThread(QThread):
    finished_signal = pyqtSignal()

    def __init__(self, device=None, apk_path=None):
        super().__init__()
        self.device = device
        self.apk_path = apk_path

    def run(self):
        try:
            subprocess.check_output(['adb', '-s', self.device, 'install', self.apk_path])
            self.finished_signal.emit()
        except subprocess.CalledProcessError as e:
            print(e.output.decode())


class MainWindow(QMainWindow):
    def __init__(self):
        super().__init__()

        self.setWindowTitle("APK Installer")
        self.setGeometry(100, 100, 400, 300)

        self.setStyleSheet("""
                            QMainWindow {
                                background-color: #222; /* 主窗口背景色为深灰色 */
                            }
                            QLabel {
                                color: #fff; /* 白色文字 */
                                font-size: 18px; /* 较大的字体 */
                            }
                            QPushButton {
                                background-color: #007BFF; /* 主色调蓝色 */
                                border: none;
                                color: #fff; /* 白色文字 */
                                padding: 10px 20px;
                                text-align: center;
                                text-decoration: none;
                                display: inline-block;
                                font-size: 16px;
                                margin: 4px 2px;
                                cursor: pointer;
                                border-radius: 5px; /* 圆角按钮 */
                            }
                            QPushButton:hover {
                                background-color: #0056b3; /* 深蓝色悬停效果 */
                            }
                            QPushButton:pressed {
                                background-color: #004080; /* 按下按钮效果 */
                            }
                            /* 为 QMessageBox 设置样式 */
                            QMessageBox {
                                background-color: #fff; /* 设置背景颜色为白色 */
                            }
                            QMessageBox QLabel {
                                color: #000; /* 设置文本颜色为黑色 */
                            }
                        """)
        self.device_list = QListWidget()
        self.device_label = QLabel("Device List:")
        self.apk_label = QLabel("Drag and drop APK here:")
        self.install_button = QPushButton("Install APK")
        self.status_label = QLabel("")

        layout = QVBoxLayout()
        layout.addWidget(self.device_label)
        layout.addWidget(self.device_list)
        layout.addWidget(self.apk_label)
        layout.addWidget(self.install_button)
        layout.addWidget(self.status_label)

        widget = QWidget()
        widget.setLayout(layout)
        self.setCentralWidget(widget)
        self.worker_thread = WorkerThread()
        self.worker_thread.finished_signal.connect(self.show_info_dialog)

        self.install_button.clicked.connect(self.install_apk_clicked)

        self.setAcceptDrops(True)
        self.populate_device_list()

    # 检查adb是否已安装并设置了环境变量
    def check_adb(self):
        try:
            subprocess.check_output(['adb', 'version'])
            return True
        except FileNotFoundError:
            return False


    def install_apk(self, device, apk_path):
        self.status_label.setText("安装进行中...")
        self.worker_thread.device = device
        self.worker_thread.apk_path = apk_path
        self.worker_thread.start()

    def populate_device_list(self):
        if not self.check_adb():
            print("ADB not found!")
            return
        devices = subprocess.check_output(['adb', 'devices']).decode().split('\n')[1:]
        for device in devices:
            if device.strip():
                self.device_list.addItem(device.split('\t')[0])

    @pyqtSlot()
    def install_apk_clicked(self):
        if self.device_list.currentItem() is None:
            print("No device selected!")
            return

        apk_path, _ = QFileDialog.getOpenFileName(self, "Select APK", "", "APK Files (*.apk)")
        if apk_path:
            device = self.device_list.currentItem().text()
            self.install_apk(device, apk_path)

    def dragEnterEvent(self, event):
        if event.mimeData().hasUrls():
            event.accept()
        else:
            event.ignore()

    def dropEvent(self, event):
        for url in event.mimeData().urls():
            file_path = str(url.toLocalFile())
            if file_path.endswith('.apk'):
                self.install_apk_from_path(file_path)

    def install_apk_from_path(self, apk_path):
        if self.device_list.currentItem() is None:
            print("No device selected!")
            return

        device = self.device_list.currentItem().text()
        self.install_apk(device, apk_path)

    def show_info_dialog(self):
        QMessageBox.information(self, '提示', '安装完成')
        self.status_label.setText("安装完成")


    def show_error_dialog(self):
        QMessageBox.critical(self, "错误", "安装失败")


if __name__ == "__main__":
    app = QApplication(sys.argv)
    window = MainWindow()
    window.show()
    sys.exit(app.exec_())

四、效果展示 


 可以看到界面的设备列表列出了当前电脑已连接的设备号

 选中设备号,将电脑上的APK拖拽到界面范围内

 下方显示安装进行中

 

安装完成后会有弹窗提示

五、代码分析


这段代码是一个基于PyQt5的APK安装器。它使用了QtWidgets模块中的各种小部件来创建一个GUI应用程序。以下是对代码的分析:

  1. 导入必要的模块:

    • sys:用于处理系统相关的操作
    • PyQt5.QtWidgets:包含了用于创建GUI应用程序的各种小部件类
    • PyQt5.QtCore:包含了用于处理核心功能的类和函数
    • subprocess:用于在代码中执行外部命令
  2. 创建一个名为WorkerThread的自定义类,继承自QThread。该类用于在后台线程中执行安装APK的操作。它包含一个自定义的finished_signal信号,用于在安装完成时发射信号。

  3. 创建一个名为MainWindow的自定义类,继承自QMainWindow。该类是应用程序的主窗口,用于显示设备列表、APK安装按钮和状态标签等部件。

  4. MainWindow类的构造函数中,设置窗口的标题、大小和样式表。

  5. 创建QListWidgetQLabelQPushButtonQLabel等部件,并使用QVBoxLayout布局将它们添加到窗口中。

  6. 设置部件的样式。

  7. 创建一个WorkerThread实例,并将其finished_signal信号连接到槽函数show_info_dialog

  8. install_button按钮的clicked信号连接到槽函数install_apk_clicked

  9. 启用拖放功能,并重写dragEnterEventdropEvent函数来处理拖放APK文件的操作。

  10. 实现一些辅助函数,例如check_adb用于检查ADB是否已安装和设置了环境变量,populate_device_list用于获取设备列表,install_apk用于安装APK文件。

  11. install_apk_clicked槽函数中,获取选择的APK文件路径,并调用install_apk函数来安装APK文件。

  12. 实现show_info_dialog槽函数,用于显示安装完成的提示对话框。

  13. 实现show_error_dialog槽函数,用于显示安装失败的错误对话框。

  14. __main__块中,创建一个QApplication实例,创建MainWindow实例并显示窗口,最后启动应用程序事件循环。

六、样式分析 


 根据上文代码中的样式美化部分,我们展开进行分析

self.setStyleSheet("""
                            QMainWindow {
                                background-color: #222; /* 主窗口背景色为深灰色 */
                            }
                            QLabel {
                                color: #fff; /* 白色文字 */
                                font-size: 18px; /* 较大的字体 */
                            }
                            QPushButton {
                                background-color: #007BFF; /* 主色调蓝色 */
                                border: none;
                                color: #fff; /* 白色文字 */
                                padding: 10px 20px;
                                text-align: center;
                                text-decoration: none;
                                display: inline-block;
                                font-size: 16px;
                                margin: 4px 2px;
                                cursor: pointer;
                                border-radius: 5px; /* 圆角按钮 */
                            }
                            QPushButton:hover {
                                background-color: #0056b3; /* 深蓝色悬停效果 */
                            }
                            QPushButton:pressed {
                                background-color: #004080; /* 按下按钮效果 */
                            }
                            /* 为 QMessageBox 设置样式 */
                            QMessageBox {
                                background-color: #fff; /* 设置背景颜色为白色 */
                            }
                            QMessageBox QLabel {
                                color: #000; /* 设置文本颜色为黑色 */
                            }
                        """)
  • QMainWindow:设置主窗口的样式,将背景色设置为深灰色(#222)。
  • QLabel:设置标签(Label)的样式,将文字颜色设置为白色(#fff),字体大小为18px。
  • QPushButton:设置按钮(PushButton)的样式,背景色为主色调蓝色(#007BFF),去除按钮边框(border: none),文字颜色为白色,设置内边距为10px上下,20px左右,居中显示文字,禁用文字下划线,设置为内联块级元素,字体大小为16px,设置按钮间的外边距为4px上下,2px左右,设置鼠标悬停时的背景色为深蓝色(#0056b3),设置按钮按下时的背景色为更深的蓝色(#004080),并且设置按钮的边角为5px,使其呈现圆角。
  • QMessageBox:为消息框设置样式,将背景颜色设置为白色(#fff)。
  • QMessageBox QLabel:为消息框中的标签设置样式,将文本颜色设置为黑色(#000)。

这些样式定义了主窗口、标签、按钮和消息框的外观,以提升用户界面的美观度和可用性。

 


网站公告

今日签到

点亮在社区的每一天
去签到