├── 示例图片 ├── maa主界面A.png ├── maa主界面B.png └── MAA_bin图片.png ├── main.py ├── requirements.txt ├── LICENSE ├── README.md ├── tool.py ├── .gitignore ├── ui.py └── control.py /示例图片/maa主界面A.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/overflow65537/Tkinter_MAA-GUI/HEAD/示例图片/maa主界面A.png -------------------------------------------------------------------------------- /示例图片/maa主界面B.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/overflow65537/Tkinter_MAA-GUI/HEAD/示例图片/maa主界面B.png -------------------------------------------------------------------------------- /示例图片/MAA_bin图片.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/overflow65537/Tkinter_MAA-GUI/HEAD/示例图片/MAA_bin图片.png -------------------------------------------------------------------------------- /main.py: -------------------------------------------------------------------------------- 1 | # 导入布局文件 2 | from ui import Win as MainWin 3 | # 导入窗口控制器 4 | from control import Controller as MainUIController 5 | # 将窗口控制器 传递给UI 6 | app = MainWin(MainUIController()) 7 | if __name__ == "__main__": 8 | # 启动 9 | app.mainloop() -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | certifi==2024.2.2 2 | chardet==5.2.0 3 | charset_normalizer==3.3.2 4 | concurrencytest==0.1.2 5 | cryptography==43.0.0 6 | idna==3.7 7 | mock==5.1.0 8 | pyOpenSSL==24.2.1 9 | pywin32==306 10 | simplejson==3.19.2 11 | urllib3==2.2.2 12 | wheel==0.43.0 13 | wmi==1.5.1 14 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2024 overflow65537 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # MAA_Tkinter 2 | 一个基于Tkinter的MAAFW-GUI项目 3 | 4 | ***项目已存档*** 5 | 6 | 新项目:https://github.com/overflow65537/MFW-PyQt6 7 | 8 | ![image](/示例图片/maa主界面A.png) 9 | ![image](/示例图片/maa主界面B.png) 10 | # 使用方式 11 | - 下载[最新版本](https://github.com/overflow65537/Tkinter_MAA-GUI/releases) 并解压 12 | - 下载需要启动的maapicli程序 13 | - 将程序放入MAA_bin文件夹 14 | - 完成后MAA_bin文件夹应该和图片一样 15 | - ![image](/示例图片/MAA_bin图片.png) 16 | - 返回MAA-GUI文件夹 17 | - 启动MAA-GUI.exe 18 | - 输入ADB端口和ADB地址并回车保存(无法输入小数点可以复制进去) 19 | - 输入任务 20 | - 开始运行 21 | 22 | # config.json设置 23 | - ```url```:填写项目的更新地址,填写后才可使用自动更新.例如```{"url":"https://api.github.com/repos/作者名/项目名/releases/latest"}``` ***必填*** 24 | - ```tag_name```:填写本地项目的版本号,例如```"tag_name": "v1.8.4"``` 更新后会自动生成 25 | - ```startapp```:填写启动app,使用后会自动生成 26 | - ```startapp_p```填写启动app参数,使用后会自动生成 27 | - ```"startapp_w```填写启动app等待时间,使用后会自动生成 28 | # 视频教程 29 | - https://bilibili.com/video/BV1ZBbJeSEHF 30 | # 任务进度 31 | - [x] ADB路径和端口的编辑和保存 32 | - [x] 客户端类型的初始显示,列表中内容的显示以及编辑和保存 33 | - [x] 任务类型选项卡的初始显示,列表中内容的显示,根据任务需求显示更多选项以及正常选择 34 | - [x] 添加任务,开始任务,上移,下移,删除按钮的正常使用 35 | - [x] 任务列表的动态更新 36 | - [x] 任务列表会根据任务的要求显示更多选项 37 | - [x] 检测到第一次使用时自动创建MAA配置文件 38 | - [x] 添加自动更新 39 | - [x] 添加自动寻找adb设备并自动填写adb信息 40 | - [x] 添加启动模拟器功能 41 | - [ ] 为每个游戏添加独立的配置文件 42 | - [ ] 开始任务后会在GUI界面实时显示maapicli的输出内容(黑框应该也不影响使用) 43 | -------------------------------------------------------------------------------- /tool.py: -------------------------------------------------------------------------------- 1 | import os 2 | import json 3 | import psutil 4 | import socket 5 | 6 | def Read_Config(path): 7 | if not os.path.exists(os.getcwd()+"\\MAA_bin\\config\\maa_pi_config.json"): 8 | os.makedirs(os.getcwd()+"\\MAA_bin\\config\\") 9 | date = {"adb": {"adb_path":"请在此处填写ADB路径,回车确定","address":"请在此处填写ADB端口,回车确定","config": {}},"controller": {"name": "安卓端","type": "Adb"},"resource":"官服","task":[]} 10 | with open(os.getcwd()+"\\MAA_bin\\config\\maa_pi_config.json","w",encoding='utf-8') as MAA_Config: 11 | json.dump(date,MAA_Config,indent=4,ensure_ascii=False) 12 | with open(path,"r",encoding='utf-8') as MAA_Config: 13 | # 打开json并传入MAA_data 14 | MAA_data = json.load(MAA_Config) 15 | return MAA_data 16 | 17 | def Save_Config(path,date): 18 | # 打开json并写入data内数据 19 | with open(path,"w",encoding='utf-8') as MAA_Config: 20 | json.dump(date,MAA_Config,indent=4,ensure_ascii=False) 21 | 22 | def Get_Values_list2(path,key1): 23 | List = [] 24 | for i in Read_Config(path)[key1]: 25 | List.append(i) 26 | return List 27 | 28 | def Get_Values_list(path,key1): 29 | #获取组件的初始参数 30 | List = [] 31 | for i in Read_Config(path)[key1]: 32 | List.append(i["name"]) 33 | return List 34 | 35 | def Get_Values_list_Option(path,key1): 36 | #获取组件的初始参数 37 | List = [] 38 | for i in Read_Config(path)[key1]: 39 | if i["option"]!=[]: 40 | Option_text = str(i["name"])+" " 41 | Option_Lens = len(i["option"]) 42 | for t in range(0,Option_Lens,1): 43 | Option_text+=str(i["option"][t]["value"])+" " 44 | List.append(Option_text) 45 | else: 46 | List.append(i["name"]) 47 | return List 48 | 49 | def Get_Task_List(target): 50 | #输入option名称来输出一个包含所有该option中所有cases的name列表 51 | #具体逻辑为 interface.json文件/option键/选项名称/cases键/键为空,所以通过len计算长度来选择最后一个/name键 52 | lists = [] 53 | Task_Config = Read_Config(os.getcwd()+"\\MAA_bin\\interface.json")["option"][target]["cases"] 54 | Lens = len(Task_Config)-1 55 | for i in range(Lens,-1,-1): 56 | lists.append(Task_Config[i]["name"]) 57 | return lists 58 | def find_process_by_name(process_name): 59 | # 遍历所有程序找到指定程序 60 | for proc in psutil.process_iter(["name","exe"]): 61 | try: 62 | if proc.info["name"].lower() == process_name.lower(): 63 | #如果一样返回可执行文件的绝对路径 64 | return proc.info["exe"] 65 | except: 66 | return False 67 | def find_existing_file(info_dict): 68 | #输入一个包含可执行文件的绝对路径和可能存在ADB的相对路径,输出ADB文件的绝对地路径 69 | exe_path = info_dict.get("exe_path").rsplit("\\",1)[0] 70 | may_paths = info_dict.get("may_path", []) 71 | if not exe_path or not may_paths: 72 | return False 73 | 74 | # 遍历所有可能的相对路径 75 | for path in may_paths: 76 | 77 | # 拼接完整路径 78 | full_path = os.path.join(exe_path, path) 79 | # 检查文件是否存在 80 | if os.path.exists(full_path): 81 | return full_path 82 | 83 | # 如果没有找到任何存在的文件 84 | return False 85 | def check_port(port): 86 | port_result = [] 87 | for p in port: 88 | p = int(p.rsplit(":",1)[1]) 89 | s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 90 | try: 91 | # 尝试连接到127.0.0.1的指定端口 92 | result = s.connect_ex(('127.0.0.1', p)) 93 | # 如果connect_ex返回0,表示连接成功,即端口开启 94 | if result == 0: 95 | port_result.append("127.0.0.1:"+str(p)) 96 | except socket.error: 97 | pass 98 | finally: 99 | s.close() 100 | return port_result -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # costom 2 | /MAA_bin 3 | /json 4 | /zipfile 5 | /tkinter 6 | /requests 7 | /psutil 8 | # Byte-compiled / optimized / DLL files 9 | __pycache__/ 10 | *.py[cod] 11 | *$py.class 12 | 13 | # C extensions 14 | *.so 15 | 16 | # Distribution / packaging 17 | .Python 18 | build/ 19 | develop-eggs/ 20 | dist/ 21 | downloads/ 22 | eggs/ 23 | .eggs/ 24 | lib/ 25 | lib64/ 26 | parts/ 27 | sdist/ 28 | var/ 29 | wheels/ 30 | share/python-wheels/ 31 | *.egg-info/ 32 | .installed.cfg 33 | *.egg 34 | MANIFEST 35 | 36 | # PyInstaller 37 | # Usually these files are written by a python script from a template 38 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 39 | *.manifest 40 | *.spec 41 | 42 | # Installer logs 43 | pip-log.txt 44 | pip-delete-this-directory.txt 45 | 46 | # Unit test / coverage reports 47 | htmlcov/ 48 | .tox/ 49 | .nox/ 50 | .coverage 51 | .coverage.* 52 | .cache 53 | nosetests.xml 54 | coverage.xml 55 | *.cover 56 | *.py,cover 57 | .hypothesis/ 58 | .pytest_cache/ 59 | cover/ 60 | 61 | # Translations 62 | *.mo 63 | *.pot 64 | 65 | # Django stuff: 66 | *.log 67 | local_settings.py 68 | db.sqlite3 69 | db.sqlite3-journal 70 | 71 | # Flask stuff: 72 | instance/ 73 | .webassets-cache 74 | 75 | # Scrapy stuff: 76 | .scrapy 77 | 78 | # Sphinx documentation 79 | docs/_build/ 80 | 81 | # PyBuilder 82 | .pybuilder/ 83 | target/ 84 | 85 | # Jupyter Notebook 86 | .ipynb_checkpoints 87 | 88 | # IPython 89 | profile_default/ 90 | ipython_config.py 91 | 92 | # pyenv 93 | # For a library or package, you might want to ignore these files since the code is 94 | # intended to run in multiple environments; otherwise, check them in: 95 | # .python-version 96 | 97 | # pipenv 98 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. 99 | # However, in case of collaboration, if having platform-specific dependencies or dependencies 100 | # having no cross-platform support, pipenv may install dependencies that don't work, or not 101 | # install all needed dependencies. 102 | #Pipfile.lock 103 | 104 | # poetry 105 | # Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control. 106 | # This is especially recommended for binary packages to ensure reproducibility, and is more 107 | # commonly ignored for libraries. 108 | # https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control 109 | #poetry.lock 110 | 111 | # pdm 112 | # Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control. 113 | #pdm.lock 114 | # pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it 115 | # in version control. 116 | # https://pdm.fming.dev/latest/usage/project/#working-with-version-control 117 | .pdm.toml 118 | .pdm-python 119 | .pdm-build/ 120 | 121 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm 122 | __pypackages__/ 123 | 124 | # Celery stuff 125 | celerybeat-schedule 126 | celerybeat.pid 127 | 128 | # SageMath parsed files 129 | *.sage.py 130 | 131 | # Environments 132 | .env 133 | .venv 134 | env/ 135 | venv/ 136 | ENV/ 137 | env.bak/ 138 | venv.bak/ 139 | 140 | # Spyder project settings 141 | .spyderproject 142 | .spyproject 143 | 144 | # Rope project settings 145 | .ropeproject 146 | 147 | # mkdocs documentation 148 | /site 149 | 150 | # mypy 151 | .mypy_cache/ 152 | .dmypy.json 153 | dmypy.json 154 | 155 | # Pyre type checker 156 | .pyre/ 157 | 158 | # pytype static type analyzer 159 | .pytype/ 160 | 161 | # Cython debug symbols 162 | cython_debug/ 163 | 164 | # PyCharm 165 | # JetBrains specific template is maintained in a separate JetBrains.gitignore that can 166 | # be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore 167 | # and can be added to the global gitignore or merged into this file. For a more nuclear 168 | # option (not recommended) you can uncomment the following to ignore the entire idea folder. 169 | #.idea/ 170 | subprocess.py 171 | os.py 172 | shutil.py 173 | config.json 174 | threading.py 175 | socket.py 176 | -------------------------------------------------------------------------------- /ui.py: -------------------------------------------------------------------------------- 1 | from tool import * 2 | from tkinter import * 3 | from tkinter.ttk import * 4 | class WinGUI(Tk): 5 | def __init__(self): 6 | super().__init__() 7 | self.__win() 8 | self.tk_tabs_Main_Tabs = self.__tk_tabs_Main_Tabs(self) 9 | self.tk_label_Stable_loca = self.__tk_label_Stable_loca( self.tk_tabs_Main_Tabs_1) 10 | self.tk_button_Check_Update_Button = self.__tk_button_Check_Update_Button( self.tk_tabs_Main_Tabs_1) 11 | self.tk_label_Stable_online = self.__tk_label_Stable_online( self.tk_tabs_Main_Tabs_1) 12 | self.tk_button_Update_button = self.__tk_button_Update_button( self.tk_tabs_Main_Tabs_1) 13 | self.tk_label_frame_ADB_Setting_Frame = self.__tk_label_frame_ADB_Setting_Frame( self.tk_tabs_Main_Tabs_1) 14 | self.tk_label_ADB_Part_Label = self.__tk_label_ADB_Part_Label( self.tk_label_frame_ADB_Setting_Frame) 15 | self.tk_label_ADB_Address_Label = self.__tk_label_ADB_Address_Label( self.tk_label_frame_ADB_Setting_Frame) 16 | self.tk_button_Auto_Detect_ADB_Button = self.__tk_button_Auto_Detect_ADB_Button( self.tk_label_frame_ADB_Setting_Frame) 17 | self.tk_input_ADB_Address_Input = self.__tk_input_ADB_Address_Input( self.tk_label_frame_ADB_Setting_Frame) 18 | self.tk_input_ADB_Path_Input = self.__tk_input_ADB_Path_Input( self.tk_label_frame_ADB_Setting_Frame) 19 | self.tk_select_box_Auto_Detect_ADB_Select = self.__tk_select_box_Auto_Detect_ADB_Select( self.tk_label_frame_ADB_Setting_Frame) 20 | self.tk_label_Controller_Type_Label = self.__tk_label_Controller_Type_Label( self.tk_label_frame_ADB_Setting_Frame) 21 | self.tk_label_Resource_Type_Label = self.__tk_label_Resource_Type_Label( self.tk_label_frame_ADB_Setting_Frame) 22 | self.tk_select_box_Controller_Type_Select = self.__tk_select_box_Controller_Type_Select( self.tk_label_frame_ADB_Setting_Frame) 23 | self.tk_select_box_Resource_Type_Select = self.__tk_select_box_Resource_Type_Select( self.tk_label_frame_ADB_Setting_Frame) 24 | self.tk_label_StartApp = self.__tk_label_StartApp( self.tk_label_frame_ADB_Setting_Frame) 25 | self.tk_input_StartAPP_Address = self.__tk_input_StartAPP_Address( self.tk_label_frame_ADB_Setting_Frame) 26 | self.tk_input_StartAPP_Address_P = self.__tk_input_StartAPP_Address_P( self.tk_label_frame_ADB_Setting_Frame) 27 | self.tk_label_StartAPP_P = self.__tk_label_StartAPP_P( self.tk_label_frame_ADB_Setting_Frame) 28 | self.tk_label_frame_Task_List_Frame = self.__tk_label_frame_Task_List_Frame( self.tk_tabs_Main_Tabs_0) 29 | self.tk_list_box_Task_List = self.__tk_list_box_Task_List( self.tk_label_frame_Task_List_Frame) 30 | self.tk_label_frame_Select_Task_Frame = self.__tk_label_frame_Select_Task_Frame( self.tk_tabs_Main_Tabs_0) 31 | self.tk_label_Add_Task_Label_1 = self.__tk_label_Add_Task_Label_1( self.tk_label_frame_Select_Task_Frame) 32 | self.tk_label_Add_Task_Label_3 = self.__tk_label_Add_Task_Label_3( self.tk_label_frame_Select_Task_Frame) 33 | self.tk_label_Add_Task_Label_2 = self.__tk_label_Add_Task_Label_2( self.tk_label_frame_Select_Task_Frame) 34 | self.tk_select_box_Add_Task_Select = self.__tk_select_box_Add_Task_Select( self.tk_label_frame_Select_Task_Frame) 35 | self.tk_select_box_Add_Task_Select_2 = self.__tk_select_box_Add_Task_Select_2( self.tk_label_frame_Select_Task_Frame) 36 | self.tk_select_box_Add_Task_Select_1 = self.__tk_select_box_Add_Task_Select_1( self.tk_label_frame_Select_Task_Frame) 37 | self.tk_select_box_Add_Task_Select_3 = self.__tk_select_box_Add_Task_Select_3( self.tk_label_frame_Select_Task_Frame) 38 | self.tk_select_box_Add_Task_Select_4 = self.__tk_select_box_Add_Task_Select_4( self.tk_label_frame_Select_Task_Frame) 39 | self.tk_label_Add_Task_Label_4 = self.__tk_label_Add_Task_Label_4( self.tk_label_frame_Select_Task_Frame) 40 | self.tk_label_Add_Task_Label = self.__tk_label_Add_Task_Label( self.tk_label_frame_Select_Task_Frame) 41 | self.tk_frame_Button_Frame = self.__tk_frame_Button_Frame( self.tk_tabs_Main_Tabs_0) 42 | self.tk_button_Delete_Button = self.__tk_button_Delete_Button( self.tk_frame_Button_Frame) 43 | self.tk_button_Move_Down_Button = self.__tk_button_Move_Down_Button( self.tk_frame_Button_Frame) 44 | self.tk_button_Start_Task = self.__tk_button_Start_Task( self.tk_frame_Button_Frame) 45 | self.tk_button_Add_Task_Button = self.__tk_button_Add_Task_Button( self.tk_frame_Button_Frame) 46 | self.tk_button_Move_Up_Button = self.__tk_button_Move_Up_Button( self.tk_frame_Button_Frame) 47 | self.tk_label_Tpis_Setting = self.__tk_label_Tpis_Setting( self.tk_label_frame_ADB_Setting_Frame) 48 | self.tk_input_StartAPP_wait = self.__tk_input_StartAPP_wait( self.tk_label_frame_ADB_Setting_Frame) 49 | 50 | def __win(self): 51 | self.title("MAA-GUI") 52 | # 设置窗口大小、居中 53 | width = 800 54 | height = 460 55 | screenwidth = self.winfo_screenwidth() 56 | screenheight = self.winfo_screenheight() 57 | geometry = '%dx%d+%d+%d' % (width, height, (screenwidth - width) / 2, (screenheight - height) / 2) 58 | self.geometry(geometry) 59 | 60 | self.resizable(width=False, height=False) 61 | 62 | def scrollbar_autohide(self,vbar, hbar, widget): 63 | """自动隐藏滚动条""" 64 | def show(): 65 | if vbar: vbar.lift(widget) 66 | if hbar: hbar.lift(widget) 67 | def hide(): 68 | if vbar: vbar.lower(widget) 69 | if hbar: hbar.lower(widget) 70 | hide() 71 | widget.bind("", lambda e: show()) 72 | if vbar: vbar.bind("", lambda e: show()) 73 | if vbar: vbar.bind("", lambda e: hide()) 74 | if hbar: hbar.bind("", lambda e: show()) 75 | if hbar: hbar.bind("", lambda e: hide()) 76 | widget.bind("", lambda e: hide()) 77 | 78 | def v_scrollbar(self,vbar, widget, x, y, w, h, pw, ph): 79 | widget.configure(yscrollcommand=vbar.set) 80 | vbar.config(command=widget.yview) 81 | vbar.place(relx=(w + x) / pw, rely=y / ph, relheight=h / ph, anchor='ne') 82 | def h_scrollbar(self,hbar, widget, x, y, w, h, pw, ph): 83 | widget.configure(xscrollcommand=hbar.set) 84 | hbar.config(command=widget.xview) 85 | hbar.place(relx=x / pw, rely=(y + h) / ph, relwidth=w / pw, anchor='sw') 86 | def create_bar(self,master, widget,is_vbar,is_hbar, x, y, w, h, pw, ph): 87 | vbar, hbar = None, None 88 | if is_vbar: 89 | vbar = Scrollbar(master) 90 | self.v_scrollbar(vbar, widget, x, y, w, h, pw, ph) 91 | if is_hbar: 92 | hbar = Scrollbar(master, orient="horizontal") 93 | self.h_scrollbar(hbar, widget, x, y, w, h, pw, ph) 94 | self.scrollbar_autohide(vbar, hbar, widget) 95 | def __tk_tabs_Main_Tabs(self,parent): 96 | Style().configure('TNotebook', tabposition='wn') 97 | frame = Notebook(parent,style='TNotebook') 98 | self.tk_tabs_Main_Tabs_0 = self.__tk_frame_Main_Tabs_0(frame) 99 | frame.add(self.tk_tabs_Main_Tabs_0, text="主页") 100 | self.tk_tabs_Main_Tabs_1 = self.__tk_frame_Main_Tabs_1(frame) 101 | frame.add(self.tk_tabs_Main_Tabs_1, text="设置") 102 | frame.place(x=0, y=0, width=800, height=460) 103 | return frame 104 | def __tk_frame_Main_Tabs_0(self,parent): 105 | frame = Frame(parent) 106 | frame.place(x=0, y=0, width=800, height=460) 107 | return frame 108 | def __tk_frame_Main_Tabs_1(self,parent): 109 | frame = Frame(parent) 110 | frame.place(x=0, y=0, width=800, height=460) 111 | return frame 112 | def __tk_label_Stable_loca(self,parent): 113 | try: 114 | with open(os.getcwd()+"\\config.json", "r", encoding='utf-8') as config_file: 115 | config_data = json.load(config_file) 116 | current_tag_name = config_data["tag_name"] 117 | except Exception as e: 118 | print(e) 119 | current_tag_name = "" 120 | label = Label(parent,text="当前版本:"+current_tag_name,anchor="center", ) 121 | label.place(x=65, y=420, width=130, height=30) 122 | return label 123 | def __tk_button_Check_Update_Button(self,parent): 124 | btn = Button(parent, text="检查", takefocus=False,) 125 | btn.place(x=0, y=420, width=50, height=30) 126 | return btn 127 | def __tk_label_Stable_online(self,parent): 128 | label = Label(parent,text="在线更新",anchor="center", ) 129 | label.place(x=200, y=400, width=130, height=30) 130 | return label 131 | def __tk_button_Update_button(self,parent): 132 | btn = Button(parent, text="更新", takefocus=False,) 133 | btn.place(x=0, y=360, width=50, height=30) 134 | return btn 135 | def __tk_label_frame_ADB_Setting_Frame(self,parent): 136 | frame = LabelFrame(parent,text="设置",) 137 | frame.place(x=0, y=0, width=780, height=140) 138 | return frame 139 | def __tk_label_ADB_Part_Label(self,parent): 140 | label = Label(parent,text="ADB地址",anchor="center", ) 141 | label.place(x=0, y=0, width=70, height=30) 142 | return label 143 | def __tk_label_ADB_Address_Label(self,parent): 144 | label = Label(parent,text="ADB端口",anchor="center", ) 145 | label.place(x=0, y=40, width=70, height=30) 146 | return label 147 | def __tk_button_Auto_Detect_ADB_Button(self,parent): 148 | btn = Button(parent, text="自动检测", takefocus=False,) 149 | btn.place(x=0, y=80, width=70, height=30) 150 | return btn 151 | def __tk_input_ADB_Address_Input(self,parent): 152 | ipt = Entry(parent, ) 153 | ipt.place(x=80, y=0, width=150, height=30) 154 | return ipt 155 | def __tk_input_ADB_Path_Input(self,parent): 156 | ipt = Entry(parent, ) 157 | ipt.place(x=80, y=40, width=150, height=30) 158 | return ipt 159 | def __tk_select_box_Auto_Detect_ADB_Select(self,parent): 160 | cb = Combobox(parent, state="readonly", ) 161 | cb['values'] = ("") 162 | cb.place(x=80, y=80, width=150, height=30) 163 | return cb 164 | def __tk_label_Controller_Type_Label(self,parent): 165 | label = Label(parent,text="控制端",anchor="center", ) 166 | label.place(x=240, y=0, width=70, height=30) 167 | return label 168 | def __tk_label_Resource_Type_Label(self,parent): 169 | label = Label(parent,text="客户端",anchor="center", ) 170 | label.place(x=420, y=0, width=70, height=30) 171 | return label 172 | def __tk_select_box_Controller_Type_Select(self,parent): 173 | cb = Combobox(parent, state="readonly", ) 174 | cb['values'] = ("") 175 | cb.place(x=320, y=0, width=80, height=30) 176 | return cb 177 | def __tk_select_box_Resource_Type_Select(self,parent): 178 | cb = Combobox(parent, state="readonly", ) 179 | cb['values'] = ("") 180 | cb.place(x=500, y=0, width=80, height=30) 181 | return cb 182 | def __tk_label_StartApp(self,parent): 183 | label = Label(parent,text="启动地址",anchor="center", ) 184 | label.place(x=240, y=40, width=70, height=30) 185 | return label 186 | def __tk_input_StartAPP_Address(self,parent): 187 | ipt = Entry(parent, ) 188 | ipt.place(x=320, y=40, width=130, height=30) 189 | return ipt 190 | def __tk_label_StartAPP_P(self,parent): 191 | label = Label(parent,text="参数",anchor="center", ) 192 | label.place(x=510, y=40, width=50, height=30) 193 | return label 194 | def __tk_input_StartAPP_Address_P(self,parent): 195 | ipt = Entry(parent, ) 196 | ipt.place(x=460, y=40, width=40, height=30) 197 | return ipt 198 | def __tk_label_frame_Task_List_Frame(self,parent): 199 | frame = LabelFrame(parent,text="任务列表",) 200 | frame.place(x=460, y=10, width=300, height=425) 201 | return frame 202 | def __tk_list_box_Task_List(self,parent): 203 | lb = Listbox(parent) 204 | for item in Get_Values_list_Option(os.getcwd()+"\\MAA_bin\\config\\maa_pi_config.json","task"): 205 | lb.insert(END, item) 206 | lb.place(x=10, y=10, width=280, height=390) 207 | return lb 208 | def __tk_label_frame_Select_Task_Frame(self,parent): 209 | frame = LabelFrame(parent,text="选择任务",) 210 | frame.place(x=0, y=10, width=340, height=425) 211 | return frame 212 | def __tk_label_Add_Task_Label_1(self,parent): 213 | label = Label(parent,text="隐藏标签1",anchor="center", ) 214 | label.place(x=0, y=40, width=100, height=30) 215 | return label 216 | def __tk_label_Add_Task_Label_3(self,parent): 217 | label = Label(parent,text="隐藏标签3",anchor="center", ) 218 | label.place(x=0, y=120, width=100, height=30) 219 | return label 220 | def __tk_label_Add_Task_Label_2(self,parent): 221 | label = Label(parent,text="隐藏标签2",anchor="center", ) 222 | label.place(x=0, y=80, width=100, height=30) 223 | return label 224 | def __tk_select_box_Add_Task_Select(self,parent): 225 | cb = Combobox(parent, state="readonly", ) 226 | cb['values'] = ("") 227 | cb.place(x=110, y=0, width=200, height=30) 228 | return cb 229 | def __tk_select_box_Add_Task_Select_2(self,parent): 230 | cb = Combobox(parent, state="readonly", ) 231 | cb['values'] = ("") 232 | cb.place(x=110, y=80, width=200, height=30) 233 | return cb 234 | def __tk_select_box_Add_Task_Select_1(self,parent): 235 | cb = Combobox(parent, state="readonly", ) 236 | cb['values'] = ("") 237 | cb.place(x=110, y=40, width=200, height=30) 238 | return cb 239 | def __tk_select_box_Add_Task_Select_3(self,parent): 240 | cb = Combobox(parent, state="readonly", ) 241 | cb['values'] = ("") 242 | cb.place(x=110, y=120, width=200, height=30) 243 | return cb 244 | def __tk_select_box_Add_Task_Select_4(self,parent): 245 | cb = Combobox(parent, state="readonly", ) 246 | cb['values'] = ("") 247 | cb.place(x=110, y=160, width=200, height=30) 248 | return cb 249 | def __tk_label_Add_Task_Label_4(self,parent): 250 | label = Label(parent,text="隐藏标签4",anchor="center", ) 251 | label.place(x=0, y=160, width=100, height=30) 252 | return label 253 | def __tk_label_Add_Task_Label(self,parent): 254 | label = Label(parent,text="任务",anchor="center", ) 255 | label.place(x=0, y=0, width=100, height=30) 256 | return label 257 | def __tk_frame_Button_Frame(self,parent): 258 | frame = LabelFrame(parent,text="",) 259 | frame.place(x=350, y=20, width=100, height=230) 260 | return frame 261 | def __tk_button_Delete_Button(self,parent): 262 | btn = Button(parent, text="删除", takefocus=False,) 263 | btn.place(x=15, y=120, width=70, height=30) 264 | return btn 265 | def __tk_button_Move_Down_Button(self,parent): 266 | btn = Button(parent, text="下移", takefocus=False,) 267 | btn.place(x=15, y=80, width=70, height=30) 268 | return btn 269 | def __tk_button_Start_Task(self,parent): 270 | btn = Button(parent, text="开始任务", takefocus=False,) 271 | btn.place(x=15, y=160, width=70, height=30) 272 | return btn 273 | def __tk_button_Add_Task_Button(self,parent): 274 | btn = Button(parent, text="添加任务", takefocus=False,) 275 | btn.place(x=15, y=0, width=70, height=30) 276 | return btn 277 | def __tk_button_Move_Up_Button(self,parent): 278 | btn = Button(parent, text="上移", takefocus=False,) 279 | btn.place(x=15, y=40, width=70, height=30) 280 | return btn 281 | def __tk_label_Config_Label(self,parent): 282 | label = Label(parent,text="标签",anchor="center", ) 283 | label.place(x=0, y=0, width=50, height=30) 284 | return label 285 | def __tk_input_StartAPP_wait(self,parent): 286 | ipt = Entry(parent, ) 287 | ipt.place(x=510, y=40, width=65, height=30) 288 | return ipt 289 | def __tk_label_Tpis_Setting(self,parent): 290 | label = Label(parent,text="启动地址-启动参数-启动延迟",anchor="center", ) 291 | label.place(x=240, y=80, width=340, height=30) 292 | return label 293 | class Win(WinGUI): 294 | def __init__(self, controller): 295 | self.ctl = controller 296 | super().__init__() 297 | self.__event_bind() 298 | self.__style_config() 299 | self.ctl.init(self) 300 | def __event_bind(self): 301 | self.tk_button_Start_Task.bind('',self.ctl.Start_Task) 302 | self.tk_input_ADB_Path_Input.bind('',self.ctl.Save_ADB_Path) 303 | self.tk_input_ADB_Address_Input.bind('',self.ctl.Save_ADB_Address) 304 | self.tk_select_box_Resource_Type_Select.bind('<>',self.ctl.Save_Resource_Type_Select) 305 | self.tk_select_box_Add_Task_Select.bind('<>',self.ctl.Add_Task_Select_More_Select) 306 | self.tk_select_box_Add_Task_Select.bind('',self.ctl.Save_ADB_Address) 307 | self.tk_button_Add_Task_Button.bind('',self.ctl.Add_Task) 308 | self.tk_button_Move_Up_Button.bind('',self.ctl.Click_Move_Up_Button) 309 | self.tk_button_Move_Down_Button.bind('',self.ctl.Click_Move_Down_Button) 310 | self.tk_button_Delete_Button.bind('',self.ctl.Click_Delete_Button) 311 | self.tk_select_box_Controller_Type_Select.bind('<>',self.ctl.Save_Controller_Type_Select) 312 | self.tk_list_box_Task_List.bind('',self.ctl.Click_Delete_Button) 313 | self.tk_button_Update_button.bind('',self.ctl.Update) 314 | self.tk_button_Check_Update_Button.bind('',self.ctl.Check_Update) 315 | self.tk_button_Auto_Detect_ADB_Button.bind('',self.ctl.Click_Auto_Detect_ADB) 316 | self.tk_select_box_Auto_Detect_ADB_Select.bind('<>',self.ctl.Replace_ADB_data) 317 | def __style_config(self): 318 | pass 319 | if __name__ == "__main__": 320 | win = WinGUI() 321 | win.mainloop() -------------------------------------------------------------------------------- /control.py: -------------------------------------------------------------------------------- 1 | from ui import Win 2 | import subprocess 3 | import os 4 | from tool import * 5 | import requests 6 | from zipfile import ZipFile 7 | import shutil 8 | from tkinter import messagebox 9 | import threading 10 | import time 11 | 12 | #获取初始resource序号 13 | Add_Resource_Type_Select_Values = [] 14 | for i in Read_Config(os.getcwd()+"\\MAA_bin\\interface.json")["resource"]: 15 | Add_Resource_Type_Select_Values.append(i["name"]) 16 | Resource_Type = Read_Config(os.getcwd()+"\\MAA_bin\\config\\maa_pi_config.json")["resource"] 17 | 18 | Resource_count = 0 19 | for i in Add_Resource_Type_Select_Values: 20 | if i == Resource_Type: 21 | break 22 | else: 23 | Resource_count+=1 24 | 25 | #获取初始Controller序号 26 | Add_Controller_Type_Select_Values = [] 27 | for i in Read_Config(os.getcwd()+"\\MAA_bin\\interface.json")["controller"]: 28 | Add_Controller_Type_Select_Values.append(i["name"]) 29 | Controller_Type = Read_Config(os.getcwd()+"\\MAA_bin\\config\\maa_pi_config.json")["controller"] ["name"] 30 | 31 | Controller_count = 0 32 | for i in Add_Controller_Type_Select_Values: 33 | if i == Controller_Type: 34 | break 35 | else: 36 | Controller_count+=1 37 | 38 | 39 | #初始显示 40 | init_ADB_Path = Read_Config(os.getcwd()+"\\MAA_bin\\config\\maa_pi_config.json")["adb"]["adb_path"] 41 | init_ADB_Address = Read_Config(os.getcwd()+"\\MAA_bin\\config\\maa_pi_config.json")["adb"]["address"] 42 | init_Resource_Type = Resource_count 43 | init_Controller_Type = Controller_count 44 | 45 | class Controller: 46 | # 导入UI类后,替换以下的 object 类型,将获得 IDE 属性提示功能 47 | ui: Win 48 | def __init__(self): 49 | pass 50 | 51 | def init(self,ui): 52 | """ 53 | 得到UI实例,对组件进行初始化配置 54 | """ 55 | self.ui = ui 56 | with open(os.getcwd()+"\\config.json","r",encoding='utf-8') as f : 57 | config_data = json.load(f) 58 | if "startapp" not in config_data: 59 | config_data["startapp"] = "" 60 | if "startapp_p" not in config_data: 61 | config_data["startapp_p"] = "" 62 | if "startapp_w" not in config_data: 63 | config_data["startapp_w"] = "10" 64 | 65 | with open(os.getcwd()+"\\config.json","w",encoding="utf-8") as f: 66 | json.dump(config_data, f, indent=4) 67 | 68 | #打开config文件查找StartApp参数,如果没有创建一个新的 69 | self.ui.tk_input_StartAPP_Address.insert(0,config_data["startapp"]) 70 | self.ui.tk_input_StartAPP_Address_P.insert(0,config_data["startapp_p"]) 71 | self.ui.tk_input_StartAPP_wait.insert(0,config_data["startapp_w"]) 72 | #服务器资源 73 | self.ui.tk_select_box_Resource_Type_Select["values"] = (Get_Values_list(os.getcwd()+"\\MAA_bin\\interface.json","resource")) 74 | self.ui.tk_select_box_Add_Task_Select["values"] = (Get_Values_list(os.getcwd()+"\\MAA_bin\\interface.json","task")) 75 | self.ui.tk_select_box_Controller_Type_Select["values"] = (Get_Values_list(os.getcwd()+"\\MAA_bin\\interface.json","controller")) 76 | #ADB地址和端口输入框 77 | self.ui.tk_input_ADB_Address_Input.insert(0,init_ADB_Address) 78 | self.ui.tk_input_ADB_Path_Input.insert(0,init_ADB_Path) 79 | #服务器和任务下拉框和控制端下拉框 80 | self.ui.tk_select_box_Controller_Type_Select.current(init_Controller_Type) 81 | self.ui.tk_select_box_Resource_Type_Select.current(init_Resource_Type) 82 | self.ui.tk_select_box_Add_Task_Select.current(0) 83 | 84 | #隐藏4个给option准备的下拉框 85 | self.ui.tk_select_box_Add_Task_Select_1.place_forget() 86 | self.ui.tk_select_box_Add_Task_Select_2.place_forget() 87 | self.ui.tk_select_box_Add_Task_Select_3.place_forget() 88 | self.ui.tk_select_box_Add_Task_Select_4.place_forget() 89 | self.ui.tk_label_Add_Task_Label_1.place_forget() 90 | self.ui.tk_label_Add_Task_Label_2.place_forget() 91 | self.ui.tk_label_Add_Task_Label_3.place_forget() 92 | self.ui.tk_label_Add_Task_Label_4.place_forget() 93 | 94 | #隐藏下载进度条 进度显示 和版本显示 95 | self.ui.tk_label_Stable_online.place_forget() 96 | self.ui.tk_button_Update_button.place_forget() 97 | def Start_Task(self,evt): 98 | # 使用threading启动一个新线程来执行更新操作 99 | threading.Thread(target=self._Start_TaskB_thread_function, daemon=True).start() 100 | def _Start_TaskB_thread_function(self): 101 | #保存 102 | self.Save_ADB_Address(self) 103 | self.Save_ADB_Path(self) 104 | 105 | if self.ui.tk_input_StartAPP_Address.get() != "": 106 | APP_Path = self.ui.tk_input_StartAPP_Address.get() 107 | APP_P = self.ui.tk_input_StartAPP_Address_P.get() 108 | APP_wait = self.ui.tk_input_StartAPP_wait.get() 109 | subprocess.Popen(str(APP_Path)+str(APP_P)) 110 | 111 | seconds = int(APP_wait) 112 | while seconds > 0: 113 | print(f"剩余时间: {seconds}秒") 114 | time.sleep(1) # 等待1秒 115 | seconds -= 1 116 | #使用-d参数打开MaaPiCli.exe 117 | subprocess.Popen(os.getcwd()+"\\MAA_bin\\MaaPiCli.exe -d") 118 | 119 | def Save_APP_Setting(self,evt): 120 | with open(os.getcwd()+"\\config.json","r",encoding='utf-8') as f : 121 | config_data = json.load(f) 122 | config_data["startapp"] = self.ui.tk_input_StartAPP_Address.get() 123 | config_data["startapp_p"] = self.ui.tk_input_StartAPP_Address_P.get() 124 | config_data["startapp_w"] = self.ui.tk_input_StartAPP_wait.get() 125 | with open(os.getcwd()+"\\config.json","w",encoding="utf-8") as Config: 126 | json.dump(config_data,Config,indent=4,ensure_ascii=False) 127 | def Save_ADB_Path(self,evt): 128 | #打开maa_pi_config.json并写入新的ADB路径 129 | ADB_Path = self.ui.tk_input_ADB_Path_Input.get().replace("\\","/") 130 | MAA_Pi_Config = Read_Config(os.getcwd()+"\\MAA_bin\\config\\maa_pi_config.json") 131 | MAA_Pi_Config["adb"]["adb_path"] = ADB_Path 132 | Save_Config(os.getcwd()+"\\MAA_bin\\config\\maa_pi_config.json",MAA_Pi_Config) 133 | 134 | def Save_ADB_Address(self,evt): 135 | #打开maa_pi_config.json并写入新的ADB端口 136 | ADB_Address = self.ui.tk_input_ADB_Address_Input.get() 137 | MAA_Pi_Config = Read_Config(os.getcwd()+"\\MAA_bin\\config\\maa_pi_config.json") 138 | MAA_Pi_Config["adb"]["address"] = ADB_Address 139 | Save_Config(os.getcwd()+"\\MAA_bin\\config\\maa_pi_config.json",MAA_Pi_Config) 140 | 141 | def Save_Resource_Type_Select(self,evt): 142 | #打开maa_pi_config.json并写入新的资源 143 | Resource_Type_Select = self.ui.tk_select_box_Resource_Type_Select.get() 144 | MAA_Pi_Config = Read_Config(os.getcwd()+"\\MAA_bin\\config\\maa_pi_config.json") 145 | MAA_Pi_Config["resource"] = Resource_Type_Select 146 | Save_Config(os.getcwd()+"\\MAA_bin\\config\\maa_pi_config.json",MAA_Pi_Config) 147 | 148 | def Save_Controller_Type_Select(self,evt): 149 | #打开maa_pi_config.json并写入新的控制端 150 | Controller_Type_Select = self.ui.tk_select_box_Controller_Type_Select.get() 151 | interface_Controller = Read_Config(os.getcwd()+"\\MAA_bin\\interface.json")["controller"] 152 | 153 | for i in interface_Controller: 154 | if i["name"] == Controller_Type_Select: 155 | Controller_target = i 156 | MAA_Pi_Config = Read_Config(os.getcwd()+"\\MAA_bin\\config\\maa_pi_config.json") 157 | MAA_Pi_Config["controller"] = Controller_target 158 | Save_Config(os.getcwd()+"\\MAA_bin\\config\\maa_pi_config.json",MAA_Pi_Config) 159 | 160 | def Add_Task(self,evt): 161 | #添加任务至GUI列表并保存配置文件 162 | task = self.ui.tk_select_box_Add_Task_Select.get() 163 | 164 | Option = [] 165 | Select_Target = self.ui.tk_select_box_Add_Task_Select.get() 166 | MAA_Pi_Config = Read_Config(os.getcwd()+"\\MAA_bin\\interface.json") 167 | #Option_list存放所有带有option的键值 168 | Option_list = [] 169 | for i in MAA_Pi_Config["task"]: 170 | #将所有带有option的键值存进Option_list 171 | if i.get("option")!= None: 172 | Option_list.append(i) 173 | for i in Option_list: 174 | #检查当前选中任务的是否为option_list中的元素 175 | if Select_Target == i["name"]: 176 | l = len(i["option"]) 177 | options_dicts = [] 178 | # 根据option的长度,循环添加选项到列表中 179 | for index in range(l): 180 | select_box_name = f"tk_select_box_Add_Task_Select_{index + 1}" 181 | selected_value = getattr(self.ui, select_box_name).get() 182 | options_dicts.append({"name": i["option"][index], "value": selected_value}) 183 | Option.extend(options_dicts) 184 | MAA_Pi_Config = Read_Config(os.getcwd()+"\\MAA_bin\\config\\maa_pi_config.json") 185 | MAA_Pi_Config["task"].append({"name": task,"option": Option}) 186 | Save_Config(os.getcwd()+"\\MAA_bin\\config\\maa_pi_config.json",MAA_Pi_Config) 187 | #刷新GUI任务列表 188 | self.ui.tk_list_box_Task_List.delete(0,100)#为什么END用不了?那我直接选第100位,什么时候真有100个任务了我就写1000位 189 | for item in Get_Values_list_Option(os.getcwd()+"\\MAA_bin\\config\\maa_pi_config.json","task"): 190 | self.ui.tk_list_box_Task_List.insert(100, item) 191 | 192 | 193 | def Click_Move_Up_Button(self,evt): 194 | #上移任务 195 | Select_Target = self.ui.tk_list_box_Task_List.curselection()[0] 196 | MAA_Pi_Config = Read_Config(os.getcwd()+"\\MAA_bin\\config\\maa_pi_config.json") 197 | Select_Task = MAA_Pi_Config["task"].pop(Select_Target) 198 | MAA_Pi_Config["task"].insert(Select_Target-1, Select_Task) 199 | Save_Config(os.getcwd()+"\\MAA_bin\\config\\maa_pi_config.json",MAA_Pi_Config) 200 | #刷新GUI任务列表 201 | self.ui.tk_list_box_Task_List.delete(0,100) 202 | for item in Get_Values_list_Option(os.getcwd()+"\\MAA_bin\\config\\maa_pi_config.json","task"): 203 | self.ui.tk_list_box_Task_List.insert(100, item) 204 | self.ui.tk_list_box_Task_List.selection_set(Select_Target-1) 205 | 206 | 207 | def Click_Move_Down_Button(self,evt): 208 | #下移任务 209 | Select_Target = self.ui.tk_list_box_Task_List.curselection()[0] 210 | MAA_Pi_Config = Read_Config(os.getcwd()+"\\MAA_bin\\config\\maa_pi_config.json") 211 | Select_Task = MAA_Pi_Config["task"].pop(Select_Target) 212 | MAA_Pi_Config["task"].insert(Select_Target+1, Select_Task) 213 | Save_Config(os.getcwd()+"\\MAA_bin\\config\\maa_pi_config.json",MAA_Pi_Config) 214 | #刷新GUI任务列表 215 | self.ui.tk_list_box_Task_List.delete(0,100) 216 | for item in Get_Values_list_Option(os.getcwd()+"\\MAA_bin\\config\\maa_pi_config.json","task"): 217 | self.ui.tk_list_box_Task_List.insert(100, item) 218 | self.ui.tk_list_box_Task_List.selection_set(Select_Target+1) 219 | 220 | 221 | def Click_Delete_Button(self,evt): 222 | #删除选定任务 223 | Select_Target = self.ui.tk_list_box_Task_List.curselection()[0] 224 | self.ui.tk_list_box_Task_List.delete(Select_Target) 225 | Task_List = Get_Values_list2(os.getcwd()+"\\MAA_bin\\config\\maa_pi_config.json","task") 226 | del Task_List[Select_Target] 227 | MAA_Pi_Config = Read_Config(os.getcwd()+"\\MAA_bin\\config\\maa_pi_config.json") 228 | del MAA_Pi_Config["task"] 229 | MAA_Pi_Config.update({"task":Task_List}) 230 | Save_Config(os.getcwd()+"\\MAA_bin\\config\\maa_pi_config.json",MAA_Pi_Config) 231 | self.ui.tk_list_box_Task_List.selection_set(Select_Target-1) 232 | 233 | 234 | 235 | def Add_Task_Select_More_Select(self, evt): 236 | # 清除所有额外显示的下拉框和标签 237 | self.clear_extra_widgets() 238 | 239 | # 获取选中的任务 240 | select_target = self.ui.tk_select_box_Add_Task_Select.get() 241 | 242 | # 使用after()方法延迟执行 243 | def delayed_update(): 244 | MAA_Pi_Config = Read_Config(os.getcwd() + "\\MAA_bin\\interface.json") 245 | 246 | # 查找是否有选中的任务并包含option 247 | for task in MAA_Pi_Config["task"]: 248 | if task["name"] == select_target and task.get("option") is not None: 249 | option_length = len(task["option"]) 250 | 251 | # 根据option数量动态显示下拉框和标签 252 | for i in range(option_length): 253 | select_box = getattr(self.ui, f"tk_select_box_Add_Task_Select_{i+1}") 254 | label = getattr(self.ui, f"tk_label_Add_Task_Label_{i+1}") 255 | option_name = task["option"][i] 256 | 257 | # 填充下拉框数据 258 | select_box["values"] = tuple(Get_Task_List(option_name)) 259 | select_box.place(x=110, y=40 + (i * 40), width=200, height=30) 260 | 261 | # 显示标签 262 | label["text"] = option_name 263 | label.place(x=0, y=40 + (i * 40), width=100, height=30) 264 | 265 | # 刷新GUI 266 | select_box.update() 267 | label.update() 268 | 269 | break # 找到匹配的任务后退出循环 270 | 271 | self.ui.after(100, delayed_update) 272 | 273 | def clear_extra_widgets(self): 274 | # 隐藏并清除所有额外的下拉框和标签的选项 275 | for i in range(1, 5): 276 | select_box = getattr(self.ui, f"tk_select_box_Add_Task_Select_{i}") 277 | select_box.set("") # 清除选项 278 | select_box.place_forget() # 隐藏下拉框 279 | 280 | label = getattr(self.ui, f"tk_label_Add_Task_Label_{i}") 281 | label.place_forget() # 隐藏标签 282 | def Check_Update(self, evt): 283 | # 使用threading启动一个新线程来执行检查更新操作 284 | threading.Thread(target=self._check_update_thread_function, daemon=True).start() 285 | 286 | def _check_update_thread_function(self): 287 | with open(os.getcwd()+"\\config.json","r",encoding="utf-8") as GUI_Config: 288 | url = json.load(GUI_Config)["url"] 289 | 290 | global Cont 291 | Cont=requests.get(url).json() 292 | #显示版本 293 | self.ui.tk_label_Stable_online.place(x=200, y=420, width=130, height=30) 294 | self.ui.tk_label_Stable_online["text"] = "最新版本"+Cont["tag_name"] 295 | self.ui.tk_label_Stable_online.update() 296 | 297 | #显示进度条和更新按钮 298 | self.ui.tk_button_Update_button.place(x=0, y=380, width=50, height=30) 299 | 300 | def Update(self, evt): 301 | # 使用threading启动一个新线程来执行更新操作 302 | threading.Thread(target=self._update_thread_function, daemon=True).start() 303 | 304 | def _update_thread_function(self): 305 | #找出win-x86_64版本的下载地址 306 | browser_download_url = [] 307 | for i in Cont["assets"]: 308 | if "win-x86_64" in i["name"]: 309 | browser_download_url.append(i["browser_download_url"]) 310 | zip_url = browser_download_url[0] 311 | # 读取config.json中的当前tag_name 312 | try: 313 | with open(os.getcwd()+"\\config.json", "r", encoding="utf-8") as config_file: 314 | config_data = json.load(config_file) 315 | current_tag_name = config_data.get("tag_name", "") 316 | except Exception as e: 317 | messagebox.showinfo(f"Error reading config.json: {e}") 318 | current_tag_name = "" 319 | 320 | # 获取最新的tag_name 321 | new_tag_name = Cont["tag_name"] 322 | 323 | # 比较新旧tag_name,并更新UI和config.json(如果需要) 324 | if new_tag_name != current_tag_name: 325 | # 更新config.json 326 | config_data["tag_name"] = new_tag_name 327 | with open(os.getcwd()+"\\config.json", "w", encoding="utf-8") as config_file: 328 | json.dump(config_data, config_file, indent=4, ensure_ascii=False) 329 | 330 | # 检查MAA-bin文件夹是否存在,如果不存在则创建它 331 | maa_bin_dir = os.path.join(os.getcwd(), "MAA_bin") 332 | if not os.path.exists(maa_bin_dir): 333 | os.makedirs(maa_bin_dir) 334 | 335 | # 构建保存ZIP文件的路径 336 | zip_file_path = os.path.join(os.getcwd(), "download.zip") 337 | 338 | # 下载ZIP文件 339 | try: 340 | print("开始下载") 341 | with requests.get(zip_url, stream=True) as r: 342 | r.raise_for_status() # 如果响应状态码不是200,则抛出HTTPError异常 343 | with open(zip_file_path, "wb") as f: 344 | shutil.copyfileobj(r.raw, f) 345 | 346 | # 解压ZIP文件到MAA-bin文件夹 347 | with ZipFile(zip_file_path, "r") as zip_ref: 348 | zip_ref.extractall(maa_bin_dir) 349 | 350 | #删除ZIP文件 351 | os.remove(zip_file_path) 352 | messagebox.showinfo("成功", "文件已下载并解压到MAA-bin文件夹内!") 353 | except requests.exceptions.RequestException as e: 354 | messagebox.showerror("下载错误", str(e)) 355 | except Exception as e: 356 | messagebox.showerror("错误", str(e)) 357 | def Click_Auto_Detect_ADB(self,evt): 358 | # 使用threading启动一个新线程来执行更新操作 359 | threading.Thread(target=self._Click_Auto_Detect_ADB_thread_function, daemon=True).start() 360 | def _Click_Auto_Detect_ADB_thread_function(self): 361 | emulator = [ 362 | { 363 | "name":"BlueStacks", 364 | "exe_name":"HD-Player.exe", 365 | "may_path":["HD-Adb.exe","Engine\\ProgramFiles\\HD-Adb.exe"], 366 | "port":["127.0.0.1:5555","127.0.0.1:5556","127.0.0.1:5565","127.0.0.1:5575"] 367 | }, 368 | { 369 | "name":"MuMuPlayer12", 370 | "exe_name":"MuMuPlayer.exe", 371 | "may_path":["vmonitor\\bin\\adb_server.exe","MuMu\\emulator\\nemu\\vmonitor\\bin\\adb_server.exe","adb.exe"], 372 | "port":["127.0.0.1:16384", "127.0.0.1:16416", "127.0.0.1:16448"] 373 | }, 374 | { 375 | "name":"LDPlayer", 376 | "exe_name":"dnplayer.exe", 377 | "may_path":["adb.exe"], 378 | "port":["127.0.0.1:5555","127.0.0.1:5556"] 379 | }, 380 | { 381 | "name":"Nox", 382 | "exe_name":"Nox.exe", 383 | "may_path":["nox_adb.exe"], 384 | "port":["127.0.0.1:62001", "127.0.0.1:59865"] 385 | }, 386 | { 387 | "name":"MuMuPlayer6", 388 | "exe_name":"NemuPlayer.exe", 389 | "may_path":["vmonitor\\bin\\adb_server.exe","MuMu\\emulator\\nemu\\vmonitor\\bin\\adb_server.exe","adb.exe"], 390 | "port":["127.0.0.1:7555"] 391 | }, 392 | { 393 | "name":"MEmuPlayer.exe", 394 | "exe_name":"MEmu", 395 | "may_path":["adb.exe"], 396 | "port":["127.0.0.1:21503"] 397 | }, 398 | { 399 | "name":"ADV", 400 | "exe_name":"qemu-system.exe", 401 | "may_path":["..\\..\\..\\platform-tools\\adb.exe"], 402 | "port":["127.0.0.1:5555"] 403 | } 404 | ] 405 | 406 | global emulator_result 407 | emulator_result = [] 408 | print("开始查找") 409 | for app in emulator: 410 | process_path = find_process_by_name(app["exe_name"]) 411 | 412 | if process_path: 413 | #判断程序是否正在运行,是进行下一步,否则放弃 414 | info_dict = {"exe_path":process_path,"may_path":app["may_path"]} 415 | ADB_path = find_existing_file(info_dict) 416 | if ADB_path: 417 | 418 | #判断ADB地址是否存在,是进行下一步,否则放弃 419 | port_data = check_port(app["port"]) 420 | if port_data: 421 | #判断端口是否存在,是则组合字典,否则放弃 422 | emulator_result.extend([{"name":app["name"],"path":ADB_path,"port": item} for item in port_data]) 423 | 424 | if emulator_result: 425 | processed_list = [] 426 | messagebox.showinfo("提示","查找完成") 427 | for i in emulator_result: 428 | processed_s = f"{i["name"]}" 429 | processed_list.append(processed_s) 430 | self.ui.tk_select_box_Auto_Detect_ADB_Select["values"] = processed_list 431 | self.ui.tk_select_box_Auto_Detect_ADB_Select.update() 432 | else: 433 | messagebox.showerror("错误","未找到模拟器") 434 | def Replace_ADB_data(self,evt): 435 | print(emulator_result[self.ui.tk_select_box_Auto_Detect_ADB_Select.current()]["port"]) 436 | print(emulator_result[self.ui.tk_select_box_Auto_Detect_ADB_Select.current()]["path"]) 437 | self.ui.tk_input_ADB_Address_Input.delete(0,100) 438 | self.ui.tk_input_ADB_Path_Input.delete(0,100) 439 | self.ui.tk_input_ADB_Address_Input.insert(0,emulator_result[self.ui.tk_select_box_Auto_Detect_ADB_Select.current()]["port"]) 440 | self.ui.tk_input_ADB_Path_Input.insert(0,emulator_result[self.ui.tk_select_box_Auto_Detect_ADB_Select.current()]["path"]) 441 | self.Save_ADB_Address(self) 442 | self.Save_ADB_Path(self) --------------------------------------------------------------------------------