无法让 LabelFrame 小部件显示在 ttk 笔记本上 (python 3.5.1)

Cannot get LabelFrame widget to display on ttk notebook (python 3.5.1)

我有一个应用程序,其中 ttk Notebook 选项卡上的 (tkinter) LabelFrame 小部件不会自行更新。在该程序的简化版本(下面的代码摘录)中,小部件甚至不会出现在 GUI 上。

GUI 上的其他一切工作正常,包括更新选项卡标题、应用程序标题(和图标)以及更新所有笔记本选项卡上的 Label、Checkbutton 和 Radiobutton 小部件。使用 ttk 版本(例如 ttk.LabelFrame)创建这些小部件并没有解决问题。我还尝试在更新小部件属性后立即使用“update_idletasks”(有些人认为这是一种拼凑)但没有成功。

在实际应用中,所有 GUI 小部件的文本会根据同一 GUI 上“选择语言”小部件的状态而变化(详情请参见:Need Python/tkinter GUI to dynamically update itself (for a multi-lingual GUI))。众所周知,所有 GUI 文本值(WidgetName["text"] 属性),包括丢失的 LabelFrame,都被正确更新以匹配该小部件的状态。

Notebook 选项卡上的 LabelFrame 小部件有什么“特别”之处吗?我忽略了什么(可能很简单)的东西?
此外,其他人的任何 confirmation/denial 都将有助于确定问题是否是我的系统独有的——这是一个明显的可能性,因为我的机器是由公司 IM 管理的(在处理异常用户的需求时,他们没有最好的记录,比如我)。

谢谢


下面是问题的完整示例。当 运行 时,LabelFrame 小部件(应该出现在选项卡 1 的 (0, 0) 处)不会出现。单击 "language" 小部件会导致其他所有内容以 "language" 小部件选择的语言显示文本。

来自“LanguageInUse.py”切换语言的代码:

import sys

c_cedille_lower    = "\u00E7" #  UTF 8/16 (code page 863?) French character
e_circumflex_lower = "\u00EA"

English  = 'English'                            # string shown on GUI
Francais = 'Fran' + c_cedille_lower + 'ais'    # string shown on GUI

DefaultLanguage = Francais
DefaultLanguage = English       # comment out this line to use French

user_language = DefaultLanguage     # set up language shown on GUI

# Define all language-dependent GUI strings (as "Application-Level" globals)

ComplianceMessage   = None
ReportTab1Title     = None
ReportTab2Title     = None
XCheckbuttonMessage = None
XLabelFrameMessage  = None
XLabelMessage       = None
XRadioButtonMessage = None

'''=========================================================================='''

def SetGuiLanguage( user_language ) :

    global  ComplianceMessage, LanguagePrompt        
    global  ReportTab1Title, ReportTab2Title 
    global XLabelFrameMessage, XCheckbuttonMessage, XLabelMessage, XRadioButtonMessage

    if ( user_language == English ):    
        LanguagePrompt      = "Language"
        ReportTab1Title     = "Message Counts"
        ReportTab2Title     = "Communications Compliance"
        XLabelFrameMessage  = "LabelFrame"
        XCheckbuttonMessage = "Checkbox"
        XLabelMessage       = "Label"
        XRadioButtonMessage = 'Radiobutton'
        ComplianceMessage  = "Compliance (engish)"        
    elif ( user_language == Francais ):    
        LanguagePrompt     = "Langage"
        ReportTab1Title    = "Comtes de message"
        ReportTab2Title    = "Compliance Communications"
        XLabelFrameMessage  = "LabelFrame en "  + Francais
        XCheckbuttonMessage = "Checkbox en "    + Francais
        XLabelMessage       = "Label en "       + Francais
        XRadioButtonMessage = "Radiobutton en " + Francais
        ComplianceMessage  =  "Compliance - "   + Francais        
    else:   
        print (' An unknown user language was specified' )   
        sys.exit()

    return

'''==========================================================================''' 

SetGuiLanguage( user_language )     # initialize all tkinter strings at startup

'''==========================  End of File  ================================''' 

来自“SelectReports.py”)构建笔记本的代码:

