如果调用另一个函数,则不会加载 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("", self.toggle_fullscreen) self.tk.bind("", self.end_fullscreen) print("初始化运行") self.listen_rfid() # 将其注释掉会使 GUI 出现,取消注释意味着没有 GUI :( def toggle_fullscreen(自我,事件=无): self.state = not self.state # 只是切换布尔值 self.tk.attributes("-fullscreen", self.state) 打印(“切换”) 返回“休息” def end_fullscreen(自我,事件=无): self.state = 假 self.tk.attributes("-fullscreen", False) 返回“休息” def listen_rfid(self): print("主循环运行") dbHost = '本地主机' dbName = '蟒蛇' dbUser = '蟒蛇' dbPass = '密码' dbConnection = MySQLdb.connect(host=dbHost, user=dbUser, passwd=dbPass, db=dbName) cur = dbConnection.cursor(MySQLdb.cursors.DictCursor) 使用 open('/dev/stdin', 'r') 作为 tty: 而真: RFID_input = tty.readline().rstrip() cur.execute("SELECT * FROM access_list WHERE rfid_code = '%s'" % (RFID_input)) 如果 cur.rowcount != 1: print("访问被拒绝") 别的: user_info = cur.fetchone() print("欢迎 %s!!" % (user_info['name'])) tty.close() listen_rfid() 如果 __name__ == '__main__': w = 全屏窗口() w.tk.mainloop()

我确信这很简单,但由于我是 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 帧的主要内容,如果未能解决你的问题,请参考以下文章

Tkinter - 在类中使用Button命令从另一个类调用函数

Tkinter GIF 动画步履蹒跚并且像素化

在另一个模块中调用时,tkinter 组合框功能不起作用

函数调用过程栈帧变化详解

Erlang堆栈跟踪中包含多少条目?

如何使用文件协议在 Chrome/Webkit 中从一帧调用 JavaScript 函数