2

I made a little login screen. After I put in the credentials I want the Tkinter to be closed and open a new python file. If you execute the code below it will give me the error NameError: name ttk is not defined. In the other file I imported everything and gave it the correct name.

The file I use for the login:

from tkinter import *
import tkinter.messagebox as tm

class LoginFrame(Frame):
    def __init__(self, master):
        super().__init__(master)

        self.label_username = Label(self, text="Username")
        self.label_password = Label(self, text="Password")
        self.photo = PhotoImage(file="sbshreg.png")

        self.label_image = Label(root, image=self.photo)
        self.label_image.image = self.photo

        self.entry_username = Entry(self)
        self.entry_password = Entry(self, show="*")

        self.label_username.grid(row=0, sticky=E)
        self.label_password.grid(row=1, sticky=E)
        self.label_image.grid(row=3, column=2, rowspan=2, columnspan=2, sticky=W, padx=10)

        self.entry_username.grid(row=0, column=1, sticky=E)
        self.entry_password.grid(row=1, column=1, sticky=E)

        self.logbtn = Button(self, text="Login", command=self._login_btn_clicked)
        self.logbtn.grid(columnspan=2, column=1, row=2, sticky=S+E+N+W)

        self.grid()

    def _login_btn_clicked(self):
        username = self.entry_username.get()
        password = self.entry_password.get()

        if username == "123" and password == "123":
            tm.showinfo("SBSHREG", "Welcome 123")
            #The sweet spot where all goes wrong...
            self.destroy()
            exec(open("./BatchFrame.py").read())
        else:
            tm.showerror("SBSHREG", "Incorrect username")

root = Tk()
root.title("SBSHREG")
root.geometry("235x120")
lf = LoginFrame(root)
root.mainloop()

In the other file I got this: from tkinter import ttk as ttk which should prevent the error in the other file from happening.

from tkinter import *
import tkinter.messagebox as tm
from tkinter import ttk as ttk

