-1

I'm new to python I'm trying to create a login using RFID. I started to create a simple GUI using lots of Toplevel windows, but I want to create a GUI in only one window and the controller solves it. Now I'm having a problem since I'm trying to get the value of a variable "id" and use it as a reference to get data from my database.

Here's my code.

class Kiosk(Tk):
    def __init__(self, *args, **kwargs):
        Tk.__init__(self, *args, **kwargs)

        self.title_font = tkfont.Font(family='Helvetica', size=18, weight="bold", slant="italic")

        GPIO.setwarnings(False)

        container = Frame(self)
        container.pack(side="top", fill="both", expand=True)
        container.grid_rowconfigure(0, weight=1)
        container.grid_columnconfigure(0, weight=1)

        self.style = Style()
        self.style.theme_use('alt')
        self.style.map('TCombobox', fieldbackground=[('readonly','white')])

        self.db = pymysql.connect(host = "192.168.1.11",port = 3306, user = "root",passwd = "justin",db= "thesis_db")
        self.cursor = self.db.cursor()
        self.QueryResident = "CREATE TABLE IF NOT EXISTS residents_db (FIRST_NAME varchar(255) not null, MIDDLE_NAME varchar(255) not null, LAST_NAME varchar(255) not null,SEX varchar(255) not null, BIRTH_DATE date, CIVIL_STATUS varchar(255) not null, PLACE_OF_BIRTH varchar(255) not null, RFID varchar(255) not null)"
        self.cursor.execute(self.QueryResident)

        self.frames = {}
        for F in (Start_Page, Registration_Page_Admin, Registration_Page_User, Update_User, Update_Admin, Login_RFID, main_page):
            page_name = F.__name__
            frame = F(parent=container, controller=self)
            self.frames[page_name] = frame

            frame.grid(row=0, column=0, sticky="nsew")

        self.show_frame("Start_Page")

    def show_frame(self, page_name):
        frame = self.frames[page_name]
        frame.tkraise()
    def get_page(self, page_class):
        return self.frames[page_class]

class Start_Page(Frame):

    def __init__(self, parent, controller):
        Frame.__init__(self, parent)
        self.controller = controller
        label = Label(self, text="Login", font=controller.title_font)
        label.pack(side="top", fill="x", pady=10)

        button1 = Button(self, text="Login",
                            command=lambda: controller.show_frame("Login_RFID"))
        button1.pack()

class Login_RFID(Frame):
    def __init__(self, parent, controller):
        Frame.__init__(self, parent)
        self.parent = parent
        self.controller = controller
        self.get_Firstn1 = None
        self.img_login = (Image.open("Scan_RFID.png"))
        self.image_login = self.img_login.resize((550,417))
        self.img_background_login = ImageTk.PhotoImage(self.image_login)

        self.db = pymysql.connect(host = "192.168.1.11",port = 3306, user = "root",passwd = "justin",db= "thesis_db")
        self.cursor = self.db.cursor()

        label_login = Label(self, image = self.img_background_login) 
        label_login.pack(fill="both", expand = 1)
        label_login.bind('<Enter>',self.login_RFID)


    def login_RFID(self, event):
        self.reader = SimpleMFRC522()
        self.id, self.text = self.reader.read()
        self.cursor.execute("SELECT * FROM residents_admin WHERE RFID = %s",str(self.id))
        if (self.cursor.fetchone() is not None): 
            self.controller.show_frame("main_page")
            qwe = False
        else:
            self.cursor.execute("SELECT * FROM residents_db WHERE RFID = %s", str(self.id))
            if (self.cursor.fetchone() is not None): 
                self.controller.show_frame("main_page")
                qwe = False
            else:
                messagebox.showerror("Warning!","Your RFID card is not yet registered!")

    def get_data(self):
        self.cursor.execute("SELECT * FROM residents_admin WHERE RFID = %s",str(self.id))
        if (self.cursor.fetchone() is not None): 
            self.cursor.execute("SELECT FIRST_NAME FROM residents_admin WHERE RFID = %s", str(self.id))
            self.get_Firstn = self.cursor.fetchone()
            self.get_Firstn1 = str(self.get_Firstn[0])
            self.cursor.execute("SELECT MIDDLE_NAME FROM residents_admin WHERE RFID = %s", str(self.id))
            self.get_Middlen = self.cursor.fetchone()
            self.get_Middlen1 = str(self.get_Middlen[0])
            self.cursor.execute("SELECT LAST_NAME FROM residents_admin WHERE RFID = %s", str(self.id))
            self.get_Lastn = self.cursor.fetchone()
            self.get_Lastn1 = str(self.get_Lastn[0])
            self.cursor.execute("SELECT BIRTH_DATE FROM residents_admin WHERE RFID = %s", str(self.id))
            self.get_Birthd = self.cursor.fetchone()
            self.get_Birthd1 = str(self.get_Birthd[0])
            self.cursor.execute("SELECT PLACE_OF_BIRTH FROM residents_admin WHERE RFID = %s", str(self.id))
            self.get_Placeb = self.cursor.fetchone()
            self.get_Placeb1 = str(self.get_Placeb[0])
            self.cursor.execute("SELECT CIVIL_STATUS FROM residents_admin WHERE RFID = %s", str(self.id))
            self.get_Civils = self.cursor.fetchone()
            self.get_Civils1 = str(self.get_Civils[0])
            self.cursor.execute("SELECT SEX FROM residents_admin WHERE RFID = %s", str(self.id))
            self.get_Sex = self.cursor.fetchone()
            self.get_Sex1 = str(self.get_Sex[0])

