防止电脑休眠模式的Python代码(Python上的Wakelock)

18

如何在Python中避免睡眠模式,而不需使用额外的应用程序来处理不同的操作系统(Ubuntu、Windows……),但大多数情况下我需要Linux解决方案。

我正在制作一个需要运行较长时间的应用程序。它会使用大约80%的CPU,因此用户只需启动该应用程序并离开键盘即可。因此,我认为我需要类似于系统API或库的东西来锁定睡眠模式。我相信这是存在的。例如,如果您在操作系统上打开任何视频播放器,则您的(PC、笔记本电脑)不会进入睡眠模式,浏览器也是如此。

另外,在Android (WakeLock) 或 Windows (SetThreadExecutionState) 中也有类似的功能。


这是什么类型的应用程序?它是使用 tkinter 制作的吗?您需要提供详细信息以便其他人帮助您。 - mishsx
@mishsx 不,它不使用tkinter,只是在终端中运行的简单应用程序。 - Max Dev
我不会期望有一个跨平台的解决方案可用。你可能需要自己实现一个特定于平台的解决方案,该解决方案使用 WakeLockSetThreadExecutionState 或其他依赖于其运行的操作系统的内容。 - zvone
我为此创建了一个名为stay-awake的软件包PyPi: https://pypi.org/project/stay-awake/Github: https://github.com/singhsidhukuldeep/stay-awake 更多细节请查看答案 - Kuldeep Singh Sidhu
10个回答

14
在Google上搜索解决方案时,发现没有可用的软件包,所以我决定将其打包并放到PyPI上:wakepy。我收到了关于跨平台支持的PR,并且目前wakepy支持Windows、Linux和macOS。

命令行界面

python -m wakepy [-p]

使用可选的-p标志将使用演示模式,该模式保持屏幕常亮且解锁。

Python API

防止休眠:

from wakepy import keep

with keep.running() as k:
    # do stuff that takes long time

防止屏幕保护程序/屏幕锁定(和休眠):

from wakepy import keep

with keep.presenting() as k:
    # do stuff that takes long time

返回的对象 k 具有 success 属性,该属性指示是否成功获取禁止锁。

13

我遇到了类似的情况,一个进程执行时间太长导致Windows进入休眠状态。为了解决这个问题,我编写了一个脚本。

下面这段简单的代码可以解决这个问题。当使用时,它会要求Windows在脚本运行时不要进入睡眠状态。(某些情况下,如电池电量不足时,Windows会忽略您的请求。)

    class WindowsInhibitor:
        '''Prevent OS sleep/hibernate in windows; code from:
        https://github.com/h3llrais3r/Deluge-PreventSuspendPlus/blob/master/preventsuspendplus/core.py
        API documentation:
        https://msdn.microsoft.com/en-us/library/windows/desktop/aa373208(v=vs.85).aspx'''
        ES_CONTINUOUS = 0x80000000
        ES_SYSTEM_REQUIRED = 0x00000001

        def __init__(self):
            pass

        def inhibit(self):
            import ctypes
            print("Preventing Windows from going to sleep")
            ctypes.windll.kernel32.SetThreadExecutionState(
                WindowsInhibitor.ES_CONTINUOUS | \
                WindowsInhibitor.ES_SYSTEM_REQUIRED)

        def uninhibit(self):
            import ctypes
            print("Allowing Windows to go to sleep")
            ctypes.windll.kernel32.SetThreadExecutionState(
                WindowsInhibitor.ES_CONTINUOUS)

只需运行脚本:

    import os

    osSleep = None
    # in Windows, prevent the OS from sleeping while we run
    if os.name == 'nt':
        osSleep = WindowsInhibitor()
        osSleep.inhibit()

    # do slow stuff

    if osSleep:
        osSleep.uninhibit()

9

创建了样例TK应用程序以保持Windows处于唤醒状态

import tkinter as tk
import ctypes
import sys

def display_on():
    global root
    print("Always On")
    ctypes.windll.kernel32.SetThreadExecutionState(0x80000002)
    root.iconify()

def display_reset():
    ctypes.windll.kernel32.SetThreadExecutionState(0x80000000)
    sys.exit(0)


root = tk.Tk()
root.geometry("200x60")
root.title("Display App")
frame = tk.Frame(root)
frame.pack()

