Map/unmap 或 raise/lower 堆叠顺序到 show/hide 选项卡显示?

Map/unmap or raise/lower stacking order to show/hide tabbed displays?

我想构建一个像 ttk::notebook 小部件一样的选项卡式显示,这是我以前在 JavaScript 中做过的事情,现在想转到 Tcl/Tk。我在 Tk Docs 网站上读到笔记本选项卡是为少量小尺寸选项卡制作的,无论如何都需要对其进行一些自定义才能在每个选项卡上获得关闭按钮和类似功能。

在 JavaScript 中,每个选项卡的“框架”具有相同的几何形状,并且绝对位于父容器内的同一点;并且在任何时候都只能看到一个标签框。单击选项卡按钮时,当前选项卡的可见性设置为隐藏,新选项卡可见。

要在 Tk 中做同样的事情,所有选项卡是否应该在同一行和同一列中“网格化”,并且在单击新选项卡按钮时更改堆叠顺序;或者,由于 Tk 保留了已移除框架的几何形状,是否应该一次只网格化一个选项卡并在每次单击选项卡按钮时替换?或者有更好的方法吗?

每个选项卡都有一个可编辑的文本小部件,如果用户编辑文本、将焦点留在文本小部件中并单击新选项卡按钮,我需要捕获任何更改。

谢谢。


package require Tk
ttk::style theme use clam
ttk::style configure TScrollbar -arrowsize 20

wm geometry . "[winfo screenwidth .]x[winfo screenheight .]+0+0"
wm title . "Notebook Test"

ttk::label .main_l -text "Main Label"

grid .main_l -row 0 -column 0 -sticky ew
grid columnconfigure . 0 -weight 1
grid rowconfigure . 1 -weight 1

foreach n { 3 2 1 } {
  ttk::frame .f$n -relief solid
  tk::label .f$n.l -text "Tab $n"
  tk::text .f$n.t -yscrollcommand ".f$n.v set" \
                  -background white -padx 10 -pady 10 -font { Georgia 12 } \
                  -wrap word -borderwidth 10 -relief flat

  ttk::scrollbar .f$n.v -orient vertical -command ".f$n.t yview"

  grid .f$n -row 1 -column 0 -sticky nsew
  grid .f$n.l -in .f$n -row 0 -columnspan 2 -sticky ew
  grid .f$n.t -in .f$n -row 1 -column 0 -sticky nsew
  grid .f$n.v -in .f$n -row 1 -column 1 -sticky ns

  grid rowconfigure .f$n 1 -weight 1
  grid columnconfigure .f$n 0 -weight 1

  set tab_foc($n) .f$n
}

focus .f1
set cur_tab 1

bind . <Alt-KeyPress-1> { navigate 1 }
bind . <Alt-KeyPress-2> { navigate 2 }
bind . <Alt-KeyPress-3> { navigate 3 }

proc navigate { n } {
  global cur_tab tab_foc
  set tab_foc($cur_tab) [focus]
  set cur_tab $n
  focus $tab_foc($n)
  raise .f$n
}

我尝试了一个简单的示例,使用 ttk::notebook 样式设置为没有选项卡,并使用笔记本的 select 方法更改选项卡。更改选项卡时会出现一些闪烁,使用上面的先前代码不会出现这种情况。也许,笔记本使用 grid remove 并且它的行为类似于 display:block and display:none 与 JavaScript 中的 visibility:visible and visibility:hidden,因为 display 改变布局并可能导致闪烁,而 visibility 不改变布局。当然,我不确定。

package require Tk
ttk::style theme use clam
ttk::style configure TScrollbar -arrowsize 20
ttk::style layout Plain.TNotebook.Tab null

wm geometry . "[winfo screenwidth .]x[winfo screenheight .]+0+0"
wm title . "Notebook Test"

set nb [ttk::notebook .pnb -style Plain.TNotebook]
ttk::label .main_l -text "Main Label"
grid .main_l -row 0 -column 0 -sticky ew
grid columnconfigure . 0 -weight 1
grid rowconfigure . 1 -weight 1
grid $nb -row 1 -column 0 -sticky nsew

foreach n { 1 2 3 } {
  set f [ ttk::frame $nb.f$n -relief solid ]
  tk::label $f.l -text "Tab $n"
  tk::text $f.t -yscrollcommand "$f.v set" \
                 -background white -padx 10 -pady 10 \
                 -font { Georgia 12 } -wrap word -borderwidth 10 -relief flat

  ttk::scrollbar $f.v -orient vertical -command "$f.t yview"

  $nb add $f -sticky nsew
  grid rowconfigure $f 1 -weight 1
  grid columnconfigure $f 0 -weight 1
  grid $f.l -in $f -row 0 -columnspan 2 -sticky ew
  grid $f.t -in $f -row 1 -column 0 -sticky nsew
  grid $f.v -in $f -row 1 -column 1 -sticky ns
}

$nb select $nb.f1

bind . <Alt-KeyPress-1> { navigate 1 }
bind . <Alt-KeyPress-2> { navigate 2 }
bind . <Alt-KeyPress-3> { navigate 3 }

proc navigate { n } {
  global nb
  $nb select $nb.f$n
}

我也试过使用 place,但仍然遇到闪烁。也许,我编码有误。

package require Tk
ttk::style theme use clam
ttk::style configure TScrollbar -arrowsize 20

wm geometry . "[winfo screenwidth .]x[winfo screenheight .]+0+0"
wm title . "Notebook Test"

