如何监视Ubuntu中应用程序的打开,关闭,最小化等事件?

如何监视Ubuntu中应用程序的打开,关闭,最小化等事件?

我想知道是否可以在 Ubuntu 中监控应用程序的打开/关闭/最小化事件。我最初的目标是监控我打开电报查看消息的次数。

PS:我正在使用 Ubuntu 20.04LTS(X11)。

答案1

只是为了好玩,因为你在 X 上

使用 python(或其他一些语言),我们可以使用来自屏幕窗口关注窗口的创建和/或状态变化。这包括最大化和最小化窗口。

这正是以下脚本所做的。随后,它会维护一个日志文件,如果您创建、最小化或取消最小化特定 WM_CLASS(以及应用程序)的窗口,该文件将更新。您可以通过打开终端、键入xprop WM_CLASS+ Return,然后单击窗口主题(“telegramdesktop”表示电报或类似名称)来找到目标应用程序的 WMCLASS。

请注意,我让脚本在每次(日志记录)会话后重置日志文件,否则日志文件会随着时间的推移变得巨大。

日志文件里有什么?

日志文件 ( ~/.windowlog.txt) 将记录给定 WM_Class 的窗口的创建、关闭和状态更改。每次窗口取消最小化时,计数器都会加一,因此在一天结束时,您可以看到以下活动:

found window: Telegram
state changed: visual(1)
state changed: minimized
state changed: visual(2)
state changed: minimized
state changed: visual(3)
window closed:Telegram
new window:Telegram
state changed: visual(4)
state changed: minimized
state changed: visual(5)

笔记脚本是为应用程序编写的,就像您问题中的一样。为了保持更详细的记录每个窗口,处理数据需要更复杂的编码。

剧本

#!/usr/bin/env python3
import gi
gi.require_version("Gtk", "3.0")
gi.require_version("Wnck", "3.0")
from gi.repository import Gtk, Wnck
import os
import sys


class WatchWindow:

    def __init__(self, wmclass):
        self.visual = None
        self.count_visual = 0
        self.wnck_scr = Wnck.Screen.get_default()
        self.wmclass = wmclass
        self.logpath = os.environ["HOME"] + "/.windowlog.txt"
        self.run_watching()
        Gtk.main()

    def write_to_log(self, newline):
        with open(self.logpath, "a+") as logfile:
            logfile.write(newline + "\n")

    def readable_state(self, minimized):
        n = ""
        if not minimized:
            self.count_visual = self.count_visual + 1
            n = "(" + str(self.count_visual) + ")"
        return ["minimized", "visual"][[True, False].index(minimized)] + n
    
    def logstate(self, window, *args):
        old_state = self.visual
        new_state = window.is_minimized()
        # only register if minimized state really changed
        if old_state != new_state:
            self.visual = new_state
            message = "state changed: " + self.readable_state(self.visual) # log
            print(message)
            self.write_to_log(message)

    def log_new(self, screen, window):
        if  window.get_class_group_name().lower() == self.wmclass:
            message = "new window:" + window.get_name() # log new
            print(message)
            self.write_to_log(message)
            self.watch_window(window)
            self.logstate(window)

    def log_closed(self, screen, window):
        if  window.get_class_group_name().lower() == self.wmclass:
            name = window.get_name()
            self.visual = None
            print("window closed:", name) # log closed
    
    def watch_window(self, window, firstcall=False):
        if  window.get_class_group_name().lower() == self.wmclass:
            if firstcall:
                message = "found window:" + window.get_name()
                print(message) # log please
                self.write_to_log("found window: " + window.get_name())
                self.logstate(window)
            window.connect("state_changed", self.logstate)

    def run_watching(self):
        try:
            os.remove(self.logpath)
        except FileNotFoundError:
            pass
        self.wnck_scr.force_update()
        for w in self.wnck_scr.get_windows():
            self.watch_window(w, True)
        self.wnck_scr.connect("window-opened", self.log_new)
        self.wnck_scr.connect("window-closed", self.log_closed)

args = sys.argv[1:]
if not args:
    print("Insufficient arguments! We need a wm_class to watch...")
else:
    WatchWindow(args[0])

设置

  1. 将脚本复制到一个空文件中,另存windowlogger.py使其可执行

  2. 测试-在终端窗口中运行它,使用 WM_CLASS 作为参数运行它(我想telegramdesktop),所以:

    /path/to/windowlogger telegramdesktop
    
  3. 查看终端中的输出是否正常,查看日志文件内部~/.windowlog.txt是否一切正常。

  4. 如果您愿意,可以将其添加到您的启动应用程序中。

注意:

可能您需要添加一个或多个库,请检查终端输出。

日志窗口是否处于活动状态?

从评论中,我了解到您认为窗口只有在活动窗口时才“已使用”。
在这种情况下,我们可以使脚本变得更简单,因为我们仅有的需要查看active_window_changed信号。如果我们还记录时间使用情况(每次使用/总使用时间),您可以清楚地了解您花了多少时间盯着(任何)电报窗口。日志文件如下所示:

start_time:  woensdag, oktober 06 2021, 11:32:53
window activated (1)
window hidden or closed, was active: 0:00:04    total: 0:00:04
window activated (2)
window hidden or closed, was active: 0:00:06    total: 0:00:10
window activated (3)
window hidden or closed, was active: 0:00:12    total: 0:00:22
window activated (4)
window hidden or closed, was active: 0:00:07    total: 0:00:29

在这种情况下,脚本:

#!/usr/bin/env python3
import gi
gi.require_version("Gtk", "3.0")
gi.require_version("Wnck", "3.0")
from gi.repository import Gtk, Wnck
import os
import sys
import time
import datetime

class WatchWindow:

    def __init__(self, wmclass):
        self.visual = False
        self.count_visual = 1
        self.wnck_scr = Wnck.Screen.get_default()
        self.wmclass = wmclass
        self.logpath = os.environ["HOME"] + "/.windowlog.txt"
        self.total_time = 0
        self.last_time = time.time()
        self.run_watching()
        Gtk.main()

    def write_to_log(self, newline):
        with open(self.logpath, "a+") as logfile:
            logfile.write(newline + "\n")

    def get_readable_time(self, elapsed):
        return str(datetime.timedelta(seconds=elapsed))

    def log_active(self, *args):
        try:
            # active_class can be None, e.g. on startup
            active_class = self.wnck_scr.get_active_window().get_class_group_name()
        except AttributeError:
            active_class = ""
        newvisual = active_class.lower() == self.wmclass.lower()
        oldvisual = self.visual
        currtime = time.time()
        if newvisual != oldvisual:
            if newvisual:
                self.last_time = currtime
                message = "window activated (" + str(self.count_visual) + ")"
                self.count_visual = self.count_visual + 1
            else:
                winactive_time = currtime - self.last_time
                self.last_time = currtime
                self.total_time = self.total_time + winactive_time
                message = "window hidden or closed, was active: " + \
                          self.get_readable_time(round(winactive_time)) +\
                          "\t" + "total: " +\
                          self.get_readable_time(round(self.total_time))
            self.write_to_log(message)
        self.visual = newvisual

    def run_watching(self):
        try:
            os.remove(self.logpath)
        except FileNotFoundError:
            pass
        time_stamp_message = "start_time: " + time.strftime(" %A, %B %d %Y, %H:%M:%S")
        self.write_to_log(time_stamp_message)
        self.wnck_scr.force_update()
        self.wnck_scr.connect("active-window-changed", self.log_active)
        self.log_active()

args = sys.argv[1:]
if not args:
    print("Insufficient arguments! We need a wm_class to watch...")
else:
    WatchWindow(args[0])

设置相同。

相关内容