button = tk.Button(frame,
                   text="Quit",
                   fg="red",
                   command=display_reset)
button.pack(side=tk.LEFT)
slogan = tk.Button(frame,
                   text="Always ON",
                   command=display_on)
slogan.pack(side=tk.LEFT)

root.mainloop()

1
在Windows 10上对我有用。谢谢。 - user1766438
1
这个可以运行,但如果在调用SetThreadExecutionState(0x80000002)之前显示器已经进入睡眠模式,我该如何唤醒它? - Zythyr
1
@Zythyr:它可以防止Windows进入睡眠状态,但无法唤醒屏幕。 - Devil

4

我创建了一个名为stay-awake的包。

PyPi:https://pypi.org/project/stay-awake/

Github:https://github.com/singhsidhukuldeep/stay-awake

转到 https://pypi.org/project/stay-awake/ 转到 https://pypi.org/project/stay-awake/ 转到 https://pypi.org/project/stay-awake/ 转到 https://pypi.org/project/stay-awake/

Stay-Awake是一个简单的、跨平台的Python包,可以让您的系统保持醒眼状态而不影响工作流程!

  • 这会影响工作流程吗?

不会,在您不进行任何鼠标移动时才会触发此功能!

  • 有GUI界面吗?

初衷是提供一种轻量级解决方案,因此目前只有命令行界面!

  • 它是如何工作的?

如果在60秒的时间段内没有移动您的鼠标,此脚本将自动随机移动鼠标1到4个像素,不会有任何鼠标位移!如果您正在工作,它会完全没有影响!

安装和配置

安装软件包

pip3 install stay-awake

运行

python3 -m stay-awake

您还可以设置自定义超时时间 例如:5分钟(默认为1分钟)python3 -m stay-awake 5

进入图像描述


3

根据我在互联网上发现的多种方法,我制作了以下模块。特别感谢@mishsx提供的Windows解决方案。

使用它非常简单。您可以选择使用standby_lock装饰器方法或通过StandbyLock上下文管理器:

## decorator
@standby_lock
def foo(*args, **kwargs):
    # do something lazy here...
    pass

## context manager
with StandbyLock():
    # ...or do something lazy here instead
    pass

当执行 foo 时,您的系统将保持唤醒状态。

注意:仍有一些注意事项,因为Linux可能需要sudo特权,而OS XDarwin)尚未经过测试。

from functools import wraps
import platform

class MetaStandbyLock(type):
    """
    """

    SYSTEM = platform.system()

    def __new__(cls, name: str, bases: tuple, attrs: dict) -> type:
        if not ('inhibit' in attrs and 'release' in attrs):
            raise TypeError("Missing implementations for classmethods 'inhibit(cls)' and 'release(cls)'.")
        else:
            if name == 'StandbyLock':
                cls._superclass = super().__new__(cls, name, bases, attrs)
                return cls._superclass
            if cls.SYSTEM.upper() in name.upper():
                if not hasattr(cls, '_superclass'):
                    raise ValueError("Class 'StandbyLock' must be implemented.")
                cls._superclass._subclass = super().__new__(cls, name, bases, attrs)
                return cls._superclass._subclass
            else:
                return super().__new__(cls, name, bases, attrs)

class StandbyLock(metaclass=MetaStandbyLock):
    """
    """

    _subclass = None

    @classmethod
    def inhibit(cls):
        if cls._subclass is None:
            raise OSError(f"There is no 'StandbyLock' implementation for OS '{platform.system()}'.")
        else:
            return cls._subclass.inhibit()

    @classmethod
    def release(cls):
        if cls._subclass is None:
            raise OSError(f"There is no 'StandbyLock' implementation for OS '{platform.system()}'.")
        else:
            return cls._subclass.release()

    def __enter__(self, *args, **kwargs):
        self.inhibit()
        return self

    def __exit__(self, *args, **kwargs):
        self.release()

class WindowsStandbyLock(StandbyLock):
    """
    """

    ES_CONTINUOUS      = 0x80000000
    ES_SYSTEM_REQUIRED = 0x00000001

    INHIBIT = ES_CONTINUOUS | ES_SYSTEM_REQUIRED
    RELEASE = ES_CONTINUOUS

    @classmethod
    def inhibit(cls):
        import ctypes
        ctypes.windll.kernel32.SetThreadExecutionState(cls.INHIBIT)

    @classmethod
    def release(cls):
        import ctypes
        ctypes.windll.kernel32.SetThreadExecutionState(cls.RELEASE)

