Qt Writing Custom Control 35-GIF Recording Screen Control

I. Preface

In the ordinary writing process, it is often necessary to capture some action and effect maps into gif format, which makes the information covered more comprehensive and vivid. Sometimes, the whole operation process and operation effect can be recorded into MP4, but the file size is relatively large, and many websites are not easy to upload. Basically, GIF motion maps are supported. In general, a GIF of about 5 seconds, 800*600 resolution, can be well controlled within 500 KB, so it is more perfect to support the transmission map on major websites. ScreenGif.exe was first used. It used for a long time and felt good. A friend of mine recommended LICEcap.exe, which was smaller and had a higher compression ratio. Then I found out that there was an open source class of gif.h. It was possible to merge multiple pictures into one GIF by calling the method. It was also cross-platform, my own relatives. The free WIN+UBUNTU test was successful. The original code was given by Ni, and I improved it on this basis, so that the size of the form can be directly dragged to change the size of the recording area. Added support for Qt4 and other compilers.

II. Functions of Realization

  • 1: The width of the screen to be recorded can be set to support direct pull-and-change in the lower right corner.
  • 2: Width can be set
  • 3: Set the background color of the video control
  • 4: The number of frames recorded can be set
  • 5: The recording area can be freely dragged and selected

III. EFFECT CHARACTERISTICS

Header file code

#ifndef GIFWIDGET_H
#define GIFWIDGET_H

/**
 * GIF Author of Video Recording Control: Feiyang Qingyun (QQ: 517216493) 2019-4-3
 * 1:The width of the screen to be recorded can be set to support direct pull-and-change in the lower right corner.
 * 2:Width can be set
 * 3:Set the background color of the recording control
 * 4:The number of frames recorded can be set
 * 5:Recording area can be dragged freely
 */

#include <QDialog>
#include "gif.h"

class QLineEdit;
class QLabel;

#ifdef quc
#if (QT_VERSION < QT_VERSION_CHECK(5,7,0))
#include <QtDesigner/QDesignerExportWidget>
#else
#include <QtUiPlugin/QDesignerExportWidget>
#endif

class QDESIGNER_WIDGET_EXPORT GifWidget : public QDialog
#else
class GifWidget : public QDialog
#endif

{
    Q_OBJECT
    Q_PROPERTY(int borderWidth READ getBorderWidth WRITE setBorderWidth)
    Q_PROPERTY(QColor bgColor READ getBgColor WRITE setBgColor)

public:
    static GifWidget *Instance();
    explicit GifWidget(QWidget *parent = 0);

protected:
    bool eventFilter(QObject *watched, QEvent *event);
    void resizeEvent(QResizeEvent *);
    void paintEvent(QPaintEvent *);

private:
    static QScopedPointer<GifWidget> self;
    QWidget *widgetTop;         //title bar
    QWidget *widgetMain;        //Intermediate part
    QWidget *widgetBottom;      //Bottom column
    QLineEdit *txtFps;          //Frame Rate Input Box
    QLineEdit *txtWidth;        //Width input box
    QLineEdit *txtHeight;       //Height input box
    QPushButton *btnStart;      //Start button
    QLabel *labStatus;          //show status information

    int fps;                    //The frame number 100 is 1s
    int borderWidth;            //Border width
    QColor bgColor;             //background color

    int count;                  //Frame count
    QString fileName;           //Save the file name
    QRect rectGif;              //Screen capture area
    QTimer *timer;              //Screen capture timer

    Gif gif;                    //gif class object
    Gif::GifWriter *gifWriter;  //gif write object

public:
    int getBorderWidth()        const;
    QColor getBgColor()         const;

private slots:
    void initControl();
    void initForm();
    void saveImage();
    void record();
    void closeAll();
    void resizeForm();

public Q_SLOTS:
    void setBorderWidth(int borderWidth);
    void setBgColor(const QColor &bgColor);
};

#endif // GIFWIDGET_H


V. Core Code

#pragma execution_character_set("utf-8")

