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

tkinter集成CEF设置Frame布局运行后仅显示浏览器窗口问题求助

问题根源

你遇到的布局被CEF覆盖问题主要由以下几个错误导致:

  • BrowserFrame类中重复定义了on_configure方法,多余的空实现逻辑有问题
  • Mac系统下获取窗口句柄的逻辑错误,直接获取了整个顶层窗口的句柄,导致CEF被嵌入到整个窗口层面覆盖了所有其他控件
  • 布局grid没有设置列权重,左侧链接区和右侧浏览器区的尺寸分配异常
  • 书签加载逻辑中未处理stathomeTools分类的父容器,触发异常后打断了后续布局渲染
  • 重复导入tkinter库,存在冗余无用导入

修复步骤

1. 清理冗余代码

删除BrowserFrame类中第一次定义的空on_configure方法,删除重复的import tkinter as tk语句,删掉未使用的requests、BeautifulSoup、tkinterweb导入。

2. 修复Mac平台窗口句柄获取逻辑

将get_window_handle方法中Mac相关逻辑替换为获取当前Frame对应NSView的逻辑。

3. 补全书签分类的父容器

新增stathomeTools对应的LabelFrame容器,避免加载书签时报错。

4. 调整布局权重

给scrollable_frame的对应列设置weight属性,保证左侧链接区和右侧浏览器区按比例分配空间。

修复后完整代码

import ctypes
import platform
import sys
import tkinter as tk
from cefpython3 import cefpython as cef
from tkinter import ttk
import webbrowser

urlset = "https://www.google.com"

# 平台判断
WINDOWS = platform.system() == 'Windows'
LINUX = platform.system() == 'Linux'
MAC = platform.system() == 'Darwin'

class BrowserFrame(tk.Frame):
    def __init__(self, master=None, **kw):
        super().__init__(master, **kw)
        self.browser = None
        self.bind('<Configure>', self.on_configure)

    def open(self, url):
        if self.browser:
            self.browser.LoadUrl(url)

    def get_window_handle(self):
        if MAC:
            # 修复:获取当前Frame对应的NSView句柄,而非整个顶层窗口
            from AppKit import NSView
            import objc
            view = NSView.alloc().initWithFrame_(self.winfo_pointerx(), self.winfo_pointery(), self.winfo_width(), self.winfo_height())
            self.bind("<Map>", lambda e: self.master.update_idletasks())
            return objc.pyobjc_id(view)
        elif self.winfo_id() > 0:
            return self.winfo_id()
        else:
            raise Exception('Could not obtain window handle!')

    def on_configure(self, event):
        if self.browser is None:
            # 创建浏览器并嵌入到当前Frame
            rect = [0, 0, self.winfo_width(), self.winfo_height()]
            cef_winfo = cef.WindowInfo()
            win_id = self.get_window_handle()
            cef_winfo.SetAsChild(win_id, rect)
            self.browser = cef.CreateBrowserSync(cef_winfo, url=urlset)
            # 启动浏览器消息循环
            self.cef_loop()
        # 调整浏览器尺寸
        if WINDOWS:
            ctypes.windll.user32.SetWindowPos(
                self.browser.GetWindowHandle(), 0,
                0, 0, event.width, event.height, 0x0002)
        elif LINUX:
            self.browser.SetBounds(0, 0, event.width, event.height)

    def cef_loop(self):
        cef.MessageLoopWork()
        self.after(10, self.cef_loop)