from tkinter import Checkbutton, Radiobutton, Label, LabelFrame, Frame
from tkinter import ttk 

import LanguageInUse

# Locally defined entities importable by other modules (often
# Tkinter Application level objects whose language can be changed)

ComplianceMessageText    = None
NotebookFrame            = None

XCheckbutton = None
XLabel       = None
XLabelFrame  = None  # NOT APPEARING ON THE GUI 
XRadiobutton = None

'''==========================================================================''' 

def TabbedReports( ParentFrame ) :

    global ComplianceMessageText,  NotebookFrame 
    global SelectReportFrame, UplinkFileWarningText 
    global XCheckbutton, XLabel, XLabelFrame, XRadiobutton

    # Builds the notebook and it's widgits

    NotebookFrame = ttk.Notebook( ParentFrame )   
    NotebookFrame.grid( row = 0, column = 1 )

    Tab1Frame = Frame( NotebookFrame ) 
    Tab2Frame = Frame( NotebookFrame ) 

    NotebookFrame.add( Tab1Frame )
    NotebookFrame.add( Tab2Frame )

    # Create widgets on Tab 1

    XLabelFrame  = LabelFrame(  Tab1Frame )  # NOT APPEARING ON GUI
    XCheckbutton = Checkbutton( Tab1Frame )
    XLabel       = Label(       Tab1Frame )
    XRadiobutton = Radiobutton( Tab1Frame )

    XLabelFrame.grid(  row = 1, column = 0 )  # NOT ON GUI 
    XCheckbutton.grid( row = 1, column = 1 )    
    XLabel.grid(       row = 2, column = 0 )  
    XRadiobutton.grid( row = 2, column = 1 )

    XLabelFrame.configure(  text = LanguageInUse.XLabelFrameMessage )   # NOT ON GUI 
    XCheckbutton.configure( text = LanguageInUse.XCheckbuttonMessage )
    XLabel.configure(       text = LanguageInUse.XLabelMessage )
    XRadiobutton.configure( text = LanguageInUse.XRadioButtonMessage )

     #  .tab() gives same effect as .configure() for other widget types 

    NotebookFrame.tab( 0 , text = LanguageInUse.ReportTab1Title )
    NotebookFrame.tab( 1 , text = LanguageInUse.ReportTab2Title )

    # Create the only widget on Tab 2  (uses other method to specify text)

    ComplianceMessageText = Label( Tab2Frame )
    ComplianceMessageText.grid(  row = 0, column = 0 )    
    ComplianceMessageText['text']  = LanguageInUse.ComplianceMessage 

    return

来自“ChangeLanguageOnGui.py”更新所有笔记本小部件的代码:

import sys, os
from tkinter import StringVar, Radiobutton, PhotoImage

#from TkinterRoot import root

import LanguageInUse
import SelectReports

'''==========================================================================''' 

def ChangeLanguageOnGui() :    

    SelectReports.XLabelFrame.configure(  text = LanguageInUse.XLabelFrameMessage )   # NOT ON GUI 
    SelectReports.XCheckbutton.configure( text = LanguageInUse.XCheckbuttonMessage )
    SelectReports.XLabel.configure(       text = LanguageInUse.XLabelMessage )
    SelectReports.XRadiobutton.configure( text = LanguageInUse.XRadioButtonMessage )

    #  .tab() gives the same effect as .configure() for other widget types

    SelectReports.NotebookFrame.tab( 0 , text = LanguageInUse.ReportTab1Title )
    SelectReports.NotebookFrame.tab( 1 , text = LanguageInUse.ReportTab2Title )

    SelectReports.ComplianceMessageText['text']  = LanguageInUse.ComplianceMessage

'''==========================================================================''' 