class BatchFrame(Frame):
    def __init__(self, master):
        super().__init__(master)

        self.photo = PhotoImage(file="sbshreg.png")
        self.label_photo = Label(root, image=self.photo)
        self.label_photo.image = self.photo

        self.label_photo.grid(row=0, column=2, sticky=N, padx=10, pady=10)

        #Add frame starting here
        frame = LabelFrame(self.master, text='Voeg batch toe')
        frame.grid (row=0, column=0, padx=10)

        self.label_batch = Label(frame, text="Batchnummer")
        self.label_employee = Label(frame, text="Medewerker")
        self.label_material = Label(frame, text="Materiaalsoort")
        self.label_weight = Label(frame, text="Gewicht")

        self.entry_batch = Entry(frame)
        self.entry_employee = Entry(frame)
        self.entry_material= Entry(frame)
        self.entry_weight = Entry(frame)

        self.label_batch.grid(row=0, column=0, sticky=S+E+N+W, columnspan=2, padx=10)
        self.label_employee.grid(row=2, column=0,  sticky=S+E+N+W, columnspan=2, padx=10)
        self.label_material.grid(row=4, column=0,  sticky=S+E+N+W, columnspan=2, padx=10)
        self.label_weight.grid(row=6, column=0,  sticky=S+E+N+W, columnspan=2, padx=10)

        self.entry_batch.grid(row=1, column=0, sticky=S+E+N+W, columnspan=2, padx=10)
        self.entry_employee.grid(row=3, column=0, sticky=S+E+N+W, columnspan=2, padx=10)
        self.entry_material.grid(row=5, column=0, sticky=S+E+N+W, columnspan=2, padx=10)
        self.entry_weight.grid(row=7, column=0, sticky=S+E+N+W, columnspan=2, padx=10)

        self.btnadd = Button(frame, text='Voeg toe', command=self._btn_add_clicked)
        self.btnadd.grid(column=0, row=8, pady=10)

        #Search frame starting here
        framesearch = LabelFrame(self.master, text='Zoek')
        framesearch.grid(row=0, column=1, sticky=N)

        self.label_batch = Label(framesearch, text="Batchnummer")
        self.label_employee = Label(framesearch, text="Medewerker")

        self.entry_batch = Entry(framesearch)
        self.entry_employee = Entry(framesearch)

        self.label_batch.grid(row=0, column=0, sticky=S, columnspan=2, padx=10)
        self.label_employee.grid(row=2, column=0, sticky=S, columnspan=2, padx=10)

        self.entry_batch.grid(row=1, column=0, sticky=S + E + N + W, columnspan=2, padx=10, pady=10)
        self.entry_employee.grid(row=3, column=0, sticky=S + E + N + W, columnspan=2, padx=10, pady=10)

        self.btnbatch = Button(framesearch, text="Zoek", command=self._btn_batch_clicked)
        self.btnemployee = Button(framesearch, text="Zoek", command=self._btn_employee_clicked)

        self.btnbatch.grid(columnspan=1, column=2, row=1, sticky=W, padx=10)
        self.btnemployee.grid(columnspan=1, column=2, row=3, sticky=W, padx=10)

        #This is the viewingarea for the data
        self.tree = ttk.Treeview (height=10, columns=("Batchnummer", "Medewerker", "Materiaalsoort", "Gewicht"))
        self.tree.grid (row=9, columnspan=10, padx=10, pady=10)
        self.tree.heading('#1', text='Batchnummer', anchor=W)
        self.tree.heading('#2', text='Medewerker', anchor=W)
        self.tree.heading('#3', text='Materiaalsoort', anchor=W)
        self.tree.heading('#4', text='Gewicht', anchor=W)
        self.tree.column('#0', stretch=NO, minwidth=0, width=0)
        self.tree.column('#1', stretch=NO, minwidth=0, width=100)
        self.tree.column('#2', stretch=NO, minwidth=0, width=100)
        self.tree.column('#3', stretch=NO, minwidth=0, width=100)
        self.tree.column('#4', stretch=NO, minwidth=0, width=100)

        self.grid()

    def _btn_add_clicked(self):
        batch = self.entry_batch.get()

    def _btn_batch_clicked(self):
        batch = self.entry_batch.get()

    def _btn_employee_clicked(self):
        batch = self.entry_employee.get()


root = Tk()
root.title("SBSHREG")
root.geometry("432x480")
lf = BatchFrame(root)
root.mainloop()

If I change self.destroy() to root.destroy() I get the following error: _tkinter.TclError: can't invoke "label" command: application has been destroyed. In the second file the functions are not done yet because I'm still working on the file, but this shouldn't have any impact on the error.

I searched everywhere and tried a lot and I still got no clue whatsoever...

Lauk-k
  • 80
  • 1
  • 8

3 Answers3

2

Consider initializing frames in a top level class, GUI, that handles opening of both frames where LoginFrame calls its parent's open_batch() (now lambda implemented) method. Below assumes LoginFrame.py and BatchFrame.py resides in same folder as GUI_App script.

In this way, scripts run as standalone modules on one Tk() instance.

GUIApp (calls child frames, LoginFrame, and BatchFrame)

from tkinter import *
import LoginFrame as LF
import BatchFrame as BF

class GUI():

    def __init__(self):    
        self.root = Tk()
        self.root.title("SBSHREG")
        self.root.geometry("235x120")

        self.root.open_batch = self.open_batch

        lf = LF.LoginFrame(self.root)
        self.root.mainloop()

    def open_batch(self):
        bf = BF.BatchFrame(self.root)

app = GUI()

LoginFrame

from tkinter import *
import tkinter.messagebox as tm

