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"
def
get_display_info():
wmi_service
=
wmi.WMI(namespace
=
'wmi'
)
displays
=
wmi_service.WmiMonitorID()
return
[display.InstanceName
for
display
in
displays]
def
list_input_sources():
input_sources
=
set
()
for
source
in
InputSource:
input_sources.add((source.value, source.name))
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))
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"
)
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()
def
switch_to_hdmi():
if
button_sources[
"HDMI"
]
is
not
None
:
switch_input_source(button_sources[
"HDMI"
])
def
switch_to_dp():
if
button_sources[
"DP"
]
is
not
None
:
switch_input_source(button_sources[
"DP"
])
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)
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_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_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"
)
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()