上传文件至 python
python源码位于此文件夹
This commit is contained in:
185
python/app.py
Normal file
185
python/app.py
Normal file
@@ -0,0 +1,185 @@
|
|||||||
|
import tkinter as tk
|
||||||
|
from tkinter import scrolledtext
|
||||||
|
import logging
|
||||||
|
import pystray
|
||||||
|
from PIL import Image, ImageDraw
|
||||||
|
from pystray import MenuItem as item
|
||||||
|
import threading
|
||||||
|
import subprocess
|
||||||
|
import json
|
||||||
|
from flask import Flask, render_template, request, jsonify
|
||||||
|
|
||||||
|
app = Flask(__name__)
|
||||||
|
# 配置日志记录
|
||||||
|
logging.basicConfig(
|
||||||
|
filename='app.log',
|
||||||
|
level=logging.DEBUG,
|
||||||
|
format='%(asctime)s - %(levelname)s - %(message)s'
|
||||||
|
)
|
||||||
|
# 托盘应用类
|
||||||
|
class TrayApp:
|
||||||
|
def __init__(self, root):
|
||||||
|
self.root = root
|
||||||
|
self.root.title("虚拟机管理控制面板")
|
||||||
|
self.root.geometry("300x200")
|
||||||
|
|
||||||
|
# 左侧控制面板
|
||||||
|
control_frame = tk.Frame(self.root, width=300, bg="lightgray")
|
||||||
|
control_frame.pack(side="left", fill="y")
|
||||||
|
|
||||||
|
# 最小化按钮
|
||||||
|
minimize_button = tk.Button(control_frame, text="最小化到托盘", command=self.hide_window)
|
||||||
|
minimize_button.pack(pady=20)
|
||||||
|
|
||||||
|
# 启动 Flask 服务器的按钮
|
||||||
|
start_server_button = tk.Button(control_frame, text="启动 Flask 服务器", command=self.start_flask_server)
|
||||||
|
start_server_button.pack(pady=10)
|
||||||
|
|
||||||
|
# 后台线程运行托盘图标
|
||||||
|
self.tray_thread = threading.Thread(target=self.create_tray_icon, daemon=True)
|
||||||
|
self.tray_thread.start()
|
||||||
|
|
||||||
|
def hide_window(self):
|
||||||
|
"""隐藏窗口到托盘"""
|
||||||
|
self.root.withdraw() # 隐藏主窗口
|
||||||
|
|
||||||
|
def show_window(self, icon, item):
|
||||||
|
"""从托盘显示窗口"""
|
||||||
|
self.root.deiconify() # 显示主窗口
|
||||||
|
|
||||||
|
def exit_app(self, icon, item):
|
||||||
|
"""退出应用程序"""
|
||||||
|
icon.stop() # 停止托盘图标
|
||||||
|
self.root.quit() # 退出Tkinter主循环
|
||||||
|
self.root.destroy() # 销毁窗口
|
||||||
|
|
||||||
|
def create_tray_icon(self):
|
||||||
|
"""创建系统托盘图标"""
|
||||||
|
image = self.create_image()
|
||||||
|
menu = (item('显示', self.show_window), item('退出', self.exit_app))
|
||||||
|
tray_icon = pystray.Icon("VM Manager", image, "虚拟机管理", menu)
|
||||||
|
tray_icon.run()
|
||||||
|
|
||||||
|
def create_image(self, width=64, height=64, color1="black", color2="white"):
|
||||||
|
"""生成托盘图标"""
|
||||||
|
image = Image.new("RGB", (width, height), color1)
|
||||||
|
dc = ImageDraw.Draw(image)
|
||||||
|
dc.rectangle((width // 2, 0, width, height // 2), fill=color2)
|
||||||
|
dc.rectangle((0, height // 2, width // 2, height), fill=color2)
|
||||||
|
return image
|
||||||
|
|
||||||
|
def start_flask_server(self):
|
||||||
|
"""启动 Flask 服务器"""
|
||||||
|
flask_thread = threading.Thread(target=self.run_flask, daemon=True)
|
||||||
|
flask_thread.start()
|
||||||
|
|
||||||
|
def run_flask(self):
|
||||||
|
"""在后台运行 Flask 服务器"""
|
||||||
|
app.run(host='0.0.0.0', port=5000)
|
||||||
|
|
||||||
|
def log_message(self, message):
|
||||||
|
"""更新日志到GUI中"""
|
||||||
|
self.log_box.config(state="normal")
|
||||||
|
self.log_box.insert(tk.END, message + '\n')
|
||||||
|
self.log_box.yview(tk.END)
|
||||||
|
self.log_box.config(state="disabled")
|
||||||
|
|
||||||
|
|
||||||
|
# Hyper-V 相关操作
|
||||||
|
def run_powershell(command):
|
||||||
|
"""运行 PowerShell 命令并返回输出"""
|
||||||
|
try:
|
||||||
|
result = subprocess.run(
|
||||||
|
["powershell", "-Command", command],
|
||||||
|
capture_output=True, text=True, shell=True
|
||||||
|
)
|
||||||
|
# 返回标准输出和错误输出合并的结果
|
||||||
|
print(result.stdout.strip() + "\n" + result.stderr.strip())
|
||||||
|
return result.stdout.strip() + "\n" + result.stderr.strip()
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
print(f"运行 PowerShell 命令时出错: {e}")
|
||||||
|
return ""
|
||||||
|
|
||||||
|
def get_vms():
|
||||||
|
"""
|
||||||
|
获取所有Hyper-V虚拟机及其状态
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
command = "Get-VM | Select-Object Name, State | ConvertTo-Json"
|
||||||
|
output = run_powershell(command)
|
||||||
|
|
||||||
|
# 找到JSON开始的部分(第一个 '[' 出现的位置)
|
||||||
|
json_start = output.find('[')
|
||||||
|
if json_start != -1:
|
||||||
|
json_data = output[json_start:] # 从 '[' 开始提取
|
||||||
|
vms = json.loads(json_data)
|
||||||
|
else:
|
||||||
|
raise ValueError("未找到有效的JSON数据")
|
||||||
|
|
||||||
|
# 如果只有一个VM,转换为列表
|
||||||
|
if isinstance(vms, dict):
|
||||||
|
vms = [vms]
|
||||||
|
return vms
|
||||||
|
except subprocess.CalledProcessError as e:
|
||||||
|
print("Error fetching VMs:", e)
|
||||||
|
return []
|
||||||
|
except json.JSONDecodeError as e:
|
||||||
|
print("JSON 解析错误:", e)
|
||||||
|
print("原始输出:", output)
|
||||||
|
return []
|
||||||
|
|
||||||
|
|
||||||
|
@app.route('/')
|
||||||
|
def index():
|
||||||
|
"""显示虚拟机状态的网页"""
|
||||||
|
vms = get_vms()
|
||||||
|
return render_template('index.html', vms=vms)
|
||||||
|
|
||||||
|
|
||||||
|
@app.route('/control_vm', methods=['POST'])
|
||||||
|
def control_vm():
|
||||||
|
"""控制虚拟机的操作(启动、停止等)"""
|
||||||
|
try:
|
||||||
|
vm_name = request.json['name']
|
||||||
|
action = request.json['action']
|
||||||
|
|
||||||
|
if action == 'start':
|
||||||
|
command = f"Start-VM -Name '{vm_name}'"
|
||||||
|
elif action == 'stop':
|
||||||
|
command = f"Stop-VM -Name '{vm_name}' -Force"
|
||||||
|
else:
|
||||||
|
return jsonify({'status': 'error', 'message': '无效操作'})
|
||||||
|
|
||||||
|
output = run_powershell(command)
|
||||||
|
print(output)
|
||||||
|
if "虚拟机已处于指定的状态" not in output :
|
||||||
|
result = "操作成功"
|
||||||
|
else:
|
||||||
|
result = "操作失败"
|
||||||
|
|
||||||
|
logging.info(f"虚拟机 {vm_name} 执行操作: {result}")
|
||||||
|
return jsonify({'status': 'success', 'message': result})
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
logging.error(f"控制虚拟机时出错: {e}")
|
||||||
|
return jsonify({'status': 'error', 'message': '操作失败'})
|
||||||
|
|
||||||
|
|
||||||
|
# 启动 Flask 和 GUI 的主程序
|
||||||
|
def gui_thread():
|
||||||
|
root = tk.Tk()
|
||||||
|
app = TrayApp(root)
|
||||||
|
# 自动最小化到托盘
|
||||||
|
app.hide_window() # 启动时隐藏窗口
|
||||||
|
# 在后台启动 Flask 服务器
|
||||||
|
flask_thread = threading.Thread(target=run_flask, daemon=True)
|
||||||
|
flask_thread.start()
|
||||||
|
root.mainloop()
|
||||||
|
|
||||||
|
def run_flask():
|
||||||
|
"""在后台运行 Flask 服务器"""
|
||||||
|
app.run(host='0.0.0.0', port=5050)
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
gui_thread()
|
||||||
Reference in New Issue
Block a user