class LoginFrame(Frame):

    def __init__(self, master):
        super().__init__(master)

        self.label_username = Label(self, text="Username")
        self.label_password = Label(self, text="Password")
        self.photo = PhotoImage(file="sbshreg.png")

        self.label_image = Label(self, image=self.photo)
        self.label_image.image = self.photo

        self.entry_username = Entry(self)
        self.entry_password = Entry(self, show="*")

        self.label_image.grid(row=0, column=2, rowspan=2, columnspan=2, sticky=W, padx=10)
        self.label_username.grid(row=2, sticky=E)
        self.label_password.grid(row=3, sticky=E)

        self.entry_username.grid(row=2, column=1, sticky=E)
        self.entry_password.grid(row=3, column=1, sticky=E)

        self.logbtn = Button(self, text="Login", command=lambda: self._login_btn_clicked(master))
        self.logbtn.grid(row=4, column=1, columnspan=2, sticky=S+E+N+W)

        self.grid()

    def _login_btn_clicked(self, controller):
        username = self.entry_username.get()
        password = self.entry_password.get()

        if username == "123" and password == "123":
            tm.showinfo("SBSHREG", "Welcome 123")

            self.destroy()
            controller.open_batch()
        else:
            tm.showerror("SBSHREG", "Incorrect username")

BatchFrame

from tkinter import *
import tkinter.messagebox as tm
from tkinter import ttk as ttk

class BatchFrame(Frame):

    def __init__(self, master):
        super().__init__(master)    

        self.photo = PhotoImage(file="sbshreg.png")
        self.label_photo = Label(master, image=self.photo)
        self.label_photo.image = self.photo

        self.label_photo.grid(row=0, column=2, sticky=N, padx=10, pady=10)

        #Add frame starting here
        frame = LabelFrame(master, text='Voeg batch toe')
        frame.grid (row=0, column=0, padx=10)

        self.label_batch = Label(frame, text="Batchnummer")
        self.label_employee = Label(frame, text="Medewerker")
        self.label_material = Label(frame, text="Materiaalsoort")
        self.label_weight = Label(frame, text="Gewicht")

        self.entry_batch = Entry(frame)
        self.entry_employee = Entry(frame)
        self.entry_material= Entry(frame)
        self.entry_weight = Entry(frame)

        self.label_batch.grid(row=0, column=0, sticky=S+E+N+W, columnspan=2, padx=10)
        self.label_employee.grid(row=2, column=0,  sticky=S+E+N+W, columnspan=2, padx=10)
        self.label_material.grid(row=4, column=0,  sticky=S+E+N+W, columnspan=2, padx=10)
        self.label_weight.grid(row=6, column=0,  sticky=S+E+N+W, columnspan=2, padx=10)

        self.entry_batch.grid(row=1, column=0, sticky=S+E+N+W, columnspan=2, padx=10)
        self.entry_employee.grid(row=3, column=0, sticky=S+E+N+W, columnspan=2, padx=10)
        self.entry_material.grid(row=5, column=0, sticky=S+E+N+W, columnspan=2, padx=10)
        self.entry_weight.grid(row=7, column=0, sticky=S+E+N+W, columnspan=2, padx=10)

        self.btnadd = Button(frame, text='Voeg toe', command=self._btn_add_clicked)
        self.btnadd.grid(column=0, row=8, pady=10)

        #Search frame starting here
        framesearch = LabelFrame(master, text='Zoek')
        framesearch.grid(row=0, column=1, sticky=N)

        self.label_batch = Label(framesearch, text="Batchnummer")
        self.label_employee = Label(framesearch, text="Medewerker")

        self.entry_batch = Entry(framesearch)
        self.entry_employee = Entry(framesearch)

        self.label_batch.grid(row=0, column=0, sticky=S, columnspan=2, padx=10)
        self.label_employee.grid(row=2, column=0, sticky=S, columnspan=2, padx=10)

        self.entry_batch.grid(row=1, column=0, sticky=S + E + N + W, columnspan=2, padx=10, pady=10)
        self.entry_employee.grid(row=3, column=0, sticky=S + E + N + W, columnspan=2, padx=10, pady=10)

        self.btnbatch = Button(framesearch, text="Zoek", command=self._btn_batch_clicked)
        self.btnemployee = Button(framesearch, text="Zoek", command=self._btn_employee_clicked)

        self.btnbatch.grid(columnspan=1, column=2, row=1, sticky=W, padx=10)
        self.btnemployee.grid(columnspan=1, column=2, row=3, sticky=W, padx=10)

        #This is the viewingarea for the data
        self.tree = ttk.Treeview (height=10, columns=("Batchnummer", "Medewerker", "Materiaalsoort", "Gewicht"))
        self.tree.grid (row=9, columnspan=10, padx=10, pady=10)
        self.tree.heading('#1', text='Batchnummer', anchor=W)
        self.tree.heading('#2', text='Medewerker', anchor=W)
        self.tree.heading('#3', text='Materiaalsoort', anchor=W)
        self.tree.heading('#4', text='Gewicht', anchor=W)
        self.tree.column('#0', stretch=NO, minwidth=0, width=0)
        self.tree.column('#1', stretch=NO, minwidth=0, width=100)
        self.tree.column('#2', stretch=NO, minwidth=0, width=100)
        self.tree.column('#3', stretch=NO, minwidth=0, width=100)
        self.tree.column('#4', stretch=NO, minwidth=0, width=100)

        self.grid()

    def _btn_add_clicked(self):
        batch = self.entry_batch.get()

    def _btn_batch_clicked(self):
        batch = self.entry_batch.get()

    def _btn_employee_clicked(self):
        batch = self.entry_employee.get()
