重构完代码结构了

This commit is contained in:
Robofish 2025-07-26 01:22:52 +08:00
parent 47e0b8419f
commit 501a9ddff4
108 changed files with 224 additions and 2017 deletions

BIN
1.xlsx

Binary file not shown.

View File

@ -8,10 +8,11 @@ OutputBaseFilename=MRobotInstaller
[Files]
Source: "dist\MRobot.exe"; DestDir: "{app}"; Flags: ignoreversion
Source: "img\*"; DestDir: "{app}\img"; Flags: ignoreversion recursesubdirs
Source: "User_code\*"; DestDir: "{app}\User_code"; Flags: ignoreversion recursesubdirs
Source: "mech_lib\*"; DestDir: "{app}\mech_lib"; Flags: ignoreversion recursesubdirs
Source: "assets\logo\*"; DestDir: "{app}\assets\logo"; Flags: ignoreversion recursesubdirs
Source: "assets\User_code\*"; DestDir: "{app}\assets\User_code"; Flags: ignoreversion recursesubdirs
Source: "assets\mech_lib\*"; DestDir: "{app}\assets\mech_lib"; Flags: ignoreversion recursesubdirs
Source: "assets\logo\M.ico"; DestDir: "{app}\assets\logo"; Flags: ignoreversion
[Icons]
Name: "{group}\MRobot"; Filename: "{app}\MRobot.exe"; IconFilename: "{app}\img\M.ico"
Name: "{userdesktop}\MRobot"; Filename: "{app}\MRobot.exe"; IconFilename: "{app}\img\M.ico"
Name: "{group}\MRobot"; Filename: "{app}\MRobot.exe"; IconFilename: "{app}\assets\logo\M.ico"
Name: "{userdesktop}\MRobot"; Filename: "{app}\MRobot.exe"; IconFilename: "{app}\assets\logo\M.ico"

File diff suppressed because it is too large Load Diff

View File

@ -123,3 +123,10 @@ pyinstaller --noconfirm --onefile --windowed --add-data "img;img" --add-data "Us
pyinstaller MRobot.py
pyinstaller --noconfirm --onefile --windowed --icon=img/M.ico --add-data "img;img" --add-data "User_code;User_code" --add-data "mech_lib;mech_lib" MRobot.py
pyinstaller --onefile --windowed --icon=assets/logo/M.ico --add-data "assets/logo:assets/logo" --add-data "assets/User_code:assets/User_code" --add-data "assets/mech_lib:assets/mech_lib" --collect-all pandas MRobot.py
pyinstaller --onefile --windowed --icon=assets/logo/M.ico --add-data "assets/logo:assets/logo" --add-data "assets/User_code:assets/User_code" --add-data "assets/mech_lib:assets/mech_lib" MRobot.py
python3 -m pyinstaller MRobot.py --onefile --windowed --add-data "assets:assets" --add-data "app:app" --add-data "app/tools:app/tools"

21
ai.py
View File

