我正在使用Python 3.x创建一个多语言“日历”小部件,该小部件具有用于年,月,日等的单独的旋转框。要求是使用任何小部件必须永远不要隐藏任何GUI的任何部分时间,因此排除了下拉列表等,使得旋转盒成为明显的选择。
如果有人知道纯python / tkinter的公共域源代码“date&时间条目“widgit(理想情况下处理闰年和所有月末案例)可以找到,指向我的存储库将不胜感激。
我能找到的最接近的解决方案是这个OptionMenu解决方案(Change OptionMenu based on what is selected in another OptionMenu),但由于“不隐藏”的要求,它无法使用。使用它作为灵感(并在http://infohost.nmt.edu/tcc/help/pubs/tkinter/web/spinbox.html审查了Spinbox文档后),我想出了下面的代码。为了简单起见,我删除了处理闰年和月末问题的代码。代码工作除了两个问题:
问题1 - 我无法让“DaySpinBox”动态调整其'范围,将自己设置为“MonthSpinBox”中的月份:
根据我如何阅读Mark Lutz的“Programming Python”一书(第三版,第383页和第384页),我应该能够动态设置DaySpinBox的最大/最后一天(通过使用“values =”或“to” =“and”from_ =“options”匹配MonthSpinBox中出现的月份。我正在跟踪MonthSpinBox变量(“SelectedMonth”),并且根据本书,我尝试使用以下方法更新DaySpinBox:
1 - 使用“to = SelectedMonth”。对此的一些尝试在创建DaySpinBox的调用中显示为注释。 (我是lambdas的新手,也许我没有正确使用它们?),
2 - 使用“DaySpinBox ['to'] = SelectedMonth”,和
3 - 使用“DaySpinBox.config(to = SelectedMonth)”
我也尝试使用“values =”,所有方法都没有成功(首选“to = ....”选项)。
问题1:可以将Spinbox设置为使用动态范围,或者正如我开始怀疑的那样,在创建Spinbox后它们的范围是不可更改的吗?
问题2:经过多次网络搜索后,我没有发现任何明确说明哪些Spinbox参数(“to =”,“from =”和“value =”关键字)可以动态更改哪些不能 - 哪里我能找到这些信息(适用于所有小部件类型)吗?
问题2 - MonthSpinBox始终初始化为1月而不是当月
我正在使用“textvariable”关键字将年,月,日旋转框初始化为“今天”。这适用于YearSpinBox和DaySpinBox,但不适用于MonthSpinBox。 (令人讨厌的是,我认为MonthSpinBox正在运行,但我试图修复DaySpinBox而破坏它)。唯一明显的区别是Year和Day旋转框使用整数,而Month旋转框使用字符串。有什么建议吗?
对于这两个问题,我考虑了LEGB问题的可能性,但没有嵌套函数,因此变量隐藏不应成为问题 - 除非我的一个变量复制并隐藏在tkinter中定义的一个,等等。
我错过了什么?
顺便说一下,我知道使用全局变量的优缺点,也知道PEP8(http://www.python.org/dev/peps/pep-0008/) - 请参阅目录中的第二个项目符号。 ;>)感谢。
from tkinter import *
from tkinter import ttk
from datetime import datetime
from collections import OrderedDict
root = Tk() # put here so IntVar (etc.) is useable during initialization
StartUpDateTime = datetime.now()
DefaultYear = StartUpDateTime.year
DefaultMonthNumber = StartUpDateTime.month
DefaultDayOfMonth = StartUpDateTime.day
# These are only used to build the Month name & length dictionary
YearAndMonthLengths = [ 365, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 ]
EnglishMonthNames = ( 'Entire Year', 'January', 'February', 'March',
'April', 'May', 'June', 'July', 'August', 'September',
'October', 'November', 'December' )
FrenchMonthNames = ( 'année entière', 'janvier', 'février', 'mars',
'avril', 'mai', 'juin', 'juillet', 'août', 'septembre',
'octobre', 'novembre', 'décembre' )
#SpanishMonthNames = ....
#ItalianMonthNames = ....
Month_Names = FrenchMonthNames
Month_Names = EnglishMonthNames # comment out this line to use French
# Create dictionary containing month names that appear in the Month spinbox.
# OrderedDict used because Month name (=key) field must be language-independent.
DaysInMonth = OrderedDict()
for i in range( 0, len( Month_Names ) ) :
DaysInMonth[ Month_Names[ i ] ] = YearAndMonthLengths[ i ]
DefaultMonthName = Month_Names[ DefaultMonthNumber ]
DefaultMonthLength = DaysInMonth[ DefaultMonthName ]
# Initialize the Spinbox interface variables to todays date
SelectedYear = IntVar( value = DefaultYear )
SelectedMonthName = StringVar( value = DefaultMonthName )
SelectedMonthLength = IntVar( value = DefaultMonthLength )
SelectedDay = IntVar( value = DefaultDayOfMonth )
#-------------------------------------------------------------------------------
def GetChosenMonthLength( *args ) :
global SelectedMonthLength
SelectedMonthLength.set( DaysInMonth[ SelectedMonthName.get() ] )
return
#-------------------------------------------------------------------------------
mainframe = Frame( root )
mainframe.grid( row = 0, column = 0 )
# Show today's date. If it's July 17th, 2023 the spinboxes must be initialized
# to "July" ("julliet" if using French), "17" and "2023".
YearSpinBox = Spinbox( mainframe,
from_ = 2000, to = 2050,
textvariable = SelectedYear,
wrap = TRUE, state = 'readonly', width = 5 )
MonthSpinBox = Spinbox( mainframe,
values = tuple( DaysInMonth.keys() )[1:],
textvariable = SelectedMonthName,
wrap = TRUE, state = 'readonly', width = 10 )
DaySpinBox = Spinbox( mainframe,
from_ = 1,
to = SelectedMonthLength.get(),
# to = DaysInMonth[ SelectedMonthName.get() ],
# values = tuple( str( i ) for i in range( 1, SelectedMonthLength.get() + 1 ) ),
# values = lambda : tuple( str( i ) for i in range( 1, DaysInMonth[ SelectedMonthName.get() ] + 1 ) )
textvariable = SelectedDay,
wrap = TRUE, state = 'readonly', width = 16
)
MonthSpinBox.grid( row = 0, column = 0 )
DaySpinBox.grid( row = 0, column = 1 )
YearSpinBox.grid( row = 0, column = 2 )
SelectedMonthName.trace( 'w', GetChosenMonthLength )
mainloop()
BTW,示例代码省略了调试代码,证明所有Spinbox接口变量都正在正确更新 - 包括包含更改/新月的最后一天的变量(使用“”将其“发送”到Day Spinbox TO =“关键字”。
答案 0 :(得分:2)
所有小部件的所有选项始终可以动态配置。如果我没记错的话,只有一个例外,这是几乎没有人使用的功能:帧上的class
选项。
与更新日期旋转框相关,我没有看到您尝试更新它的位置,因此我不确定您认为它应该更新的原因。要更新旋转框,请将命令附加到月份旋转框,然后在回调中执行更新。
例如:
def update_days():
maxdays = int(DaysInMonth[SelectedMonthName.get()])
DaySpinBox.config(to=maxdays)
MonthSpinBox = Spinbox(...,command=update_days)
至于为什么月份没有设定到当月......我不知道。看起来你做得对。这个和其他小部件之间的唯一区别是这是一个StringVar而不是IntVar,而月份spinbox有一个values
参数。也许这是由这种差异触发的tkinter中的错误或记录不完整的功能。
简单的解决方法是在创建月份旋转框后添加SelectedMonthName.set(DefaultMonthName)
以将stringvar重置为正确的默认值。