Skip to content

Commit ed5bff5

Browse files
committed
增加导入导出功能
增加build-env工具脚本 变更图标
1 parent 43163bb commit ed5bff5

6 files changed

Lines changed: 289 additions & 55 deletions

File tree

‎Info.plist‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
<key>CFBundlePackageType</key>
1616
<string>APPL</string>
1717
<key>CFBundleShortVersionString</key>
18-
<string>1.0.0</string>
18+
<string>${VERSION}</string>
1919
<key>LSMinimumSystemVersion</key>
2020
<string>10.13.0</string>
2121
<key>NSHighResolutionCapable</key>

‎build-env.sh‎

Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
#!/bin/bash
2+
3+
# 定义帮助信息
4+
show_help() {
5+
echo "用法: $0 [选项]"
6+
echo "选项:"
7+
echo " -i, --install 安装依赖"
8+
echo " -g, --generate 生成图标"
9+
echo " -c, --clean 清理生成的文件和目录"
10+
echo " -s, --svg FILE 指定SVG源文件路径"
11+
echo " -h, --help 显示帮助信息"
12+
echo ""
13+
echo "示例:"
14+
echo " 1. 安装所有依赖:"
15+
echo " $0 -i"
16+
echo ""
17+
echo " 2. 使用默认SVG文件生成图标:"
18+
echo " $0 -g"
19+
echo ""
20+
echo " 3. 指定SVG文件生成图标:"
21+
echo " $0 -g -s custom_icon.svg"
22+
echo ""
23+
echo " 4. 同时安装依赖并生成图标:"
24+
echo " $0 -i -g"
25+
echo ""
26+
echo " 5. 完整示例(自定义SVG):"
27+
echo " $0 -g -s path/to/icon.svg"
28+
}
29+
30+
# 默认值
31+
INSTALL_DEPS=false
32+
GENERATE_ICONS=false
33+
CLEAN_FILES=false
34+
SVG_FILE="icon.svg"
35+
36+
# 解析命令行参数
37+
while [[ $# -gt 0 ]]; do
38+
case $1 in
39+
-i|--install)
40+
INSTALL_DEPS=true
41+
shift
42+
;;
43+
-c|--clean)
44+
CLEAN_FILES=true
45+
shift
46+
;;
47+
-g|--generate)
48+
GENERATE_ICONS=true
49+
shift
50+
;;
51+
-s|--svg)
52+
SVG_FILE="$2"
53+
shift 2
54+
;;
55+
-h|--help)
56+
show_help
57+
exit 0
58+
;;
59+
*)
60+
echo "错误: 未知参数 $1"
61+
show_help
62+
exit 1
63+
;;
64+
esac
65+
done
66+
67+
# 安装依赖
68+
if [ "$INSTALL_DEPS" = true ]; then
69+
echo "正在安装依赖..."
70+
pip3 install pyinstaller
71+
pip3 install dmgbuild
72+
pip install Pillow
73+
pip install cairosvg
74+
brew install cairo
75+
brew install inkscape
76+
fi
77+
78+
# 生成图标
79+
if [ "$GENERATE_ICONS" = true ]; then
80+
echo "正在生成图标..."
81+
# 确保输出目录存在
82+
mkdir -p "icon.iconset"
83+
84+
# 定义图标尺寸数组
85+
sizes=(16 32 64 128 256 512 1024)
86+
87+
# 循环生成不同尺寸的图标
88+
for size in "${sizes[@]}"; do
89+
echo "生成 ${size}x${size} 图标..."
90+
inkscape --export-type=png --export-filename="icon.iconset/icon_${size}x${size}.png" -w $size -h $size "$SVG_FILE"
91+
done
92+
93+
# 使用iconutil生成icns文件
94+
echo "正在生成icns文件..."
95+
iconutil -c icns icon.iconset
96+
echo "图标生成完成"
97+
fi
98+
99+
# 清理文件和目录
100+
if [ "$CLEAN_FILES" = true ]; then
101+
echo "正在清理文件和目录..."
102+
# 清理图标相关文件
103+
rm -rf "icon.iconset"
104+
rm -f "icon.icns"
105+
# 清理打包产物
106+
rm -rf "build"
107+
rm -rf "dist"
108+
rm -f "*.spec"
109+
rm -f "*.dmg"
110+
# 清理Python缓存文件
111+
find . -type d -name "__pycache__" -exec rm -rf {} +
112+
find . -type f -name "*.pyc" -delete
113+
echo "清理完成"
114+
fi
115+
116+
# 如果没有指定任何操作,显示帮助信息
117+
if [ "$INSTALL_DEPS" = false ] && [ "$GENERATE_ICONS" = false ] && [ "$CLEAN_FILES" = false ]; then
118+
show_help
119+
exit 1
120+
fi