class LinuxStandbyLock(metaclass=MetaStandbyLock):
    """
    """

    COMMAND = 'systemctl'
    ARGS = ['sleep.target', 'suspend.target', 'hibernate.target', 'hybrid-sleep.target']

    @classmethod
    def inhibit(cls):
        import subprocess
        subprocess.run([cls.COMMAND, 'mask', *cls.ARGS])

    @classmethod
    def release(cls):
        import subprocess
        subprocess.run([cls.COMMAND, 'unmask', *cls.ARGS])

class DarwinStandbyLock(metaclass=MetaStandbyLock):
    """
    """

    COMMAND = 'caffeinate'
    BREAK = b'\003'

    _process = None

    @classmethod
    def inhibit(cls):
        from subprocess import Popen, PIPE
        cls._process = Popen([cls.COMMAND], stdin=PIPE, stdout=PIPE)

    @classmethod
    def release(cls):
        cls._process.stdin.write(cls.BREAK)
        cls._process.stdin.flush()
        cls._process.stdin.close()
        cls._process.wait()

def standby_lock(callback):
    """ standby_lock(callable) -> callable
        This decorator guarantees that the system will not enter standby mode while 'callable' is running.
    """
    @wraps(callback)
    def new_callback(*args, **kwargs):
        with StandbyLock():
            return callback(*args, **kwargs)
    return new_callback

1
太棒了!你介意将它发布为 PyPI 包吗? - Ben Hagen
1
这非常好。你测试过Linux/OSX版本了吗?在装饰器和上下文管理器方法中有一个注意点,即如果您不小心使用两次,即使在不同的进程中,第一个退出的将会删除“锁定”。 - Niko Pasanen
谢谢你的建议!我的方法确实非常简单,我没有考虑到多进程的情况。我想我会遵循@BenHagen的要求,很快构建一个pypi包,考虑到多进程的问题。 - Pedro
嘿,我也刚发布了一个 PyPI 包 wakepy 用于 Windows 情况,因为我没有找到这样的包。我可以将其包含到 Linux/OS X 解决方案中,但我无法很好地测试它们。 - Niko Pasanen

2

这段代码用于每隔n分钟移动鼠标光标以保持系统不休眠。

首先需要安装Python的pyautogui库。

运行以下代码:

import pyautogui
import time
import sys
from datetime import datetime
pyautogui.FAILSAFE = False
numMin = None
if ((len(sys.argv)<2) or sys.argv[1].isalpha() or int(sys.argv[1])<1):
    numMin = 2 # move cursor after numMin minutes
else:
    numMin = int(sys.argv[1])
while(True):
    x=0
    while(x<numMin):
        time.sleep(60)
        x+=1
    for i in range(0,50):
        pyautogui.moveTo(i*6+50,i*4)
    pyautogui.moveTo(1,1)
    for i in range(0,3):
        pyautogui.press("shift")
    print("Movement made at {}".format(datetime.now().time()))

2
Keep.Awake的逻辑可以解决您在Ubuntu或任何运行Gnome(包括Unity)的Linux发行版上的问题,可在Wayland和X上使用。它易于使用。
我在这里发布了类似问题的解决方案:https://askubuntu.com/a/1231975/183131 因此,从逻辑上讲,请执行以下操作:
  1. 通过subprocess.Popen(...)使用DBus命令清除睡眠/暂停计数器。
  2. 使用psutil.cpu_percent()定期查询CPU使用量或在程序完成其任务后放置逻辑以重新设置睡眠配置。
您可以在此处查看代码以获取详细信息或提示,了解如何修改代码:https://launchpad.net/keep.awake 或者,您可以在正在运行CPU密集型程序的Linux Box上运行keepawake.py,它将解决您的问题! 它只是有效! 从网页中获取的用法示例: 要作为后台服务运行并将最小CPU负载设置为13%:
nohup ./keepawake.py -c 13 -r > /dev/null 2>&1 &

要作为后台服务运行,并将15分钟(900秒)设置为用户活动空闲时间,以确定用户是否处于空闲状态:

