我正在尝试更改ttk Combobox的弹出列表的宽度。设置Combobox的宽度也会改变列表框的宽度,使部分值不可读。
我在Tk / Tcl中读过this solution,但我不熟悉这种语言,想用Python解决问题。我尝试更改主题参数,但似乎没有帮助。下面是一段示例代码。
import tkinter as tk
from tkinter import ttk
root = tk.Tk()
root.title("testing the combobox")
root.geometry('300x300+50+50')
fruit = ['apples are the best', 'bananas are better']
c = ttk.Combobox(root, values=fruit, width=10)
c.pack()
# Trying to change the width, does not work
c.option_add("*TCombobox*Listbox*Width", 50)
root.mainloop()
这里的任何人可以帮助我或给我一些指示吗?
答案 0 :(得分:2)
实现菜单发布的tk库文件(ttk / combobox.tcl)显式读取组合框小部件的宽度,并将菜单设置为相同的宽度(ttk::combobox::PlacePopdown
)。但是,您可以使用-postoffset
配置选项应用主题样式来覆盖它。这样做是为了允许某些主题来抵消下拉列表,但我们可以将其设置为允许自定义宽度。注意:使用此样式的所有小部件都将获得相同的宽度,因此您可能希望派生自定义样式。
在Tcl / Tk中,这是:ttk::style configure TCombobox -postoffset {0 0 300 0}
将下拉列表设置为300像素宽(x宽度高度)。
以下python tkinter代码向<Configure>
事件添加一些代码以获取组合框的大小并更新postoffset
宽度,以使下拉列表与列表中第一个字符串的大小相匹配字符串。结果如下:
import tkinter as tk
import tkinter.ttk as ttk
import tkinter.font as tkfont
def on_combo_configure(event):
global fruit
font = tkfont.nametofont(str(event.widget.cget('font')))
width = font.measure(fruit[0] + "0") - event.width
style = ttk.Style()
style.configure('TCombobox', postoffset=(0,0,width,0))
root = tk.Tk()
root.title("testing the combobox")
root.geometry('300x300+50+50')
fruit = ['apples are the best', 'bananas are better']
c = ttk.Combobox(root, values=fruit, width=10)
c.bind('<Configure>', on_combo_configure)
c.pack()
root.mainloop()
我们使用事件绑定来执行此操作,因为这可以确保我们可以在屏幕上获取窗口小部件的实际大小以从偏移量中移除该宽度。
答案 1 :(得分:2)
详细说明patthoyts使用派生样式而不是修改TCombobox
样式来获得通用解决方案的好答案(但要注意Tk错误,稍后会详细介绍)。
基本上,为每个组合框创建一个具有唯一名称的新样式(我不知道它可以如何扩展 - 也许只在需要的地方应用它更安全)。此外,组合框值从小部件本身读取并且最长的一个是:如果插入了短文本,还要检查以避免弹出窗口小于小窗口。
import tkinter as tk
import tkinter.ttk as ttk
import tkinter.font as tkfont
def on_combo_configure(event):
combo = event.widget
style = ttk.Style()
# check if the combobox already has the "postoffest" property
current_combo_style = combo.cget('style') or "TCombobox"
if len(style.lookup(current_combo_style, 'postoffset'))>0:
return
combo_values = combo.cget('values')
if len(combo_values) == 0:
return
longest_value = max(combo_values, key=len)
font = tkfont.nametofont(str(combo.cget('font')))
width = font.measure(longest_value + "0") - event.width
if (width<0):
# no need to make the popdown smaller
return
# create an unique style name using widget's id
unique_name='Combobox{}'.format(combo.winfo_id())
# the new style must inherit from curret widget style (unless it's our custom style!)
if unique_name in current_combo_style:
style_name = current_combo_style
else:
style_name = "{}.{}".format(unique_name, current_combo_style)
style.configure(style_name, postoffset=(0,0,width,0))
combo.configure(style=style_name)
root = tk.Tk()
root.title("testing the combobox")
root.geometry('300x300+50+50')
fruit = ['apples are the best', 'bananas are way more better']
c = ttk.Combobox(root, values=fruit, width=10)
c.bind('<Configure>', on_combo_configure)
c.pack()
c1 = ttk.Combobox(root, values=['shorter','than','widget'], width=15)
c1.bind('<Configure>', on_combo_configure)
c1.pack()
root.mainloop()
如前所述,Tk Combobox中存在一个错误:postoffest
属性只能从TCombobox
样式读取,而不能从派生样式中读取。
这可以通过编辑[python-install-dir]\tcl\tk[version]\ttk\combobox.tcl
来解决;在PlacePopdown方法中找到这一行:
set postoffset [ttk::style lookup TCombobox -postoffset {} {0 0 0 0}]
并将其替换为:
set style [$cb cget -style]
set postoffset [ttk::style lookup $style -postoffset {} {0 0 0 0}]
或者,等待我的pull request合并并释放。
答案 2 :(得分:2)
晚了聚会,但是我最近遇到了这个问题...
我尝试了dipanda的解决方案,并确认派生样式在<Configure>
事件中被更改,但是由于某些原因,postoffset
选项没有任何作用。已经设置好了,但是弹出窗口的宽度与原始小部件的宽度相同。
以下是对我有用的:非常类似于dipanda的代码:
def combo_configure(event):
combo = event.widget
style = ttk.Style()
long = max(combo.cget('values'), key=len)
font = tkfont.nametofont(str(combo.cget('font')))
width = max(0,font.measure(long.strip() + '0') - combo.winfo_width())
style.configure('TCombobox', postoffset=(0,0,width,0))
但这是我很奇怪的地方:
我没有将其绑定到<Configure>
事件,而是将其绑定到<ButtonPress>
事件:
widget.bind('<ButtonPress>', combo_configure)
我承认所做的更改会影响窗口中的所有组合框,但是由于一次只能放下一个组合框,因此这并不重要。就用户而言,它是透明的。另外,无需修改combobox.tcl
文件!
只有我的$ 1.05 ...