def SetUpGuiLanguage( LanguageFrame ) :

    GUI_Language = StringVar( value = LanguageInUse.user_language )

    #---------------------------------------------------------------------------
    def switchLanguage():  

        LanguageInUse.user_language = GUI_Language.get()                
        LanguageInUse.SetGuiLanguage( LanguageInUse.user_language )   

        ChangeLanguageOnGui()

        return

    #---------------------------------------------------------------------------

    UsingEnglish = Radiobutton( LanguageFrame, indicatoron = False,
                                variable = GUI_Language,
                                command = lambda: switchLanguage(),
                                value =  LanguageInUse.English )  
    UsingFrancais = Radiobutton( LanguageFrame, indicatoron = False,
                                 variable = GUI_Language,
                                 command = lambda: switchLanguage(),
                                 value = LanguageInUse.Francais )    
    UsingEnglish.grid(  row = 0, column = 0 )    
    UsingFrancais.grid( row = 1, column = 0 )

    UsingEnglish.configure(  text = LanguageInUse.English )
    UsingFrancais.configure( text = LanguageInUse.Francais )    

来自"TkinterRoot.py" 使 root 随处可导入的代码(显式导入此代码避免了 IntVar() 在其他模块的初始化阶段不可用等问题):

from tkinter import Tk  # possibly the worlds shortest useful python module

root = Tk()             # makes root an importable "Application Level" global

最后"A.py",主线文件:

from TkinterRoot import root 

from tkinter import LabelFrame
from tkinter import ttk

import ChangeLanguageOnGui, LanguageInUse, SelectReports, sys

LanguageFrame      = None

if __name__ == "__main__":

    LanguageChoice = LanguageInUse.English 

    if ( LanguageChoice == LanguageInUse.English ) :
        LanguageInUse.user_language = LanguageChoice
    elif ( LanguageChoice == 'Francais' ) :
        LanguageInUse.user_language = LanguageInUse.Francais
    else :
        print( "Unknown Language: " + LanguageChoice  )
        sys.exit()

    mainframe = ttk.Frame( root )
    mainframe.grid( row = 0, column = 0 )

    LanguageFrame = LabelFrame( mainframe, text = LanguageInUse.LanguagePrompt )
    LanguageFrame.grid( row = 0, column = 0 )

    ChangeLanguageOnGui.SetUpGuiLanguage( LanguageFrame )

    SelectReports.TabbedReports( mainframe ) 

    try:  
        root.mainloop()     
    except:
        print( 'Exception Occurred' )        

sys.exit()

环境为64位Python3.5.1、64位Win 7 Enterprise SP 1、64位Eclipse Mars 2(JavaEEIDE版) 运行宁 64 位 PyDev 5.1.2.201606231256。使用了 Pydev 的 "one user"(无管理员权限)版本,这需要 Microsoft 补丁 KB2999226(Win10 的一部分)到 Win7 上的 运行。最终的目标分发是 32 位 Windows 应用程序(因此它可以 运行 在 32 位和 64 位 Windows 上)- 并且 if/when 时间允许 - Linux。

需要记住一个小问题:在实际程序中使用了多个包。在每个包中,每个函数都被隔离在它自己的文件中。所有必须在外部可见(或需要在包的文件之间共享)的 objects(例如 tkinter 小部件)都在包的 _ _ init.py _ _ 文件中声明。必须对外部可见的函数通过使用相对导入(例如“from .Function1 import Function1”)显式导入_ _ init.py _ _。

您的标题具有误导性,因为您将 LabelFrame 放在框架中,而不是直接放在笔记本选项卡上。您的问题是 labelframe 没有出现在其 parent 框架中。 Notebook 无关紧要,所有相关代码应在发布前删除。

甚至框架也无关紧要,因为将标签框架直接放在根目录中会出现同样的问题。这是演示问题和解决方案的最少代码。

from tkinter import Tk
from tkinter import ttk

root = Tk()
lf1 = ttk.LabelFrame(root, text='labelframe 1')
lf2 = ttk.LabelFrame(root, text='labelframe 2', width=200, height=100)
lf1.pack()
lf2.pack()

我 运行 这是在带有 3.6a2 的 Win 10 上,它与 tk 8.6.4 一起提供。只有 lf2 可见,因为标签框的默认大小与框架一样是 0 x 0。non-default 大小来自显式大小调整或内容。有点令人惊讶的是,该标签不算作内容,也不会强制使用 non-default 大小。我在框架(您的情况)和选项卡上使用标签框重现了相同的结果。