在Tcl/Tk中抓住一个新窗口

在Tcl/Tk中抓住一个新窗口,tcl,tk,Tcl,Tk,我有一个GUI,其中有一个在新窗口中打开的属性窗口。在某些情况下,(随机且不可确定地再现),当我打开窗口时,会出现衰减误差: grab failed: window not viewable 它不会干扰程序的正常功能,除了打印该消息之外,似乎对任何事情都没有任何影响 创建新窗口的代码为: proc _prop_menu_make_top {{elem {}}} { toplevel .prop_menu #...initialize some variables...

我有一个GUI,其中有一个在新窗口中打开的属性窗口。在某些情况下,(随机且不可确定地再现),当我打开窗口时,会出现衰减误差:

grab failed: window not viewable
它不会干扰程序的正常功能,除了打印该消息之外,似乎对任何事情都没有任何影响

创建新窗口的代码为:

proc _prop_menu_make_top  {{elem {}}}  {
    toplevel .prop_menu
    #...initialize some variables...

    wm title .prop_menu "Properties for $_prop_attr(name)"

    #...create and display the window widgets...

    bind    .prop_menu  <Key-KP_Enter>  {_prop_menu_ok_button}
    bind    .prop_menu  <Return>        {_prop_menu_ok_button}
    bind    .prop_menu  <Escape>        {_prop_menu_cancel_button}

    # catch presses on the window's `x` button
    wm protocol .prop_menu WM_DELETE_WINDOW {
        _prop_menu_cancel_button
    }

    # make the top window unusable
    center_the_toplevel .prop_menu

    focus .prop_menu.main_frame.model_name.entry
    grab release .
    grab set .prop_menu
}

proc center_the_toplevel { window } {    
    if { [string equal $window [winfo toplevel $window]] } {
        set width   [winfo reqwidth $window]
        set height  [winfo reqheight $window]
        set x       [expr {([winfo vrootwidth  $window] - $width) / 2}]
        set y       [expr {([winfo vrootheight $window] - $height) / 2 }]

        wm geometry $window +${x}+${y}
    }
    return
}

proc _prop_menu_ok_button {} {
   #....saving the needed data...
   _prop_menu_cancel_button
}

proc _prop_menu_cancel_button {} {
    destroy .prop_menu
    # make the top window usable again
    grab set .
    # redraw the canvas
    nlv_draw
}
proc_prop_menu_make_top{{{elem{}}{
toplevel.prop_菜单
#…初始化一些变量。。。
wm title.prop_菜单“属性为$prop_attr(名称)”
#…创建并显示窗口小部件。。。
bind.prop_菜单{{u prop_菜单{u ok_按钮}
bind.prop_菜单{{u prop_菜单{u ok_按钮}
bind.prop_菜单{{{u prop_菜单{u取消}
#catch按下窗口的“x”按钮
wm协议。属性菜单wm\U删除窗口{
_道具菜单取消按钮
}
#使顶部窗口无法使用
将顶层的属性菜单居中
focus.prop\u menu.main\u frame.model\u name.entry
抓住释放装置。
抓斗设置。道具菜单
}
进程中心{u顶层{window}{
如果{[string equal$window[winfo topflevel$window]]}{
设置宽度[winfo reqwidth$窗口]
设置高度[winfo reqheight$窗口]
集合x[expr{([winfo vrootwidth$window]-$width)/2}]
设置y[expr{([winfo vrootheight$window]-$height)/2}]
wm几何体$window+${x}+${y}
}
回来
}
程序属性菜单确定按钮{}{
#…保存所需的数据。。。
_道具菜单取消按钮
}
程序属性菜单取消按钮{}{
破坏。道具菜单
#使顶部窗口再次可用
抓取集合。
#重新绘制画布
nlv_绘图
}
有人知道是什么导致了这个问题吗? 有人对如何使bug更容易被重新复制有什么建议吗

编辑: 运行64位的Tcl 8.4.6版,不知道是哪个tk版本。

解释 出于各种原因(一些技术、一些设计原则),Tk只允许在映射到屏幕上的窗口上设置抓取。这几乎肯定是你想要的;鼠标点击应该会进入一个你能看到的窗口

你遇到的问题是,你试图抢得太早了。特别是,Tk推迟为每个小部件创建底层X11/OS窗口(取决于平台),直到它完成决定该小部件的配置,当Tk变为“空闲”时,该配置将被视为。空闲被定义为当进入事件循环且没有待处理的事件时。此时,Tk告诉基本系统图形引擎分配屏幕属性(窗口)的矩形块并将其放在屏幕上。这反过来会触发一系列事件和处理(在这一点上有很多事情在进行),最终会向您显示窗口;只有当窗口显示时,您才能在其上设置抓取

那你怎么知道什么时候可以抓取?嗯,你得等窗户打开。这意味着等待一个事件:您可能会关心此任务的关键事件是
。它们分别指示窗口在逻辑上何时存在于根窗口中,何时实际可查看的内容发生更改,以及何时需要重新绘制某些内容。(在Windows上,第一个和最后一个都是等价的,Tk会在内部重新映射,但Windows根本不会告诉您实际的可见性变化

解决方案 等待小部件可抓取(然后进行抓取)的最简单方法是使用:

tkwait visibility $theWidget
grab set $theWidget
它只是在事件循环中等待
事件在该窗口中出现。(由于该平台上没有事件类型,因此无法在Windows上运行。)您可以将上述内容重写为:

bind $theWidget <Visibility> [list set waiting($theWidget) gotit]
vwait waiting($theWidget)
bind $theWidget <Visibility> {}    ;# remove the binding again!
grab set $theWidget
但是,如果你做的不仅仅是设置抓斗,它可能会变得非常复杂。(Tcl 8.6的协同程序有助于解决这一混乱局面,但它们绝对不能移植到8.4。)


[*]我忘记了在使用非X11 Tk构建的OSX时是否存在问题。如果您在意,请自己检查。

解释 出于各种原因(一些技术、一些设计原则),Tk只允许在映射到屏幕上的窗口上设置抓取。这几乎肯定是你想要的;鼠标点击应该会进入一个你能看到的窗口

你遇到的问题是,你试图抢得太早了。特别是,Tk推迟为每个小部件创建底层X11/OS窗口(取决于平台),直到它完成决定该小部件的配置,当Tk变为“空闲”时,该配置将被视为。空闲被定义为当进入事件循环且没有待处理的事件时。此时,Tk告诉基本系统图形引擎分配屏幕属性(窗口)的矩形块并将其放在屏幕上。这反过来会触发一系列事件和处理(在这一点上有很多事情在进行),最终会向您显示窗口;只有当窗口显示时,您才能在其上设置抓取

那你怎么知道什么时候可以抓取?嗯,你得等窗户打开。这意味着等待一个事件:您可能会关心此任务的关键事件是
。它们分别指示窗口在逻辑上何时存在于根窗口中,何时实际可查看的内容发生更改,以及何时需要重新绘制某些内容。(在Windows上,第一个和最后一个都是等价的,Tk会在内部重新映射,但Windows根本不会告诉您实际的可见性变化

解决方案 等待小部件变得可抓取(然后执行抓取)的最简单方法
bind $theWidget <Map> [list set waiting($theWidget) gotit]
vwait waiting($theWidget)
bind $theWidget <Map> {}           ;# remove the binding again!
grab set $theWidget
bind $theWidget <Map> {
    bind %W <Map> {}
    grab set %W
}