class main_page(Frame):
    def __init__(self, parent, controller):
        Frame.__init__(self, parent)
        self.controller = controller
        label = Label(self, text="Welcome", font=controller.title_font)
        label.pack()
        login_rfid_page = self.controller.get_page("Login_RFID")
        login_rfid_page.get_data(None)
        value = login_rfid_page.get_Firstn1
        print(value)

        button1 = Button(self, text="Register User",
                            command=lambda: self.controller.show_frame("Registration_Page_User"))
        button2 = Button(self, text="Register Admin",
                            command=lambda: self.controller.show_frame("Registration_Page_Admin"))
        button3 = Button(self, text="Update User",
                            command=lambda: self.controller.show_frame("Update_User"))
        button4 = Button(self, text="Update Admin",
                            command=lambda: self.controller.show_frame("Update_Admin"))
        button1.pack()
        button2.pack()
        button3.pack()
        button4.pack()

Here's my error.

    Exception in Tkinter callback
Traceback (most recent call last):
  File "/usr/lib/python3.7/tkinter/__init__.py", line 1705, in __call__
    return self.func(*args)
  File "/home/pi/Thesis/qweqweqweq.py", line 85, in login_RFID
    self.controller.show_frame("main_page")
  File "/home/pi/Thesis/qweqweqweq.py", line 45, in show_frame
    frame = self.frames[page_name]
KeyError: 'main_page'

I think I don't have any problem scanning my RFID. I just need to get the value of the scanned RFID from id to the main page to use it as a reference to get the required data.

I followed the solution answered by Sir Bryan Oakley here and to his other answers to other questions. I read it all night, I somehow get what he meant to his answer I just don't know why I can't do it.

I hope you guys can help me. I trimmed the code above and provided the only code related to the error. Thank you!

Nuuuuuub
  • 3
  • 3
  • error shows it has problem to change Frame in window - it can't find `main_page` in `self.frames` - it looks like it didn't create this Frame or it was deleted. How do you run it? – furas Jan 24 '20 at 07:15
  • 1
    Remove the quotes from the show frame call. It should be `self.controller.show_frame(main_page)`. Same for all the rest of the show_frame calls. Don't use quotes; use the actual object. – Novel Jan 24 '20 at 07:20
  • You must create an instance of `Kiosk` to create all the frames. – martineau Jan 24 '20 at 07:21
  • Hi sir @furas, I am running it without error if I removed the 'value = login_rfid_page.id' but I can't get the value of id to the main_page class. When I added the 'value = login_rfid_page.id' I'm getting the error stated in my question. Thank you for your response sir! – Nuuuuuub Jan 24 '20 at 07:21
  • when I run it then it shows me problem to get `id` becuse you don't create it in `__init__` (which is executed at start) but in `login_RFID`. And you don't use `self.id` but `id` - so it is local variable and it can't be accessed from other places. – furas Jan 24 '20 at 07:23
  • 1
    @Novel: That's incorrect. `show_frame()` expects a string argument (the name of a `Frame` subclass). – martineau Jan 24 '20 at 07:24
  • @Novel, I removed it but still, the error occurs. I don't have any error if I removed the `value = login_rfid_page.id`. I think my problem is when I will get the value of the variable id. – Nuuuuuub Jan 24 '20 at 07:24
  • sir @furas, I changed it to self.id. it's still running and working if I removed the `value = login_rfid_page.id`. I still get the error even if I changed it from `value = login_rfid_page.self.id` but still got an error :( – Nuuuuuub Jan 24 '20 at 07:30
  • if you want ot get `login_rfid_page.id` then first you have to create it as `self.id` in `Login_RFID.__init__`. You may have to assign some default value - ie. `None` to recognize that you didn't run yet `self.id, self.text = self.reader.read()` in `login_RFID`. OR you would have to run `login_rfid_page.login_RFID()` before you get self.id – furas Jan 24 '20 at 07:30
  • I don't know what value you expect to get from `id` but first you have to create this variable i.e. `self.id = None` in `Login_RFID.__init__()` and then you can access it as `login_rfid_page.id` – furas Jan 24 '20 at 07:36
  • Sir @furas, I think I'm almost there, the error disappeared when I put `self.id = None` to my `Login_RFID.__init__` I can now proceed to the next frame when I logged in. But the value of my `self.id` is `None` when I print it, not the string scanned by my reader. – Nuuuuuub Jan 24 '20 at 07:38
  • you didn'r run scaner. You would need to run `login_rfid_page.login_RFID()`. And scaner assings to local varialble `id` - you have to uses `self.id` in line `self.id, self.text = self.reader.read()` – furas Jan 24 '20 at 07:42
  • @furas, Thank you so much, sir. I cannot ask any more questions since the StackOverflow limit me to one question only. and I understand that since I'm really stubborn and stupid. anyway, thank you so much for your help. – Nuuuuuub Jan 24 '20 at 09:52

