import bpy import threading import http.server import socketserver import webbrowser import os import tempfile import json import time from pathlib import Path class BlenderWebServer: def __init__(self, port=8000): self.port = port self.server = None self.thread = None self.html_file = None def create_html_content(self): """创建简化的HTML页面内容""" return """ Blender 简单控制面板

Blender 控制面板

简单的Web控制界面

就绪 - 等待操作
""" def start_server(self): """启动HTTP服务器""" try: # 创建HTML文件 temp_dir = tempfile.gettempdir() self.html_file = os.path.join(temp_dir, "blender_web_panel.html") with open(self.html_file, 'w', encoding='utf-8') as f: f.write(self.create_html_content()) # 创建自定义HTTP处理器 class BlenderHTTPRequestHandler(http.server.SimpleHTTPRequestHandler): def do_GET(self): if self.path == '/': # 修复:直接使用服务器的html_file属性 self.path = self.server.html_file elif self.path.startswith('/api/'): self.handle_api_request() return return http.server.SimpleHTTPRequestHandler.do_GET(self) def do_POST(self): if self.path.startswith('/api/'): self.handle_api_request() return return http.server.SimpleHTTPRequestHandler.do_POST(self) def handle_api_request(self): """处理API请求""" try: if self.path == '/api/scene_info': self.send_scene_info() elif self.path == '/api/add_cube': self.handle_add_cube() else: self.send_error(404, "API not found") except Exception as e: self.send_error(500, str(e)) def send_json_response(self, data): """发送JSON响应""" self.send_response(200) self.send_header('Content-type', 'application/json') self.send_header('Access-Control-Allow-Origin', '*') self.send_header( 'Access-Control-Allow-Methods', 'GET, POST, OPTIONS') self.send_header( 'Access-Control-Allow-Headers', 'Content-Type') self.end_headers() self.wfile.write(json.dumps( data, ensure_ascii=False).encode('utf-8')) def send_scene_info(self): """发送场景信息""" try: scene = bpy.context.scene data = { 'scene_name': scene.name, 'object_count': len(bpy.data.objects), 'material_count': len(bpy.data.materials), 'mesh_count': len(bpy.data.meshes), 'blender_version': bpy.app.version_string } self.send_json_response(data) except Exception as e: self.send_json_response({'error': str(e)}) def handle_add_cube(self): """处理添加立方体请求""" try: bpy.ops.mesh.primitive_cube_add() self.send_json_response({'message': '立方体已添加'}) except Exception as e: self.send_json_response({'error': str(e)}) # 创建自定义服务器类 class BlenderTCPServer(socketserver.TCPServer): def __init__(self, server_address, RequestHandlerClass, html_file): self.html_file = html_file super().__init__(server_address, RequestHandlerClass) # 启动服务器 with BlenderTCPServer(("", self.port), BlenderHTTPRequestHandler, self.html_file) as httpd: self.server = httpd print(f"🎨 Blender Web服务器启动在端口 {self.port}") print(f"🌐 访问地址: http://localhost:{self.port}") httpd.serve_forever() except Exception as e: print(f"❌ 服务器启动失败: {e}") def start(self): """在后台线程中启动服务器""" self.thread = threading.Thread(target=self.start_server, daemon=True) self.thread.start() def stop(self): """停止服务器""" if self.server: self.server.shutdown() self.server.server_close() # 全局服务器实例 web_server = None def start_web_server(port=8000): """启动Web服务器""" global web_server if web_server is None: web_server = BlenderWebServer(port) web_server.start() print("✅ Web服务器已启动") return web_server def open_web_panel(): """在Blender中打开Web面板""" # 启动服务器 server = start_web_server() # 检查Blender版本是否支持WEB_BROWSER try: # 尝试在Blender中打开Web浏览器面板 bpy.ops.screen.area_split(direction='VERTICAL', factor=0.5) # 检查可用的区域类型 available_areas = ('EMPTY', 'VIEW_3D', 'IMAGE_EDITOR', 'NODE_EDITOR', 'SEQUENCE_EDITOR', 'CLIP_EDITOR', 'DOPESHEET_EDITOR', 'GRAPH_EDITOR', 'NLA_EDITOR', 'TEXT_EDITOR', 'CONSOLE', 'INFO', 'TOPBAR', 'STATUSBAR', 'OUTLINER', 'PROPERTIES', 'FILE_BROWSER', 'SPREADSHEET', 'PREFERENCES') # 尝试使用TEXT_EDITOR作为替代 for area in bpy.context.screen.areas: if area.type == 'INFO': area.type = 'TEXT_EDITOR' # 创建一个简单的HTML显示 text_editor = area.spaces[0] text_editor.text = bpy.data.texts.new("Web Panel") text_editor.text.write(f""" Blender 简单控制面板 服务器已启动在端口 {server.port} 访问地址: http://localhost:{server.port} 功能: - 添加立方体 - 查看场景信息 请在浏览器中打开上述地址使用Web界面。 """) break print(f"🌐 Web面板信息已在文本编辑器中显示") except Exception as e: print(f"❌ 在Blender中打开Web面板失败: {e}") # 如果失败,尝试在外部浏览器中打开 try: webbrowser.open(f"http://localhost:{server.port}") print(f" 已在外部浏览器中打开Web面板") except Exception as e2: print(f"❌ 打开外部浏览器失败: {e2}") # Blender操作符类 class StartWebServerOperator(bpy.types.Operator): bl_idname = "wm.start_web_server" bl_label = "启动Web服务器" bl_description = "启动Blender Web控制服务器" def execute(self, context): start_web_server() self.report({'INFO'}, f"Web服务器已启动在端口8000") return {'FINISHED'} class OpenWebPanelOperator(bpy.types.Operator): bl_idname = "wm.open_web_panel" bl_label = "打开Web面板" bl_description = "在Blender中打开Web控制面板" def execute(self, context): open_web_panel() return {'FINISHED'} class StopWebServerOperator(bpy.types.Operator): bl_idname = "wm.stop_web_server" bl_label = "停止Web服务器" bl_description = "停止Blender Web控制服务器" def execute(self, context): global web_server if web_server: web_server.stop() web_server = None self.report({'INFO'}, "Web服务器已停止") else: self.report({'WARNING'}, "Web服务器未运行") return {'FINISHED'} # 菜单类 class WebPanelMenu(bpy.types.Menu): bl_idname = "VIEW3D_MT_web_panel" bl_label = "Web控制面板" def draw(self, context): layout = self.layout layout.operator("wm.start_web_server") layout.operator("wm.open_web_panel") layout.operator("wm.stop_web_server") # 面板类 class WebPanelPanel(bpy.types.Panel): bl_label = "Web控制面板" bl_idname = "VIEW3D_PT_web_panel" bl_space_type = 'VIEW_3D' bl_region_type = 'UI' bl_category = 'Web控制' def draw(self, context): layout = self.layout # 服务器状态 global web_server if web_server and web_server.server: layout.label(text="✅ 服务器运行中", icon='CHECKMARK') layout.label(text=f"端口: {web_server.port}") else: layout.label(text="❌ 服务器未运行", icon='ERROR') # 控制按钮 col = layout.column(align=True) col.operator("wm.start_web_server", text="启动服务器", icon='PLAY') col.operator("wm.open_web_panel", text="打开面板", icon='URL') col.operator("wm.stop_web_server", text="停止服务器", icon='X') # 注册函数 def register(): bpy.utils.register_class(StartWebServerOperator) bpy.utils.register_class(OpenWebPanelOperator) bpy.utils.register_class(StopWebServerOperator) bpy.utils.register_class(WebPanelMenu) bpy.utils.register_class(WebPanelPanel) # 添加到视图菜单 def draw_menu(self, context): layout = self.layout layout.menu("VIEW3D_MT_web_panel") bpy.types.VIEW3D_MT_view.append(draw_menu) def unregister(): bpy.utils.unregister_class(StartWebServerOperator) bpy.utils.unregister_class(OpenWebPanelOperator) bpy.utils.unregister_class(StopWebServerOperator) bpy.utils.unregister_class(WebPanelMenu) bpy.utils.unregister_class(WebPanelPanel) # 从视图菜单移除 bpy.types.VIEW3D_MT_view.remove(draw_menu) # 主执行函数 def main(): """主函数 - 一键启动Web服务器和面板""" print("🚀 启动Blender 简单Web控制面板...") # 启动服务器 server = start_web_server() # 等待服务器启动 time.sleep(1) # 打开Web面板 open_web_panel() print("✅ Blender 简单Web控制面板启动完成!") print(f"🌐 访问地址: http://localhost:{server.port}") print(" 提示: 可以在3D视图的侧边栏找到'Web控制'面板") # 注册所有类 register() # 如果直接运行脚本,自动启动 if __name__ == "__main__": main()