在使用 Tkinter 的 OptionMenu 小部件时,如果选项列表较多或选项内容较长,可能会导致溢出的问题(例如,选项框变得过大或超出窗口边界)。以下是常见的溢出问题及解决方法:

解决Tkinter中OptionMenu溢出问题_Python

1、问题背景

当在 Windows 系统下使用 Python 2.7 创建 Tkinter 应用程序时,OptionMenu 的右上角(单击时会显示选项的下拉按钮)被截断在中间。以下代码可以重现此问题:

from Tkinter import Tk, StringVar
from ttk import OptionMenu

root = Tk()

options = list('ABC')
var = StringVar(value='A')
om = OptionMenu(root, var, var.get(), *options)
om.configure(width=25)
om.pack()

root.mainloop()

问题现象如下图所示:

解决Tkinter中OptionMenu溢出问题_Python_02

经过尝试,修改填充布局管理器的 padx 和 ipadx 关键字以及使用网格布局均无法完全显示下拉箭头。

2、解决方案

对于该问题,有以下解决方案:

  1. 更新 Tcl/Tk 版本

根据 bug 报告,此问题在 Tk 8.5.8 中已修复。更新 Python 中的 Tcl/Tk 可以解决此问题,但更新过程可能比较复杂。

  1. 修改 vistaTheme.tcl 脚本文件

也可以修改 Tk 库中包含的脚本文件 vistaTheme.tcl 来解决此问题。在 Python 中使用以下代码获取 Tk 库的路径:

from Tkinter import Tk
tk = Tk()
tk.eval("set tk_library")

然后,编辑 /ttk/vistaTheme.tcl 文件,并将内容修改为以下内容:

#vistaTheme.tcl
#
#Vista Theme for Tk 8.5 and above
proc vistaTheme::Style { style }= {
   if {$style ==  "palette"} {
      set map  {
        activeBackground  #ffffff
        activeForeground  #000000
        background  #ffffff
        disabledBackground  #f0f0f0
        disabledForeground  #808080
        foreground  #000000
        highlightBackground  #4682b4
        highlightColor  #4682b4
        highlightThickness  1
        inactiveBackground  #ffffff
        inactiveForeground  #505050
        selectBackground  #d9d9f3
        selectForeground  #000000
      }

      set bar map {
        troughcolor		#ffffff
        thumbcolor		#d9d9f3
        troughrelief		flat
      }

      set pens map {
        background		#ffffff
        foreground		#000000
        disabledforeground	#808080
        disabledbackground	#f0f0f0
        selectbackground	#d9d9f3
        activebackground	#4682b4
        activeforeground	#ffffff
      }

      set menu map {
        background               #ffffff
        disabledforeground        #808080
        disabledbackground        #d9d9d9
        foreground               #000000
        activebackground          #4682b4
        activeforeground          #ffffff
      }

      set scrollbar map {
        troughcolor                 #ffffff
        troughrelief                flat
        background                 #d9d9f3
        thumbcolor                 #666666
        arrowcolor                 #666666
        bordercolor                #666666
        relief                     flat
      }

      set progressbar map {
        troughcolor                 #ffffff
        troughrelief                flat
        background                 #d9d9f3
        barcolor                   #4682b4
        bordercolor                #4682b4
        relief                     flat
      }
      return
   }

   vistaTheme::Style palette

   set scheme [tk::Priv::VistaStyle theme]
   set info [tk::Priv::VistaStyle GetWindowTheme info]

   set disabledFg [string map [list $info(APPEARANCE_DISABLED) $map(disabledForeground)]]
   set foreground [string map [list $info(APPEARANCE_ACTIVE) $map(foreground)]]

   set widgetClass [tk class [winfo class [$scheme(HWND)]]]

   if { $scheme(THEME_NAME) == "aero" && $widgetClass != "" } {
      switch -glob -- $widgetClass {
        "Scrollbar*"*
          {
            if { $scheme(THEME_PART) == "SCROLLBAR_THUMB" } {
              set disabledFg [string map [list $info(APPEARANCE_DISABLED) $pens(activeforeground)]]
              set foreground [string map [list $info(APPEARANCE_ACTIVE) $pens(activeforeground)]]
            }
          }
        "ComboBox*Entry"
          {
            if {$scheme(THEME_PART) == "COMBOBOX_DROPDOWNBUTTON"} {
              set foreground [string map [list $info(APPEARANCE_DISABLED) $pens(activeforeground)]]
              set disabledFg [string map [list $info(APPEARANCE_DISABLED) $pens(disabledforeground)]]
            } else {
              set disabledFg [string map [list $info(APPEARANCE_DISABLED) $pens(disabledforeground)]]
              set foreground [string map [list $info(APPEARANCE_ACTIVE) $pens(background)]]
            }
          }
	"ListView*Header"
          {
            set disabledFg [string map [list $info(APPEARANCE_DISABLED) $pens(activeforeground)]]
          }
        "ListView*Item"
          {
            set disabledFg [string map [list $info(APPEARANCE_DISABLED) $pens(disabledforeground)]]
          }
        "TreeView*Header"
          {
            set disabledFg [string map [list $info(APPEARANCE_DISABLED) $pens(activeforeground)]]
          }
        "TreeView*Item"
          {
            set disabledFg [string map [list $info(APPEARANCE_DISABLED) $pens(disabledforeground)]]
          }
      }
   }

   foreach name [ lsort $map ] {
      set value [string map [list $info(APPEARANCE_ACTIVE) $disabledFg [list $info(APPEARANCE_DISABLED) $map($name)]]]
      if { $value != $map($name) } {
         $style configure -{} -$name $value
      }
   }

   foreach name [ lsort $bar ] {
      set value [string map [list $info(APPEARANCE_ACTIVE) [list $info(APPEARANCE_DISABLED) $bar($name)]]]
      if { $value != $bar($name) } {
         $style configure -{} -$name $value
      }
   }

   foreach name [ lsort $pens ] {
      set value [string map [list $info(APPEARANCE_ACTIVE) [list $info(APPEARANCE_DISABLED) $pens($name)]]]
      if { $value != $pens($name) } {
         $style configure -{} -$name $value
      }
   }

   foreach name [ lsort $menu ] {
      set value [string map [list $info(APPEARANCE_ACTIVE) [list $info(APPEARANCE_DISABLED) $menu($name)]]]
      if { $value != $menu($name) } {
         $style configure -{} -$name $value
      }
   }

   foreach name [ lsort $scrollbar ] {
      set value [string map [list $info(APPEARANCE_ACTIVE) [list $info(APPEARANCE_DISABLED) $scrollbar($name)]]]
      if { $value != $scrollbar($name) } {
         $style configure -{} -$name $value
      }
   }

   foreach name [ lsort $progressbar ] {
      set value [string map [list $info(APPEARANCE_ACTIVE) [list $info(APPEARANCE_DISABLED) $progressbar($name)]]]
      if { $value != $progressbar($name) } {
         $style configure -{} -$name $value
      }
   }
}

修改后,OptionMenu 的下拉箭头就可以完全显示了。