#include "gifwidget.h"
#include "qmutex.h"
#include "qlabel.h"
#include "qlineedit.h"
#include "qpushbutton.h"
#include "qlayout.h"
#include "qpainter.h"
#include "qevent.h"
#include "qstyle.h"
#include "qpixmap.h"
#include "qtimer.h"
#include "qdatetime.h"
#include "qapplication.h"
#include "qdesktopwidget.h"
#include "qdesktopservices.h"
#include "qfiledialog.h"
#include "qurl.h"
#include "qdebug.h"
#if (QT_VERSION > QT_VERSION_CHECK(5,0,0))
#include "qscreen.h"
#endif

QScopedPointer<GifWidget> GifWidget::self;
GifWidget *GifWidget::Instance()
{
    if (self.isNull()) {
        static QMutex mutex;
        QMutexLocker locker(&mutex);
        if (self.isNull()) {
            self.reset(new GifWidget);
        }
    }

    return self.data();
}

GifWidget::GifWidget(QWidget *parent) : QDialog(parent)
{
    this->initControl();
    this->initForm();
}

bool GifWidget::eventFilter(QObject *watched, QEvent *event)
{
    static QPoint mousePoint;
    static bool mousePressed = false;

    QMouseEvent *mouseEvent = static_cast<QMouseEvent *>(event);
    if (mouseEvent->type() == QEvent::MouseButtonPress) {
        if (mouseEvent->button() == Qt::LeftButton) {
            mousePressed = true;
            mousePoint = mouseEvent->globalPos() - this->pos();
            return true;
        }
    } else if (mouseEvent->type() == QEvent::MouseButtonRelease) {
        mousePressed = false;
        return true;
    } else if (mouseEvent->type() == QEvent::MouseMove) {
        if (mousePressed && (mouseEvent->buttons() && Qt::LeftButton)) {
            this->move(mouseEvent->globalPos() - mousePoint);
            return true;
        }
    }

    return QWidget::eventFilter(watched, event);
}

void GifWidget::resizeEvent(QResizeEvent *e)
{
    //Pull the lower right corner to change the size of the automatic assignment
    txtWidth->setText(QString::number(widgetMain->width()));
    txtHeight->setText(QString::number(widgetMain->height()));
    QDialog::resizeEvent(e);
}

void GifWidget::paintEvent(QPaintEvent *)
{
    int width = txtWidth->text().toInt();
    int height = txtHeight->text().toInt();
    rectGif = QRect(borderWidth, widgetTop->height(), width - (borderWidth * 2), height);

    QPainter painter(this);
    painter.setPen(Qt::NoPen);
    painter.setBrush(bgColor);
    painter.drawRoundedRect(this->rect(), 5, 5);
    painter.setCompositionMode(QPainter::CompositionMode_Clear);
    painter.fillRect(rectGif, Qt::SolidPattern);
}

int GifWidget::getBorderWidth() const
{
    return this->borderWidth;
}

QColor GifWidget::getBgColor() const
{
    return this->bgColor;
}

