我最近通过将子类包裹在Thetkinter Combobox的定义上解决了一个小问题。在这样做时做笔记之后,我认为我也可以整理并分享我的所作所为。
做这种事情有很多教程 - 我并不是说这是特别或更好的事情。
问题
碰巧的是,我多年来完成了大部分应用程序编码的环境是提供“ ComboBox”控制又称下拉列表的环境。
但是,在那种环境中,我非常习惯将组合蛋白供给小桌子,通常是用钥匙和显示列的行。控件绑定到键,但显示显示列 - 这是通过将第一列的处置宽度设置为零来实现的。因此,Combobox仅具有显示值,但是当我阅读用户选择的内容时,我会得到密钥。大多数时候,我都使用数字键和字符串显示。当通过表提供这些订单时,可以指定订单 - 可能与显示值无关。
曾在Python中编写应用程序并使用TKINTER,这令人沮丧的是,这仅支持显示值的概念,完全是该控件作为用户选择的返回。
虽然令人讨厌,但这个问题还不够重要,无法竭尽所能解决。由于某种原因,我突然觉得自己要解决 - 因此,这是带注释的旅程。
- 注意:我故意忽略我们可以获取所选项目的索引,而不是显示的值。在大多数情况下,我并不真正在乎显示的下拉列表中的任何位置 - 想要自由重新订购或将项目遗漏。但是,正如我们稍后将要使用的那样,一旦我们工作了如何将一个班级包装在另一个课程中,则该索引可能是有用的。
输入枚举
作为旧的Pascal程序员,我非常喜欢在编程中使用定义的类型和枚举。喜欢它们的最重要原因(尤其是作为组合)是,编译阶段将挑选任何尝试跨越类型定义分配的尝试。
但是,Python作为一种具有动态键入的解释语言并不能真正提供该好处。尽管如此,我仍然非常喜欢使用枚举,因为它使我可以在程序代码中编写符号值,并在很大程度上忽略特定值。
引用WikiBooks: Pascal Programming/Enumerations
type
weekday = (Monday, Tuesday, Wednesday, Thursday, Friday, Saturday, Sunday);
var
startOfWeek: weekday;
begin
startOfWeek := Sunday;
end.
用于编译程序的内部使用,将用于枚举值的实际(二进制)值无关紧要。作为一种强烈键入的语言,Pascal编译器将对任何尝试为变量“ startofweek”分配非“工作日”值的尝试都会出现错误。
当然,Python完全不同,我不会详细解释 - 请参阅enum — Support for enumerations的标准Python文档 - 但可以说我发现它们足够有用。
但是,这确实对组合蛋白构成了问题 - 因为它们需要用字符串喂食才能显示,因此它们将这些字符串返回为选定的值。
为了解决这个问题,我一直在创建我的枚举,然后通过编写功能来添加到它们,以将每个枚举值表示为显示/选择字符串和相反功能,以获取具有特定字符串的枚举。一切都起作用,但看起来笨拙。
我想要的是拥有可以通过对列表的组合蛋白,以便将字符串的一半显示给用户,但我可以将枚举的一半(键)(键)恢复到该字符串。
当前方法
目前,我要做的是拥有一个绑定到Combobox
属性的TK变量
因此,代码的设置部分确实:
- 创建TK变量
- 创建与变量结合的TK Combox控件
- 为Combobox Control设置
values
- 通过设置界变量来设置ComboBox的默认值
t06_t2_str_scheme = m_tkntr.StringVar()
t06_t2_cb_schemes = m_tkntr_ttk.Combobox( t06_t2_frame02, width = 50, textvariable = t06_t2_str_scheme )
t06_t2_cb_schemes['values'] = cmntry.MatchScheme_GetTuple()
t06_t2_str_scheme.set( cmntry.MatchScheme_Default_Value() )
我从某些我与枚举一起定义的支持功能中撤出 - 即:
MatchScheme_GetTuple()
MatchScheme_Default_Value()
然后,为了稍后阅读选择,我们只是get
绑定变量的值
got_selection = t06_t2_str_scheme.get()
然后,我需要调用另一个函数才能从所选字符串转换为相应的枚举。
enum_selection = MatchScheme_EnumOfString( got_selection)
并不是说这很难,这只是小事。
我想做的事
我想能够
- 有一个元组列表,作为我为Combobox设置的内容
- 每个元组中的第一个值是我要回来的值
- 每个元组中的第二个值是我要显示的值
例如。定义元组列表
the_ListTuplePair = [ ( 1, "One" ), ( 2, "Two"), ( 3, "Three"), ( 4, "Four") ]
例如。创建Combobox对象
t06_t2_cb_schemes = TuplePairCombobox( t06_t2_frame02, width = 50, listTuplePair = the_ListTuplePair, defaultKey = 2 )
例如。选择的集合
the_selection = t06_t2_cb_schemes.getSelectedKey()
警告和案件
在考虑一些潜在问题时,我认为我需要处理:
- 不应重复重复键并显示,因此未选择
- 显示值的重复是允许并迎合
- 匹配不提供密钥的默认密钥意味着未设置默认值
- 没有选择回报为无
实现A-使用绑定到控件的值
空气代码
提醒自己一些关于班级定义在python中的工作方式(我确实倾向于避开它们),我坐下来输入以下内容(周围有很多玩具):
class TuplePairCombobox( m_tkntr_ttk.Combobox ):
def _process_listTuplePair( ip_listTuplePair ):
# ensure the listTuplePair has no repeats of the keys
t_list_keys = [] # used to detect repeats
r_dict_valu2key = {} # use both to populate the combobox and reverse lookup after selection
for tpl in ip_listTuplePair:
if tpl[0] in t_list_keys :
pass
else
if tpl[1] in i_dict_valu2key:
n = t_list_keys # number of space to pad the display string
i_dict_valu2key[ tpl[1] + ( " "*n ) ] = tpl[0]
else
i_dict_valu2key[ tpl[1] ] = tpl[0]
t_list_keys.append( tpl[0] )
return r_dict_valu2key
def __init__(self, container, p_listTuplePair, p_defaultKey, *args, **kwargs):
self.i_StringVar = m_tkntr.StringVar()
i_dict_valu2key = _process_listTuplePair( p_listTuplePair )
super().__init__(container, textvariable = self.i_StringVar, *args, **kwargs)
self['values'] = tuple( list( i_dict_valu2key.keys() ) )
if p_defaultKey in list( i_dict_valu2key.keys() ) :
self.i_StringVar.set( self.i_defaultKey )
def getSelectedKey():
txt_selection = self.i_StringVar.get()
r_key = i_dict_tpls[ txt_selection ]
return r_key
似乎涵盖了大多数想法的运作方式。简短的描述可能是:
- 处理提供的单元列表以构建词典,该字典将既充当反向查找 - 从所选的显示到元组键 - 以及在Combobox 中显示的内容列表
- 应用我有关重复,键和值的逻辑规则
- 要处理任何重复的值,寻找冲突并在其末端添加额外的空间,当我们沿着我们沿着不同数量的空间
实际代码
这是我粘贴空气代码并修复其一些监督后所拥有的。
班上
class TuplePairCombobox( m_tkntr_ttk.Combobox ):
def _process_listTuplePair( self, ip_listTuplePair ):
# ensure the listTuplePair has no repeats of the keys
t_list_keys = [] # used to detect repeats
r_dict_valu2key = {} # use both to populate the combobox and reverse lookup after selection
for tpl in ip_listTuplePair:
if tpl[0] in t_list_keys :
pass
else :
if tpl[1] in r_dict_valu2key:
n = len( t_list_keys ) # number of space to pad the display string
r_dict_valu2key[ tpl[1] + ( " "*n ) ] = tpl[0]
else :
r_dict_valu2key[ tpl[1] ] = tpl[0]
t_list_keys.append( tpl[0] )
return r_dict_valu2key
def __init__(self, container, p_listTuplePair, p_defaultKey, *args, **kwargs):
self.i_StringVar = m_tkntr.StringVar()
self.i_dict_valu2key = self._process_listTuplePair( p_listTuplePair )
super().__init__(container, textvariable = self.i_StringVar, *args, **kwargs)
self['values'] = tuple( list( self.i_dict_valu2key.keys() ) )
if p_defaultKey in list( self.i_dict_valu2key.keys() ) :
self.i_StringVar.set( self.i_defaultKey )
def getSelectedKey( self ):
txt_selection = self.i_StringVar.get()
if txt_selection in self.i_dict_valu2key:
r_key = self.i_dict_valu2key[ txt_selection ]
else :
r_key = None
return r_key
物体
这是使用新功能的方式。
首先是Combobox对象的创建,并带有通常的tkinter对:创建和放置步骤。
i_defaultKey = 3
t02_cb_schemer = TuplePairCombobox( t02_frame50, i_listTuplePair, i_defaultKey, width = 50)
t02_cb_schemer.pack( side = m_tkntr.LEFT, padx=tk_padx(), pady=tk_pady() )
值获取
这是如何从Combobox收集值。
print( t02_cb_schemer.getSelectedKey() )
元组清单
这是一个模拟示例,用于向新的TuplePairCombobox
对象提供的元素列表。
#i_listTuplePair = [ ( 1, "One" ), ( 2, "Two"), ( 3, "Three"), ( 4, "Four") ]
重复值的元组列表
仅检查我的额外边缘案例代码是否有效,这是一个替代列表,其中有两个相同的显示字符串。
i_listTuplePair = [ ( 1, "One" ), ( 2, "Two"), ( 3, "Three"), ( 4, "Two") ]
在这种情况下,所有四个选项均在Combobox中,每个选项都可以返回一个独特的键 - 即,在其中处理了两个显示字符串。
警告:
- 这样的机制非常简单, 对于源元组中的某些组合而言会失败。我将不得不决定是否写一些更健壮的东西。
带有重复键的元组列表
类似地,这里是一个替代列表,其中有两个相同的钥匙值。
i_listTuplePair = [ ( 1, "One" ), ( 2, "Two"), ( 2, "Three"), ( 4, "Four") ]
在这种情况下,根据我写的逻辑,将省略密钥2
的第二次出现。
方法B-使用选择的索引
在方法A中,我们刚刚围绕了组合“返回”显示值之一的问题。
但是,使用标准的TKINTER COMBOBOX,我们也可以选择取出所选值的索引。可以用来做出更好的方法吗?
空气代码
为此,我复制了完成的方法A代码,并将getSelectedKey
的定义更改为使用current()
,然后又一次地考虑了它需要存在的数据结构。我选择并行构建两个列表,以便相应的键和显示字符串在相同的索引号上。
为我提供了以下代码,可以植入我的程序。我逆转了“ tuplepair”的一部分变成“ pairtuple”,以便可以轻松地将用法调用进行测试。
class PairTupleCombobox( m_tkntr_ttk.Combobox ):
def _process_listTuplePair( self, ip_listTuplePair ):
r_list_keys = []
r_list_shows = []
for tpl in ip_listTuplePair:
r_list_keys.append( tpl[0] )
r_list_shows.append( tpl[1] )
return r_list_keys, r_list_shows
def __init__(self, container, p_listTuplePair, p_defaultKey, *args, **kwargs):
self.i_list_keys, self.i_list_shows = self._process_listTuplePair( p_listTuplePair )
super().__init__(container, textvariable = self.i_StringVar, *args, **kwargs)
self['values'] = tuple( self.i_list_shows )
# still need to set the default value from the nominated key
def getSelectedKey( self ):
i_index = self.current()
return self.i_list_keys[ i_index ]
实际代码
这是最终代码。
class PairTupleCombobox( m_tkntr_ttk.Combobox ):
def _process_listPairTuple( self, ip_listPairTuple ):
r_list_keys = []
r_list_shows = []
for tpl in ip_listPairTuple:
r_list_keys.append( tpl[ 0] )
r_list_shows.append( tpl[ 1] )
return r_list_keys, r_list_shows
def __init__( self, container, p_listPairTuple, p_defaultKey, *args, **kwargs):
self.i_list_keys, self.i_list_shows = self._process_listPairTuple( p_listPairTuple )
super().__init__(container, *args, **kwargs)
self['values'] = tuple( self.i_list_shows )
# still need to set the default value from the nominated key
try:
t_default_key_index = self.i_list_keys.index( p_defaultKey )
self.current( t_default_key_index )
except:
pass
def getSelectedKey( self ):
try:
i_index = self.current()
return self.i_list_keys[ i_index ]
except:
return None
主要更改是,一旦通过初始测试,我将try
条款添加到:
- 处理查找默认值的索引,并使用
current
作为setter - 句柄如果未设置默认设置并且没有选择选择
概括
作为练习,这是一个很好的组合:
- 使自己能够返工并简化我使用Combobox Control的大多数地方
- 重新发现的是,使用课程重新工作其他班级并不像我期望的那样棘手
我确实认为我应该为新课程找到一个更好的名称,并且/或选择其中哪一个可以长期保留。