Skip to content

0.2.25 程心自定义窗口优先挂到宿主根窗口,避免多出空白 tk 窗口 - #41

Merged
lunzhiPenxil merged 2 commits into
OlivOS-Team:mainfrom
Desom-fu:main
Oct 27, 2026
Merged

lunzhiPenxil merged 2 commits into
OlivOS-Team:mainfrom
Desom-fu:main

Conversation

@Desom-fu

@Desom-fu Desom-fu commented Oct 26, 2026 •

Copy link
Copy Markdown
Member

问题

宿主在没有 tk 根窗口的环境中运行(system.proc_mode = webUI,此时 pluginAPI 的 enable_gui 为假,插件进程不会创建隐藏的 main_tk)时,打开「程心自定义」面板:

  • 面板能正常显示,但会额外弹出一个标题为 tk 的空白窗口
  • 关闭面板,空白 tk 窗口不跟着关
  • 关闭空白 tk 窗口,面板跟着一起消失

编辑窗口、打包窗口同样使用裸 Toplevel(),产生相同的空白根窗口。

终端模式(进程里有 main_tk)不出现该现象,所以只在 webUI 模式复现。

原因

ChanceCustom/GUI.py 里三处顶层窗口都是:

self.UIObject['root'] = tkinter.Toplevel()
self.UIObject['edit_root'] = tkinter.Toplevel()    # 编辑窗口
self.UIObject['edit_root'] = tkinter.Toplevel()    # 打包窗口

tkinter.Toplevel() 不传 master 时会取 tkinter._default_root;当进程里还没有根窗口时,tkinter.BaseWidget._setup() 会隐式创建 Tk()——标题默认就是 tk,窗口成为它的子窗口。于是关窗口时根窗口仍在,关根窗口时 destroy() 连带销毁所有子窗口。

实测(CPython 3.12):

before: _default_root = None
after : _default_root = .
root title      = 'tk'
panel master is root: True
after closing panel -> root still exists: True
after closing tk root -> panel ... application has been destroyed

宿主提供了标准入口 Proc.get_main_root()(即 Proc_data['main_tk']),这里没有使用,也没有兜底分支。

改动

  • 新增 resolve_host_root():经 Proc.get_main_root() 取宿主根窗口,取不到返回 None
  • 新增 create_window():优先挂宿主根窗口,其次复用已存在的 _default_root,都没有则 tkinter.Tk() 自己当主窗口
  • 主面板 start()、编辑窗口 start()、打包窗口 start() 全部改用 create_window()
  • 版本号 0.2.24 → 0.2.25,svn 39 → 40

验证

  • flake8 通过;ruff check 没有引入新的告警(文件里既有的 UP/B 类告警未改动)
  • 行为测试(真实 tkinter,覆盖三种宿主环境):
== ChanceCustom
  OK   A 无宿主 root 前 _default_root 为 None
  OK   A 返回 Tk 而不是 Toplevel
  OK   A 自己成为默认根窗口
  OK   A 面板自身即根窗口(没有额外的 tk 空白窗)
  OK   B 返回 Toplevel
  OK   B 挂在宿主 main_tk 上
  OK   C 宿主抛异常时不崩,仍挂在已有根窗口
  OK   D 复用已存在的默认根窗口

兼容性

  • 终端 / auto 模式:行为与改动前一致,仍挂在宿主 main_tk 上
  • webUI / 无头模式:不再产生空白 tk 窗口
  • 未修改 OlivOS 侧代码;宿主不提供 get_main_root 的旧版本会走 _default_root / Tk() 分支,不会报错

Summary by Sourcery

为程心自定义的各类界面统一管理 Tk 根窗口,避免无头环境产生多余空白窗口。

Bug Fixes:

  • 修复 webUI 等无宿主 Tk 根窗口环境下打开面板、编辑或打包窗口时产生额外空白 tk 窗口及错误联动关闭的问题。

Enhancements:

  • 统一通过共享根窗口创建自定义界面窗口,兼容宿主根窗口、已有默认根窗口及无根窗口场景。