void GifWidget::initControl()
{
    this->setObjectName("GifWidget");
    this->resize(800, 600);
    this->setSizeGripEnabled(true);
    QVBoxLayout *verticalLayout = new QVBoxLayout(this);
    verticalLayout->setSpacing(0);
    verticalLayout->setContentsMargins(11, 11, 11, 11);
    verticalLayout->setObjectName("verticalLayout");
    verticalLayout->setContentsMargins(0, 0, 0, 0);

    widgetTop = new QWidget(this);
    widgetTop->setObjectName("widgetTop");
    widgetTop->setMinimumSize(QSize(0, 35));
    widgetTop->setMaximumSize(QSize(16777215, 35));

    QHBoxLayout *layoutTop = new QHBoxLayout(widgetTop);
    layoutTop->setSpacing(0);
    layoutTop->setContentsMargins(11, 11, 11, 11);
    layoutTop->setObjectName("layoutTop");
    layoutTop->setContentsMargins(0, 0, 0, 0);

    QPushButton *btnIcon = new QPushButton(widgetTop);
    btnIcon->setObjectName("btnIcon");
    QSizePolicy sizePolicy(QSizePolicy::Minimum, QSizePolicy::Expanding);
    sizePolicy.setHorizontalStretch(0);
    sizePolicy.setVerticalStretch(0);
    sizePolicy.setHeightForWidth(btnIcon->sizePolicy().hasHeightForWidth());
    btnIcon->setSizePolicy(sizePolicy);
    btnIcon->setMinimumSize(QSize(35, 0));
    btnIcon->setFlat(true);
    layoutTop->addWidget(btnIcon);

    QLabel *labTitle = new QLabel(widgetTop);
    labTitle->setObjectName("labTitle");
    layoutTop->addWidget(labTitle);

    QSpacerItem *horizontalSpacer = new QSpacerItem(87, 20, QSizePolicy::Expanding, QSizePolicy::Minimum);
    layoutTop->addItem(horizontalSpacer);

    QPushButton *btnClose = new QPushButton(widgetTop);
    btnClose->setObjectName("btnClose");
    sizePolicy.setHeightForWidth(btnClose->sizePolicy().hasHeightForWidth());
    btnClose->setSizePolicy(sizePolicy);
    btnClose->setMinimumSize(QSize(35, 0));
    btnClose->setFocusPolicy(Qt::NoFocus);
    btnClose->setFlat(true);
    layoutTop->addWidget(btnClose);
    verticalLayout->addWidget(widgetTop);

    widgetMain = new QWidget(this);
    widgetMain->setObjectName("widgetMain");
    QSizePolicy sizePolicy1(QSizePolicy::Preferred, QSizePolicy::Expanding);
    sizePolicy1.setHorizontalStretch(0);
    sizePolicy1.setVerticalStretch(0);
    sizePolicy1.setHeightForWidth(widgetMain->sizePolicy().hasHeightForWidth());
    widgetMain->setSizePolicy(sizePolicy1);
    verticalLayout->addWidget(widgetMain);

    widgetBottom = new QWidget(this);
    widgetBottom->setObjectName("widgetBottom");
    widgetBottom->setMinimumSize(QSize(0, 45));
    widgetBottom->setMaximumSize(QSize(16777215, 45));

    QHBoxLayout *layoutBottom = new QHBoxLayout(widgetBottom);
    layoutBottom->setSpacing(6);
    layoutBottom->setContentsMargins(11, 11, 11, 11);
    layoutBottom->setObjectName("layoutBottom");
    layoutBottom->setContentsMargins(9, 9, -1, -1);

    QLabel *labFps = new QLabel(widgetBottom);
    labFps->setObjectName("labFps");
    layoutBottom->addWidget(labFps);

    txtFps = new QLineEdit(widgetBottom);
    txtFps->setObjectName("txtFps");
    txtFps->setMaximumSize(QSize(50, 16777215));
    txtFps->setAlignment(Qt::AlignCenter);
    layoutBottom->addWidget(txtFps);

    QLabel *labWidth = new QLabel(widgetBottom);
    labWidth->setObjectName("labWidth");
    layoutBottom->addWidget(labWidth);

    txtWidth = new QLineEdit(widgetBottom);
    txtWidth->setObjectName("txtWidth");
    txtWidth->setEnabled(true);
    txtWidth->setMaximumSize(QSize(50, 16777215));
    txtWidth->setAlignment(Qt::AlignCenter);
    layoutBottom->addWidget(txtWidth);

    QLabel *labHeight = new QLabel(widgetBottom);
    labHeight->setObjectName("labHeight");
    layoutBottom->addWidget(labHeight);

    txtHeight = new QLineEdit(widgetBottom);
    txtHeight->setObjectName("txtHeight");
    txtHeight->setEnabled(true);
    txtHeight->setMaximumSize(QSize(50, 16777215));
    txtHeight->setAlignment(Qt::AlignCenter);
    layoutBottom->addWidget(txtHeight);

    labStatus = new QLabel(widgetBottom);
    labStatus->setObjectName("labStatus");
    QSizePolicy sizePolicy2(QSizePolicy::Expanding, QSizePolicy::Preferred);
    sizePolicy2.setHorizontalStretch(0);
    sizePolicy2.setVerticalStretch(0);
    sizePolicy2.setHeightForWidth(labStatus->sizePolicy().hasHeightForWidth());
    labStatus->setSizePolicy(sizePolicy2);
    labStatus->setAlignment(Qt::AlignCenter);
    layoutBottom->addWidget(labStatus);

    btnStart = new QPushButton(widgetBottom);
    btnStart->setObjectName("btnStart");
    sizePolicy.setHeightForWidth(btnStart->sizePolicy().hasHeightForWidth());
    btnStart->setSizePolicy(sizePolicy);
    layoutBottom->addWidget(btnStart);
    verticalLayout->addWidget(widgetBottom);

    labTitle->setText("GIF Video Recording Tool(QQ:517216493)");
    labFps->setText("frame rate");
    labWidth->setText("width");
    labHeight->setText("height");
    btnStart->setText("start");
    this->setWindowTitle(labTitle->text());

    btnIcon->setIcon(style()->standardIcon(QStyle::SP_ComputerIcon));
    btnClose->setIcon(style()->standardIcon(QStyle::SP_TitleBarCloseButton));

    connect(btnClose, SIGNAL(clicked(bool)), this, SLOT(closeAll()));
    connect(btnStart, SIGNAL(clicked(bool)), this, SLOT(record()));
    connect(txtWidth, SIGNAL(editingFinished()), this, SLOT(resizeForm()));
    connect(txtHeight, SIGNAL(editingFinished()), this, SLOT(resizeForm()));
}

