添加配置文件和设置对话框

This commit is contained in:
2026-03-19 09:49:31 +08:00
parent d4634c3631
commit 7640923c41

View File

@@ -4,46 +4,118 @@ import os
import sys
import socket
import time
import json
from loguru import logger
from PySide6.QtWidgets import (QApplication, QMainWindow, QPushButton, QMessageBox,
QVBoxLayout, QWidget, QLabel, QGroupBox, QGridLayout,
QStatusBar, QProgressBar, QFrame)
from PySide6.QtCore import QTimer, Qt, QThread, Signal
from PySide6.QtGui import QFont, QPalette, QColor
QStatusBar, QFrame, QDialog, QLineEdit, QFormLayout,
QDialogButtonBox, QFileDialog)
from PySide6.QtCore import Qt, QThread, Signal
from PySide6.QtGui import QFont, QAction
logger.add(sys.stderr, format="{time:YYYY-MM-DD HH:mm:ss} | {level} | {message}", level="INFO")
SERVER_IP = "192.168.1.100"
MEDIAMTX_PATH = r"D:\ScreenCast\mediamtx\mediamtx.exe"
FFMPEG_PATH = r"D:\ScreenCast\ffmpeg\bin\ffmpeg.exe"
STREAM_PATH = "screen"
CONFIG_FILE = os.path.join(os.path.dirname(os.path.abspath(__file__)), "config.json")
def load_config():
if os.path.exists(CONFIG_FILE):
with open(CONFIG_FILE, 'r', encoding='utf-8') as f:
return json.load(f)
return {
"server_ip": "192.168.1.100",
"mediamtx_path": r"D:\ScreenCast\mediamtx\mediamtx.exe",
"ffmpeg_path": r"D:\ScreenCast\ffmpeg\bin\ffmpeg.exe",
"stream_path": "screen"
}
def save_config(config):
with open(CONFIG_FILE, 'w', encoding='utf-8') as f:
json.dump(config, f, ensure_ascii=False, indent=4)
logger.info(f"配置已保存到 {CONFIG_FILE}")
class ConfigDialog(QDialog):
def __init__(self, config, parent=None):
super().__init__(parent)
self.config = config
self.setWindowTitle("设置")
self.setFixedSize(450, 220)
layout = QFormLayout(self)
self.server_ip_edit = QLineEdit(config["server_ip"])
self.mediamtx_path_edit = QLineEdit(config["mediamtx_path"])
self.ffmpeg_path_edit = QLineEdit(config["ffmpeg_path"])
self.stream_path_edit = QLineEdit(config["stream_path"])
self.mediamtx_btn = QPushButton("浏览...")
self.mediamtx_btn.clicked.connect(lambda: self.browse("mediamtx"))
self.ffmpeg_btn = QPushButton("浏览...")
self.ffmpeg_btn.clicked.connect(lambda: self.browse("ffmpeg"))
path_layout1 = QGridLayout()
path_layout1.addWidget(self.mediamtx_path_edit, 0, 0)
path_layout1.addWidget(self.mediamtx_btn, 0, 1)
path_layout1.addWidget(self.ffmpeg_path_edit, 1, 0)
path_layout1.addWidget(self.ffmpeg_btn, 1, 1)
layout.addRow("服务器IP:", self.server_ip_edit)
layout.addRow("MediaMTX路径:", self.mediamtx_path_edit)
layout.addRow("FFmpeg路径:", self.ffmpeg_path_edit)
layout.addRow("推流路径:", self.stream_path_edit)
buttons = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel)
buttons.accepted.connect(self.accept)
buttons.rejected.connect(self.reject)
layout.addRow(buttons)
def browse(self, field):
if field == "mediamtx":
path = QFileDialog.getOpenFileName(self, "选择MediaMTX", "", "Executable (*.exe)")[0]
if path:
self.mediamtx_path_edit.setText(path)
elif field == "ffmpeg":
path = QFileDialog.getOpenFileName(self, "选择FFmpeg", "", "Executable (*.exe)")[0]
if path:
self.ffmpeg_path_edit.setText(path)
def get_config(self):
return {
"server_ip": self.server_ip_edit.text().strip(),
"mediamtx_path": self.mediamtx_path_edit.text().strip(),
"ffmpeg_path": self.ffmpeg_path_edit.text().strip(),
"stream_path": self.stream_path_edit.text().strip()
}
class ConnectionChecker(QThread):
status_update = Signal(dict)
def run(self):
while True:
cfg = self.config
status = {
"mediamtx": self.check_mediamtx(),
"ffmpeg": self.check_ffmpeg(),
"server": self.check_server(),
"server_port": self.check_port(SERVER_IP, 8554),
"mediamtx": self.check_mediamtx(cfg["mediamtx_path"]),
"ffmpeg": self.check_ffmpeg(cfg["ffmpeg_path"]),
"server": self.check_server(cfg["server_ip"]),
"server_port": self.check_port(cfg["server_ip"], 8554),
}
self.status_update.emit(status)
time.sleep(3)
def check_mediamtx(self):
def set_config(self, config):
self.config = config
def check_mediamtx(self, path):
for proc in psutil.process_iter(['name']):
if proc.info['name'] == 'mediamtx.exe':
return True
return False
def check_ffmpeg(self):
return os.path.exists(FFMPEG_PATH)
def check_ffmpeg(self, path):
return os.path.exists(path)
def check_server(self):
def check_server(self, ip):
try:
socket.create_connection((SERVER_IP, 8554), timeout=2)
socket.create_connection((ip, 8554), timeout=2)
return True
except:
return False
@@ -61,9 +133,12 @@ class ConnectionChecker(QThread):
class MainWindow(QMainWindow):
def __init__(self):
super().__init__()
self.config = load_config()
self.setWindowTitle("投屏源控制")
self.setFixedSize(520, 420)
self.create_menu()
self.status_bar = QStatusBar()
self.setStatusBar(self.status_bar)
@@ -106,16 +181,9 @@ class MainWindow(QMainWindow):
config_group.setLayout(config_layout)
main_layout.addWidget(config_group)
info_group = QGroupBox("流信息")
info_layout = QGridLayout()
info_layout.addWidget(QLabel("服务器IP:"), 0, 0)
info_layout.addWidget(QLabel(SERVER_IP), 0, 1)
info_layout.addWidget(QLabel("推流地址:"), 1, 0)
info_layout.addWidget(QLabel(f"rtsp://{SERVER_IP}:8554/{STREAM_PATH}"), 1, 1)
info_layout.addWidget(QLabel("访问地址:"), 2, 0)
info_layout.addWidget(QLabel(f"http://{SERVER_IP}:8889/webrtc.html?src=screen"), 2, 1)
info_group.setLayout(info_layout)
main_layout.addWidget(info_group)
self.info_group = QGroupBox("流信息")
self.update_info_group()
main_layout.addWidget(self.info_group)
self.btn_push = QPushButton("🎬 开始全屏投屏")
self.btn_push.setFixedHeight(50)
@@ -130,44 +198,73 @@ class MainWindow(QMainWindow):
main_layout.addWidget(self.log_label)
self.checker = ConnectionChecker()
self.checker.set_config(self.config)
self.checker.status_update.connect(self.update_status)
self.checker.start()
logger.info("投屏源控制界面已初始化")
def create_menu(self):
settings_action = QAction("⚙ 设置", self)
settings_action.triggered.connect(self.open_settings)
self.menuBar().addAction(settings_action)
def update_info_group(self):
cfg = self.config
if self.info_group.layout():
QWidget().setLayout(self.info_group.layout())
info_layout = QGridLayout()
info_layout.addWidget(QLabel("服务器IP:"), 0, 0)
info_layout.addWidget(QLabel(cfg["server_ip"]), 0, 1)
info_layout.addWidget(QLabel("推流地址:"), 1, 0)
info_layout.addWidget(QLabel(f"rtsp://{cfg['server_ip']}:8554/{cfg['stream_path']}"), 1, 1)
info_layout.addWidget(QLabel("访问地址:"), 2, 0)
info_layout.addWidget(QLabel(f"http://{cfg['server_ip']}:8889/webrtc.html?src={cfg['stream_path']}"), 2, 1)
self.info_group.setLayout(info_layout)
def open_settings(self):
dialog = ConfigDialog(self.config, self)
if dialog.exec():
self.config = dialog.get_config()
save_config(self.config)
self.checker.set_config(self.config)
self.update_info_group()
self.log_label.setText("日志: 配置已更新")
def update_status(self, status):
if status["mediamtx"]:
self.mediamtx_indicator.setStyleSheet("color: green; font-size: 16px;")
self.mediamtx_label.setText(f"MediaMTX: ✓ 运行中")
self.mediamtx_label.setText("MediaMTX: ✓ 运行中")
else:
self.mediamtx_indicator.setStyleSheet("color: red; font-size: 16px;")
self.mediamtx_label.setText(f"MediaMTX: ✗ 未运行")
self.mediamtx_label.setText("MediaMTX: ✗ 未运行")
if status["ffmpeg"]:
self.ffmpeg_indicator.setStyleSheet("color: green; font-size: 16px;")
self.ffmpeg_label.setText(f"FFmpeg: ✓ 已安装")
self.ffmpeg_label.setText("FFmpeg: ✓ 已安装")
else:
self.ffmpeg_indicator.setStyleSheet("color: red; font-size: 16px;")
self.ffmpeg_label.setText(f"FFmpeg: ✗ 未找到")
self.ffmpeg_label.setText("FFmpeg: ✗ 未找到")
if status["server"]:
self.server_indicator.setStyleSheet("color: green; font-size: 16px;")
self.server_label.setText(f"服务器连接: ✓ 可连接")
self.server_label.setText("服务器连接: ✓ 可连接")
else:
self.server_indicator.setStyleSheet("color: orange; font-size: 16px;")
self.server_label.setText(f"服务器连接: ✗ 连接失败")
self.server_label.setText("服务器连接: ✗ 连接失败")
if status["server_port"]:
self.port_indicator.setStyleSheet("color: green; font-size: 16px;")
self.port_label.setText(f"RTSP端口: ✓ 端口开放")
self.port_label.setText("RTSP端口: ✓ 端口开放")
else:
self.port_indicator.setStyleSheet("color: orange; font-size: 16px;")
self.port_label.setText(f"RTSP端口: ✗ 端口关闭")
self.port_label.setText("RTSP端口: ✗ 端口关闭")
def start_mediamtx(self):
if not self.checker.check_mediamtx():
cfg = self.config
if not self.checker.check_mediamtx(cfg["mediamtx_path"]):
logger.info("启动MediaMTX服务")
subprocess.Popen([MEDIAMTX_PATH], cwd=r"D:\ScreenCast\mediamtx", creationflags=subprocess.CREATE_NEW_CONSOLE)
subprocess.Popen([cfg["mediamtx_path"]], cwd=os.path.dirname(cfg["mediamtx_path"]), creationflags=subprocess.CREATE_NEW_CONSOLE)
time.sleep(3)
logger.info("MediaMTX服务已启动")
self.log_label.setText("日志: MediaMTX服务已启动")
@@ -176,25 +273,26 @@ class MainWindow(QMainWindow):
self.log_label.setText("日志: MediaMTX已在运行")
def push_full_screen(self):
cfg = self.config
logger.info("开始全屏投屏")
self.start_mediamtx()
if not os.path.exists(FFMPEG_PATH):
if not os.path.exists(cfg["ffmpeg_path"]):
logger.error("FFmpeg未安装")
QMessageBox.warning(self, "错误", "FFmpeg未安装请检查配置")
return
cmd = [
FFMPEG_PATH,
cfg["ffmpeg_path"],
"-f", "gdigrab", "-framerate", "30", "-i", "desktop",
"-c:v", "libx264", "-preset", "ultrafast", "-tune", "zerolatency",
"-f", "rtsp", f"rtsp://{SERVER_IP}:8554/{STREAM_PATH}"
"-f", "rtsp", f"rtsp://{cfg['server_ip']}:8554/{cfg['stream_path']}"
]
logger.info(f"执行FFmpeg命令: {' '.join(cmd)}")
subprocess.Popen(cmd, creationflags=subprocess.CREATE_NEW_CONSOLE)
logger.info("FFmpeg推流已启动")
self.log_label.setText("日志: FFmpeg推流已启动正在向服务器推送...")
QMessageBox.information(self, "提示", f"全屏投屏已启动!\n\n接收端可打开浏览器访问:\nhttp://{SERVER_IP}:8889/webrtc.html?src=screen")
QMessageBox.information(self, "提示", f"全屏投屏已启动!\n\n接收端可打开浏览器访问:\nhttp://{cfg['server_ip']}:8889/webrtc.html?src={cfg['stream_path']}")
def closeEvent(self, event):
self.checker.terminate()