1 Answers1

0

First: you have to use self.id instead of id to have access to id from different places

def login_RFID(self, event):

    self.reader = SimpleMFRC522()
    self.id, self.text = self.reader.read()  # self.id

Second: you would have to run this function in main_page to get correct value

    login_rfid_page = self.controller.get_page("Login_RFID")

    login_rfid_page.login_RFID(None)  # run scanner

    value = login_rfid_page.id
    print(value)
furas
  • 134,197
  • 12
  • 106
  • 148
  • Hi sir, thank you for this simplified answer. But it's not working, my GUI is not popping up if I added the `login_rfid_page.login_RFID(None)` I removed it and it's working properly but the id's value is None. When I used simple MFRC522 RFID reader, the whole program is frozen until I scanned an RFID. I think that's the reason why... – Nuuuuuub Jan 24 '20 at 07:56
  • when I used the `self.reader = SimpleMFRC522()` my whole GUI is frozen. I don't know why that's the reason I used bind. It will respond again If I scanned an RFID. – Nuuuuuub Jan 24 '20 at 07:58
  • 1
    if you have to run long running code then you may have to run it in separted `thread` - but it is different big problem. Long running code blocks `mainloop` which gets key/mouse events from system, send them to widgets, redraw widgets - so it looks like frozen. – furas Jan 24 '20 at 07:59
  • 1
    it's frozen whenever I declare `self.reader = SimpleMFRC522()` It is also the code to start the MFRC522 to start to scan. can I create another method to pass the value of id from `login_RFID` to '`new_method`? and then I will call `login_rfid_page.new_method(None)` is it possible? – Nuuuuuub Jan 24 '20 at 08:07
  • I am back to the previous error when I do `login_rfid_page.login_RFID(None)` – Nuuuuuub Jan 24 '20 at 08:11
  • did you use `self.id` in line `self.id, self.text = self.reader.read()` ? – furas Jan 24 '20 at 08:25
  • It's working now the id value is not `None`, now it's a string and the right value. But I need to scan an RFID first in order for my GUI to pop out. I don't know why... I need to open my GUI first, before starting to scan the RFID. – Nuuuuuub Jan 24 '20 at 08:32
  • are there any alternatives to fix this? I mean, it's the reader that's preventing the GUI to pop out, but I can't do anything with it... – Nuuuuuub Jan 24 '20 at 08:33
  • if you need RFID before GUI then read RFID before you create `Kiosk()` – furas Jan 24 '20 at 08:43
  • No sir, I need my GUI to pop up first. If it pops up, there's a login button when I clicked it, there's a window that will tell the user to scan RFID to login. But it this case, since I called the `value = login_rfid_page.login_RFID` it will also call the `self.reader = SimpleMFRC522()` which is the reason why my window GUI is not popping up. – Nuuuuuub Jan 24 '20 at 08:46
  • you can use `root.update()` to force GUI to redraw window/widgets. But you may have move `login_rfid_page.login_RFID` after buttons. ANd use `root.update()` before `login_rfid_page.login_RFID()` – furas Jan 24 '20 at 09:02
  • I put `root.update()` before `login_rfid_page.login_RFID()` the window did popped out. But I still ned to scan RFID to go to login page. – Nuuuuuub Jan 24 '20 at 09:17
  • What if I get the required details put it to `def method_details` and access it by `doing login_rfid_page.method_details` and `values = login_rfid_page.first_name` – Nuuuuuub Jan 24 '20 at 09:18
  • I did a revision of code in my question. can you look at it again? I declared the method `get_data(self)` I used `self.id` as a reference to get the required details. then I change the `login_rfid_page.get_data(None)` and `values = login_rfid_page.get_Firstn1` but I received an error. `TypeError: get_data() takes 1 positional argument but 2 were given` – Nuuuuuub Jan 24 '20 at 09:28
  • I edit it again and back to the previous error. `Exception in Tkinter callback Traceback (most recent call last): File "/usr/lib/python3.7/tkinter/__init__.py", line 1705, in __call__ return self.func(*args) File "/home/pi/Thesis/qweqweqweq.py", line 85, in login_RFID self.controller.show_frame("main_page") File "/home/pi/Thesis/qweqweqweq.py", line 45, in show_frame frame = self.frames[page_name] KeyError: 'main_page'` I hope you can help me ToT – Nuuuuuub Jan 24 '20 at 09:36
  • create new question on new page - you will have more place to show full code which we could run. – furas Jan 24 '20 at 09:46
  • Okay sir, I will post another question. Thank you so much. I will mark this as an answer since you have answered my previous questions correctly. – Nuuuuuub Jan 24 '20 at 09:48