nohup ./keepawake.py -u 900 -r > /dev/null 2>&1 &

要作为后台服务运行并将最小网络流量设置为5KB(5120字节):

nohup ./keepawake.py -s 5120 -r > /dev/null 2>&1 &

要将其作为后台服务运行并在1小时后设置计划休眠/挂起(仅当确定用户活动、CPU和网络流量均为空闲时才设置此值):

nohup ./keepawake.py -w 3600 -r > /dev/null 2>&1 &

要一次性运行上述所有设置(网络、CPU、用户闲置、睡眠计划),并将日志文件路径设置为“/home/$USER/sleep/log/Keep.Awake/”,并输出详细信息:
nohup ./keepawake.py -s 5120 -c 13 -u 900 -w 3600 -l /home/$USER/sleep/log/Keep.Awake/ -v Detail -r > /dev/null 2>&1 &

0
在Windows上,不确定这是否会对任何人有所帮助,但以上方法对我无效。最终有效的方法是运行第二个进程,每30秒调用一次“shutdown /a”(中止关机)。Windows说你有大约1分钟的时间来这样做,所以才设置为30秒。
完全愚蠢的解决方案,但这是一个相当愚蠢的问题,没有操作系统提供的解决方案。
import subprocess
from time import sleep

def main():
    while(True):
        p = subprocess.Popen("shutdown /a", stdout=subprocess.PIPE, 
                             stderr=subprocess.PIPE)
        res = p.communicate()
        p.kill()
        sleep(30) # sleep 30 sec.

if __name__ == "__main__":
    main()

0

GUI应用程序以保持窗口活动


Python3

安装库

pip install pywin32

将下面的代码保存为alive.pyw文件

from ctypes import windll, wintypes, byref, c_uint, sizeof, Structure
import tkinter as tk
import ctypes
import sys
import threading
import time
import win32api
import win32con


stop_threads = True
SET_IDLE_TIME = 40 #in seconds

class LASTINPUTINFO(Structure):
    _fields_ = [
        ('cbSize', c_uint),
        ('dwTime', c_uint),
    ]

def get_idle_duration():
    lastInputInfo = LASTINPUTINFO()
    lastInputInfo.cbSize = sizeof(lastInputInfo)
    windll.user32.GetLastInputInfo(byref(lastInputInfo))
    millis = windll.kernel32.GetTickCount() - lastInputInfo.dwTime
    return millis / 1000.0


def press_key_2():
    global stop_threads
    while True:
        if not stop_threads:
            break
        idle_time = get_idle_duration() #seconds
        time.sleep(0.1)
        if idle_time < SET_IDLE_TIME:
            continue

        print("in ideal state pressing cltr")
        win32api.keybd_event(ord('x'), 0, win32con.KEYEVENTF_EXTENDEDKEY, 0)


#---------------- Monitor threads ------------------------------

t1 = threading.Thread(target=press_key_2, name='t1')
t1.daemon = True

#----------------- TK functions ----------------------

def display_on():
    global tk, t1, stop_threads
    stop_threads = True
    print("Always On")
    ctypes.windll.kernel32.SetThreadExecutionState(0x80000002)
    root.iconify()
    t1.start()
    # t2.start()

def display_reset():
    print("quit pressed")
    global stop_threads
    stop_threads = False
    ctypes.windll.kernel32.SetThreadExecutionState(0x80000000)
    sys.exit(0)



root = tk.Tk()
root.geometry("200x110")
root.title("Display App")
frame = tk.Frame(root)
frame.pack()

var = tk.StringVar()
label = tk.Label(frame, textvariable =  var)#, bd = 5, justify = tk.RIGHT, padx = 10, pady = 10)
var.set("")
button = tk.Button(frame,
                   text="Quit",
                   fg="red",
                   command=display_reset)

slogan = tk.Button(frame,
                   text="Always ON",
                   command=display_on)

label.pack(side=tk.BOTTOM,padx=0, pady=0)
slogan.pack(side=tk.LEFT,padx=15, pady=20)
button.pack(side=tk.LEFT,padx=15, pady=20)

root.mainloop()
ctypes.windll.kernel32.SetThreadExecutionState(0x80000000)

0

Stack Overflow 是用于 编程 问题的。OP 正在询问如何以编程方式实现,而不是作为最终用户。 - Chris

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接