Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

TkInter Frame doesn't load if another function is called

I'm writing a Python programme which listens for RFID input and only runs if a valid token is presented. The programme also has a GUI which I'm wanting to build using TkInter.

Both parts of the puzzle work fine in isolation, however as it stands I seem to be able to choose one or the other - but not both! I can draw my TkInter window fine, however if I call the function to start listening for the RFID input then whilst that bit runs OK and works... there's no GUI.

Code is below. You can see my debugging efforts so far with my printouts to the terminal...

    #!/usr/bin/env python3
    import sys
    import MySQLdb

    if sys.version_info[0] == 2:
        from Tkinter import *
        import Tkinter as ttk
    else:
        from tkinter import *
        import tkinter as ttk

    class Fullscreen_Window:
        def __init__(self):
            self.tk = Tk()
            self.frame = Frame(self.tk)
            self.frame.pack()
            ttk.Button(self.tk, text="hello world").pack()

            self.tk.attributes('-zoomed', True)
            self.state = False
            self.tk.bind("<F11>", self.toggle_fullscreen)
            self.tk.bind("<Escape>", self.end_fullscreen)

            print("init running")
            self.listen_rfid() # Commenting this out makes the GUI appear, uncommenting means no GUI :(

        def toggle_fullscreen(self, event=None):
            self.state = not self.state  # Just toggling the boolean
            self.tk.attributes("-fullscreen", self.state)
            print("Toggling")
            return "break"

        def end_fullscreen(self, event=None):
            self.state = False
            self.tk.attributes("-fullscreen", False)
            return "break"

        def listen_rfid(self):
            print("Main loop running")
            dbHost = 'localhost'
            dbName = 'python'
            dbUser = 'python'
            dbPass = 'PASSWORD'

            dbConnection = MySQLdb.connect(host=dbHost, user=dbUser, passwd=dbPass, db=dbName)
            cur = dbConnection.cursor(MySQLdb.cursors.DictCursor)

            with open('/dev/stdin', 'r') as tty:
                while True:
                    RFID_input = tty.readline().rstrip()
                    cur.execute("SELECT * FROM access_list WHERE rfid_code = '%s'" % (RFID_input))

                    if cur.rowcount != 1:
                        print("ACCESS DENIED")
                    else:
                        user_info = cur.fetchone()
                        print("Welcome %s!!" % (user_info['name']))


            tty.close()
            listen_rfid()

    if __name__ == '__main__':
        w = Fullscreen_Window()
        w.tk.mainloop()

I'm sure it's something really simple but as I'm a Python/TkInter n00b it's beaten me and I'm all done Googling. Any help gratefully received :)

like image 539
Paul Freeman-Powell Avatar asked Jun 20 '17 16:06

Paul Freeman-Powell


People also ask

What does Mainloop () do in tkinter?

window.mainloop() tells Python to run the Tkinter event loop. This method listens for events, such as button clicks or keypresses, and blocks any code that comes after it from running until you close the window where you called the method.

How does frame work in tkinter?

It works like a container, which is responsible for arranging the position of other widgets. It uses rectangular areas in the screen to organize the layout and to provide padding of these widgets. A frame can also be used as a foundation class to implement complex widgets.

What is Tk () in tkinter Python?

The tkinter package (“Tk interface”) is the standard Python interface to the Tcl/Tk GUI toolkit. Both Tk and tkinter are available on most Unix platforms, including macOS, as well as on Windows systems.

How do you print output from a script in GUI called in another tkinter script?

You can also import script and execute function from script. But first you will have to redirect sys. stdout using class with write() and then it will catch all printed text. Show activity on this post.


1 Answers

Tkinter (and all GUIs) has an infinite loop called the mainloop that keeps the GUI active and responsive. When you make another infinite loop (while True) you block Tkinter's mainloop; and the GUI fails. You need to either put your loop in a separate thread or use Tkinter's mainloop to do your work. Since you are using a blocking readline, the thread is the best way to go. As a guess, replace your call with this:

from threading import Thread
t = Thread(target=self.listen_rfid)
t.daemon = True # this line tells the thread to quit if the GUI (master thread) quits.
t.start()

Edit: BTW, your imports are very bad. "ttk" is a subset of tkinter, not an alias, the alias "tk" is usually used for tkinter, and wildcard imports are bad and should be avoided. This is how your tkinter imports should look:

try:
    # python 2
    import Tkinter as tk
    import ttk
except ImportError:
    # python 3
    import tkinter as tk
    from tkinter import ttk

And then you use the appropriate prefix:

self.tk = tk.Tk()
self.frame = tk.Frame(self.tk)
like image 131
Novel Avatar answered Oct 14 '22 08:10

Novel