Search code examples
pythontkintercustomtkinter

How to Import Python Libraries Only Once


I'm working on a GUI app to add clients to a database. Now, I'm stuck with the tkinter library. I would like to import models only once, and only in the app.py file because I will add more functions to this file. I would like to add tkinter elements in file adduser.py. Here is the error:

Traceback (most recent call last):
  File "app.py", line 8, in <module>
    from adduser import adduser
  File "C:\*************adduser.py", line 10, in <module>
    name_label = tk.Label(root, text="Name:")
NameError: name 'tk' is not defined
# app.py

import os
import re
import sys
import time
import requests
from adduser import adduser

import tkinter as tk
import ttkbootstrap as ttk
from ttkbootstrap.scrolled import ScrolledFrame
from ttkbootstrap.toast import ToastNotification
from ttkbootstrap.tooltip import ToolTip
from ttkbootstrap.widgets import DateEntry, Floodgauge, Meter

root = ttk.Window(themename = "vapor")
root.geometry('800x600')
root.title('Users')

adduser()

root.mainloop()

I would like to add tkinter elements in file adduser.py

# adduser.py

import sqlite3

conn = sqlite3.connect('users.db')

def adduser(name, email):
    conn.execute('INSERT INTO users (name, email) VALUES (?, ?)', (name, email))
    conn.commit()
    print(f"User {name} added successfully!")

name_label = tk.Label(root, text="Name:")
name_label.pack()
name_entry = tk.Entry(root)
name_entry.pack()

email_label = tk.Label(root, text="Email:")
email_label.pack()
email_entry = tk.Entry(root)
email_entry.pack()

submit_button = tk.Button(root, text="Submit", command=get_input)
submit_button.pack()

adduser(name, email)

conn.close()

I appreciate your help and effort.

I've tried to import tkinter in both files, but that's not what I would like to achieve.


Solution

  • Hopefully this points you in the right direction. As the comments have mentioned, to be able to access tkinter in another file you need to either import it or pass it along so that it is accessible. Passing it along can be in the form of function arguments like this:

    # in adduser.py
    def adduser(name, email, tk, root):
        pass
        # here you now have tk available to use
    
    # in app.py
    import tkinter as tk
    import ttkbootstrap as ttk
    from adduser import adduser
    
    root = ttk.Window(themename = "vapor")
    root.geometry('800x600')
    root.title('Users')
    
    adduser("John Doe", "john@doe.com", tk, root)
    

    However, I would recommend using the import over passing modules across like this in function calls. One thing to keep in mind is that when you make an import statement, all the top-level code runs in that file. Importing adduser.py in the question above, would run the name_label, email_label and submit_button sections at import time. A better structure would be like this:

    # app.py
    
    import os
    import re
    import sys
    import time
    import requests
    from adduser import adduser, initialize_user_tk
    
    import tkinter as tk
    import ttkbootstrap as ttk
    from ttkbootstrap.scrolled import ScrolledFrame
    from ttkbootstrap.toast import ToastNotification
    from ttkbootstrap.tooltip import ToolTip
    from ttkbootstrap.widgets import DateEntry, Floodgauge, Meter
    
    root = ttk.Window(themename = "vapor")
    root.geometry('800x600')
    root.title('Users')
    
    initialize_user_tk(root)
    adduser("John Doe", "john@doe.com")
    
    root.mainloop()
    
    # adduser.py
    
    import sqlite3
    import tkinter as tk
    
    
    def adduser(name, email):
        conn = sqlite3.connect('users.db')
        conn.execute('INSERT INTO users (name, email) VALUES (?, ?)', (name, email))
        conn.commit()
        print(f"User {name} added successfully!")
        conn.close()
    
    def initialize_user_tk(root):
        name_label = tk.Label(root, text="Name:")
        name_label.pack()
        name_entry = tk.Entry(root)
        name_entry.pack()
    
        email_label = tk.Label(root, text="Email:")
        email_label.pack()
        email_entry = tk.Entry(root)
        email_entry.pack()
    
        def invoke_callback():
            name = name_entry.get()
            email = email_entry.get()
            adduser(name, email)
    
        submit_button = tk.Button(root, text="Submit", command=invoke_callback)
        submit_button.pack()
    

    In this way, the name_label, email_label and submit_button sections run when you are ready to call them