tkinter OptionMenu 问题(错误?):GUI 和程序值未保持同步(python 3.x) [英] tkinter OptionMenu issue (bug?): GUI and program values not kept in lockstep (python 3.x)

查看:23
本文介绍了tkinter OptionMenu 问题(错误?):GUI 和程序值未保持同步(python 3.x)的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在某些情况下(下面的演示),OpenMenu 小部件上显示的值与程序使用的值不匹配,这会导致在用户期望选项 A 时执行选项 B - 导致 WTF??用户的反应.

In some cases (demo below) the value shown on the OpenMenu widget does not match that used by the program, this causes option B to be done when the user is expecting option A - causing a WTF?? reaction by the user.

不幸的是,OptionMenu 小部件没有我与其他小部件一起使用的命令"选项来轻松处理问题(例如演示中的A_Button"小部件).我试过使用绑定,但到目前为止我还没有解决问题的灵丹妙药".

Unfortunately the OptionMenu widget does not have the "command" option that I've used with other widgets to easily handle the problem (e.g. "A_Button" widget in demo). I've tried using bindings but so far I haven't a "magic bullet" that fixes the problem.

我检查了常用位置(NMT、effbot、此处等),发现几乎没有关于此小部件的有用文档,尤其是在处理下拉列表中的项目时.(了解如何确定列表中项目的数量、列表中当前选定值的位置/索引以及如何在 GUI 的选项卡序列中包含小部件会很有用).

I've checked the usual places (NMT, effbot, here, etc.) and found close to no useful documention on this widget, especially when it comes to working with the items on the dropdown list. (Knowing how to determine the number of items in the list, the position/index of the currently selected value in the list and how to include the widget in the GUI's tab sequence would be useful).

我的申请是多语言的;当语言更改显示值时,OptionMenu 小部件的下拉列表必须相应更改.(顺便说一句,多语言意味着您不能在代码中直接使用 .get() 的结果,特别是如果添加了另一种语言.为了获得语言独立性,我使用了通过将 .get() 值与值匹配来创建的索引在选项菜单中 - 有更好的方法吗?)