‎icon.png‎

10 KB
Loading

‎icon.svg‎

Lines changed: 18 additions & 4 deletions
Loading

‎main.py‎

Lines changed: 150 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
import sys
2020
import re
2121
import subprocess
22+
import json
2223
from PyQt6.QtWidgets import (QApplication, QMainWindow, QWidget, QVBoxLayout, QHBoxLayout,
2324
QFormLayout, QLabel, QLineEdit, QPushButton, QTableWidget, QTableWidgetItem,
2425
QHeaderView, QMessageBox, QSystemTrayIcon, QMenu, QToolBar, QStatusBar,
@@ -408,7 +409,43 @@ def test_command(self):
408409
msg.setText(content)
409410
msg.setDetailedText(content)
410411
msg.setIcon(QMessageBox.Icon.Information if process.returncode == 0 else QMessageBox.Icon.Warning)
411-
msg.exec()
412+
413+
# 添加打开日志目录和脚本目录的按钮
414+
open_logs_button = msg.addButton('打开日志目录', QMessageBox.ButtonRole.ActionRole)
415+
open_scripts_button = msg.addButton('打开脚本目录', QMessageBox.ButtonRole.ActionRole)
416+
msg.addButton(QMessageBox.StandardButton.Ok)
417+
418+
# 设置弹窗样式和布局
419+
msg.setStyleSheet("""
420+
QMessageBox {
421+
min-width: 600px;
422+
}
423+
QLabel {
424+
min-width: 500px;
425+
font-size: 13px;
426+
padding: 10px;
427+
}
428+
QMessageBox QLabel#qt_msgbox_label {
429+
min-height: 40px;
430+
}
431+
QMessageBox QLabel#qt_msgboxex_icon_label {
432+
min-width: 100px;
433+
padding-right: 20px;
434+
}
435+
QPushButton {
436+
min-width: 100px;
437+
padding: 6px;
438+
}
439+
""")
440+
441+
result = msg.exec()
442+
443+
# 处理按钮点击事件
444+
clicked_button = msg.clickedButton()
445+
if clicked_button == open_logs_button:
446+
self.parent().open_logs_directory()
447+
elif clicked_button == open_scripts_button:
448+
self.parent().open_scripts_directory()
412449

413450
# 清理临时文件
414451
os.unlink(temp_file.name)
@@ -893,6 +930,22 @@ def setup_ui(self):
893930

894931
# 帮助菜单
895932
help_menu = menubar.addMenu('帮助')
933+
help_menu = menubar.addMenu('帮助')
934+
935+
# 添加导入/导出菜单
936+
import_export_menu = menubar.addMenu('导入/导出')
937+
self.export_action = QAction('导出任务', self)
938+
self.export_action.setShortcut('Ctrl+Shift+E')
939+
self.export_action.setToolTip('将当前任务配置导出到文件')
940+
self.export_action.triggered.connect(self.export_tasks)
941+
import_export_menu.addAction(self.export_action)
942+
943+
self.import_action = QAction('导入任务', self)
944+
self.import_action.setShortcut('Ctrl+Shift+I')
945+
self.import_action.setToolTip('从文件导入任务配置')
946+
self.import_action.triggered.connect(self.import_tasks)
947+
import_export_menu.addAction(self.import_action)
948+
896949
about_action = QAction('关于', self)
897950
about_action.triggered.connect(self.show_about)
898951
help_menu.addAction(about_action)
@@ -976,54 +1029,7 @@ def setup_ui(self):
9761029
self.refresh_action.triggered.connect(self.refresh_jobs)
9771030
self.view_log_action.triggered.connect(self.view_log)
9781031

979-
def test_command(self):
980-
"""测试命令功能"""
981-
command = self.command_edit.toPlainText().strip()
982-
if not command:
983-
QMessageBox.warning(self, '警告', '请输入要测试的命令')
984-
return
985-
986-
try:
987-
# 创建一个临时的日志文件来捕获命令输出
988-
import tempfile
989-
import subprocess
990-
991-
with tempfile.NamedTemporaryFile(mode='w+', delete=False) as temp_file:
992-
process = subprocess.Popen(
993-
command,
994-
shell=True,
995-
stdout=subprocess.PIPE,
996-
stderr=subprocess.PIPE,
997-
text=True
998-
)
999-
1000-
stdout, stderr = process.communicate(timeout=10)
1001-
1002-
if process.returncode == 0:
1003-
temp_file.write(f"命令执行成功!\n\n输出:\n{stdout}")
1004-
title = '测试成功'
1005-
else:
1006-
temp_file.write(f"命令执行失败!\n\n错误输出:\n{stderr}\n\n标准输出:\n{stdout}")
1007-
title = '测试失败'
1008-
1009-
# 显示结果对话框
1010-
with open(temp_file.name, 'r') as f:
1011-
content = f.read()
1012-
1013-
msg = QMessageBox(self)
1014-
msg.setWindowTitle(title)
1015-
msg.setText(content)
1016-
msg.setDetailedText(content)
1017-
msg.setIcon(QMessageBox.Icon.Information if process.returncode == 0 else QMessageBox.Icon.Warning)
1018-
msg.exec()
1019-
1020-
# 清理临时文件
1021-
os.unlink(temp_file.name)
1022-
1023-
except subprocess.TimeoutExpired:
1024-
QMessageBox.critical(self, '错误', '命令执行超时')
1025-
except Exception as e:
1026-
QMessageBox.critical(self, '错误', f'执行命令时发生错误:{str(e)}')
1032+
10271033

10281034
def add_job(self):
10291035
dialog = JobDialog(self)
@@ -1163,7 +1169,7 @@ def create_script_file(self, name, command):
11631169
script_path = self.get_script_path(name)
11641170
log_path = self.get_log_path(name)
11651171

1166-
script_content = f"#!/bin/bash\n\n# Task: {name}\n# Created: {datetime.datetime.now()}\n\n## {command}\n\n# 设置错误处理\nset -e\n\n# 添加分隔符\necho \"\n----------------------------------------\n执行时间: $(date '+%Y-%m-%d %H:%M:%S')\n----------------------------------------\n\" | tee -a \"{log_path}\"\n\n# 执行命令并记录日志\n{{ {command}; }} 2>&1 | tee -a \"{log_path}\" || {{\n echo \"[$(date '+%Y-%m-%d %H:%M:%S')] 执行失败\" | tee -a \"{log_path}\"\n exit 1\n}}\n\necho \"[$(date '+%Y-%m-%d %H:%M:%S')] 执行成功\" | tee -a \"{log_path}\"\n"
1172+
script_content = f"#!/bin/bash\n\n# Task: {name}\n# Created: {datetime.datetime.now()}\n\n## {command}\n\n# 设置错误处理\nset -e\n\n# 设置工作目录\ncd $(dirname \"$0\")\n\n# 添加分隔符\necho \"\n----------------------------------------\n执行时间: $(date '+%Y-%m-%d %H:%M:%S')\n----------------------------------------\n\" | tee -a \"{log_path}\"\n\n# 执行命令并记录日志\n{{ {command}; }} 2>&1 | tee -a \"{log_path}\" || {{\n echo \"[$(date '+%Y-%m-%d %H:%M:%S')] 执行失败\" | tee -a \"{log_path}\"\n exit 1\n}}\n\necho \"[$(date '+%Y-%m-%d %H:%M:%S')] 执行成功\" | tee -a \"{log_path}\"\n"
11671173

11681174
try:
11691175
with open(script_path, 'w') as f:
@@ -1440,6 +1446,100 @@ def show_about(self):
14401446
'</div>'
14411447
QMessageBox.about(self, '关于', about_text)
14421448

1449+
def export_tasks(self):
1450+
"""导出任务配置到文件"""
1451+
file_path, _ = QFileDialog.getSaveFileName(
1452+
self,
1453+
'导出任务',
1454+
'',
1455+
'JSON文件 (*.json);;所有文件 (*)'
1456+
)
1457+
if not file_path:
1458+
return
1459+
1460+
try:
1461+
tasks = []
1462+
for row in range(self.table.rowCount()):
1463+
task = {
1464+
'name': self.table.item(row, 0).text(),
1465+
'command': self.table.item(row, 1).text(),
1466+
'schedule': self.table.item(row, 2).text(),
1467+
'enabled': self.table.item(row, 3).text() == '是'
1468+
}
1469+
tasks.append(task)
1470+
1471+
with open(file_path, 'w', encoding='utf-8') as f:
1472+
json.dump(tasks, f, ensure_ascii=False, indent=2)
1473+
1474+
QMessageBox.information(self, '成功', '任务配置已成功导出!')
1475+
except Exception as e:
1476+
QMessageBox.critical(self, '错误', f'导出任务配置时发生错误:{str(e)}')
1477+
1478+
def import_tasks(self):
1479+
"""从文件导入任务配置"""
1480+
file_path, _ = QFileDialog.getOpenFileName(
1481+
self,
1482+
'导入任务',
1483+
'',
1484+
'JSON文件 (*.json);;所有文件 (*)'
1485+
)
1486+
if not file_path:
1487+
return
1488+
1489+
try:
1490+
with open(file_path, 'r', encoding='utf-8') as f:
1491+
tasks = json.load(f)
1492+
1493+
if not isinstance(tasks, list):
1494+
raise ValueError('无效的任务配置文件格式')
1495+
1496+
for task in tasks:
1497+
if not all(key in task for key in ['name', 'command', 'schedule', 'enabled']):
1498+
raise ValueError('任务配置缺少必要字段')
1499+
1500+
# 添加任务到表格
1501+
row = self.table.rowCount()
1502+
self.table.insertRow(row)
1503+
self.table.setItem(row, 0, QTableWidgetItem(task['name']))
1504+
self.table.setItem(row, 1, QTableWidgetItem(task['command']))
1505+
self.table.setItem(row, 2, QTableWidgetItem(task['schedule']))
1506+
self.table.setItem(row, 3, QTableWidgetItem('是' if task['enabled'] else '否'))
1507+
1508+
QMessageBox.information(self, '成功', '任务配置已成功导入!')
1509+
self.save_tasks() # 保存导入的任务
1510+
except Exception as e:
1511+
QMessageBox.critical(self, '错误', f'导入任务配置时发生错误:{str(e)}')
1512+
1513+
def save_tasks(self):
1514+
"""将表格中的任务保存到 crontab"""
1515+
try:
1516+
# 清空现有的 crontab 任务
1517+
self.cron.remove_all()
1518+
1519+
# 遍历表格中的所有任务
1520+
for row in range(self.table.rowCount()):
1521+
name = self.table.item(row, 0).text()
1522+
command = self.table.item(row, 1).text()
1523+
schedule = self.table.item(row, 2).text()
1524+
enabled = self.table.item(row, 3).text() == '⬤' and self.table.item(row, 3).foreground().color().name() == '#2e7d32'
1525+
1526+
# 创建执行脚本
1527+
script_path = self.create_script_file(name, command)
1528+
1529+
# 添加到 crontab
1530+
job = self.cron.new(command=script_path, comment=name)
1531+
job.setall(schedule)
1532+
1533+
# 设置任务状态
1534+
if not enabled:
1535+
job.enable(False)
1536+
1537+
# 写入 crontab 文件
1538+
self.cron.write()
1539+
self.refresh_jobs()
1540+
except Exception as e:
1541+
QMessageBox.critical(self, '错误', f'保存任务配置时发生错误:{str(e)}')
1542+
14431543
def toggle_stay_on_top(self):
14441544
flags = self.windowFlags()
14451545
if self.stay_on_top_action.isChecked():

‎screenshots/tray_menu.png‎

82.7 KB
Loading

0 commit comments

Comments
 (0)