#include "MainPanel.h" #include #include #include #include #include #include #include #include #include "widgets/bubbletip.h" #include "widgets/chatView/chatwindow.h" #include "widgets/functionbutton.h" #include "widgets/maskoverlay.h" #include "widgets/statswidget.h" #include "widgets/userprofilewidget.h" #include "AvPlayer2/PlayWidget.h" #include "api/roomapi.h" #include "appevent.h" #include "ui/av_recorder.h" #include "widgets/audiodeviceselectoricon.h" // #include "widgets/audiodeviceselectoricon_decoupled.h" namespace IconUtils { QIcon createSettingsIcon() { QPixmap icon(24, 24); icon.fill(Qt::transparent); QPainter painter(&icon); painter.setRenderHint(QPainter::Antialiasing); // 绘制齿轮形状 painter.setPen(Qt::white); painter.setBrush(Qt::white); painter.drawEllipse(4, 4, 16, 16); painter.setPen(Qt::NoPen); painter.setBrush(QColor(64, 158, 255)); painter.drawEllipse(8, 8, 8, 8); painter.setBrush(Qt::white); painter.drawRect(11, 2, 2, 6); painter.drawRect(11, 16, 2, 6); painter.drawRect(2, 11, 6, 2); painter.drawRect(16, 11, 6, 2); return QIcon(icon); } QIcon createSearchIcon() { QPixmap icon(24, 24); icon.fill(Qt::transparent); QPainter painter(&icon); painter.setRenderHint(QPainter::Antialiasing); // 绘制放大镜 painter.setPen(Qt::white); painter.setBrush(Qt::white); painter.drawEllipse(2, 2, 14, 14); painter.setPen(Qt::NoPen); painter.setBrush(QColor(64, 158, 255)); painter.drawEllipse(4, 4, 10, 10); painter.setBrush(Qt::white); QPolygon handle; handle << QPoint(14, 14) << QPoint(20, 20) << QPoint(18, 22) << QPoint(12, 16); painter.drawPolygon(handle); return QIcon(icon); } QIcon createUserIcon() { QPixmap icon(24, 24); icon.fill(Qt::transparent); QPainter painter(&icon); painter.setRenderHint(QPainter::Antialiasing); // 绘制用户轮廓 painter.setPen(Qt::white); painter.setBrush(Qt::white); painter.drawEllipse(4, 2, 16, 16); // 头部 painter.drawRect(6, 18, 12, 4); // 身体 return QIcon(icon); } QIcon createAudioDeviceIcon() { QPixmap icon(24, 24); icon.fill(Qt::transparent); QPainter painter(&icon); painter.setRenderHint(QPainter::Antialiasing); // 绘制音频设备图标(麦克风+扬声器组合) painter.setPen(QPen(Qt::white, 1.5)); painter.setBrush(Qt::white); // 麦克风部分 painter.drawRoundedRect(3, 2, 6, 8, 2, 2); painter.drawLine(6, 10, 6, 13); painter.drawLine(4, 13, 8, 13); // 扬声器部分 painter.drawRect(13, 8, 3, 3); painter.drawPolygon(QPolygon() << QPoint(16, 8) << QPoint(19, 6) << QPoint(19, 13) << QPoint(16, 11)); painter.drawArc(20, 7, 2, 4, 0, 180 * 16); return QIcon(icon); } } // namespace IconUtils MainPanel::MainPanel(QWidget *parent) : QWidget(parent) , userProfile(nullptr) , chatView(nullptr) { // 初始化防抖定时器 m_debounceTimer = new QTimer(this); m_debounceTimer->setSingleShot(true); m_debounceTimer->setInterval(500); // 500ms防抖延迟 connect(m_debounceTimer, &QTimer::timeout, this, &MainPanel::handleDebouncedPlay); // setupUI userProfile = new UserProfileWidget(this); webSocketClient = new WebSocketClient(this); chatView = new ChatWindow(webSocketClient); chatView->setMinimumWidth(400); statsWidget = new StatsWidget(this); QWidget *rightWidget = new QWidget; QVBoxLayout *vbox = new QVBoxLayout(rightWidget); vbox->setContentsMargins(0, 0, 0, 0); vbox->addWidget(userProfile, 0); vbox->addWidget(statsWidget, 0); vbox->addWidget(chatView, 1); splitter = new QSplitter(Qt::Horizontal, this); playerContainer = new QWidget(this); splitter->addWidget(playerContainer); splitter->addWidget(rightWidget); splitter->setStretchFactor(0, 60); splitter->setStretchFactor(1, 30); QVBoxLayout *mainLayout = new QVBoxLayout(this); mainLayout->addWidget(splitter, 1); mainLayout->setContentsMargins(0, 0, 0, 0); mainLayout->setSpacing(0); setLayout(mainLayout); // 为playerContainer设置初始布局 QVBoxLayout *playerLayout = new QVBoxLayout(playerContainer); playerLayout->setContentsMargins(0, 0, 0, 0); buttonGroup = new PopoverButtonGroup(Qt::Horizontal, playerContainer); // 添加功能按钮 FunctionButton *settingsBtn = new FunctionButton(IconUtils::createSettingsIcon(), "设置", this); Popover *settingsPopover = new Popover(this); // settingsPopover->setContentWidget(settingsContent); buttonGroup->addButton(settingsBtn, settingsPopover); FunctionButton *searchBtn = new FunctionButton(IconUtils::createSearchIcon(), "搜索", this); Popover *searchPopover = new Popover(this); // searchPopover->setContentWidget(searchContent); buttonGroup->addButton(searchBtn, searchPopover); FunctionButton *userBtn = new FunctionButton(IconUtils::createUserIcon(), "用户", this); Popover *userPopover = new Popover(this); // userPopover->setContentWidget(userContent); buttonGroup->addButton(userBtn, userPopover); // 添加音频设备选择按钮 FunctionButton *audioDeviceBtn = new FunctionButton(IconUtils::createAudioDeviceIcon(), "音频设备", this); Popover *audioDevicePopover = new Popover(this); // // 使用解耦版本的音频设备选择器 // m_audioDeviceSelectorDecoupled = new AudioDeviceSelectorIconDecoupled(this); // audioDevicePopover->setContentWidget(m_audioDeviceSelectorDecoupled); // buttonGroup->addButton(audioDeviceBtn, audioDevicePopover); // // 初始化音频设备选择器 // initAudioDeviceSelectors(); // // 连接解耦版本的音频设备选择信号 // connect(m_audioDeviceSelectorDecoupled, &AudioDeviceSelectorIconDecoupled::microphoneDeviceSelected, // this, &MainPanel::onMicrophoneDeviceSelected); // connect(m_audioDeviceSelectorDecoupled, &AudioDeviceSelectorIconDecoupled::speakerDeviceSelected, // this, &MainPanel::onSpeakerDeviceSelected); // 保持原有的AudioDeviceSelectorIcon作为备用(兼容模式) // m_audioDeviceSelector = new AudioDeviceSelectorIcon(this); // connect(m_audioDeviceSelector, &AudioDeviceSelectorIcon::microphoneDeviceSelected, // this, [this](const AudioDeviceInfo& device) { // qDebug() << "[MainPanel] 麦克风设备已选择(兼容模式):" << device.name; // // 通知AvRecorder进行设备切换 // if (AvRecorder *avRecorder = qobject_cast(playerWidget)) { // //avRecorder->switchMicrophoneDevice(device.id, device.name); // } // }); // connect(m_audioDeviceSelector, &AudioDeviceSelectorIcon::speakerDeviceSelected, // this, [this](const AudioDeviceInfo& device) { // qDebug() << "[MainPanel] 扬声器设备已选择:" << device.name; // // 通知AvRecorder进行设备切换 // if (AvRecorder *avRecorder = qobject_cast(playerWidget)) { // // avRecorder->switchSpeakerDevice(device.id, device.name); // } // }); // 添加一个动作按钮到按钮组(没有Popover) FunctionButton *actionButton = new FunctionButton(IconUtils::createSettingsIcon(), "执行操作", this); buttonGroup->addButton(actionButton, nullptr); // 将buttonGroup添加到playerContainer的布局中 playerLayout->addStretch(1); // 添加弹性空间,将buttonGroup推到底部 playerLayout->addWidget(buttonGroup, 0); // 添加buttonGroup到底部,不拉伸 buttonGroup->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); // 使用固定大小策略 // initConnect connect(AppEvent::instance(), &AppEvent::connectionStateChanged, this, [this](bool connected) { if (userProfile) { userProfile->setStatus(connected ? "在线" : "离线"); } }); connect(userProfile, &UserProfileWidget::logoutClicked, this, &MainPanel::logoutClicked); connect(webSocketClient, &WebSocketClient::statsUpdate, statsWidget, &StatsWidget::updateStats); connect(webSocketClient, &WebSocketClient::liveStatus, this, [this](const QString &msg) { // 这里可以处理 liveStatus 相关逻辑 QJsonParseError err; QJsonDocument doc = QJsonDocument::fromJson(msg.toUtf8(), &err); if (err.error != QJsonParseError::NoError || !doc.isObject()) { qDebug() << "[MainPanel] liveStatus: 解析失败" << err.errorString(); return; } QJsonObject obj = doc.object(); int liveStatus = obj.value("liveStatus").toInt(0); // 默认-1 if (liveStatus == 1) { qDebug() << "[MainPanel] liveStatus: 直播中" << chatView; if (chatView) { const QString id = webSocketClient->roomId(); // 使用防抖机制处理频繁的请求 m_pendingRoomId = id; m_debounceTimer->start(); // 重新开始计时,如果在500ms内再次收到请求,会重置定时器 } // 你的处理逻辑 } else if (liveStatus == 2) { qDebug() << "[MainPanel] liveStatus: 未开播"; // 你的处理逻辑 } else { qDebug() << "[MainPanel] liveStatus: 未知状态" << liveStatus; } }); } MainPanel::~MainPanel() { if (userProfile) { delete userProfile; userProfile = nullptr; } } void MainPanel::setRole(const QStringList &roleList) { QWidget *newPlayer = nullptr; if (roleList.contains("role.admin")) { newPlayer = new AvRecorder(this); } else { newPlayer = new PlayWidget(this); } setPlayerWidget(newPlayer); // 设置初始化信息 const QString &name = AppEvent::instance()->userName(); userProfile->setUsername(name); } void MainPanel::setPushRoomId(const QString &id) { // 推流配置 if (AvRecorder *avRecorder = qobject_cast(playerWidget)) { SettingsPage::Param param; param.liveUrl = "rtmp://106.55.186.74:1935/stream/V1"; param.liveName = id.toStdString(); avRecorder->setSettings(param); } // 重新进入房间 chatView->initWebsocket(id); if (PlayWidget *playWidget = qobject_cast(playerWidget)) { MaskOverlay::instance()->show(nullptr, 0, MaskOverlay::ActiveWindow); QFuture getRoomFuture = getRoomApi(id); QtPromise::QPromise roomListPromise = QtPromise::resolve(getRoomFuture); roomListPromise .then([this, playWidget, id](const HttpResponse &response) { qDebug() << response.code << response.data << response.message; if (response.code != 0) { BubbleTip::showTip(this, response.message, BubbleTip::Top, 3000); return; } RoomInfo roomInfo = JsonMapper::formJsonEx(response.data.toObject()); qDebug() << "roomInfo.liveStatus.has_value()" << roomInfo.liveStatus.has_value(); int status = roomInfo.liveStatus.value_or(0); if (status == 1) { qDebug() << "open" << ("rtmp://106.55.186.74:1935/stream/V1/" + id); playWidget->startToPlay("rtmp://106.55.186.74:1935/stream/V1/" + id); } }) .finally([]() { MaskOverlay::instance()->hide(); }); } } void MainPanel::setPlayerWidget(QWidget *newPlayer) { if (playerWidget) { playerWidget->setParent(nullptr); playerWidget->deleteLater(); } playerWidget = newPlayer; playerWidget->setParent(playerContainer); // 获取现有布局并清理(保留buttonGroup) QVBoxLayout *vbox = qobject_cast(playerContainer->layout()); if (vbox) { // 清理除了buttonGroup之外的所有项目 QLayoutItem *item; while (vbox->count() > 0) { item = vbox->takeAt(0); if (item->widget() && item->widget() != buttonGroup) { item->widget()->setParent(nullptr); } if (item->spacerItem()) { delete item; // 删除spacer } else if (item->widget() != buttonGroup) { delete item; } } } else { // 如果没有布局或布局类型不对,创建新的 if (playerContainer->layout()) { delete playerContainer->layout(); } vbox = new QVBoxLayout(playerContainer); vbox->setContentsMargins(0, 0, 0, 0); } // 重新添加组件:播放器在上,buttonGroup在下 vbox->addWidget(playerWidget, 1); // 添加拉伸因子,让播放器组件占据所有可用空间 vbox->addWidget(buttonGroup, 0); // 添加buttonGroup到底部,不拉伸 // 确保播放器组件能够正确拉伸 playerWidget->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); // 确保buttonGroup固定在底部 buttonGroup->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); // 使用固定大小策略 } void MainPanel::handleDebouncedPlay() { // 防抖处理后的播放逻辑 if (m_pendingRoomId.isEmpty() || !chatView) { return; } if (PlayWidget *playWidget = qobject_cast(playerWidget)) { if (!m_isStartingPlay) { m_isStartingPlay = true; qDebug() << "[MainPanel] 防抖处理后开始播放:" << m_pendingRoomId; playWidget->startToPlay("rtmp://106.55.186.74:1935/stream/V1/" + m_pendingRoomId); m_isStartingPlay = false; // 如果 startToPlay 是同步的 } } // 清空待处理的房间ID m_pendingRoomId.clear(); } void MainPanel::initAudioDeviceSelectors() { // 注册音频设备选择器工厂 // DeviceManager* deviceManager = DeviceManager::instance(); // AudioDeviceSelectorFactory* audioFactory = new AudioDeviceSelectorFactory(this); // deviceManager->registerFactory("audio", audioFactory); // // 创建麦克风和扬声器选择器 // m_microphoneSelector = deviceManager->createSelector("audio", "microphone"); // m_speakerSelector = deviceManager->createSelector("audio", "speaker"); // // 设置到解耦版本的UI组件 // if (m_audioDeviceSelectorDecoupled) { // m_audioDeviceSelectorDecoupled->setMicrophoneSelector(m_microphoneSelector); // m_audioDeviceSelectorDecoupled->setSpeakerSelector(m_speakerSelector); // } qDebug() << "[MainPanel] 音频设备选择器初始化完成"; } void MainPanel::onMicrophoneDeviceSelected(const DeviceInfo& device) { qDebug() << "[MainPanel] 麦克风设备已选择:" << device.name; // 通知AvRecorder进行设备切换 if (AvRecorder *avRecorder = qobject_cast(playerWidget)) { // 这里可以添加设备切换逻辑 // avRecorder->switchMicrophoneDevice(device.id, device.name); } } void MainPanel::onSpeakerDeviceSelected(const DeviceInfo& device) { qDebug() << "[MainPanel] 扬声器设备已选择:" << device.name; // 通知AvRecorder进行设备切换 if (AvRecorder *avRecorder = qobject_cast(playerWidget)) { // 这里可以添加设备切换逻辑 // avRecorder->switchSpeakerDevice(device.id, device.name); } }