Qt双屏实时显示与录制需求:寻求QLabels展示及录屏存储最优方案
Hey there! Let's fix your dual-screen real-time display issue first, then add the screen recording feature you need. I'll break this down step by step.
First: Fix the "Picture-in-Picture" & Dual-Screen Display Problem
Your earlier code had two main issues causing the weird display behavior:
- Calling
on_pbtn_stop_clicked()(which clears the label) every time you capture a frame creates unnecessary flickering and potential overlap. - You weren't targeting individual screens—you only grabbed the primary screen, and didn't account for the second display's geometry.
Here's the revised code to handle dual-screen real-time display properly:
1. Update Your Header (ScreenCapture.h)
Add necessary declarations for the timer and screen handling:
#ifndef SCREENCAPTURE_H #define SCREENCAPTURE_H #include <QWidget> #include <QTimer> #include <QScreen> #include <QPixmap> QT_BEGIN_NAMESPACE namespace Ui { class ScreenCapture; } QT_END_NAMESPACE class ScreenCapture : public QWidget { Q_OBJECT public: ScreenCapture(QWidget *parent = nullptr); ~ScreenCapture(); private slots: void on_pbtn_start_clicked(); void on_pbtn_stop_clicked(); void captureAndUpdateScreens(); // New slot for dual-screen capture private: Ui::ScreenCapture *ui; QTimer *captureTimer; }; #endif // SCREENCAPTURE_H
2. Update Your Implementation (ScreenCapture.cpp)
#include "ScreenCapture.h" #include "ui_ScreenCapture.h" ScreenCapture::ScreenCapture(QWidget* parent) : QWidget(parent) , ui(new Ui::ScreenCapture) { ui->setupUi(this); // Configure labels to scale captured frames to fit their size ui->Label1->setScaledContents(true); ui->Label2->setScaledContents(true); // Initialize timer for ~30fps capture captureTimer = new QTimer(this); captureTimer->setInterval(33); // 1000ms / 30 ≈ 33ms connect(captureTimer, &QTimer::timeout, this, &ScreenCapture::captureAndUpdateScreens); } ScreenCapture::~ScreenCapture() { delete ui; delete captureTimer; } void ScreenCapture::on_pbtn_start_clicked() { captureTimer->start(); } void ScreenCapture::on_pbtn_stop_clicked() { captureTimer->stop(); // Clear both labels when stopping ui->Label1->clear(); ui->Label2->clear(); } void ScreenCapture::captureAndUpdateScreens() { QList<QScreen*> screens = QGuiApplication::screens(); if (screens.size() < 2) { // Handle case where only one screen is available return; } // Capture and update first screen (display1) QScreen* screen1 = screens[0]; const QRect screen1Geom = screen1->geometry(); QPixmap pixmap1 = screen1->grabWindow(0, screen1Geom.x(), screen1Geom.y(), screen1Geom.width(), screen1Geom.height()); ui->Label1->setPixmap(pixmap1); // Capture and update second screen (display2) QScreen* screen2 = screens[1]; const QRect screen2Geom = screen2->geometry(); QPixmap pixmap2 = screen2->grabWindow(0, screen2Geom.x(), screen2Geom.y(), screen2Geom.width(), screen2Geom.height()); ui->Label2->setPixmap(pixmap2); }
Key fixes here:
- No more clearing labels before updating—this eliminates flickering and the "picture-in-picture" effect
- We explicitly target each screen's geometry with
grabWindow()to capture only that display's content - Labels are set to scale frames to fit their size, so you don't get cropped content
Second: Add Screen Recording Functionality
For recording, Qt 6.2+ introduced QScreenCapture which makes this straightforward. If you're using Qt 5, you'll need to use a third-party library like FFmpeg or OpenCV to encode frames into a video, but I'll cover the Qt 6 approach first.
1. Update Header with Recording Classes
// Add these includes to ScreenCapture.h #include <QScreenCapture> #include <QMediaRecorder> #include <QMediaEncoderSettings> // Add these members to the private section QScreenCapture *screenCapture1; QScreenCapture *screenCapture2; QMediaRecorder *recorder1; QMediaRecorder *recorder2;
2. Initialize Recording Objects in Constructor
Add this to your ScreenCapture constructor:
// Initialize screen capture objects for each display QList<QScreen*> screens = QGuiApplication::screens(); if (screens.size() >= 2) { screenCapture1 = new QScreenCapture(screens[0], this); screenCapture2 = new QScreenCapture(screens[1], this); // Set up recorders for each screen recorder1 = new QMediaRecorder(this); recorder2 = new QMediaRecorder(this); recorder1->setSource(screenCapture1); recorder2->setSource(screenCapture2); // Configure recording settings (MP4 format, 30fps) QMediaEncoderSettings encoderSettings; encoderSettings.setCodec("video/mp4"); encoderSettings.setFrameRate(30); // Set output for screen 1 encoderSettings.setResolution(screens[0]->geometry().size()); recorder1->setEncoderSettings(encoderSettings); recorder1->setOutputLocation(QUrl::fromLocalFile("./screen1_recording.mp4")); // Set output for screen 2 encoderSettings.setResolution(screens[1]->geometry().size()); recorder2->setEncoderSettings(encoderSettings); recorder2->setOutputLocation(QUrl::fromLocalFile("./screen2_recording.mp4")); }
3. Add Start/Stop Recording Slots
Add these slots to handle recording controls:
// Add these declarations to ScreenCapture.h private slots void on_pbtn_start_record_clicked(); void on_pbtn_stop_record_clicked(); // Implement in ScreenCapture.cpp void ScreenCapture::on_pbtn_start_record_clicked() { if (screenCapture1 && recorder1) { screenCapture1->start(); recorder1->record(); } if (screenCapture2 && recorder2) { screenCapture2->start(); recorder2->record(); } } void ScreenCapture::on_pbtn_stop_record_clicked() { if (recorder1 && recorder1->isRecording()) { recorder1->stop(); screenCapture1->stop(); } if (recorder2 && recorder2->isRecording()) { recorder2->stop(); screenCapture2->stop(); } }
If you're stuck on Qt 5, you can modify the captureAndUpdateScreens() slot to save each QPixmap to a list, then use OpenCV's cv::VideoWriter to encode the list into a video file. Just convert each QPixmap to an OpenCV Mat first.
内容的提问来源于stack exchange,提问作者Mr490

