You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Jupyter中内嵌Flask+three.js应用?无需先打开浏览器

问题分析与解决方案

核心原因

你的问题出在Flask开发服务器的默认行为上:

  • 默认是单线程模式,同一时间只能处理一个请求
  • 第一次请求会触发应用初始化(比如加载three.js资源、初始化路由),如果没有提前发起请求,IFrame的请求会因为服务器未就绪而返回空白
  • webbrowser.open相当于手动触发了第一个请求,让服务器完成初始化并进入可处理请求的状态

无需打开浏览器的解决方法

修改Flask的运行参数,启用多线程并关闭自动重载,同时添加短延迟确保服务器启动完成:

修改后的代码

import dataclasses
import socket
import threading
import time

from zndraw import app, globals


@dataclasses.dataclass
class ZnDraw:
    file: str
    port: int = None
    width: int = 700
    height: int = 600

    def __post_init__(self):
        if self.port is None:
            sock = socket.socket()
            sock.bind(("", 0))
            self.port = sock.getsockname()[1]
            sock.close()

        globals.config.file = self.file
        # 启用多线程,关闭自动重载和调试模式,避免线程冲突
        self.thread = threading.Thread(
            target=app.run,
            kwargs={
                "port": self.port,
                "threaded": True,
                "use_reloader": False,
                "debug": False
            }
        )
        self.thread.daemon = True  # 设置守护线程,随Jupyter进程退出自动结束
        self.thread.start()
        
        # 等待服务器启动就绪(可根据实际情况调整延迟时间)
        time.sleep(1)

    def _repr_html_(self):
        from IPython.display import IFrame
        return IFrame(
            src=f"http://127.0.0.1:{self.port}", 
            width=self.width, 
            height=self.height
        )._repr_html_()

    def __del__(self):
        if hasattr(self, 'thread') and self.thread.is_alive():
            # Flask开发服务器没有优雅关闭的简单方法,这里直接终止线程
            self.thread.join(timeout=1)

关键改动说明

  1. threaded=True:让Flask服务器支持多线程,可同时处理IFrame的请求,无需提前触发初始化
  2. use_reloader=False:关闭自动重载功能,避免重载时创建额外线程导致冲突
  3. debug=False:调试模式在多线程环境下不稳定,建议关闭
  4. time.sleep(1):给服务器预留启动时间,确保IFrame请求时服务器已就绪
  5. 守护线程:设置daemon=True,避免Jupyter关闭后Flask线程残留

兼容MyBinder/Colab的优化方案

远程环境(如Binder、Colab)中,本地端口无法直接通过127.0.0.1访问,需要借助Jupyter的服务器代理功能:

步骤1:安装依赖

  • 在Binder的requirements.txt中添加:jupyter-server-proxy
  • 在Colab中执行:!pip install jupyter-server-proxy

步骤2:修改IFrame的源路径

将_repr_html_中的src改为Jupyter代理路径:

def _repr_html_(self):
    from IPython.display import IFrame
    # 使用Jupyter的代理路由,兼容本地和远程环境
    src = f"/proxy/{self.port}"
    return IFrame(src=src, width=self.width, height=self.height)._repr_html_()

原理

Jupyter Server Proxy会将/proxy/{port}的请求转发到本地对应端口,远程环境的用户可以通过Jupyter的域名访问Flask应用,无需额外配置端口转发。

额外注意事项

  • 如果你的Flask应用有复杂的初始化逻辑,建议在__post_init__中提前完成初始化,而不是等待第一个请求触发
  • 避免在Jupyter中长时间运行多个Flask实例,可能导致端口占用或资源泄漏
  • 生产环境建议使用Gunicorn等专业WSGI服务器,但Jupyter环境中用Flask开发服务器足够

内容的提问来源于stack exchange,提问作者PythonF

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.23 11:17:16