Search code examples
pythontkinterdialog

how to get input dialog from various labels with tkinter


I am trying to get user input data from a tkinter dialog box with several labels. I found a website with an example close to my case here.

I want the user to type 3 data: first name, last name, and identification number. Once the validate button is clicked, I need to check if the identification number is composed only of numbers and if the name is valid, ensuring that it starts with a capital letter. If those conditions are satisfied, I will create a link and store the inputs into a file.

I adapted the code example to my case, but I can't get it to work.

Here is what I tried in file1.py:

from userDialogBox import UserDialogBox

# ask user to type their "id number", "first name", "last name"
user_dialog = UserDialogBox(None)
# if input data are valid
if user_dialog.validate():
    # build link web page
    pass

In userDialogBox.py:

import tkinter as tk
from tkinter import simpledialog

class UserDialogBox(simpledialog.Dialog):
    def body(self, master):
        self.title("Données utilisateur")
        self.iconbitmap("logo_km.ico")

        tk.Label(master, text="Matricule", font=('Arial', 15)).grid(row=0)
        tk.Label(master, text="Prénom", font=('Arial', 15)).grid(row=1)
        tk.Label(master, text="Nom", font=('Arial', 15)).grid(row=2)

        self.matricule_entry = tk.Entry(master)
        self.first_name_entry = tk.Entry(master)
        self.last_name_entry = tk.Entry(master)

        self.matricule_entry.grid(row=0, column=1)
        self.first_name_entry.grid(row=1, column=1)
        self.last_name_entry.grid(row=2, column=1)

    def validate(self):
        matricule = self.matricule_entry.get()
        first_name = self.first_name_entry.get()
        last_name = self.last_name_entry.get()

        if not matricule.isdigit():
            tk.messagebox.showerror("Erreur", "Matricule invalide.")
            return False

        if not first_name:
            tk.messagebox.showerror("Erreur", "Prénom invalide.")
            return False
        
        if not last_name:
            tk.messagebox.showerror("Erreur", "Nom invalide.")
            return False

        return True

    def apply(self):
        self.matricule = self.matricule_entry.get()
        self.first_name = self.first_name_entry.get()
        self.last_name = self.last_name_entry.get()

Once I tested my code, I received the following error message:

matricule = self.matricule_entry.get()
File "C:\Python310\lib\tkinter\__init__.py", line 3082, in get
    return self.tk.call(self._w, 'get')
_tkinter.TclError: invalid command name ".!userdialogbox.!frame.!entry"

I rarely write Python code, so I would appreciate any help you can offer. Thank you!


Solution

  • Validation is done every time you click the OK button. If you inspect the whole traceback (not only the snippet posted) you might realize that your error is triggered from the the validation call you do manually in file1 which is after the dialog has either been accepted or canceled. Either way the widgets you are trying to access are destroyed (see the functions from Dialog below):

    def ok(self, event=None):
        if not self.validate():
            self.initial_focus.focus_set() # put focus back
            return
        self.withdraw()
        self.update_idletasks()
        try:
            self.apply()
        finally:
            self.cancel()
    def cancel(self, event=None):
        # put focus back to the parent window
        if self.parent is not None:
            self.parent.focus_set()
        self.destroy()
    

    You can implement a new instance variable valid to your dialog to hold information of whether the input was accepted. This is how it can be done:

    import tkinter as tk
    from tkinter import simpledialog
    
    
    class UserDialogBox(simpledialog.Dialog):
        def body(self, master):
            self.valid = False  # implement valid to check if input was provided
            self.title("Données utilisateur")
            self.iconbitmap("logo_km.ico")
    
            tk.Label(master, text="Matricule", font=('Arial', 15)).grid(row=0)
            tk.Label(master, text="Prénom", font=('Arial', 15)).grid(row=1)
            tk.Label(master, text="Nom", font=('Arial', 15)).grid(row=2)
    
            self.matricule_entry = tk.Entry(master)
            self.first_name_entry = tk.Entry(master)
            self.last_name_entry = tk.Entry(master)
    
            self.matricule_entry.grid(row=0, column=1)
            self.first_name_entry.grid(row=1, column=1)
            self.last_name_entry.grid(row=2, column=1)
    
        def validate(self):
            matricule = self.matricule_entry.get()
            first_name = self.first_name_entry.get()
            last_name = self.last_name_entry.get()
    
            if not matricule.isdigit():
                tk.messagebox.showerror("Erreur", "Matricule invalide.")
                return False
    
            if not first_name:
                tk.messagebox.showerror("Erreur", "Prénom invalide.")
                return False
    
            if not last_name:
                tk.messagebox.showerror("Erreur", "Nom invalide.")
                return False
    
            self.valid = True  # set valid to true if the input is correct
            return True
    
        def apply(self):
            self.matricule = self.matricule_entry.get()
            self.first_name = self.first_name_entry.get()
            self.last_name = self.last_name_entry.get()
    
    
    if __name__ == '__main__':
        # ask user to type their "id number", "first name", "last name"
        user_dialog = UserDialogBox(tk.Tk())
        # if input data are valid
        if user_dialog.valid:  # check for valid instead of calling the validation again
            # print data
            print(user_dialog.matricule, user_dialog.first_name, user_dialog.last_name)