- html - 出于某种原因,IE8 对我的 Sass 文件中继承的 html5 CSS 不友好?
- JMeter 在响应断言中使用 span 标签的问题
- html - 在 :hover and :active? 上具有不同效果的 CSS 动画
- html - 相对于居中的 html 内容固定的 CSS 重复背景?
Tkinter 和 asyncio 一起工作时存在一些问题:它们都是想要无限期阻塞的事件循环,如果您尝试在同一线程上运行它们,其中一个将阻止另一个执行。这意味着,如果您想运行 tk 事件循环 (Tk.mainloop()),则所有 asyncio 任务都不会运行;如果你想运行 asyncio 事件循环,你的 GUI 将永远不会绘制到屏幕上。为了解决这个问题,我们可以通过调用 Tk.update() 作为 asyncio 任务来模拟 Tk 的事件循环(如下面的 ui_update_task() 所示)。除了一个问题之外,这对我来说效果很好:窗口管理器事件阻止异步事件循环。其中包括窗口拖动/调整大小操作。我不需要调整大小,因此我在程序中禁用了它(在下面的 MCVE 中未禁用),但用户可能需要拖动窗口,我非常希望我的应用程序在此期间继续运行.
这个问题的目的是看看是否可以在单个线程中解决这个问题。这里和其他地方有几个答案,通过在一个线程中运行 tk 的事件循环和在另一个线程中运行 asyncio 的事件循环来解决此问题,通常使用队列将数据从一个线程传递到另一个线程。我对此进行了测试,并确定由于多种原因,这对于我的问题来说是一个不受欢迎的解决方案。如果可能的话,我希望在单个线程中完成此任务。
我也尝试过overrideredirect(True)
完全删除标题栏并将其替换为仅包含标签和 X 按钮的 tk.Frame,并实现了我自己的拖动方法。这也有删除任务栏图标的不良副作用,可以补救by making an invisible root window that pretends to be your real window 。这种解决方法的兔子洞可能会更糟,但我真的不想重新实现和破解这么多基本的窗口操作。但是,如果我找不到这个问题的解决方案,这很可能就是我采取的路线。
import asyncio
import tkinter as tk
class tk_async_window(tk.Tk):
def __init__(self, loop, update_interval=1/20):
super(tk_async_window, self).__init__()
self.protocol('WM_DELETE_WINDOW', self.close)
self.geometry('400x100')
self.loop = loop
self.tasks = []
self.update_interval = update_interval
self.status = 'working'
self.status_label = tk.Label(self, text=self.status)
self.status_label.pack(padx=10, pady=10)
self.close_event = asyncio.Event()
def close(self):
self.close_event.set()
async def ui_update_task(self, interval):
while True:
self.update()
await asyncio.sleep(interval)
async def status_label_task(self):
"""
This keeps the Status label updated with an alternating number of dots so that you know the UI isn't
frozen even when it's not doing anything.
"""
dots = ''
while True:
self.status_label['text'] = 'Status: %s%s' % (self.status, dots)
await asyncio.sleep(0.5)
dots += '.'
if len(dots) >= 4:
dots = ''
def initialize(self):
coros = (
self.ui_update_task(self.update_interval),
self.status_label_task(),
# additional network-bound tasks
)
for coro in coros:
self.tasks.append(self.loop.create_task(coro))
async def main():
gui = tk_async_window(asyncio.get_event_loop())
gui.initialize()
await gui.close_event.wait()
gui.destroy()
if __name__ == '__main__':
asyncio.run(main(), debug=True)
如果运行上面的示例代码,您将看到一个带有标签的窗口,其中显示: Status: working
接下来是 0-3 个点。如果按住标题栏,您会注意到这些点将停止动画,这意味着异步事件循环被阻止。这是因为调用self.update()
被阻止在 ui_update_task()
。释放标题栏后,您应该在控制台中收到来自 asyncio 的消息: Executing <Handle <TaskWakeupMethWrapper object at 0x041F4B70>(<Future finis...events.py:396>) created at C:\Program Files (x86)\Python37-32\lib\asyncio\futures.py:288> took 1.984 seconds
秒数是您拖动窗口的时间。我想要的是某种方法来处理拖动事件而不阻塞 asyncio 或生成新线程。有什么办法可以实现这一点吗?
最佳答案
实际上,您正在 asyncio 事件循环内执行单独的 Tk 更新,并且遇到 update()
阻塞的地方。另一种选择是反转逻辑并从 Tkinter 计时器内部调用异步事件循环的单个步骤 - 即使用 Widget.after
继续调用run_once
.
以下是经过上述更改的代码:
import asyncio
import tkinter as tk
class tk_async_window(tk.Tk):
def __init__(self, loop, update_interval=1/20):
super(tk_async_window, self).__init__()
self.protocol('WM_DELETE_WINDOW', self.close)
self.geometry('400x100')
self.loop = loop
self.tasks = []
self.status = 'working'
self.status_label = tk.Label(self, text=self.status)
self.status_label.pack(padx=10, pady=10)
self.after(0, self.__update_asyncio, update_interval)
self.close_event = asyncio.Event()
def close(self):
self.close_event.set()
def __update_asyncio(self, interval):
self.loop.call_soon(self.loop.stop)
self.loop.run_forever()
if self.close_event.is_set():
self.quit()
self.after(int(interval * 1000), self.__update_asyncio, interval)
async def status_label_task(self):
"""
This keeps the Status label updated with an alternating number of dots so that you know the UI isn't
frozen even when it's not doing anything.
"""
dots = ''
while True:
self.status_label['text'] = 'Status: %s%s' % (self.status, dots)
await asyncio.sleep(0.5)
dots += '.'
if len(dots) >= 4:
dots = ''
def initialize(self):
coros = (
self.status_label_task(),
# additional network-bound tasks
)
for coro in coros:
self.tasks.append(self.loop.create_task(coro))
if __name__ == '__main__':
gui = tk_async_window(asyncio.get_event_loop())
gui.initialize()
gui.mainloop()
gui.destroy()
不幸的是,我无法在我的机器上测试它,因为阻塞 update()
的问题似乎不会出现在 Linux 上,在 Linux 上,窗口的移动是由窗口管理器组件处理的桌面而不是程序本身。
关于python - tkinter 和 asyncio,窗口拖动/调整大小阻止事件循环,单线程,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/55464512/
我刚开始学习JSP技术,遇到了瓶颈。 如何从 JSP 声明 block ? 这不起作用: ... 服务器说没有“out”。 U: 我确实知道如何使用返回字符串的方法重写代码,但是有没有办法在 ?
在一个字段中,我想设置一个具有自定义过滤器的自定义分析器-着眼于词干-因此,“闪存卡”和“闪存卡”的词根相同,因此返回的结果相同 当我运行以下查询时,我的命中率很高,但是“闪存卡”和“闪存卡”各自返回
快速提问。 我有一个通过 PInvoke 使用 native DLL 的应用程序,这个 DLL 可能会调用 PostQuitMessage()。 如何避免? (因为我的应用程序不应该关闭) 我试过 A
一些给定的 HTML 文章,例如: Content 与一些基本的 Jquery 结合使用,例如: $(".some_
我正在构建一个灯箱相册。当第一个图像加载时,CSS 转换起作用。当加载后的每个图像都没有。任何想法为什么?加载第一张之后的照片,但没有过渡。 Image.prototype.load = functi
这个问题在这里已经有了答案: Disable recent tasks button on Android 5.0 (2 个答案) 关闭 2 年前。 我知道这个问题之前在这里被问过 Android
我是 Objective-C 的新手,我只是想弄清楚我是否可以使用 block 或选择器作为 UIAlertView 的 UIAlertViewDelegate 参数 - 哪个更合适? 我已经尝试了以
我是 Linux (UNIX) 套接字下套接字编程的新手。我在 Internet 上找到了以下代码,用于为每个连接生成一个线程的 tcp 服务器。但是它不起作用。accept() 函数立即返回,不等待
recv()库函数手册页提到: It returns the number of bytes received. It normally returns any data available, up
我有一个用于其他项目的共享 ts 库。在这个库中有被同一个库的其他资源使用的资源。该库的结构分为 components/*、interfaces/*、services/* 等目录。在每个目录的根目录中
我想在同一行中一个接一个地显示我的 ListView ,但 ListView 显示每个新行中的每个项目。我怎样才能防止换行显示。以便它显示为段落 ListView.builder( shr
我有一个包含数千行的表格。 import React from "react" import { useSelector } from "react-redux"; import { useEffec
假设我通常希望收到关于代码中不完整模式的警告,但有时我知道某个函数的模式不完整,我知道这很好。 是still true GHC 的警告粒度是每个模块的,并且没有办法更改有关特定功能或定义的警告? 最佳
我的网络应用程序发送浏览器通知,我知道如何检查通知的浏览器权限,以及如果未授予权限,如何请求权限。 但是,即使用户授予我的站点发送通知的权限,她可能仍然无法收到通知,因为它们 might be dis
我有 Xcode 3.2.1,并且喜欢使用它,但是当我编辑文本中带有超链接的文件时(例如,带有引用的注释:# see http://example.com)Xcode 将文本变成可点击的超链接。尝试编
关闭。这个问题不符合Stack Overflow guidelines .它目前不接受答案。 我们不允许在 Stack Overflow 上提出有关通用计算硬件和软件的问题。您可以编辑问题,使其成为
我有一个在 MY_Controller 中运行的 acl。如果权限被拒绝,那么此刻,我只是执行 redirect('denied') - 这是一个非常基本的 Controller ,它加载一个非常基本
我一直很好奇尝试从 Chrome 切换到 Firefox Quantum,但是对于 Web 开发遇到了一个我无法轻松解决的主要障碍——它正在缓存我的本地主机文件,因此当我尝试在本地主机加载各种 emb
这真的让我很兴奋!在任何时候,我都会参与多个项目。当我退出Xcode时,下次打开Xcode时,我前一天的所有项目都会自动一一打开。 经常我最终编辑错误的文件,AHHHHHHHHHHH!我可以阻止这种行
我的Wiki上有500个左右的Spambot和大约5个实际注册用户。我已经使用nuke删除了他们的页面,但是他们一直在重新发布。我已经使用reCaptcha控制了spambot的注册。现在,我只需要一
我是一名优秀的程序员,十分优秀!