简体   繁体   中英

Tkinter OptionMenu widget is not displaying values

At the moment I am working on a project using Python 3.6 Tkinter. At the moment, I am trying to make a user access rights OptionMenu for me to put either "User" or "Admin". I have tried various methods, but cannot seem to either fix it myself or to find helpful documentation online. The problem isn't in making the OptionMenu and displaying it, nor is it that the value of the StringVar variable isn't changing. The problem is that the text inside of the OptionMenu isn't changing when any new option is selected.

class UsersDetailsEditPage(Tk):
def __init__(self, *args, **kwargs):
    Tk.__init__(self, *args, **kwargs)
    self.title("Edit User Details")
    self.option_add("*Font", 'TkDefaultFont')

    self.noteBook = ttk.Notebook(self)

    for i in range(len(users)):
        self.noteBook.add(self.getUserViewFrame(users[i]), text=users[i][2])
    self.noteBook.pack()

    self.resizable(width=False, height=False)

def getUserViewFrame(self, user):
    frame = Frame(self)
    frame.grid_rowconfigure(1, weight=1)
    frame.grid_columnconfigure(1, weight=1)

    Label(frame, text="User's name:").grid(row=0, column=0, sticky=W)
    nameText = Text(frame, height=1, width=20)
    nameText.insert("1.0", user[2])
    nameText.edit_reset()
    nameText.grid(row=0, column=1, sticky=E)

    Label(frame, text="Username:").grid(row=1, column=0, sticky=W)
    usernameText = Text(frame, height=1, width=20)
    usernameText.insert("1.0", user[0])
    usernameText.edit_reset()
    usernameText.grid(row=1, column=1, sticky=E)

    Label(frame, text="Password:").grid(row=2, column=0, sticky=W)
    passwordText = Text(frame, height=1, width=20)
    passwordText.insert("1.0", user[1])
    passwordText.edit_reset()
    passwordText.grid(row=2, column=1, sticky=E)

    # the constructor syntax is:
    # OptionMenu(master, variable, *values)

    Label(frame, text="User Access:").grid(row=3, column=0, sticky=W)
    self.options = StringVar()
    self.options.set("User")

    self.userAccessDrop = ttk.OptionMenu(frame, self.options, "User", *("User", "Admin"))
    self.userAccessDrop.config(width=10)
    self.userAccessDrop.grid(row=3, column=1, sticky=E)

    return frame

This is the output of the code

I have all the library imports that are needed (I think):

from tkinter import *
from tkinter import messagebox
import tkinter.ttk as ttk
import csv
import os

If anyone can work out how to make this work it would be much appreciated. Thanks

I tried your code and it works for me. I can only guess that this is a garbage collection problem. Try assigning the option menu to frame rather than self, so that it doesn't get overwritten.

frame.options = StringVar()
frame.options.set("User")

frame.userAccessDrop = ttk.OptionMenu(frame, frame.options, "User", *("User", "Admin"))
frame.userAccessDrop.config(width=10)
frame.userAccessDrop.grid(row=3, column=1, sticky=E)

You really should rewrite this so that you subclass a Frame to make the user instances.

Edit: for example:

import tkinter as tk
from tkinter import ttk

class UserFrame(tk.Frame):
    def __init__(self, master=None, data=None, **kwargs):
        tk.Frame.__init__(self, master, **kwargs)

        data = data[2], data[0], data[1] # rearrange data
        labels = ("User's name:", "Username:", "Password:")
        for row, (label, value) in enumerate(zip(labels, data)):
            lbl = tk.Label(self, text=label)
            lbl.grid(row=row, column=0, sticky=tk.W)
            ent = tk.Entry(self)
            ent.insert(0, value)
            ent.grid(row=row, column=1, sticky=tk.E)

        lbl = tk.Label(self, text="User Access:")
        lbl.grid(row=3, column=0, sticky=tk.W)
        self.options = tk.StringVar(self, "User")

        self.userAccessDrop = ttk.OptionMenu(self,
            self.options,
            "User", # starting value
            "User", "Admin", # options
            )
        self.userAccessDrop.config(width=10)
        self.userAccessDrop.grid(row=len(labels), column=1, sticky=tk.E)

class UsersDetailsEditPage(tk.Tk):
    def __init__(self, *args, **kwargs):
        tk.Tk.__init__(self, *args, **kwargs)
        self.title("Edit User Details")
        self.option_add("*Font", 'TkDefaultFont')

        self.noteBook = ttk.Notebook(self)

        for user in users:
            self.noteBook.add(UserFrame(self, user), text=user[2])
        self.noteBook.pack()

        self.resizable(width=False, height=False)

It's really not functionally any different from what you have, just neater code (which actually makes a huge difference in coding time). Note I also got rid of the evil wildcard import and condensed your code a bit. Remember, if you are copying and pasting code blocks you are doing the computer's job. I also moved you to Entry widgets, which are a single line Text widget. Also, try to avoid the "convenience" initializing and laying out a widget on the same line. It's ugly and it leads to bugs.

I used 'self' in for the "self.options" and "self.userAccessDrop", but since we subclassed the Frame, "self" now refers to the Frame instance. In other words it's the same as my above block where I used "frame.options".

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM