吾爱破解 - 52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 627|回复: 18
收起左侧

[已解决] 关于python写显示器输入源切换的问题

[复制链接]
XinCb 发表于 2024-9-25 20:12


最近有几台电脑需要共用一台显示器,每次手动osd里面改。比较麻烦
于是用gpt写了一个方便快捷切换。
目前遇到一个问题,就是只能从HDMI设备切换到另外俩个,无法从另两个输入源切换回HDMI。
大佬看看是什么问题。下是代码
捕获.JPG

[Python] 纯文本查看 复制代码
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()

免费评分

参与人数 3吾爱币 +3 热心值 +3 收起 理由
yjn866y + 1 + 1 热心回复!
52bulesky + 1 + 1 我很赞同!
guhantong + 1 + 1 --------

查看全部评分

发帖前要善用论坛搜索功能,那里可能会有你要找的答案或者已经有人发布过相同内容了,请勿重复发帖。

 楼主| XinCb 发表于 2024-9-26 22:10
本帖最后由 ccb666 于 2024-9-26 22:11 编辑

已解决

成品:https://www.52pojie.cn/thread-1967992-1-1.html
源码:

[Python] 纯文本查看 复制代码
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 [display.InstanceName for display in displays]

# 列出所有可能的输入源,包括 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[button_name] = 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[button_name] is not None:
        if button_name == "HDMI":
            hdmi_button.configure(text=f"HDMI ({button_sources[button_name]})")
        elif button_name == "DP":
            dp_button.configure(text=f"DP ({button_sources[button_name]})")
        elif button_name == "USB-C":
            usbc_button.configure(text=f"USB-C ({button_sources[button_name]})")

# 获取输入框中的源编号,并在点击按钮时切换
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[button_name] is not None:
        # 如果输入框没有手动值,则使用已保存的默认值
        switch_input_source(button_sources[button_name])
    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[1]} (Code: {source[0]})\n")

    # 加载配置并更新按钮文本
    load_config()

    # 注册热键
    register_hotkeys()

    root.mainloop()

if __name__ == "__main__":
    create_gui()

免费评分

参与人数 1吾爱币 +3 热心值 +1 收起 理由
Monitor + 3 + 1 感谢发布原创作品,吾爱破解论坛因你更精彩!

查看全部评分

xixicoco 发表于 2024-9-25 23:05

回帖奖励 +1 CB吾爱币

ddc代码不支持非hdmi的运行,一般要自定义的,很多厂家不支持,都是私有代码
 楼主| XinCb 发表于 2024-9-26 00:30
本帖最后由 ccb666 于 2024-9-26 00:31 编辑



搞定,由于不同显示器的输出源编号不同,只能是列出所有可能的,然后手动输入确定好后保存到按钮上,比如我的HDMI是16,1-15都是切到DP/tpyc的
虽然麻烦点,最终还是达到了我的需求。


捕获.JPG
hubindong 发表于 2024-9-26 08:02
这个想法很好,正好也有类似需求。
bachelor66 发表于 2024-9-26 08:42
大佬,有完整的码学习吗?                              
 楼主| XinCb 发表于 2024-9-26 09:43
bachelor66 发表于 2024-9-26 08:42
大佬,有完整的码学习吗?

还有一些小问题没完成
xmp788 发表于 2024-9-26 10:18
程序在这电脑上运行,显示器切换到其他电脑去了,如何切换回来  不会用  
chenzhigang 发表于 2024-9-26 10:21
插眼 后续学习一下
52bulesky 发表于 2024-9-26 10:37
牛逼 牛逼 牛逼 牛逼
llyaomo 发表于 2024-9-26 12:04
xmp788 发表于 2024-9-26 10:18
程序在这电脑上运行,显示器切换到其他电脑去了,如何切换回来  不会用

有道理,程序在A电脑上,把显示器切给了B,那怎么切回来?B电脑也放个软件吗?这样的话,我觉得,还是不如遥控器或者显示器直接切换,不算更麻烦的吧?
您需要登录后才可以回帖 登录 | 注册[Register]

本版积分规则

返回列表

RSS订阅|小黑屋|处罚记录|联系我们|吾爱破解 - LCG - LSG ( 京ICP备16042023号 | 京公网安备 11010502030087号 )

GMT+8, 2024-11-24 11:37

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表