Parfait
  • 104,375
  • 17
  • 94
  • 125
  • This seems to work properly, but one problem persists. The error: `UnboundLocalError: local variable 'bf' referenced before assignment` has something to do with `bf = bf.TestBatchFrame()` – Lauk-k Apr 16 '18 at 14:55
  • Whoops! Simply rename to anything other than import alias! See edit using `new_bf = bf.TestBatchFrame()`. – Parfait Apr 16 '18 at 15:14
  • This simply just works perfectly, nothing more to add! Thanks for the knowledge, learned again something more today – Lauk-k Apr 16 '18 at 15:16
  • Excellent! Glad to hear and help. Happy coding! – Parfait Apr 16 '18 at 15:17
  • So why remove the use of `master`? Is it not better to maintain one instance of `Tk()` instead of creating new ones? – Mike - SMT Apr 16 '18 at 15:26
  • Actually, *master* is more substituted for *root*. Instead of a passed in parameter to the class it is initialized as an attribute to the class. In this way, `Tk()` is localized in the class scope of both frames and not a global instance. – Parfait Apr 16 '18 at 15:32
  • I have been under the impression its best to have one instance of `Tk()` when working with tkinter instead of multiple instance. In this particular case I see nothing wrong with it but in general I believe it is considered standard practice to have one `Tk()` instance. See this Q/A on the matter: [Why are multiple instances of Tk discouraged?](https://stackoverflow.com/questions/48045401/why-are-multiple-instances-of-tk-discouraged) – Mike - SMT Apr 16 '18 at 16:00
  • Interesting point! OP can keep *super* and *master* in the child frames and use a top level class to handle both all on on instance `Tk` and one set of `import` lines. See edit. – Parfait Apr 16 '18 at 17:29
1

I do not agree with the method of how you are invoking your 2nd python file. You really dont need to use exec here.

However if you really want to you need to add import tkinter.ttk as ttk to your first page for it to work right.

Your best option is to import the 2nd file on the 1st file and invoke it by calling the class name.

so for you imports on the first page you need to add import BatchFrame.

Then call it in your code.

Replace:

exec(open("./test2.py").read())

with:

BatchFrame.BatchFrame(root)

I do see one mistake in your BatchFrame class thought.

change this:

self.label_photo = Label(root, image=self.photo)

to this:

self.label_photo = Label(master, image=self.photo)

UPDATE: To address your issue from the comment make these changes:

1: Add this to your BatchFrame Class:

class BatchFrame(Frame):
    def __init__(self, master):
        super().__init__(master)
        self.master = master # add this
        self.master.title("SBSHREG") # and this
        self.master.geometry("432x480") # and this

2: Remove this from your BatchFrame file:

root = Tk()
root.title("SBSHREG")
root.geometry("432x480")
lf = BatchFrame(root)
root.mainloop()

3: Add this to your LoginFrame class:

class LoginFrame(Frame):
    def __init__(self, master):
        super().__init__(master)
        self.master = master # add this

4: In the LoginFrame class change this:

BatchFrame.BatchFrame(root)

to this:

BatchFrame.BatchFrame(self.master)
Mike - SMT
  • 14,784
  • 4
  • 35
  • 79
  • Yeah, I was afraid of this. Probably that it is just to hard and gives too much problems trying to execute a file. It's smarter to put the code in the first file. Also, if i change root to master, it crashes with the error: _tkinter.TclError: image "pyimage2" doesn't exist. I will try to merge the 2 files together! – Lauk-k Apr 16 '18 at 13:50
  • I replaced the code you suggested with the line exec(open("./test2.py").read()). No error is popping up right now, buth the problem is that the BatchFrame is now popping up first, instead of the login window. – Lauk-k Apr 16 '18 at 13:54
  • @Lauk-k that is because on your second file you are still creating an instance of `Tk(). I will update my answer with the corrections you need. – Mike - SMT Apr 16 '18 at 14:33
  • @Lauk-k I have added a few corrections to the code to fix the issue of your BatchFrame popping up before login. – Mike - SMT Apr 16 '18 at 14:37
  • *It's smarter to put the code in the first file.* ... not quite. Many Python libraries runs with classes and methods across many .py scripts. The `import` strategy as other answers emphasize is very robust, borrowing from other object-oriented languages like Java, C#, C++! – Parfait Apr 16 '18 at 15:12
  • @Lauk-k as Parfait pointed out it does not hurt to have separate files. Its just really a matter of learning how to implement the code from one file to the next. Once you have that figure out it will keep things nice and clean by using separate files. Its good for managing upkeep of your code. – Mike - SMT Apr 16 '18 at 15:25
0

I recommend importing batchframe instead of executing it.

from tkinter import *
import tkinter.messagebox as tm
from tkinter import ttk as ttk
from batchframe import BatchFrame

class LoginFrame(Frame):
    def __init__(self, master):
        super().__init__(master)
        self.master = master

        self.label_username = Label(self, text="Username")
        self.label_password = Label(self, text="Password")
        self.photo = PhotoImage(file="icon.png")

        self.label_image = Label(root, image=self.photo)
        self.label_image.image = self.photo

        self.entry_username = Entry(self)
        self.entry_password = Entry(self, show="*")

        self.label_username.grid(row=0, sticky=E)
        self.label_password.grid(row=1, sticky=E)
        self.label_image.grid(row=3, column=2, rowspan=2, columnspan=2, sticky=W, padx=10)

        self.entry_username.grid(row=0, column=1, sticky=E)
        self.entry_password.grid(row=1, column=1, sticky=E)

        self.logbtn = Button(self, text="Login", command=self._login_btn_clicked)
        self.logbtn.grid(columnspan=2, column=1, row=2, sticky=S+E+N+W)

        self.grid()

    def _login_btn_clicked(self):
        username = self.entry_username.get()
        password = self.entry_password.get()

        if username == "123" and password == "123":
            tm.showinfo("SBSHREG", "Welcome 123")
            #The sweet spot where all goes wrong...
            self.destroy()
            # Create the instance of the BatchFrame class, passing in self.master
            self.batchframe = BatchFrame(self.master)
        else:
            tm.showerror("SBSHREG", "Incorrect username")

Then in batchframe.py change variable reference from root to master

Ron Norris
  • 2,642
  • 1
  • 9
  • 13
  • And also, OP needs to remove the non-Class lines in *BatchFrame*: `root = Tk()...root.mainloop()`. Otherwise it will run on `import`! – Parfait Apr 16 '18 at 14:18
  • Yeah I turned them off, but for some reason in the login file it doesn't recognize `from batchframe import BatchFrame` – Lauk-k Apr 16 '18 at 14:25
  • Okay I needed to change `from batchframe import BatchFrame` to `from BatchFrame import BatchFrame` but after this it still doesn't recognize import Batchframe. It says unresolved reference. – Lauk-k Apr 16 '18 at 14:29