My application is multilingual; when the language changes the displayed value and the dropdown list of the OptionMenu widget must change accordingly. (BTW, multilingualism means that you cannot directly use the results of .get() in the code, especially if another language gets added. To get language independence I'm using an index created by matching the .get() value to the values in the option menu - is there a better method? )

在演示代码中,所选语言决定了 OptionMenu 小部件显示的值.使用日期"(按钮小部件)正是实际应用程序的启动方式(以及为什么 GUI 和程序值必须始终匹配 - 这并不总是发生).相反,今天是什么日子??"(也是一个按钮小部件)使用命令选项来实现预期/正确的行为 - 正如在我的应用程序中多次成功完成的那样.

In the demo code the chosen language determines the values shown by the OptionMenu widget. "Use the date" (a Button widget) is exactly how the real application gets launched (and why it's mandatory that the GUI and program values match at all times - which is is not always happening). In contrast, "What day is it??" (also a Button widget) uses the command option to implement the expected/correct behaviour - as has been successfully done many times in my application.

要查看问题,请运行演示,选择任何语言.在不改变语言的情况下,一天会改变几次.请注意,打印值(由我的应用程序使用)始终是 GUI 小部件上显示的值后面的一个选项.接下来,不更改日期,选择不同的语言(打印新语言).OptionMenu 下拉值直到 鼠标离开 OptionMenu 小部件后才会更改 - 并且其显示的值永远被翻译"成新语言.

To see the problem run the demo, selecting any language. Without changing languages change the day several times. Note that the printed value (used by my application) is always one selection behind that shown on the GUI widget. Next, without changing days, select a different language (the new language gets printed). The OptionMenu dropdown values do not change until after the mouse leaves the OptionMenu widget - and its displayed value never gets "translated" into the new language.

我忽略/遗漏/做错了什么?

What am I overlooking/missing/doing wrong?

from tkinter import Button, IntVar, Label, OptionMenu, Radiobutton, Tk, StringVar
#    You can skip over this section, it's basic gruntwork needed to set up the demo (values used have no impact on the problem).
English    = 0    
French     = 1
Spanish    = 2
DayNumbers = 3
DefaultDay = 2              # Corresponds to Tuesday, emulates the user's choice of day (on the GUI)
DefaultLanguage = English
Languages  = [ "English", "French", "Spanish", "Day Numbers" ] # for use on GUI
EnglishWeekdays  = [ "Sunday",   "Monday", "Tuesday", "Wednesday", "Thursday", "Friday",   "Saturday" ]
FrenchWeekdays   = [ "dimanche", "lundi",  "mardi",   "mecredi",   "jeudi",    "vendredi", "samedi"   ]
SpanishWeekdays  = [ "domingo",  "lunes",  "martes",  "miercoles", "jeuves",   "viernes",  "sabado"   ]
NumberedWeekdays = [ "Day 0",    "Day 1",  "Day 2",   "Day 3",     "Day 4",    "Day 5",    "Day 6"    ]
DayNames = [ EnglishWeekdays, FrenchWeekdays, SpanishWeekdays, NumberedWeekdays ]
#   The variables
LanguageInUse = DefaultLanguage
Weekdays      = DayNames[ LanguageInUse ]
Today         = DefaultDay  # Isolates application code from language on GUI
#-------------------------------------------------------------------------------
def ChooseLanguage( ParentFrame ) :   
    global LanguageInUse, DropdownMenu
    GUI_Language = IntVar( value = LanguageInUse )   
    #---------------------------------------------------------------------------
    def SwitchLanguage():        
        global LanguageInUse , Weekdays       
        LanguageInUse = GUI_Language.get()   
        print( "SwitchLanguage sets language index to", LanguageInUse, "(" + Languages[ LanguageInUse ] + ")"  )             
        Weekdays = DayNames[ LanguageInUse ]
        DropdownMenu[ 'menu' ][ 'title' ] =  Weekdays[ Today ]         
        for i, DayName in enumerate( Weekdays ) :
            DropdownMenu[ 'menu' ].entryconfig( i )['label' ] = DayName
        return           
    #---------------------------------------------------------------------------
    LanguageButton = []
    for LanguageIndex, Language in enumerate( Languages ) :   
        LanguageButton = LanguageButton + [ Radiobutton( ParentFrame, 
                    indicatoron = False, width = 12, 
                    variable = GUI_Language, command = lambda: SwitchLanguage(),
                    text = Language, value = LanguageIndex ) ]
        LanguageButton[ LanguageIndex ].grid( row = 0 , column = LanguageIndex )    
    return
#-------------------------------------------------------------------------------
def GetDayIndex() :    
    global Today, DropdownMenu
    Today = 0    
    for Index, DayName in enumerate( Weekdays ) :
        if ( GUI_Value.get() == DayName ) :
            Today = Index
            break
    print( "GetDayIndex sets weekday index to", Today, "(" + Weekdays[ Today ] + ")" )

    for i, j in enumerate( Weekdays ) :
        DropdownMenu[ 'menu' ].entryconfig( i , label = j )
    return
#-------------------------------------------------------------------------------
def DoSomethingUseful() :
    print( "   Program uses " +  str( Today ) + " (" +  Weekdays[ Today ]  +")" )
    return
#-------------------------------------------------------------------------------
# The mainline
root = Tk() 
GUI_Value = StringVar( value = Weekdays[ Today ] )
Widget1 = Label( root, text = "Today is" )
Widget1.grid( row = 1, column = 0 )

DropdownMenu = OptionMenu(  root, GUI_Value, *DayNames[ LanguageInUse ] )    # NOT in TAB key sequence !!!
DropdownMenu.grid( row = 1, column = 1  )

DropdownMenu.bind( "<Leave>", lambda _ : GetDayIndex() )

#OptionMenu_Configuration( DropdownMenu )
A_Button = Button( root, text = "What day is it??", command = lambda : GetDayIndex() )
B_Button = Button( root, text = "Use the date",  command = lambda: DoSomethingUseful() )
A_Button.grid( row = 1, column = 2 )
B_Button.grid( row = 1, column = 3 )
ChooseLanguage( root )  # creates/manages the language choice widgets

root.mainloop()

推荐答案

你的程序逻辑对我来说不是很清楚,尤其是 '' 绑定,但让我们试着回答你的问题一般:

Your program's logic isn't clear for me, especially '<Leave>' binding, but lets try to answer your questions in general:

不幸的是,OptionMenu 小部件没有我与其他小部件一起使用的命令"选项来轻松处理问题(例如演示中的A_Button"小部件).

Unfortunately the OptionMenu widget does not have the "command" option that I've used with other widgets to easily handle the problem (e.g. "A_Button" widget in demo).

你错了,因为它有选项:

import tkinter as tk


def dropdown_callback(selected=None):
    print('Currently selected value is:\t%s' % selected)

root = tk.Tk()
str_var = tk.StringVar()
dropdown_menu = tk.OptionMenu(root, str_var, *['foo', 'bar', 'baz'], command=dropdown_callback)
dropdown_menu.pack()

root.mainloop()

此外,您可以为每个条目指定一个单独的 command(很少有用):

Also, you can specify a separate command for each of your entries (rarely useful):

import tkinter as tk


def dropdown_callback(event=None):
    print('Currently selected value is:\t%s' % event)


def dropdown_callback_foo():
    print('Called callback is:\t%s' % dropdown_callback_foo.__name__)


def dropdown_callback_bar():
    print('Called callback is:\t%s' % dropdown_callback_bar.__name__)


root = tk.Tk()
str_var = tk.StringVar()
dropdown_menu = tk.OptionMenu(root, str_var, *['foo', 'bar', 'baz'], command=dropdown_callback)
dropdown_menu._OptionMenu__menu.entryconfig(0, command=dropdown_callback_foo)
dropdown_menu._OptionMenu__menu.entryconfig(1, command=dropdown_callback_bar)
dropdown_menu.pack()

root.mainloop()

...列表中当前选定值的位置/索引...

...the position/index of the currently selected value in the list...

再说一次,有一个选项:

import tkinter as tk


def dropdown_callback(selected=None):
    selected_index = root.tk.call(dropdown_menu.menuname, 'index', selected)
    print('Currently selected value is:\t%s\t\ton position:\t%d' % (selected, selected_index))

root = tk.Tk()
str_var = tk.StringVar()
dropdown_menu = tk.OptionMenu(root, str_var, *['foo', 'bar', 'baz'], command=dropdown_callback)
dropdown_menu.pack()

root.mainloop()

...确定列表中的项目数...

...determine the number of items in the list...

这也是可以实现的,因为项目数只是最后一个索引 + 1:

It's also achievable, since number of items is just a last index + 1:

import tkinter as tk


def dropdown_callback(selected=None):
    selected_index = root.tk.call(dropdown_menu.menuname, 'index', selected)
    total_count = root.tk.call(dropdown_menu.menuname, 'index', 'end') + 1
    print('Currently selected value is:\t%s\t\ton position:\t%d\t\twith total count:\t%d' 
          % (selected, selected_index, total_count))

root = tk.Tk()
str_var = tk.StringVar()
dropdown_menu = tk.OptionMenu(root, str_var, *['foo', 'bar', 'baz'], command=dropdown_callback)
dropdown_menu.pack()

root.mainloop()

从这一点上我认为,现在您对 OptionMenu 的困惑已经解决了.除了堆叠顺序,当然,但您可以随时将 OptionMenu 替换为 ttk.Combobox. 没有这样的 Menu 小部件的行为,因为它们对像 lift 这样的命令有不同的反应.当然,这也是可以实现的,但这是另一个问题,因为越来越多的假设"!

From this point I think, that now your confusions with OptionMenu are solved. Except a stacking order, for sure, but you can replace your OptionMenu with ttk.Combobox at any time. There's no such <Tab>, behavior for Menu widgets, because they react differently on commands like lift. Of course, it's achievable too, but it's another question, because of many and more of "what if"'s!

仅此而已!

这篇关于tkinter OptionMenu 问题(错误?):GUI 和程序值未保持同步(python 3.x)的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