为什么函数 运行 没有?

Why is it doesn't the function run?

好的,下面的代码有多个问题:

  1. 当组合框中选择的键被按住时,它一直打印“You Pressed It”,有没有办法避免这种情况?
  2. 当我按下设置的热键时,标签发生变化,但 process() 中的 while 循环没有,它应该执行任务的过程,但我简化了它以打印这个问题。
run = False

def press():
    global run
    while True:
        if keyboard.read_key(hotCombo.get()):
            print("You Pressed It")
            run = not run
            keyboard.wait(hotCombo.get())
            if run == True:
                status["text"]="Working"
            else:
                status["text"]="Not Working"
            
def process():
    while run == True:
        print("runnning")

一直在鼓捣,发现问题比较多

我最终得到了这个,但是当它打印时 run 我似乎无法阻止它

def process():
    global run
    while True:
        if keyboard.read_key(hotCombo.get()):
            print("kijanbdsjokn")
            run = not run
            keyboard.wait(hotCombo.get())
            if run == True:
                status["text"]="Working"
            else:
                status["text"]="Not Working"
            while run == True:
                print("run")
                time.sleep(1)
            

处理密钥的一种方法是将其变成 two-phase 循环:

def press():
    global run
    while True:
        while not keyboard.read_key(hotCombo.get()):
            time.sleep(0.2)
        run = True
        status["text"]="Working"
        while keyboard.read_key(hotCombo.get()):
            print("running")
            time.sleep(0.2)
        run == False
        status["text"]="Not Working"

Can I ask why I cant just integrate tkinter into a working python script using threading?

一个Python脚本通常是线性的。你按顺序做事然后退出。

tkinter 程序中,您的代码由三部分组成。

  • 设置 window 和小部件的代码。
  • 全局变量的初始化(如果将它们隐藏在 class 实例中并不重要;它们仍然是全局变量)。
  • mainloop.
  • 中时,其中大部分将被 functions/methods 作为 tkinter 的回调调用

因此,在 tkinter 程序中,您的大部分代码都是 mainloop 中的 guest。它在响应事件时以小块执行。这是一种完全不同的程序。它被称为 event-driven 基于消息的 编程很久以前它在 Web 服务器和框架中变得很酷。

那么,您可以在 tkinter 程序中集成脚本吗?是的,有可能。 基本上可以通过三种方式实现;

  • 将代码分成小块,可以通过 after 超时调用。这涉及对代码进行最多的重组。为了保持 GUI 响应,事件处理程序(如超时)不应花费太长时间; 50 毫秒似乎是一个合理的上限。
  • 运行 它在不同的线程中。我们将在下面详细介绍。
  • 运行 它在不同的进程中。 大致 类似于线程中的 运行(threading.Threadmultiprocessing.Process 的 API 在设计上几乎相同)。最大的区别是进程之间的通信必须通过例如显式地完成。 QueuePipe.

使用额外线程时,您必须考虑一些事项,尤其是在 tkinter 程序中。

1) Python版本

您需要使用 Python 3。由于超出此答案范围的原因,这在 Python 2 中效果不佳。 Python 3 中更好的线程抢占是其中很大一部分。

2) 多线程 tkinter 构建

tkinter(或者更确切地说是底层的 tcl 解释器)需要在启用线程的情况下构建。我收集到 ms-windows 的官方 python.org 构建是,但除了那个 YMMV。在某些 UNIX-like 系统上,例如 Linux 或 *BSD,packages/ports 系统为您提供了一个选择。

3) 把你的代码变成一个函数

您需要将原始脚本的核心封装在一个函数中,以便您可以在一个线程中启动它。

4) 使函数thread-friendly

您可能希望能够 中断 如果该线程花费的时间太长。所以你必须调整它以定期检查它是否应该继续。检查名为 run 的全局变量是否为 True 是一种方法。请注意 threading API 而不是 允许您终止线程。

5 多线程的正常危险

同时从两个线程修改小部件或全局变量时必须小心。

在撰写本文时,Python GIL 可以在这方面为您提供帮助。由于它确保 一次只有一个线程 正在执行 Python 字节码,因此可以在单个字节码中完成的任何更改作为副作用都是多线程安全的。

比如看modify函数中一个global的修改:

In [1]: import dis

In [2]: data = []
Out[2]: []

In [3]: def modify():
   ...:     global data
   ...:     newdata = [1,2,3]
   ...:     data = newdata
   ...:     

In [4]: dis.dis(modify)
  3           0 BUILD_LIST               0
              2 LOAD_CONST               1 ((1, 2, 3))
              4 LIST_EXTEND              1
              6 STORE_FAST               0 (newdata)

  4           8 LOAD_FAST                0 (newdata)
             10 STORE_GLOBAL             0 (data)
             12 LOAD_CONST               0 (None)
             14 RETURN_VALUE

看看新的list是如何单独建起来的,建好后才赋给global。 (这并非偶然。)

只需一条字节码指令 (STORE_GLOBAL) 即可将全局变量设置为新创建的列表。所以 data 的值在任何时候都不能含糊不清。

但是很多事情需要不止一个字节码。因此,一个线程在修改变量或小部件时有可能被另一个线程抢占。有多大的机会取决于这些情况发生的频率和时间。

IIRC,当前线程每 15 毫秒被抢占一次。 因此,保证 需要更长时间的更改会被抢占。与任何为 I/O.

丢弃 GIL 的任务一样

因此,如果您看到奇怪的事情发生,请确保使用 Lock 来规范对共享资源的访问。

如果例如小部件或变量仅从 一个线程 修改 ,并且仅从所有其他线程 读取