因为自己笔记本接了个外置显示器,有时需要关闭笔记本的内置的,但是我的笔记本是岔开站着扣在桌面方便散热,所以每次都要翻过来弄,感觉不方便,就叫AI写了个python小程序,方便在桌面就能看到状态而且可以开关任一个显示器.
第一步先安装gnone-randar.
# 进入临时目录
cd /tmp
# 克隆仓库
git clone https://gitlab.com/Oschowa/gnome-randr.git
# 进入目录
cd gnome-randr
# 复制脚本到系统路径
sudo cp gnome-randr.py /usr/local/bin/gnome-randr
# 赋予执行权限
sudo chmod +x /usr/local/bin/gnome-randr
然后把pythone代码保存在自己喜欢的路径,名字叫display_switcher.py.
#!/usr/bin/env python3
import subprocess
import re
import tkinter as tk
from tkinter import messagebox
INTERNAL = "eDP-1"
EXTERNAL = "DP-2"
def run_gnome_randr(*args):
cmd = ["gnome-randr"] + list(args)
print(f"[执行] {' '.join(cmd)}")
try:
subprocess.run(cmd, check=True, stdout=subprocess.DEVNULL, stderr=subprocess.PIPE)
return True
except subprocess.CalledProcessError as e:
err = e.stderr.decode() if e.stderr else "未知错误"
print(f"[错误] {err}")
messagebox.showerror("执行失败", f"命令出错:\n{err}")
return False
def get_status(display):
"""
通过解析 gnome-randr 输出来判断显示器是否启用。
逻辑:查找 "logical monitor" 块,如果显示器名称出现在该块的 "associated physical monitors" 列表中,则为 ON,否则为 OFF。
"""
try:
output = subprocess.check_output(["gnome-randr"], text=True)
except Exception as e:
print(f"[解析错误] 无法执行 gnome-randr: {e}")
return "error"
# 检查物理连接
if display not in output:
return "disconnected"
# 分割 logical monitor 块
blocks = re.split(r'(?=^logical monitor \d+:)', output, flags=re.MULTILINE)
for block in blocks:
if not block.startswith('logical monitor'):
continue
# 提取 associated physical monitors 下面的显示器列表
# 格式示例:
# associated physical monitors:
# DP-2 27C1U
lines = block.splitlines()
in_assoc = False
for line in lines:
if 'associated physical monitors:' in line:
in_assoc = True
continue
if in_assoc:
# 如果遇到空行或下一个 logical monitor,停止
if line.strip() == '' or line.startswith('logical monitor'):
break
# 检查显示器名称(去除后面的型号描述)
if line.strip().startswith(display):
return "ON"
return "OFF"
class MonitorSwitcher:
def __init__(self, root):
self.root = root
root.title("显示器切换器")
root.geometry("380x180")
root.resizable(False, False)
try:
subprocess.run(["gnome-randr"], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL, check=True)
except:
messagebox.showerror("错误", "未找到 'gnome-randr',请先安装。")
root.destroy()
return
tk.Label(root, text="点击按钮切换显示器状态\n绿色=开启 红色=关闭 灰色=未连接",
font=("Arial", 10), pady=10).pack()
frame = tk.Frame(root)
frame.pack(pady=5)
self.btn_internal = tk.Button(frame, text=f"内置 ({INTERNAL})", width=15,
command=self.toggle_internal)
self.btn_internal.pack(side=tk.LEFT, padx=10)
self.btn_external = tk.Button(frame, text=f"外接 ({EXTERNAL})", width=15,
command=self.toggle_external)
self.btn_external.pack(side=tk.LEFT, padx=10)
tk.Button(root, text="刷新状态", command=self.update_buttons, width=15).pack(pady=5)
tk.Label(root, text="※ 关闭内置后合盖不影响外接使用", fg="gray").pack()
self.update_buttons()
print("=== 初始化完成 ===")
def update_buttons(self):
s_int = get_status(INTERNAL)
s_ext = get_status(EXTERNAL)
print(f"[刷新] {INTERNAL}: {s_int}, {EXTERNAL}: {s_ext}")
# 内置按钮
if s_int == "ON":
self.btn_internal.config(bg="lightgreen", state=tk.NORMAL, text=f"内置 ({INTERNAL})")
elif s_int == "OFF":
self.btn_internal.config(bg="lightcoral", state=tk.NORMAL, text=f"内置 ({INTERNAL})")
else:
self.btn_internal.config(bg="gray", state=tk.DISABLED, text="内置 (未连接)")
# 外接按钮
if s_ext == "ON":
self.btn_external.config(bg="lightgreen", state=tk.NORMAL, text=f"外接 ({EXTERNAL})")
elif s_ext == "OFF":
self.btn_external.config(bg="lightcoral", state=tk.NORMAL, text=f"外接 ({EXTERNAL})")
elif s_ext == "disconnected":
self.btn_external.config(bg="gray", state=tk.DISABLED, text="外接 (未连接)")
else:
self.btn_external.config(bg="gray", state=tk.DISABLED)
def toggle_internal(self):
current = get_status(INTERNAL)
ext_status = get_status(EXTERNAL)
print(f"[操作] 切换内置,当前状态: {current}")
if current == "ON":
# 关闭内置,确保外接为主
if ext_status != "ON":
messagebox.showerror("错误", "外接显示器未开启,无法单独关闭内置屏幕")
return
success = run_gnome_randr(
"--output", INTERNAL, "--off",
"--output", EXTERNAL, "--auto", "--primary"
)
elif current == "OFF":
if ext_status == "ON":
# 关键修复:使用 --auto 且不重复指定显示器,顺序:先主后副
success = run_gnome_randr(
"--output", EXTERNAL, "--auto", "--primary",
"--output", INTERNAL, "--auto", "--right-of", EXTERNAL
)
else:
success = run_gnome_randr("--output", INTERNAL, "--auto", "--primary")
else:
messagebox.showerror("错误", f"{INTERNAL} 未连接")
return
if success:
self.root.after(1000, self.update_buttons) # 给 Mutter 足够的配置时间
def toggle_external(self):
current = get_status(EXTERNAL)
int_status = get_status(INTERNAL)
print(f"[操作] 切换外接,当前状态: {current}")
if current == "ON":
if int_status != "ON":
messagebox.showerror("错误", "内置显示器未开启,无法单独关闭外接屏幕")
return
success = run_gnome_randr(
"--output", EXTERNAL, "--off",
"--output", INTERNAL, "--auto", "--primary"
)
elif current == "OFF":
if int_status == "ON":
success = run_gnome_randr(
"--output", INTERNAL, "--auto", "--primary",
"--output", EXTERNAL, "--auto", "--left-of", INTERNAL
)
else:
success = run_gnome_randr("--output", EXTERNAL, "--auto", "--primary")
else:
messagebox.showerror("错误", f"{EXTERNAL} 未连接")
return
if success:
self.root.after(1000, self.update_buttons)
if __name__ == "__main__":
root = tk.Tk()
app = MonitorSwitcher(root)
root.mainloop()
# 赋予执行权限(如果还没有)
chmod +x ~/display_switcher.py
python3 ~/display_switcher.py
看是否工作.
确定没问题后,在桌面建立快捷方式.
在桌面文件夹建立
display_switcher.desktop
修改里面的内容如下.
[Desktop Entry]
Version=1.0
Type=Application
Name=显示器切换器
Comment=开关内置/外接显示器
Exec=/home/macro/display_switcher.py
Icon=preferences-desktop-display
Terminal=false
Categories=Utility;
右键桌面图标 → “允许启动”,之后双击即可使用。
样子如下