ttk::label .main_l -text "Main Label"
place .main_l -relx 0.0 -rely 0.0  -relwidth 1.0 -height 30

foreach n { 3 2 1 } {
  set f [ ttk::frame .f$n -relief solid ]
  tk::label $f.l -text "Tab $n"
  tk::text $f.t -yscrollcommand "$f.v set" \
                 -background white -padx 10 -pady 10 \
                 -font { Georgia 12 } -wrap word -borderwidth 10 -relief flat

  ttk::scrollbar $f.v -orient vertical -command "$f.t yview"

  place $f -relx 0.0 -y 31 -relwidth 1.0 -relheight 1.0 -height -30
  place $f.l -in $f -relx 0.0 -rely 0.0 -relwidth 1.0 -height 30
  place $f.t -in $f -relx 0.0 -rely 0.0 -y 30 -relwidth 1.0 -width -23 -relheight 1.0 -height -30
  place $f.v -in $f -relx 1.0 -x -22 -rely 0.0 -y 31 -width 22 -relheight 1.0 -height -30

  if { $n > 1 } { place forget $f }
}

set curTab 1

bind . <Alt-KeyPress-1> { navigate 1 }
bind . <Alt-KeyPress-2> { navigate 2 }
bind . <Alt-KeyPress-3> { navigate 3 }

proc navigate { n } {
   global curTab
   place .f$n -relx 0.0 -y 31 -relwidth 1.0 -relheight 1.0 -height -30
   raise .f$n
   place forget .f$curTab
   set curTab $n
}

单独使用 grid 也有闪烁,但是在映射之前使用 lower 并在删除当前映射的选项卡之前执行 update idletasks 似乎已经消除或显着减少了闪烁。也就是说,尝试将选项卡映射到当前选项卡后面,使其类似于隐藏,更新 idletasks 以调整其大小,然后取消映射当前选项卡。对我来说奇怪的是,如果单击一个条目元素然后导航到另一个选项卡并在执行任何其他操作之前键入一些文本,该文本会出现在被 grid remove 删除的选项卡中。为什么?它不再处于焦点循环中,但在移除后仍保留键盘焦点。

grid .main_l -row 0 -column 0 -sticky ew
grid columnconfigure . 0 -weight 1
grid rowconfigure . 1 -weight 1

foreach n { 3 2 1 } {
  set f [ ttk::frame .f$n -relief solid ]
  tk::label $f.l -text "Tab $n"
  ttk::entry .f$n.e1
  ttk::entry .f$n.e2
  tk::text $f.t -yscrollcommand "$f.v set" \
                 -background white -padx 10 -pady 10 -font { Georgia 12 } -wrap word -borderwidth 10 -relief flat

  ttk::scrollbar $f.v -orient vertical -command "$f.t yview"

  grid .f$n -row 1 -column 0 -sticky nsew
  grid rowconfigure .f$n 1 -weight 1
  grid columnconfigure .f$n 0 -weight 1
  grid .f$n.l -in .f$n -row 0 -columnspan 2 -sticky ew
  grid .f$n.t -in .f$n -row 1 -column 0 -sticky nsew
  grid .f$n.v -in .f$n -row 1 -column 1 -sticky ns
  grid .f$n.e1 -in .f$n -row 0 -column 2
  grid .f$n.e2 -in .f$n -row 1 -column 2

  if { $n > 1 } { grid remove $f }

}

set curTab 1

bind . <Alt-KeyPress-1> { if { $curTab != 1 } { navigate 1 } }
bind . <Alt-KeyPress-2> { if { $curTab != 2 } { navigate 2 } }
bind . <Alt-KeyPress-3> { if { $curTab != 3 } { navigate 3 } }

proc navigate { n } {
   global curTab
   lower .f$n
   grid .f$n
   update idletasks
   grid remove .f$curTab
   set curTab $n
}

经过一些研究,我发现有一种方法可以用 ttk::notebook 做你想做的事(这很好;它处理了大小计算等所有棘手的问题)和这个在 ttk::notebook wiki page:

上有描述
# Need this style
ttk::style layout Plain.TNotebook.Tab null

# Make the notebook and pages
ttk::notebook .foo -style Plain.TNotebook
.foo add [button .foo.bar -text bar]
.foo add [button .foo.baz -text baz]

# Select a page; you'll need to provide a mechanism for users to switch
.foo select .foo.bar

边界有一些额外的复杂性,您可能不想对此采取任何措施。 (样式编码很复杂,唉。)


否则,grid remove 是一个有用的工具,因为它会停止管理小部件(导致它变得未映射且不可聚焦),而无需清除您希望如何管理它的设置;当您将小部件放回原位时,它将使用之前的内容。但是,您仍然会遇到内容小部件大小不同的问题,因此每次更改页面内容时 运行 调整大小的风险。

由于您在这样做时已经开始做很多工作,您可能会考虑改用 place。主要技巧是在每个页面上绑定 <Configure>,以便您可以找出它们的大小(if/when 会发生变化)。

只需更改 raiselower 的堆叠顺序 不会 起作用。关键问题是这不会正确影响键盘焦点周期;用户将能够 Tab 进入他们看不到的小部件,这非常令人困惑!


在回答您在评论中提出的问题时,默认情况下选择在 Tk 应用程序中是全局的(或者在 X11 上的所有应用程序中是全局的;这就是 PRIMARY 选择的工作方式)。您可以通过将小部件配置为不导出选择来为其提供独立选择。