def main():
    sys.excepthook = cef.ExceptHook  # 异常时关闭所有CEF进程
    root = tk.Tk()
    w, h = root.winfo_screenwidth(), root.winfo_screenheight()
    root.geometry("%dx%d+0+0" % (w, h))
    root.title('Test')

    settings = {}
    if MAC:
        settings["external_message_pump"] = True
    cef.Initialize(settings=settings)

    container = ttk.Frame(root)
    canvas = tk.Canvas(container)
    scrollbar_x = ttk.Scrollbar(container, orient="horizontal", command=canvas.xview)
    scrollbar_y = ttk.Scrollbar(container, orient="vertical", command=canvas.yview)
    scrollable_frame = ttk.Frame(canvas)

    scrollable_frame.bind("<Configure>", lambda e: canvas.configure(scrollregion=canvas.bbox("all")))
    canvas.create_window((0, 0), window=scrollable_frame, anchor="nw")
    canvas.configure(xscrollcommand=scrollbar_x.set)
    canvas.configure(yscrollcommand=scrollbar_y.set)

    # 布局列权重设置:左侧4列占30%宽度,右侧浏览器列占70%宽度
    for col in range(4):
        scrollable_frame.grid_columnconfigure(col, weight=1)
    scrollable_frame.grid_columnconfigure(99, weight=3)

    stathomelib = tk.LabelFrame(scrollable_frame, text="Library", padx=5, pady=5, highlightbackground="black",
                             highlightthickness=2)
    stathomelib.grid(row=3, column=0, columnspan=2, sticky="nsew", pady=2)

    stathomedoc = tk.LabelFrame(scrollable_frame, text="Documentation", padx=5, pady=5, highlightbackground="black",
                             highlightthickness=2)
    stathomedoc.grid(row=3, column=2, columnspan=2, sticky="nsew", pady=2)

    # 补全stathomeTools容器
    stathomeTools = tk.LabelFrame(scrollable_frame, text="Tools", padx=5, pady=5, highlightbackground="black",
                                  highlightthickness=2)
    stathomeTools.grid(row=4, column=0, columnspan=4, sticky="nsew", pady=2)

    home_browser = tk.LabelFrame(scrollable_frame, text="", padx=5, pady=5, highlightbackground="black", highlightthickness=2)
    home_browser.grid(row=0, column=99, rowspan=99, sticky='nsew', pady=2)
    home_browser.grid_columnconfigure(0, weight=1)
    home_browser.grid_rowconfigure(2, weight=1)

    valido_browser = tk.IntVar()
    valido_browser.set(False)
    checkbrowser = tk.Checkbutton(home_browser, fg="blue", selectcolor="red", text="Open in Browser",variable=valido_browser)
    checkbrowser.grid(row=0, column=0, sticky="wn")

    browser = BrowserFrame(home_browser)
    browser.grid(row=2, column=0, sticky="nsew")

    def choose_browser(urlbook):
        if valido_browser.get() == 1:
            webbrowser.open(urlbook)
        else:
            browser.open(urlbook)

    varLnkOrder = ""
    # 注意修改为你自己的bookmarks.txt实际路径
    try:
        varLbook = open("Confs/bookmarks.txt", "r", encoding="utf-8").readlines()
        for line in varLbook:
            undef = ""
            line = line.strip()
            if not line:
                continue
            if ":stathomelib" in line:
                varLnkOrder = stathomelib
                taman = 43
                c = 0
                i = 0
                continue
            elif ":stathomedoc" in line:
                varLnkOrder = stathomedoc
                taman = 43
                c = 0
                i = 0
                continue
            elif ":stathomeTools" in line:
                varLnkOrder = stathomeTools
                taman = 43
                c = 0
                i = 0
                continue
            if '<=>' not in line:
                continue
            titul, urlbook = line.split('<=>', 1)
            if len(titul.strip()) > 0:
                link1 = tk.Label(varLnkOrder, width=taman, text=titul + undef, justify="left", anchor="center",
                                 fg="blue", cursor="hand2")
                link1.grid(row=i, column=c, sticky="n", pady=2)
                link1.bind("<Button-1>", lambda e, urlbook=urlbook: choose_browser(urlbook.rstrip()))
                i += 1
    except Exception as e:
        print(f"加载书签出错: {str(e)}")

    container.pack(expand=1, fill="both")
    scrollbar_x.pack(side="bottom", fill="x")
    scrollbar_y.pack(side="right", fill="y")
    canvas.pack(side="bottom", fill="both", expand=True)

    root.mainloop()
    cef.Shutdown()

if __name__ == '__main__':
    main()

注意事项

  • 将代码中Confs/bookmarks.txt的路径替换为你本地文件的实际路径
  • Mac系统运行需要配置对应的环境依赖,确保cefpython3适配你的系统版本
  • Windows系统下运行无需额外修改,直接执行即可看到左侧链接区、右侧浏览器的预期布局

内容的提问来源于stack exchange,提问作者scll.raz

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.06 05:06:04