【问题标题】:python+windows automation - windows glitch sometimespython+windows自动化——windows有时会出现故障
【发布时间】:2011-07-17 19:11:10
【问题描述】:

我正在使用 Python 自动化一些 Windows 的东西,使用 PIL 来截取屏幕截图等。问题是,有时,在某些功能的深处,我会做一个time.sleep(1),我与之交互的 GUI 应用程序会在那一秒内冻结。这很奇怪,因为在其他地方做time.sleep 不会导致冻结。我无法准确确定导致冻结的原因和没有冻结的原因。它发生在除主线程之外的线程和主线程中。

有什么想法吗?我觉得我只需要一个“放松”功能来让 Python 停止它正在做的任何事情,让目标 UI 恢复并刷新和处理输入,然后将其带回 Python。什么功能会做到这一点? (windows Sleep 函数似乎就像 python 的 time.sleep 一样。)

【问题讨论】:

  • 可能是 CPython 蹩脚的线程把事情搞砸了。
  • @schlenk 如果是这样,那么如果 CPython 的蹩脚线程能够扰乱不同进程的执行,那么它的影响力就会很大。
  • 见注释部分:msdn.microsoft.com/en-us/library/ms687032(v=vs.85).aspx 如果他与 Windows 交互,可能会出现问题,因为 time.sleep() 实现使用的是 WaitForSingleObject() 而不是 Ex 版本。

标签: python windows automation ui-automation pywin32


【解决方案1】:

我觉得我只需要“放松一下” 导致 Python 停止的函数 不管它在做什么,让 有针对性的 UI 恢复和刷新 处理输入,然后将其带回 Python。什么功能可以做到这一点?

调用sleep()(它的任何变体)将阻止您调用它的线程的执行。如果您希望在该线程中拥有响应式 UI,这只是您想要做的最后一件事。简而言之,一旦你睡着了,你就什么都做不了!

当您有无响应的用户界面时,这表明一个长时间运行的任务正在处理 UI 的线程中执行,或者 UI 线程正在阻塞。因此,要遵循逻辑,如果您的 1 秒睡眠(它们实际上是打瞌睡而不是正常的睡眠!)导致 UI 挂起,它们必须与 UI 发生在同一个线程中,或者 UI 所在的线程中等待。

我真的不知道您如何使用 PIL 以及您如何与您正在抓取其图像的应用程序进行交互,但您似乎不太可能将其注入其他进程。所以我猜这个进程正在等待你的线程。如果是这样,那么解决您的问题就没有真正的捷径。你只需要花更少的时间去做你正在做的事情。致电sleep() 只会让事情变得更糟。

致电SwitchToThread() 也无济于事。它所要做的就是允许另一个线程运行(如果有一个等待并准备好运行),但您希望您所在的线程运行。无论如何它不会有太大的不同,因为系统会知道您的线程想要运行并会在适当的时候再次安排它。

底线是您的代码在阻止该进程的 UI 进程中运行。您只需要尽快让它发生,以尽量减少干扰。

【讨论】:

  • hmm 我的代码怎么会意外地在另一个线程中运行? (我绝对不是故意这样做的).. 常用方法是什么?
  • 注入并不少见。实际上更有可能其他应用只是在等待您的应用。
  • 替代我可以使用“在另一个线程中运行其余函数”的方法.. 听起来真的很丑
  • 这怎么可能发生? (我只是调用一些 WINAPI 命令的应用程序最终会等待我吗?)
  • 我已经对我的答案进行了一些编辑。我怀疑你在注射(不知道我为什么走那条路)。无论如何,你是如何与这个其他进程交互的?你没有给我们任何细节。您在其他过程中观察到什么行为?
【解决方案2】:

您可能不希望当前线程休眠,而是让应用程序有机会处理其消息。

由于很难委派给应用程序的主消息泵,传统上大多数长时间运行的任务调用their own temporary message pump

def process_messages():
    msg = wintypes.MSG ()
    while user32.GetMessageA (byref (msg), None, 0, 0) != 0:  
        user32.TranslateMessage (byref (msg))
        user32.DispatchMessageA (byref (msg))

微软支持的版本是here。代码会将事件传递给启动线程的应用程序的事件循环,并在事件队列为空时返回。

我在 Python 和第三方库中查找了一些功能实现,但在如此快速的搜索中没有找到。

简而言之,您应该在调用自己的 process_messages() 函数时执行上述操作,就像在 Visual Basic 和其他几种仅 Windows 的语言中所做的那样,并避免调用 sleep()

附言您可以调整消息泵以仅在您等待的任务完成之前泵送消息。

【讨论】:

    【解决方案3】:

    哦,SwitchToThread 看起来很有希望。或者用 Python 术语,ctypes.windll.kernel32.SwitchToThread()

    编辑:嗯,似乎并没有完全做到这一点......

    【讨论】:

    • 它不会,它只会让另一个线程现在运行,如果有一个线程在等待,但可能没有,所以它很可能会马上回来!
    猜你喜欢
    • 2022-06-22
    • 2020-11-07
    • 2012-12-08
    • 2019-10-06
    • 1970-01-01
    • 2016-10-05
    • 1970-01-01
    • 2023-04-09
    • 2014-12-22
    相关资源
    最近更新 更多