void GifWidget::initForm()
{
    borderWidth = 3;
    bgColor = QColor(34, 163, 169);

    fps = 10;
    txtFps->setText(QString::number(fps));
    gifWriter = 0;

    timer = new QTimer(this);
    timer->setInterval(100);
    connect(timer, SIGNAL(timeout()), this, SLOT(saveImage()));

    this->setAttribute(Qt::WA_TranslucentBackground);
    this->setWindowFlags(Qt::FramelessWindowHint | Qt::WindowSystemMenuHint | Qt::WindowMinMaxButtonsHint | Qt::WindowStaysOnTopHint);
    this->installEventFilter(this);

    QStringList qss;
    qss.append("QLabel{color:#ffffff;}");
    qss.append("#btnClose,#btnIcon{border:none;border-radius:0px;}");
    qss.append("#btnClose:hover{background-color:#ff0000;}");
    qss.append("#btnClose{border-top-right-radius:5px;}");
    qss.append("#labTitle{font:bold 16px;}");
    qss.append("#labStatus{font:15px;}");
    this->setStyleSheet(qss.join(""));
}

void GifWidget::saveImage()
{
    if (!gifWriter) {
        return;
    }

#if (QT_VERSION <= QT_VERSION_CHECK(5,0,0))
    //Because qt4 does not have RGBA8888, ARGB32 is the closest to RGBA8888, and the color will be a little deviated.
    QPixmap pix = QPixmap::grabWindow(0, x() + rectGif.x(), y() + rectGif.y(), rectGif.width(), rectGif.height());
    QImage image = pix.toImage().convertToFormat(QImage::Format_ARGB32);
#else
    QScreen *screen = QApplication::primaryScreen();
    QPixmap pix = screen->grabWindow(0, x() + rectGif.x(), y() + rectGif.y(), rectGif.width(), rectGif.height());
    QImage image = pix.toImage().convertToFormat(QImage::Format_RGBA8888);
#endif

    gif.GifWriteFrame(gifWriter, image.bits(), rectGif.width(), rectGif.height(), fps);
    count++;
    labStatus->setText(QString("Recording No. %1 frame").arg(count));
}

void GifWidget::record()
{
    if (btnStart->text() == "start") {
        if (0 != gifWriter) {
            delete gifWriter;
            gifWriter = 0;
        }

        //First pop-up file save dialog box
        //fileName = qApp->applicationDirPath() + "/" + QDateTime::currentDateTime().toString("yyyy-MM-dd-hh-mm-ss.gif");
        fileName = QFileDialog::getSaveFileName(this, "Select a save location", qApp->applicationDirPath() + "/", "gif picture(*.gif)");
        if (fileName.isEmpty()) {
            return;
        }

        int width = txtWidth->text().toInt();
        int height = txtHeight->text().toInt();
        fps = txtFps->text().toInt();

        gifWriter = new Gif::GifWriter;
        bool bOk = gif.GifBegin(gifWriter, fileName.toLocal8Bit().data(), width, height, fps);
        if (!bOk) {
            delete gifWriter;
            gifWriter = 0;
            return;
        }

        count = 0;
        labStatus->setText("start recording...");
        btnStart->setText("Stop it");
        //Delayed startup
        timer->setInterval(1000 / fps);
        QTimer::singleShot(1000, timer, SLOT(start()));
        //saveImage();
    } else {
        timer->stop();
        gif.GifEnd(gifWriter);

        delete gifWriter;
        gifWriter = 0;

        labStatus->setText(QString("Recording Completed %1 frame").arg(count));
        btnStart->setText("start");
        QDesktopServices::openUrl(QUrl(fileName));
    }
}

