如何在 Ubuntu 22.04 中自动关闭旧的/空闲文件窗口?

如何在 Ubuntu 22.04 中自动关闭旧的/空闲文件窗口?

我经常发现自己同时打开了多个文件窗口。然后我必须查看每个窗口才能找到我需要的内容。文件窗口是允许您浏览目录和文件的窗口。

是否有一个操作系统或 GNOME 设置、应用程序或脚本可以自动关闭旧的/空闲的文件窗口?

我可以右键单击并关闭所有文件窗口,但我的目标是自动关闭旧文件窗口,同时保留较新的文件窗口。

答案1

听了 FedKad 的评论后,我能够创建一个 Python 脚本,在 N 秒后关闭 Nautilus 窗口。现在我的桌面可以自行清理,也就是说,旧的和大量的 Nautilus 窗口都会自动关闭。

这对我适用于 Ubuntu 22.04。

下面是我的 python 脚本,以及有关如何配置 cron 每 N 秒自动运行此 python 脚本的一些注释。

#!/usr/bin/env python3
import subprocess
import os
import re
import datetime
import json

# Sources:
# https://askubuntu.com/questions/860212/close-nautilus-window-automatically-after-drive-is-removed
# https://stackoverflow.com/questions/32483997/get-current-date-time-and-compare-with-other-date
# https://stackoverflow.com/questions/25325882/python-time-difference-in-if-statements
# https://stackoverflow.com/a/72069726/13003478
# https://superuser.com/questions/190801/linux-wmctrl-cannot-open-display-when-session-initiated-via-sshscreen

# Instructions
# ##############################################################################
# 1) Adjust variable_n_allowed_life_of_window_in_seconds to your liking and save the file
# 2) Use cron to configure this script to run every M seconds

# Expected result:
# After every M seconds, any window older than variable_n_allowed_life_of_window_in_seconds
# should be closed. The exception is if the window is actively being used, e.g. the window is
# above all other windows or is displayed vertically on the side.


# Instructions to configure with cron
# ##############################################################################
# 1) Run this script 1 time and determine what the DISPLAY and XAUTHORITY variables are. Comment or remove when no longer needed.
# print(os.environ.get("DISPLAY"))
# print(os.environ.get("XAUTHORITY"))
# 2) In cron, you can configure the python script as follows:

# */1 * * * * export DISPLAY=:1 && export XAUTHORITY\=/run/user/1000/gdm/Xauthority && python3 /home/apricot/Applications/auto_close_old_nautilus_windows.py >>/home/apricot/Applications/script.log 2>>/home/apricot/Applications/script.log
# @reboot    export DISPLAY=:1 && export XAUTHORITY\=/run/user/1000/gdm/Xauthority && python3 /home/apricot/Applications/auto_close_old_nautilus_windows.py >>/home/apricot/Applications/script.log 2>>/home/apricot/Applications/script.log

# The line above is configured to run my python script every 1 minute. You can change to 5 minutes with: */5 * * * *
#    If the DISPLAY and XAUTHORITY are not properly set, you will get this error.
# Cannot open display.
# Traceback (most recent call last):
#   File "/home/usera/Applications/auto_close_old_nautilus_windows.py", line 55, in <module>
#     wlist1 = [l.strip() for l in get(["wmctrl", "-lp"]).splitlines() if nautpid in l]
# AttributeError: 'NoneType' object has no attribute 'splitlines'

# Variables
# ##############################################################################
# Any window older than variable_n_allowed_life_of_window_in_seconds will be closed
variable_n_allowed_life_of_window_in_seconds = 300 # 5 minutes (5 min. * 60 sec. = 300 sec.)

location_to_store_json = '/home/apricot/Applications/20221028_auto_close_old_nautilus_windows/'
name_of_json_file = 'nautilus_windows_recently_opened.json'

# Main Logic
# ##############################################################################
os.chdir(location_to_store_json)


def get(cmd):
    try:
        return subprocess.check_output(cmd).decode('utf-8').strip()
    except subprocess.CalledProcessError:
        pass


def decide_to_keep_this_window(unique_identifier):
    # Get the output of this: xprop -id 0x03030e9a | grep "_NET_WM_STATE(ATOM)"
    arguments_a = ['xprop', '-id', unique_identifier]
    output_a = get(arguments_a)

    # If the window is currently being looked at, e.g. the state is _NET_WM_STATE_MAXIMIZED_VERT,
    # do not close the window. Keep the window opened.
    # _NET_WM_STATE_MODAL, ATOM
    # _NET_WM_STATE_STICKY, ATOM
    # _NET_WM_STATE_MAXIMIZED_VERT, ATOM        # Keep the window
    # _NET_WM_STATE_MAXIMIZED_HORZ, ATOM        # Keep the window
    # _NET_WM_STATE_SHADED, ATOM
    # _NET_WM_STATE_SKIP_TASKBAR, ATOM
    # _NET_WM_STATE_SKIP_PAGER, ATOM
    # _NET_WM_STATE_HIDDEN, ATOM
    # _NET_WM_STATE_FULLSCREEN, ATOM           # Keep the window
    # _NET_WM_STATE_ABOVE, ATOM                # Keep the window
    # _NET_WM_STATE_BELOW, ATOM
    # _NET_WM_STATE_DEMANDS_ATTENTION, ATOM

    # Per the source below, .match only works from the start, so you need to add '^.*' to find a match
    # Source: https://stackoverflow.com/a/17680674/13003478
    pattern = '^.*(_NET_WM_STATE_MAXIMIZED_VERT|_NET_WM_STATE_MAXIMIZED_HORZ|_NET_WM_STATE_FULLSCREEN|_NET_WM_STATE_ABOVE)'
    pattern_b = '^.*_NET_WM_STATE_HIDDEN'

    keep_window = False

    if output_a is not None:
        if re.match(pattern, output_a, re.IGNORECASE):
            keep_window = True

        if re.match(pattern_b, output_a, re.IGNORECASE):
            keep_window = False

    return keep_window


nautpid = get(['pgrep', 'nautilus'])
connected1 = [item_b for item_b in get('lsblk').splitlines() if 'media' in item_b]
wlist1 = []
if nautpid is not None:
    wlist1 = [item_b.strip() for item_b in get(['wmctrl', '-lp']).splitlines() if nautpid in item_b]


# Read in the JSON if it exists
current_windows = []

if os.path.exists(name_of_json_file) is True:
    with open(name_of_json_file) as json_file:
        current_windows = json.load(json_file)

previous_list_of_unique_identifiers = []

for item_a in current_windows:
    previous_list_of_unique_identifiers.append(item_a['unique_identifier'])

# For any new nautilus windows, store the unique identifier and the creation time
for item_a in wlist1:
    date_and_time_window_detected = str(datetime.datetime.now())
    date_and_time_window_detected = datetime.datetime.strptime(date_and_time_window_detected, '%Y-%m-%d %H:%M:%S.%f')

    new_string = re.sub(r'\s+', ' ', item_a)
    entries_a = new_string.split(' ')

    # Only add new windows to the list
    if (entries_a[0] in previous_list_of_unique_identifiers) is False:
        current_windows.append({
            'unique_identifier': entries_a[0], 
            'pid': entries_a[2], 
            'creation_time': str(date_and_time_window_detected), 
            'windows_was_terminated': False
        })

# If the window is being actively used, reset its creation date and avoid appearing too old.
for i, item_a in enumerate(current_windows):
    window_is_active = decide_to_keep_this_window(item_a['unique_identifier'])

    if window_is_active is True:
        date_and_time_window_detected = str(datetime.datetime.now())
        date_and_time_window_detected = datetime.datetime.strptime(date_and_time_window_detected, '%Y-%m-%d %H:%M:%S.%f')
        current_windows[i]['creation_time'] = str(date_and_time_window_detected)

# For windows that are too old, close the window
list_of_processes_to_close = []

for i, item_a in enumerate(current_windows):
    time_when_created = item_a['creation_time']
    time_when_created_date_object = datetime.datetime.strptime(time_when_created, '%Y-%m-%d %H:%M:%S.%f')

    current_time_date_object = datetime.datetime.now()
    total = current_time_date_object - time_when_created_date_object

    if total.seconds > variable_n_allowed_life_of_window_in_seconds:
        list_of_processes_to_close.append(item_a['unique_identifier'])
        arguments_a = ['wmctrl', '-ic', item_a['unique_identifier']]
        subprocess.Popen(arguments_a)
        current_windows[i]['windows_was_terminated'] = True

# Keep only the non-terminated windows
filtered = filter(lambda item_a: item_a['windows_was_terminated'] is False, current_windows)
current_windows = list(filtered)

# Dump the information of the windows to a JSON file
with open(name_of_json_file, 'w') as convert_file:
    convert_file.write(json.dumps(current_windows))

相关内容