From 70e042f73a857805a6e5cf9a471a77ca79e4e0eb Mon Sep 17 00:00:00 2001 From: elmgates Date: Tue, 8 Oct 2024 19:14:44 +0800 Subject: [PATCH] =?UTF-8?q?=E4=B8=8A=E4=BC=A0=E6=96=87=E4=BB=B6=E8=87=B3?= =?UTF-8?q?=20python?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit python源码位于此文件夹 --- python/app.py | 185 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 185 insertions(+) create mode 100644 python/app.py diff --git a/python/app.py b/python/app.py new file mode 100644 index 0000000..a7a6e58 --- /dev/null +++ b/python/app.py @@ -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()