运行 Python 使用 Tkinter(有时)无头或替换 root.after()

Run Python with Tkinter (sometimes) headless OR replacement for root.after()

我有下面的工作代码。

我有一套用Python操作的机器。我在 Tkinter 中有一个 gui,但这些机器经常 运行 无头, python 代码在启动时自动启动。

我非常喜欢使用 root.after() 启动多个任务并让它们继续运行的设计模式。我的问题是,这来自 Tkinter 库,当 运行ning headless 行 "root=Tk()" 将抛出错误。

我有两个问题

  1. 我可以执行一些技巧让代码忽略没有显示的事实吗?

  1. 是否有符合 Tkinter 设计模式的库 "root.after(time_in_ms,function_to_call)"。

我确实尝试查看 Tkinter 的底层代码,看看是否有另一个库被 Tkinter 包装,但我没有解码该库中发生的事情的技能。

此代码适用于已连接的显示器:(打印 hello 11 次然后结束)

from Tkinter import *

#   def __init__(self, screenName=None, baseName=None, className='Tk', useTk=1, sync=0, use=None):

root = Tk()  # error is thrown here if starting this command in headless hardware setup

h = None
count = 0
c = None


def stop_saying_hello():
    global count
    global h
    global c
    if count > 10:
        root.after_cancel(h)
        print "counting cancelled"
    else:
        c = root.after(200, stop_saying_hello)


def hello():
    global h
    global count
    print "hello " + str(count)
    count += 1
    h = root.after(1000, hello)


h = root.after(1000, hello)  # time in ms, function
c = root.after(200, stop_saying_hello)


root.mainloop()

如果这是 运行 headless - 在来自远程计算机的 ssh 会话中,则会返回此错误消息

Traceback (most recent call last): File "tkinter_headless.py", line 5, in root = Tk() File "/usr/lib/python2.7/lib-tk/Tkinter.py", line 1813, in init self.tk = _tkinter.create(screenName, baseName, className, interactive, wantobjects, useTk, sync, use) _tkinter.TclError: no display name and no $DISPLAY environment variable

您可以使用

或使用自己的 after()mainloop()

创建自己的任务管理器

简单示例

import time

class TaskManager():

    def __init__(self):
        self.tasks = dict()
        self.index = 0
        self.running = True

    def after(self, delay, callback):
        # calcuate time using delay
        current_time = time.time()*1000
        run_time = current_time + delay

        # add to tasks
        self.index += 1
        self.tasks[self.index] = (run_time, callback)

        # return index
        return self.index

    def after_cancel(self, index):
        if index in self.tasks:
            del self.tasks[index]

    def mainloop(self):

        self.running = True

        while self.running:

            current_time = time.time()*1000

            # check all tasks
            # Python 3 needs `list(self.tasks.keys())` 
            # because `del` changes `self.tasks.keys()`
            for key in self.tasks.keys():
                if key in self.tasks:
                    run_time, callback = self.tasks[key]
                    if current_time >= run_time:
                        # execute task  
                        callback()
                        # remove from list
                        del self.tasks[key]

            # to not use all CPU
            time.sleep(0.1)

    def quit(self):
        self.running = False

    def destroy(self):
        self.running = False

# --- function ---

def stop_saying_hello():
    global count
    global h
    global c

    if count > 10:
        root.after_cancel(h)
        print "counting cancelled"
    else:
        c = root.after(200, stop_saying_hello)

def hello():
    global count
    global h

    print "hello", count
    count += 1

    h = root.after(1000, hello)

# --- main ---

count = 0
h = None
c = None

root = TaskManager()

h = root.after(1000, hello)  # time in ms, function
c = root.after(200, stop_saying_hello)

d = root.after(12000, root.destroy)

root.mainloop()