Chores:

  • 将插件版本从 0.2.24(39) 更新至 0.2.25(40)。

主面板、编辑窗口、打包窗口原先直接 tkinter.Toplevel(),依赖进程里刚好已有
tk 根窗口。宿主在没有根窗口的环境下运行(system.proc_mode = webUI,
enable_gui 为假)时,tkinter 会隐式创建一个标题为 tk 的空白根窗口,
这些窗口成为它的子窗口:关它们它留着,关它窗口一起消失。

- GUI.py 新增 resolve_host_root(),经 Proc.get_main_root() 取宿主 main_tk
- 新增 create_window():有根窗口就 Toplevel(master),没有就 Tk() 自己当主窗口
- 主面板 start() 与编辑/打包窗口 start() 改用 create_window()
@sourcery-ai

sourcery-ai Bot commented Oct 26, 2026 •

Copy link
Copy Markdown

Reviewer's Guide

本次 PR 通过统一顶层窗口创建路径,将窗口优先挂载到宿主提供的 main_tk,并为 webUI 或无根窗口环境提供默认根窗口复用及 Tk 兜底,从而消除额外空白 tk 窗口及错误的窗口销毁联动;同时更新发布版本标识。

Sequence diagram for host-aware window creation

sequenceDiagram
    participant Caller as ChanceCustom窗口
    participant Factory as create_window
    participant Resolver as resolve_host_root
    participant Proc as globalProc
    participant Tk as tkinter

    Caller->>Factory: create_window()
    Factory->>Resolver: resolve_host_root()
    Resolver->>Proc: get_main_root()
    alt 宿主提供可用根窗口
        Proc-->>Resolver: main_tk
        Resolver-->>Factory: main_tk
        Factory->>Tk: Toplevel(main_tk)
        Tk-->>Caller: 子窗口
    else 无宿主根窗口
        Resolver-->>Factory: None
        Factory->>Tk: _default_root
        alt 已存在默认根窗口
            Tk-->>Factory: default_root
            Factory->>Tk: Toplevel(default_root)
            Tk-->>Caller: 子窗口
        else 无默认根窗口或根窗口不可用
            Factory->>Tk: Tk()
            Tk-->>Caller: 主窗口
        end
    end
Loading

File-Level Changes

Change Details Files
统一顶层窗口创建逻辑,优先使用宿主根窗口并避免 Tkinter 隐式生成空白根窗口。
  • 新增宿主根窗口解析逻辑,兼容缺少入口或调用异常的旧宿主。
  • 新增窗口工厂:优先挂载宿主根窗口,其次复用现有默认根窗口,最后创建独立 Tk 根窗口。
  • 主面板、编辑窗口和打包窗口改用统一窗口工厂。
ChanceCustom/GUI.py
更新插件版本和构建标识以发布修复版本。
  • 将版本号从 0.2.24 更新至 0.2.25。
  • 将 svn 标识从 39 更新至 40。
ChanceCustom/app.json
ChanceCustom/main.py

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey - I've reviewed your changes and they look great!

Sourcery assessment

Approved.


Sourcery is free for open source - if you like our reviews please consider sharing them ✨

按 OlivaDiceNativeGUI 同款评审意见对齐实现:

- 取根窗口时比较解释器:宿主 main_tk 与 tkinter._default_root 不是同一个 Tk 时
  以 _default_root 为准,避免控件与未指定 master 的 StringVar/BooleanVar 分属
  两个 Tcl 解释器(初始化失败或取值不生效)
- 宿主没有根窗口时不再让窗口自己当根窗口,改为懒加载一个 withdraw() 的隐藏根,
  主面板/编辑窗口/打包窗口都是它的 Toplevel:互为兄弟,关一个不带走另一个,
  也不会出现可见的空白 tk 窗口
@lunzhiPenxil
lunzhiPenxil merged commit c5c7a1f into OlivOS-Team:main Oct 27, 2026
3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants