【问题标题】:Monitering time spent on computer w/ Python and xorg-server使用 / Python 和 xorg-server 监控计算机上花费的时间
【发布时间】:2020-10-27 08:40:46
【问题描述】:

我正在尝试制作一个脚本,它可以帮助我跟踪我在计算机上花费了多长时间。这个脚本应该跟踪我何时开始、停止以及我在每个“任务”上花费了多长时间。经过一番搜索,我找到了一个名为xdotool 的终端实用程序,它将返回当前聚焦的窗口,并且在运行时它的标题如下:xdotool getwindowfocus getwindowna me。例如。当关注此窗口时,它会返回:

linux - Monitering time spent on computer w/ Python and xorg-server - Stack Overflow — Firefox Developer Edition

这正是我想要的。我的第一个想法是检测焦点窗口何时更改,然后获取发生这种情况的时间,但是我找不到任何结果,所以我使用了一个 while 循环,它每 5 秒运行一次这个命令,但这是相当hack-y 并且已经证明很麻烦,我会高度更喜欢 on-focus-change 方法,但这是我现在的代码:

#!/usr/bin/env python3

from subprocess import run
from time import time, sleep

log = []
prevwindow = ""

while True:
    currentwindow = run(['xdotool', 'getwindowfocus', 'getwindowname'], 
                                               capture_output=True, text=True).stdout
    if currentwindow != prevwindow:
        for entry in log:
            if currentwindow in entry:
                pass # Calculate time spent

        print(f"{time()}:\t{currentwindow}")
        log.append((time(), currentwindow))
    prevwindow = currentwindow
    sleep(5)

我在 Arch linux 上使用 dwm 应该有什么关系

【问题讨论】:

    标签: python linux monitoring xorg


    【解决方案1】:

    this gist。只需将您的日志记录机制放入 handle_change 函数中,它应该可以工作,正如在 Arch Linux - dwm 系统上测试的那样。

    出于存档目的,我在这里包含了代码。所有功劳归于 GitHub 上的 Stephan Sokolow (sskolow)。

    from contextlib import contextmanager
    from typing import Any, Dict, Optional, Tuple, Union  # noqa
    
    from Xlib import X
    from Xlib.display import Display
    from Xlib.error import XError
    from Xlib.xobject.drawable import Window
    from Xlib.protocol.rq import Event
    
    # Connect to the X server and get the root window
    disp = Display()
    root = disp.screen().root
    
    # Prepare the property names we use so they can be fed into X11 APIs
    NET_ACTIVE_WINDOW = disp.intern_atom('_NET_ACTIVE_WINDOW')
    NET_WM_NAME = disp.intern_atom('_NET_WM_NAME')  # UTF-8
    WM_NAME = disp.intern_atom('WM_NAME')           # Legacy encoding
    
    last_seen = {'xid': None, 'title': None}  # type: Dict[str, Any]
    
    
    @contextmanager
    def window_obj(win_id: Optional[int]) -> Window:
        """Simplify dealing with BadWindow (make it either valid or None)"""
        window_obj = None
        if win_id:
            try:
                window_obj = disp.create_resource_object('window', win_id)
            except XError:
                pass
        yield window_obj
    
    
    def get_active_window() -> Tuple[Optional[int], bool]:
        """Return a (window_obj, focus_has_changed) tuple for the active window."""
        response = root.get_full_property(NET_ACTIVE_WINDOW,
                                          X.AnyPropertyType)
        if not response:
            return None, False
        win_id = response.value[0]
    
        focus_changed = (win_id != last_seen['xid'])
        if focus_changed:
            with window_obj(last_seen['xid']) as old_win:
                if old_win:
                    old_win.change_attributes(event_mask=X.NoEventMask)
    
            last_seen['xid'] = win_id
            with window_obj(win_id) as new_win:
                if new_win:
                    new_win.change_attributes(event_mask=X.PropertyChangeMask)
    
        return win_id, focus_changed
    
    
    def _get_window_name_inner(win_obj: Window) -> str:
        """Simplify dealing with _NET_WM_NAME (UTF-8) vs. WM_NAME (legacy)"""
        for atom in (NET_WM_NAME, WM_NAME):
            try:
                window_name = win_obj.get_full_property(atom, 0)
            except UnicodeDecodeError:  # Apparently a Debian distro package bug
                title = "<could not decode characters>"
            else:
                if window_name:
                    win_name = window_name.value  # type: Union[str, bytes]
                    if isinstance(win_name, bytes):
                        # Apparently COMPOUND_TEXT is so arcane that this is how
                        # tools like xprop deal with receiving it these days
                        win_name = win_name.decode('latin1', 'replace')
                    return win_name
                else:
                    title = "<unnamed window>"
    
        return "{} (XID: {})".format(title, win_obj.id)
    
    
    def get_window_name(win_id: Optional[int]) -> Tuple[Optional[str], bool]:
        """Look up the window name for a given X11 window ID"""
        if not win_id:
            last_seen['title'] = None
            return last_seen['title'], True
    
        title_changed = False
        with window_obj(win_id) as wobj:
            if wobj:
                try:
                    win_title = _get_window_name_inner(wobj)
                except XError:
                    pass
                else:
                    title_changed = (win_title != last_seen['title'])
                    last_seen['title'] = win_title
    
        return last_seen['title'], title_changed
    
    
    def handle_xevent(event: Event):
        """Handler for X events which ignores anything but focus/title change"""
        if event.type != X.PropertyNotify:
            return
    
        changed = False
        if event.atom == NET_ACTIVE_WINDOW:
            if get_active_window()[1]:
                get_window_name(last_seen['xid'])  # Rely on the side-effects
                changed = True
        elif event.atom in (NET_WM_NAME, WM_NAME):
            changed = changed or get_window_name(last_seen['xid'])[1]
    
        if changed:
            handle_change(last_seen)
    
    
    def handle_change(new_state: dict):
        """Replace this with whatever you want to actually do"""
        print(new_state)
    
    if __name__ == '__main__':
        # Listen for _NET_ACTIVE_WINDOW changes
        root.change_attributes(event_mask=X.PropertyChangeMask)
    
        # Prime last_seen with whatever window was active when we started this
        get_window_name(get_active_window()[0])
        handle_change(last_seen)
    
        while True:  # next_event() sleeps until we get an event
            handle_xevent(disp.next_event())
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2020-11-26
      • 1970-01-01
      • 2020-06-03
      • 1970-01-01
      • 1970-01-01
      • 2014-05-09
      • 2014-09-05
      相关资源
      最近更新 更多