如果调用另一个函数,则不会加载 TkInter 帧
Posted
技术标签:
【中文标题】如果调用另一个函数,则不会加载 TkInter 帧【英文标题】:TkInter Frame doesn't load if another function is called 【发布时间】:2017-11-23 07:25:42 【问题描述】:我正在编写一个 Python 程序,它侦听 RFID 输入并且仅在提供有效令牌时运行。该程序还有一个我想使用 TkInter 构建的 GUI。
谜题的两个部分单独工作都很好,但就目前而言,我似乎可以选择一个或另一个 - 但不能同时选择两者!我可以很好地绘制我的 TkInter 窗口,但是如果我调用该函数来开始侦听 RFID 输入,那么当该位运行正常并且工作时……没有 GUI。
代码如下。到目前为止,您可以通过我在终端上的打印输出来查看我的调试工作...
#!/usr/bin/env python3 导入系统 导入 mysql 数据库 如果 sys.version_info[0] == 2: 从 Tkinter 导入 * 将 Tkinter 导入为 ttk 别的: 从 tkinter 导入 * 将 tkinter 导入为 ttk 全屏窗口类: def __init__(self): self.tk = Tk() self.frame = 框架(self.tk) self.frame.pack() ttk.Button(self.tk, text="hello world").pack() self.tk.attributes('-zoomed', True) self.state = 假 self.tk.bind("我确信这很简单,但由于我是 Python/TkInter n00b,所以它打败了我,我已经完成了谷歌搜索。感谢您提供任何帮助:)
【问题讨论】:
Fulscreen_Window.listen_rfid
调用的函数listen_rfid
定义在哪里?应该是self.listen_rfid
?
在第 38 行定义了“end_fullscreen”函数之后。
那么你的意思是self.listen_rfid
?
【参考方案1】:
Tkinter(和所有 GUI)有一个称为 mainloop 的无限循环,它使 GUI 保持活跃和响应。当您进行另一个无限循环(while True
)时,您会阻塞 Tkinter 的主循环;并且 GUI 失败。您需要将循环放在单独的线程中或使用 Tkinter 的 mainloop 来完成您的工作。由于您使用的是阻塞readline
,因此线程是最好的方法。作为猜测,将您的电话替换为:
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()
编辑:顺便说一句,你的进口很糟糕。 “ttk”是 tkinter 的子集,不是别名,别名“tk”通常用于 tkinter,通配符导入不好,应该避免。这就是您的 tkinter 导入的外观:
try:
# python 2
import Tkinter as tk
import ttk
except ImportError:
# python 3
import tkinter as tk
from tkinter import ttk
然后你使用适当的前缀:
self.tk = tk.Tk()
self.frame = tk.Frame(self.tk)
【讨论】:
非常感谢,现在效果很好。也谢谢你的解释。很有意义,很高兴知道:) 虽然它们现在都在工作,但 listen_rfid 函数似乎只在终端(从程序运行的地方)获得焦点时侦听和响应输入。如果 GUI 框架有焦点,它不会响应输入。我已将“拒绝访问”/“欢迎”消息更改为输出到框架,如果终端有焦点,它们会很好地执行此操作,但如果框架被聚焦,则不会。有什么指点吗?【参考方案2】:您应该使用after
运行listen_rfid
。问题是您编写的 listen_rfid 将永远运行,这意味着 mainloop
永远不会启动。如果你这样做:
#!/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()
它会每半秒检查一次命令行是否有输入并进行处理。
【讨论】:
readline
被阻止。它不会“检查”,它会等待(锁定程序)直到出现一些输入。
这就是为什么我首先使用select
检查输入。
啊,我错过了,对不起。
感谢您的建议 - 我已将第一个答案标记为已接受,因为它首先发布,我先尝试了它并且它有效。不过谢谢,我也会记住这种方法:)以上是关于如果调用另一个函数,则不会加载 TkInter 帧的主要内容,如果未能解决你的问题,请参考以下文章