void GifWidget::closeAll()
{
    if (0 != gifWriter) {
        delete gifWriter;
        gifWriter = 0;
    }

    this->close();
}

void GifWidget::resizeForm()
{
    int width = txtWidth->text().toInt();
    int height = txtHeight->text().toInt();
    this->resize(width, height + widgetTop->height() + widgetBottom->height());
}

void GifWidget::setBorderWidth(int borderWidth)
{
    if (this->borderWidth != borderWidth) {
        this->borderWidth = borderWidth;
        this->update();
    }
}

void GifWidget::setBgColor(const QColor &bgColor)
{
    if (this->bgColor != bgColor) {
        this->bgColor = bgColor;
        this->update();
    }
}


Introduction of Control

  1. More than 149 exquisite controls, covering a variety of dashboards, progress bars, progress balls, compasses, curves, rulers, thermometers, navigation bars, navigation bars, flatui, highlighted buttons, sliding selectors, the lunar calendar and so on. Far more controls than qwt integrates.
  2. Each class can be separated into a separate control, zero-coupling, each control has a header file and an implementation file, independent of other files, to facilitate the integration of a single control in the form of source code into the project, less code. The control classes of qwt are interlinked and highly coupled. If you want to use one of the controls, you must include all the code.
  3. All pure Qt writing, QWidget+QPainter drawing, support any Qt version from Qt4.6 to Qt5.12, support mingw, msvc, gcc and other compilers, support any operating system such as windows+linux+mac + embedded linux, no scrambling, can be directly integrated into Qt Creator, as with its own controls, most of the effects as long as set up. Several attributes can be used, which is very convenient.
  4. Each control has a corresponding separate DEMO containing the source code of the control, which is convenient for reference. It also provides an integrated DEMO for all controls.
  5. The source code of each control has detailed Chinese annotations, which are compiled in accordance with the unified design specifications. It is convenient to learn how to compile custom controls.
  6. Each control's default color matching and demo's corresponding color matching are very beautiful.
  7. More than 130 visible controls and 6 invisible controls.
  8. Some controls provide a variety of style choices, a variety of indicator style choices.
  9. All controls adapt to form stretching changes.
  10. Integrate custom control property designer, support drag design, WYSIWYG, support import and export xml format.
  11. With activex control demo, all controls can run directly in ie browser.
  12. Integrate fontawesome graphic fonts + hundreds of graphic fonts collected by Alibaba iconfont to enjoy the fun of graphic fonts.
  13. All controls finally generate a dll Dynamic Library file, which can be directly integrated into the qtcreator for dragging design use.
  14. At present, there is a version of qml, pyqt version will be considered later, if the user demand is large.

7. SDK Download

  • SDK Download Link: https://pan.baidu.com/s/1A5Gd77kExm8Co5ckT51vvQ Extraction code: 877p
  • The download link contains various versions of dynamic library files, header files of all controls, using demo, custom control + property designer.
  • Custom control plug-in open dynamic library dll use (permanent free), without any back door and restrictions, please feel free to use.
  • At present, 26 versions of dll have been provided, including qt5.12.3 MSVC 2017 32 + 64 MinGW 32 + 64.
  • Increase control and improve control from time to time, update SDK from time to time. Welcome to make suggestions. Thank you!
  • widget version (QQ: 517216493) qml version (QQ: 373955953) camel (QQ: 278969898).
  • The Way to Advance Qt in Taoge's Knowledge Column https://zhuanlan.zhihu.com/TaoQt
  • Welcome to pay attention to Wechat Public Number, C++/Python, learning methods, writing skills, popular technology, career development and other content, more dry goods, more benefits!

Keywords: Mobile Qt SDK Qt5 Linux

Added by mrgrammar on Mon, 22 Jul 2019 11:01:05 +0300