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 :)
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)
You should run listen_rfid
using after
. The problem is that listen_rfid as you have written it will run forever meaning that mainloop
never starts. If you do this:
#!/usr/bin/env python3
import sys
import select
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")
# Schedule self.listen_rfid to run after the mainloop starts
self.tk.after(0, self.listen_rfid)
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)
# readline is blocking so check that there is input
# before attempting to read it.
r, w, x = select.select([sys.stdin], [], [], 0)
if r:
# There is available input, so read a line.
RFID_input = sys.stdin.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']))
# keep running every 500 milliseconds for as long as
# the mainloop is active.
self.tk.after(500, self.listen_rfid)
if __name__ == '__main__':
w = Fullscreen_Window()
w.tk.mainloop()
it will check every half second whether there is some input on the command line and process it.
链接地址: http://www.djcxy.com/p/65024.html