2013-12-11 63 views
1

我使用Python 3.x创建多语言“日历”窗口小部件,该窗口小部件具有年,月,日等独立的spinboxes。需求是,无论使用哪个窗口小部件都必须随时隐藏GUI的任何部分,从而排除掉下拉列表等,使得旋转盒成为一个明显的选择。动态设置tkinter Spinbox的范围

如果有人知道公共领域的纯python/tkinter“日期&时间条目”的源代码widgit(理想情况下处理闰年和所有月末的情况下)可以找到,指出我的存储库将不胜感激。

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

问题1 - 我不能让“DaySpinBox”来动态地调整其范围将自身设置为月,在‘MonthSpinBox’:

据我怎么读Mark Lutz的“编程Python”一书(第三版,第383页& 384)我应该能够动态设置DaySpinBox的最大值/最后一天(通过使用“values =”或“to =”和“from_ =”选项)以匹配MonthSpinBox中显示的月份。我正在追踪MonthSpinBox变量(“SelectedMonth”),并根据本书,我尝试使用以下方法更新DaySpinBox: 1 - 使用“to = SelectedMonth”。在创建DaySpinBox的调用中,一些尝试会显示为注释。 (我刚接触lambdas,也许我没有正确使用它们),
2 - 使用“DaySpinBox ['to'] = SelectedMonth”和 3 - 使用“DaySpinBox.config(to = SelectedMonth)”。
我也尝试使用“值=”与所有方法没有成功(“to = ....”选项是首选)。

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

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

问题2 - MonthSpinBox总是初始化到一月,而不是当前的月份

的我使用了“textvariable”关键字来初始化年,月,日spinboxes到“今天”。这适用于YearSpinBox和DaySpinBox,但不适用于MonthSpinBox。 (令人讨厌的是,我认为MonthSpinBox正在工作,但是我试图修复DaySpinBox时破坏了它)。唯一明显的区别是,Year spinbox使用整数,而“月份”spinbox使用字符串。有什么建议么?

对于这两个问题,我认为是一个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接口变量正确更新调试代码 - 包括包含改变/新月份的最后一天(这被“送”给了天变Spinbox使用“TO =”关键字)。

+0

如果将这些问题中的每一个都作为独特的问题提出,您可能会收到更快的答复 – duhaime

回答

1

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

与更新一天spinbox相关,我没有看到您要更新它的位置,所以我不确定为什么您认为它应该更新。要更新Spinnerbox,请将命令附加到月份Spbox,然后在回调中进行更新。

例如:

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

+0

感谢@Bryan确认我的理解,但我没有试图更新Day Spinbox的方式。 问题在于日期旋转框的MAXIMUM值未更新为与月份旋转框中月份的最后一天相匹配。 (Spinbox中的值正确更新。) 要查看该问题,请选择1月31日,然后将月份更改为2月。根据需要,Spinbox中的DISPLAYED日期将更改为2月28日(忽略闰年) - 但Day Spinbox仍然允许将该日更改为2月31日!这是我需要解决的(年份和月份相关)问题。 – user1459519

+0

@ user1459519:我已经更新了我的答案,告诉你如何去做。 –

+0

Doh!非常感谢您发现问题,再次感谢您的帮助。我已经看了很长时间的代码,看不到森林的树(省略“command =”关键字,它是关联的帮助函数)。在进行这两项更改后,代码现在可以运行其他人应该注意到,我还在Year Spinbox中添加了相同的“command =”行,以便在年份发生变化时处理2月29日。 – user1459519