关于python写显示器输入源切换的问题
最近有几台电脑需要共用一台显示器,每次手动osd里面改。比较麻烦
于是用gpt写了一个方便快捷切换。
目前遇到一个问题,就是只能从HDMI设备切换到另外俩个,无法从另两个输入源切换回HDMI。
大佬看看是什么问题。下是代码
import sys
import customtkinter as ctk
from monitorcontrol import get_monitors
# 定义切换信号源的函数
def switch_input_source(source):
monitors = get_monitors()
for monitor in monitors:
with monitor:
monitor.set_input_source(source)
# 定义按钮点击事件
def on_click(source):
# 直接切换输入源
switch_input_source(source)
# 更新标签显示当前输入源
label.configure(text=f"当前输入源: {source}")
# 创建主窗口
class InputSwitcher(ctk.CTk):
def __init__(self):
super().__init__()
self.initUI()
self.protocol("WM_DELETE_WINDOW", self.on_closing)
def initUI(self):
self.title('输入源切换器')
self.geometry('300x250')
global label
label = ctk.CTkLabel(self, text='当前输入源: 未知')
label.pack(pady=20)
hdmi_button = ctk.CTkButton(self, text='HDMI', command=lambda: on_click(1))
hdmi_button.pack(pady=10)
dp_button = ctk.CTkButton(self, text='DisplayPort', command=lambda: on_click(2))
dp_button.pack(pady=10)
typec_button = ctk.CTkButton(self, text='Type-C', command=lambda: on_click(3))
typec_button.pack(pady=10)
def on_closing(self):
self.destroy()
sys.exit()
# 主函数
if __name__ == '__main__':
ctk.set_appearance_mode("System")
ctk.set_default_color_theme("blue")
app = InputSwitcher()
app.mainloop()
本帖最后由 ccb666 于 2024-9-26 22:11 编辑
已解决
成品:https://www.52pojie.cn/thread-1967992-1-1.html
源码:
import wmi
from monitorcontrol import get_monitors, InputSource
import customtkinter as ctk
import tkinter as tk
import json
import os
import threading
from PIL import Image, ImageTk
import pystray
from pystray import MenuItem as item
import keyboard# 用于捕获全局热键
import sys
# 配置文件路径
CONFIG_FILE = "C:/HDMIfig.json"
# 保存输入源的编号
button_sources = {"HDMI": None, "DP": None, "USB-C": None}
# 托盘图标对象(全局变量,确保只创建一次)
tray_icon = None
# 图标路径
if getattr(sys, 'frozen', False):
ICON_PATH = os.path.join(sys._MEIPASS, "hdmi", "hdmi.ico")
else:
ICON_PATH = "hdmi/hdmi.ico"
# 获取显示器设备信息 (通过 WMI)
def get_display_info():
wmi_service = wmi.WMI(namespace='wmi')
displays = wmi_service.WmiMonitorID()
return
# 列出所有可能的输入源,包括 DDC/CI 协议常用编号和厂商自定义源
def list_input_sources():
input_sources = set()
# 方式 1: 从 monitorcontrol 的 InputSource 枚举中获取输入源
for source in InputSource:
input_sources.add((source.value, source.name))
# 方式 2: 常见输入源的硬编码列表,扩展到更多厂商常见编号
common_sources = {
1: "HDMI 1",
2: "HDMI 2",
3: "DisplayPort",
4: "DVI",
5: "VGA",
6: "USB-C",
7: "Thunderbolt",
8: "Composite",
9: "Component",
10: "SCART",
11: "S-Video",
12: "Mini DisplayPort",
13: "eDP",
14: "MHL",
15: "Wireless Display",
16: "USB 3.0",
17: "HDMI 3",
18: "DisplayPort 2",
19: "Thunderbolt 3",
}
for key, value in common_sources.items():
input_sources.add((key, value))
# 方式 3: 通过 WMI 获取设备信息
display_info = get_display_info()
for index, display in enumerate(display_info):
input_sources.add((index + 100, f"Display {index + 1}: {display}"))
return input_sources
# 切换显示器输入源
def switch_input_source(source_value):
try:
monitors = get_monitors()
for monitor in monitors:
with monitor:
monitor.set_input_source(InputSource(source_value))
output_textbox.insert(tk.END, f"成功切换到 {source_value} 输入源\n")
except Exception as e:
output_textbox.insert(tk.END, f"切换失败: {e}\n")
# 保存自定义按钮源编号并更新按钮名称
def save_source(button_name, source_value):
button_sources = source_value
update_button_text(button_name)
save_config()
output_textbox.insert(tk.END, f"已保存 {button_name} 为输入源编号 {source_value}\n")
# 更新按钮文本
def update_button_text(button_name):
if button_sources is not None:
if button_name == "HDMI":
hdmi_button.configure(text=f"HDMI ({button_sources})")
elif button_name == "DP":
dp_button.configure(text=f"DP ({button_sources})")
elif button_name == "USB-C":
usbc_button.configure(text=f"USB-C ({button_sources})")
# 获取输入框中的源编号,并在点击按钮时切换
def switch_and_save_source(button_name, entry_widget):
# 获取当前输入框的值
source_value = entry_widget.get()
if source_value.isdigit():
source_value = int(source_value)
save_source(button_name, source_value)
switch_input_source(source_value)
elif button_sources is not None:
# 如果输入框没有手动值,则使用已保存的默认值
switch_input_source(button_sources)
else:
output_textbox.insert(tk.END, "请输入有效的数字编号\n")
# 保存配置到文件
def save_config():
with open(CONFIG_FILE, 'w') as f:
json.dump(button_sources, f)
# 从文件加载配置
def load_config():
global button_sources
if os.path.exists(CONFIG_FILE):
with open(CONFIG_FILE, 'r') as f:
button_sources = json.load(f)
output_textbox.insert(tk.END, "配置已加载\n")
# 确保加载后更新按钮名称
update_button_text("HDMI")
update_button_text("DP")
update_button_text("USB-C")
else:
output_textbox.insert(tk.END, "没有找到配置文件,使用默认设置\n")
# 设置默认 HDMI 和 DP 编号
button_sources["HDMI"] = 16
button_sources["DP"] = 1
button_sources["USB-C"] = None
update_button_text("HDMI")
update_button_text("DP")
update_button_text("USB-C")
# 读取当前显示器的输入源
get_current_input_source()
# 读取当前显示器的输入源
def get_current_input_source():
try:
monitors = get_monitors()
for monitor in monitors:
with monitor:
current_source = monitor.get_input_source()
output_textbox.insert(tk.END, f"当前输入源编号为: {current_source.value} ({current_source.name})\n")
return current_source.value
except Exception as e:
output_textbox.insert(tk.END, f"无法读取当前输入源: {e}\n")
return None
# 恢复窗口
def restore_window(icon, item):
global tray_icon
root.after(0, root.deiconify)
tray_icon.stop()
# 最小化到托盘功能
def minimize_to_tray():
global tray_icon
root.withdraw()# 隐藏窗口
if tray_icon is None:# 确保托盘图标只创建一次
image = Image.open(ICON_PATH)
menu = (item('打开主窗口', restore_window), item('关闭程序', quit_application))
tray_icon = pystray.Icon("Monitor Switcher", image, "Monitor Switcher", menu)
threading.Thread(target=tray_icon.run, daemon=True).start()
# 退出程序
def quit_application(icon=None, item=None):
global tray_icon
if tray_icon:
tray_icon.stop()
root.quit()
# 处理窗口最小化事件
def on_minimize(event):
minimize_to_tray()
# 切换到 HDMI 的快捷键处理
def switch_to_hdmi():
if button_sources["HDMI"] is not None:
switch_input_source(button_sources["HDMI"])
# 切换到 DP 的快捷键处理
def switch_to_dp():
if button_sources["DP"] is not None:
switch_input_source(button_sources["DP"])
# 切换到 USB-C 的快捷键处理
def switch_to_usbc():
if button_sources["USB-C"] is not None:
switch_input_source(button_sources["USB-C"])
# 注册全局热键
def register_hotkeys():
keyboard.add_hotkey('alt+1', switch_to_hdmi)
keyboard.add_hotkey('alt+2', switch_to_dp)
keyboard.add_hotkey('alt+3', switch_to_usbc)
# GUI 界面
def create_gui():
global root
# 初始化主窗口
root = ctk.CTk()
root.title("显示器输入源切换器")
root.geometry("300x250")
# 设置窗口图标
root.iconbitmap(ICON_PATH)
# 捕捉窗口最小化事件
root.protocol("WM_DELETE_WINDOW", quit_application)# 点击关闭时退出程序
root.bind("<Unmap>", lambda event: on_minimize(event) if root.state() == 'iconic' else None)# 最小化事件
# 创建按钮和对应的输入框
global hdmi_button, dp_button, usbc_button
# HDMI
hdmi_frame = ctk.CTkFrame(root)
hdmi_frame.pack(pady=2)
hdmi_button = ctk.CTkButton(hdmi_frame, text="HDMI", command=lambda: switch_and_save_source("HDMI", hdmi_entry))
hdmi_button.pack(side="left", padx=1)
hdmi_entry = ctk.CTkEntry(hdmi_frame, width=30)
hdmi_entry.pack(side="left")
# DP
dp_frame = ctk.CTkFrame(root)
dp_frame.pack(pady=2)
dp_button = ctk.CTkButton(dp_frame, text="DP", command=lambda: switch_and_save_source("DP", dp_entry))
dp_button.pack(side="left", padx=1)
dp_entry = ctk.CTkEntry(dp_frame, width=30)
dp_entry.pack(side="left")
# USB-C
usbc_frame = ctk.CTkFrame(root)
usbc_frame.pack(pady=2)
usbc_button = ctk.CTkButton(usbc_frame, text="USB-C", command=lambda: switch_and_save_source("USB-C", usbc_entry))
usbc_button.pack(side="left", padx=1)
usbc_entry = ctk.CTkEntry(usbc_frame, width=30)
usbc_entry.pack(side="left")
# 输出信息框
global output_textbox
output_textbox = ctk.CTkTextbox(root, height=200, width=400)
output_textbox.pack(pady=2)
# 列出所有可能的输入源
output_textbox.insert(tk.END, "可能的输入源:\n")
input_sources = list_input_sources()
for source in input_sources:
output_textbox.insert(tk.END, f"{source} (Code: {source})\n")
# 加载配置并更新按钮文本
load_config()
# 注册热键
register_hotkeys()
root.mainloop()
if __name__ == "__main__":
create_gui()
ddc代码不支持非hdmi的运行,一般要自定义的,很多厂家不支持,都是私有代码 本帖最后由 ccb666 于 2024-9-26 00:31 编辑
搞定,由于不同显示器的输出源编号不同,只能是列出所有可能的,然后手动输入确定好后保存到按钮上,比如我的HDMI是16,1-15都是切到DP/tpyc的
虽然麻烦点,最终还是达到了我的需求。
这个想法很好,正好也有类似需求。 大佬,有完整的码学习吗? bachelor66 发表于 2024-9-26 08:42
大佬,有完整的码学习吗?
还有一些小问题没完成 程序在这电脑上运行,显示器切换到其他电脑去了,如何切换回来不会用 插眼 后续学习一下 牛逼 牛逼 牛逼 牛逼 xmp788 发表于 2024-9-26 10:18
程序在这电脑上运行,显示器切换到其他电脑去了,如何切换回来不会用
有道理,程序在A电脑上,把显示器切给了B,那怎么切回来?B电脑也放个软件吗?这样的话,我觉得,还是不如遥控器或者显示器直接切换,不算更麻烦的吧?
页:
[1]
2