@ -1,21 +0,0 @@
import requests
import json
url = "http://154.37.215.220:11434/api/generate"
payload = {
"model": "qwen3:0.6b",
"prompt": "你好,介绍一下你自己"
}
response = requests.post(url, json=payload, stream=True)
for line in response.iter_lines():
if line:
try:
data = json.loads(line.decode('utf-8'))
# 只输出 response 字段内容
print(data.get("response", ""), end="", flush=True)
# 如果 done 为 True则换行
if data.get("done", False):
print()
except Exception as e:
pass # 忽略解析异常

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -1,7 +1,9 @@
from PyQt5.QtWidgets import QWidget, QVBoxLayout, QMessageBox
from PyQt5.QtCore import Qt
from PyQt5.QtCore import Qt, QUrl
from PyQt5.QtGui import QDesktopServices
from qfluentwidgets import PrimaryPushSettingCard, FluentIcon
from qfluentwidgets import InfoBar, InfoBarPosition
from qfluentwidgets import InfoBar, InfoBarPosition, SubtitleLabel
from .function_fit_interface import FunctionFitInterface
from app.tools.check_update import check_update
@ -15,11 +17,18 @@ class AboutInterface(QWidget):
layout = QVBoxLayout(self)
layout.setAlignment(Qt.AlignTop)
layout.setContentsMargins(20, 30, 20, 20) # 添加边距
title = SubtitleLabel("MRobot 帮助页面", self)
title.setAlignment(Qt.AlignCenter)
layout.addWidget(title)
# 添加空间隔
layout.addSpacing(10)
card = PrimaryPushSettingCard(
text="检查更新",
icon=FluentIcon.DOWNLOAD,
title="关于",
title="更新",
content=f"MRobot_Toolbox 当前版本:{__version__}",
)
card.clicked.connect(self.on_check_update_clicked)
@ -29,9 +38,11 @@ class AboutInterface(QWidget):
try:
latest = check_update(__version__)
if latest:
# 直接用浏览器打开下载链接
QDesktopServices.openUrl(QUrl("https://github.com/goldenfishs/MRobot/releases/latest"))
InfoBar.success(
title="发现新版本",
content=f"检测到新版本:{latest}请前往官网或仓库下载更新",
content=f"检测到新版本:{latest}已为你打开下载页面",
parent=self,
position=InfoBarPosition.TOP,
duration=5000

View File

@ -6,8 +6,10 @@ import os
import requests
import zipfile
import io
import re
import shutil
import yaml
import textwrap
from jinja2 import Template
class IocConfig:
@ -253,7 +255,7 @@ class DataInterface(QWidget):
def update_user_template(self):
url = "http://gitea.qutrobot.top/robofish/MRobot/archive/User_code.zip"
local_dir = "User_code"
local_dir = os.path.join(os.path.dirname(os.path.abspath(__file__)), "../assets/User_code")
try:
resp = requests.get(url, timeout=30)
resp.raise_for_status()
@ -287,7 +289,7 @@ class DataInterface(QWidget):
def show_user_code_files(self):
self.file_tree.clear()
base_dir = os.path.join(os.path.dirname(os.path.abspath(__file__)), "../User_code")
base_dir = os.path.join(os.path.dirname(os.path.abspath(__file__)), "../assets/User_code")
user_dir = os.path.join(self.project_path, "User")
sub_dirs = ["bsp", "component", "device", "module"]
@ -407,7 +409,7 @@ class DataInterface(QWidget):
def generate_code(self):
import shutil
base_dir = "User_code"
base_dir = os.path.join(os.path.dirname(os.path.abspath(__file__)), "../assets/User_code")
user_dir = os.path.join(self.project_path, "User")
copied = []
files = self.get_checked_files()
@ -690,7 +692,7 @@ class DataInterface(QWidget):
def generate_task_code(self, task_list):
base_dir = os.path.dirname(os.path.abspath(__file__))
template_dir = os.path.join(base_dir, "User_code", "task")
template_dir = os.path.join(base_dir, "../assets/User_code/task")
output_dir = os.path.join(self.project_path, "User", "task")
os.makedirs(output_dir, exist_ok=True)

View File

@ -1,7 +1,8 @@
from PyQt5.QtWidgets import QWidget, QVBoxLayout, QHBoxLayout, QPushButton, QFileDialog, QTableWidgetItem, QApplication
from PyQt5.QtCore import Qt
from qfluentwidgets import TitleLabel, BodyLabel, TableWidget, PushButton, SubtitleLabel, SpinBox, ComboBox, InfoBar,InfoBarPosition, FluentIcon
import pandas as pd
from openpyxl import load_workbook, Workbook
import numpy as np
from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas
from matplotlib.figure import Figure
@ -131,24 +132,30 @@ class FunctionFitInterface(QWidget):
self.dataTable.removeRow(row)
def import_excel(self):
path, _ = QFileDialog.getOpenFileName(self, "导入 Excel", "", "Excel Files (*.xlsx *.xls)")
path, _ = QFileDialog.getOpenFileName(self, "导入 Excel", "", "Excel Files (*.xlsx)")
if path:
df = pd.read_excel(path)
self.dataTable.setRowCount(0) # 清空原有数据
for row_data in df.values.tolist():
wb = load_workbook(path)
ws = wb.active
self.dataTable.setRowCount(0)
for row_data in ws.iter_rows(min_row=2, values_only=True): # 跳过表头
row = self.dataTable.rowCount()
self.dataTable.insertRow(row)
for col, value in enumerate(row_data):
item = QTableWidgetItem(str(value))
for col, value in enumerate(row_data[:2]):
item = QTableWidgetItem(str(value) if value is not None else "")
self.dataTable.setItem(row, col, item)
def export_excel(self):
path, _ = QFileDialog.getSaveFileName(self, "导出 Excel", "", "Excel Files (*.xlsx)")
if path:
data = self.parse_data()
if data is not None:
df = pd.DataFrame(data, columns=["x", "y"])
df.to_excel(path, index=False)
wb = Workbook()
ws = wb.active
ws.append(["x", "y"])
for row in data:
ws.append(row)
wb.save(path)
def parse_data(self):
data = []
@ -184,9 +191,9 @@ class FunctionFitInterface(QWidget):
self.figure.clear()
ax = self.figure.add_subplot(111)
ax.scatter(x, y, color='blue', label='原始数据')
ax.plot(x_fit, y_fit, color='red', label=f'拟合: {degree}')
ax.set_title('函数图像')
ax.scatter(x, y, color='blue', label='raw data')
ax.plot(x_fit, y_fit, color='red', label=f'Fitted curve')
ax.set_title('graph of a function')
ax.set_xlabel('x')
ax.set_ylabel('y')
ax.legend()

View File

@ -1,6 +1,15 @@
from PyQt5.QtWidgets import QWidget, QVBoxLayout
from PyQt5.QtCore import Qt
from qfluentwidgets import SubtitleLabel, BodyLabel, HorizontalSeparator, ImageLabel, FluentLabelBase, TitleLabel
import sys
import os
def resource_path(relative_path):
"""获取资源文件的绝对路径,兼容打包和开发环境"""
if hasattr(sys, '_MEIPASS'):
# PyInstaller 打包后的临时目录
return os.path.join(sys._MEIPASS, relative_path)
return os.path.join(os.path.abspath("."), relative_path)
class HomeInterface(QWidget):
def __init__(self, parent=None):
@ -19,7 +28,7 @@ class HomeInterface(QWidget):
content_layout.setContentsMargins(48, 48, 48, 48)
# Logo
logo = ImageLabel('img/MRobot.png')
logo = ImageLabel(resource_path('assets/logo/MRobot.png'))
logo.scaledToHeight(80)
content_layout.addWidget(logo, alignment=Qt.AlignHCenter) # 居中对齐

View File

@ -106,7 +106,7 @@ class MainWindow(FluentWindow):
# main_window.py 只需修改关闭事件
def closeEvent(self, e):
if self.themeListener and self.themeListener.isRunning():
self.themeListener.terminate()
self.themeListener.deleteLater()
# if self.themeListener and self.themeListener.isRunning():
# self.themeListener.terminate()
# self.themeListener.deleteLater()
super().closeEvent(e)

View File

@ -10,12 +10,13 @@ from urllib.parse import quote
class PartLibraryInterface(QWidget):
SERVER_URL = "http://154.37.215.220:5000"
SECRET_KEY = "MRobot_Download"
LOCAL_LIB_DIR = "mech_lib"
LOCAL_LIB_DIR = "assets/mech_lib"
def __init__(self, parent=None):
super().__init__(parent=parent)
self.setObjectName("partLibraryInterface")
layout = QVBoxLayout(self)
layout.setContentsMargins(20, 20, 20, 20) # 添加边距
layout.setSpacing(16)
layout.addWidget(SubtitleLabel("零件库在线bate版"))

Binary file not shown.

Binary file not shown.

20
app/tools/code_utils.py Normal file
View File

@ -0,0 +1,20 @@
import re
def preserve_user_region(new_code, old_code, region_name):
"""
替换 new_code region_name 区域为 old_code 中的内容如果有
region_name: 'USER INCLUDE'
"""
pattern = re.compile(
rf"/\*\s*{region_name}\s*BEGIN\s*\*/(.*?)/\*\s*{region_name}\s*END\s*\*/",
re.DOTALL
)
old_match = pattern.search(old_code or "")
if not old_match:
return new_code # 旧文件没有该区域,直接返回新代码
old_content = old_match.group(1)
def repl(m):
return m.group(0).replace(m.group(1), old_content)
# 替换新代码中的该区域
return pattern.sub(repl, new_code, count=1)

25
app/tools/ioc_config.py Normal file
View File

@ -0,0 +1,25 @@
class IocConfig:
def __init__(self, ioc_path):
self.ioc_path = ioc_path
self.config = {}
self._parse()
def _parse(self):
with open(self.ioc_path, encoding='utf-8') as f:
for line in f:
line = line.strip()
if not line or line.startswith('#'):
continue
if '=' in line:
key, value = line.split('=', 1)
self.config[key.strip()] = value.strip()
def is_freertos_enabled(self):
ip_keys = [k for k in self.config if k.startswith('Mcu.IP')]
for k in ip_keys:
if self.config[k] == 'FREERTOS':
return True
for k in self.config:
if k.startswith('FREERTOS.'):
return True
return False

View File

@ -0,0 +1,109 @@
import os
import yaml
import textwrap
from jinja2 import Template
from .code_utils import preserve_user_region
def generate_task_code(task_list, project_path):
# base_dir = os.path.dirname(os.path.abspath(__file__))
project_root = os.path.abspath(os.path.join(os.path.dirname(__file__), "../../"))
template_dir = os.path.join(project_root, "User_code", "task")
output_dir = os.path.join(project_path, "User", "task")
os.makedirs(output_dir, exist_ok=True)
user_task_h_tpl = os.path.join(template_dir, "user_task.h.template")
user_task_c_tpl = os.path.join(template_dir, "user_task.c.template")
init_c_tpl = os.path.join(template_dir, "init.c.template")
task_c_tpl = os.path.join(template_dir, "task.c.template")
freq_tasks = [t for t in task_list if t.get("freq_control", True)]
def render_template(path, context):
with open(path, encoding="utf-8") as f:
tpl = Template(f.read())
return tpl.render(**context)
context_h = {
"thread_definitions": "\n".join([f" osThreadId_t {t['name']};" for t in task_list]),
"freq_definitions": "\n".join([f" float {t['name']};" for t in freq_tasks]),
"stack_definitions": "\n".join([f" UBaseType_t {t['name']};" for t in task_list]),
"last_up_time_definitions": "\n".join([f" float {t['name']};" for t in freq_tasks]),
"task_frequency_definitions": "\n".join([f"#define {t['name'].upper()}_FREQ ({t['frequency']})" for t in freq_tasks]),
"task_init_delay_definitions": "\n".join([f"#define {t['name'].upper()}_INIT_DELAY ({t['delay']})" for t in task_list]),
"task_attr_declarations": "\n".join([f"extern const osThreadAttr_t attr_{t['name']};" for t in task_list]),
"task_function_declarations": "\n".join([f"void {t['function']}(void *argument);" for t in task_list]),
}
# ----------- 生成 user_task.h -----------
user_task_h_path = os.path.join(output_dir, "user_task.h")
new_user_task_h = render_template(user_task_h_tpl, context_h)
if os.path.exists(user_task_h_path):
with open(user_task_h_path, "r", encoding="utf-8") as f:
old_code = f.read()
for region in ["USER INCLUDE", "USER MESSAGE", "USER CONFIG"]:
new_user_task_h = preserve_user_region(new_user_task_h, old_code, region)
with open(user_task_h_path, "w", encoding="utf-8") as f:
f.write(new_user_task_h)
# ----------- 生成 user_task.c -----------
context_c = {
"task_attr_definitions": "\n".join([
f"const osThreadAttr_t attr_{t['name']} = {{\n"
f" .name = \"{t['name']}\",\n"
f" .priority = osPriorityNormal,\n"
f" .stack_size = {t['stack']} * 4,\n"
f"}};"
for t in task_list
])
}
user_task_c = render_template(user_task_c_tpl, context_c)
with open(os.path.join(output_dir, "user_task.c"), "w", encoding="utf-8") as f:
f.write(user_task_c)
# ----------- 生成 init.c -----------
thread_creation_code = "\n".join([
f" task_runtime.thread.{t['name']} = osThreadNew({t['function']}, NULL, &attr_{t['name']});"
for t in task_list
])
context_init = {
"thread_creation_code": thread_creation_code,
}
init_c = render_template(init_c_tpl, context_init)
init_c_path = os.path.join(output_dir, "init.c")
if os.path.exists(init_c_path):
with open(init_c_path, "r", encoding="utf-8") as f:
old_code = f.read()
for region in ["USER INCLUDE", "USER CODE", "USER CODE INIT"]:
init_c = preserve_user_region(init_c, old_code, region)
with open(init_c_path, "w", encoding="utf-8") as f:
f.write(init_c)
# ----------- 生成 task.c -----------
for t in task_list:
desc = t.get("description", "")
desc_wrapped = "\n ".join(textwrap.wrap(desc, 20))
context_task = {
"task_name": t["name"],
"task_function": t["function"],
"task_frequency": f"{t['name'].upper()}_FREQ" if t.get("freq_control", True) else None,
"task_delay": f"{t['name'].upper()}_INIT_DELAY",
"task_description": desc_wrapped,
"freq_control": t.get("freq_control", True)
}
with open(task_c_tpl, encoding="utf-8") as f:
tpl = Template(f.read())
code = tpl.render(**context_task)
task_c_path = os.path.join(output_dir, f"{t['name']}.c")
if os.path.exists(task_c_path):
with open(task_c_path, "r", encoding="utf-8") as f:
old_code = f.read()
for region in ["USER INCLUDE", "USER STRUCT", "USER CODE", "USER CODE INIT"]:
code = preserve_user_region(code, old_code, region)
with open(task_c_path, "w", encoding="utf-8") as f:
f.write(code)
# ----------- 保存任务配置到 config.yaml -----------
config_yaml_path = os.path.join(output_dir, "config.yaml")
with open(config_yaml_path, "w", encoding="utf-8") as f:
yaml.safe_dump(task_list, f, allow_unicode=True)

View File

@ -1,114 +0,0 @@
from PyQt5.QtWidgets import QDialog, QVBoxLayout, QHBoxLayout
from qfluentwidgets import TitleLabel, BodyLabel, TableWidget, PushButton, SubtitleLabel, SpinBox, InfoBar, InfoBarPosition, LineEdit, CheckBox
from PyQt5.QtCore import Qt
import yaml
import os
class TaskConfigDialog(QDialog):
def __init__(self, parent=None, config_path=None):
super().__init__(parent)
self.setWindowTitle("任务配置")
self.resize(900, 480)
layout = QVBoxLayout(self)
layout.setContentsMargins(32, 32, 32, 32)
layout.setSpacing(18)
layout.addWidget(TitleLabel("FreeRTOS 任务配置"))
layout.addWidget(BodyLabel("请添加并配置您的任务参数,支持频率控制与描述。"))
self.table = TableWidget(self)
self.table.setColumnCount(6)
self.table.setHorizontalHeaderLabels(["任务名称", "运行频率", "初始化延迟", "堆栈大小", "任务描述", "频率控制"])
self.table.horizontalHeader().setSectionResizeMode(0, self.table.horizontalHeader().Stretch)
self.table.horizontalHeader().setSectionResizeMode(1, self.table.horizontalHeader().ResizeToContents)
self.table.horizontalHeader().setSectionResizeMode(2, self.table.horizontalHeader().ResizeToContents)
self.table.horizontalHeader().setSectionResizeMode(3, self.table.horizontalHeader().ResizeToContents)
self.table.horizontalHeader().setSectionResizeMode(4, self.table.horizontalHeader().Stretch)
self.table.horizontalHeader().setSectionResizeMode(5, self.table.horizontalHeader().ResizeToContents)
self.table.setMinimumHeight(260)
layout.addWidget(self.table)
btn_layout = QHBoxLayout()
self.add_btn = PushButton("添加任务")
self.del_btn = PushButton("删除选中")
self.ok_btn = PushButton("生成")
self.cancel_btn = PushButton("取消")
btn_layout.addWidget(self.add_btn)
btn_layout.addWidget(self.del_btn)
btn_layout.addStretch()
btn_layout.addWidget(self.ok_btn)
btn_layout.addWidget(self.cancel_btn)
layout.addLayout(btn_layout)
self.add_btn.clicked.connect(self.add_row)
self.del_btn.clicked.connect(self.del_row)
self.ok_btn.clicked.connect(self.accept)
self.cancel_btn.clicked.connect(self.reject)
# 自动读取配置文件
if config_path and os.path.exists(config_path):
try:
with open(config_path, "r", encoding="utf-8") as f:
tasks = yaml.safe_load(f)
if tasks:
for t in tasks:
self.add_row(t)
except Exception:
pass
def add_row(self, task=None):
row = self.table.rowCount()
self.table.insertRow(row)
name = LineEdit(task.get("name", f"Task{row+1}") if task else f"Task{row+1}")
freq = SpinBox()
freq.setRange(1, 10000)
freq.setValue(task.get("frequency", 500) if task else 500)
delay = SpinBox()
delay.setRange(0, 10000)
delay.setValue(task.get("delay", 0) if task else 0)
stack = SpinBox()
stack.setRange(128, 8192)
stack.setSingleStep(128)
stack.setValue(task.get("stack", 256) if task else 256)
desc = LineEdit(task.get("description", "") if task else "请填写任务描述")
freq_ctrl = CheckBox("启用")
freq_ctrl.setChecked(task.get("freq_control", True) if task else True)
self.table.setCellWidget(row, 0, name)
self.table.setCellWidget(row, 1, freq)
self.table.setCellWidget(row, 2, delay)
self.table.setCellWidget(row, 3, stack)
self.table.setCellWidget(row, 4, desc)
self.table.setCellWidget(row, 5, freq_ctrl)
def del_row(self):
selected = self.table.selectedItems()
if selected:
rows = set(item.row() for item in selected)
for row in sorted(rows, reverse=True):
self.table.removeRow(row)
def get_tasks(self):
tasks = []
for row in range(self.table.rowCount()):
name = self.table.cellWidget(row, 0).text().strip()
freq = self.table.cellWidget(row, 1).value()
delay = self.table.cellWidget(row, 2).value()
stack = self.table.cellWidget(row, 3).value()
desc = self.table.cellWidget(row, 4).text().strip()
freq_ctrl = self.table.cellWidget(row, 5).isChecked()
# 校验 stack 必须为 128*2^n
if stack < 128 or (stack & (stack - 1)) != 0 or stack % 128 != 0:
raise ValueError(f"{row+1}行任务“{name}”的堆栈大小必须为128、256、512、1024等128*2^n")
task = {
"name": name,
"function": f"Task_{name}",
"delay": delay,
"stack": stack,
"description": desc,
"freq_control": freq_ctrl
}
if freq_ctrl:
task["frequency"] = freq
tasks.append(task)
return tasks

Binary file not shown.

136
fluent.py
View File

@ -1,136 +0,0 @@
import os
import sys
# 将当前工作目录设置为程序所在的目录,确保无论从哪里执行,其工作目录都正确设置为程序本身的位置,避免路径错误。
os.chdir(os.path.dirname(sys.executable) if getattr(sys, 'frozen', False)else os.path.dirname(os.path.abspath(__file__)))
import pyuac
if not pyuac.isUserAdmin():
try:
pyuac.runAsAdmin(False)
sys.exit(0)
except Exception:
sys.exit(1)
import atexit
import base64
def first_run():
# if not cfg.get_value(base64.b64decode("YXV0b191cGRhdGU=").decode("utf-8")):
# log.error("首次使用请先打开图形界面 March7th Launcher.exe")
input("按回车键关闭窗口. . .")
sys.exit(0)
def run_main_actions():
while True:
version.start()
game.start()
reward.start_specific("dispatch")
Daily.start()
reward.start()
game.stop(True)
def run_sub_task(action):
game.start()
sub_tasks = {
"daily": lambda: (Daily.run(), reward.start()),
"power": Power.run,
"fight": Fight.start,
"universe": Universe.start,
"forgottenhall": lambda: challenge.start("memoryofchaos"),
"purefiction": lambda: challenge.start("purefiction"),
"apocalyptic": lambda: challenge.start("apocalyptic"),
"redemption": Redemption.start
}
task = sub_tasks.get(action)
if task:
task()
game.stop(False)
def run_sub_task_gui(action):
gui_tasks = {
"universe_gui": Universe.gui,
"fight_gui": Fight.gui
}
task = gui_tasks.get(action)
if task and not task():
input("按回车键关闭窗口. . .")
sys.exit(0)
def run_sub_task_update(action):
update_tasks = {
"universe_update": Universe.update,
"fight_update": Fight.update
}
task = update_tasks.get(action)
if task:
task()
input("按回车键关闭窗口. . .")
sys.exit(0)
def run_notify_action():
notif.notify(cfg.notify_template['TestMessage'], "./assets/app/images/March7th.jpg")
input("按回车键关闭窗口. . .")
sys.exit(0)
def main(action=None):
first_run()
# 完整运行
if action is None or action == "main":
run_main_actions()
# 子任务
elif action in ["daily", "power", "fight", "universe", "forgottenhall", "purefiction", "apocalyptic", "redemption"]:
run_sub_task(action)
# 子任务 原生图形界面
elif action in ["universe_gui", "fight_gui"]:
run_sub_task_gui(action)
# 子任务 更新项目
elif action in ["universe_update", "fight_update"]:
run_sub_task_update(action)
elif action in ["screenshot", "plot"]:
tool.start(action)
elif action == "game":
game.start()
elif action == "notify":
run_notify_action()
else:
log.error(f"未知任务: {action}")
input("按回车键关闭窗口. . .")
sys.exit(1)
# 程序结束时的处理器
def exit_handler():
"""注册程序退出时的处理函数用于清理OCR资源."""
ocr.exit_ocr()
if __name__ == "__main__":
try:
atexit.register(exit_handler)
main(sys.argv[1]) if len(sys.argv) > 1 else main()
except KeyboardInterrupt:
log.error("发生错误: 手动强制停止")
input("按回车键关闭窗口. . .")
sys.exit(1)
except Exception as e:
log.error(cfg.notify_template['ErrorOccurred'].format(error=e))
notif.notify(cfg.notify_template['ErrorOccurred'].format(error=e))
input("按回车键关闭窗口. . .")
sys.exit(1)

BIN
img/.DS_Store vendored

Binary file not shown.

BIN
img/M.ico

Binary file not shown.

Before

Width:  |  Height:  |  Size: 20 KiB

BIN
img/M.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 105 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 130 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 96 KiB

Some files were not shown because too many files have changed in this diff Show More