首页 > 解决方案 > 映射/取消映射或提高/降低堆叠顺序以显示/隐藏选项卡式显示?

问题描述

我想构建一个类似于 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笔记本的方法来更改标签。更改选项卡时会有一点闪烁,使用上面的先前代码不存在。也许,notebook 使用grid remove它的行为类似于display:block and display:noneJavaScript 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
}

标签: tcltk

解决方案


经过一番研究,我发现有一种方法可以做你想做的事情ttk::notebook(这很好;它可以处理大小计算等所有棘手的负载),这在ttk::notebook wiki 页面上进行了描述:

# 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>每个页面,以便您可以找出它们的大小(如果/何时发生变化)。

raise只是用and改变堆叠顺序lower是行不通。关键问题是这不会正确影响键盘焦点循环;用户将能够Tab进入他们看不到的小部件,这非常令人困惑!


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


推荐阅读