本文目录导读:

- 使用 Python + Tkinter(最简单)
- 使用 Python + PyQt5(功能强大)
- 使用 HTML + JavaScript + Python后端(Web界面)
- 使用 Electron(桌面应用,跨平台)
- 最佳实践建议:
我来介绍几种编写带界面脚本的方法,从简单到复杂:
使用 Python + Tkinter(最简单)
文件管理工具示例:
import tkinter as tk
from tkinter import filedialog, messagebox, ttk
import os
import shutil
class FileManager:
def __init__(self, root):
self.root = root
self.root.title("文件管理工具")
self.root.geometry("500x400")
# 创建界面组件
self.create_widgets()
def create_widgets(self):
# 文件选择区域
frame = ttk.Frame(self.root, padding="10")
frame.grid(row=0, column=0, sticky=(tk.W, tk.E))
ttk.Button(frame, text="选择文件",
command=self.select_file).grid(row=0, column=0, padx=5)
self.file_label = ttk.Label(frame, text="未选择文件")
self.file_label.grid(row=0, column=1, padx=5)
# 操作按钮区域
frame2 = ttk.Frame(self.root, padding="10")
frame2.grid(row=1, column=0, sticky=(tk.W, tk.E))
ttk.Button(frame2, text="重命名",
command=self.rename_file).grid(row=0, column=0, padx=5)
ttk.Button(frame2, text="复制",
command=self.copy_file).grid(row=0, column=1, padx=5)
ttk.Button(frame2, text="移动",
command=self.move_file).grid(row=0, column=2, padx=5)
ttk.Button(frame2, text="删除",
command=self.delete_file).grid(row=0, column=3, padx=5)
def select_file(self):
filename = filedialog.askopenfilename()
if filename:
self.file_label.config(text=filename)
def rename_file(self):
# 实现重命名功能
pass
def copy_file(self):
# 实现复制功能
pass
def move_file(self):
# 实现移动功能
pass
def delete_file(self):
# 实现删除功能
pass
# 运行应用
if __name__ == "__main__":
root = tk.Tk()
app = FileManager(root)
root.mainloop()
使用 Python + PyQt5(功能强大)
批量重命名工具:
import sys
from PyQt5.QtWidgets import *
from PyQt5.QtCore import Qt
import os
class BatchRename(QWidget):
def __init__(self):
super().__init__()
self.initUI()
def initUI(self):
self.setWindowTitle('批量重命名工具')
self.setGeometry(300, 300, 600, 400)
# 主布局
layout = QVBoxLayout()
# 文件夹选择
folder_layout = QHBoxLayout()
self.folder_input = QLineEdit()
self.folder_input.setPlaceholderText('选择文件夹...')
self.browse_button = QPushButton('浏览')
self.browse_button.clicked.connect(self.browse_folder)
folder_layout.addWidget(self.folder_input)
folder_layout.addWidget(self.browse_button)
# 重命名规则
rule_group = QGroupBox('重命名规则')
rule_layout = QVBoxLayout()
# 前缀
prefix_layout = QHBoxLayout()
prefix_layout.addWidget(QLabel('前缀:'))
self.prefix_input = QLineEdit()
prefix_layout.addWidget(self.prefix_input)
rule_layout.addLayout(prefix_layout)
# 后缀
suffix_layout = QHBoxLayout()
suffix_layout.addWidget(QLabel('后缀:'))
self.suffix_input = QLineEdit()
suffix_layout.addWidget(self.suffix_input)
rule_layout.addLayout(suffix_layout)
# 起始编号
number_layout = QHBoxLayout()
number_layout.addWidget(QLabel('起始编号:'))
self.number_input = QSpinBox()
self.number_input.setRange(1, 9999)
number_layout.addWidget(self.number_input)
rule_layout.addLayout(number_layout)
rule_group.setLayout(rule_layout)
# 预览列表
self.file_list = QListWidget()
# 执行按钮
self.execute_button = QPushButton('执行重命名')
self.execute_button.clicked.connect(self.rename_files)
# 添加到主布局
layout.addLayout(folder_layout)
layout.addWidget(rule_group)
layout.addWidget(QLabel('文件预览:'))
layout.addWidget(self.file_list)
layout.addWidget(self.execute_button)
self.setLayout(layout)
def browse_folder(self):
folder = QFileDialog.getExistingDirectory(self, '选择文件夹')
if folder:
self.folder_input.setText(folder)
self.load_files(folder)
def load_files(self, folder):
self.file_list.clear()
for file in os.listdir(folder):
if os.path.isfile(os.path.join(folder, file)):
self.file_list.addItem(file)
def rename_files(self):
folder = self.folder_input.text()
prefix = self.prefix_input.text()
suffix = self.suffix_input.text()
start_num = self.number_input.value()
if not folder or not os.path.exists(folder):
QMessageBox.warning(self, '错误', '请选择有效的文件夹')
return
count = 0
for i in range(self.file_list.count()):
old_name = self.file_list.item(i).text()
old_path = os.path.join(folder, old_name)
# 构建新文件名
ext = os.path.splitext(old_name)[1]
new_name = f"{prefix}{start_num + i}{suffix}{ext}"
new_path = os.path.join(folder, new_name)
try:
os.rename(old_path, new_path)
count += 1
except Exception as e:
QMessageBox.warning(self, '错误', f'重命名失败: {old_name}')
QMessageBox.information(self, '成功', f'成功重命名 {count} 个文件')
self.load_files(folder)
# 运行应用
if __name__ == '__main__':
app = QApplication(sys.argv)
window = BatchRename()
window.show()
sys.exit(app.exec_())
使用 HTML + JavaScript + Python后端(Web界面)
系统监控工具网页版:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">系统监控工具</title>
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
<style>
body {
font-family: Arial, sans-serif;
background: #f0f2f5;
margin: 0;
padding: 20px;
}
.container {
max-width: 1000px;
margin: 0 auto;
}
.stats-grid {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 20px;
margin-bottom: 20px;
}
.stat-card {
background: white;
border-radius: 8px;
padding: 20px;
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
}
.stat-value {
font-size: 32px;
font-weight: bold;
color: #1890ff;
}
.stat-label {
color: #666;
margin-bottom: 10px;
}
button {
background: #1890ff;
color: white;
border: none;
padding: 10px 20px;
border-radius: 4px;
cursor: pointer;
margin: 5px;
}
button:hover {
background: #40a9ff;
}
#log-output {
background: #000;
color: #0f0;
padding: 15px;
border-radius: 4px;
height: 200px;
overflow-y: auto;
font-family: monospace;
}
</style>
</head>
<body>
<div class="container">
<h1>系统监控工具</h1>
<div class="stats-grid">
<div class="stat-card">
<div class="stat-label">CPU使用率</div>
<div class="stat-value" id="cpu">0%</div>
</div>
<div class="stat-card">
<div class="stat-label">内存使用</div>
<div class="stat-value" id="memory">0%</div>
</div>
<div class="stat-card">
<div class="stat-label">磁盘使用</div>
<div class="stat-value" id="disk">0%</div>
</div>
</div>
<div style="background: white; padding: 20px; border-radius: 8px; margin-bottom: 20px;">
<h2>性能趋势</h2>
<canvas id="trendChart"></canvas>
</div>
<div style="background: white; padding: 20px; border-radius: 8px; margin-bottom: 20px;">
<h2>操作面板</h2>
<button onclick="runCommand('start_service')">启动服务</button>
<button onclick="runCommand('stop_service')">停止服务</button>
<button onclick="runCommand('reboot')">重启系统</button>
<button onclick="runCommand('update_all')">更新所有包</button>
</div>
<div style="background: white; padding: 20px; border-radius: 8px;">
<h2>系统日志</h2>
<div id="log-output"></div>
</div>
</div>
<script>
// 更新统计信息
function updateStats() {
fetch('/api/stats')
.then(response => response.json())
.then(data => {
document.getElementById('cpu').textContent = data.cpu + '%';
document.getElementById('memory').textContent = data.memory + '%';
document.getElementById('disk').textContent = data.disk + '%';
// 更新图表
updateChart(data);
});
}
// 图表更新
let chart;
function updateChart(data) {
const ctx = document.getElementById('trendChart').getContext('2d');
if (!chart) {
chart = new Chart(ctx, {
type: 'line',
data: {
labels: [],
datasets: [{
label: 'CPU',
data: [],
borderColor: 'rgb(255, 99, 132)',
backgroundColor: 'rgba(255, 99, 132, 0.2)'
}, {
label: 'Memory',
data: [],
borderColor: 'rgb(54, 162, 235)',
backgroundColor: 'rgba(54, 162, 235, 0.2)'
}]
},
options: { responsive: true }
});
}
// 更新数据
chart.data.labels.push(new Date().toLocaleTimeString());
chart.data.datasets[0].data.push(data.cpu);
chart.data.datasets[1].data.push(data.memory);
chart.update();
}
// 执行命令
function runCommand(command) {
fetch('/api/command', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ command: command })
})
.then(response => response.json())
.then(data => {
logOutput(data.message);
});
}
// 日志输出
function logOutput(message) {
const log = document.getElementById('log-output');
const entry = document.createElement('div');
entry.textContent = `[${new Date().toLocaleTimeString()}] ${message}`;
log.appendChild(entry);
log.scrollTop = log.scrollHeight;
}
// 定时更新
setInterval(updateStats, 5000);
updateStats();
</script>
</body>
</html>
Python后端:
from flask import Flask, jsonify, request, render_template
import psutil
import subprocess
import json
app = Flask(__name__)
@app.route('/')
def index():
return render_template('index.html')
@app.route('/api/stats')
def get_stats():
cpu_percent = psutil.cpu_percent(interval=1)
memory_percent = psutil.virtual_memory().percent
disk_percent = psutil.disk_usage('/').percent
return jsonify({
'cpu': cpu_percent,
'memory': memory_percent,
'disk': disk_percent
})
@app.route('/api/command', methods=['POST'])
def execute_command():
data = request.json
command = data.get('command')
commands = {
'start_service': 'sudo systemctl start myservice',
'stop_service': 'sudo systemctl stop myservice',
'reboot': 'sudo reboot',
'update_all': 'sudo apt-get update && sudo apt-get upgrade -y'
}
cmd = commands.get(command, 'echo "Unknown command"')
try:
result = subprocess.run(cmd, shell=True, capture_output=True, text=True)
return jsonify({'message': f'命令执行完成: {result.stdout}'})
except Exception as e:
return jsonify({'message': f'命令执行失败: {str(e)}'})
if __name__ == '__main__':
app.run(debug=True)
使用 Electron(桌面应用,跨平台)
package.json:
{
"name": "system-monitor",
"version": "1.0.0",
"main": "main.js",
"scripts": {
"start": "electron ."
},
"devDependencies": {
"electron": "^21.0.0"
}
}
main.js:
const { app, BrowserWindow } = require('electron');
function createWindow() {
const win = new BrowserWindow({
width: 800,
height: 600,
webPreferences: {
nodeIntegration: true
}
});
win.loadFile('index.html');
}
app.whenReady().then(createWindow);
最佳实践建议:
-
选择框架建议
- 简单工具:Python Tkinter
- 专业桌面应用:PyQt5 / Electron
- Web界面:Flask + HTML/JS
- 系统集成:结合实际需求选择
-
界面设计原则
- 保持简洁直观
- 提供清晰的反馈
- 错误处理友好
- 支持撤销/恢复
-
功能实现技巧
- 异步处理耗时操作
- 使用线程池
- 完善日志记录
- 支持配置保存
-
部署分发
- PyInstaller(Python)
- Electron Builder
- Docker(Web版)
选择合适的技术栈,根据用户需求逐步完善功能,学会使用现成的UI库和组件能大大提升开发效率。