我正在使用Python 3.x创建一个多语言的“日历”小部件,该小部件具有分别用于年,月,日等的旋转框。一个要求是使用的任何小部件都绝不能在任何时间隐藏GUI的任何部分,因此排除下拉列表等,使旋转框成为显而易见的选择。

如果有人知道在哪里可以找到纯python / tkinter“日期和时间输入” widgit的公共领域源代码(理想情况下处理and年和所有月末情况),请将我指向该存储库将不胜感激。

我能找到的最接近的解决方案是OptionMenu解决方案(Change OptionMenu based on what is selected in another OptionMenu),但是由于“不要隐藏”的要求而无法使用。以此为灵感(在查阅http://infohost.nmt.edu/tcc/help/pubs/tkinter/web/spinbox.html的Spinbox文档之后),我想到了下面的代码。为简单起见,我删除了处理leap年和月末问题的代码。该代码有效,除了两个问题:

问题1 –我无法让“ DaySpinBox”动态调整其范围以将自己设置为“ MonthSpinBox”中的月份:

根据我阅读Mark Lutz的“ Programming Python”(第三版,第383和384页)的方式,我应该能够动态设置DaySpinBox的最大/最后一天(通过使用“ values =”或“ to =”和“ from_ =”选项)以匹配出现在MonthSpinBox中的月份。我正在跟踪MonthSpinBox变量(“ SelectedMonth”),并且根据本书,我尝试使用以下方法更新DaySpinBox:
1-使用“ to = SelectedMonth”。在创建DaySpinBox的调用中,对此的一些尝试会显示为注释。 (我是lambda的新手,也许我没有正确使用它们?),
2-使用“ DaySpinBox ['to'] = SelectedMonth”,和
3-使用“ DaySpinBox.config(to = SelectedMonth)”。
我还尝试过将“ values =”与所有方法一起使用均未成功(首选“ to = ....”选项)。

问题1:Spinbox可以设置为使用动态范围吗?或者,正如我开始怀疑的那样,Spinbox创建后,它们的范围是否不可更改?

问题2:经过大量的网络搜索后,我没有发现任何内容明确指出可以动态更改哪些Spinbox参数(“ to =”,“ from =”和“ value =”关键字),而哪些则不能更改–在哪里可以找到此信息(适用于所有小部件类型)?

问题2 – MonthSpinBox始终初始化为一月而不是当前月份

我使用“ 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()


顺便说一句,该示例代码省略了证明所有Spinbox接口变量都已正确更新的调试代码-包括包含更改/新月份的最后一天(使用“ TO =”“发送”到Day Spinbox的)变量。关键词)。

最佳答案

所有小部件的所有选项始终可以动态配置。如果我没记错的话,只有一个例外,这是几乎没人使用的功能:框架上的class选项。

与更新日旋转框相关,我看不到您要在哪里更新,因此我不确定您为什么认为应该更新它。要更新Spinbox,请将命令附加到month Spinbox,然后在回调中进行更新。

例如:

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重置为正确的默认值。

10-08 05:07