├── .gitignore
├── QmlFireworks
├── README.md
├── QmlFireworks_zh_CN.qm
├── resource
│ ├── background.png
│ └── res.qrc
├── src
│ ├── src.pri
│ ├── qml
│ │ ├── qml.qrc
│ │ └── main.qml
│ └── main.cpp
├── QmlFireworks_zh_CN.ts
└── QmlFireworks.pro
├── QmlDemo
├── QmlDemo_zh_CN.qm
├── src
│ ├── src.pri
│ └── customItem
│ │ ├── customItem.pri
│ │ ├── clickwaveeffect.h
│ │ └── clickwaveeffect.cpp
├── qml.qrc
├── res.qrc
├── README.md
├── QmlDemo.pro
├── main.cpp
├── QmlDemo_zh_CN.ts
└── main.qml
├── VideoPlayer
├── resource
│ ├── back.png
│ ├── file.png
│ ├── play.png
│ ├── stop.png
│ ├── pause.png
│ ├── volume.png
│ ├── goahead.png
│ ├── settings.png
│ ├── full_screen.png
│ ├── non-volume.png
│ ├── exit_full_screen.png
│ └── res.qrc
├── screenshot
│ └── run.png
├── VideoPlayer_zh_CN.qm
├── shaders
│ ├── shaders.qrc
│ ├── vertex.vsh
│ └── fragment.fsh
├── src
│ ├── src.pri
│ ├── util
│ │ ├── util.pri
│ │ ├── keyboardcontrollor.h
│ │ └── keyboardcontrollor.cpp
│ ├── player
│ │ ├── player.pri
│ │ ├── config.h
│ │ ├── ffmpeg.h
│ │ ├── audiooutput.h
│ │ ├── videoplayer_p.h
│ │ ├── videorenderer.h
│ │ ├── videoplayer_p.cpp
│ │ ├── audiooutput.cpp
│ │ ├── videoplayer.h
│ │ ├── ffmpegdecoder.h
│ │ └── videoplayer.cpp
│ └── main.cpp
├── qml
│ ├── qml.qrc
│ ├── PlaySlider.qml
│ └── Toast.qml
├── README.md
├── VideoPlayer.pro
└── VideoPlayer_zh_CN.ts
├── CustomWidgetDemos
├── CustomWidgetDemos_zh_CN.qm
├── resource
│ └── image
│ │ └── close.png
├── screenshot
│ ├── CustomWidgetDemos_1.png
│ └── CustomWidgetDemos_2.png
├── res.qrc
├── src
│ ├── src.pri
│ ├── main.cpp
│ ├── customWidgets
│ │ ├── customWidgets.pri
│ │ ├── countdownbutton.h
│ │ ├── countdownbutton.cpp
│ │ ├── progressdial.h
│ │ ├── progressdial.cpp
│ │ ├── aligniconbutton.cpp
│ │ ├── progressbutton.cpp
│ │ ├── notifymanager.h
│ │ ├── toast.h
│ │ ├── rotatestackedwidget.h
│ │ ├── notifywidget.h
│ │ ├── progressbutton.h
│ │ ├── aligniconbutton.h
│ │ ├── translationstackedwidget.h
│ │ ├── notifymanager.cpp
│ │ ├── toast.cpp
│ │ ├── rotatestackedwidget.cpp
│ │ ├── notifywidget.cpp
│ │ └── translationstackedwidget.cpp
│ ├── mainwidget.h
│ └── mainwidget.cpp
├── CustomWidgetDemos.pro
├── README.md
└── CustomWidgetDemos_zh_CN.ts
├── MultithreadedDownloader
├── MultithreadedDownloader_zh_CN.qm
├── image
│ ├── MultithreadedDownloader_1.png
│ └── MultithreadedDownloader_2.png
├── res.qrc
├── src
│ ├── customWidgets
│ │ ├── customWidgets.pri
│ │ ├── toast.h
│ │ ├── translationstackedwidget.h
│ │ ├── toast.cpp
│ │ └── translationstackedwidget.cpp
│ ├── src.pri
│ ├── downloader
│ │ ├── downloader.pri
│ │ ├── util.h
│ │ ├── multithreadeddownloaderwriter.cpp
│ │ ├── abstractmission.h
│ │ ├── multithreadeddownloaderwriter.h
│ │ ├── downloadmission.h
│ │ ├── downloadmission.cpp
│ │ ├── multithreadeddownloader.h
│ │ └── multithreadeddownloader.cpp
│ ├── mainwidget.h
│ ├── main.cpp
│ ├── mainwidget.cpp
│ └── mainwidget.ui
├── MultithreadedDownloader.pro
├── README.md
└── MultithreadedDownloader_zh_CN.ts
├── QtDemos.pro
├── LICENSE
├── README.CN.md
└── README.md
/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 | *.pro.user*
--------------------------------------------------------------------------------
/QmlFireworks/README.md:
--------------------------------------------------------------------------------
1 | # QmlFireworks
2 | * 一个基于 `QML Particle System` 的烟花 Demo。
--------------------------------------------------------------------------------
/QmlDemo/QmlDemo_zh_CN.qm:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ho-229/QtDemos/HEAD/QmlDemo/QmlDemo_zh_CN.qm
--------------------------------------------------------------------------------
/VideoPlayer/resource/back.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ho-229/QtDemos/HEAD/VideoPlayer/resource/back.png
--------------------------------------------------------------------------------
/VideoPlayer/resource/file.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ho-229/QtDemos/HEAD/VideoPlayer/resource/file.png
--------------------------------------------------------------------------------
/VideoPlayer/resource/play.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ho-229/QtDemos/HEAD/VideoPlayer/resource/play.png
--------------------------------------------------------------------------------
/VideoPlayer/resource/stop.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ho-229/QtDemos/HEAD/VideoPlayer/resource/stop.png
--------------------------------------------------------------------------------
/VideoPlayer/resource/pause.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ho-229/QtDemos/HEAD/VideoPlayer/resource/pause.png
--------------------------------------------------------------------------------
/VideoPlayer/resource/volume.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ho-229/QtDemos/HEAD/VideoPlayer/resource/volume.png
--------------------------------------------------------------------------------
/VideoPlayer/screenshot/run.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ho-229/QtDemos/HEAD/VideoPlayer/screenshot/run.png
--------------------------------------------------------------------------------
/QmlFireworks/QmlFireworks_zh_CN.qm:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ho-229/QtDemos/HEAD/QmlFireworks/QmlFireworks_zh_CN.qm
--------------------------------------------------------------------------------
/VideoPlayer/VideoPlayer_zh_CN.qm:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ho-229/QtDemos/HEAD/VideoPlayer/VideoPlayer_zh_CN.qm
--------------------------------------------------------------------------------
/VideoPlayer/resource/goahead.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ho-229/QtDemos/HEAD/VideoPlayer/resource/goahead.png
--------------------------------------------------------------------------------
/VideoPlayer/resource/settings.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ho-229/QtDemos/HEAD/VideoPlayer/resource/settings.png
--------------------------------------------------------------------------------
/QmlDemo/src/src.pri:
--------------------------------------------------------------------------------
1 | INCLUDEPATH += $$PWD
2 | DEPENDPATH += $$PWD
3 |
4 | include($$PWD/customItem/customItem.pri)
5 |
--------------------------------------------------------------------------------
/QmlFireworks/resource/background.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ho-229/QtDemos/HEAD/QmlFireworks/resource/background.png
--------------------------------------------------------------------------------
/VideoPlayer/resource/full_screen.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ho-229/QtDemos/HEAD/VideoPlayer/resource/full_screen.png
--------------------------------------------------------------------------------
/VideoPlayer/resource/non-volume.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ho-229/QtDemos/HEAD/VideoPlayer/resource/non-volume.png
--------------------------------------------------------------------------------
/QmlDemo/qml.qrc:
--------------------------------------------------------------------------------
1 |
2 |
3 | main.qml
4 |
5 |
6 |
--------------------------------------------------------------------------------
/QmlFireworks/src/src.pri:
--------------------------------------------------------------------------------
1 | INCLUDEPATH += $$PWD
2 | DEPENDPATH += $$PWD
3 |
4 | SOURCES += \
5 | $$PWD/main.cpp
6 |
--------------------------------------------------------------------------------
/VideoPlayer/resource/exit_full_screen.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ho-229/QtDemos/HEAD/VideoPlayer/resource/exit_full_screen.png
--------------------------------------------------------------------------------
/CustomWidgetDemos/CustomWidgetDemos_zh_CN.qm:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ho-229/QtDemos/HEAD/CustomWidgetDemos/CustomWidgetDemos_zh_CN.qm
--------------------------------------------------------------------------------
/CustomWidgetDemos/resource/image/close.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ho-229/QtDemos/HEAD/CustomWidgetDemos/resource/image/close.png
--------------------------------------------------------------------------------
/QmlFireworks/src/qml/qml.qrc:
--------------------------------------------------------------------------------
1 |
2 |
3 | main.qml
4 |
5 |
6 |
--------------------------------------------------------------------------------
/QmlDemo/res.qrc:
--------------------------------------------------------------------------------
1 |
2 |
3 | QmlDemo_zh_CN.qm
4 |
5 |
6 |
--------------------------------------------------------------------------------
/QmlFireworks/resource/res.qrc:
--------------------------------------------------------------------------------
1 |
2 |
3 | background.png
4 |
5 |
6 |
--------------------------------------------------------------------------------
/CustomWidgetDemos/screenshot/CustomWidgetDemos_1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ho-229/QtDemos/HEAD/CustomWidgetDemos/screenshot/CustomWidgetDemos_1.png
--------------------------------------------------------------------------------
/CustomWidgetDemos/screenshot/CustomWidgetDemos_2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ho-229/QtDemos/HEAD/CustomWidgetDemos/screenshot/CustomWidgetDemos_2.png
--------------------------------------------------------------------------------
/MultithreadedDownloader/MultithreadedDownloader_zh_CN.qm:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ho-229/QtDemos/HEAD/MultithreadedDownloader/MultithreadedDownloader_zh_CN.qm
--------------------------------------------------------------------------------
/MultithreadedDownloader/image/MultithreadedDownloader_1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ho-229/QtDemos/HEAD/MultithreadedDownloader/image/MultithreadedDownloader_1.png
--------------------------------------------------------------------------------
/MultithreadedDownloader/image/MultithreadedDownloader_2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ho-229/QtDemos/HEAD/MultithreadedDownloader/image/MultithreadedDownloader_2.png
--------------------------------------------------------------------------------
/MultithreadedDownloader/res.qrc:
--------------------------------------------------------------------------------
1 |
2 |
3 | MultithreadedDownloader_zh_CN.qm
4 |
5 |
6 |
--------------------------------------------------------------------------------
/QtDemos.pro:
--------------------------------------------------------------------------------
1 | TEMPLATE = subdirs
2 |
3 | SUBDIRS += \
4 | CustomWidgetDemos \
5 | MultithreadedDownloader \
6 | QmlDemo \
7 | QmlFireworks \
8 | VideoPlayer
9 |
--------------------------------------------------------------------------------
/VideoPlayer/shaders/shaders.qrc:
--------------------------------------------------------------------------------
1 |
2 |
3 | fragment.fsh
4 | vertex.vsh
5 |
6 |
7 |
--------------------------------------------------------------------------------
/QmlDemo/src/customItem/customItem.pri:
--------------------------------------------------------------------------------
1 | INCLUDEPATH += $$PWD
2 | DEPENDPATH += $$PWD
3 |
4 | HEADERS += \
5 | $$PWD/clickwaveeffect.h
6 |
7 | SOURCES += \
8 | $$PWD/clickwaveeffect.cpp
9 |
--------------------------------------------------------------------------------
/VideoPlayer/src/src.pri:
--------------------------------------------------------------------------------
1 | INCLUDEPATH += $$PWD
2 | DEPENDPATH += $$PWD
3 |
4 | SOURCES += \
5 | $$PWD/main.cpp
6 |
7 | include($$PWD/player/player.pri)
8 | include($$PWD/util/util.pri)
9 |
--------------------------------------------------------------------------------
/VideoPlayer/src/util/util.pri:
--------------------------------------------------------------------------------
1 | INCLUDEPATH += $$PWD
2 | DEPENDPATH += $$PWD
3 |
4 | HEADERS += \
5 | $$PWD/keyboardcontrollor.h
6 |
7 | SOURCES += \
8 | $$PWD/keyboardcontrollor.cpp
9 |
--------------------------------------------------------------------------------
/VideoPlayer/qml/qml.qrc:
--------------------------------------------------------------------------------
1 |
2 |
3 | main.qml
4 | PlaySlider.qml
5 | Toast.qml
6 |
7 |
8 |
--------------------------------------------------------------------------------
/CustomWidgetDemos/res.qrc:
--------------------------------------------------------------------------------
1 |
2 |
3 | CustomWidgetDemos_zh_CN.qm
4 |
5 |
6 | resource/image/close.png
7 |
8 |
9 |
--------------------------------------------------------------------------------
/VideoPlayer/shaders/vertex.vsh:
--------------------------------------------------------------------------------
1 | #version 440
2 | layout (location = 0) in vec2 vertex;
3 | layout (location = 1) in vec2 texCoord;
4 |
5 | out vec2 v_texCoord;
6 | void main(void)
7 | {
8 | gl_Position = vec4(vertex, 0, 1.0f);
9 | v_texCoord = texCoord;
10 | }
11 |
--------------------------------------------------------------------------------
/MultithreadedDownloader/src/customWidgets/customWidgets.pri:
--------------------------------------------------------------------------------
1 | INCLUDEPATH += $$PWD
2 | DEPENDPATH += $$PWD
3 |
4 | HEADERS += \
5 | $$PWD/toast.h \
6 | $$PWD/translationstackedwidget.h
7 |
8 | SOURCES += \
9 | $$PWD/toast.cpp \
10 | $$PWD/translationstackedwidget.cpp
11 |
--------------------------------------------------------------------------------
/CustomWidgetDemos/src/src.pri:
--------------------------------------------------------------------------------
1 | INCLUDEPATH += $$PWD
2 | DEPENDPATH += $$PWD
3 |
4 | SOURCES += \
5 | $$PWD/main.cpp \
6 | $$PWD/mainwidget.cpp
7 |
8 | HEADERS += \
9 | $$PWD/mainwidget.h
10 |
11 | FORMS += \
12 | $$PWD/mainwidget.ui
13 |
14 | include($$PWD/customWidgets/customWidgets.pri)
15 |
--------------------------------------------------------------------------------
/MultithreadedDownloader/src/src.pri:
--------------------------------------------------------------------------------
1 | INCLUDEPATH += $$PWD
2 | DEPENDPATH += $$PWD
3 |
4 | SOURCES += \
5 | $$PWD/main.cpp \
6 | $$PWD/mainwidget.cpp
7 |
8 | HEADERS += \
9 | $$PWD/mainwidget.h
10 |
11 | FORMS += \
12 | $$PWD/mainwidget.ui
13 |
14 | include($$PWD/downloader/downloader.pri)
15 | include($$PWD/customWidgets/customWidgets.pri)
16 |
--------------------------------------------------------------------------------
/QmlFireworks/QmlFireworks_zh_CN.ts:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | main
6 |
7 |
8 | Fireworks
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/MultithreadedDownloader/src/downloader/downloader.pri:
--------------------------------------------------------------------------------
1 | INCLUDEPATH += $$PWD
2 | DEPENDPATH += $$PWD
3 |
4 | HEADERS += \
5 | $$PWD/abstractmission.h \
6 | $$PWD/downloadmission.h \
7 | $$PWD/multithreadeddownloader.h \
8 | $$PWD/multithreadeddownloaderwriter.h \
9 | $$PWD/util.h
10 |
11 | SOURCES += \
12 | $$PWD/downloadmission.cpp \
13 | $$PWD/multithreadeddownloader.cpp \
14 | $$PWD/multithreadeddownloaderwriter.cpp
15 |
--------------------------------------------------------------------------------
/VideoPlayer/src/player/player.pri:
--------------------------------------------------------------------------------
1 | INCLUDEPATH += $$PWD
2 | DEPENDPATH += $$PWD
3 |
4 | HEADERS += \
5 | $$PWD/audiooutput.h \
6 | $$PWD/config.h \
7 | $$PWD/ffmpeg.h \
8 | $$PWD/ffmpegdecoder.h \
9 | $$PWD/videoplayer.h \
10 | $$PWD/videoplayer_p.h \
11 | $$PWD/videorenderer.h
12 |
13 | SOURCES += \
14 | $$PWD/audiooutput.cpp \
15 | $$PWD/ffmpegdecoder.cpp \
16 | $$PWD/videoplayer.cpp \
17 | $$PWD/videoplayer_p.cpp \
18 | $$PWD/videorenderer.cpp
19 |
--------------------------------------------------------------------------------
/VideoPlayer/README.md:
--------------------------------------------------------------------------------
1 | # Video Player
2 | * A video player based on `Qt`, using `FFmpeg` for decoding and `OpenGL` for rendering.
3 |
4 | * Screenshot
5 | 
6 |
7 | ## Featrues
8 | - [x] Play audio and video.
9 | - [x] Play progress control.
10 | - [x] Play volume control.
11 | - [x] Subtitle support.
12 | - [x] `.ass` subtitle support.
13 | - [x] `Bitmap` subtitle support.
14 | - [x] Subtitle track select.
15 | - [x] Audio track select.
16 | - [x] Play internet stream
17 |
--------------------------------------------------------------------------------
/VideoPlayer/resource/res.qrc:
--------------------------------------------------------------------------------
1 |
2 |
3 | file.png
4 | play.png
5 | pause.png
6 | back.png
7 | goahead.png
8 | full_screen.png
9 | exit_full_screen.png
10 | stop.png
11 | volume.png
12 | non-volume.png
13 | settings.png
14 |
15 |
16 |
17 |
--------------------------------------------------------------------------------
/CustomWidgetDemos/src/main.cpp:
--------------------------------------------------------------------------------
1 | /**
2 | * @brief Demo
3 | * @anchor Ho229
4 | * @date 2020/12/12
5 | */
6 |
7 | #include "mainwidget.h"
8 |
9 | #include
10 | #include
11 |
12 | int main(int argc, char *argv[])
13 | {
14 | QApplication a(argc, argv);
15 |
16 | #ifdef Q_OS_WIN
17 | a.setFont(QFont("Microsoft YaHei", 9));
18 | #endif
19 |
20 | QTextCodec::setCodecForLocale( // Set codec to UTF-8
21 | QTextCodec::codecForName("UTF-8"));
22 |
23 | MainWidget w;
24 | w.show();
25 | return a.exec();
26 | }
27 |
--------------------------------------------------------------------------------
/VideoPlayer/src/player/config.h:
--------------------------------------------------------------------------------
1 | /**
2 | * @brief Static Configurations
3 | * @anchor Ho 229
4 | * @date 2023/4/21
5 | */
6 |
7 | #ifndef CONFIG_H
8 | #define CONFIG_H
9 |
10 | #define VIDEO_CACHE_SIZE 256
11 | #define AUDIO_CACHE_SIZE 256
12 | #define SUBTITLE_CACHE_SIZE 64
13 |
14 | // FFmpegDecoder::decode() will be called asynchronously
15 | // when the time difference between the current frame and the last cached frame
16 | // is less than MIN_DECODED_DURATION, in seconds
17 | #define MIN_DECODED_DURATION 0.25
18 |
19 | #define MAX_DECODED_DURATION MIN_DECODED_DURATION * 2
20 |
21 | #endif // CONFIG_H
22 |
--------------------------------------------------------------------------------
/VideoPlayer/src/util/keyboardcontrollor.h:
--------------------------------------------------------------------------------
1 | /**
2 | * @brief keyboard Controllor
3 | * @author Ho 229
4 | */
5 |
6 | #ifndef KEYBOARDCONTROLLOR_H
7 | #define KEYBOARDCONTROLLOR_H
8 |
9 | #include
10 |
11 | class KeyboardControllor : public QObject
12 | {
13 | Q_OBJECT
14 | public:
15 | explicit KeyboardControllor(QObject *parent = nullptr);
16 |
17 | signals:
18 | void play();
19 | void goahead();
20 | void back();
21 | void escape();
22 |
23 | private:
24 | bool eventFilter(QObject *obj, QEvent *event) Q_DECL_OVERRIDE;
25 | };
26 |
27 | #endif // KEYBOARDCONTROLLOR_H
28 |
--------------------------------------------------------------------------------
/QmlFireworks/src/main.cpp:
--------------------------------------------------------------------------------
1 | #include
2 | #include
3 |
4 | int main(int argc, char *argv[])
5 | {
6 | #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
7 | QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
8 | #endif
9 |
10 | QGuiApplication app(argc, argv);
11 |
12 | QQmlApplicationEngine engine;
13 | const QUrl url(QStringLiteral("qrc:/main.qml"));
14 | QObject::connect(&engine, &QQmlApplicationEngine::objectCreated,
15 | &app, [url](QObject *obj, const QUrl &objUrl) {
16 | if (!obj && url == objUrl)
17 | QCoreApplication::exit(-1);
18 | }, Qt::QueuedConnection);
19 | engine.load(url);
20 |
21 | return app.exec();
22 | }
23 |
--------------------------------------------------------------------------------
/CustomWidgetDemos/src/customWidgets/customWidgets.pri:
--------------------------------------------------------------------------------
1 | INCLUDEPATH += $$PWD
2 | DEPENDPATH += $$PWD
3 |
4 | HEADERS += \
5 | $$PWD/aligniconbutton.h \
6 | $$PWD/countdownbutton.h \
7 | $$PWD/notifymanager.h \
8 | $$PWD/notifywidget.h \
9 | $$PWD/progressbutton.h \
10 | $$PWD/progressdial.h \
11 | $$PWD/rotatestackedwidget.h \
12 | $$PWD/toast.h \
13 | $$PWD/translationstackedwidget.h
14 |
15 | SOURCES += \
16 | $$PWD/aligniconbutton.cpp \
17 | $$PWD/countdownbutton.cpp \
18 | $$PWD/notifymanager.cpp \
19 | $$PWD/notifywidget.cpp \
20 | $$PWD/progressbutton.cpp \
21 | $$PWD/progressdial.cpp \
22 | $$PWD/rotatestackedwidget.cpp \
23 | $$PWD/toast.cpp \
24 | $$PWD/translationstackedwidget.cpp
25 |
--------------------------------------------------------------------------------
/VideoPlayer/src/player/ffmpeg.h:
--------------------------------------------------------------------------------
1 | /**
2 | * @brief FFmpeg Library
3 | * @anchor Ho 229
4 | * @date 2021/4/10
5 | */
6 |
7 | #ifndef FFMPEG_H
8 | #define FFMPEG_H
9 |
10 | extern "C"
11 | {
12 | #ifdef __cplusplus
13 | # define __STDC_CONSTANT_MACROS
14 | # ifdef _STDINT_H
15 | # undef _STDINT_H
16 | # endif
17 | # include
18 | #endif
19 |
20 | #ifndef INT64_C
21 | #define INT64_C(c) (c ## LL)
22 | #define UINT64_C(c) (c ## ULL)
23 | #endif
24 |
25 | #include
26 | #include
27 | #include
28 | #include
29 | #include
30 | #include
31 | #include
32 | #include
33 | }
34 |
35 | #endif // FFMPEG_H
36 |
--------------------------------------------------------------------------------
/QmlDemo/README.md:
--------------------------------------------------------------------------------
1 | # QmlDemo
2 | * Just a QML Demo.
3 | ## Items
4 | * [ClickWaveEffect](./src/customItem/clickwaveeffect.h)
5 | * Add [ClickWaveEffect](./src/customItem/clickwaveeffect.h) class to your project.
6 | * Example
7 | ```cpp
8 | // main.cpp
9 | ...
10 | #include "clickwaveeffect"
11 |
12 | int main(int argc, char*[] argv)
13 | {
14 | ...
15 | qmlRegisterType("com.MyItems.Effect", 1, 0, "ClickWaveEffect");
16 | ...
17 | }
18 | ```
19 | ```qml
20 | // main.qml
21 | import QtQuick 2.12
22 | import com.MyItems.Effect 1.0
23 |
24 | Rectangle {
25 | id: root
26 |
27 | ClickWaveEffect {
28 | target: root
29 |
30 | anchors.fill: parent
31 | }
32 | }
33 | ```
--------------------------------------------------------------------------------
/QmlDemo/QmlDemo.pro:
--------------------------------------------------------------------------------
1 | QT += quick
2 |
3 | CONFIG += c++11
4 |
5 | # You can make your code fail to compile if it uses deprecated APIs.
6 | # In order to do so, uncomment the following line.
7 | #DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0
8 |
9 | SOURCES += \
10 | main.cpp
11 |
12 | RESOURCES += qml.qrc \
13 | res.qrc
14 |
15 | TRANSLATIONS += \
16 | QmlDemo_zh_CN.ts
17 |
18 | include($$_PRO_FILE_PWD_/src/src.pri)
19 |
20 | # Additional import path used to resolve QML modules in Qt Creator's code model
21 | QML_IMPORT_PATH =
22 |
23 | # Additional import path used to resolve QML modules just for Qt Quick Designer
24 | QML_DESIGNER_IMPORT_PATH =
25 |
26 | # Default rules for deployment.
27 | qnx: target.path = /tmp/$${TARGET}/bin
28 | else: unix:!android: target.path = /opt/$${TARGET}/bin
29 | !isEmpty(target.path): INSTALLS += target
30 |
--------------------------------------------------------------------------------
/QmlFireworks/QmlFireworks.pro:
--------------------------------------------------------------------------------
1 | QT += quick
2 |
3 | CONFIG += c++11
4 |
5 | # You can make your code fail to compile if it uses deprecated APIs.
6 | # In order to do so, uncomment the following line.
7 | #DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0
8 |
9 | include($$_PRO_FILE_PWD_/src/src.pri)
10 |
11 | RESOURCES += $$_PRO_FILE_PWD_/src/qml/qml.qrc \
12 | $$_PRO_FILE_PWD_/resource/res.qrc
13 |
14 | TRANSLATIONS += \
15 | QmlFireworks_zh_CN.ts
16 |
17 | # Additional import path used to resolve QML modules in Qt Creator's code model
18 | QML_IMPORT_PATH =
19 |
20 | # Additional import path used to resolve QML modules just for Qt Quick Designer
21 | QML_DESIGNER_IMPORT_PATH =
22 |
23 | # Default rules for deployment.
24 | qnx: target.path = /tmp/$${TARGET}/bin
25 | else: unix:!android: target.path = /opt/$${TARGET}/bin
26 | !isEmpty(target.path): INSTALLS += target
27 |
--------------------------------------------------------------------------------
/VideoPlayer/src/util/keyboardcontrollor.cpp:
--------------------------------------------------------------------------------
1 | /**
2 | * @brief keyboard Controllor
3 | * @author Ho 229
4 | */
5 |
6 | #include "keyboardcontrollor.h"
7 |
8 | #include
9 |
10 | KeyboardControllor::KeyboardControllor(QObject *parent) : QObject(parent)
11 | {
12 |
13 | }
14 |
15 | bool KeyboardControllor::eventFilter(QObject *obj, QEvent *event)
16 | {
17 | if(event->type() == QEvent::KeyPress)
18 | {
19 | QKeyEvent *keyEvent = static_cast(event);
20 |
21 | switch(keyEvent->key())
22 | {
23 | case Qt::Key_Space:
24 | emit play();
25 | break;
26 | case Qt::Key_Left:
27 | emit back();
28 | break;
29 | case Qt::Key_Right:
30 | emit goahead();
31 | break;
32 | case Qt::Key_Escape:
33 | emit escape();
34 | break;
35 | }
36 | }
37 |
38 | return QObject::eventFilter(obj, event);
39 | }
40 |
--------------------------------------------------------------------------------
/MultithreadedDownloader/src/downloader/util.h:
--------------------------------------------------------------------------------
1 | /**
2 | * @brief Until
3 | * @anchor NiceBlueChai
4 | * @date 2020/07/16
5 | */
6 |
7 | #ifndef UTIL_H
8 | #define UTIL_H
9 |
10 | #include
11 |
12 | namespace Until {
13 |
14 | /**
15 | * @brief readableFileSize
16 | * @param value
17 | * @param precision
18 | * @return readable size
19 | */
20 | QString readableFileSize(const qint64 value, int precision = 2)
21 | {
22 | qint64 kbSize = value / 1024;
23 | if (kbSize > 1024)
24 | {
25 | qreal mbRet = static_cast(kbSize) / 1024.0;
26 |
27 | if (mbRet - 1024.0 > 0.000001)
28 | {
29 | qreal gbRet = mbRet / 1024.0;
30 | return QString::number(gbRet, 'f', precision) + "GB";
31 | }
32 | else
33 | return QString::number(mbRet, 'f', precision) + "MB";
34 | }
35 | else
36 | return QString::number(kbSize) + "KB";
37 | }
38 | }
39 |
40 | #endif // UTIL_H
41 |
--------------------------------------------------------------------------------
/CustomWidgetDemos/src/customWidgets/countdownbutton.h:
--------------------------------------------------------------------------------
1 | /**
2 | * Countdown Button
3 | * @brief 倒计时 Button
4 | * @anchor Ho229<2189684957@qq.com>
5 | * @date 2021/2/1
6 | */
7 |
8 | #ifndef COUNTDOWNBUTTON_H
9 | #define COUNTDOWNBUTTON_H
10 |
11 | #include "progressbutton.h"
12 |
13 | class QPropertyAnimation;
14 |
15 | class CountdownButton : public ProgressButton
16 | {
17 | Q_OBJECT
18 |
19 | Q_PROPERTY(int countdown READ countdown WRITE setCountdown)
20 |
21 | public:
22 | explicit CountdownButton(QWidget *parent = nullptr);
23 | virtual ~CountdownButton() Q_DECL_OVERRIDE;
24 |
25 | /**
26 | * @brief 设置倒计时时长 (毫秒)
27 | */
28 | void setCountdown(int ms);
29 | int countdown() const;
30 |
31 | /**
32 | * @brief 启动倒计时
33 | */
34 | void conutdownCilk();
35 |
36 | void conutdownCilk(int ms)
37 | {
38 | this->setCountdown(ms);
39 | this->conutdownCilk();
40 | }
41 |
42 | private:
43 | QPropertyAnimation *m_animation = nullptr;
44 |
45 | };
46 |
47 | #endif // COUNTDOWNBUTTON_H
48 |
--------------------------------------------------------------------------------
/VideoPlayer/qml/PlaySlider.qml:
--------------------------------------------------------------------------------
1 | import QtQuick 2.0
2 | import QtQuick.Controls 2.12
3 |
4 | Slider {
5 | id: slider
6 |
7 | background: Rectangle {
8 | x: slider.leftPadding
9 | y: slider.topPadding + slider.availableHeight / 2 - height / 2
10 |
11 | implicitWidth: 200
12 | implicitHeight: 5
13 |
14 | width: slider.availableWidth
15 | height: implicitHeight
16 |
17 | radius: 3
18 | color: "#bdbebf"
19 |
20 | Rectangle {
21 | width: slider.visualPosition * parent.width
22 | height: parent.height
23 | color: "#3F85FF"
24 | radius: 2
25 | }
26 | }
27 |
28 | handle: Rectangle {
29 | x: slider.leftPadding + slider.visualPosition * (slider.availableWidth - width)
30 | y: slider.topPadding + slider.availableHeight / 2 - height / 2
31 | implicitWidth: 18
32 | implicitHeight: 18
33 | radius: 13
34 | color: slider.pressed ? "#f0f0f0" : "#f6f6f6"
35 | border.color: "#bdbebf"
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2021 Ho 229
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/VideoPlayer/shaders/fragment.fsh:
--------------------------------------------------------------------------------
1 | #version 440
2 | in vec2 v_texCoord;
3 | out vec4 fragColor;
4 |
5 | layout (location = 0) uniform sampler2D texY;
6 | layout (location = 1) uniform sampler2D texU;
7 | layout (location = 2) uniform sampler2D texV;
8 | layout (location = 3) uniform sampler2D texSubtitle;
9 |
10 | layout (location = 4) uniform mat3 colorConversion;
11 | layout (location = 5) uniform bool is10Bit;
12 |
13 | void main(void)
14 | {
15 | vec3 yuv;
16 | yuv.x = texture(texY, v_texCoord).x;
17 | yuv.y = texture(texU, v_texCoord).x;
18 | yuv.z = texture(texV, v_texCoord).x;
19 |
20 | if(is10Bit)
21 | {
22 | vec3 yuv_h;
23 | yuv_h.x = texture(texY, v_texCoord).a;
24 | yuv_h.y = texture(texU, v_texCoord).a;
25 | yuv_h.z = texture(texV, v_texCoord).a;
26 | yuv = (yuv * 255.0 + yuv_h * 255.0 * 256.0) / 1023.0;
27 | }
28 |
29 | yuv -= vec3(16. / 255., 128. / 255., 128. / 255.);
30 |
31 | vec3 rgb = colorConversion * yuv;
32 | vec4 subtitle = texture(texSubtitle, v_texCoord);
33 | fragColor = mix(vec4(rgb, 1), subtitle, subtitle.a);
34 | }
35 |
--------------------------------------------------------------------------------
/MultithreadedDownloader/MultithreadedDownloader.pro:
--------------------------------------------------------------------------------
1 | QT += core gui network
2 |
3 | greaterThan(QT_MAJOR_VERSION, 4): QT += widgets
4 |
5 | CONFIG += c++11
6 |
7 | # The following define makes your compiler emit warnings if you use
8 | # any Qt feature that has been marked deprecated (the exact warnings
9 | # depend on your compiler). Please consult the documentation of the
10 | # deprecated API in order to know how to port your code away from it.
11 | DEFINES += QT_DEPRECATED_WARNINGS
12 |
13 | # You can also make your code fail to compile if it uses deprecated APIs.
14 | # In order to do so, uncomment the following line.
15 | # You can also select to disable deprecated APIs only up to a certain version of Qt.
16 | #DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0
17 |
18 | include($$_PRO_FILE_PWD_/src/src.pri)
19 |
20 | TRANSLATIONS += \
21 | MultithreadedDownloader_zh_CN.ts
22 |
23 | # Default rules for deployment.
24 | qnx: target.path = /tmp/$${TARGET}/bin
25 | else: unix:!android: target.path = /opt/$${TARGET}/bin
26 | !isEmpty(target.path): INSTALLS += target
27 |
28 | RESOURCES += \
29 | res.qrc
30 |
--------------------------------------------------------------------------------
/CustomWidgetDemos/CustomWidgetDemos.pro:
--------------------------------------------------------------------------------
1 | QT += core gui
2 |
3 | greaterThan(QT_MAJOR_VERSION, 4): QT += widgets
4 |
5 | CONFIG += c++11
6 |
7 | # The following define makes your compiler emit warnings if you use
8 | # any Qt feature that has been marked deprecated (the exact warnings
9 | # depend on your compiler). Please consult the documentation of the
10 | # deprecated API in order to know how to port your code away from it.
11 | DEFINES += QT_DEPRECATED_WARNINGS
12 |
13 | # You can also make your code fail to compile if it uses deprecated APIs.
14 | # In order to do so, uncomment the following line.
15 | # You can also select to disable deprecated APIs only up to a certain version of Qt.
16 | #DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0
17 |
18 | include($$_PRO_FILE_PWD_/src/src.pri)
19 |
20 | # Default rules for deployment.
21 | qnx: target.path = /tmp/$${TARGET}/bin
22 | else: unix:!android: target.path = /opt/$${TARGET}/bin
23 | !isEmpty(target.path): INSTALLS += target
24 |
25 | TRANSLATIONS += \
26 | CustomWidgetDemos_zh_CN.ts
27 |
28 | RESOURCES += \
29 | res.qrc
30 |
31 | DISTFILES +=
32 |
33 | HEADERS +=
34 |
35 | SOURCES +=
36 |
--------------------------------------------------------------------------------
/CustomWidgetDemos/src/customWidgets/countdownbutton.cpp:
--------------------------------------------------------------------------------
1 | /**
2 | * Countdown Button
3 | * @brief 倒计时 Button
4 | * @anchor Ho229<2189684957@qq.com>
5 | * @date 2021/2/1
6 | */
7 |
8 | #include "countdownbutton.h"
9 |
10 | #include
11 |
12 | CountdownButton::CountdownButton(QWidget *parent) :
13 | ProgressButton(parent),
14 | m_animation(new QPropertyAnimation(this, "value"))
15 | {
16 | m_animation->setDuration(2000);
17 | m_animation->setStartValue(this->minimun());
18 | m_animation->setEndValue(this->maximun());
19 | QObject::connect(m_animation, &QPropertyAnimation::finished, this,
20 | [this]{
21 | this->setValue(0);
22 | this->click();
23 | });
24 | }
25 |
26 | CountdownButton::~CountdownButton()
27 | {
28 |
29 | }
30 |
31 | void CountdownButton::setCountdown(int ms)
32 | {
33 | m_animation->setDuration(ms);
34 | }
35 |
36 | int CountdownButton::countdown() const
37 | {
38 | return m_animation->duration();
39 | }
40 |
41 | void CountdownButton::conutdownCilk()
42 | {
43 | if(m_animation->state() == QPropertyAnimation::Running)
44 | m_animation->stop();
45 |
46 | m_animation->start();
47 | }
48 |
--------------------------------------------------------------------------------
/QmlDemo/main.cpp:
--------------------------------------------------------------------------------
1 | #include
2 | #include
3 | #include
4 | #include
5 |
6 | #include "clickwaveeffect.h"
7 |
8 | int main(int argc, char *argv[])
9 | {
10 | #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
11 | QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
12 | #endif
13 |
14 | QGuiApplication app(argc, argv);
15 |
16 | #ifdef Q_OS_WIN
17 | QGuiApplication::setFont(QFont("Microsoft YaHei", 12));
18 | #endif
19 |
20 | QTranslator tr_CN;
21 | if(QLocale::system().language() == QLocale::Chinese)
22 | {
23 | tr_CN.load(":/translate/QmlDemo_zh_CN.qm");
24 | app.installTranslator(&tr_CN);
25 | }
26 |
27 | qmlRegisterType("com.MyItems.Effect", 1, 0, "ClickWaveEffect");
28 |
29 | QQmlApplicationEngine engine;
30 | const QUrl url(QStringLiteral("qrc:/main.qml"));
31 | QObject::connect(&engine, &QQmlApplicationEngine::objectCreated,
32 | &app, [url](QObject *obj, const QUrl &objUrl) {
33 | if (!obj && url == objUrl)
34 | QCoreApplication::exit(-1);
35 | }, Qt::QueuedConnection);
36 | engine.load(url);
37 |
38 | return app.exec();
39 | }
40 |
--------------------------------------------------------------------------------
/README.CN.md:
--------------------------------------------------------------------------------
1 | # QtDemos
2 |
3 | [English](./README.md) | 简体中文
4 |
5 | * 这是一个关于 `Qt5` 的项目实践,未来会添加一些控件。欢迎大家留言评论,参考学习和测试。
6 |
7 | | 名称 | 描述 |
8 | | ---- | ---------------- |
9 | | [CustomWidgetDemos](./CustomWidgetDemos) | 自定义动画控件和使用示例 |
10 | | [MultithreadedDownloader](./MultithreadedDownloader) | 多线程下载器 |
11 | | [QmlDemo](./QmlDemo) | QML 学习项目 |
12 | | [QmlFireworks](./QmlFireworks) | QML 烟花 (粒子系统) Demo |
13 | | [VideoPlayer](./VideoPlayer) | 视频播放器 (使用了 `FFmpeg` 和 `OpenGL`) |
14 |
15 | ## 开发环境
16 |
17 | * 工具集 : `Qt 5.15.2`
18 | * 编译器 : `Microsoft Visual C++ 2019` , `GCC 10`
19 |
20 | ## 如何编译
21 |
22 | * 需要 `Qt5` 和 `FFmpeg` 环境。
23 |
24 | * 安装 Qt5。
25 |
26 | * 安装 FFmpeg。
27 | * Windows
28 |
29 | 下载 [FFmpeg(shared libraries)](https://github.com/BtbN/FFmpeg-Builds/releases),将库路径添加到系统环境变量 `FFMPEG_PATH`。
30 |
31 | * Linux
32 |
33 | ```shell
34 | sudo pacman -S ffmpeg # Arch
35 | sudo apt install ffmpeg libavfilter-dev # Debian
36 | ```
37 |
38 | * 克隆仓库到本地并编译项目。
39 |
40 | ```shell
41 | git clone https://github.com/ho-229/QtDemos.git
42 | # or https://gitee.com/ho229/QtDemos.git
43 | cd QtDemos
44 | mkdir build
45 | cd build
46 | qmake ..
47 | make -j
48 | ```
49 |
--------------------------------------------------------------------------------
/CustomWidgetDemos/src/customWidgets/progressdial.h:
--------------------------------------------------------------------------------
1 | /**
2 | * Progress Dial
3 | * @brief 进度圆盘按钮
4 | * @anchor Ho229
5 | * @date 2021/2/24
6 | */
7 |
8 | #ifndef PROGRESSDIAL_H
9 | #define PROGRESSDIAL_H
10 |
11 | #include
12 | #include
13 |
14 | #define TransAngle(x) (x * 16)
15 |
16 | class ProgressDial : public QDial
17 | {
18 | Q_OBJECT
19 | public:
20 | explicit ProgressDial(QWidget *parent = nullptr);
21 | virtual ~ProgressDial() Q_DECL_OVERRIDE;
22 |
23 | /**
24 | * @brief 设置内圈颜色
25 | * @param color
26 | */
27 | void setProgressColor(const QColor color){ m_pen.setColor(color); }
28 |
29 | /**
30 | * @return 内圈颜色
31 | */
32 | QColor progressColor() const { return m_pen.color(); }
33 |
34 | /**
35 | * @brief 设置内圈宽度
36 | * @param width
37 | */
38 | void setProgressWidth(qreal width){ m_pen.setWidthF(width); }
39 |
40 | /**
41 | * @return 内圈宽度
42 | */
43 | qreal progressWidth() const { return m_pen.widthF(); }
44 |
45 | protected:
46 | virtual void paintEvent(QPaintEvent *event) Q_DECL_OVERRIDE;
47 |
48 | private:
49 | QPen m_pen;
50 |
51 | inline void drawProgress(int start, int angle);
52 |
53 | };
54 |
55 | #endif // PROGRESSDIAL_H
56 |
--------------------------------------------------------------------------------
/VideoPlayer/src/player/audiooutput.h:
--------------------------------------------------------------------------------
1 | /**
2 | * @brief Audio Output
3 | * @anchor Ho 229
4 | * @date 2021/5/1
5 | */
6 |
7 | #ifndef AUDIOOUTPUT_H
8 | #define AUDIOOUTPUT_H
9 |
10 | #include
11 |
12 | #include
13 | #include
14 |
15 | class QAudioOutput;
16 | class AudioDevice;
17 |
18 | class AudioOutput : public QObject
19 | {
20 | Q_OBJECT
21 | public:
22 | using Callback = std::function;
23 |
24 | explicit AudioOutput(const Callback &callback, QObject *parent = nullptr);
25 | ~AudioOutput() Q_DECL_OVERRIDE;
26 |
27 | /**
28 | * @brief Update audio output when the audio format changed
29 | */
30 | void updateAudioOutput(const QAudioFormat &format);
31 |
32 | void setVolume(qreal volume);
33 | qreal volume() const;
34 |
35 | void play();
36 | void pause();
37 | void stop();
38 |
39 | /**
40 | * @brief Reset the buffer of audio output
41 | */
42 | void reset();
43 |
44 | bool isLowDataLeft() const;
45 | qreal bufferDuration() const;
46 |
47 | private:
48 | QAudioOutput *m_output = nullptr;
49 | AudioDevice *m_audioDevice = nullptr;
50 | qreal m_bufferDuration = 0;
51 | };
52 |
53 | #endif // AUDIOOUTPUT_H
54 |
--------------------------------------------------------------------------------
/CustomWidgetDemos/src/mainwidget.h:
--------------------------------------------------------------------------------
1 | /**
2 | * @brief Demo
3 | * @anchor Ho229
4 | * @date 2020/12/12
5 | */
6 |
7 | #ifndef MAINWIDGET_H
8 | #define MAINWIDGET_H
9 |
10 | #include
11 |
12 | #include "customWidgets/toast.h"
13 | #include "customWidgets/notifymanager.h"
14 |
15 | class QTranslator;
16 | class QButtonGroup;
17 | class QAbstractButton;
18 |
19 | QT_BEGIN_NAMESPACE
20 | namespace Ui { class MainWidget; }
21 | QT_END_NAMESPACE
22 |
23 | class MainWidget : public QWidget
24 | {
25 | Q_OBJECT
26 |
27 | public:
28 | MainWidget(QWidget *parent = nullptr);
29 | ~MainWidget() Q_DECL_OVERRIDE;
30 |
31 | private slots:
32 | void on_toastBtn_clicked();
33 |
34 | void on_buttonClicked(int id);
35 |
36 | void on_countdownStartBtn_clicked();
37 |
38 | void on_countdownBtn_clicked();
39 |
40 | void on_notifyBtn_clicked();
41 |
42 | private:
43 | Ui::MainWidget *ui;
44 |
45 | Toast *m_toast = nullptr;
46 |
47 | QButtonGroup *m_buttonGroup_1 = nullptr;
48 | QButtonGroup *m_buttonGroup_2 = nullptr;
49 | QButtonGroup *m_langGroup = nullptr;
50 |
51 | NotifyManager *m_notifyManager = nullptr;
52 |
53 | QTranslator *m_trans = nullptr;
54 |
55 | inline void initUI();
56 | };
57 | #endif // MAINWIDGET_H
58 |
--------------------------------------------------------------------------------
/MultithreadedDownloader/src/mainwidget.h:
--------------------------------------------------------------------------------
1 | /**
2 | * @brief MainWidget
3 | * @anchor Ho229<2189684957@qq.com>
4 | * @date 2021/2/1
5 | */
6 |
7 | #ifndef MAINWIDGET_H
8 | #define MAINWIDGET_H
9 |
10 | #include "multithreadeddownloader.h"
11 |
12 | #include "toast.h"
13 |
14 | #include
15 |
16 | QT_BEGIN_NAMESPACE
17 | namespace Ui { class MainWidget; }
18 | QT_END_NAMESPACE
19 |
20 | class MainWidget : public QWidget
21 | {
22 | Q_OBJECT
23 |
24 | public:
25 | MainWidget(QWidget *parent = nullptr);
26 | ~MainWidget() Q_DECL_OVERRIDE;
27 |
28 | void initThreadNumber(int num)
29 | {
30 | if(num > 0)
31 | m_downloader->setThreadCount(num);
32 | }
33 |
34 | void initDownloadUrl(const QString& url);
35 |
36 | private slots:
37 | void on_downloadBtn_clicked();
38 |
39 | void on_startBtn_clicked();
40 |
41 | void on_pauseBtn_clicked();
42 |
43 | void on_stopBtn_clicked();
44 |
45 | private:
46 | Ui::MainWidget *ui;
47 |
48 | MultithreadedDownloader* m_downloader = nullptr;
49 |
50 | Toast *m_toast = nullptr;
51 |
52 | qint64 m_old_progressed_bytes;
53 |
54 | qint64 m_oldProgressedBytes;
55 |
56 | inline void initUI();
57 | inline void initSignalSlots();
58 | };
59 | #endif // MAINWIDGET_H
60 |
--------------------------------------------------------------------------------
/VideoPlayer/qml/Toast.qml:
--------------------------------------------------------------------------------
1 | import QtQuick 2.15
2 |
3 | Rectangle {
4 | id: myToast
5 |
6 | visible: false
7 |
8 | property bool closeAvailable: true
9 |
10 | function toast() {
11 | if(showAnimation.running)
12 | return;
13 | else if(pauseTimer.running)
14 | {
15 | pauseTimer.restart();
16 | return;
17 | }
18 | else if(hideAnimation.running)
19 | hideAnimation.stop();
20 |
21 | opacity = 0;
22 | visible = true;
23 | showAnimation.start();
24 | }
25 |
26 | OpacityAnimator {
27 | id: showAnimation
28 |
29 | target: myToast;
30 | from: 0
31 | to: 1
32 | duration: 300
33 |
34 | onFinished: pauseTimer.start();
35 | }
36 |
37 | OpacityAnimator {
38 | id: hideAnimation
39 |
40 | target: myToast
41 | from: 1
42 | to: 0
43 | duration: 300
44 |
45 | onFinished: visible = false
46 | }
47 |
48 | Timer {
49 | id: pauseTimer
50 |
51 | interval: 3000
52 |
53 | onTriggered: {
54 | if(!closeAvailable)
55 | pauseTimer.restart();
56 | else
57 | hideAnimation.start()
58 | }
59 | }
60 | }
61 |
--------------------------------------------------------------------------------
/MultithreadedDownloader/src/downloader/multithreadeddownloaderwriter.cpp:
--------------------------------------------------------------------------------
1 | /**
2 | * @brief MultithreadedDownloaderWriter
3 | * @anchor Ho229<2189684957@qq.com>
4 | * @date 2021/2/1
5 | */
6 |
7 | #include "multithreadeddownloaderwriter.h"
8 |
9 | MultithreadedDownloaderWriter::MultithreadedDownloaderWriter(QObject *parent)
10 | : QThread(parent)
11 | {
12 | QDir::setCurrent("./");
13 | }
14 |
15 | MultithreadedDownloaderWriter::~MultithreadedDownloaderWriter()
16 | {
17 | if(m_downloadFile.isOpen())
18 | m_downloadFile.close();
19 | }
20 |
21 | void MultithreadedDownloaderWriter::write(const QByteArray& data, const qint64 seek)
22 | {
23 | WriteMisson newMisson = { data, seek };
24 |
25 | m_mutex.lock();
26 | m_writeList.push_back(newMisson);
27 | m_mutex.unlock();
28 |
29 | if(!this->isRunning()) // Stopped
30 | this->start();
31 | }
32 |
33 | void MultithreadedDownloaderWriter::run()
34 | {
35 | do
36 | {
37 | m_downloadFile.seek(m_writeList.first().second);
38 | m_downloadFile.write(m_writeList.first().first);
39 |
40 | m_mutex.lock();
41 | m_writeList.pop_front();
42 | m_mutex.unlock();
43 |
44 | if(m_writeList.isEmpty()) // Hold on !!!
45 | this->msleep(200);
46 | }
47 | while(!m_writeList.isEmpty());
48 | }
49 |
--------------------------------------------------------------------------------
/CustomWidgetDemos/src/customWidgets/progressdial.cpp:
--------------------------------------------------------------------------------
1 | /**
2 | * Progress Dial
3 | * @brief 进度圆盘按钮
4 | * @anchor Ho229
5 | * @date 2021/2/24
6 | */
7 |
8 | #include "progressdial.h"
9 |
10 | #include
11 |
12 | ProgressDial::ProgressDial(QWidget *parent) : QDial(parent)
13 | {
14 | // init Pen
15 | m_pen.setWidth(5);
16 | m_pen.setColor(QColor(55, 120, 249));
17 | }
18 |
19 | ProgressDial::~ProgressDial()
20 | {
21 |
22 | }
23 |
24 | void ProgressDial::paintEvent(QPaintEvent *event)
25 | {
26 | QDial::paintEvent(event);
27 |
28 | if(this->wrapping())
29 | this->drawProgress(-90, 360);
30 | else
31 | this->drawProgress(-120, 290);
32 | }
33 |
34 | void ProgressDial::drawProgress(int start, int angle)
35 | {
36 | int drawAngle = static_cast(
37 | static_cast(this->value()) / (this->maximum() - this->minimum()) * angle);
38 | if(drawAngle <= 0)
39 | return;
40 |
41 | QPainter painter(this);
42 | painter.setRenderHint(QPainter::Antialiasing);
43 | painter.setPen(m_pen);
44 |
45 | QRect rect;
46 | int min = qMin(this->width(), this->height()) - m_pen.width() * 2;
47 | min -= min / 6;
48 | rect.setSize(QSize(min, min));
49 | rect.moveCenter(this->rect().center());
50 |
51 | painter.drawArc(rect, TransAngle(start), TransAngle(start - drawAngle));
52 | }
53 |
--------------------------------------------------------------------------------
/VideoPlayer/VideoPlayer.pro:
--------------------------------------------------------------------------------
1 | QT += quick multimedia
2 |
3 | CONFIG += c++11
4 |
5 | # You can make your code fail to compile if it uses deprecated APIs.
6 | # In order to do so, uncomment the following line.
7 | #DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0
8 |
9 | RESOURCES += $$_PRO_FILE_PWD_/qml/qml.qrc \
10 | resource/res.qrc \
11 | shaders/shaders.qrc
12 |
13 | TRANSLATIONS += \
14 | VideoPlayer_zh_CN.ts
15 |
16 | include($$_PRO_FILE_PWD_/src/src.pri)
17 |
18 | # Additional import path used to resolve QML modules in Qt Creator's code model
19 | QML_IMPORT_PATH =
20 |
21 | # Additional import path used to resolve QML modules just for Qt Quick Designer
22 | QML_DESIGNER_IMPORT_PATH =
23 |
24 | # Default rules for deployment.
25 | qnx: target.path = /tmp/$${TARGET}/bin
26 | else: unix:!android: target.path = /opt/$${TARGET}/bin
27 | !isEmpty(target.path): INSTALLS += target
28 |
29 | win32 {
30 | # FFmpeg
31 | LIBS += -L$$(FFMPEG_PATH)/lib/ -lavutil -lavcodec -lavformat -lavfilter -lswresample -lswscale
32 | INCLUDEPATH += $$(FFMPEG_PATH)/include
33 | DEPENDPATH += $$(FFMPEG_PATH)/include
34 | }
35 |
36 | unix {
37 | # FFmpeg
38 | LIBS += -L/usr/lib64/ -lavutil -lavcodec -lavformat -lavfilter -lswresample -lswscale
39 | INCLUDEPATH += /usr/include/ffmpeg
40 | DEPENDPATH += /usr/include/ffmpeg
41 | }
42 |
--------------------------------------------------------------------------------
/CustomWidgetDemos/src/customWidgets/aligniconbutton.cpp:
--------------------------------------------------------------------------------
1 | /**
2 | * @brief 左右 icon 对齐 Push Button
3 | * @anchor Ho229<2189684957@qq.com>
4 | * @date 2021/2/1
5 | */
6 |
7 | #include "aligniconbutton.h"
8 |
9 | #include
10 |
11 | AlignIconButton::AlignIconButton(QWidget *parent) : QPushButton(parent)
12 | {
13 |
14 | }
15 |
16 | AlignIconButton::~AlignIconButton()
17 | {
18 |
19 | }
20 |
21 | void AlignIconButton::paintEvent(QPaintEvent *event)
22 | {
23 | QPushButton::paintEvent(event);
24 |
25 | QPainter painter(this);
26 | this->drawIcon(&painter);
27 | }
28 |
29 | void AlignIconButton::drawIcon(QPainter *painter)
30 | {
31 | QRect rect;
32 | rect.setSize(QSize(this->width() - m_sideMargin * 2,
33 | this->height() - m_topBottomMargin * 2));
34 | rect.moveCenter(this->rect().center());
35 |
36 | if(!m_leftIcon.isNull())
37 | {
38 | m_leftIcon.paint(painter, rect, Qt::AlignLeft,
39 | this->isEnabled() ? QIcon::Normal : QIcon::Disabled,
40 | this->isChecked() ? QIcon::On : QIcon::Off);
41 | }
42 |
43 | if(!m_rightIcon.isNull())
44 | {
45 | m_rightIcon.paint(painter, rect, Qt::AlignRight,
46 | this->isEnabled() ? QIcon::Normal : QIcon::Disabled,
47 | this->isChecked() ? QIcon::On : QIcon::Off);
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/CustomWidgetDemos/src/customWidgets/progressbutton.cpp:
--------------------------------------------------------------------------------
1 | /**
2 | * Progress Button
3 | * @brief 进度条按钮
4 | * @anchor Ho229
5 | * @date 2021/2/22
6 | */
7 |
8 | #include "progressbutton.h"
9 |
10 | #include
11 |
12 | ProgressButton::ProgressButton(QWidget *parent, const QString &style) :
13 | QPushButton(parent)
14 | {
15 | m_pen.setCapStyle(Qt::RoundCap);
16 | m_pen.setWidth(3);
17 | m_pen.setColor(QColor(69, 198, 214));
18 |
19 | this->setStyleSheet(style);
20 | }
21 |
22 | ProgressButton::~ProgressButton()
23 | {
24 |
25 | }
26 |
27 | void ProgressButton::setValue(int value)
28 | {
29 | value = qMin(value, m_maximun);
30 |
31 | if(value == m_value) // No changed
32 | return;
33 |
34 | m_value = value;
35 | emit valueChanged(value);
36 | this->repaint();
37 | }
38 |
39 | void ProgressButton::paintEvent(QPaintEvent *event)
40 | {
41 | QPushButton::paintEvent(event);
42 |
43 | int drawAngle = static_cast(
44 | static_cast(m_value) / (m_maximun - m_minimun) * 360);
45 | if(drawAngle <= 0)
46 | return;
47 |
48 | QPainter painter(this);
49 | painter.setRenderHint(QPainter::Antialiasing);
50 | painter.setPen(m_pen);
51 |
52 | QRect rect;
53 | int min = qMin(this->width(), this->height()) - m_pen.width() * 2;
54 | rect.setSize(QSize(min, min));
55 | rect.moveCenter(this->rect().center());
56 |
57 | painter.drawArc(rect, TransAngle(90), TransAngle(90 - drawAngle));
58 | }
59 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # QtDemos
2 |
3 | English | [简体中文](./README.CN.md)
4 |
5 | * This is a small project about `Qt5`. More controls will be added in the future. Welcome to try it out and leave your comments.
6 |
7 | | Name | Description |
8 | | ---- | ---------------- |
9 | | [CustomWidgetDemos](./CustomWidgetDemos) | Custom animation controls and examples |
10 | | [MultithreadedDownloader](./MultithreadedDownloader) | Multi-threaded Downloader |
11 | | [QmlDemo](./QmlDemo) | QML Learning project |
12 | | [QmlFireworks](./QmlFireworks) | QML Fireworks (Particle System) Demo |
13 | | [VideoPlayer](./VideoPlayer) | Video Player (Use `FFmpeg` and `OpenGL`) |
14 |
15 | ## Development Environment
16 |
17 | * Tool Kit : `Qt 5.15.2`.
18 | * Complier : `Microsoft Visual C++ 2019` , `GCC 10`.
19 |
20 | ## Build
21 |
22 | * Require `Qt5` and `FFmpeg`.
23 |
24 | * Install Qt5.
25 |
26 | * Install FFmpeg.
27 | * Windows
28 |
29 | Download [FFmpeg(shared libraries)](https://github.com/BtbN/FFmpeg-Builds/releases) and add the path to your system environment variable `FFMPEG_PATH`.
30 |
31 | * Linux
32 |
33 | ```shell
34 | sudo pacman -S ffmpeg # Arch
35 | sudo apt install ffmpeg libavfilter-dev # Debian
36 | ```
37 |
38 | * Clone this repository and build from source.
39 |
40 | ```shell
41 | git clone https://github.com/ho-229/QtDemos.git
42 | # or https://gitee.com/ho229/QtDemos.git
43 | cd QtDemos
44 | mkdir build
45 | cd build
46 | qmake ..
47 | make -j
48 | ```
49 |
--------------------------------------------------------------------------------
/MultithreadedDownloader/src/main.cpp:
--------------------------------------------------------------------------------
1 | /**
2 | * @anchor Ho229<2189684957@qq.com>
3 | * @date 2021/2/1
4 | */
5 |
6 | #include "mainwidget.h"
7 |
8 | #include
9 | #include
10 | #include
11 | #include
12 |
13 | int main(int argc, char *argv[])
14 | {
15 | QApplication a(argc, argv);
16 |
17 | #ifdef Q_OS_WIN
18 | a.setFont(QFont("Microsoft YaHei", 9));
19 | #endif
20 |
21 | QTranslator tr_CN;
22 | if(QLocale::system().language() == QLocale::Chinese)
23 | {
24 | tr_CN.load(":/translations/MultithreadedDownloader_zh_CN.qm");
25 | a.installTranslator(&tr_CN);
26 | }
27 |
28 | a.setApplicationName("Multithreaded Downloader");
29 |
30 | QCommandLineOption threadNumOpt({"t", "thread-number"},
31 | "Set the thread number for download.",
32 | "number",
33 | "0");
34 |
35 | QCommandLineOption urlOpt({"u", "url"},
36 | "The URL to download.",
37 | "URL");
38 |
39 | QCommandLineParser parser;
40 | parser.setApplicationDescription("Qt Multithreaded Downloader Example.");
41 | parser.addHelpOption();
42 | parser.addOption(urlOpt);
43 | parser.addOption(threadNumOpt);
44 | parser.process(a);
45 |
46 | MainWidget w;
47 | w.initThreadNumber(parser.value(threadNumOpt).toInt());
48 | w.show();
49 | w.initDownloadUrl(parser.value(urlOpt));
50 | return a.exec();
51 | }
52 |
--------------------------------------------------------------------------------
/CustomWidgetDemos/src/customWidgets/notifymanager.h:
--------------------------------------------------------------------------------
1 | /**
2 | * Notify Manager
3 | * @brief 右下角提示窗管理器
4 | * @anchor Ho229<2189684957@qq.com>
5 | * @date 2021/2/1
6 | */
7 |
8 | #ifndef NOTIFYMANAGER_H
9 | #define NOTIFYMANAGER_H
10 |
11 | #include
12 | #include
13 |
14 | #include "notifywidget.h"
15 |
16 | typedef QPair // Show Time
18 | NotifyItem;
19 |
20 | class NotifyManager Q_DECL_FINAL : public QObject
21 | {
22 | Q_OBJECT
23 | public:
24 | explicit NotifyManager(QObject *parent = nullptr);
25 | ~NotifyManager() Q_DECL_OVERRIDE;
26 |
27 | /**
28 | * @brief 设置最大弹窗数
29 | */
30 | void setMaximum(int value){ m_maximum = qMax(1, value); }
31 | int maximum() const { return m_maximum; }
32 |
33 | /**
34 | * @return 当前弹窗数
35 | */
36 | int showCount() const { return m_showCount; }
37 |
38 | public slots:
39 | /**
40 | * @brief 弹出提示窗
41 | * @param parent 父对象
42 | * @param title 提示窗标题
43 | * @param message 提示消息
44 | * @param showTime 展示时间 ( 为 -1 时永久展示 )
45 | */
46 | NotifyWidget* notify(QWidget *parent, const QString& title, const QString& message,
47 | const QIcon& icon = QIcon(), int showTime = 5000);
48 |
49 | signals:
50 | void windowClosed();
51 |
52 | private slots:
53 | void onNotifyClosed();
54 |
55 | private:
56 | QList m_list;
57 | int m_maximum = 5;
58 | int m_showCount = 0;
59 |
60 | const QSize m_desktopSize;
61 |
62 | void updateNotifys();
63 |
64 | };
65 |
66 | #endif // NOTIFYMANAGER_H
67 |
--------------------------------------------------------------------------------
/MultithreadedDownloader/src/downloader/abstractmission.h:
--------------------------------------------------------------------------------
1 | /**
2 | * @brief AbstractMission
3 | * @anchor Ho229<2189684957@qq.com>
4 | * @date 2021/2/1
5 | */
6 |
7 | #ifndef ABSTRACTMISSION_H
8 | #define ABSTRACTMISSION_H
9 |
10 | #include
11 | #include
12 | #include
13 |
14 | class AbstractMission : public QObject
15 | {
16 | Q_OBJECT
17 | public:
18 | enum State
19 | {
20 | Running,
21 | Paused,
22 | Stopped
23 | };
24 |
25 | virtual ~AbstractMission() Q_DECL_OVERRIDE = default;
26 |
27 | template
28 | void setUrl(const T& url)
29 | {
30 | m_url = url;
31 | if(!m_url.isValid())
32 | qWarning("AbstractMission : url is not valid.");
33 | }
34 |
35 | QUrl url() const { return m_url; }
36 |
37 | bool isFinished() const { return m_isFinished; }
38 |
39 | virtual void start() = 0;
40 | virtual void pause() = 0;
41 | virtual void stop() = 0;
42 |
43 | State state() const { return m_state; }
44 |
45 | protected:
46 | explicit AbstractMission(QObject* parent = nullptr) : QObject(parent) {}
47 |
48 | inline void updateState(const State state)
49 | {
50 | m_state = state;
51 | emit stateChanged(state);
52 | }
53 |
54 | inline void setFinished(const bool isFinished = true)
55 | {
56 | m_isFinished = isFinished;
57 |
58 | if(isFinished)
59 | emit finished();
60 | }
61 |
62 | QUrl m_url;
63 | State m_state = Stopped;
64 | bool m_isFinished = false;
65 |
66 | signals:
67 | void finished();
68 | void stateChanged(const AbstractMission::State state);
69 | };
70 |
71 | #endif // ABSTRACTMISSION_H
72 |
--------------------------------------------------------------------------------
/VideoPlayer/VideoPlayer_zh_CN.ts:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | main
6 |
7 |
8 | Video Player
9 |
10 |
11 |
12 |
13 | Volume:
14 |
15 |
16 |
17 |
18 | Settings
19 |
20 |
21 |
22 |
23 | Audio Track
24 |
25 |
26 |
27 |
28 | <font color='white'>Open file<br>or drop here</font>
29 |
30 |
31 |
32 |
33 |
34 | <font color='white'>Open file<br>Or drop here</font>
35 |
36 |
37 |
38 |
39 | <font color='white'>Release to open file</font>
40 |
41 |
42 |
43 |
44 |
--------------------------------------------------------------------------------
/MultithreadedDownloader/src/downloader/multithreadeddownloaderwriter.h:
--------------------------------------------------------------------------------
1 | /**
2 | * @brief MultithreadedDownloaderWriter
3 | * @anchor Ho229<2189684957@qq.com>
4 | * @date 2021/2/1
5 | */
6 |
7 | #ifndef MULTITHREADEDDOWNLOADERWRITER_H
8 | #define MULTITHREADEDDOWNLOADERWRITER_H
9 |
10 | #include
11 | #include
12 | #include
13 | #include
14 |
15 | typedef QPair WriteMisson;
16 |
17 | class MultithreadedDownloaderWriter Q_DECL_FINAL : public QThread
18 | {
19 | Q_OBJECT
20 | public:
21 | explicit MultithreadedDownloaderWriter(QObject *parent = nullptr);
22 | ~MultithreadedDownloaderWriter() Q_DECL_OVERRIDE;
23 |
24 | /**
25 | * @brief 打开文件
26 | * @return 操作是否成功
27 | */
28 | bool open()
29 | { return m_downloadFile.open(QFile::WriteOnly) && m_downloadFile.resize(m_fileSize); }
30 |
31 | /**
32 | * @brief 关闭文件
33 | */
34 | void close()
35 | { m_downloadFile.close(); }
36 |
37 | /**
38 | * @brief 设置下载文件名
39 | * @param name 下载文件名
40 | */
41 | void setFileName(const QString& name){ m_downloadFile.setFileName(name); }
42 | QString fileName() const { return m_downloadFile.fileName(); }
43 |
44 | /**
45 | * @brief 设置下载文件大小
46 | * @param size 下载文件大小 byte
47 | */
48 | void setSize(const qint64 size){ m_fileSize = size; }
49 | qint64 size() const { return m_fileSize; }
50 |
51 | /**
52 | * @brief 添加写任务
53 | * @param data 写入数据
54 | * @param seek 写入偏移量
55 | */
56 | void write(const QByteArray& data, const qint64 seek);
57 |
58 | private:
59 | void run() Q_DECL_OVERRIDE;
60 |
61 | qint64 m_fileSize = 0;
62 | QFile m_downloadFile;
63 |
64 | mutable QMutex m_mutex;
65 | QList m_writeList;
66 | };
67 |
68 | #endif // MULTITHREADEDDOWNLOADERWRITER_H
69 |
--------------------------------------------------------------------------------
/VideoPlayer/src/main.cpp:
--------------------------------------------------------------------------------
1 | /**
2 | * @brief Main
3 | * @anchor Ho 229
4 | * @date 2021/4/10
5 | */
6 |
7 | #include
8 | #include
9 | #include
10 |
11 | #include "videoplayer.h"
12 | #include "keyboardcontrollor.h"
13 |
14 | int main(int argc, char *argv[])
15 | {
16 | #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
17 | QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
18 | #endif
19 |
20 | QGuiApplication app(argc, argv);
21 |
22 | #ifdef Q_OS_WIN
23 | app.setFont(QFont("Microsoft YaHei", 9));
24 | #endif
25 |
26 | app.setOrganizationName("Ho229");
27 | app.setOrganizationDomain("https://github.com/ho229v3666/");
28 |
29 | qmlRegisterType("com.multimedia.videoplayer", 1, 0, "VideoPlayer");
30 |
31 | KeyboardControllor qmlKey;
32 | QQmlApplicationEngine engine;
33 | const QUrl url(QStringLiteral("qrc:/main.qml"));
34 | QObject::connect(&engine, &QQmlApplicationEngine::objectCreated,
35 | &app, [url](QObject *obj, const QUrl &objUrl) {
36 | if (!obj && url == objUrl)
37 | QCoreApplication::exit(-1);
38 | }, Qt::QueuedConnection);
39 |
40 | engine.load(url);
41 |
42 | engine.rootContext()->setContextProperty("qmlKey", &qmlKey);
43 |
44 | const QList objList = engine.rootObjects();
45 | objList.first()->installEventFilter(&qmlKey);
46 |
47 | QObject::connect(&qmlKey, SIGNAL(play()), objList.first(),
48 | SLOT(onPlay()));
49 | QObject::connect(&qmlKey, SIGNAL(back()), objList.first(),
50 | SLOT(onBack()));
51 | QObject::connect(&qmlKey, SIGNAL(goahead()), objList.first(),
52 | SLOT(onGoahead()));
53 | QObject::connect(&qmlKey, SIGNAL(escape()), objList.first(),
54 | SLOT(onEscape()));
55 |
56 | return app.exec();
57 | }
58 |
--------------------------------------------------------------------------------
/VideoPlayer/src/player/videoplayer_p.h:
--------------------------------------------------------------------------------
1 | /**
2 | * @brief Video Player Private
3 | * @anchor Ho 229
4 | * @date 2021/4/24
5 | */
6 |
7 | #ifndef VIDEOPLAYERPRIVATE_H
8 | #define VIDEOPLAYERPRIVATE_H
9 |
10 | #include "videoplayer.h"
11 |
12 | #include
13 |
14 | struct AVFrame;
15 |
16 | class AudioOutput;
17 | class FFmpegDecoder;
18 | class VideoRenderer;
19 |
20 | class Clock
21 | {
22 | qreal m_time = 0;
23 | QElapsedTimer m_updateClock;
24 | public:
25 | explicit Clock() = default;
26 |
27 | qreal time() const { return m_time + qreal(m_updateClock.elapsed()) / 1000; }
28 | bool isValid() const { return m_updateClock.isValid(); }
29 |
30 | void update(qreal time)
31 | {
32 | m_time = time;
33 | m_updateClock.start();
34 | }
35 |
36 | void pause()
37 | {
38 | if(m_updateClock.isValid())
39 | m_time += qreal(m_updateClock.elapsed()) / 1000;
40 | }
41 | void resume() { m_updateClock.start(); }
42 |
43 | void invalidate() { m_updateClock.invalidate(); }
44 | };
45 |
46 | class VideoPlayerPrivate
47 | {
48 | public:
49 | VideoPlayerPrivate(VideoPlayer *parent) : q_ptr(parent) {}
50 |
51 | FFmpegDecoder *decoder = nullptr;
52 |
53 | AudioOutput *audioOutput = nullptr;
54 | VideoRenderer *videoRenderer = nullptr;
55 |
56 | VideoPlayer::State state = VideoPlayer::Stopped;
57 |
58 | int position = 0;
59 |
60 | int interval = 0;
61 | int timerId = -1;
62 |
63 | AVFrame *audioFrame = nullptr;
64 | qint64 audioFramePos = 0;
65 |
66 | void restartAudioOutput();
67 |
68 | Clock videoClock;
69 | Clock audioClock;
70 |
71 | qint64 updateAudioData(char *data, qint64 maxlen);
72 | void updateVideoFrame();
73 | void updateSubtitleFrame();
74 |
75 | private:
76 | inline void updateTimer(int newInterval);
77 |
78 | VideoPlayer *const q_ptr;
79 | Q_DECLARE_PUBLIC(VideoPlayer)
80 | };
81 |
82 | #endif // VIDEOPLAYERPRIVATE_H
83 |
--------------------------------------------------------------------------------
/MultithreadedDownloader/README.md:
--------------------------------------------------------------------------------
1 | # MultithreadedDownloader
2 | > *我想凭时间的有效利用去弥补匆匆流逝的光阴。*
3 | > *—— 蒙田《蒙田随笔》*
4 |
5 | 
6 | 
7 |
8 | ------
9 | ### 使用方法
10 | * Qt Multithreaded Downloader Example
11 | ```
12 | Usage: MultithreadedDownloader [options]
13 | Qt Multithreaded Downloader Example.
14 |
15 | Options:
16 | -?, -h, --help Displays help on commandline options.
17 | --help-all Displays help including Qt specific options.
18 | -u, --url The URL to download.
19 | -t, --thread-number Set the thread number for download.
20 | ```
21 | * Class MultithreadedDownloader
22 | * Add [src/downloader](./src/downloader) directory to your project.
23 | * `#include "multithreadeddownloader.h"`
24 | ```cpp
25 | MultithreadedDownloader *downloader = new MultithreadedDownloader(this);
26 | downloader->setUrl("https://mirrors.tuna.tsinghua.edu.cn/qt/official_releases/qt/5.15/5.15.2/submodules/qtbase-everywhere-src-5.15.2.zip");
27 |
28 | if(!downloader->load())
29 | return; // Failed to get resource.
30 |
31 | QDir::setCurrent("./"); // Set download path
32 | downloader->start(); // Start download
33 | ```
34 | ------
35 | ### 原理介绍
36 | * 这是一个基于 `Qt5` 的多线程下载器。
37 | * `Class AbstractMission` 是一个任务抽象类。
38 | * `Class DownloadMission` 继承于 `AbstractMission` ,是一个下载任务类,它提供 `setRange()` 以用于分段下载,还实现了 `start()`,`pause()`,`stop()` 等任务控制函数。它的内部维护着一个 `QNetworkReply` 对象,当 `QNetworkReply::readyRead` 信号发射时,它会读出 reply 的缓冲区数据并调用 `MultithreadedDownloaderWriter::write()` 将一个写请求添加到处理队列。
39 | * `Class MultithreadedDownloader` 继承于 `AbstractMission` ,管理着多个 `DownloadMission` 以实现文件分段并发下载,它也实现了 `start()`,`pause()`,`stop()` 等函数以控制多个 `DownloadMission`。分段下载(并发)数默认为 `QThread::idealThreadCount()` (CPU核心数)。它还管理着一个 `MultithreadedDownloaderWriter`。
40 | * `Class MultithreadedDownloaderWriter` 继承于 `QThread` ,它负责文件写入,同时管理着写线程和下载的文件。
41 | --------
42 | *大概就这么多吧,不知道有没有人看,qwq。*
--------------------------------------------------------------------------------
/MultithreadedDownloader/src/customWidgets/toast.h:
--------------------------------------------------------------------------------
1 | /**
2 | * Toast Widget
3 | * @brief 自动消失提示框
4 | * @anchor Ho229
5 | * @date 2020/12/12
6 | */
7 |
8 | #ifndef TOAST_H
9 | #define TOAST_H
10 |
11 | #include
12 |
13 | class QTimer;
14 | class QHBoxLayout;
15 | class QPropertyAnimation;
16 | class QSequentialAnimationGroup;
17 |
18 | #define DEFULT_TOAST_STYLE "\
19 | QLabel{\
20 | color:#FFFFFF;\
21 | font:15px;\
22 | font-weight:500;\
23 | background-color:rgba(0,0,0,150);\
24 | padding:3px;\
25 | border-radius:9;\
26 | }"\
27 |
28 | class Toast : public QWidget
29 | {
30 | Q_OBJECT
31 | public:
32 |
33 | /**
34 | * @brief Toast
35 | * @param parent 父对象
36 | * @param horizontalMargin 水平方向的边界
37 | * @param verticalMargin 竖直方向上的边界
38 | * @param maxmaximumWidth 最大宽度
39 | * @param wordWrap 启用自动换行
40 | * @param waitMsece 等待时间
41 | * @param style 提示框样式表:注意字体大小和宽高效果要配合好
42 | */
43 | explicit Toast(QWidget *parent = nullptr, int horizontalMargin = 12, int verticalMargin = 12,
44 | int maxmaximumWidth = 1400, bool wordWrap = false, int waitMsecs = 1200,
45 | const QString &style = DEFULT_TOAST_STYLE);
46 | ~Toast();
47 |
48 | /**
49 | * @brief 设置提示文字
50 | * @param text 提示文字
51 | */
52 | void setText(const QString& text);
53 |
54 | /**
55 | * @brief 弹出提示
56 | * @warning 此函数不会重新调整弹出位置
57 | */
58 | void toast();
59 |
60 | /**
61 | * @brief 弹出提示
62 | * @param text 提示文字
63 | */
64 | void toast(const QString& text)
65 | {
66 | this->setText(text);
67 | this->toast();
68 | }
69 |
70 | private:
71 | QLabel *m_messageLabel = nullptr;
72 | QHBoxLayout *m_layout = nullptr;
73 |
74 | QSequentialAnimationGroup *m_animation = nullptr;
75 | QPropertyAnimation *m_posAnimation = nullptr; // 弹出动画
76 | QPropertyAnimation *m_opacityAnimation = nullptr; // 消失动画
77 |
78 | };
79 |
80 | #endif // TOAST_H
81 |
--------------------------------------------------------------------------------
/CustomWidgetDemos/src/customWidgets/toast.h:
--------------------------------------------------------------------------------
1 | /**
2 | * Toast Widget
3 | * @brief 自动消失提示框
4 | * @anchor Ho229
5 | * @date 2020/12/12
6 | */
7 |
8 | #ifndef TOAST_H
9 | #define TOAST_H
10 |
11 | #include
12 |
13 | class QTimer;
14 | class QHBoxLayout;
15 | class QPropertyAnimation;
16 | class QSequentialAnimationGroup;
17 |
18 | #define DEFULT_TOAST_STYLE "\
19 | QLabel\
20 | {\
21 | color:#FFFFFF;\
22 | font:15px;\
23 | font-weight:500;\
24 | background-color:rgba(0,0,0,150);\
25 | padding:3px;\
26 | border-radius:9;\
27 | }"
28 |
29 | class Toast Q_DECL_FINAL : public QWidget
30 | {
31 | Q_OBJECT
32 | public:
33 |
34 | /**
35 | * @brief Toast
36 | * @param parent 父对象
37 | * @param horizontalMargin 水平方向的边界
38 | * @param verticalMargin 竖直方向上的边界
39 | * @param maxmaximumWidth 最大宽度
40 | * @param wordWrap 启用自动换行
41 | * @param waitMsece 等待时间
42 | * @param style 提示框样式表:注意字体大小和宽高效果要配合好
43 | */
44 | explicit Toast(QWidget *parent = nullptr, int horizontalMargin = 12, int verticalMargin = 12,
45 | int maxmaximumWidth = 1400, bool wordWrap = false, int waitMsecs = 1200,
46 | const QString &style = DEFULT_TOAST_STYLE);
47 | ~Toast() Q_DECL_OVERRIDE;
48 |
49 | /**
50 | * @brief 设置提示文字
51 | * @param text 提示文字
52 | */
53 | void setText(const QString& text);
54 |
55 | /**
56 | * @brief 弹出提示
57 | * @warning 此函数不会重新调整弹出位置
58 | */
59 | void toast();
60 |
61 | /**
62 | * @brief 弹出提示
63 | * @param text 提示文字
64 | */
65 | void toast(const QString& text)
66 | {
67 | this->setText(text);
68 | this->toast();
69 | }
70 |
71 | private:
72 | QLabel *m_messageLabel = nullptr;
73 | QHBoxLayout *m_layout = nullptr;
74 |
75 | QSequentialAnimationGroup *m_animation = nullptr;
76 | QPropertyAnimation *m_posAnimation = nullptr; // 弹出动画
77 | QPropertyAnimation *m_opacityAnimation = nullptr; // 消失动画
78 |
79 | };
80 |
81 | #endif // TOAST_H
82 |
--------------------------------------------------------------------------------
/QmlDemo/QmlDemo_zh_CN.ts:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | main
6 |
7 |
8 |
9 | Hello World
10 | 你好,世界
11 |
12 |
13 |
14 | Font Style
15 | 字体样式
16 |
17 |
18 |
19 | Underline
20 | 下划线
21 |
22 |
23 |
24 | Italic
25 | 斜体
26 |
27 |
28 |
29 | Bold
30 | 粗体
31 |
32 |
33 |
34 | Color
35 | 颜色
36 |
37 |
38 |
39 | Black
40 | 黑色
41 |
42 |
43 |
44 | Red
45 | 红色
46 |
47 |
48 |
49 | Blud
50 | 蓝色
51 |
52 |
53 |
54 | Push Button
55 |
56 |
57 |
58 |
59 | Close
60 | 关闭
61 |
62 |
63 |
64 |
--------------------------------------------------------------------------------
/VideoPlayer/src/player/videorenderer.h:
--------------------------------------------------------------------------------
1 | /**
2 | * @brief Video Renderer
3 | * @anchor Ho 229
4 | * @date 2021/4/14
5 | */
6 |
7 | #ifndef VIDEORENDERER_H
8 | #define VIDEORENDERER_H
9 |
10 | #include "ffmpegdecoder.h"
11 |
12 | #include
13 | #include
14 | #include
15 | #include
16 | #include
17 | #include
18 | #include
19 |
20 | struct AVFrame;
21 |
22 | class QOpenGLTexture;
23 | class VideoPlayerPrivate;
24 |
25 | class VideoRenderer : public QQuickFramebufferObject::Renderer,
26 | protected QOpenGLFunctions_4_4_Core
27 | {
28 | public:
29 | VideoRenderer();
30 | ~VideoRenderer() Q_DECL_OVERRIDE;
31 |
32 | void render() Q_DECL_OVERRIDE;
33 |
34 | QOpenGLFramebufferObject *createFramebufferObject(
35 | const QSize &size) Q_DECL_OVERRIDE;
36 |
37 | void synchronize(QQuickFramebufferObject *) Q_DECL_OVERRIDE;
38 |
39 | void updateVideoFrame(AVFrame *frame);
40 | void updateSubtitleFrame(SubtitleFrame *frame);
41 |
42 | private:
43 | QOpenGLTexture *m_texture[4] = { nullptr }; // [0]: Y, [1]: U, [2]: V, [3]: Subtitle
44 |
45 | QOpenGLBuffer m_vbo;
46 | QOpenGLVertexArrayObject m_vao;
47 |
48 | QOpenGLShaderProgram m_program;
49 |
50 | QSize m_size, m_videoSize;
51 | QRect m_viewRect;
52 | QOpenGLTexture::PixelFormat m_pixelFormat;
53 |
54 | quint8 m_flags;
55 |
56 | AVFrame *m_frame = nullptr;
57 | SubtitleFrame *m_subtitle = nullptr;
58 | QScopedArrayPointer m_dummySubtitle;
59 |
60 | bool m_textureAlloced = false;
61 |
62 | void updateVideoTextureData();
63 | void updateSubtitleTextureData();
64 |
65 | void resize();
66 |
67 | void initializeProgram();
68 |
69 | void setupTexture();
70 | void allocateTexture(const QSize sizes[3]);
71 | void updateSubtitleTexture(const QSize &size);
72 |
73 | void destoryTexture();
74 | };
75 |
76 | #endif // VIDEORENDERER_H
77 |
--------------------------------------------------------------------------------
/CustomWidgetDemos/src/customWidgets/rotatestackedwidget.h:
--------------------------------------------------------------------------------
1 | /**
2 | * Animation Stacked Widget
3 | * @brief 翻转动画 Stacked Widget
4 | * @anchor Ho229
5 | * @date 2020/12/12
6 | */
7 |
8 | #ifndef ROTATESTACKEDWIDGET_H
9 | #define ROTATESTACKEDWIDGET_H
10 |
11 | #include
12 | #include
13 |
14 | class QVariantAnimation;
15 |
16 | class RotateStackedWidget Q_DECL_FINAL : public QStackedWidget
17 | {
18 | Q_OBJECT
19 |
20 | Q_PROPERTY(int animationDuration READ animationDuration WRITE setAnimationDuration)
21 | Q_PROPERTY(QEasingCurve animationEasingCurve READ animationEasingCurve WRITE setAnimationEasingCurve)
22 |
23 | public:
24 |
25 | /**
26 | * @brief RotateStackedWidget
27 | * @param parent 父对象
28 | */
29 | explicit RotateStackedWidget(QWidget *parent = nullptr);
30 | ~RotateStackedWidget() Q_DECL_OVERRIDE;
31 |
32 | /**
33 | * @brief 翻转到 index
34 | * @param index 目标 index
35 | * @param exec 是否启用局部事件循环
36 | */
37 | void rotate(int index, bool exec = true);
38 |
39 | /**
40 | * @brief 设置动画持续时间
41 | * @param duration 持续时间(msecs)
42 | */
43 | void setAnimationDuration(int duration);
44 |
45 | /**
46 | * @return 动画持续时间
47 | */
48 | int animationDuration();
49 |
50 | /**
51 | * @brief 设置动画缓和曲线
52 | * @param easing 动画缓和曲线
53 | */
54 | void setAnimationEasingCurve(const QEasingCurve& easing);
55 | /**
56 | * @return 动画缓和曲线
57 | */
58 | QEasingCurve animationEasingCurve();
59 |
60 | private slots:
61 | void on_finished();
62 |
63 | private:
64 | QVariantAnimation *m_animation = nullptr;
65 |
66 | int m_nextIndex;
67 | qreal m_rotateValue = 0;
68 |
69 | QPixmap m_currentPixmap;
70 | QPixmap m_nextPixmap;
71 |
72 | QWidget *m_currentWidget = nullptr;
73 | QWidget *m_nextWidget = nullptr;
74 |
75 | inline void updateFrame();
76 |
77 | void paintEvent(QPaintEvent *event) Q_DECL_OVERRIDE;
78 | void resizeEvent(QResizeEvent *event) Q_DECL_OVERRIDE;
79 | };
80 |
81 | #endif // ROTATESTACKEDWIDGET_H
82 |
--------------------------------------------------------------------------------
/CustomWidgetDemos/src/customWidgets/notifywidget.h:
--------------------------------------------------------------------------------
1 | /**
2 | * Notify Widget
3 | * @brief 右下角提示窗
4 | * @anchor Ho229<2189684957@qq.com>
5 | * @date 2021/2/1
6 | */
7 |
8 | #ifndef NOTIFYWIDGET_H
9 | #define NOTIFYWIDGET_H
10 |
11 | #include
12 | #include
13 |
14 | class QLabel;
15 | class QHBoxLayout;
16 | class QVBoxLayout;
17 | class QSpacerItem;
18 | class CountdownButton;
19 | class QPropertyAnimation;
20 |
21 | #define DEFULT_NOTIFY_STYLE "\
22 | NotifyWidget\
23 | {\
24 | background:rgb(46, 47, 48);\
25 | border-style:none;\
26 | }\
27 | QLabel\
28 | {\
29 | color:white;\
30 | }"
31 |
32 | class NotifyWidget Q_DECL_FINAL : public QWidget
33 | {
34 | Q_OBJECT
35 | public:
36 | explicit NotifyWidget(QWidget *parent = nullptr, const QString& title = "",
37 | const QString& message = "", const QIcon& icon = QIcon(),
38 | const QString& style = DEFULT_NOTIFY_STYLE);
39 | ~NotifyWidget() Q_DECL_OVERRIDE;
40 |
41 | void setCloseCountdown(int ms);
42 | int closeCountdown() const;
43 |
44 | void animatMove(int x, int y);
45 |
46 | bool isClosing() const { return m_isClosing; }
47 |
48 | QIcon icon() const { return m_icon; }
49 |
50 | /**
51 | * @return 剩余时间
52 | */
53 | int leftTime() const;
54 |
55 | signals:
56 | void closed();
57 | void clicked();
58 |
59 | private slots:
60 | void closeAnimation();
61 |
62 | private:
63 | QLabel *m_iconLabel = nullptr;
64 | QLabel *m_titleLabel = nullptr;
65 | QLabel *m_messageLabel = nullptr;
66 |
67 | QHBoxLayout *m_hLayout = nullptr;
68 | QHBoxLayout *m_mLayout = nullptr;
69 | QVBoxLayout *m_vLayout = nullptr;
70 | QSpacerItem *m_hSpacer = nullptr;
71 |
72 | QIcon m_icon;
73 |
74 | CountdownButton *m_closeButton = nullptr;
75 |
76 | QPropertyAnimation *m_animation = nullptr;
77 |
78 | bool m_isClosing = false;
79 |
80 | void showEvent(QShowEvent *event) Q_DECL_OVERRIDE;
81 | void closeEvent(QCloseEvent *event) Q_DECL_OVERRIDE;
82 | void mouseReleaseEvent(QMouseEvent *event) Q_DECL_OVERRIDE;
83 | };
84 |
85 | #endif // NOTIFYWIDGET_H
86 |
--------------------------------------------------------------------------------
/QmlDemo/src/customItem/clickwaveeffect.h:
--------------------------------------------------------------------------------
1 | /**
2 | * Click Wave Effect
3 | * @anchor Ho 229
4 | * @date 2021/7/18
5 | */
6 |
7 | #ifndef CLICKWAVEEFFECT_H
8 | #define CLICKWAVEEFFECT_H
9 |
10 | #include
11 |
12 | class QVariantAnimation;
13 | class QSequentialAnimationGroup;
14 |
15 | class ClickWaveEffect : public QQuickPaintedItem
16 | {
17 | Q_OBJECT
18 |
19 | Q_PROPERTY(QQuickItem* target READ target WRITE setTarget NOTIFY targetChanged)
20 | Q_PROPERTY(QColor color READ color WRITE setColor NOTIFY colorChanged)
21 | Q_PROPERTY(int waveDuration READ waveDuration WRITE setWaveDuration NOTIFY waveDurationChanged)
22 | Q_PROPERTY(int maxRadius READ maxRadius WRITE setMaxRadius NOTIFY maxRadiusChanged)
23 |
24 | public:
25 | explicit ClickWaveEffect(QQuickItem *parent = nullptr);
26 | virtual ~ClickWaveEffect() Q_DECL_OVERRIDE;
27 |
28 | void setTarget(QQuickItem *target);
29 | QQuickItem* target() const { return m_target; }
30 |
31 | void setColor(const QColor& color);
32 | QColor color() const { return m_color; }
33 |
34 | void setWaveDuration(int duration);
35 | int waveDuration() const;
36 |
37 | void setMaxRadius(int radius);
38 | int maxRadius() const { return m_maxRadius; }
39 |
40 | signals:
41 | void waveDurationChanged(int duration);
42 | void targetChanged(QQuickItem *target);
43 | void maxRadiusChanged(int radius);
44 | void colorChanged(QColor color);
45 | void finished();
46 |
47 | protected:
48 | virtual void paint(QPainter *painter) Q_DECL_OVERRIDE;
49 | virtual bool eventFilter(QObject *obj, QEvent *event) Q_DECL_OVERRIDE;
50 |
51 | private slots:
52 | void on_finished();
53 |
54 | private:
55 | int m_radius = 0;
56 | int m_maxRadius = -1;
57 | bool m_isPressed = false;
58 |
59 | QQuickItem *m_target = nullptr;
60 |
61 | QPoint m_pos;
62 | QColor m_color;
63 | QColor m_currentColor;
64 |
65 | QSequentialAnimationGroup *m_animation = nullptr;
66 | QVariantAnimation *m_alphaAnimation = nullptr;
67 | QVariantAnimation *m_radiusAnimation = nullptr;
68 |
69 | static int maxRadius(const QPoint& pos, const QSize &size);
70 | };
71 |
72 | #endif // CLICKWAVEEFFECT_H
73 |
--------------------------------------------------------------------------------
/MultithreadedDownloader/src/downloader/downloadmission.h:
--------------------------------------------------------------------------------
1 | /**
2 | * @brief DownloadMission
3 | * @anchor Ho229<2189684957@qq.com>
4 | * @date 2021/2/1
5 | */
6 |
7 | #ifndef DOWNLOADMISSION_H
8 | #define DOWNLOADMISSION_H
9 |
10 | #include
11 |
12 | #include "abstractmission.h"
13 | #include "multithreadeddownloaderwriter.h"
14 |
15 | class QNetworkAccessManager;
16 |
17 | class DownloadMission : public AbstractMission
18 | {
19 | Q_OBJECT
20 | public:
21 | explicit DownloadMission(QObject *parent);
22 | ~DownloadMission() Q_DECL_OVERRIDE;
23 |
24 | /**
25 | * @brief 设置下载范围
26 | * @param start 下载起始位置
27 | * @param end 下载结束位置
28 | */
29 | void setRange(qint64 start, qint64 end)
30 | {
31 | if(m_state == Stopped)
32 | {
33 | m_start = start;
34 | m_end = end;
35 | m_totalSize = end - start;
36 | }
37 | }
38 |
39 | void setManager(QNetworkAccessManager *manager){ m_manager = manager; }
40 | QNetworkAccessManager* manager() const { return m_manager; }
41 |
42 | void setWriter(MultithreadedDownloaderWriter *writer){ m_writer = writer; }
43 | MultithreadedDownloaderWriter* writer() const { return m_writer; }
44 |
45 | /**
46 | * @return 下载大小
47 | */
48 | qint64 downloadedSize() const { return m_downloadedSize; }
49 |
50 | QString replyErrorString() const
51 | { return m_reply == nullptr ? QString() : m_reply->errorString(); }
52 |
53 | void start() Q_DECL_OVERRIDE;
54 | void pause() Q_DECL_OVERRIDE;
55 | void stop() Q_DECL_OVERRIDE;
56 |
57 | signals:
58 | void replyError(QNetworkReply::NetworkError err);
59 |
60 | private slots:
61 | void on_finished();
62 | void writeData();
63 |
64 | private:
65 | MultithreadedDownloaderWriter* m_writer = nullptr;
66 | QNetworkAccessManager* m_manager = nullptr;
67 | QNetworkReply* m_reply = nullptr;
68 |
69 | qint64 m_start = -1;
70 | qint64 m_end = -1;
71 |
72 | qint64 m_totalSize = 0;
73 | qint64 m_downloadedSize = 0;
74 |
75 | inline void reset(); // 重置状态
76 | inline void destoryReply();
77 | };
78 |
79 | #endif // DOWNLOADMISSION_H
80 |
--------------------------------------------------------------------------------
/CustomWidgetDemos/src/customWidgets/progressbutton.h:
--------------------------------------------------------------------------------
1 | /**
2 | * Progress Button
3 | * @brief 进度条按钮
4 | * @anchor Ho229
5 | * @date 2021/2/22
6 | */
7 |
8 | #ifndef PROGRESSBUTTON_H
9 | #define PROGRESSBUTTON_H
10 |
11 | #include
12 | #include
13 |
14 | #define TransAngle(x) (x * 16)
15 |
16 | #define DEFULT_BUTTON_STYLE "\
17 | QPushButton\
18 | {\
19 | background:transparent;\
20 | border-style:none;\
21 | }\
22 | QPushButton:hover\
23 | {\
24 | background:transparent;\
25 | border-radius:7px;\
26 | border:1px solid rgb(119,119,119);\
27 | }\
28 | QPushButton:pressed\
29 | {\
30 | background:rgba(100, 234, 255, 200);\
31 | }"
32 |
33 | class ProgressButton : public QPushButton
34 | {
35 | Q_OBJECT
36 |
37 | Q_PROPERTY(int value READ value WRITE setValue)
38 | Q_PROPERTY(int maximun READ maximun WRITE setMaximun)
39 | Q_PROPERTY(int minimun READ minimun WRITE setMinimun)
40 |
41 | Q_PROPERTY(int progressWidth READ progressWidth WRITE setProgressWidth)
42 | Q_PROPERTY(QColor progressColor READ progressColor WRITE setProgressColor)
43 |
44 | public:
45 | explicit ProgressButton(QWidget *parent = nullptr,
46 | const QString &style = DEFULT_BUTTON_STYLE);
47 | virtual ~ProgressButton() Q_DECL_OVERRIDE;
48 |
49 | void setMaximun(const int value){ m_maximun = qMax(value, m_minimun); }
50 | void setMinimun(const int value){ m_minimun = qMin(value, m_maximun); }
51 | void setValue(int value);
52 |
53 | void setProgressWidth(const int width){ m_pen.setWidth(width); }
54 | void setProgressColor(const QColor& color){ m_pen.setColor(color); }
55 |
56 | int maximun() const { return m_maximun; }
57 | int minimun() const { return m_minimun; }
58 | int value() const { return m_value; }
59 |
60 | int progressWidth() const { return m_pen.width(); }
61 | QColor progressColor() const { return m_pen.color(); }
62 |
63 | signals:
64 | void valueChanged(int value);
65 |
66 | protected:
67 | virtual void paintEvent(QPaintEvent *event) Q_DECL_OVERRIDE;
68 |
69 | private:
70 | int m_maximun = 100;
71 | int m_minimun = 0;
72 | int m_value = 0;
73 |
74 | QPen m_pen;
75 | };
76 |
77 | #endif // PROGRESSBUTTON_H
78 |
--------------------------------------------------------------------------------
/CustomWidgetDemos/src/customWidgets/aligniconbutton.h:
--------------------------------------------------------------------------------
1 | /**
2 | * @brief 左右 icon 对齐 Push Button
3 | * @anchor Ho229<2189684957@qq.com>
4 | * @date 2021/2/1
5 | */
6 |
7 | #ifndef ALIGNICONBUTTON_H
8 | #define ALIGNICONBUTTON_H
9 |
10 | #include
11 |
12 | class AlignIconButton : public QPushButton
13 | {
14 | Q_OBJECT
15 |
16 | Q_PROPERTY(QIcon leftIcon READ leftIcon WRITE setLeftIcon)
17 | Q_PROPERTY(QIcon rightIcon READ rightIcon WRITE setRightIcon)
18 | Q_PROPERTY(int sideMargin READ sideMargin WRITE setSideMargin)
19 | Q_PROPERTY(int topBottomMargin READ topBottomMargin WRITE setTopBottomMargin)
20 |
21 | public:
22 | explicit AlignIconButton(QWidget *parent = nullptr);
23 | virtual ~AlignIconButton() Q_DECL_OVERRIDE;
24 |
25 | /**
26 | * @brief 设置左对齐 icon
27 | */
28 | void setLeftIcon(QIcon icon)
29 | {
30 | m_leftIcon = icon;
31 | this->repaint();
32 | }
33 |
34 | /**
35 | * @return 左对齐 icon
36 | */
37 | QIcon leftIcon() const { return m_leftIcon; }
38 |
39 | /**
40 | * @brief 设置右对齐 icon
41 | */
42 | void setRightIcon(QIcon icon)
43 | {
44 | m_rightIcon = icon;
45 | this->repaint();
46 | }
47 |
48 | /**
49 | * @return 右对齐 icon
50 | */
51 | QIcon rightIcon() const { return m_rightIcon; }
52 |
53 | /**
54 | * @brief 设置左右边距
55 | * @warning 效果将在下次 repaint() 生效
56 | */
57 | void setSideMargin(int margin){ m_sideMargin = margin; }
58 |
59 | /**
60 | * @return 左右边距
61 | */
62 | int sideMargin() const { return m_sideMargin; }
63 |
64 | /**
65 | * @brief 设置上下边距
66 | * @warning 效果将在下次 repaint() 生效
67 | */
68 | void setTopBottomMargin(int margin){ m_topBottomMargin = margin; }
69 |
70 | /**
71 | * @return 上下边距
72 | */
73 | int topBottomMargin() const { return m_topBottomMargin; }
74 |
75 | protected:
76 | virtual void paintEvent(QPaintEvent *event) Q_DECL_OVERRIDE;
77 |
78 | private:
79 | QIcon m_leftIcon;
80 | QIcon m_rightIcon;
81 |
82 | int m_sideMargin = 5;
83 | int m_topBottomMargin = 5;
84 |
85 | inline void drawIcon(QPainter *painter);
86 | };
87 |
88 | #endif // ALIGNICONBUTTON_H
89 |
--------------------------------------------------------------------------------
/MultithreadedDownloader/src/customWidgets/translationstackedwidget.h:
--------------------------------------------------------------------------------
1 | /**
2 | * Animation Stacked Widget
3 | * @brief 平移动画 Stacked Widget
4 | * @anchor Ho229
5 | * @date 2020/12/12
6 | */
7 |
8 | #ifndef TRANSLATIONSTACKEDWIDGET_H
9 | #define TRANSLATIONSTACKEDWIDGET_H
10 |
11 | #include
12 | #include
13 |
14 | class QVariantAnimation;
15 |
16 | class TranslationStackedWidget : public QStackedWidget
17 | {
18 | Q_OBJECT
19 |
20 | Q_PROPERTY(int animationDuration READ animationDuration WRITE setAnimationDuration)
21 | Q_PROPERTY(QEasingCurve animationEasingCurve READ animationEasingCurve WRITE setAnimationEasingCurve)
22 |
23 | public:
24 | /**
25 | * @brief TranslationStackedWidget
26 | * @param parent 父对象
27 | */
28 | explicit TranslationStackedWidget(QWidget *parent = nullptr);
29 | ~TranslationStackedWidget() Q_DECL_OVERRIDE;
30 |
31 | /**
32 | * @brief 平移到 index
33 | * @param index 目标 index
34 | * @param exec 是否启用局部事件循环
35 | */
36 | void moveToIndex(int index, bool exec = true);
37 |
38 | /**
39 | * @brief 设置动画持续时间
40 | * @param duration 持续时间(msecs)
41 | */
42 | void setAnimationDuration(int duration);
43 |
44 | /**
45 | * @return 动画持续时间
46 | */
47 | int animationDuration();
48 |
49 | /**
50 | * @brief 设置动画缓和曲线
51 | * @param easing 动画缓和曲线
52 | */
53 | void setAnimationEasingCurve(const QEasingCurve& easing);
54 | /**
55 | * @return 动画缓和曲线
56 | */
57 | QEasingCurve animationEasingCurve();
58 |
59 | private slots:
60 | void on_finished();
61 |
62 | private:
63 | QVariantAnimation *m_animation = nullptr;
64 |
65 | QPixmap m_animationPixmap;
66 |
67 | QWidget *m_currentWidget = nullptr;
68 | QWidget *m_nextWidget = nullptr;
69 |
70 | int m_animationX = 0;
71 | int m_nextIndex;
72 |
73 | static QPixmap mergePixmap(const QPixmap& leftPixmap, const QPixmap& rightPixmap);
74 |
75 | inline void updateFrame();
76 | inline void updateAnimation();
77 |
78 | void paintEvent(QPaintEvent* event) Q_DECL_OVERRIDE;
79 | void resizeEvent(QResizeEvent* event) Q_DECL_OVERRIDE;
80 |
81 | };
82 |
83 | #endif // TRANSLATIONSTACKEDWIDGET_H
84 |
--------------------------------------------------------------------------------
/CustomWidgetDemos/src/customWidgets/translationstackedwidget.h:
--------------------------------------------------------------------------------
1 | /**
2 | * Animation Stacked Widget
3 | * @brief 平移动画 Stacked Widget
4 | * @anchor Ho229
5 | * @date 2020/12/12
6 | */
7 |
8 | #ifndef TRANSLATIONSTACKEDWIDGET_H
9 | #define TRANSLATIONSTACKEDWIDGET_H
10 |
11 | #include
12 | #include
13 |
14 | class QVariantAnimation;
15 |
16 | class TranslationStackedWidget Q_DECL_FINAL : public QStackedWidget
17 | {
18 | Q_OBJECT
19 |
20 | Q_PROPERTY(int animationDuration READ animationDuration WRITE setAnimationDuration)
21 | Q_PROPERTY(QEasingCurve animationEasingCurve READ animationEasingCurve WRITE setAnimationEasingCurve)
22 |
23 | public:
24 | /**
25 | * @brief TranslationStackedWidget
26 | * @param parent 父对象
27 | */
28 | explicit TranslationStackedWidget(QWidget *parent = nullptr);
29 | ~TranslationStackedWidget() Q_DECL_OVERRIDE;
30 |
31 | /**
32 | * @brief 平移到 index
33 | * @param index 目标 index
34 | * @param exec 是否启用局部事件循环
35 | */
36 | void moveToIndex(int index, bool exec = true);
37 |
38 | /**
39 | * @brief 设置动画持续时间
40 | * @param duration 持续时间(msecs)
41 | */
42 | void setAnimationDuration(int duration);
43 |
44 | /**
45 | * @return 动画持续时间
46 | */
47 | int animationDuration();
48 |
49 | /**
50 | * @brief 设置动画缓和曲线
51 | * @param easing 动画缓和曲线
52 | */
53 | void setAnimationEasingCurve(const QEasingCurve& easing);
54 | /**
55 | * @return 动画缓和曲线
56 | */
57 | QEasingCurve animationEasingCurve();
58 |
59 | private slots:
60 | void on_finished();
61 |
62 | private:
63 | QVariantAnimation *m_animation = nullptr;
64 |
65 | QPixmap m_animationPixmap;
66 |
67 | QWidget *m_currentWidget = nullptr;
68 | QWidget *m_nextWidget = nullptr;
69 |
70 | int m_animationX = 0;
71 | int m_nextIndex;
72 |
73 | static QPixmap mergePixmap(const QPixmap& leftPixmap, const QPixmap& rightPixmap);
74 |
75 | inline void updateFrame();
76 | inline void updateAnimation();
77 |
78 | void paintEvent(QPaintEvent* event) Q_DECL_OVERRIDE;
79 | void resizeEvent(QResizeEvent* event) Q_DECL_OVERRIDE;
80 |
81 | };
82 |
83 | #endif // TRANSLATIONSTACKEDWIDGET_H
84 |
--------------------------------------------------------------------------------
/CustomWidgetDemos/src/customWidgets/notifymanager.cpp:
--------------------------------------------------------------------------------
1 | /**
2 | * Notify Manager
3 | * @brief 右下角提示窗管理器
4 | * @anchor Ho229<2189684957@qq.com>
5 | * @date 2021/2/1
6 | */
7 |
8 | #include "notifymanager.h"
9 |
10 | #include
11 | #include
12 | #include
13 | #include
14 |
15 | NotifyManager::NotifyManager(QObject *parent) :
16 | QObject(parent),
17 | m_desktopSize(QGuiApplication::primaryScreen()->size())
18 | {
19 |
20 | }
21 |
22 | NotifyManager::~NotifyManager()
23 | {
24 |
25 | }
26 |
27 | NotifyWidget* NotifyManager::notify(QWidget *parent, const QString& title,
28 | const QString& message, const QIcon& icon, int showTime)
29 | {
30 | NotifyWidget *newNotofy = new NotifyWidget(parent, title, message, icon);
31 | QObject::connect(newNotofy, &NotifyWidget::closed, this,
32 | &NotifyManager::onNotifyClosed);
33 | m_list.push_back({newNotofy, showTime});
34 |
35 | this->updateNotifys();
36 |
37 | return newNotofy;
38 | }
39 |
40 | void NotifyManager::onNotifyClosed()
41 | {
42 | NotifyWidget *closedWidget = static_cast(this->sender());
43 |
44 | const QList& constlist = m_list;
45 | for(const NotifyItem& item : constlist)
46 | {
47 | if(item.first == closedWidget)
48 | {
49 | m_list.removeOne(item);
50 | break;
51 | }
52 | }
53 | this->updateNotifys();
54 | emit windowClosed();
55 | }
56 |
57 | void NotifyManager::updateNotifys()
58 | {
59 | if(m_list.isEmpty())
60 | return;
61 |
62 | m_showCount = 0;
63 | for(int i = 0; i < m_list.size() && i < m_maximum; i++)
64 | {
65 | NotifyItem item = m_list.at(i);
66 |
67 | if(item.first->isHidden())
68 | {
69 | item.first->move(QPoint(m_desktopSize.width(),
70 | m_desktopSize.height() - (i + 1) * 170));
71 | if(item.second > 0)
72 | item.first->setCloseCountdown(item.second);
73 |
74 | item.first->show();
75 | }
76 | else
77 | if(item.first->isClosing())
78 | item.first->animatMove(m_desktopSize.width(),
79 | m_desktopSize.height() - (i + 1) * 170);
80 | else
81 | item.first->animatMove(m_desktopSize.width() - item.first->width(),
82 | m_desktopSize.height() - (i + 1) * 170);
83 | m_showCount++;
84 | }
85 | }
86 |
--------------------------------------------------------------------------------
/VideoPlayer/src/player/videoplayer_p.cpp:
--------------------------------------------------------------------------------
1 | /**
2 | * @brief Video Player Private
3 | * @anchor Ho 229
4 | * @date 2023/4/21
5 | */
6 |
7 | #include "videoplayer_p.h"
8 |
9 | #include "audiooutput.h"
10 | #include "ffmpegdecoder.h"
11 | #include "videorenderer.h"
12 |
13 | void VideoPlayerPrivate::updateTimer(int newInterval)
14 | {
15 | Q_Q(VideoPlayer);
16 |
17 | interval = newInterval;
18 | q->killTimer(timerId);
19 | q->startTimer(interval, Qt::PreciseTimer);
20 | }
21 |
22 | void VideoPlayerPrivate::restartAudioOutput()
23 | {
24 | audioOutput->updateAudioOutput(decoder->audioFormat());
25 |
26 | if(state == VideoPlayer::Playing)
27 | audioOutput->play();
28 | }
29 |
30 | qint64 VideoPlayerPrivate::updateAudioData(char *data, qint64 maxlen)
31 | {
32 | if(!data)
33 | return 0;
34 |
35 | qint64 free = maxlen;
36 | char *dest = data;
37 |
38 | while(free)
39 | {
40 | if(!audioFrame)
41 | {
42 | if(!(audioFrame = decoder->takeAudioFrame()))
43 | break;
44 | }
45 |
46 | if(!audioClock.isValid() || (audioOutput->isLowDataLeft() && dest == data))
47 | audioClock.update(FFmpegDecoder::framePts(audioFrame));
48 |
49 | const auto size = qMin(qint64(audioFrame->linesize[0]) - audioFramePos, free);
50 |
51 | memcpy(dest, audioFrame->data[0] + audioFramePos, size);
52 | dest += size;
53 | free -= size;
54 | audioFramePos += size;
55 |
56 | if(audioFramePos >= audioFrame->linesize[0])
57 | {
58 | audioFramePos = 0;
59 | av_frame_free(&audioFrame);
60 | }
61 | }
62 |
63 | return maxlen - free;
64 | }
65 |
66 | void VideoPlayerPrivate::updateVideoFrame()
67 | {
68 | AVFrame *frame = nullptr;
69 | while((frame = decoder->takeVideoFrame()))
70 | {
71 | const qreal pts = FFmpegDecoder::framePts(frame);
72 | if(!qFuzzyCompare(pts, -1))
73 | {
74 | videoClock.update(pts);
75 | auto nextInterval = FFmpegDecoder::frameDuration(frame);
76 |
77 | if(audioClock.isValid())
78 | nextInterval -= audioClock.time() - videoClock.time()/* - audioOutput->bufferDuration()*/;
79 | nextInterval *= 1000;
80 |
81 | if(nextInterval < 1)
82 | {
83 | av_frame_free(&frame);
84 | continue;
85 | }
86 |
87 | if(interval != nextInterval)
88 | this->updateTimer(nextInterval);
89 | }
90 |
91 | videoRenderer->updateVideoFrame(frame);
92 | break;
93 | }
94 | }
95 |
96 | void VideoPlayerPrivate::updateSubtitleFrame()
97 | {
98 | SubtitleFrame *frame = nullptr;
99 | if((frame = decoder->takeSubtitleFrame(videoClock.time())))
100 | videoRenderer->updateSubtitleFrame(frame);
101 | }
102 |
--------------------------------------------------------------------------------
/CustomWidgetDemos/src/customWidgets/toast.cpp:
--------------------------------------------------------------------------------
1 | /**
2 | * Toast Widget
3 | * @brief 自动消失提示框
4 | * @anchor Ho229
5 | * @date 2020/12/12
6 | */
7 |
8 | #include "toast.h"
9 |
10 | #include
11 | #include
12 | #include
13 | #include
14 | #include
15 |
16 | Toast::Toast(QWidget *parent, int horizontalMargin, int verticalMargin,
17 | int maximumWidth, bool wordWrap, int waitMsecs, const QString &style) :
18 | QWidget(parent),
19 | m_messageLabel(new QLabel(this)),
20 | m_layout(new QHBoxLayout(this)),
21 | m_animation(new QSequentialAnimationGroup(this)),
22 | m_posAnimation(new QPropertyAnimation(this, "pos")),
23 | m_opacityAnimation(new QPropertyAnimation(this, "windowOpacity"))
24 | {
25 | this->setAttribute(Qt::WA_TransparentForMouseEvents);
26 | this->setAttribute(Qt::WA_TranslucentBackground);
27 | this->setWindowFlags(Qt::FramelessWindowHint | Qt::ToolTip | Qt::CustomizeWindowHint);
28 |
29 | m_layout->addWidget(m_messageLabel);
30 | m_layout->setContentsMargins(0, 0, 0, 0);
31 |
32 | m_messageLabel->setStyleSheet(style);
33 | m_messageLabel->setContentsMargins(horizontalMargin, verticalMargin,
34 | horizontalMargin, verticalMargin);
35 | m_messageLabel->setAlignment(Qt::AlignCenter);
36 | m_messageLabel->setMaximumWidth(maximumWidth);
37 | m_messageLabel->setWordWrap(wordWrap);
38 |
39 | /* 显示动画 */
40 | m_posAnimation->setDuration(300);
41 | m_posAnimation->setEasingCurve(QEasingCurve::OutCubic);
42 |
43 | /* 消失动画 */
44 | m_opacityAnimation->setDuration(250);
45 | m_opacityAnimation->setStartValue(1);
46 | m_opacityAnimation->setEndValue(0);
47 |
48 | m_animation->addAnimation(m_posAnimation);
49 | m_animation->addPause(waitMsecs);
50 | m_animation->addAnimation(m_opacityAnimation);
51 |
52 | connect(m_animation, &QSequentialAnimationGroup::finished, this, &Toast::close);
53 | }
54 |
55 | Toast::~Toast()
56 | {
57 |
58 | }
59 |
60 | void Toast::setText(const QString &text)
61 | {
62 | // 自适应大小
63 | m_messageLabel->setText(text);
64 | m_messageLabel->adjustSize();
65 | this->adjustSize();
66 |
67 | // 计算动画起止坐标
68 | QRect rect = this->rect();
69 | QRect parentGeometry = this->parentWidget() == nullptr ?
70 | QGuiApplication::primaryScreen()->geometry() : this->parentWidget()->frameGeometry();
71 |
72 | rect.moveCenter(parentGeometry.center());
73 | rect.translate(0, parentGeometry.height() / 4);
74 |
75 | m_posAnimation->setEndValue(rect.topLeft());
76 |
77 | rect.translate(0, parentGeometry.height() / 4 - this->height() / 2);
78 |
79 | m_posAnimation->setStartValue(rect.topLeft());
80 | }
81 |
82 | void Toast::toast()
83 | {
84 | if(m_animation->state() == QSequentialAnimationGroup::Running)
85 | m_animation->stop();
86 |
87 | this->setWindowOpacity(1); // 透明度复位
88 | this->show();
89 | m_animation->start();
90 | }
91 |
--------------------------------------------------------------------------------
/MultithreadedDownloader/src/customWidgets/toast.cpp:
--------------------------------------------------------------------------------
1 | /**
2 | * Toast Widget
3 | * @brief 自动消失提示框
4 | * @anchor Ho229
5 | * @date 2020/12/12
6 | */
7 |
8 | #include "toast.h"
9 |
10 | #include
11 | #include
12 | #include
13 | #include
14 | #include
15 |
16 | Toast::Toast(QWidget *parent, int horizontalMargin, int verticalMargin,
17 | int maximumWidth, bool wordWrap, int waitMsecs, const QString &style) :
18 | QWidget(parent),
19 | m_messageLabel(new QLabel(this)),
20 | m_layout(new QHBoxLayout(this)),
21 | m_animation(new QSequentialAnimationGroup(this)),
22 | m_posAnimation(new QPropertyAnimation(this, "pos")),
23 | m_opacityAnimation(new QPropertyAnimation(this, "windowOpacity"))
24 | {
25 | this->setAttribute(Qt::WA_TransparentForMouseEvents);
26 | this->setAttribute(Qt::WA_TranslucentBackground);
27 | this->setWindowFlags(Qt::FramelessWindowHint | Qt::Tool | Qt::CustomizeWindowHint);
28 |
29 | m_layout->addWidget(m_messageLabel);
30 | m_layout->setContentsMargins(0, 0, 0, 0);
31 |
32 | m_messageLabel->setStyleSheet(style);
33 | m_messageLabel->setContentsMargins(horizontalMargin, verticalMargin,
34 | horizontalMargin, verticalMargin);
35 | m_messageLabel->setAlignment(Qt::AlignCenter);
36 | m_messageLabel->setMaximumWidth(maximumWidth);
37 | m_messageLabel->setWordWrap(wordWrap);
38 |
39 | /* 显示动画 */
40 | m_posAnimation->setDuration(300);
41 | m_posAnimation->setEasingCurve(QEasingCurve::OutCubic);
42 |
43 | /* 消失动画 */
44 | m_opacityAnimation->setDuration(250);
45 | m_opacityAnimation->setStartValue(1);
46 | m_opacityAnimation->setEndValue(0);
47 |
48 | m_animation->addAnimation(m_posAnimation);
49 | m_animation->addPause(waitMsecs);
50 | m_animation->addAnimation(m_opacityAnimation);
51 |
52 | connect(m_animation, &QSequentialAnimationGroup::finished, this, &Toast::close);
53 | }
54 |
55 | Toast::~Toast()
56 | {
57 |
58 | }
59 |
60 | void Toast::setText(const QString &text)
61 | {
62 | // 自适应大小
63 | m_messageLabel->setText(text);
64 | m_messageLabel->adjustSize();
65 | this->adjustSize();
66 |
67 | // 计算动画起止坐标
68 | QRect rect = this->rect();
69 | QRect parentGeometry = this->parentWidget() == nullptr ?
70 | QGuiApplication::primaryScreen()->geometry() : this->parentWidget()->frameGeometry();
71 |
72 | rect.moveCenter(parentGeometry.center());
73 | rect.translate(0, parentGeometry.height() / 4);
74 |
75 | m_posAnimation->setEndValue(rect.topLeft());
76 |
77 | rect.translate(0, parentGeometry.height() / 4 - this->height() / 2);
78 |
79 | m_posAnimation->setStartValue(rect.topLeft());
80 | }
81 |
82 | void Toast::toast()
83 | {
84 | if(m_animation->state() == QSequentialAnimationGroup::Running)
85 | m_animation->stop();
86 |
87 | this->setWindowOpacity(1); // 透明度复位
88 | this->show();
89 | m_animation->start();
90 | }
91 |
--------------------------------------------------------------------------------
/MultithreadedDownloader/src/downloader/downloadmission.cpp:
--------------------------------------------------------------------------------
1 | /**
2 | * @brief DownloadMission
3 | * @anchor Ho229<2189684957@qq.com>
4 | * @date 2021/2/1
5 | */
6 |
7 | #include "downloadmission.h"
8 |
9 | #include
10 | #include
11 | #include
12 | #include
13 |
14 | DownloadMission::DownloadMission(QObject *parent)
15 | : AbstractMission(parent)
16 | {
17 |
18 | }
19 |
20 | DownloadMission::~DownloadMission()
21 | {
22 | if(m_state == Running)
23 | this->DownloadMission::stop();
24 | }
25 |
26 | void DownloadMission::start()
27 | {
28 | if(m_state == Running)
29 | return;
30 |
31 | QNetworkRequest request(m_url);
32 |
33 | if(m_start >= 0 && m_end > 0)
34 | request.setRawHeader("Range", QString("bytes=%1-%2")
35 | .arg(m_start + m_downloadedSize).arg(m_end).toLatin1());
36 | request.setAttribute(QNetworkRequest::FollowRedirectsAttribute, true);
37 |
38 | m_reply = m_manager->get(request);
39 |
40 | QObject::connect(m_reply, &QNetworkReply::finished, this,
41 | &DownloadMission::on_finished);
42 | #if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0)
43 | QObject::connect(m_reply, &QNetworkReply::errorOccurred, this,
44 | &DownloadMission::replyError);
45 | #else
46 | QObject::connect(m_reply, QOverload::of(&QNetworkReply::error), this,
47 | &DownloadMission::replyError);
48 | #endif
49 | QObject::connect(m_reply, &QNetworkReply::readyRead, this,
50 | &DownloadMission::writeData);
51 |
52 | this->setFinished(false);
53 | this->updateState(Running);
54 | }
55 |
56 | void DownloadMission::pause()
57 | {
58 | if(m_state == Running)
59 | {
60 | this->updateState(Paused);
61 |
62 | m_reply->abort();
63 | this->destoryReply();
64 | }
65 | }
66 |
67 | void DownloadMission::stop()
68 | {
69 | if(m_state != Stopped)
70 | {
71 | this->updateState(Stopped);
72 |
73 | if(m_state == Running)
74 | {
75 | m_reply->abort();
76 | this->destoryReply();
77 | }
78 |
79 | this->reset();
80 | }
81 | }
82 |
83 | void DownloadMission::on_finished()
84 | {
85 | if(m_state == Running)
86 | {
87 | this->updateState(Stopped);
88 | this->setFinished();
89 | }
90 | }
91 |
92 | void DownloadMission::writeData()
93 | {
94 | QByteArray data = m_reply->readAll();
95 | m_writer->write(data, m_start + m_downloadedSize);
96 | m_downloadedSize += data.size();
97 | }
98 |
99 | void DownloadMission::reset()
100 | {
101 | m_start = -1;
102 | m_end = -1;
103 |
104 | m_totalSize = 0;
105 | m_downloadedSize = 0;
106 | }
107 |
108 | void DownloadMission::destoryReply()
109 | {
110 | m_reply->deleteLater();
111 | m_reply = nullptr;
112 | }
113 |
--------------------------------------------------------------------------------
/CustomWidgetDemos/README.md:
--------------------------------------------------------------------------------
1 | # CustomWidgetDemos
2 | 
3 | 
4 |
5 | -------
6 | ## Class AlignIconButton
7 | | File |
8 | | ---- |
9 | | [aligniconbutton.h](./src/customWidgets/aligniconbutton.h) |
10 | | [aligniconbutton.cpp](./src/customWidgets/aligniconbutton.cpp) |
11 | * 左右 icon 对齐 Push Button
12 | * Example
13 | ```cpp
14 | AlignIconButton *pushButton = new AlignIconButton(this);
15 | pushButton->setLeftIcon(leftIcon);
16 | pushButton->setRightIcon(rightIcon);
17 | pushButton->show();
18 | ```
19 | -----
20 | ## Class NotifyWidget & NotifyManager
21 | | File |
22 | | ---- |
23 | | [notifywidget.h](./src/customWidgets/notifywidget.h) |
24 | | [notifywidget.cpp](./src/customWidgets/notifywidget.cpp) |
25 | | [notifymanager.h](./src/customWidgets/notifymanager.h) |
26 | | [notifymanager.cpp](./src/customWidgets/notifymanager.cpp) |
27 | * 桌面右下角弹窗
28 | * 注意:
29 | * `NotifyWidget` 是一次性的,关闭窗口时将被销毁。
30 | * Example
31 | ```cpp
32 | NotifyManager *manager = new NotifyManager(this);
33 | manager->notify(this, "Hello", "Hello World.\nHow are you today.");
34 | ```
35 | -----
36 | ## Class ProgressButton
37 | | File |
38 | | ---- |
39 | | [progressbutton.h](./src/customWidgets/progressbutton.h) |
40 | | [progressbutton.cpp](./src/customWidgets/progressbutton.cpp) |
41 | * 进度条按钮
42 | * 提供了类似 `QProgressBar` 的API
43 | * Example
44 | ```cpp
45 | ProgressButton *button = new ProgressButton(this);
46 | button->setValue(50);
47 | button->show();
48 | ```
49 | -----
50 | ## Class RotateStackedWidget
51 | | File |
52 | | ---- |
53 | | [rotatestackedwidget.h](./src/customWidgets/rotatestackedwidget.h) |
54 | | [rotatestackedwidget.cpp](./src/customWidgets/rotatestackedwidget.cpp) |
55 |
56 | * 带有翻转动画的 Stacked Widget
57 | * Example
58 | ```cpp
59 | RotateStackedWidget *stackedWidget = new RotateStackedWidget(this);
60 | stackedWidget->addWidget(widget_1);
61 | stackedWidget->addWidget(widget_2);
62 | stackedWidget->setCurrentIndex(0);
63 | stackedWidget->rotate(1); // 页面翻转
64 | ```
65 | -----
66 | ## Class Toast
67 | | File |
68 | | ---- |
69 | | [toast.h](./src/customWidgets/toast.h) |
70 | | [toast.cpp](./src/customWidgets/toast.cpp) |
71 |
72 | * Toast 提示窗
73 |
74 | 注意:
75 | * 1.当 `parent == nullptr` 时,Toast会出现在活动桌面水平居中垂直 3/4 的地方,`parent != nullptr` 时则Toast会出现在父窗口水平居中垂直 3/4 的地方。
76 | * 2.当 Toast 正在显示消息时,再次调用 `Toast::toast()` 将显示新消息。
77 | * Example
78 | ```cpp
79 | Toast *toast = new Toast(this);
80 | toast->toast("Hello");
81 | ```
82 | -----
83 | ## Class TranslationStackedWidget
84 | | File |
85 | | ---- |
86 | | [translationstackedwidget.h](./src/customWidgets/translationstackedwidget.h) |
87 | | [translationstackedwidget.cpp](./src/customWidgets/translationstackedwidget.cpp) |
88 |
89 | * 具有平移动画的 Stacked Widget
90 | * Example
91 | ```cpp
92 | TranslationStackedWidget *stackedWidget = new TranslationStackedWidget(this);
93 | stackedWidget->addWidget(widget_1);
94 | stackedWidget->addWidget(widget_2);
95 | stackedWidget->setCurrentIndex(0);
96 | stackedWidget->moveToIndex(1); // 页面平移
97 | ```
--------------------------------------------------------------------------------
/VideoPlayer/src/player/audiooutput.cpp:
--------------------------------------------------------------------------------
1 | /**
2 | * @brief Audio Output
3 | * @anchor Ho 229
4 | * @date 2021/5/1
5 | */
6 |
7 | #include "audiooutput.h"
8 |
9 | #include
10 | #include
11 | #include
12 |
13 | class AudioDevice final : public QIODevice
14 | {
15 | const AudioOutput::Callback m_callback;
16 | public:
17 | AudioDevice(const AudioOutput::Callback &callback, QObject *parent = nullptr)
18 | : QIODevice(parent)
19 | , m_callback(callback)
20 | { this->setOpenMode(QIODevice::ReadOnly); }
21 |
22 | qint64 readData(char *data, qint64 maxlen) override
23 | {
24 | if(!maxlen)
25 | return 0;
26 |
27 | return m_callback(data, maxlen);
28 | }
29 |
30 | qint64 writeData(const char *, qint64) override { return 0; }
31 | };
32 |
33 | AudioOutput::AudioOutput(const Callback &callback, QObject *parent) :
34 | QObject(parent)
35 | {
36 | m_audioDevice = new AudioDevice(callback, this);
37 | }
38 |
39 | AudioOutput::~AudioOutput()
40 | {
41 | this->stop();
42 | }
43 |
44 | void AudioOutput::updateAudioOutput(const QAudioFormat &format)
45 | {
46 | if(m_output)
47 | this->stop();
48 |
49 | if(!format.isValid())
50 | return;
51 |
52 | m_output = new QAudioOutput(format, this);
53 |
54 | // restart when encounter unexpected idle
55 | QObject::connect(m_output, &QAudioOutput::stateChanged, this,
56 | [this](QAudio::State s) {
57 | if(s == QAudio::IdleState)
58 | m_output->start(m_audioDevice);
59 | });
60 | }
61 |
62 | void AudioOutput::setVolume(qreal volume)
63 | {
64 | if (m_output)
65 | m_output->setVolume(volume);
66 | }
67 |
68 | qreal AudioOutput::volume() const
69 | {
70 | return m_output ? m_output->volume() : 0.;
71 | }
72 |
73 | void AudioOutput::play()
74 | {
75 | if (!m_output)
76 | return;
77 |
78 | m_output->start(m_audioDevice);
79 | m_bufferDuration = qreal(m_output->format().durationForBytes(m_output->bufferSize())) / 1000000;
80 |
81 | if (m_output->error() != QAudio::NoError)
82 | qCritical() << __FUNCTION__ << ":" << m_output->error();
83 | }
84 |
85 | void AudioOutput::pause()
86 | {
87 | if (m_output)
88 | m_output->reset();
89 | }
90 |
91 | void AudioOutput::stop()
92 | {
93 | if (!m_output)
94 | return;
95 |
96 | m_output->reset();
97 | m_output->deleteLater();
98 | m_output = nullptr;
99 | }
100 |
101 | void AudioOutput::reset()
102 | {
103 | if (!m_output)
104 | return;
105 |
106 | const auto previousState = m_output->state();
107 | m_output->reset();
108 |
109 | if (previousState == QAudio::ActiveState)
110 | m_output->start(m_audioDevice);
111 | }
112 |
113 | bool AudioOutput::isLowDataLeft() const
114 | {
115 | if(!m_output)
116 | return false;
117 |
118 | return m_output->bytesFree() > m_output->bufferSize() * 0.75;
119 | }
120 |
121 | qreal AudioOutput::bufferDuration() const
122 | {
123 | return m_bufferDuration;
124 | }
125 |
--------------------------------------------------------------------------------
/MultithreadedDownloader/src/downloader/multithreadeddownloader.h:
--------------------------------------------------------------------------------
1 | /**
2 | * @brief MultithreadedDownloader
3 | * @anchor Ho229<2189684957@qq.com>
4 | * @date 2021/2/1
5 | */
6 |
7 | #ifndef MULTITHREADEDDOWNLOADER_H
8 | #define MULTITHREADEDDOWNLOADER_H
9 |
10 | #include
11 |
12 | #include "downloadmission.h"
13 | #include "multithreadeddownloaderwriter.h"
14 |
15 | class QNetworkAccessManager;
16 |
17 | class MultithreadedDownloader : public AbstractMission
18 | {
19 | Q_OBJECT
20 | public:
21 | enum Error
22 | {
23 | OpenFileFailed,
24 | DownloadFailed
25 | };
26 |
27 | explicit MultithreadedDownloader(QObject *parent = nullptr);
28 | ~MultithreadedDownloader() Q_DECL_OVERRIDE;
29 |
30 | /**
31 | * @brief 设置下载线程数
32 | * @param num 下载线程数
33 | */
34 | void setThreadCount(int num){ m_threadCount = num; }
35 | int threadCount() const { return m_threadCount; }
36 |
37 | /**
38 | * @brief 初始化下载
39 | * @note 获取下载文件大小,文件名
40 | * @return 是否成功
41 | */
42 | bool load();
43 |
44 | /**
45 | * @brief 设置下载文件名
46 | * @param name 下载文件名
47 | */
48 | void setFileName(const QString& name){ m_writer->setFileName(name); }
49 | QString fileName() const { return m_writer->fileName(); }
50 |
51 | /**
52 | * @return 下载文件大小
53 | */
54 | qint64 downloadSize() const { return m_writer->size(); }
55 |
56 | /**
57 | * @return 目标服务器是否支持 Range Header
58 | * @note 如果不支持 Range,多线程下载和暂停将不可用
59 | */
60 | bool isRangeSupport() const { return m_isRangeSupport; }
61 |
62 | /**
63 | * @brief The interval at downloadProgress() will emit.
64 | * @default 500
65 | */
66 | void setNotifyInterval(int interval) { m_notifyInterval = interval; }
67 | int notifyInterval() const { return m_notifyInterval; }
68 |
69 | QNetworkReply::NetworkError networkError() const { return m_networkError; }
70 | QString networkErrorString() const { return m_networkErrorString; }
71 |
72 | void start() Q_DECL_OVERRIDE;
73 | void pause() Q_DECL_OVERRIDE;
74 | void stop() Q_DECL_OVERRIDE;
75 |
76 | signals:
77 | void error(const MultithreadedDownloader::Error err);
78 | void downloadProgress(qint64 bytesReceived, qint64 bytesTotal);
79 |
80 | private slots:
81 | void errorHanding(QNetworkReply::NetworkError err); // 错误处理
82 | void on_finished();
83 |
84 | private:
85 | QNetworkAccessManager* m_manager = nullptr;
86 | MultithreadedDownloaderWriter *m_writer = nullptr;
87 |
88 | int m_threadCount = 0;
89 | int m_finishedCount = 0;
90 |
91 | int m_timerId = 0;
92 |
93 | int m_notifyInterval = 500;
94 |
95 | bool m_isRangeSupport = false;
96 |
97 | QList m_missions;
98 |
99 | QNetworkReply::NetworkError m_networkError = QNetworkReply::NoError;
100 | QString m_networkErrorString;
101 |
102 | inline DownloadMission* createMission(qint64 start, qint64 end);
103 | inline void destoryMissions();
104 | inline void updateProgress();
105 | inline void reset();
106 |
107 | void timerEvent(QTimerEvent* event) Q_DECL_OVERRIDE; // 更新进度
108 | };
109 |
110 | #endif // MULTITHREADEDDOWNLOADER_H
111 |
--------------------------------------------------------------------------------
/VideoPlayer/src/player/videoplayer.h:
--------------------------------------------------------------------------------
1 | /**
2 | * @brief Video Player
3 | * @anchor Ho 229
4 | * @date 2021/4/14
5 | */
6 |
7 | #ifndef VIDEOPLAYER_H
8 | #define VIDEOPLAYER_H
9 |
10 | #include
11 |
12 | class VideoPlayerPrivate;
13 |
14 | class VideoPlayer : public QQuickFramebufferObject
15 | {
16 | Q_OBJECT
17 |
18 | Q_PROPERTY(QUrl source READ source WRITE setSource NOTIFY sourceChanged)
19 | Q_PROPERTY(qreal volume READ volume WRITE setVolume NOTIFY volumeChanged)
20 |
21 | Q_PROPERTY(int activeVideoTrack READ activeVideoTrack WRITE setActiveVideoTrack NOTIFY activeVideoTrackChanged)
22 | Q_PROPERTY(int activeAudioTrack READ activeAudioTrack WRITE setActiveAudioTrack NOTIFY activeAudioTrackChanged)
23 | Q_PROPERTY(int activeSubtitleTrack READ activeSubtitleTrack WRITE setActiveSubtitleTrack NOTIFY activeSubtitleTrackChanged)
24 |
25 | // Read only property
26 | Q_PROPERTY(int position READ position NOTIFY positionChanged)
27 |
28 | Q_PROPERTY(QString errorString READ errorString NOTIFY errorOccurred)
29 | Q_PROPERTY(State playbackState READ playbackState NOTIFY playbackStateChanged)
30 |
31 | Q_PROPERTY(int duration READ duration NOTIFY loaded)
32 |
33 | Q_PROPERTY(bool hasVideo READ hasVideo NOTIFY loaded)
34 | Q_PROPERTY(bool hasAudio READ hasAudio NOTIFY loaded)
35 | Q_PROPERTY(bool hasSubtitle READ hasSubtitle NOTIFY loaded)
36 |
37 | Q_PROPERTY(bool seekable READ seekable NOTIFY loaded)
38 |
39 | Q_PROPERTY(int videoTrackCount READ videoTrackCount NOTIFY loaded)
40 | Q_PROPERTY(int audioTrackCount READ audioTrackCount NOTIFY loaded)
41 | Q_PROPERTY(int subtitleTrackCount READ subtitleTrackCount NOTIFY loaded)
42 |
43 | public:
44 | enum State
45 | {
46 | Playing,
47 | Paused,
48 | Stopped
49 | };
50 | Q_ENUM(State)
51 |
52 | VideoPlayer(QQuickItem *parent = nullptr);
53 | virtual ~VideoPlayer() Q_DECL_OVERRIDE;
54 |
55 | Renderer *createRenderer() const Q_DECL_OVERRIDE;
56 |
57 | void setSource(const QUrl& source);
58 | QUrl source() const;
59 |
60 | State playbackState() const;
61 |
62 | void setVolume(qreal volume);
63 | qreal volume() const;
64 |
65 | void setActiveVideoTrack(int index);
66 | int activeVideoTrack() const;
67 |
68 | void setActiveAudioTrack(int index);
69 | int activeAudioTrack() const;
70 |
71 | void setActiveSubtitleTrack(int index);
72 | int activeSubtitleTrack() const;
73 |
74 | int videoTrackCount() const;
75 | int audioTrackCount() const;
76 | int subtitleTrackCount() const;
77 |
78 | /**
79 | * @return duration of the media in seconds.
80 | */
81 | int duration() const;
82 | int position() const;
83 |
84 | bool hasVideo() const;
85 | bool hasAudio() const;
86 | bool hasSubtitle() const;
87 |
88 | bool seekable() const;
89 |
90 | QString errorString() const;
91 |
92 | Q_INVOKABLE void play();
93 | Q_INVOKABLE void pause();
94 | Q_INVOKABLE void stop();
95 |
96 | Q_INVOKABLE void seek(int position);
97 |
98 | signals:
99 | void errorOccurred(QString);
100 |
101 | void loaded();
102 | void sourceChanged(QUrl);
103 | void playbackStateChanged(VideoPlayer::State);
104 | void volumeChanged(qreal);
105 | void positionChanged(int);
106 |
107 | void activeVideoTrackChanged(int);
108 | void activeAudioTrackChanged(int);
109 | void activeSubtitleTrackChanged(int);
110 |
111 | private:
112 | VideoPlayerPrivate *const d_ptr;
113 | Q_DECLARE_PRIVATE(VideoPlayer)
114 |
115 | void timerEvent(QTimerEvent *) Q_DECL_OVERRIDE;
116 | };
117 |
118 | #endif // VIDEOPLAYER_H
119 |
--------------------------------------------------------------------------------
/CustomWidgetDemos/src/customWidgets/rotatestackedwidget.cpp:
--------------------------------------------------------------------------------
1 | /**
2 | * Animation Stacked Widget
3 | * @brief 翻转动画 Stacked Widget
4 | * @anchor Ho229
5 | * @date 2020/12/12
6 | */
7 |
8 | #include "rotatestackedwidget.h"
9 |
10 | #include
11 | #include
12 | #include
13 |
14 | RotateStackedWidget::RotateStackedWidget(QWidget *parent) :
15 | QStackedWidget(parent),
16 | m_animation(new QVariantAnimation(this))
17 | {
18 | m_animation->setStartValue(0);
19 | m_animation->setEndValue(180);
20 | m_animation->setDuration(500);
21 | m_animation->setEasingCurve(QEasingCurve::Linear);
22 |
23 | connect(m_animation, &QVariantAnimation::valueChanged, this,
24 | [this](const QVariant value){
25 | m_rotateValue = value.toInt();
26 | this->repaint();
27 | });
28 | connect(m_animation, &QVariantAnimation::finished, this,
29 | &RotateStackedWidget::on_finished);
30 | }
31 |
32 | RotateStackedWidget::~RotateStackedWidget()
33 | {
34 |
35 | }
36 |
37 | void RotateStackedWidget::rotate(int index, bool exec)
38 | {
39 | if(m_animation->state() == QVariantAnimation::Running || this->currentIndex() == index)
40 | return;
41 |
42 | m_nextIndex = index;
43 |
44 | m_currentWidget = this->currentWidget();
45 | m_nextWidget = this->widget(m_nextIndex);
46 |
47 | if(m_nextWidget == nullptr)
48 | return;
49 |
50 | this->updateFrame();
51 |
52 | m_currentWidget->hide();
53 | m_animation->start();
54 |
55 | if(exec)
56 | {
57 | QEventLoop loop;
58 | connect(m_animation, &QVariantAnimation::finished, &loop, &QEventLoop::quit);
59 | loop.exec(QEventLoop::ExcludeUserInputEvents);
60 | }
61 | }
62 |
63 | void RotateStackedWidget::setAnimationDuration(int duration)
64 | {
65 | m_animation->setDuration(duration);
66 | }
67 |
68 | int RotateStackedWidget::animationDuration()
69 | {
70 | return m_animation->duration();
71 | }
72 |
73 | void RotateStackedWidget::setAnimationEasingCurve(const QEasingCurve& easing)
74 | {
75 | m_animation->setEasingCurve(easing);
76 | }
77 |
78 | QEasingCurve RotateStackedWidget::animationEasingCurve()
79 | {
80 | return m_animation->easingCurve();
81 | }
82 |
83 | void RotateStackedWidget::on_finished()
84 | {
85 | m_rotateValue = 0;
86 |
87 | m_nextWidget->show();
88 | m_nextWidget->raise();
89 |
90 | this->setCurrentIndex(m_nextIndex);
91 | this->repaint();
92 | }
93 |
94 | void RotateStackedWidget::updateFrame()
95 | {
96 | m_nextWidget->setGeometry(0, 0, this->width(), this->height());
97 | m_currentWidget->setGeometry(0, 0, this->width(), this->height());
98 |
99 | m_currentPixmap = QPixmap(m_currentWidget->size());
100 | m_currentWidget->render(&m_currentPixmap);
101 |
102 | m_nextPixmap = QPixmap(m_nextWidget->size());
103 | m_nextWidget->render(&m_nextPixmap);
104 | }
105 |
106 | void RotateStackedWidget::paintEvent(QPaintEvent *event)
107 | {
108 | if(m_animation->state() == QVariantAnimation::Running)
109 | {
110 | QPainter painter(this);
111 |
112 | QTransform transform;
113 | if(m_rotateValue > 90)
114 | {
115 | transform.translate(this->width() / 2, 0);
116 | transform.rotate(m_rotateValue + 180, Qt::YAxis);
117 |
118 | painter.setTransform(transform);
119 | painter.drawPixmap(-1 * this->width() / 2, 0, m_nextPixmap);
120 | }
121 | else
122 | {
123 | transform.translate(this->width() / 2, 0);
124 | transform.rotate(m_rotateValue, Qt::YAxis);
125 |
126 | painter.setTransform(transform);
127 | painter.drawPixmap(-1 * this->width() / 2, 0, m_currentPixmap);
128 | }
129 | }
130 | else
131 | QStackedWidget::paintEvent(event);
132 | }
133 |
134 | void RotateStackedWidget::resizeEvent(QResizeEvent *event)
135 | {
136 | if(m_animation->state() == QVariantAnimation::Running)
137 | this->updateFrame();
138 |
139 | QStackedWidget::resizeEvent(event);
140 | }
141 |
--------------------------------------------------------------------------------
/QmlFireworks/src/qml/main.qml:
--------------------------------------------------------------------------------
1 | import QtQuick 2.15
2 | import QtQuick.Window 2.15
3 | import QtQuick.Particles 2.12
4 |
5 | Window {
6 | width: 640
7 | height: 480
8 | visible: true
9 | title: qsTr("Fireworks")
10 |
11 | maximumHeight: height
12 | minimumHeight: height
13 | maximumWidth: width
14 | minimumWidth: width
15 |
16 | Image {
17 | id: background
18 | source: "qrc:/image/background.png"
19 | anchors.fill: parent
20 | visible: true
21 | }
22 |
23 | ParticleSystem {
24 | id: fireworksSystem
25 | anchors.fill: parent
26 |
27 | Emitter {
28 | x: 274
29 | y: 358
30 | width: 314
31 | height: 35
32 | rotation: -25
33 |
34 | group: "launch"
35 | emitRate: 2
36 | lifeSpan: 1500
37 | size: 40
38 | endSize: 10
39 |
40 | maximumEmitted: 6
41 |
42 | velocity: AngleDirection {
43 | angle: 255
44 | angleVariation: 5
45 |
46 | magnitude: 250
47 | magnitudeVariation: 25
48 | }
49 |
50 | acceleration: PointDirection { y: 100 }
51 | }
52 |
53 | ImageParticle {
54 | id: fireball
55 | groups: "launch"
56 | source: "qrc:///particleresources/star.png"
57 | colorVariation: 1
58 | }
59 |
60 | ImageParticle {
61 | id: flame
62 | groups: ["flame", "burstFlame"]
63 | source: "qrc:///particleresources/glowdot.png"
64 | }
65 |
66 | TrailEmitter {
67 | id: fireballFlame
68 | anchors.fill: parent
69 | group: "flame"
70 | follow: "launch"
71 |
72 | emitRatePerParticle: 50
73 | lifeSpan: 400
74 | lifeSpanVariation: 50
75 |
76 | size: 10
77 | endSize: 3
78 |
79 |
80 |
81 | onEmitFollowParticles: {
82 | for(var i = 0; i < particles.length; i++) {
83 | particles[i].red = followed.red;
84 | particles[i].green = followed.green;
85 | particles[i].blue = followed.blue;
86 | }
87 | }
88 | }
89 |
90 | ImageParticle {
91 | id: burstFirework
92 | groups: "burst"
93 | source: "qrc:///particleresources/star.png"
94 | }
95 |
96 | Emitter {
97 | id: burstEmitter
98 | group: "burst"
99 | lifeSpan: 2300
100 | lifeSpanVariation: 200
101 |
102 | size: 30
103 | endSize: 15
104 |
105 | enabled: false
106 | velocity: AngleDirection {
107 | angleVariation: 360
108 | magnitudeVariation: 60
109 | }
110 | acceleration: PointDirection { y: 15 }
111 | }
112 |
113 | TrailEmitter {
114 | group: "burstFlame"
115 | follow: "burst"
116 | anchors.fill: parent
117 |
118 | lifeSpan: 400
119 |
120 | emitRatePerParticle: 30
121 | size: 7
122 | endSize: 5
123 |
124 | onEmitFollowParticles: {
125 | for(var i = 0; i < particles.length; i++) {
126 | particles[i].red = followed.red;
127 | particles[i].green = followed.green;
128 | particles[i].blue = followed.blue;
129 | //flame.color = Qt.rgba(followed.red, followed.green, followed.blue, 1); Linux use this
130 | }
131 | }
132 | }
133 |
134 | Affector {
135 | x: 17
136 | y: 46
137 | width: 607
138 | height: 192
139 | once: true
140 | groups: "launch"
141 |
142 | onAffectParticles: {
143 | for(const particle of particles) {
144 | if(particle.lifeLeft() < 0.02) {
145 | burstFirework.color = Qt.hsva(Math.random(), 1, 1, 1);
146 | burstEmitter.burst(150, particle.x, particle.y);
147 | }
148 | }
149 | }
150 | }
151 | }
152 | }
153 |
--------------------------------------------------------------------------------
/CustomWidgetDemos/src/customWidgets/notifywidget.cpp:
--------------------------------------------------------------------------------
1 | /**
2 | * Notify Widget
3 | * @brief 右下角提示窗
4 | * @anchor Ho229<2189684957@qq.com>
5 | * @date 2021/2/1
6 | */
7 |
8 | #include "notifywidget.h"
9 |
10 | #include
11 | #include
12 | #include
13 | #include
14 | #include
15 | #include
16 | #include
17 | #include
18 |
19 | #include "countdownbutton.h"
20 |
21 | NotifyWidget::NotifyWidget(QWidget *parent, const QString &title,
22 | const QString &messsage, const QIcon& icon,
23 | const QString &style) :
24 | QWidget(parent),
25 | m_iconLabel(new QLabel(this)),
26 | m_titleLabel(new QLabel(this)),
27 | m_messageLabel(new QLabel(this)),
28 | m_hLayout(new QHBoxLayout()),
29 | m_mLayout(new QHBoxLayout()),
30 | m_vLayout(new QVBoxLayout(this)),
31 | m_hSpacer(new QSpacerItem(40, 20, QSizePolicy::Expanding, QSizePolicy::Minimum)),
32 | m_icon(icon),
33 | m_closeButton(new CountdownButton(this)),
34 | m_animation(new QPropertyAnimation(this, "pos", this))
35 | {
36 | // Title and close button
37 | m_hLayout->addWidget(m_titleLabel);
38 | m_hLayout->addSpacerItem(m_hSpacer);
39 | m_hLayout->addWidget(m_closeButton);
40 |
41 | // Icon and message
42 | m_mLayout->addWidget(m_iconLabel);
43 | m_mLayout->addWidget(m_messageLabel);
44 | m_mLayout->setStretch(1, 1);
45 | m_mLayout->setSpacing(9);
46 |
47 | m_vLayout->addLayout(m_hLayout);
48 | m_vLayout->addLayout(m_mLayout);
49 |
50 | QFont font = m_titleLabel->font();
51 | font.setPointSize(15);
52 | font.setBold(true);
53 | m_titleLabel->setFont(font);
54 | m_titleLabel->setText(title);
55 | m_titleLabel->setMaximumWidth(300);
56 |
57 | if(m_icon.isNull())
58 | m_iconLabel->hide();
59 | else
60 | {
61 | m_iconLabel->setPixmap(m_icon.pixmap(m_iconLabel->size()));
62 | m_iconLabel->adjustSize();
63 | }
64 |
65 | m_messageLabel->setText(messsage);
66 | m_messageLabel->setWordWrap(true);
67 | m_messageLabel->setMaximumSize(QSize(300, 70));
68 |
69 | m_closeButton->setFixedSize(QSize(30, 30));
70 | m_closeButton->setIcon(QIcon(":/image/resource/image/close.png"));
71 |
72 | QObject::connect(m_closeButton, &CountdownButton::clicked, this,
73 | &NotifyWidget::closeAnimation);
74 |
75 | m_animation->setDuration(350);
76 | m_animation->setEasingCurve(QEasingCurve::OutQuart);
77 |
78 | this->setAttribute(Qt::WA_DeleteOnClose);
79 | this->setWindowFlags(Qt::FramelessWindowHint | Qt::Tool | Qt::WindowStaysOnTopHint
80 | | Qt::WindowSystemMenuHint);
81 | this->setStyleSheet(style);
82 | this->setLayout(m_vLayout);
83 | this->adjustSize();
84 | }
85 |
86 | NotifyWidget::~NotifyWidget()
87 | {
88 |
89 | }
90 |
91 | void NotifyWidget::setCloseCountdown(int ms)
92 | {
93 | m_closeButton->conutdownCilk(ms);
94 | }
95 |
96 | int NotifyWidget::closeCountdown() const
97 | {
98 | return m_closeButton->countdown();
99 | }
100 |
101 | void NotifyWidget::animatMove(int x, int y)
102 | {
103 | if(m_animation->state() == QPropertyAnimation::Running)
104 | m_animation->stop();
105 |
106 | m_animation->setEndValue(QPoint(x, y));
107 | m_animation->setStartValue(this->pos());
108 |
109 | m_animation->start();
110 | }
111 |
112 | int NotifyWidget::leftTime() const
113 | {
114 | return m_animation->duration() - m_animation->currentLoopTime();
115 | }
116 |
117 | void NotifyWidget::showEvent(QShowEvent *event)
118 | {
119 | this->animatMove(this->x() - this->width(), this->y());
120 |
121 | return QWidget::showEvent(event);
122 | }
123 |
124 | void NotifyWidget::closeAnimation()
125 | {
126 | m_isClosing = true;
127 | this->animatMove(this->x() + this->width(), this->y());
128 |
129 | QObject::connect(m_animation, &QPropertyAnimation::finished, this,
130 | [this]{
131 | if(QGuiApplication::primaryScreen()->size().width() == this->x())
132 | this->close();
133 | });
134 | }
135 | void NotifyWidget::closeEvent(QCloseEvent *event)
136 | {
137 | emit closed();
138 | return QWidget::closeEvent(event);
139 | }
140 |
141 | void NotifyWidget::mouseReleaseEvent(QMouseEvent *event)
142 | {
143 | emit clicked();
144 | return QWidget::mouseReleaseEvent(event);
145 | }
146 |
--------------------------------------------------------------------------------
/CustomWidgetDemos/src/customWidgets/translationstackedwidget.cpp:
--------------------------------------------------------------------------------
1 | /**
2 | * Animation Stacked Widget
3 | * @brief 平移动画 Stacked Widget
4 | * @anchor Ho229
5 | * @date 2020/12/12
6 | */
7 |
8 | #include "translationstackedwidget.h"
9 |
10 | #include
11 | #include
12 | #include
13 |
14 | TranslationStackedWidget::TranslationStackedWidget(QWidget *parent) :
15 | QStackedWidget(parent),
16 | m_animation(new QVariantAnimation(this))
17 | {
18 | m_animation->setDuration(500);
19 | m_animation->setEasingCurve(QEasingCurve::OutQuint);
20 |
21 | connect(m_animation, &QVariantAnimation::finished, this,
22 | &TranslationStackedWidget::on_finished);
23 | connect(m_animation, &QVariantAnimation::valueChanged, this,
24 | [this](const QVariant value){
25 | m_animationX = value.toInt();
26 | this->repaint();
27 | });
28 | }
29 |
30 | TranslationStackedWidget::~TranslationStackedWidget()
31 | {
32 |
33 | }
34 |
35 | void TranslationStackedWidget::moveToIndex(int index, bool exec)
36 | {
37 | if(m_animation->state() == QVariantAnimation::Running || this->currentIndex() == index)
38 | return;
39 |
40 | m_nextIndex = index;
41 |
42 | m_currentWidget = this->currentWidget();
43 | m_nextWidget = this->widget(index);
44 |
45 | if(m_nextWidget == nullptr)
46 | return;
47 |
48 | this->updateFrame();
49 | this->updateAnimation();
50 |
51 | m_currentWidget->hide();
52 | m_animation->start();
53 |
54 | if(exec)
55 | {
56 | QEventLoop loop;
57 | connect(m_animation, &QVariantAnimation::finished, &loop, &QEventLoop::quit);
58 | loop.exec(QEventLoop::ExcludeUserInputEvents);
59 | }
60 | }
61 |
62 | void TranslationStackedWidget::setAnimationDuration(int duration)
63 | {
64 | m_animation->setDuration(duration);
65 | }
66 |
67 | int TranslationStackedWidget::animationDuration()
68 | {
69 | return m_animation->duration();
70 | }
71 |
72 | void TranslationStackedWidget::setAnimationEasingCurve(const QEasingCurve &easing)
73 | {
74 | m_animation->setEasingCurve(easing);
75 | }
76 |
77 | QEasingCurve TranslationStackedWidget::animationEasingCurve()
78 | {
79 | return m_animation->easingCurve();
80 | }
81 |
82 | void TranslationStackedWidget::on_finished()
83 | {
84 | m_nextWidget->show();
85 | m_nextWidget->raise();
86 |
87 | this->setCurrentIndex(m_nextIndex);
88 | this->repaint();
89 | }
90 |
91 | QPixmap TranslationStackedWidget::mergePixmap(const QPixmap &leftPixmap, const QPixmap &rightPixmap)
92 | {
93 | QPixmap newPixmap(leftPixmap.width() + rightPixmap.width(),
94 | qMax(leftPixmap.height(), rightPixmap.height()));
95 |
96 | QPainter painter(&newPixmap);
97 |
98 | painter.drawPixmap(QPoint(0, 0), leftPixmap);
99 | painter.drawPixmap(QPoint(leftPixmap.width(), 0), rightPixmap);
100 |
101 | return newPixmap;
102 | }
103 |
104 | void TranslationStackedWidget::updateFrame()
105 | {
106 | m_nextWidget->setGeometry(0, 0, this->width(), this->height());
107 | m_currentWidget->setGeometry(0, 0, this->width(), this->height());
108 |
109 | m_nextWidget->setGeometry(0, 0, this->width(), this->height());
110 |
111 | QPixmap currentPixmap(m_currentWidget->size());
112 | m_currentWidget->render(¤tPixmap);
113 |
114 | QPixmap nextPixmap(m_nextWidget->size());
115 | m_nextWidget->render(&nextPixmap);
116 |
117 | if(m_nextIndex < this->currentIndex())
118 | m_animationPixmap = mergePixmap(nextPixmap, currentPixmap);
119 | else
120 | m_animationPixmap = mergePixmap(currentPixmap, nextPixmap);
121 | }
122 |
123 |
124 | void TranslationStackedWidget::updateAnimation()
125 | {
126 | if(m_nextIndex < this->currentIndex())
127 | {
128 | // Last page
129 | m_animation->setStartValue(-this->width() + 1);
130 | m_animation->setEndValue(0);
131 | }
132 | else
133 | {
134 | // Next page
135 | m_animation->setStartValue(0);
136 | m_animation->setEndValue(-this->width() - 1);
137 | }
138 | }
139 |
140 | void TranslationStackedWidget::paintEvent(QPaintEvent *event)
141 | {
142 | if(m_animation->state() == QVariantAnimation::Running)
143 | {
144 | QPainter painter(this);
145 | painter.drawPixmap(QPoint(m_animationX, 0), m_animationPixmap);
146 | }
147 | else
148 | QStackedWidget::paintEvent(event);
149 | }
150 |
151 | void TranslationStackedWidget::resizeEvent(QResizeEvent *event)
152 | {
153 | if(m_animation->state() == QVariantAnimation::Running)
154 | {
155 | this->updateFrame();
156 | this->updateAnimation();
157 | }
158 |
159 | QStackedWidget::resizeEvent(event);
160 | }
161 |
--------------------------------------------------------------------------------
/MultithreadedDownloader/src/customWidgets/translationstackedwidget.cpp:
--------------------------------------------------------------------------------
1 | /**
2 | * Animation Stacked Widget
3 | * @brief 平移动画 Stacked Widget
4 | * @anchor Ho229
5 | * @date 2020/12/12
6 | */
7 |
8 | #include "translationstackedwidget.h"
9 |
10 | #include
11 | #include
12 | #include
13 |
14 | TranslationStackedWidget::TranslationStackedWidget(QWidget *parent) :
15 | QStackedWidget(parent),
16 | m_animation(new QVariantAnimation(this))
17 | {
18 | m_animation->setDuration(350);
19 | m_animation->setEasingCurve(QEasingCurve::InCubic);
20 |
21 | connect(m_animation, &QVariantAnimation::finished, this,
22 | &TranslationStackedWidget::on_finished);
23 | connect(m_animation, &QVariantAnimation::valueChanged, this,
24 | [this](const QVariant value){
25 | m_animationX = value.toInt();
26 | this->repaint();
27 | });
28 | }
29 |
30 | TranslationStackedWidget::~TranslationStackedWidget()
31 | {
32 |
33 | }
34 |
35 | void TranslationStackedWidget::moveToIndex(int index, bool exec)
36 | {
37 | if(m_animation->state() == QVariantAnimation::Running || this->currentIndex() == index)
38 | return;
39 |
40 | m_nextIndex = index;
41 |
42 | m_currentWidget = this->currentWidget();
43 | m_nextWidget = this->widget(index);
44 |
45 | if(m_nextWidget == nullptr)
46 | return;
47 |
48 | this->updateFrame();
49 | this->updateAnimation();
50 |
51 | m_currentWidget->hide();
52 | m_animation->start();
53 |
54 | if(exec)
55 | {
56 | QEventLoop loop;
57 | connect(m_animation, &QVariantAnimation::finished, &loop, &QEventLoop::quit);
58 | loop.exec(QEventLoop::ExcludeUserInputEvents);
59 | }
60 | }
61 |
62 | void TranslationStackedWidget::setAnimationDuration(int duration)
63 | {
64 | m_animation->setDuration(duration);
65 | }
66 |
67 | int TranslationStackedWidget::animationDuration()
68 | {
69 | return m_animation->duration();
70 | }
71 |
72 | void TranslationStackedWidget::setAnimationEasingCurve(const QEasingCurve &easing)
73 | {
74 | m_animation->setEasingCurve(easing);
75 | }
76 |
77 | QEasingCurve TranslationStackedWidget::animationEasingCurve()
78 | {
79 | return m_animation->easingCurve();
80 | }
81 |
82 | void TranslationStackedWidget::on_finished()
83 | {
84 | m_nextWidget->show();
85 | m_nextWidget->raise();
86 |
87 | this->setCurrentIndex(m_nextIndex);
88 | this->repaint();
89 | }
90 |
91 | QPixmap TranslationStackedWidget::mergePixmap(const QPixmap &leftPixmap, const QPixmap &rightPixmap)
92 | {
93 | QPixmap newPixmap(leftPixmap.width() + rightPixmap.width(),
94 | qMax(leftPixmap.height(), rightPixmap.height()));
95 |
96 | QPainter painter(&newPixmap);
97 |
98 | painter.drawPixmap(QPoint(0, 0), leftPixmap);
99 | painter.drawPixmap(QPoint(leftPixmap.width(), 0), rightPixmap);
100 |
101 | return newPixmap;
102 | }
103 |
104 | void TranslationStackedWidget::updateFrame()
105 | {
106 | m_nextWidget->setGeometry(0, 0, this->width(), this->height());
107 | m_currentWidget->setGeometry(0, 0, this->width(), this->height());
108 |
109 | m_nextWidget->setGeometry(0, 0, this->width(), this->height());
110 |
111 | QPixmap currentPixmap(m_currentWidget->size());
112 | m_currentWidget->render(¤tPixmap);
113 |
114 | QPixmap nextPixmap(m_nextWidget->size());
115 | m_nextWidget->render(&nextPixmap);
116 |
117 | if(m_nextIndex < this->currentIndex())
118 | m_animationPixmap = mergePixmap(nextPixmap, currentPixmap);
119 | else
120 | m_animationPixmap = mergePixmap(currentPixmap, nextPixmap);
121 | }
122 |
123 |
124 | void TranslationStackedWidget::updateAnimation()
125 | {
126 | if(m_nextIndex < this->currentIndex())
127 | {
128 | // Last page
129 | m_animation->setStartValue(0 - this->width());
130 | m_animation->setEndValue(0);
131 | }
132 | else
133 | {
134 | // Next page
135 | m_animation->setStartValue(0);
136 | m_animation->setEndValue(0 - this->width());
137 | }
138 | }
139 |
140 | void TranslationStackedWidget::paintEvent(QPaintEvent *event)
141 | {
142 | if(m_animation->state() == QVariantAnimation::Running)
143 | {
144 | QPainter painter(this);
145 | painter.drawPixmap(QPoint(m_animationX, 0), m_animationPixmap);
146 | }
147 | else
148 | QStackedWidget::paintEvent(event);
149 | }
150 |
151 | void TranslationStackedWidget::resizeEvent(QResizeEvent *event)
152 | {
153 | if(m_animation->state() == QVariantAnimation::Running)
154 | {
155 | this->updateFrame();
156 | this->updateAnimation();
157 | }
158 |
159 | QStackedWidget::resizeEvent(event);
160 | }
161 |
--------------------------------------------------------------------------------
/QmlDemo/main.qml:
--------------------------------------------------------------------------------
1 | import QtQuick 2.12
2 | import QtQuick.Window 2.12
3 | import QtQuick.Controls 2.12
4 | import QtQuick.Layouts 1.12
5 |
6 | import com.MyItems.Effect 1.0
7 |
8 | Window {
9 | id: root
10 | width: 550
11 | height: 400
12 | visible: true
13 | title: qsTr("Hello World")
14 |
15 | minimumWidth: 400
16 | minimumHeight: 350
17 |
18 | ColumnLayout {
19 | anchors.fill: parent
20 | spacing: 6
21 |
22 | GroupBox {
23 | id:fontStyleGroup
24 | title: qsTr("Font Style")
25 | Layout.fillWidth: true
26 | Layout.margins: 9
27 |
28 | RowLayout {
29 | anchors.fill: parent
30 |
31 | CheckBox {
32 | id: underlineCheck
33 | text: qsTr("Underline")
34 |
35 | onCheckedChanged: {
36 | textEdit.font.underline = checkState == Qt.Checked
37 | }
38 | }
39 | CheckBox {
40 | id: italicCheck
41 | text: qsTr("Italic")
42 |
43 | onCheckStateChanged: {
44 | textEdit.font.italic = checkState == Qt.Checked
45 | }
46 | }
47 | CheckBox {
48 | id: boldCheck
49 | text: qsTr("Bold")
50 |
51 | onCheckStateChanged: {
52 | textEdit.font.bold = checkState == Qt.Checked
53 | }
54 | }
55 | }
56 | }
57 |
58 | GroupBox {
59 | id: fontColorGroup
60 | title: qsTr("Color")
61 | Layout.fillWidth: true
62 | Layout.margins: 9
63 |
64 | RowLayout {
65 | anchors.fill: parent
66 |
67 | RadioButton {
68 | id: blackRadio
69 | text: qsTr("Black")
70 | hoverEnabled: true
71 | checked: true
72 |
73 | onClicked: {
74 | textEdit.color = "black"
75 | }
76 | }
77 | RadioButton {
78 | id: redRadio
79 | text: qsTr("Red")
80 |
81 | onClicked: {
82 | textEdit.color = "red"
83 | }
84 | }
85 | RadioButton {
86 | id: blueRadio
87 | text: qsTr("Blud")
88 |
89 | onClicked: {
90 | textEdit.color = "blue"
91 | }
92 | }
93 | }
94 | }
95 |
96 | TextField {
97 | id: textEdit
98 | text: qsTr("Hello World")
99 | Layout.fillWidth: true
100 | Layout.fillHeight: true
101 | Layout.margins: 9
102 |
103 | font.pointSize: 20
104 | horizontalAlignment: Text.AlignHCenter
105 | verticalAlignment: Text.AlignVCenter
106 | selectionColor: "#0000ff"
107 |
108 | selectByMouse: true
109 | readOnly: true
110 |
111 | background: ClickWaveEffect {
112 | target: textEdit
113 |
114 | maxRadius: 20
115 | waveDuration: 100
116 |
117 | anchors.fill: parent
118 | }
119 | }
120 |
121 | RowLayout {
122 | id: rowLayout
123 | Layout.preferredWidth: parent.width
124 |
125 | Button {
126 | id: myButton
127 | text: qsTr("Push Button")
128 | Layout.margins: 9
129 | Layout.alignment: Qt.AlignLeft //| Qt.AlignHCenter
130 |
131 | background: Rectangle {
132 | implicitWidth: 100
133 | implicitHeight: 40
134 |
135 | color: "#E0E0E0"
136 |
137 | ClickWaveEffect {
138 | target: myButton
139 |
140 | anchors.fill: parent
141 | }
142 | }
143 | }
144 |
145 | Button {
146 | id: closeButton
147 | text: qsTr("Close")
148 | Layout.alignment: Qt.AlignRight //| Qt.AlignHCenter
149 | Layout.margins: 9
150 |
151 | background: Rectangle {
152 | implicitWidth: 100
153 | implicitHeight: 40
154 |
155 | color: "#E0E0E0"
156 |
157 | ClickWaveEffect {
158 | target: closeButton
159 |
160 | anchors.fill: parent
161 | }
162 | }
163 |
164 | onClicked: root.close()
165 | }
166 | }
167 | }
168 | }
169 |
--------------------------------------------------------------------------------
/VideoPlayer/src/player/ffmpegdecoder.h:
--------------------------------------------------------------------------------
1 | /**
2 | * @brief FFmpeg decoder
3 | * @anchor Ho 229
4 | * @date 2021/4/13
5 | */
6 |
7 | #ifndef FFMPEGDECODER_H
8 | #define FFMPEGDECODER_H
9 |
10 | #include
11 | #include
12 | #include
13 | #include
14 | #include
15 | #include
16 | #include
17 | #include
18 | #include
19 | #include
20 |
21 | #include
22 |
23 | #define FUNC_ERROR qCritical() << __FUNCTION__
24 |
25 | struct SubtitleFrame
26 | {
27 | SubtitleFrame(int width, int height) :
28 | image(width, height, QImage::Format_RGBA8888)
29 | { image.fill(Qt::transparent); }
30 |
31 | QImage image;
32 | qreal start = 0;
33 | };
34 |
35 | class FFmpegDecoder final : public QObject
36 | {
37 | Q_OBJECT
38 | public:
39 | enum State
40 | {
41 | Opened,
42 | Closed
43 | };
44 | Q_ENUM(State)
45 |
46 | explicit FFmpegDecoder(QObject *parent = nullptr);
47 | ~FFmpegDecoder() Q_DECL_OVERRIDE;
48 |
49 | /**
50 | * @brief Request the interruption of the FFmpegDecoer::decode
51 | */
52 | void requestInterrupt() { m_runnable = false; }
53 |
54 | void setUrl(const QUrl& url) { m_url = url; }
55 | QUrl url() const { return m_url; }
56 |
57 | State state() const { return m_state; }
58 |
59 | QString errorString() const { return m_errorBuf; }
60 |
61 | int activeVideoTrack() const;
62 | int activeAudioTrack() const;
63 | int activeSubtitleTrack() const;
64 |
65 | int videoTrackCount() const;
66 | int audioTrackCount() const;
67 | int subtitleTrackCount() const;
68 |
69 | bool hasFrame() const { return m_videoCache.count() || m_audioCache.count(); }
70 |
71 | bool seekable() const;
72 |
73 | bool isEnd() const { return m_isEnd; }
74 |
75 | /**
76 | * @return duration of the media in seconds.
77 | */
78 | int duration() const;
79 |
80 | const QAudioFormat audioFormat() const;
81 |
82 | AVFrame *takeVideoFrame();
83 | AVFrame *takeAudioFrame();
84 | SubtitleFrame *takeSubtitleFrame(qreal time);
85 |
86 | /**
87 | * @return qQNaN() if not available(eg. no video frames or only a single frame like album cover),
88 | * otherwise returns frame rate of video stream
89 | */
90 | qreal fps() const;
91 |
92 | static qreal framePts(const AVFrame *frame);
93 | static qreal frameDuration(const AVFrame *frame);
94 |
95 | signals:
96 | void stateChanged(FFmpegDecoder::State);
97 |
98 | void activeVideoTrackChanged(int);
99 | void activeAudioTrackChanged(int);
100 | void activeSubtitleTrackChanged(int);
101 |
102 | public slots:
103 | void load();
104 | void release();
105 |
106 | void seek(int position);
107 |
108 | void setActiveVideoTrack(int index);
109 | void setActiveAudioTrack(int index);
110 | void setActiveSubtitleTrack(int index);
111 |
112 | void decode();
113 |
114 | private:
115 | void decodeVideo(AVPacket *packet);
116 | void decodeAudio(AVPacket *packet);
117 | void decodeSubtitle(AVPacket *packet);
118 |
119 | bool shouldDecode() const;
120 |
121 | void clearCache();
122 |
123 | bool openCodecContext(AVStream *&stream, AVCodecContext *&codecContext,
124 | AVMediaType type, int index);
125 | void closeCodecContext(AVStream *&stream, AVCodecContext *&codecContext);
126 |
127 | bool openSubtitleFilter(const QString &args, const QString &filterDesc);
128 | void closeSubtitleFilter();
129 |
130 | private:
131 | State m_state = Closed;
132 |
133 | char m_errorBuf[AV_ERROR_MAX_STRING_SIZE];
134 |
135 | mutable QMutex m_mutex;
136 |
137 | QUrl m_url;
138 |
139 | AVFormatContext *m_formatContext = nullptr;
140 |
141 | AVStream *m_videoStream = nullptr;
142 | AVCodecContext *m_videoCodecContext = nullptr;
143 |
144 | AVStream *m_audioStream = nullptr;
145 | AVCodecContext *m_audioCodecContext = nullptr;
146 |
147 | AVStream *m_subtitleStream = nullptr;
148 | AVCodecContext *m_subtitleCodecContext = nullptr;
149 |
150 | AVFilterGraph *m_filterGraph = nullptr;
151 | AVFilterContext *m_buffersrcContext = nullptr;
152 | AVFilterContext *m_buffersinkContext = nullptr;
153 |
154 | SwrContext *m_swrContext = nullptr;
155 | SwsContext *m_swsContext = nullptr;
156 |
157 | QContiguousCache m_videoCache;
158 | QContiguousCache m_audioCache;
159 | QContiguousCache m_subtitleCache;
160 |
161 | qreal m_fps = qQNaN(); // See also FFmpegDecoder::fps()
162 |
163 | volatile bool m_isDecoding = false;
164 | volatile bool m_runnable = false; // Is FFmpegDecoder::decode() could run
165 | volatile bool m_isEnd = false;
166 |
167 | int m_seekTarget = -1; // -1 means undefined
168 |
169 | QList m_videoIndexes;
170 | QList m_audioIndexes;
171 | QList m_subtitleIndexes;
172 | int m_subtitleIndex = -1;
173 | };
174 |
175 | #endif // FFMPEGDECODER_H
176 |
--------------------------------------------------------------------------------
/QmlDemo/src/customItem/clickwaveeffect.cpp:
--------------------------------------------------------------------------------
1 | /**
2 | * Click Wave Effect
3 | * @anchor Ho 229
4 | * @date 2021/7/18
5 | */
6 |
7 | #include "clickwaveeffect.h"
8 |
9 | #include
10 | #include
11 | #include
12 |
13 | ClickWaveEffect::ClickWaveEffect(QQuickItem *parent)
14 | : QQuickPaintedItem(parent),
15 | m_animation(new QSequentialAnimationGroup(this)),
16 | m_alphaAnimation(new QVariantAnimation(this)),
17 | m_radiusAnimation(new QVariantAnimation(this))
18 | {
19 | m_color = {207, 207, 207}; // Default color
20 |
21 | m_alphaAnimation->setStartValue(255);
22 | m_alphaAnimation->setEndValue(0);
23 | m_alphaAnimation->setDuration(200);
24 |
25 | m_radiusAnimation->setStartValue(0);
26 | m_radiusAnimation->setDuration(400);
27 |
28 | m_animation->addAnimation(m_radiusAnimation);
29 | m_animation->addAnimation(m_alphaAnimation);
30 |
31 | QObject::connect(m_radiusAnimation, &QVariantAnimation::valueChanged, this,
32 | [this](const QVariant& value){
33 | m_radius = value.toInt();
34 | this->update();
35 | });
36 | QObject::connect(m_alphaAnimation, &QVariantAnimation::valueChanged, this,
37 | [this](const QVariant& value){
38 | m_currentColor.setAlpha(value.toInt());
39 | this->update();
40 | });
41 | QObject::connect(m_radiusAnimation, &QVariantAnimation::finished, this,
42 | [this]{
43 | if(m_isPressed)
44 | m_animation->pause();
45 | });
46 | QObject::connect(m_animation, &QSequentialAnimationGroup::finished, this,
47 | &ClickWaveEffect::on_finished);
48 | }
49 |
50 | ClickWaveEffect::~ClickWaveEffect()
51 | {
52 |
53 | }
54 |
55 | void ClickWaveEffect::setTarget(QQuickItem *target)
56 | {
57 | if(!target)
58 | return;
59 |
60 | target->installEventFilter(this);
61 | m_target = target;
62 |
63 | emit targetChanged(target);
64 | }
65 |
66 | void ClickWaveEffect::setColor(const QColor &color)
67 | {
68 | m_alphaAnimation->setStartValue(color.alpha());
69 | m_color = color;
70 | emit colorChanged(color);
71 | }
72 |
73 | void ClickWaveEffect::setWaveDuration(int duration)
74 | {
75 | m_radiusAnimation->setDuration(duration);
76 | emit waveDurationChanged(duration);
77 | }
78 |
79 | int ClickWaveEffect::waveDuration() const
80 | {
81 | return m_radiusAnimation->duration();
82 | }
83 |
84 | void ClickWaveEffect::setMaxRadius(int radius)
85 | {
86 | m_maxRadius = radius;
87 | emit maxRadiusChanged(radius);
88 | }
89 |
90 | void ClickWaveEffect::paint(QPainter *painter)
91 | {
92 | painter->setPen(Qt::NoPen);
93 | painter->setBrush(m_currentColor);
94 | painter->drawEllipse(m_pos, m_radius, m_radius);
95 | }
96 |
97 | bool ClickWaveEffect::eventFilter(QObject *obj, QEvent *event)
98 | {
99 | if(obj == m_target)
100 | {
101 | if(event->type() == QEvent::MouseButtonPress)
102 | {
103 | QMouseEvent *mouseEvent = static_cast(event);
104 | m_pos = mouseEvent->pos();
105 | m_isPressed = true;
106 |
107 | if(m_animation->state() == QSequentialAnimationGroup::Running)
108 | {
109 | m_animation->stop();
110 | m_radius = 0;
111 | }
112 |
113 | m_currentColor = m_color;
114 |
115 | m_radiusAnimation->setEndValue(
116 | m_maxRadius < 0 ? maxRadius(m_pos, this->size().toSize()) : m_maxRadius);
117 |
118 | m_animation->start();
119 | }
120 | else if(event->type() == QEvent::MouseButtonRelease)
121 | {
122 | m_isPressed = false;
123 |
124 | if(m_animation->state() == QSequentialAnimationGroup::Paused)
125 | m_animation->resume();
126 | }
127 | }
128 |
129 | return QQuickPaintedItem::eventFilter(obj, event);
130 | }
131 |
132 | void ClickWaveEffect::on_finished()
133 | {
134 | m_radius = 0;
135 | emit finished();
136 | }
137 |
138 | int ClickWaveEffect::maxRadius(const QPoint &pos, const QSize &size)
139 | {
140 | int radius = 0;
141 |
142 | auto dist = [](const QPoint& x, const QPoint& y) -> int {
143 | const QPoint diff = x - y;
144 | return diff.manhattanLength();
145 | };
146 |
147 | if(pos.x() < size.width() / 2) // Left
148 | {
149 | if(pos.y() < size.height() / 2) // Top
150 | radius = dist(pos, {size.width(), size.height()}); // RightBottom
151 | else // Bottom
152 | radius = dist(pos, {size.width(), 0}); // RightTop
153 | }
154 | else // Right
155 | {
156 | if(pos.y() < size.height() / 2) // Top
157 | radius = dist(pos, {0, size.height()}); // LeftBottom
158 | else // Bottom
159 | radius = dist(pos, {0, 0}); // RightBottom
160 | }
161 |
162 | return radius;
163 | }
164 |
--------------------------------------------------------------------------------
/MultithreadedDownloader/src/mainwidget.cpp:
--------------------------------------------------------------------------------
1 | /**
2 | * @brief MainWidget
3 | * @anchor Ho229<2189684957@qq.com>
4 | * @date 2021/2/1
5 | */
6 |
7 | #include "util.h"
8 | #include "mainwidget.h"
9 | #include "ui_mainwidget.h"
10 |
11 | #include
12 | #include
13 |
14 | MainWidget::MainWidget(QWidget *parent)
15 | : QWidget(parent)
16 | , ui(new Ui::MainWidget),
17 | m_downloader(new MultithreadedDownloader(this)),
18 | m_toast(new Toast(this))
19 | {
20 | ui->setupUi(this);
21 |
22 | m_oldProgressedBytes = 0;
23 | m_old_progressed_bytes = 0;
24 |
25 | this->initUI();
26 | this->initSignalSlots();
27 | }
28 |
29 | MainWidget::~MainWidget()
30 | {
31 | delete ui;
32 | }
33 |
34 | void MainWidget::initDownloadUrl(const QString &url)
35 | {
36 | if(url.isEmpty())
37 | return;
38 |
39 | ui->urlEdit->setPlainText(url);
40 | this->on_downloadBtn_clicked();
41 | }
42 |
43 | inline void MainWidget::initUI()
44 | {
45 | ui->failedLabel->hide();
46 | ui->retranslateUi(this);
47 | }
48 |
49 | inline void MainWidget::initSignalSlots()
50 | {
51 | QObject::connect(m_downloader, &MultithreadedDownloader::finished, this,
52 | [this]{
53 | QMessageBox::information(this, tr("infomation"), tr("download finished."));
54 | ui->stackedWidget->moveToIndex(0);
55 | });
56 |
57 | QObject::connect(m_downloader, &MultithreadedDownloader::error, this,
58 | [this](MultithreadedDownloader::Error err){
59 | if(err == MultithreadedDownloader::OpenFileFailed)
60 | {
61 | QMessageBox::critical(this, tr("error"), tr("File can not open."));
62 | this->on_stopBtn_clicked();
63 | }
64 | else
65 | {
66 | if(QMessageBox::critical(this, tr("error"),
67 | tr("Download Failed.\nNetwork Error:%1\nRetry ?")
68 | .arg(m_downloader->networkErrorString()),
69 | QMessageBox::Yes | QMessageBox::No) == QMessageBox::Yes)
70 | this->on_startBtn_clicked();
71 | else
72 | this->on_stopBtn_clicked();
73 | }
74 | });
75 |
76 | QObject::connect(m_downloader, &MultithreadedDownloader::stateChanged, this,
77 | [this](MultithreadedDownloader::State state){
78 | switch (state)
79 | {
80 | case MultithreadedDownloader::Running:
81 | ui->stateLabel->setText(tr("Running"));
82 | break;
83 | case MultithreadedDownloader::Paused:
84 | ui->stateLabel->setText(tr("Paused"));
85 | break;
86 | case MultithreadedDownloader::Stopped:
87 | ui->stateLabel->setText(tr("Stopped"));
88 | break;
89 | }
90 | });
91 |
92 | QObject::connect(m_downloader, &MultithreadedDownloader::downloadProgress, this,
93 | [this](qint64 bytesReceived, qint64 bytesTotal){
94 |
95 | ui->progressBar->setValue(static_cast(
96 | static_cast(bytesReceived) / bytesTotal * 100));
97 |
98 | ui->byteLabel->setText(tr("Speed: %1/s | %2 / %3")
99 | .arg(Until::readableFileSize(
100 | static_cast(
101 | static_cast(bytesReceived - m_oldProgressedBytes)
102 | / (1000 / m_downloader->notifyInterval()))))
103 |
104 | .arg(Until::readableFileSize(bytesReceived))
105 | .arg(Until::readableFileSize(bytesTotal)));
106 |
107 | m_oldProgressedBytes = bytesReceived;
108 | });
109 | }
110 |
111 | void MainWidget::on_downloadBtn_clicked()
112 | {
113 |
114 | ui->retranslateUi(this);
115 | QString url(ui->urlEdit->toPlainText());
116 | if(url.isEmpty())
117 | {
118 | m_toast->toast(tr("URL is empty."));
119 | return;
120 | }
121 |
122 | m_downloader->setUrl(url);
123 | if(m_downloader->load())
124 | {
125 | ui->failedLabel->hide();
126 |
127 | QString dir = QFileDialog::getExistingDirectory(this,
128 | tr("Please select the download directory"), QDir::homePath());
129 |
130 | if(dir.isEmpty())
131 | return;
132 |
133 | ui->fileNameLabel->setText(m_downloader->fileName());
134 | ui->startBtn->setEnabled(false);
135 | ui->pauseBtn->setEnabled(m_downloader->isRangeSupport());
136 | ui->stopBtn->setEnabled(true);
137 |
138 | m_toast->toast(tr("Download started."));
139 |
140 | ui->stackedWidget->moveToIndex(1);
141 |
142 | QDir::setCurrent(dir);
143 | m_downloader->start();
144 | }
145 | else
146 | ui->failedLabel->show();
147 | }
148 |
149 | void MainWidget::on_startBtn_clicked()
150 | {
151 | ui->startBtn->setEnabled(false);
152 | ui->pauseBtn->setEnabled(true);
153 | ui->stopBtn->setEnabled(true);
154 | m_downloader->start();
155 | m_toast->toast(tr("Download started."));
156 | }
157 |
158 | void MainWidget::on_pauseBtn_clicked()
159 | {
160 | ui->startBtn->setEnabled(true);
161 | ui->pauseBtn->setEnabled(false);
162 | ui->stopBtn->setEnabled(true);
163 |
164 | m_downloader->pause();
165 | m_toast->toast(tr("Download has been pause."));
166 | }
167 |
168 | void MainWidget::on_stopBtn_clicked()
169 | {
170 | m_downloader->stop();
171 | m_toast->toast(tr("Download terminated."));
172 | ui->stackedWidget->moveToIndex(0);
173 | }
174 |
--------------------------------------------------------------------------------
/CustomWidgetDemos/src/mainwidget.cpp:
--------------------------------------------------------------------------------
1 | /**
2 | * @brief Demo
3 | * @anchor Ho229
4 | * @date 2020/12/12
5 | */
6 |
7 | #include "mainwidget.h"
8 | #include "ui_mainwidget.h"
9 |
10 | #include
11 | #include
12 | #include
13 |
14 | #ifdef Q_OS_WIN
15 | # if _MSC_VER >= 1600
16 | # pragma execution_character_set("utf-8")
17 | # endif
18 | #endif
19 |
20 | MainWidget::MainWidget(QWidget *parent)
21 | : QWidget(parent)
22 | , ui(new Ui::MainWidget),
23 | m_toast(new Toast(this)),
24 | m_buttonGroup_1(new QButtonGroup(this)),
25 | m_buttonGroup_2(new QButtonGroup(this)),
26 | m_langGroup(new QButtonGroup(this)),
27 | m_notifyManager(new NotifyManager(this)),
28 | m_trans(new QTranslator(this))
29 | {
30 | ui->setupUi(this);
31 |
32 | m_buttonGroup_1->addButton(ui->rotate_0, 0);
33 | m_buttonGroup_1->addButton(ui->rotate_1, 1);
34 | m_buttonGroup_1->addButton(ui->rotate_2, 2);
35 | #if QT_VERSION >= QT_VERSION_CHECK(5,15,0)
36 | connect(m_buttonGroup_1, &QButtonGroup::idClicked, this,
37 | &MainWidget::on_buttonClicked);
38 | #else
39 | connect(m_buttonGroup_1, QOverload::of(&QButtonGroup::buttonClicked), this,
40 | &MainWidget::on_buttonClicked);
41 | #endif
42 |
43 | m_buttonGroup_2->addButton(ui->translation_0, 0);
44 | m_buttonGroup_2->addButton(ui->translation_1, 1);
45 | m_buttonGroup_2->addButton(ui->translation_3, 2);
46 | #if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0)
47 | connect(m_buttonGroup_2, &QButtonGroup::idClicked, this,
48 | &MainWidget::on_buttonClicked);
49 | #else
50 | connect(m_buttonGroup_2, QOverload::of(&QButtonGroup::buttonClicked), this,
51 | &MainWidget::on_buttonClicked);
52 | #endif
53 |
54 | m_langGroup->addButton(ui->EnBtn, 0);
55 | m_langGroup->addButton(ui->CnBtn, 1);
56 | #if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0)
57 | connect(m_langGroup, &QButtonGroup::idClicked, this,
58 | &MainWidget::on_buttonClicked);
59 | #else
60 | connect(m_langGroup, QOverload::of(&QButtonGroup::buttonClicked), this,
61 | &MainWidget::on_buttonClicked);
62 | #endif
63 |
64 | ui->pushButton->setLeftIcon(QApplication::style()->
65 | standardIcon(QStyle::SP_MessageBoxInformation));
66 | ui->pushButton->setRightIcon(QApplication::style()->
67 | standardIcon(QStyle::SP_MessageBoxWarning));
68 | #if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0)
69 | connect(ui->sideMarginEdit, &QSpinBox::textChanged, this,
70 | [this](QString value){
71 | ui->pushButton->setSideMargin(value.toInt());
72 | ui->pushButton->repaint();
73 | });
74 |
75 | connect(ui->topBottomMarginEdit, &QSpinBox::textChanged, this,
76 | [this](QString value){
77 | ui->pushButton->setTopBottomMargin(value.toInt());
78 | ui->pushButton->repaint();
79 | });
80 | #else
81 | connect(ui->sideMarginEdit, QOverload::of(&QSpinBox::valueChanged), this,
82 | [this](int value){
83 | ui->pushButton->setSideMargin(value);
84 | ui->pushButton->repaint();
85 | });
86 |
87 | connect(ui->topBottomMarginEdit, QOverload::of(&QSpinBox::valueChanged), this,
88 | [this](int value){
89 | ui->pushButton->setTopBottomMargin(value);
90 | ui->pushButton->repaint();
91 | });
92 | #endif
93 |
94 | ui->progressButton->setValue(25);
95 | #if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0)
96 | connect(ui->valueSpinBox, &QSpinBox::textChanged, this,
97 | [this](QString value){
98 | ui->progressButton->setValue(value.toInt());
99 | ui->progressButton->setText(value.append("%"));
100 | });
101 | #else
102 | connect(ui->valueSpinBox, QOverload::of(&QSpinBox::valueChanged), this,
103 | [this](int value){
104 | ui->progressButton->setValue(value);
105 | ui->progressButton->setText(QString::number(value).append("%"));
106 | });
107 | #endif
108 |
109 | ui->countdownBtn->setIcon(QApplication::style()->
110 | standardIcon(QStyle::SP_MessageBoxInformation));
111 |
112 | m_trans->load(":/translations/CustomWidgetDemos_zh_CN.qm");
113 | }
114 |
115 | MainWidget::~MainWidget()
116 | {
117 | delete ui;
118 | }
119 |
120 | void MainWidget::on_toastBtn_clicked()
121 | {
122 | QString text = ui->toastEdit->text();
123 | if(text.isEmpty())
124 | m_toast->toast(tr("Please enter the tip text."));
125 | else
126 | m_toast->toast(ui->toastEdit->text());
127 | }
128 |
129 | void MainWidget::on_buttonClicked(int id)
130 | {
131 | QObject *sender = this->sender();
132 |
133 | if(sender == m_buttonGroup_1) // Rotate Stacked Widget
134 | ui->rotateStackedWidget->rotate(id);
135 | else if(sender == m_buttonGroup_2) // Translation Stacked Widget
136 | ui->translationStackedWidget->moveToIndex(id);
137 | else if(sender == m_langGroup) // Language
138 | {
139 | if(id == 0)
140 | QApplication::removeTranslator(m_trans);
141 | else
142 | QApplication::installTranslator(m_trans);
143 | ui->retranslateUi(this);
144 | }
145 | }
146 |
147 | void MainWidget::on_countdownStartBtn_clicked()
148 | {
149 | ui->countdownBtn->conutdownCilk(ui->countdownSpinBox->value());
150 | }
151 |
152 | void MainWidget::on_countdownBtn_clicked()
153 | {
154 | m_toast->toast(tr("Countdown Button clicked."));
155 | }
156 |
157 | void MainWidget::on_notifyBtn_clicked()
158 | {
159 | QString title = ui->titleEdit->text();
160 | QString message = ui->messageEdit->toPlainText();
161 |
162 | m_notifyManager->notify(this, title.isEmpty() ? "Hello" : title,
163 | message.isEmpty() ? "Hello World.\nHow are you today." : message,
164 | QApplication::style()->
165 | standardIcon(QStyle::SP_MessageBoxInformation));
166 | }
167 |
--------------------------------------------------------------------------------
/MultithreadedDownloader/MultithreadedDownloader_zh_CN.ts:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | MainWidget
6 |
7 |
8 | Qt Multithreaded Downloader
9 | Multithreaded Downloader
10 | Qt 多线程下载器
11 |
12 |
13 |
14 | Please enter your URL
15 | Please enter your URL.
16 | 请输入下载链接
17 |
18 |
19 |
20 | <html><head/><body><p><span style=" color:#ef0000;">Failed to get resource.</span></p></body></html>
21 | <html><head/><body><p><span style=" color:#ef0000;">资源失踪了哦。</span></p></body></html>
22 |
23 |
24 |
25 | Download
26 | 开始下载
27 |
28 |
29 |
30 | FileName
31 |
32 |
33 |
34 |
35 | Download Progress:
36 | 下载进度:
37 |
38 |
39 |
40 | Received: 0 / Total: 0 (Byte)
41 |
42 |
43 |
44 |
45 |
46 | Running
47 | 下载中
48 |
49 |
50 |
51 | Start
52 | 开始
53 |
54 |
55 |
56 | Pause
57 | 暂停
58 |
59 |
60 |
61 | Stop
62 | 停止
63 |
64 |
65 |
66 | infomation
67 | 消息
68 |
69 |
70 |
71 | download finished.
72 | 下载完成。
73 |
74 |
75 |
76 |
77 | error
78 | 错误
79 |
80 |
81 |
82 | File can not open.
83 | 文件无法打开。
84 |
85 |
86 | Download Failed.
87 | Network Error Code:
88 | Download Failed.
89 | Network Error Code
90 | 下载失败。
91 | 网络错误码:
92 |
93 |
94 |
95 | Download Failed.
96 | Network Error:%1
97 | Retry ?
98 | 下载失败。
99 | 网络错误:%1
100 | 是否重试?
101 |
102 |
103 |
104 | Paused
105 | 暂停
106 |
107 |
108 |
109 | Stopped
110 | 停止
111 |
112 |
113 | Received: %1 / Total: %2 (Byte)
114 | 已接收: %1 / 总计: %2 (字节)
115 |
116 |
117 | Received: %1 / Total: %2
118 | 已接收: %1 / 总计: %2
119 |
120 |
121 |
122 | Speed: %1/s | %2 / %3
123 | Speed: %1 | %2 / %3
124 | 速度: %1/s | %2 / %3
125 |
126 |
127 |
128 | URL is empty.
129 | 下载链接为空。
130 |
131 |
132 |
133 | Please select the download directory
134 | 请选择下载目录
135 |
136 |
137 |
138 |
139 | Download started.
140 | 下载已开始。
141 |
142 |
143 |
144 | Download has been pause.
145 | 下载已暂停。
146 |
147 |
148 |
149 | Download terminated.
150 | 下载已停止。
151 |
152 |
153 |
154 |
--------------------------------------------------------------------------------
/CustomWidgetDemos/CustomWidgetDemos_zh_CN.ts:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | MainWidget
6 |
7 |
8 | Qt Custom Widgets
9 | Qt 自定义控件
10 |
11 |
12 |
13 | RotateStackedWidget
14 |
15 |
16 |
17 |
18 |
19 | Index
20 | 索引
21 |
22 |
23 |
24 |
25 |
26 |
27 | Page 1
28 | 第一页
29 |
30 |
31 |
32 |
33 |
34 |
35 | Page 2
36 | 第二页
37 |
38 |
39 |
40 |
41 |
42 |
43 | Page 3
44 | 第三页
45 |
46 |
47 |
48 | TranslationStackedWidget
49 |
50 |
51 |
52 |
53 | AlignIconButton
54 |
55 |
56 |
57 |
58 | Side Margin:
59 | 左右边距:
60 |
61 |
62 |
63 | Top and Bottom Margin:
64 | 上下边距:
65 |
66 |
67 |
68 | PushButton
69 | 按钮
70 |
71 |
72 |
73 | ProgressButton
74 |
75 |
76 |
77 |
78 | Value:
79 | 值:
80 |
81 |
82 |
83 | 25%
84 |
85 |
86 |
87 |
88 | CountdownButton
89 |
90 |
91 |
92 |
93 | Countdown:
94 | 倒计时:
95 |
96 |
97 |
98 | ms
99 | 毫秒
100 |
101 |
102 |
103 | Start
104 | 开始
105 |
106 |
107 |
108 | Toast
109 |
110 |
111 |
112 |
113 | Tip Text:
114 | Tip Text
115 | 提示文字:
116 |
117 |
118 |
119 | Show
120 | 显示
121 |
122 |
123 |
124 | Language
125 | 语言
126 |
127 |
128 |
129 | English
130 |
131 |
132 |
133 |
134 | 简体中文
135 |
136 |
137 |
138 |
139 | ProgressDial
140 |
141 |
142 |
143 |
144 | Notfily
145 |
146 |
147 |
148 |
149 | Title:
150 | 标题:
151 |
152 |
153 |
154 | Message:
155 | 消息:
156 |
157 |
158 |
159 | Show Time:
160 | 展示时间:
161 |
162 |
163 |
164 | Notify
165 | 弹出提示
166 |
167 |
168 |
169 | Please enter the tip text.
170 | Please enter your information.
171 | 请输入提示文本。
172 |
173 |
174 |
175 | Countdown Button clicked.
176 | 倒计时按钮已按下。
177 |
178 |
179 |
180 |
--------------------------------------------------------------------------------
/MultithreadedDownloader/src/downloader/multithreadeddownloader.cpp:
--------------------------------------------------------------------------------
1 | /**
2 | * @brief MultithreadedDownloader
3 | * @anchor Ho229<2189684957@qq.com>
4 | * @date 2021/2/1
5 | */
6 |
7 | #include "multithreadeddownloader.h"
8 |
9 | #include
10 | #include
11 | #include
12 | #include
13 | #include
14 | #include
15 |
16 | MultithreadedDownloader::MultithreadedDownloader(QObject *parent)
17 | : AbstractMission(parent),
18 | m_manager(new QNetworkAccessManager(this)),
19 | m_writer(new MultithreadedDownloaderWriter(this)),
20 | m_threadCount(QThread::idealThreadCount())
21 | {
22 |
23 | }
24 |
25 | MultithreadedDownloader::~MultithreadedDownloader()
26 | {
27 | if(m_state == Running)
28 | this->MultithreadedDownloader::stop();
29 |
30 | if(m_writer->isRunning())
31 | m_writer->terminate();
32 | }
33 |
34 | bool MultithreadedDownloader::load()
35 | {
36 | if(!m_url.isValid() || m_state != Stopped)
37 | return false;
38 |
39 | QNetworkRequest request(m_url);
40 | request.setAttribute(QNetworkRequest::FollowRedirectsAttribute, true);
41 |
42 | QPointer reply = m_manager->head(request);
43 |
44 | QEventLoop loop;
45 | QObject::connect(reply, &QNetworkReply::finished, &loop, &QEventLoop::quit);
46 | loop.exec(QEventLoop::ExcludeUserInputEvents);
47 |
48 | qint64 size = 0;
49 | bool ok = false;
50 | if(reply->hasRawHeader("Content-Length"))
51 | size = reply->header(QNetworkRequest::ContentLengthHeader).toLongLong(&ok);
52 |
53 | QString fileName;
54 | if(reply->hasRawHeader("Content-Disposition"))
55 | fileName = reply->header(QNetworkRequest::ContentDispositionHeader)
56 | .toString().split("filename=").at(1);
57 | else
58 | fileName = reply->url().fileName();
59 |
60 | m_isRangeSupport = reply->rawHeader("Accept-Ranges") == "bytes";
61 |
62 | if(fileName.isEmpty() || (!ok) || size <= 0)
63 | return false;
64 | else
65 | {
66 | m_writer->setFileName(fileName);
67 | m_writer->setSize(size);
68 | }
69 |
70 | return true;
71 | }
72 |
73 | void MultithreadedDownloader::start()
74 | {
75 | if(m_state == Running || m_writer->fileName().isEmpty() ||
76 | m_writer->size() <= 0 || m_url.isEmpty())
77 | return;
78 |
79 | if(m_state == Stopped)
80 | {
81 | if(!m_writer->open())
82 | {
83 | emit error(OpenFileFailed);
84 | return;
85 | }
86 |
87 | if(m_isRangeSupport)
88 | {
89 | qint64 start, end;
90 | qint64 segmentSize = m_writer->size() / m_threadCount;
91 | for(int i = 0; i < m_threadCount; i++)
92 | {
93 | start = i * segmentSize;
94 | if(i != m_threadCount - 1)
95 | end = start + segmentSize -1;
96 | else
97 | end = m_writer->size(); // Last mission
98 |
99 | m_missions.push_back(this->createMission(start, end));
100 | }
101 | }
102 | else
103 | m_missions.push_back(this->createMission(0, -1));
104 |
105 |
106 | this->setFinished(false);
107 | }
108 | else // Paused
109 | {
110 | const QList& constlist = m_missions;
111 | for(DownloadMission *mission : constlist)
112 | if(!mission->isFinished())
113 | mission->start();
114 | }
115 |
116 | m_timerId = this->startTimer(m_notifyInterval);
117 | this->updateState(Running);
118 | }
119 |
120 | void MultithreadedDownloader::pause()
121 | {
122 | if(m_state == Running)
123 | {
124 | if(!m_isRangeSupport)
125 | {
126 | qWarning() << __FUNCTION__ << ": Pause is not available";
127 | return;
128 | }
129 |
130 | const QList& constlist = m_missions;
131 | for(DownloadMission *misson : constlist)
132 | if(!misson->isFinished())
133 | misson->pause();
134 |
135 | this->killTimer(m_timerId);
136 | this->updateState(Paused);
137 | }
138 | }
139 |
140 | void MultithreadedDownloader::stop()
141 | {
142 | if(m_state != Stopped)
143 | {
144 | if(m_state == Running)
145 | this->killTimer(m_timerId);
146 |
147 | this->destoryMissions();
148 |
149 | if(m_writer->isRunning()) // Wait for the write finish
150 | {
151 | QEventLoop loop;
152 | QObject::connect(m_writer, &MultithreadedDownloaderWriter::finished, &loop,
153 | &QEventLoop::quit);
154 | loop.exec();
155 | }
156 |
157 | m_writer->close();
158 | m_finishedCount = 0;
159 |
160 | this->reset();
161 | this->updateState(Stopped);
162 | }
163 | }
164 |
165 | void MultithreadedDownloader::errorHanding(QNetworkReply::NetworkError err)
166 | {
167 | m_networkError = err;
168 | m_networkErrorString = qobject_cast
169 | (this->sender())->replyErrorString();
170 | if(err == QNetworkReply::OperationCanceledError || err == QNetworkReply::NoError)
171 | return;
172 |
173 | this->pause();
174 | emit error(DownloadFailed);
175 | }
176 |
177 | void MultithreadedDownloader::on_finished()
178 | {
179 | m_finishedCount++;
180 |
181 | qDebug() << "MultithreadedDownloader: finishedCount:" << m_finishedCount;
182 |
183 | if(m_isRangeSupport && m_finishedCount != m_threadCount)
184 | return;
185 |
186 | this->updateProgress();
187 | this->stop();
188 |
189 | emit finished();
190 | }
191 |
192 | DownloadMission *MultithreadedDownloader::createMission(qint64 start, qint64 end)
193 | {
194 | DownloadMission *mission = new DownloadMission(this);
195 |
196 | QObject::connect(mission, &DownloadMission::finished, this,
197 | &MultithreadedDownloader::on_finished);
198 | QObject::connect(mission, &DownloadMission::replyError, this,
199 | &MultithreadedDownloader::errorHanding);
200 |
201 | mission->setManager(m_manager);
202 | mission->setWriter(m_writer);
203 | mission->setRange(start, end);
204 | mission->setUrl(m_url);
205 | mission->start();
206 |
207 | return mission;
208 | }
209 |
210 | void MultithreadedDownloader::destoryMissions()
211 | {
212 | const QList& constlist = m_missions;
213 | for(DownloadMission *mission : constlist)
214 | {
215 | if(mission->state() != Stopped)
216 | mission->stop();
217 | mission->deleteLater();
218 | }
219 | m_missions.clear();
220 | }
221 |
222 | void MultithreadedDownloader::updateProgress()
223 | {
224 | qint64 bytesReceived = 0;
225 |
226 | const QList& constlist = m_missions;
227 | for(const DownloadMission* mission : constlist)
228 | bytesReceived += mission->downloadedSize();
229 |
230 | emit downloadProgress(bytesReceived, m_writer->size());
231 | }
232 |
233 | void MultithreadedDownloader::reset()
234 | {
235 | m_writer->setFileName({});
236 | m_writer->setSize(0);
237 |
238 | m_timerId = 0;
239 | m_isRangeSupport = false;
240 | }
241 |
242 | void MultithreadedDownloader::timerEvent(QTimerEvent *event)
243 | {
244 | if(event->timerId() == m_timerId)
245 | this->updateProgress();
246 | }
247 |
--------------------------------------------------------------------------------
/MultithreadedDownloader/src/mainwidget.ui:
--------------------------------------------------------------------------------
1 |
2 |
3 | MainWidget
4 |
5 |
6 |
7 | 0
8 | 0
9 | 375
10 | 223
11 |
12 |
13 |
14 | Qt Multithreaded Downloader
15 |
16 |
17 |
18 | 0
19 |
20 |
21 | 0
22 |
23 |
24 | 0
25 |
26 |
27 | 0
28 |
29 |
30 | 0
31 |
32 | -
33 |
34 |
35 | 0
36 |
37 |
38 |
39 | #startPage
40 | {
41 | background:white;
42 | }
43 |
44 | #urlEdit
45 | {
46 | background:white;
47 | padding:3px;
48 | border-radius:5px;
49 | border:1px solid rgb(119,119,119);
50 | }
51 |
52 | QPushButton
53 | {
54 | color:white;
55 | background:rgb(102,153,255);
56 | border-style:none;
57 | border-radius:5px;
58 | }
59 |
60 | QPushButton:hover
61 | {
62 | background:rgb(75, 135, 255);
63 | }
64 |
65 |
66 |
-
67 |
68 |
69 |
70 | 微软雅黑
71 | 12
72 |
73 |
74 |
75 | Please enter your URL
76 |
77 |
78 |
79 | -
80 |
81 |
82 |
83 | 微软雅黑
84 |
85 |
86 |
87 | <html><head/><body><p><span style=" color:#ef0000;">Failed to get resource.</span></p></body></html>
88 |
89 |
90 |
91 | -
92 |
93 |
94 |
95 | 0
96 | 35
97 |
98 |
99 |
100 |
101 | 微软雅黑
102 | 12
103 |
104 |
105 |
106 | Download
107 |
108 |
109 | true
110 |
111 |
112 |
113 |
114 |
115 |
116 |
117 | #downloadPage
118 | {
119 | background:white;
120 | }
121 |
122 | QLabel
123 | {
124 | color:black;
125 | }
126 |
127 | QPushButton
128 | {
129 | color:white;
130 | background:rgb(102,153,255);
131 | border-style:none;
132 | border-radius:5px;
133 | }
134 |
135 | QPushButton:hover
136 | {
137 | background:rgb(75, 135, 255);
138 | }
139 |
140 |
141 | -
142 |
143 |
144 |
145 | 微软雅黑
146 | 22
147 |
148 |
149 |
150 | FileName
151 |
152 |
153 |
154 | -
155 |
156 |
157 | Qt::Vertical
158 |
159 |
160 |
161 | 20
162 | 19
163 |
164 |
165 |
166 |
167 | -
168 |
169 |
170 |
171 | 微软雅黑
172 | 11
173 |
174 |
175 |
176 | Download Progress:
177 |
178 |
179 |
180 | -
181 |
182 |
183 |
184 | 16777215
185 | 10
186 |
187 |
188 |
189 |
190 | 微软雅黑
191 | 8
192 |
193 |
194 |
195 | QProgressBar
196 | {
197 | border:1px solid #FFFFFF;
198 | background: white;
199 | }
200 |
201 | QProgressBar::chunk
202 | {
203 | background-color:#05B8CC;
204 | border-radius:3px;
205 | }
206 |
207 |
208 | 0
209 |
210 |
211 | Qt::AlignCenter
212 |
213 |
214 |
215 | -
216 |
217 |
-
218 |
219 |
220 |
221 | 微软雅黑
222 | 12
223 |
224 |
225 |
226 | Received: 0 / Total: 0 (Byte)
227 |
228 |
229 | Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter
230 |
231 |
232 |
233 | -
234 |
235 |
236 |
237 | 微软雅黑
238 | 12
239 |
240 |
241 |
242 | Running
243 |
244 |
245 | Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter
246 |
247 |
248 |
249 |
250 |
251 | -
252 |
253 |
254 | Qt::Vertical
255 |
256 |
257 |
258 | 20
259 | 20
260 |
261 |
262 |
263 |
264 | -
265 |
266 |
-
267 |
268 |
269 |
270 | 0
271 | 35
272 |
273 |
274 |
275 |
276 | 微软雅黑
277 | 12
278 |
279 |
280 |
281 | Start
282 |
283 |
284 |
285 | -
286 |
287 |
288 |
289 | 0
290 | 35
291 |
292 |
293 |
294 |
295 | 微软雅黑
296 | 12
297 |
298 |
299 |
300 | Pause
301 |
302 |
303 |
304 | -
305 |
306 |
307 |
308 | 0
309 | 35
310 |
311 |
312 |
313 |
314 | 微软雅黑
315 | 12
316 |
317 |
318 |
319 | Stop
320 |
321 |
322 |
323 |
324 |
325 |
326 |
327 |
328 |
329 |
330 |
331 |
332 |
333 | TranslationStackedWidget
334 | QStackedWidget
335 | translationstackedwidget.h
336 | 1
337 |
338 |
339 |
340 |
341 |
342 |
--------------------------------------------------------------------------------
/VideoPlayer/src/player/videoplayer.cpp:
--------------------------------------------------------------------------------
1 | /**
2 | * @brief Video Player
3 | * @anchor Ho 229
4 | * @date 2021/4/14
5 | */
6 |
7 | #include "audiooutput.h"
8 | #include "videoplayer.h"
9 | #include "videoplayer_p.h"
10 | #include "videorenderer.h"
11 |
12 | #include
13 | #include
14 | #include
15 | #include
16 |
17 | VideoPlayer::VideoPlayer(QQuickItem *parent) :
18 | QQuickFramebufferObject(parent),
19 | d_ptr(new VideoPlayerPrivate(this))
20 | {
21 | Q_D(VideoPlayer);
22 |
23 | d->decoder = new FFmpegDecoder(nullptr);
24 | d->decoder->moveToThread(new QThread(this));
25 | d->decoder->thread()->start();
26 |
27 | d->audioOutput = new AudioOutput([d](char *data, qint64 maxlen)
28 | { return d->updateAudioData(data, maxlen); }, this);
29 |
30 | QObject::connect(d->decoder, &FFmpegDecoder::activeVideoTrackChanged,
31 | this, &VideoPlayer::activeVideoTrackChanged);
32 | QObject::connect(d->decoder, &FFmpegDecoder::activeAudioTrackChanged,
33 | this, &VideoPlayer::activeAudioTrackChanged);
34 | QObject::connect(d->decoder, &FFmpegDecoder::activeSubtitleTrackChanged,
35 | this, &VideoPlayer::activeSubtitleTrackChanged);
36 |
37 | QObject::connect(d->decoder, &FFmpegDecoder::activeAudioTrackChanged,
38 | this, [this] { d_ptr->restartAudioOutput(); });
39 | }
40 |
41 | VideoPlayer::~VideoPlayer()
42 | {
43 | Q_D(VideoPlayer);
44 |
45 | if(d->state != Stopped)
46 | this->stop();
47 |
48 | // Tell the decode thread exit
49 | d->decoder->thread()->quit();
50 |
51 | // Wait for finished
52 | if(!d->decoder->thread()->wait())
53 | FUNC_ERROR << ": Decode thread exit failed";
54 |
55 | // Delete the VideoPlayerPrivate
56 | delete d;
57 | }
58 |
59 | QQuickFramebufferObject::Renderer *VideoPlayer::createRenderer() const
60 | {
61 | d_ptr->videoRenderer = new VideoRenderer;
62 | return d_ptr->videoRenderer; // Create custom renderer
63 | }
64 |
65 | void VideoPlayer::setSource(const QUrl& source)
66 | {
67 | Q_D(VideoPlayer);
68 |
69 | d->decoder->setUrl(source);
70 | emit sourceChanged(source);
71 | }
72 |
73 | QUrl VideoPlayer::source() const
74 | {
75 | return d_ptr->decoder->url();
76 | }
77 |
78 | VideoPlayer::State VideoPlayer::playbackState() const
79 | {
80 | return d_ptr->state;
81 | }
82 |
83 | void VideoPlayer::play()
84 | {
85 | Q_D(VideoPlayer);
86 |
87 | if(d->state == Playing)
88 | return;
89 | else if(d->state == Stopped && d->decoder->state() == FFmpegDecoder::Closed)
90 | {
91 | QEventLoop loop;
92 | QObject::connect(d->decoder, &FFmpegDecoder::stateChanged, &loop, &QEventLoop::exit);
93 | QMetaObject::invokeMethod(d->decoder, &FFmpegDecoder::load, Qt::QueuedConnection);
94 |
95 | if(loop.exec() != FFmpegDecoder::Opened)
96 | {
97 | emit errorOccurred(this->errorString());
98 | return;
99 | }
100 |
101 | emit loaded();
102 |
103 | const auto fps = d->decoder->fps();
104 | d->interval = qIsNaN(fps) ? 1000.0 : 1000 / fps;
105 | }
106 | else if(d->state == Paused)
107 | {
108 | d->videoClock.resume();
109 | d->audioClock.resume();
110 | }
111 |
112 | d->timerId = this->startTimer(d->interval, Qt::PreciseTimer);
113 | d->audioOutput->play();
114 |
115 | d->state = Playing;
116 | emit playbackStateChanged(Playing);
117 | }
118 |
119 | void VideoPlayer::pause()
120 | {
121 | Q_D(VideoPlayer);
122 |
123 | if(d->state != Playing)
124 | return;
125 |
126 | this->killTimer(d->timerId);
127 | d->audioOutput->pause();
128 |
129 | d->videoClock.pause();
130 | d->audioClock.pause();
131 |
132 | d->state = Paused;
133 | emit playbackStateChanged(Paused);
134 | }
135 |
136 | void VideoPlayer::stop()
137 | {
138 | Q_D(VideoPlayer);
139 |
140 | if(d->state == Stopped)
141 | return;
142 | else if(d->state == Playing)
143 | this->killTimer(d->timerId);
144 |
145 | d->audioOutput->stop();
146 |
147 | d->decoder->requestInterrupt();
148 | QEventLoop loop;
149 | QObject::connect(d->decoder, &FFmpegDecoder::stateChanged, &loop, &QEventLoop::quit);
150 | QMetaObject::invokeMethod(d->decoder, &FFmpegDecoder::release, Qt::QueuedConnection);
151 | loop.exec();
152 |
153 | av_frame_free(&d->audioFrame);
154 | d->audioFramePos = 0;
155 |
156 | d->videoClock.invalidate();
157 | d->audioClock.invalidate();
158 | d->videoRenderer->updateSubtitleFrame(nullptr);
159 |
160 | d->position = 0;
161 | emit positionChanged(0);
162 |
163 | d->videoRenderer->updateVideoFrame(nullptr);
164 | this->update();
165 |
166 | d->state = Stopped;
167 | emit playbackStateChanged(Stopped);
168 | }
169 |
170 | void VideoPlayer::setVolume(qreal volume)
171 | {
172 | Q_D(VideoPlayer);
173 | d->audioOutput->setVolume(volume);
174 | emit volumeChanged(volume);
175 | }
176 |
177 | qreal VideoPlayer::volume() const
178 | {
179 | return d_ptr->audioOutput->volume();
180 | }
181 |
182 | void VideoPlayer::setActiveVideoTrack(int index)
183 | {
184 | Q_D(VideoPlayer);
185 |
186 | if(!this->hasVideo() || d->decoder->activeVideoTrack() == index)
187 | return;
188 |
189 | d->decoder->requestInterrupt();
190 | QMetaObject::invokeMethod(d->decoder, "setActiveVideoTrack",
191 | Qt::QueuedConnection, Q_ARG(int, index));
192 | QMetaObject::invokeMethod(d->decoder, &FFmpegDecoder::decode,
193 | Qt::QueuedConnection);
194 |
195 | av_frame_free(&d->audioFrame);
196 | d->audioFramePos = 0;
197 |
198 | d->videoClock.invalidate();
199 | d->audioClock.invalidate();
200 | d->videoRenderer->updateSubtitleFrame(nullptr);
201 | }
202 |
203 | int VideoPlayer::activeVideoTrack() const
204 | {
205 | return d_ptr->decoder->activeVideoTrack();
206 | }
207 |
208 | void VideoPlayer::setActiveAudioTrack(int index)
209 | {
210 | Q_D(VideoPlayer);
211 |
212 | if(!this->hasAudio() || d->decoder->activeAudioTrack() == index)
213 | return;
214 |
215 | d->decoder->requestInterrupt();
216 | QMetaObject::invokeMethod(d->decoder, "setActiveAudioTrack",
217 | Qt::QueuedConnection, Q_ARG(int, index));
218 |
219 | av_frame_free(&d->audioFrame);
220 | d->audioFramePos = 0;
221 |
222 | d->videoClock.invalidate();
223 | d->audioClock.invalidate();
224 | d->videoRenderer->updateSubtitleFrame(nullptr);
225 | }
226 |
227 | int VideoPlayer::activeAudioTrack() const
228 | {
229 | return d_ptr->decoder->activeAudioTrack();
230 | }
231 |
232 | void VideoPlayer::setActiveSubtitleTrack(int index)
233 | {
234 | Q_D(VideoPlayer);
235 |
236 | if(!this->hasSubtitle() || d->decoder->activeSubtitleTrack() == index)
237 | return;
238 |
239 | d->decoder->requestInterrupt();
240 | QMetaObject::invokeMethod(d->decoder, "setActiveSubtitleTrack",
241 | Qt::QueuedConnection, Q_ARG(int, index));
242 | QMetaObject::invokeMethod(d->decoder, &FFmpegDecoder::decode,
243 | Qt::QueuedConnection);
244 |
245 | av_frame_free(&d->audioFrame);
246 | d->audioFramePos = 0;
247 |
248 | d->videoClock.invalidate();
249 | d->audioClock.invalidate();
250 | d->videoRenderer->updateSubtitleFrame(nullptr);
251 | }
252 |
253 | int VideoPlayer::activeSubtitleTrack() const
254 | {
255 | return d_ptr->decoder->activeSubtitleTrack();
256 | }
257 |
258 | int VideoPlayer::videoTrackCount() const
259 | {
260 | return d_ptr->decoder->videoTrackCount();
261 | }
262 |
263 | int VideoPlayer::audioTrackCount() const
264 | {
265 | return d_ptr->decoder->audioTrackCount();
266 | }
267 |
268 | int VideoPlayer::subtitleTrackCount() const
269 | {
270 | return d_ptr->decoder->subtitleTrackCount();
271 | }
272 |
273 | int VideoPlayer::duration() const
274 | {
275 | return d_ptr->decoder->duration();
276 | }
277 |
278 | int VideoPlayer::position() const
279 | {
280 | return d_ptr->position;
281 | }
282 |
283 | bool VideoPlayer::hasVideo() const
284 | {
285 | return d_ptr->decoder->videoTrackCount();
286 | }
287 |
288 | bool VideoPlayer::hasAudio() const
289 | {
290 | return d_ptr->decoder->audioTrackCount();
291 | }
292 |
293 | bool VideoPlayer::hasSubtitle() const
294 | {
295 | return d_ptr->decoder->subtitleTrackCount();
296 | }
297 |
298 | bool VideoPlayer::seekable() const
299 | {
300 | return d_ptr->decoder->seekable();
301 | }
302 |
303 | QString VideoPlayer::errorString() const
304 | {
305 | return d_ptr->decoder->errorString();
306 | }
307 |
308 | void VideoPlayer::seek(int position)
309 | {
310 | Q_D(VideoPlayer);
311 |
312 | if(d->position == position || d->state == State::Stopped || !this->seekable())
313 | return;
314 |
315 | d->position = position;
316 | emit positionChanged(position);
317 |
318 | d->decoder->requestInterrupt();
319 | QMetaObject::invokeMethod(d->decoder, "seek", Qt::BlockingQueuedConnection, Q_ARG(int, position));
320 |
321 | d->audioOutput->reset();
322 |
323 | av_frame_free(&d->audioFrame);
324 | d->audioFramePos = 0;
325 |
326 | d->videoClock.invalidate();
327 | d->audioClock.invalidate();
328 | d->videoRenderer->updateSubtitleFrame(nullptr);
329 |
330 | if(d->state == Paused)
331 | {
332 | AVFrame *frame = nullptr;
333 | while(!(frame = d->decoder->takeVideoFrame()))
334 | QThread::yieldCurrentThread();
335 |
336 | d->videoRenderer->updateVideoFrame(frame);
337 | this->update();
338 | }
339 | }
340 |
341 | void VideoPlayer::timerEvent(QTimerEvent *)
342 | {
343 | Q_D(VideoPlayer);
344 |
345 | d->updateVideoFrame();
346 | d->updateSubtitleFrame();
347 | this->update();
348 |
349 | if(d->videoClock.isValid() || d->audioClock.isValid())
350 | {
351 | const int position = d->videoClock.isValid() ? d->videoClock.time() : d->audioClock.time();
352 | if(position != d->position)
353 | {
354 | d->position = position;
355 | emit positionChanged(position);
356 | }
357 | }
358 |
359 | if(!d->decoder->hasFrame() && d->decoder->isEnd())
360 | this->stop();
361 | }
362 |
--------------------------------------------------------------------------------