mirror of
https://gitee.com/yqsphp/MediaCast.git
synced 2026-05-20 19:55:58 +08:00
406 lines
13 KiB
Python
406 lines
13 KiB
Python
import socket
|
||
import sys
|
||
import ui.resources
|
||
|
||
from PyQt5.QtCore import Qt
|
||
from PyQt5.QtGui import QIcon
|
||
from PyQt5.QtWidgets import QWidget, QVBoxLayout, QHBoxLayout, QLabel, QGroupBox, QFrame, QPushButton, \
|
||
QListWidget, QSlider, QApplication, QComboBox
|
||
|
||
|
||
class IconManager:
|
||
"""图标管理器,处理Qt资源系统的图标加载"""
|
||
|
||
@staticmethod
|
||
def get_icon(icon_name="icon"):
|
||
"""
|
||
从Qt资源系统加载图标
|
||
|
||
支持格式优先级:
|
||
1. 根据平台自动选择格式
|
||
2. 使用资源别名
|
||
3. 使用完整路径
|
||
"""
|
||
# 根据平台选择图标格式
|
||
platform = sys.platform
|
||
if platform == "win32":
|
||
extensions = [".ico", ".png"]
|
||
elif platform == "darwin": # macOS
|
||
extensions = [".icns", ".png"]
|
||
else: # Linux和其他
|
||
extensions = [".png", ".svg"]
|
||
|
||
# 尝试不同的路径格式
|
||
icon = QIcon()
|
||
|
||
for ext in extensions:
|
||
resource_path = f":/icons/{icon_name}{ext}"
|
||
icon = QIcon(resource_path)
|
||
if not icon.isNull():
|
||
print(f"✓ 从资源加载: {resource_path}")
|
||
return icon
|
||
|
||
# 如果都没找到,创建空图标
|
||
if icon.isNull():
|
||
print("⚠ 无法从资源加载图标,使用默认图标")
|
||
# 可以使用Qt内置图标作为备选
|
||
icon = QIcon.fromTheme("application-x-executable")
|
||
|
||
return icon
|
||
|
||
class UiWindow:
|
||
"""
|
||
ui窗口设计
|
||
"""
|
||
def init_ui(self):
|
||
"""初始化UI界面"""
|
||
version = "v1.2.1"
|
||
app_name = "多媒体投屏 by yqsphp"
|
||
|
||
self.setWindowTitle(app_name)
|
||
self.setGeometry(100, 100, 700, 500)
|
||
|
||
# 1. 加载主图标
|
||
icon = IconManager.get_icon()
|
||
|
||
# 2. 设置窗口图标(标题栏)
|
||
self.setWindowIcon(icon)
|
||
|
||
# 3. 设置应用程序图标(任务栏)
|
||
QApplication.instance().setWindowIcon(icon)
|
||
|
||
# Windows专用:设置AppUserModelID(非常重要!)
|
||
if sys.platform == "win32":
|
||
try:
|
||
from ctypes import windll
|
||
# 唯一ID,格式:公司名.程序名.版本
|
||
myappid = app_name + " " + version
|
||
windll.shell32.SetCurrentProcessExplicitAppUserModelID(myappid)
|
||
except ImportError:
|
||
pass
|
||
|
||
# 创建中央部件
|
||
central_widget = QWidget()
|
||
self.setCentralWidget(central_widget)
|
||
# 主布局
|
||
main_layout = QVBoxLayout(central_widget)
|
||
main_layout.setSpacing(10)
|
||
main_layout.setContentsMargins(10, 10, 10, 10)
|
||
# 水平布局用于左右面板
|
||
horizontal_layout = QHBoxLayout()
|
||
horizontal_layout.setSpacing(10)
|
||
# 左侧控制面板
|
||
left_panel = self.create_panel()
|
||
horizontal_layout.addWidget(left_panel, 1)
|
||
|
||
# 将水平布局添加到垂直布局中
|
||
main_layout.addLayout(horizontal_layout)
|
||
# 状态栏
|
||
self.statusBar().showMessage("准备就绪")
|
||
# 添加弹性空间
|
||
main_layout.addStretch()
|
||
# 底部版权信息
|
||
copyright_label = QLabel(f"© {app_name} {version}")
|
||
copyright_label.setAlignment(Qt.AlignCenter)
|
||
copyright_label.setStyleSheet("""
|
||
color: #999;
|
||
font-size: 11px;
|
||
padding: 15px 0 5px 0;
|
||
border-top: 1px solid #eee;
|
||
margin-top: 10px;
|
||
""")
|
||
main_layout.addWidget(copyright_label)
|
||
# 设置扁平化样式
|
||
self.setStyleSheet("""
|
||
/* 主窗口 */
|
||
QMainWindow {
|
||
background-color: #f5f5f5;
|
||
font-family: 'Microsoft YaHei', 'Segoe UI', sans-serif;
|
||
}
|
||
|
||
/* 分组框 - 扁平化 */
|
||
QGroupBox {
|
||
font-size: 13px;
|
||
border: 1px solid #ddd;
|
||
border-radius: 6px;
|
||
margin-top: 8px;
|
||
padding-top: 8px;
|
||
background-color: white;
|
||
}
|
||
|
||
QGroupBox::title {
|
||
subcontrol-origin: margin;
|
||
left: 10px;
|
||
padding: 0 8px 0 8px;
|
||
color: #333;
|
||
}
|
||
|
||
/* 按钮 - 扁平化 */
|
||
QPushButton, QComboBox{
|
||
border: none;
|
||
border-radius: 4px;
|
||
padding: 13px 10px;
|
||
font-size: 15px;
|
||
color: white;
|
||
background-color: #6c757d;
|
||
}
|
||
QComboBox::drop-down {
|
||
border: none;
|
||
}
|
||
QComboBox QAbstractItemView {
|
||
outline: none;
|
||
background-color:white;
|
||
selection-background-color: transparent;
|
||
}
|
||
|
||
/* 特殊按钮样式 */
|
||
#browseBtn, #speedBtn{
|
||
background:white;
|
||
border: 1px solid #ddd;
|
||
color:blank;
|
||
}
|
||
#speedBtn{
|
||
font-size: 18px;
|
||
padding: 10px 10px;
|
||
}
|
||
#refreshBtn {
|
||
background-color: #28a745;
|
||
color: white;
|
||
border: none;
|
||
}
|
||
|
||
#startCastBtn {
|
||
background-color: #007bff;
|
||
color: white;
|
||
border: none;
|
||
}
|
||
|
||
#pauseCastBtn {
|
||
background-color: #ffc107;
|
||
color: white;
|
||
border: none;
|
||
}
|
||
|
||
#stopCastBtn {
|
||
background-color: #dc3545;
|
||
color: white;
|
||
border: none;
|
||
}
|
||
#startCastBtn:disabled,
|
||
#pauseCastBtn:disabled,
|
||
#stopCastBtn:disabled{
|
||
color: #999 !important;
|
||
background-color: #f5f5f5 !important;
|
||
}
|
||
|
||
|
||
/*音量图标*/
|
||
#muteBtn{
|
||
background-color:none;
|
||
}
|
||
/* 列表控件 */
|
||
QListWidget {
|
||
border: 1px solid #ddd;
|
||
border-radius: 4px;
|
||
background-color: white;
|
||
padding: 4px;
|
||
}
|
||
|
||
QListWidget::item {
|
||
padding: 6px 8px;
|
||
border-radius: 4px;
|
||
border-bottom: 1px solid #f0f0f0;
|
||
}
|
||
|
||
QListWidget::item:selected {
|
||
color: white;
|
||
background-color: #007bff;
|
||
}
|
||
|
||
/* 文本编辑框 */
|
||
QTextEdit {
|
||
border: 1px solid #ddd;
|
||
border-radius: 4px;
|
||
background-color: white;
|
||
font-size: 11.5px;
|
||
padding: 0 4px;
|
||
}
|
||
|
||
/* 标签 */
|
||
QLabel {
|
||
color: #333;
|
||
font-size: 12px;
|
||
}
|
||
|
||
/* 输入框 */
|
||
QLineEdit {
|
||
border: 1px solid #ddd;
|
||
border-radius: 4px;
|
||
padding: 0 8px;
|
||
font-size: 12px;
|
||
}
|
||
""")
|
||
|
||
def create_panel(self):
|
||
"""创建左侧控制面板(文件和设备控制合并)"""
|
||
panel = QWidget()
|
||
layout = QVBoxLayout(panel)
|
||
layout.setSpacing(10)
|
||
|
||
# ==================== 系统信息区域 ====================
|
||
system_group = QGroupBox("系统信息")
|
||
system_layout = QVBoxLayout()
|
||
system_layout.setSpacing(8)
|
||
|
||
# 获取本机IP
|
||
try:
|
||
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
|
||
s.connect(("8.8.8.8", 80))
|
||
local_ip = s.getsockname()[0]
|
||
s.close()
|
||
except:
|
||
local_ip = "获取失败"
|
||
|
||
# 服务器状态
|
||
server_frame = QFrame()
|
||
server_layout = QHBoxLayout(server_frame)
|
||
|
||
server_layout.addWidget(QLabel("文件服务:"))
|
||
self.ip_label = QLabel(f"{local_ip}:{self.http_port}")
|
||
server_layout.addWidget(self.ip_label, 1)
|
||
|
||
system_layout.addWidget(server_frame)
|
||
|
||
# 文件服务状态
|
||
self.file_service_label = QLabel("就绪")
|
||
self.file_service_label.setStyleSheet("color: #28a745;")
|
||
server_layout.addWidget(self.file_service_label, 1)
|
||
|
||
system_group.setLayout(system_layout)
|
||
|
||
layout.addWidget(system_group)
|
||
|
||
# ==================== 文件和设备控制区域 ====================
|
||
control_group = QGroupBox("文件与设备控制")
|
||
control_layout = QVBoxLayout()
|
||
control_layout.setSpacing(12)
|
||
|
||
# 文件选择区域
|
||
file_frame = QFrame()
|
||
file_layout = QHBoxLayout(file_frame)
|
||
file_layout.setSpacing(8)
|
||
|
||
self.file_label = QLabel("未选择文件")
|
||
self.file_label.setWordWrap(True)
|
||
self.file_label.setMinimumHeight(25)
|
||
self.file_label.setStyleSheet("""
|
||
border: 1px solid #dee2e6;
|
||
border-radius: 4px;
|
||
padding-left: 5px;
|
||
""")
|
||
file_layout.addWidget(self.file_label, 1)
|
||
|
||
browse_btn = QPushButton("浏览文件")
|
||
browse_btn.setCursor(Qt.PointingHandCursor)
|
||
browse_btn.setFixedWidth(80)
|
||
browse_btn.setObjectName("browseBtn")
|
||
browse_btn.clicked.connect(self.browse_file)
|
||
file_layout.addWidget(browse_btn)
|
||
|
||
control_layout.addWidget(file_frame)
|
||
|
||
# 设备列表区域
|
||
device_frame = QFrame()
|
||
device_layout = QVBoxLayout(device_frame)
|
||
device_layout.setSpacing(8)
|
||
|
||
# 设备列表标题和刷新按钮
|
||
device_header = QHBoxLayout()
|
||
device_header.addWidget(QLabel("设备列表(单击选中):"))
|
||
device_header.addStretch()
|
||
device_layout.addLayout(device_header)
|
||
|
||
# 设备列表
|
||
self.device_list = QListWidget()
|
||
self.device_list.itemClicked.connect(self.select_device)
|
||
self.device_list.setMinimumHeight(100)
|
||
device_layout.addWidget(self.device_list)
|
||
|
||
control_layout.addWidget(device_frame)
|
||
|
||
# 播放控制按钮
|
||
playback_frame = QFrame()
|
||
playback_layout = QHBoxLayout(playback_frame)
|
||
playback_layout.setSpacing(8)
|
||
|
||
self.refresh_btn = QPushButton("刷新设备")
|
||
self.refresh_btn.setObjectName("refreshBtn")
|
||
self.refresh_btn.setCursor(Qt.PointingHandCursor)
|
||
self.refresh_btn.clicked.connect(self.refresh_devices)
|
||
|
||
# 播放速度选择框
|
||
self.speed_btn = QComboBox()
|
||
self.speed_btn.setObjectName("speedBtn")
|
||
speed = {"0.25x":0.25, "0.5x":0.5, "0.75x":0.75, "1x":1.0, "1.25x":1.25, "1.5x":1.5, "2x":2.0,"2.5x":2.50, "3x":3.0, "4x":4.0, "5x":5.0, "6x":6.0, "7x":7.0, "8x":8.0}
|
||
for key, value in speed.items():
|
||
self.speed_btn.addItem(key, value)
|
||
#默认选中1倍速度
|
||
self.speed_btn.setCurrentIndex(3)
|
||
self.speed_btn.setCursor(Qt.PointingHandCursor)
|
||
self.speed_btn.currentIndexChanged.connect(self.speed_selected)
|
||
|
||
self.start_btn = QPushButton("开始投屏")
|
||
self.start_btn.setObjectName("startCastBtn")
|
||
self.start_btn.setCursor(Qt.PointingHandCursor)
|
||
self.start_btn.setEnabled(False)
|
||
self.start_btn.clicked.connect(self.start_casting)
|
||
|
||
self.pause_btn = QPushButton("暂停投屏")
|
||
self.pause_btn.setObjectName("pauseCastBtn")
|
||
self.pause_btn.setCursor(Qt.PointingHandCursor)
|
||
self.pause_btn.clicked.connect(self.pause_casting)
|
||
self.pause_btn.setEnabled(False)
|
||
|
||
self.stop_btn = QPushButton("结束投屏")
|
||
self.stop_btn.setObjectName("stopCastBtn")
|
||
self.stop_btn.setCursor(Qt.PointingHandCursor)
|
||
self.stop_btn.clicked.connect(self.stop_casting)
|
||
self.stop_btn.setEnabled(False)
|
||
|
||
playback_layout.addWidget(self.refresh_btn)
|
||
playback_layout.addWidget(self.speed_btn)
|
||
playback_layout.addWidget(self.start_btn)
|
||
playback_layout.addWidget(self.pause_btn)
|
||
playback_layout.addWidget(self.stop_btn)
|
||
|
||
control_layout.addWidget(playback_frame)
|
||
|
||
# 音量控制
|
||
volume_frame = QFrame()
|
||
volume_layout = QHBoxLayout(volume_frame)
|
||
volume_layout.setSpacing(8)
|
||
|
||
volume_layout.addWidget(QLabel("音量:"))
|
||
self.volume_slider = QSlider(Qt.Horizontal)
|
||
self.volume_slider.setRange(0, 100)
|
||
self.volume_slider.setValue(50)
|
||
self.volume_slider.setCursor(Qt.PointingHandCursor)
|
||
self.volume_slider.valueChanged.connect(self.volume_changed)
|
||
volume_layout.addWidget(self.volume_slider, 1)
|
||
|
||
self.mute_btn = QPushButton("🔊")
|
||
self.mute_btn.setCheckable(True)
|
||
self.mute_btn.setFixedWidth(40)
|
||
self.mute_btn.setObjectName("muteBtn")
|
||
self.mute_btn.setCursor(Qt.PointingHandCursor)
|
||
self.mute_btn.clicked.connect(self.toggle_mute)
|
||
volume_layout.addWidget(self.mute_btn)
|
||
|
||
control_layout.addWidget(volume_frame)
|
||
|
||
control_group.setLayout(control_layout)
|
||
layout.addWidget(control_group, 1) # 主要区域占据更多空间
|
||
|
||
layout.addStretch()
|
||
return panel
|