Smart car terminal project based on IMX6ULL (code open source)

Foreword: This article is a hands-on teaching smart vehicle terminal project ( Linux+QT ), which is a very comprehensive Linux series project! The core board of the project uses NXP 's IMX6ULL as the CPU , which fully realizes the functional requirements of the simplified version of the vehicle terminal. There are many points to learn in the project, including: IMX6ULL hardware driver, QT transplantation and Linux multi-process operation, etc. The source code of this project is suitable for the factory image of punctual atoms, and those who only learn application layer development can also try to learn this project! I hope this blog can give you some gains, and the code at the end of the blog is open source!

Hardware physical map:

Renderings:

Considering that the overall process of the project is too complicated, the author will focus on the implementation of the software, that is, the framework of Linux+QT! The benefit of this project is: the source code can be used directly on the punctual atom IMX6ULL development board! Readers and friends who only study application layer development can directly use the factory image of punctual atom!

1. Overview of smart vehicle terminals

The intelligent vehicle terminal is a device used for modern management of transportation vehicles. It integrates GPS technology, mileage positioning technology and car black box technology, and can be used for a variety of purposes, including DVR driving recorder, smart car rearview mirror, online car-hailing operation terminal, two-passenger and one-danger fleet management terminal, etc. The popularity of smart vehicle-mounted terminals has brought the automotive industry into an accelerated stage of intelligence. Compared with traditional terminal products, smart vehicle terminals can reduce platform license fees, reduce R&D costs, shorten R&D and production time, cover terminal intellectual property rights, and quickly plan products. Smart vehicle terminals often need to cover a large number of commercial vehicles, and enterprises have certain needs for cost performance, while smart module products can help enterprises quickly carry out product iterations and achieve more efficient research and development.

With the development of technology and tram technology, the functions and requirements of smart vehicle terminals are becoming more and more complex and advanced. The length of the author's blog is limited, so this time I will only teach readers the simple implementation of the three functions and the function switching method! 

1.1 Vehicle terminal: audio and video player

At present, on the car terminal, the most used by car owners should be audio equipment. For example: radio, radio station, music player, listening to books, etc. Therefore, the vehicle-mounted terminal must be used to successfully drive audio chips (I2S, etc.). The audio device is indispensable on the car terminal, whether it uses a third-party music player or designs a music player independently!

1.2 Vehicle terminal: map function

The map function on the vehicle terminal is also indispensable, and car owners often need to perform map navigation (mobile phone navigation may be used more, but you must have the function). The realization of the functions that map navigation relies on is more complicated, and it needs to drive GPS, etc. At the same time, according to GPS and other information combined with map software for positioning and navigation!

1.3 Vehicle terminal: reversing image

Like autonomous driving, the advancement of technology often reduces the difficulty of driving for humans. Operations such as reversing into storage are difficult driving techniques, combined with modern technologies such as reversing images. It is convenient for car owners to quickly complete the operation and enjoy a happy life!

1.4 Vehicle Terminal: Miscellaneous Functions

The vehicle terminal is a very complex and commonly used device. With the continuous upgrading and iteration of tram and autonomous driving technology, the demand is also increasing day by day! We often need to add many other functions to the terminal, such as: smart home linkage, in-car environment monitoring, weather forecast and online browsing, etc.!

2. Driver of IMX6ULL vehicle project

The author emphasizes: Considering the limited space, the author only teaches part of the function implementation in this blog, and focuses on the implementation of the application layer! Friends with weak foundations can directly use the image program shipped by punctual atom to reproduce the project!

2.1 Audio Device Driver

The audio CODEC supports the I2S protocol, so the main controller must also support the I2S protocol. I.MX6ULL also provides a peripheral called SAI , which is called SynchronousAudio Interface , which translates to a synchronous audio interface.

The SAI of the i.MX6ULL is a full-duplex, frame-synchronous capable serial interface that supports I2S , AC97 , TDM , and audio DSP .

The audio schematic diagram of the Atom ALPHA development board is shown in the figure:

NXP has officially written the WM8960 driver, so we can directly configure the kernel to enable the WM8960 driver, and follow the steps below to enable the WM8960 driver.

1. Modify the device tree according to the pin diagram of IMX6ULL;

2. Enable the WM8960 driver of the kernel;

2.1. Cancel ALSA simulation OSS API

2.2. Enable WM8960 driver of I.MX6ULL

Completely visible: "[Punctual Atoms] I.MX6U Embedded Linux Driver Development Guide V1.6" page 1544

2.2 LCD driver

The IMX6ULL of punctual atom is very similar to the official "blue version" of NXP, which is imitated and produced (generally, the same is true for the project board of Dadu's products)

6ULL's eLCDIF interface driver NXP has already been written, so we don't need to modify the LCD driver part . All we need to do is modify the device tree according to the LCD we are using. Focus on three places:

①. The IO configuration used by the LCD.
②. Modify the LCD screen node, modify the corresponding attribute value, and replace it with the LCD screen parameters we use.
③. To modify the LCD backlight node information, modify the corresponding device node information according to the actual backlight IO used.

1. Modify the IO configuration used by the LCD;

Check the IO configuration used by the LCD in the device tree. In fact, NXP has already written this for us, and there is no need to modify it!

2. Modify the parameter node information on the LCD screen

Find the lcdif node in the imx6ull-alientek-emmc.dts file, the content of the node is as follows :

&lcdif {
	pinctrl-names = "default";
	pinctrl-0 = <&pinctrl_lcdif_dat
		     &pinctrl_lcdif_ctrl>;

	display = <&display0>;
	status = "okay"; 

	/* 7寸1024*600 */
	display0: display {
		bits-per-pixel = <24>;		/*  */
		bus-width = <24>;			/* LCD屏幕数据线有多少 */

		display-timings {
			native-mode = <&timing0>;
			timing0: timing0 {
			clock-frequency = <51200000>;		/* 时钟频率51.2MHZ */
			hactive = <1024>;					/* 水平像素点 */
			vactive = <600>;					/* 垂直像素点 */
			hfront-porch = <160>;
			hback-porch = <140>;
			hsync-len = <20>;
			vback-porch = <20>;
			vfront-porch = <12>;
			vsync-len = <3>;
			
			/* 像素点有效电平值 */
			hsync-active = <0>;
			vsync-active = <0>;
			de-active = <1>;
			pixelclk-active = <0>;
			};
		};
	};

	/* 4.3寸480*272 */
	/* display0: display {
		bits-per-pixel = <24>;
		bus-width = <24>;

		display-timings {
			native-mode = <&timing0>;
			timing0: timing0 {
			clock-frequency = <9000000>;
			hactive = <480>;
			vactive = <272>;
			hfront-porch = <5>;
			hback-porch = <40>;
			hsync-len = <1>;
			vback-porch = <8>;
			vfront-porch = <8>;
			vsync-len = <1>;

			hsync-active = <0>;
			vsync-active = <0>;
			de-active = <1>;
			pixelclk-active = <0>;
			};
		};
	};*/

	/* 4.3寸800*480 */
	/* display0: display {
		bits-per-pixel = <24>;
		bus-width = <24>;

		display-timings {
			native-mode = <&timing0>;
			timing0: timing0 {
			clock-frequency = <31000000>;
			hactive = <800>;
			vactive = <480>;
			hfront-porch = <40>;
			hback-porch = <88>;
			hsync-len = <48>;
			vback-porch = <32>;
			vfront-porch = <13>;
			vsync-len = <3>;

			hsync-active = <0>;
			vsync-active = <0>;
			de-active = <1>;
			pixelclk-active = <0>;
			};
		};
	};*/

};

Modify each parameter information in the red box according to the actual parameters of the LCD you use!

3. LCD screen backlight node information

The LCD interface backlight control IO of Punctual Atom is connected to the GPIO1_IO08 pin of I.MX6U. GPIO1_IO08 is multiplexed as PWM1_OUT, and the brightness of the LCD screen backlight is controlled through the PWM signal. The LCD backlight pins of the punctual atomic I.MX6U-ALPHA development board are the same as the backlight pins of the NXP official EVK development board, so the backlight device tree node does not need to be modified.

Set the backlight node. This NXP has already set it up for us. You can find the following content in the imx6ull-alientek-emmc.dts file:

backlight {
	compatible = "pwm-backlight";
	pwms = <&pwm1 0 5000000>;
	brightness-levels = <0 4 8 16 32 64 128 255>;
	default-brightness-level = <7>;
	status = "okay";
};

Line 3, set the backlight to use pwm1 , and the PWM frequency is 200Hz .
Line 4, set 8 levels of backlight (0~7), which are 0, 4, 8, 16, 32, 64, 128, 255, respectively , and the corresponding duty cycle is 0%, 1.57%, 3.13%, 6.27%, 12.55%, 25.1%, 50.19%, 100% . If you feel too little, you can add some other backlight level values ​​yourself.
Line 5, set the default backlight level to 6 , which is 50.19%  brightness.

Completely visible: "[Punctual Atoms] I.MX6U Embedded Linux Driver Development Guide V1.6" page 1386

2.3 AP3216C driver

The function page of the smart home APP replaced by the author of this project is the vehicle interior monitoring function of AP3216C!

Limited space, the use of AP3216 and QT function page design blog: http://t.csdn.cn/cN6p1

3. QT smart car device

3.1 QMusic music player

The music player is a routine project of QT practice in various embedded institutions, and the QMediaPlayer class is an advanced media player class. It can be used to play content such as songs, movies and Internet radio. It is generally used to play media files such as mp3 and mp4. The QMediaPlayer class is often used together with the QMediaPlaylist class. You can easily design a music player and video player you like.

QMediaPlayer provides a lot of signals, we can use these signals to complete a series of operations of the music player, such as the signal stateChanged(QMediaPlayer::State state) of the media state change , and judge the state of this state to know when the media is paused, played, or stopped.

The code added in the first line of the project file 14_musicplayer.pro file: multimedia

The specific code in the header file "mainwindow.h" is as follows:

/******************************************************************
* @projectName   musicplayer
* @brief         mainwindow.h
* @author        混分巨兽龙某某
* @email         [email protected]
*******************************************************************/
#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>
#include <QMediaPlayer>
#include <QMediaPlaylist>
#include <QPushButton>
#include <QSlider>
#include <QVBoxLayout>
#include <QHBoxLayout>
#include <QListWidget>
#include <QLabel>
#include <QSpacerItem>
#include <QDebug>

/* 媒体信息结构体 */
struct MediaObjectInfo {
    /* 用于保存歌曲文件名 */
    QString fileName;
    /* 用于保存歌曲文件路径 */
    QString filePath;
};

class MainWindow : public QMainWindow
{
    Q_OBJECT

public:
    MainWindow(QWidget *parent = nullptr);
    ~MainWindow();

private:
    /* 媒体播放器,用于播放音乐 */
    QMediaPlayer *musicPlayer;

    /* 媒体列表 */
    QMediaPlaylist *mediaPlaylist;

    /* 音乐列表 */
    QListWidget *listWidget;

    /* 播放进度条 */
    QSlider *durationSlider;

    /* 音乐播放器按钮 */
    QPushButton *pushButton[7];
    QPushButton *exit_button;


    /* 垂直布局 */
    QVBoxLayout *vBoxLayout[3];

    /* 水平布局 */
    QHBoxLayout *hBoxLayout[4];

    /* 垂直容器 */
    QWidget *vWidget[3];

    /* 水平容器 */
    QWidget *hWidget[4];

    /* 标签文本 */
    QLabel *label[4];

    /* 用于遮罩 */
    QWidget *listMask;

    /* 音乐布局函数 */
    void musicLayout();

    /* 主窗体大小重设大小函数重写 */
    void resizeEvent(QResizeEvent *event);

    /* 媒体信息存储 */
    QVector<MediaObjectInfo> mediaObjectInfo;

    /* 扫描歌曲 */
    void scanSongs();

    /* 媒体播放器类初始化 */
    void mediaPlayerInit();

private slots:
    /* 播放按钮点击 */
    void btn_play_clicked();

    /* 下一曲按钮点击*/
    void btn_next_clicked();

    /* 上一曲按钮点击 */
    void btn_previous_clicked();

    /* 媒体状态改变 */
    void mediaPlayerStateChanged(QMediaPlayer::State);

    /* 列表单击 */
    void listWidgetCliked(QListWidgetItem*);

    /* 媒体列表项改变 */
    void mediaPlaylistCurrentIndexChanged(int);

    /* 媒体总长度改变 */
    void musicPlayerDurationChanged(qint64);

    /* 媒体播放位置改变 */
    void mediaPlayerPositionChanged(qint64);

    /* 播放进度条松开 */
    void durationSliderReleased();
};
#endif // MAINWINDOW_H

The specific code in the source file "mainwindow.cpp" is as follows:

#include "mainwindow.h"
#include <QCoreApplication>
#include <QFileInfoList>
#include <QDir>
#include <QProcess>


QProcess * mypro;

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
{
    /* 布局初始化 */
    musicLayout();

    /* 媒体播放器初始化 */
    mediaPlayerInit();

    /* 扫描歌曲 */
    scanSongs();

    /* 按钮信号槽连接 */
    connect(pushButton[0], SIGNAL(clicked()),
            this, SLOT(btn_previous_clicked()));
    connect(pushButton[1], SIGNAL(clicked()),
            this, SLOT(btn_play_clicked()));
    connect(pushButton[2], SIGNAL(clicked()),
            this, SLOT(btn_next_clicked()));

    /* 媒体信号槽连接 */
    connect(musicPlayer,
            SIGNAL(stateChanged(QMediaPlayer::State)),
            this,
            SLOT(mediaPlayerStateChanged(QMediaPlayer::State)));
    connect(mediaPlaylist,
            SIGNAL(currentIndexChanged(int)),
            this,
            SLOT(mediaPlaylistCurrentIndexChanged(int)));
    connect(musicPlayer, SIGNAL(durationChanged(qint64)),
            this,
            SLOT(musicPlayerDurationChanged(qint64)));
    connect(musicPlayer,
            SIGNAL(positionChanged(qint64)),
            this,
            SLOT(mediaPlayerPositionChanged(qint64)));

    /* 列表信号槽连接 */
    connect(listWidget, SIGNAL(itemClicked(QListWidgetItem*)),
            this, SLOT(listWidgetCliked(QListWidgetItem*)));

    /* slider信号槽连接 */
    connect(durationSlider, SIGNAL(sliderReleased()),
            this, SLOT(durationSliderReleased()));

    /* 失去焦点 */
    this->setFocus();

    exit_button = new QPushButton(this);
    exit_button->setMinimumSize(50, 50);
    exit_button->setMaximumSize(50, 50);
    exit_button->move(760,440);
    exit_button->setStyleSheet("QPushButton{background:yellow}");

    connect(exit_button,&QPushButton::clicked,[=](){
        mypro->close();
        exit(1);//退出程序
    });
}

void MainWindow::musicLayout()
{
    /* 设置位置与大小,这里固定为800, 480 */
    this->setGeometry(0, 0, 800, 480);
    QPalette pal;

    /* 按钮 */
    for (int i = 0; i < 7; i++)
        pushButton[i] = new QPushButton();

    /* 标签 */
    for (int i = 0; i < 4; i++)
        label[i] = new QLabel();

    for (int i = 0; i < 3; i++) {
        /* 垂直容器 */
        vWidget[i] = new QWidget();
        vWidget[i]->setAutoFillBackground(true);
        /* 垂直布局 */
        vBoxLayout[i] = new QVBoxLayout();
    }

    for (int i = 0; i < 4; i++) {
        /* 水平容器 */
        hWidget[i] = new QWidget();
        hWidget[i]->setAutoFillBackground(true);
        /* 水平布局 */
        hBoxLayout[i] = new QHBoxLayout();
    }

    /* 播放进度条 */
    durationSlider = new QSlider(Qt::Horizontal);
    durationSlider->setMinimumSize(300, 15);
    durationSlider->setMaximumHeight(15);
    durationSlider->setObjectName("durationSlider");

    /* 音乐列表 */
    listWidget = new QListWidget();
    listWidget->setObjectName("listWidget");
    listWidget->resize(310, 265);
    listWidget->setVerticalScrollBarPolicy(
                Qt::ScrollBarAlwaysOff);
    listWidget->setHorizontalScrollBarPolicy(
                Qt::ScrollBarAlwaysOff);

    /* 列表遮罩 */
    listMask = new QWidget(listWidget);
    listMask->setMinimumSize(310, 50);
    listMask->setMinimumHeight(50);
    listMask->setObjectName("listMask");
    listMask->setGeometry(0,
                          listWidget->height() - 50,
                          310,
                          50);


    /* 设置对象名称 */
    pushButton[0]->setObjectName("btn_previous");
    pushButton[1]->setObjectName("btn_play");
    pushButton[2]->setObjectName("btn_next");
    pushButton[3]->setObjectName("btn_favorite");
    pushButton[4]->setObjectName("btn_mode");
    pushButton[5]->setObjectName("btn_menu");
    pushButton[6]->setObjectName("btn_volume");

    /* 设置按钮属性 */
    pushButton[1]->setCheckable(true);
    pushButton[3]->setCheckable(true);

    /* H0布局 */
    vWidget[0]->setMinimumSize(310, 480);
    vWidget[0]->setMaximumWidth(310);
    vWidget[1]->setMinimumSize(320, 480);
    QSpacerItem *hSpacer0 = new
            QSpacerItem(70, 480,
                        QSizePolicy::Minimum,
                        QSizePolicy::Maximum);

    QSpacerItem *hSpacer1 = new
            QSpacerItem(65, 480,
                        QSizePolicy::Minimum,
                        QSizePolicy::Maximum);

    QSpacerItem *hSpacer2 = new
            QSpacerItem(60, 480,
                        QSizePolicy::Minimum,
                        QSizePolicy::Maximum);

    hBoxLayout[0]->addSpacerItem(hSpacer0);
    hBoxLayout[0]->addWidget(vWidget[0]);
    hBoxLayout[0]->addSpacerItem(hSpacer1);
    hBoxLayout[0]->addWidget(vWidget[1]);
    hBoxLayout[0]->addSpacerItem(hSpacer2);
    hBoxLayout[0]->setContentsMargins(0, 0, 0, 0);

    hWidget[0]->setLayout(hBoxLayout[0]);
    setCentralWidget(hWidget[0]);

    /* V0布局 */
    listWidget->setMinimumSize(310, 265);
    hWidget[1]->setMinimumSize(310, 80);
    hWidget[1]->setMaximumHeight(80);
    label[0]->setMinimumSize(310, 95);
    label[0]->setMaximumHeight(95);
    QSpacerItem *vSpacer0 = new
            QSpacerItem(310, 10,
                        QSizePolicy::Minimum,
                        QSizePolicy::Maximum);
    QSpacerItem *vSpacer1 = new
            QSpacerItem(310, 30,
                        QSizePolicy::Minimum,
                        QSizePolicy::Minimum);
    vBoxLayout[0]->addWidget(label[0]);
    vBoxLayout[0]->addWidget(listWidget);
    vBoxLayout[0]->addSpacerItem(vSpacer0);
    vBoxLayout[0]->addWidget(hWidget[1]);
    vBoxLayout[0]->addSpacerItem(vSpacer1);
    vBoxLayout[0]->setContentsMargins(0, 0, 0, 0);

    vWidget[0]->setLayout(vBoxLayout[0]);

    /* H1布局 */
    for (int i = 0; i < 3; i++) {
        pushButton[i]->setMinimumSize(80, 80);
    }
    QSpacerItem *hSpacer3 = new
            QSpacerItem(40, 80,
                        QSizePolicy::Expanding,
                        QSizePolicy::Expanding);
    QSpacerItem *hSpacer4 = new
            QSpacerItem(40, 80,
                        QSizePolicy::Expanding,
                        QSizePolicy::Expanding);
    hBoxLayout[1]->addWidget(pushButton[0]);
    hBoxLayout[1]->addSpacerItem(hSpacer3);
    hBoxLayout[1]->addWidget(pushButton[1]);
    hBoxLayout[1]->addSpacerItem(hSpacer4);
    hBoxLayout[1]->addWidget(pushButton[2]);
    hBoxLayout[1]->setContentsMargins(0, 0, 0, 0);

    hWidget[1]->setLayout(hBoxLayout[1]);

    /* V1布局 */
    QSpacerItem *vSpacer2 = new
            QSpacerItem(320, 40,
                        QSizePolicy::Minimum,
                        QSizePolicy::Maximum);
    QSpacerItem *vSpacer3 = new
            QSpacerItem(320, 20,
                        QSizePolicy::Minimum,
                        QSizePolicy::Maximum);
    QSpacerItem *vSpacer4 = new
            QSpacerItem(320, 30,
                        QSizePolicy::Minimum,
                        QSizePolicy::Minimum);
    label[1]->setMinimumSize(320, 320);
    QImage Image;
    Image.load(":/images/cd.png");
    QPixmap pixmap = QPixmap::fromImage(Image);
    int with = 320;
    int height = 320;
    QPixmap fitpixmap =
            pixmap.scaled(with, height,
                          Qt::IgnoreAspectRatio,
                          Qt::SmoothTransformation);
    label[1]->setPixmap(fitpixmap);
    label[1]->setAlignment(Qt::AlignCenter);
    vWidget[2]->setMinimumSize(300, 80);
    vWidget[2]->setMaximumHeight(80);
    vBoxLayout[1]->addSpacerItem(vSpacer2);
    vBoxLayout[1]->addWidget(label[1]);
    vBoxLayout[1]->addSpacerItem(vSpacer3);
    vBoxLayout[1]->addWidget(durationSlider);
    vBoxLayout[1]->addWidget(vWidget[2]);
    vBoxLayout[1]->addSpacerItem(vSpacer4);
    vBoxLayout[1]->setContentsMargins(0, 0, 0, 0);

    vWidget[1]->setLayout(vBoxLayout[1]);

    /* V2布局 */
    QSpacerItem *vSpacer5 = new
            QSpacerItem(300, 10,
                        QSizePolicy::Minimum,
                        QSizePolicy::Maximum);
    hWidget[2]->setMinimumSize(320, 20);
    hWidget[3]->setMinimumSize(320, 60);
    vBoxLayout[2]->addWidget(hWidget[2]);
    vBoxLayout[2]->addSpacerItem(vSpacer5);
    vBoxLayout[2]->addWidget(hWidget[3]);
    vBoxLayout[2]->setContentsMargins(0, 0, 0, 0);

    vWidget[2]->setLayout(vBoxLayout[2]);

    /* H2布局 */
    QFont font;

    font.setPixelSize(10);

    /* 设置标签文本 */
    label[0]->setText("Q Music,Enjoy it!");
    label[2]->setText("00:00");
    label[3]->setText("00:00");
    label[2]->setSizePolicy(QSizePolicy::Expanding,
                            QSizePolicy::Expanding);
    label[3]->setSizePolicy(QSizePolicy::Expanding,
                            QSizePolicy::Expanding);
    label[3]->setAlignment(Qt::AlignRight);
    label[2]->setAlignment(Qt::AlignLeft);
    label[2]->setFont(font);
    label[3]->setFont(font);

    pal.setColor(QPalette::WindowText, Qt::white);
    label[0]->setPalette(pal);
    label[2]->setPalette(pal);
    label[3]->setPalette(pal);

    hBoxLayout[2]->addWidget(label[2]);
    hBoxLayout[2]->addWidget(label[3]);

    hBoxLayout[2]->setContentsMargins(0, 0, 0, 0);
    hWidget[2]->setLayout(hBoxLayout[2]);

    /* H3布局 */
    QSpacerItem *hSpacer5 = new
            QSpacerItem(0, 60,
                        QSizePolicy::Minimum,
                        QSizePolicy::Maximum);
    QSpacerItem *hSpacer6 = new
            QSpacerItem(80, 60,
                        QSizePolicy::Maximum,
                        QSizePolicy::Maximum);
    QSpacerItem *hSpacer7 = new
            QSpacerItem(80, 60,
                        QSizePolicy::Maximum,
                        QSizePolicy::Maximum);
    QSpacerItem *hSpacer8 = new
            QSpacerItem(80, 60,
                        QSizePolicy::Maximum,
                        QSizePolicy::Maximum);
    QSpacerItem *hSpacer9 = new
            QSpacerItem(0, 60,
                        QSizePolicy::Minimum,
                        QSizePolicy::Maximum);

    for (int i = 3; i < 7; i++) {
        pushButton[i]->setMinimumSize(25, 25);
        pushButton[i]->setMaximumSize(25, 25);
    }

    hBoxLayout[3]->addSpacerItem(hSpacer5);
    hBoxLayout[3]->addWidget(pushButton[3]);
    hBoxLayout[3]->addSpacerItem(hSpacer6);
    hBoxLayout[3]->addWidget(pushButton[4]);
    hBoxLayout[3]->addSpacerItem(hSpacer7);
    hBoxLayout[3]->addWidget(pushButton[5]);
    hBoxLayout[3]->addSpacerItem(hSpacer8);
    hBoxLayout[3]->addWidget(pushButton[6]);
    hBoxLayout[3]->addSpacerItem(hSpacer9);
    hBoxLayout[3]->setContentsMargins(0, 0, 0, 0);
    hBoxLayout[3]->setAlignment(Qt::AlignHCenter);

    hWidget[3]->setLayout(hBoxLayout[3]);

    //hWidget[0]->setStyleSheet("background-color:red");
    //hWidget[1]->setStyleSheet("background-color:#ff5599");
    //hWidget[2]->setStyleSheet("background-color:#ff55ff");
    //hWidget[3]->setStyleSheet("background-color:black");
    //vWidget[0]->setStyleSheet("background-color:#555555");
    //vWidget[1]->setStyleSheet("background-color:green");
    //vWidget[2]->setStyleSheet("background-color:gray");

}

MainWindow::~MainWindow()
{
}

void MainWindow::btn_play_clicked()
{
    int state = musicPlayer->state();

    switch (state) {
    case QMediaPlayer::StoppedState:
        /* 媒体播放 */
        musicPlayer->play();
        break;

    case QMediaPlayer::PlayingState:
        /* 媒体暂停 */
        musicPlayer->pause();
        break;

    case QMediaPlayer::PausedState:
        musicPlayer->play();
        break;
    }
}

void MainWindow::btn_next_clicked()
{
    musicPlayer->stop();
    int count = mediaPlaylist->mediaCount();
    if (0 == count)
        return;

    /* 列表下一个 */
    mediaPlaylist->next();
    musicPlayer->play();
}

void MainWindow::btn_previous_clicked()
{
    musicPlayer->stop();
    int count = mediaPlaylist->mediaCount();
    if (0 == count)
        return;

    /* 列表上一个 */
    mediaPlaylist->previous();
    musicPlayer->play();
}

void MainWindow::mediaPlayerStateChanged(
        QMediaPlayer::State
        state)
{
    switch (state) {
    case QMediaPlayer::StoppedState:
        pushButton[1]->setChecked(false);
        break;

    case QMediaPlayer::PlayingState:
        pushButton[1]->setChecked(true);
        break;

    case QMediaPlayer::PausedState:
        pushButton[1]->setChecked(false);
        break;
    }
}

void MainWindow::listWidgetCliked(QListWidgetItem *item)
{
    musicPlayer->stop();
    mediaPlaylist->setCurrentIndex(listWidget->row(item));
    musicPlayer->play();
}

void MainWindow::mediaPlaylistCurrentIndexChanged(
        int index)
{
    if (-1 == index)
        return;

    /* 设置列表正在播放的项 */
    listWidget->setCurrentRow(index);
}

void MainWindow::musicPlayerDurationChanged(
        qint64 duration)
{
    durationSlider->setRange(0, duration / 1000);
    int second  = duration / 1000;
    int minute = second / 60;
    second %= 60;

    QString mediaDuration;
    mediaDuration.clear();

    if (minute >= 10)
        mediaDuration = QString::number(minute, 10);
    else
        mediaDuration = "0" + QString::number(minute, 10);

    if (second >= 10)
        mediaDuration = mediaDuration
                + ":" + QString::number(second, 10);
    else
        mediaDuration = mediaDuration
                + ":0" + QString::number(second, 10);

    /* 显示媒体总长度时间 */
    label[3]->setText(mediaDuration);
}

void MainWindow::mediaPlayerPositionChanged(
        qint64 position)
{
    if (!durationSlider->isSliderDown())
        durationSlider->setValue(position/1000);

    int second  = position / 1000;
    int minute = second / 60;
    second %= 60;

    QString mediaPosition;
    mediaPosition.clear();

    if (minute >= 10)
        mediaPosition = QString::number(minute, 10);
    else
        mediaPosition = "0" + QString::number(minute, 10);

    if (second >= 10)
        mediaPosition = mediaPosition
                + ":" + QString::number(second, 10);
    else
        mediaPosition = mediaPosition
                + ":0" + QString::number(second, 10);

    /* 显示现在播放的时间 */
    label[2]->setText(mediaPosition);
}

void MainWindow::resizeEvent(QResizeEvent *event)
{
    Q_UNUSED(event);
    listMask->setGeometry(0,
                          listWidget->height() - 50,
                          310,
                          50);
}

void MainWindow::durationSliderReleased()
{
    /* 设置媒体播放的位置 */
    musicPlayer->setPosition(durationSlider->value() * 1000);
}

void MainWindow::scanSongs()
{
    QDir dir(QCoreApplication::applicationDirPath()
             + "/myMusic");
    QDir dirbsolutePath(dir.absolutePath());
    /* 如果目录存在 */
    if (dirbsolutePath.exists()) {
        /* 定义过滤器 */
        QStringList filter;
        /* 包含所有.mp3后缀的文件 */
        filter << "*.mp3";
        /* 获取该目录下的所有文件 */
        QFileInfoList files =
                dirbsolutePath.entryInfoList(filter, QDir::Files);
        /* 遍历 */
        for (int i = 0; i < files.count(); i++) {
            MediaObjectInfo info;
            /* 使用utf-8编码 */
            QString fileName = QString::fromUtf8(files.at(i)
                                                 .fileName()
                                                 .replace(".mp3", "")
                                                 .toUtf8()
                                                 .data());
            info.fileName = fileName + "\n"
                    + fileName.split("-").at(1);
            info.filePath = QString::fromUtf8(files.at(i)
                                              .filePath()
                                              .toUtf8()
                                              .data());
            /* 媒体列表添加歌曲 */
            if (mediaPlaylist->addMedia(
                        QUrl::fromLocalFile(info.filePath))) {
                /* 添加到容器数组里储存 */
                mediaObjectInfo.append(info);
                /* 添加歌曲名字至列表 */
                listWidget->addItem(info.fileName);
            } else {
                qDebug()<<
                           mediaPlaylist->errorString()
                           .toUtf8().data()
                        << endl;
                qDebug()<< "  Error number:"
                         << mediaPlaylist->error()
                         << endl;
            }
        }
    }
}

void MainWindow::mediaPlayerInit()
{
    musicPlayer = new QMediaPlayer(this);
    mediaPlaylist = new QMediaPlaylist(this);
    /* 确保列表是空的 */
    mediaPlaylist->clear();
    /* 设置音乐播放器的列表为mediaPlaylist */
    musicPlayer->setPlaylist(mediaPlaylist);
    /* 设置播放模式,Loop是列循环 */
    mediaPlaylist->setPlaybackMode(QMediaPlaylist::Loop);
}

The above code is the regular QT page layout, operation function and signal, the code refers to the tutorial of punctual atom. What needs special attention is the need to add an exit button to the function page of QMusic, that is, to switch to its parent process! The details are as follows:

operation result:

3.2 QVideo video player

The video player is still a regular item of QT practice in various embedded organizations. It uses the QMediaPlayer class like the music player. The difference is that you need to use setVideoOutput(QVideoWidget*) to set a video output window so that the video can be displayed in this window. The other steps are basically the same.

The code added in the first line of the project file 15_videoplayer.pro file: multimedia multimediawidgets

The specific code in the header file "mainwindow.h" is as follows

#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>
#include <QMediaPlayer>
#include <QMediaPlaylist>
#include <QPushButton>
#include <QSlider>
#include <QVBoxLayout>
#include <QHBoxLayout>
#include <QListWidget>
#include <QLabel>
#include <QSpacerItem>
#include <QVideoWidget>
#include <QDebug>

/* 媒体信息结构体 */
struct MediaObjectInfo {
    /* 用于保存视频文件名 */
    QString fileName;
    /* 用于保存视频文件路径 */
    QString filePath;
};

class MainWindow : public QMainWindow
{
    Q_OBJECT

public:
    MainWindow(QWidget *parent = nullptr);
    ~MainWindow();

private:
    /* 媒体播放器,用于播放视频 */
    QMediaPlayer *videoPlayer;

    /* 媒体列表 */
    QMediaPlaylist *mediaPlaylist;

    /* 视频显示窗口 */
    QVideoWidget *videoWidget;

    /* 视频列表 */
    QListWidget *listWidget;

    /* 播放进度条 */
    QSlider *durationSlider;

    /* 音量条 */
    QSlider *volumeSlider;

    /* 视频播放器按钮 */
    QPushButton *pushButton[5];

    QPushButton *exit_button;

    /* 水平布局 */
    QHBoxLayout *hBoxLayout[3];

    /* 水平容器 */
    QWidget *hWidget[3];

    /* 标签文本 */
    QLabel *label[2];

    /* 垂直容器 */
    QWidget *vWidget[2];

    /* 垂直界面 */
    QVBoxLayout *vBoxLayout[2];

    /* 视频布局函数 */
    void videoLayout();

    /* 主窗体大小重设大小函数重写 */
    void resizeEvent(QResizeEvent *event);

    /* 媒体信息存储 */
    QVector<MediaObjectInfo> mediaObjectInfo;

    /* 扫描本地视频文件 */
    void scanVideoFiles();

    /* 媒体初始化 */
    void mediaPlayerInit();
private slots:
    /* 播放按钮点击 */
    void btn_play_clicked();

    /* 下一个视频按钮点击 */
    void btn_next_clicked();

    /* 音量加 */
    void btn_volmeup_clicked();

    /* 音量减 */
    void btn_volmedown_clicked();

    /* 全屏 */
    void btn_fullscreen_clicked();

    /* 媒体状态改变 */
    void mediaPlayerStateChanged(QMediaPlayer::State);

    /* 列表单击 */
    void listWidgetCliked(QListWidgetItem*);

    /* 媒体列表项改变 */
    void mediaPlaylistCurrentIndexChanged(int);

    /* 媒体总长度改变 */
    void musicPlayerDurationChanged(qint64);

    /* 媒体播放位置改变 */
    void mediaPlayerPositionChanged(qint64);

    /* 播放进度条松开 */
    void durationSliderReleased();

    /* 音量条松开 */
    void volumeSliderReleased();
};
#endif // MAINWINDOW_H

The specific code in the source file  "mainwindow.cpp"  is as follows:

#include "mainwindow.h"
#include <QCoreApplication>
#include <QFileInfoList>
#include <QDir>
#include <QProcess>


QProcess * mypro;
MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
{
    /* 视频播放器布局初始化 */
    videoLayout();

    /* 媒体初始化 */
    mediaPlayerInit();

    /* 扫描本地视频 */
    scanVideoFiles();

    /* 设置按钮的属性 */
    pushButton[0]->setCheckable(true);
    pushButton[4]->setCheckable(true);

    /* 按钮连接信号槽 */
    connect(pushButton[0], SIGNAL(clicked()),
            this, SLOT(btn_play_clicked()));
    connect(pushButton[1], SIGNAL(clicked()),
            this, SLOT(btn_next_clicked()));
    connect(pushButton[2], SIGNAL(clicked()),
            this, SLOT(btn_volmedown_clicked()));
    connect(pushButton[3], SIGNAL(clicked()),
            this, SLOT(btn_volmeup_clicked()));
    connect(pushButton[4], SIGNAL(clicked()),
            this, SLOT(btn_fullscreen_clicked()));

    /* 列表连接信号槽 */
    connect(listWidget, SIGNAL(itemClicked(QListWidgetItem*)),
            this, SLOT(listWidgetCliked(QListWidgetItem*)));

    /* 媒体连接信号槽 */
    connect(videoPlayer,
            SIGNAL(stateChanged(QMediaPlayer::State)),
            this,
            SLOT(mediaPlayerStateChanged(QMediaPlayer::State)));
    connect(mediaPlaylist,
            SIGNAL(currentIndexChanged(int)),
            this,
            SLOT(mediaPlaylistCurrentIndexChanged(int)));
    connect(videoPlayer, SIGNAL(durationChanged(qint64)),
            this,
            SLOT(musicPlayerDurationChanged(qint64)));
    connect(videoPlayer,
            SIGNAL(positionChanged(qint64)),
            this,
            SLOT(mediaPlayerPositionChanged(qint64)));

    /* slider信号槽连接 */
    connect(durationSlider, SIGNAL(sliderReleased()),
            this, SLOT(durationSliderReleased()));
    connect(volumeSlider, SIGNAL(sliderReleased()),
            this, SLOT(volumeSliderReleased()));

    /* 退出按钮 */
    exit_button = new QPushButton(this);
    exit_button->setMinimumSize(60, 50);
    exit_button->setMaximumSize(60, 50);
    exit_button->move(740,10);
    exit_button->setStyleSheet("QPushButton{background: black}");

    connect(exit_button,&QPushButton::clicked,[=](){
        mypro->close();
        exit(1);//退出程序
    });

}

MainWindow::~MainWindow()
{
}

void MainWindow::videoLayout()
{
    /* 设置位置与大小,这里固定为800, 480 */
    this->setGeometry(0, 0, 800, 480);
    //    this->setMinimumSize(800, 480);
    //    this->setMaximumSize(800, 480);
    QPalette pal;
    pal.setColor(QPalette::WindowText, Qt::white);

    for (int i = 0; i < 3; i++) {
        /* 水平容器 */
        hWidget[i] = new QWidget();
        hWidget[i]->setAutoFillBackground(true);
        /* 水平布局 */
        hBoxLayout[i] = new QHBoxLayout();
    }

    for (int i = 0; i < 2; i++) {
        /* 垂直容器 */
        vWidget[i] = new QWidget();
        vWidget[i]->setAutoFillBackground(true);
        /* 垂直布局 */
        vBoxLayout[i] = new QVBoxLayout();
    }

    for (int i = 0; i < 2; i++) {
        label[i] = new QLabel();
    }

    for (int i = 0; i < 5; i++) {
        pushButton[i] = new QPushButton();
        pushButton[i]->setMaximumSize(44, 44);
        pushButton[i]->setMinimumSize(44, 44);
    }

    /* 设置 */
    vWidget[0]->setObjectName("vWidget0");
    vWidget[1]->setObjectName("vWidget1");
    hWidget[1]->setObjectName("hWidget1");
    hWidget[2]->setObjectName("hWidget2");
    pushButton[0]->setObjectName("btn_play");
    pushButton[1]->setObjectName("btn_next");
    pushButton[2]->setObjectName("btn_volumedown");
    pushButton[3]->setObjectName("btn_volumeup");
    pushButton[4]->setObjectName("btn_screen");

    QFont font;

    font.setPixelSize(18);
    label[0]->setFont(font);
    label[1]->setFont(font);

    pal.setColor(QPalette::WindowText, Qt::white);
    label[0]->setPalette(pal);
    label[1]->setPalette(pal);

    label[0]->setText("00:00");
    label[1]->setText("/00:00");

    durationSlider = new QSlider(Qt::Horizontal);
    durationSlider->setMaximumHeight(15);
    durationSlider->setObjectName("durationSlider");

    volumeSlider = new QSlider(Qt::Horizontal);
    volumeSlider->setRange(0, 100);
    volumeSlider->setMaximumWidth(80);
    volumeSlider->setObjectName("volumeSlider");
    volumeSlider->setValue(50);

    listWidget = new QListWidget();
    listWidget->setObjectName("listWidget");
    listWidget->setVerticalScrollBarPolicy(
                Qt::ScrollBarAlwaysOff);
    listWidget->setHorizontalScrollBarPolicy(
                Qt::ScrollBarAlwaysOff);
    //listWidget->setFocusPolicy(Qt::NoFocus);
    videoWidget = new QVideoWidget();
    videoWidget->setStyleSheet("border-image: none;"
                               "background: transparent;"
                               "border:none");

    /* H0布局 */
    vWidget[0]->setMinimumSize(300, 480);
    vWidget[0]->setMaximumWidth(300);
    videoWidget->setMinimumSize(500, 480);

    hBoxLayout[0]->addWidget(videoWidget);
    hBoxLayout[0]->addWidget(vWidget[0]);

    hWidget[0]->setLayout(hBoxLayout[0]);
    hBoxLayout[0]->setContentsMargins(0, 0, 0, 0);

    setCentralWidget(hWidget[0]);

    /* V0布局 */
    QSpacerItem *vSpacer0 = new
            QSpacerItem(0, 80,
                        QSizePolicy::Minimum,
                        QSizePolicy::Maximum);
    vBoxLayout[0]->addWidget(listWidget);
    vBoxLayout[0]->addSpacerItem(vSpacer0);
    vBoxLayout[0]->setContentsMargins(0, 0, 0, 0);

    vWidget[0]->setLayout(vBoxLayout[0]);

    /* V1布局 */
    /* 底板部件布局 */
    hWidget[1]->setMaximumHeight(15);
    hWidget[2]->setMinimumHeight(65);
    vBoxLayout[1]->addWidget(hWidget[1]);
    vBoxLayout[1]->addWidget(hWidget[2]);
    vBoxLayout[1]->setAlignment(Qt::AlignCenter);

    vWidget[1]->setLayout(vBoxLayout[1]);
    vWidget[1]->setParent(this);
    vWidget[1]->setGeometry(0, this->height() - 80, this->width(), 80);
    vBoxLayout[1]->setContentsMargins(0, 0, 0, 0);
    /* 位于最上层 */
    vWidget[1]->raise();

    /* H1布局 */
    hBoxLayout[1]->addWidget(durationSlider);
    hBoxLayout[1]->setContentsMargins(0, 0, 0, 0);
    hWidget[1]->setLayout(hBoxLayout[1]);

    /* H2布局 */
    QSpacerItem *hSpacer0 = new
            QSpacerItem(300, 80,
                        QSizePolicy::Expanding,
                        QSizePolicy::Maximum);

    hBoxLayout[2]->addSpacing(20);
    hBoxLayout[2]->addWidget(pushButton[0]);
    hBoxLayout[2]->addSpacing(10);
    hBoxLayout[2]->addWidget(pushButton[1]);
    hBoxLayout[2]->addSpacing(10);
    hBoxLayout[2]->addWidget(pushButton[2]);
    hBoxLayout[2]->addWidget(volumeSlider);
    hBoxLayout[2]->addWidget(pushButton[3]);
    hBoxLayout[2]->addWidget(label[0]);
    hBoxLayout[2]->addWidget(label[1]);
    hBoxLayout[2]->addSpacerItem(hSpacer0);
    hBoxLayout[2]->addWidget(pushButton[4]);
    hBoxLayout[2]->addSpacing(20);
    hBoxLayout[2]->setContentsMargins(0, 0, 0, 0);
    hBoxLayout[2]->setAlignment(Qt::AlignLeft | Qt::AlignTop);

    hWidget[2]->setLayout(hBoxLayout[2]);
}

void MainWindow::mediaPlayerInit()
{
    videoPlayer = new QMediaPlayer(this);
    mediaPlaylist = new QMediaPlaylist(this);
    /* 确保列表是空的 */
    mediaPlaylist->clear();
    /* 设置视频播放器的列表为mediaPlaylist */
    videoPlayer->setPlaylist(mediaPlaylist);
    /* 设置视频输出窗口 */
    videoPlayer->setVideoOutput(videoWidget);
    /* 设置播放模式,Loop是列循环 */
    mediaPlaylist->setPlaybackMode(QMediaPlaylist::Loop);
    /* 设置默认软件音量为50% */
    videoPlayer->setVolume(50);
}

void MainWindow::resizeEvent(QResizeEvent *event)
{
    Q_UNUSED(event);
    vWidget[1]->setGeometry(0, this->height() - 80, this->width(), 80);
}

void MainWindow::btn_play_clicked()
{
    int state = videoPlayer->state();
    switch (state) {
    case QMediaPlayer::StoppedState:
        /* 媒体播放 */
        videoPlayer->play();
        break;

    case QMediaPlayer::PlayingState:
        /* 媒体暂停 */
        videoPlayer->pause();
        break;

    case QMediaPlayer::PausedState:
        /* 设置视频输出窗口 */
        videoPlayer->play();
        break;
    }
}

void MainWindow::btn_next_clicked()
{
    videoPlayer->stop();
    int count = mediaPlaylist->mediaCount();
    if (0 == count)
        return;

    /* 列表下一个 */
    mediaPlaylist->next();
    videoPlayer->play();
}

void MainWindow::btn_volmeup_clicked()
{
    /* 点击每次音量+5 */
    volumeSlider->setValue(volumeSlider->value() + 5);
    videoPlayer->setVolume(volumeSlider->value());
}

void MainWindow::btn_fullscreen_clicked()
{
    /* 全屏/非全屏操作 */
    vWidget[0]->setVisible(!pushButton[4]->isChecked());
}

void MainWindow::btn_volmedown_clicked()
{
    /* 点击每次音量-5 */
    volumeSlider->setValue(volumeSlider->value() - 5);
    videoPlayer->setVolume(volumeSlider->value());
}

void MainWindow::mediaPlayerStateChanged(
        QMediaPlayer::State
        state)
{
    switch (state) {
    case QMediaPlayer::StoppedState:
        pushButton[0]->setChecked(false);
        break;

    case QMediaPlayer::PlayingState:
        pushButton[0]->setChecked(true);
        break;

    case QMediaPlayer::PausedState:
        pushButton[0]->setChecked(false);
        break;
    }
}

void MainWindow::listWidgetCliked(QListWidgetItem *item)
{
    videoPlayer->stop();
    mediaPlaylist->setCurrentIndex(listWidget->row(item));
    videoPlayer->play();
}

void MainWindow::mediaPlaylistCurrentIndexChanged(
        int index)
{
    if (-1 == index)
        return;

    /* 设置列表正在播放的项 */
    listWidget->setCurrentRow(index);
}

void MainWindow::musicPlayerDurationChanged(
        qint64 duration)
{
    durationSlider->setRange(0, duration / 1000);
    int second  = duration / 1000;
    int minute = second / 60;
    second %= 60;

    QString mediaDuration;
    mediaDuration.clear();

    if (minute >= 10)
        mediaDuration = QString::number(minute, 10);
    else
        mediaDuration = "0" + QString::number(minute, 10);

    if (second >= 10)
        mediaDuration = mediaDuration
                + ":" + QString::number(second, 10);
    else
        mediaDuration = mediaDuration
                + ":0" + QString::number(second, 10);

    /* 显示媒体总长度时间 */
    label[1]->setText("/" + mediaDuration);
}

void MainWindow::mediaPlayerPositionChanged(
        qint64 position)
{
    if (!durationSlider->isSliderDown())
        durationSlider->setValue(position / 1000);

    int second  = position / 1000;
    int minute = second / 60;
    second %= 60;

    QString mediaPosition;
    mediaPosition.clear();

    if (minute >= 10)
        mediaPosition = QString::number(minute, 10);
    else
        mediaPosition = "0" + QString::number(minute, 10);

    if (second >= 10)
        mediaPosition = mediaPosition
                + ":" + QString::number(second, 10);
    else
        mediaPosition = mediaPosition
                + ":0" + QString::number(second, 10);

    /* 显示现在播放的时间 */
    label[0]->setText(mediaPosition);
}

void MainWindow::durationSliderReleased()
{
    /* 设置媒体播放的位置 */
    videoPlayer->setPosition(durationSlider->value() * 1000);
}

void MainWindow::volumeSliderReleased()
{
    /* 设置音量 */
    videoPlayer->setVolume(volumeSlider->value());
}

void MainWindow::scanVideoFiles()
{
    QDir dir(QCoreApplication::applicationDirPath()
             + "/myVideo");
    QDir dirbsolutePath(dir.absolutePath());
    /* 如果目录存在 */
    if (dirbsolutePath.exists()) {
        /* 定义过滤器 */
        QStringList filter;
        /* 包含所有xx后缀的文件 */
        filter << "*.mp4" << "*.mkv" << "*.wmv" << "*.avi";
        /* 获取该目录下的所有文件 */
        QFileInfoList files =
                dirbsolutePath.entryInfoList(filter, QDir::Files);
        /* 遍历 */
        for (int i = 0; i < files.count(); i++) {
            MediaObjectInfo info;
            /* 使用utf-8编码 */
            info.fileName = QString::fromUtf8(files.at(i)
                                              .fileName()
                                              .toUtf8()
                                              .data());
            info.filePath = QString::fromUtf8(files.at(i)
                                              .filePath()
                                              .toUtf8()
                                              .data());
            /* 媒体列表添加视频 */
            if (mediaPlaylist->addMedia(
                        QUrl::fromLocalFile(info.filePath))) {
                /* 添加到容器数组里储存 */
                mediaObjectInfo.append(info);
                /* 添加视频名字至列表 */
                listWidget->addItem(info.fileName);
            } else {
                qDebug()<<
                           mediaPlaylist->errorString()
                           .toUtf8().data()
                        << endl;
                qDebug()<< "  Error number:"
                        << mediaPlaylist->error()
                        << endl;
            }
        }
    }
}

Same as the music player in the previous section, initialize the layout in the constructor, and then perform scanning of local video files. After that, there are some signal slot connections, which is basically such a process. Special attention: IMX6ULL cannot decode Chinese characters, so the file name of mp4 format with Chinese character names will become ???, but it will not affect the program operation!

operation result:

3.3 AP3216C monitoring

The function page of the smart home APP replaced by the author of this project is the vehicle interior monitoring function of AP3216C!

Limited space, the use of AP3216 and QT function page design blog: http://t.csdn.cn/cN6p1

operation result:

3.4 Realization of Terminal APP Switching

★This part is the core part of this blog, I hope readers can master the multi-process operation under Linux+QT through here!

3.4.1 Picture production

The author has always believed that the soul of QT is design aesthetics, the optimization process is smooth, and the realization of functions is a very basic part! Each author may try to make some products by himself or may not have an artist, the author provides a method here!

Select the icon and picture information we need through the Internet, and use the Go Online Go Background website to perform the background removal operation!

Import the picture after removing the background into Ubuntu, and keep the icon size as consistent as possible (some deviation is allowed!)

3.4.2 Make APP function page

Considering that the function implementation and framework of these APP icons are similar, the author here only uses QMusic as an example for teaching!

Place the APP icon we prepared before by adding  the Qt Resource File resource !

1. Time display (scan detection) of the smart vehicle terminal:

    /* 获取屏幕的分辨率,Qt官方建议使用这
    * 种方法获取屏幕分辨率,防上多屏设备导致对应不上
    * 注意,这是获取整个桌面系统的分辨率
    */
    QList <QScreen *> list_screen = QGuiApplication::screens();

    /* 如果是ARM平台,直接设置大小为屏幕的大小 */
#if __arm__
     /* 重设大小 */
     this->resize(list_screen.at(0)->geometry().width(),
                 list_screen.at(0)->geometry().height());
#else
     /* 否则则设置主窗体大小为800x480 */
     this->resize(800,400);
#endif

    /* 构建布局标题和背景色 */
    this->setWindowTitle("智能车载系统");
    this->setStyleSheet("background-color: rgb(240, 255, 255);");   //QT颜色标:http://t.csdn.cn/SF7Rc

    //给进程分配空间
    my_pro = new QProcess(this);

    //时间显示label
    QLabel *time_label = new QLabel(this);
    time_label->setGeometry(10,20,240,100);//设置坐标
    //time_label->setStyleSheet("font-size:55px;color:black");//设置大小颜色
    time_label->setFont(QFont("Helvetica", 72));
    //time_label->ssetFont(QFont("Helvetica", 30)); // 设置字体为Arial,大小为16

    //日期显示label
    QLabel *date_label = new QLabel(this);
    date_label->setGeometry(200,100,240,100);//设置坐标
    date_label->setStyleSheet("font-size:25px;color:green");//设置大小颜色

    //智能车载装置
    QFont font("Arial", 30, QFont::Normal);
    QLabel *title_label = new QLabel(this);
    title_label->setGeometry(40,180,260,50);//设置坐标
    title_label->setFont(font);
    title_label->setText("智能车载终端");

    m_pLCD = new QLCDNumber(this);
    // 设置能显示的位数
    m_pLCD->setDigitCount(8);

    m_pLCD->setGeometry(20,30,230,100);
    // 设置显示的模式为十进制
    m_pLCD->setMode(QLCDNumber::Dec);
    // 设置样式
    m_pLCD->setStyleSheet("border: 1px solid green;font-size:70%;color: green");
    m_pLCD->resize(300,100);

    //定时器刷新显示
    QTimer *timer = new QTimer(this);
    timer->start(200);

    connect(timer,&QTimer::timeout,[=](){
        /* 获取当前日期 */
        QDate date = QDate::currentDate();//获取当前日期
        QString date_msg = QString("%1-%2-%3").arg(date.year()).arg(date.month()).arg(date.day());
        date_label->setText(date_msg);

        // 获取系统当前时间
        QDateTime dateTime = QDateTime::currentDateTime();
        // 显示的内容
        m_pLCD->display(dateTime.toString("HH:mm:ss"));


        /* 判断进程状态 */
        if(my_pro->state() == QProcess::NotRunning)
        {
            this->show();//重新显示窗口
        }
        else
        {
            this->hide();
        }

    });

The above code uses Q_Label and  QLCDNumber to create the date in normal font and "LED digital tube" font respectively, create a timer, trigger a signal every 200ms , and the function in the signal updates the time! At the same time, the key is to detect whether the my_pro process stops running. If it stops running, it will display the current this process (main menu process), otherwise it will hide this process!

2. QMusic function button operation:

menubutton.h:

#ifndef MENUBUTTON_H
#define MENUBUTTON_H
#include<QPushButton>
#include<QPropertyAnimation>
#include<QString>
#include<QEvent>
#include<QMouseEvent>

#include <QObject>
#include <QWidget>

/*
 * 作者:混分巨兽龙某某
 * csdn:混分巨兽龙某某
 * 邮箱:[email protected]
 * 嵌入式技术交流群:958820627
 */

/* 创建了MenuButton的类,并且该类继承QPushButton */
class MenuButton : public QPushButton
{
    Q_OBJECT

public:
    MenuButton(QString normal_path,QString press_path="",int pixwidth=10,int pixheight=10);
    void zoom1();
    void zoom2();

private:
    QString normal_path;
    QString press_path;
    QPropertyAnimation* animation;

protected:
    //void mousePressEvent(QMouseEvent * e);
    //void mouseReleaseEvent(QMouseEvent * e);

signals:

public slots:
};

#endif // MENUBUTTON_H

Through the above  menubutton.h file, we created a MenuButton class, which inherits QPushButton and can be used as a button (core skill). We read  the APP icon in the Qt Resource File file as a button! Then, layout and size the APP buttons!

    /* 菜单按钮切屏定时器 */
    time1= new QTimer(this);

    //音乐按钮
    MenuButton * music_button=new MenuButton(":/picture/music.png","",245,245);
    music_button->setParent(this);
    music_button->move(400,10);
    //音乐按钮按下处理
    connect(music_button,&MenuButton::clicked,[=](){
        /* 设计动作图标 */
        music_button->zoom1();//弹跳
        music_button->zoom2();
        /* 延迟500ms后启动播放器进程 */
        time1->start(500);
        connect(time1,&QTimer::timeout,[=](){
            time1->stop();
            my_pro->close();
            my_pro->start("./QMusicPlayer");});
     });

When a music_button object is created  with  the MenuButton class , that is, the set function is triggered after the APP button is pressed, music_button->zoom1() and  music_button->zoom2() are actually the beating animation of the APP button (making the vehicle terminal more beautified). Turn on time1 , trigger the signal after 500ms , turn off time1 , close the current my_pro process, and open the process as a QMusicPlayer process! And so on for the rest!

What we need to pay attention to is that the process name behind the start function must exist in the current directory and can run in the current device environment.

3.4. 3 Complete code

menubutton.h:

#ifndef MENUBUTTON_H
#define MENUBUTTON_H
#include<QPushButton>
#include<QPropertyAnimation>
#include<QString>
#include<QEvent>
#include<QMouseEvent>

#include <QObject>
#include <QWidget>

/*
 * 作者:混分巨兽龙某某
 * csdn:混分巨兽龙某某
 * 邮箱:[email protected]
 * 嵌入式技术交流群:958820627
 */

/* 创建了MenuButton的类,并且该类继承QPushButton */
class MenuButton : public QPushButton
{
    Q_OBJECT

public:
    MenuButton(QString normal_path,QString press_path="",int pixwidth=10,int pixheight=10);
    void zoom1();
    void zoom2();

private:
    QString normal_path;
    QString press_path;
    QPropertyAnimation* animation;

protected:

signals:

public slots:
};

#endif // MENUBUTTON_H

mainwindow.h:

#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>
#include <QLCDNumber>
#include <QProcess>
#include <QTimer>

QT_BEGIN_NAMESPACE
namespace Ui { class MainWindow; }
QT_END_NAMESPACE

/*
 * 作者:混分巨兽龙某某
 * csdn:混分巨兽龙某某
 * 邮箱:[email protected]
 * 嵌入式技术交流群:958820627
 */

class MainWindow : public QMainWindow
{
    Q_OBJECT

public:
    MainWindow(QWidget *parent = nullptr);
    ~MainWindow();

private:
    QLCDNumber *m_pLCD;
    QProcess *my_pro;
    QTimer *time1;

    /* 布局初始化 */
    void layoutInit();

private slots:
    //void onTimeOut();
};
#endif // MAINWINDOW_H

menubutton.cpp:

#include "menubutton.h"
#include <QDebug>

MenuButton::MenuButton(QString normal_path,QString press_path,int pixwidth,int pixheight)
{
    this->normal_path=normal_path;
    this->press_path=press_path;

    QPixmap pix;
    bool ret = pix.load(this->normal_path);
    if(!ret)
    {
        qDebug()<<"图片加载失败";
        return ;

    }
    //设置图片固定大小
    this->setFixedSize(pixwidth,pixheight);
    //设置不规则图片样式
    this->setStyleSheet( "QPushButton{border:0px;}" );
    //设置图标
    this->setIcon(pix);
    //设置图标大小
    this->setIconSize(QSize(pixwidth,pixheight));

    this->setFocusPolicy(Qt::NoFocus);     //去除虚线边框

    animation = new QPropertyAnimation(this,"geometry");

}

void MenuButton::zoom1()
{
    //设置动画时间间隔
    animation->setDuration(200);
    //设置起始位置
    animation->setStartValue(QRect(this->x(),this->y()+10,this->width(),this->height()));
    //设置结束位置
    animation->setEndValue(QRect(this->x(),this->y(),this->width(),this->height()));
    //设置弹跳曲线
    animation->setEasingCurve(QEasingCurve::OutBounce);
    //执行动画
    animation->start();
}

void MenuButton::zoom2()
{
    //设置动画时间间隔
    animation->setDuration(200);
    //设置起始位置
    animation->setStartValue(QRect(this->x(),this->y(),this->width(),this->height()));
    //设置结束位置
    animation->setEndValue(QRect(this->x(),this->y()-10,this->width(),this->height()));
    //设置弹跳曲线
    animation->setEasingCurve(QEasingCurve::InElastic);
    //执行动画
    animation->start();
}

mainwindow.cpp:

#include "mainwindow.h"
#include "ui_mainwindow.h"
#include<QLabel>
#include<QTimer>
#include<QTime>
#include<QProcess>
#include "menubutton.h"
#include <QScreen>
#include<QFont>

/*
 * 作者:混分巨兽龙某某
 * csdn:混分巨兽龙某某
 * 邮箱:[email protected]
 * 嵌入式技术交流群:958820627
 */

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
{
    /* 布局初始化 */
    layoutInit();
}

/* 析构函数 */
MainWindow::~MainWindow()
{

}

/* 屏幕布局初始化 */
void MainWindow::layoutInit()
{
    /* 获取屏幕的分辨率,Qt官方建议使用这
    * 种方法获取屏幕分辨率,防上多屏设备导致对应不上
    * 注意,这是获取整个桌面系统的分辨率
    */
    QList <QScreen *> list_screen = QGuiApplication::screens();

    /* 如果是ARM平台,直接设置大小为屏幕的大小 */
#if __arm__
     /* 重设大小 */
     this->resize(list_screen.at(0)->geometry().width(),
                 list_screen.at(0)->geometry().height());
#else
     /* 否则则设置主窗体大小为800x480 */
     this->resize(800,400);
#endif

    /* 构建布局标题和背景色 */
    this->setWindowTitle("智能车载系统");
    this->setStyleSheet("background-color: rgb(240, 255, 255);");   //QT颜色标:http://t.csdn.cn/SF7Rc

    //给进程分配空间
    my_pro = new QProcess(this);

    //时间显示label
    QLabel *time_label = new QLabel(this);
    time_label->setGeometry(10,20,240,100);//设置坐标
    //time_label->setStyleSheet("font-size:55px;color:black");//设置大小颜色
    time_label->setFont(QFont("Helvetica", 72));
    //time_label->ssetFont(QFont("Helvetica", 30)); // 设置字体为Arial,大小为16

    //日期显示label
    QLabel *date_label = new QLabel(this);
    date_label->setGeometry(200,100,240,100);//设置坐标
    date_label->setStyleSheet("font-size:25px;color:green");//设置大小颜色

    //智能车载装置
    QFont font("Arial", 30, QFont::Normal);
    QLabel *title_label = new QLabel(this);
    title_label->setGeometry(40,180,260,50);//设置坐标
    title_label->setFont(font);
    title_label->setText("智能车载终端");

    m_pLCD = new QLCDNumber(this);
    // 设置能显示的位数
    m_pLCD->setDigitCount(8);

    m_pLCD->setGeometry(20,30,230,100);
    // 设置显示的模式为十进制
    m_pLCD->setMode(QLCDNumber::Dec);
    // 设置样式
    m_pLCD->setStyleSheet("border: 1px solid green;font-size:70%;color: green");
    m_pLCD->resize(300,100);

    //定时器刷新显示
    QTimer *timer = new QTimer(this);
    timer->start(200);

    connect(timer,&QTimer::timeout,[=](){
        /* 获取当前日期 */
        QDate date = QDate::currentDate();//获取当前日期
        QString date_msg = QString("%1-%2-%3").arg(date.year()).arg(date.month()).arg(date.day());
        date_label->setText(date_msg);

        // 获取系统当前时间
        QDateTime dateTime = QDateTime::currentDateTime();
        // 显示的内容
        m_pLCD->display(dateTime.toString("HH:mm:ss"));


        /* 判断进程状态 */
        if(my_pro->state() == QProcess::NotRunning)
        {
            this->show();//重新显示窗口
        }
        else
        {
            this->hide();
        }

    });

    /* 菜单按钮切屏定时器 */
    time1= new QTimer(this);

    //音乐按钮
    MenuButton * music_button=new MenuButton(":/picture/music.png","",245,245);
    music_button->setParent(this);
    music_button->move(400,10);
    //音乐按钮按下处理
    connect(music_button,&MenuButton::clicked,[=](){
        /* 设计动作图标 */
        music_button->zoom1();//弹跳
        music_button->zoom2();
        /* 延迟500ms后启动播放器进程 */
        time1->start(500);
        connect(time1,&QTimer::timeout,[=](){
            time1->stop();
            my_pro->close();
            my_pro->start("./QMusicPlayer");});
        });


    //视频按钮
    MenuButton * video_button=new MenuButton(":/picture/bofangqi.png","",215,215);
    //time1= new QTimer(this);
    video_button->setParent(this);
    video_button->move(710,20);
    //视频按钮按下处理
    connect(video_button,&MenuButton::clicked,[=](){
        video_button->zoom1();//弹跳
        video_button->zoom2();
        time1->start(500);//定时500ms
        connect(time1,&QTimer::timeout,[=](){
            time1->stop(); //关闭定时器
            my_pro->close();
            my_pro->start("./QVideo");   });
        });

    //家居按钮
    MenuButton * sensor_button=new MenuButton(":/picture/sensor.png","",245,245);
    //time1= new QTimer(this);
    sensor_button->setParent(this);
    sensor_button->move(45,300);
    //硬件数据按下处理
    connect(sensor_button,&MenuButton::clicked,[=](){
        sensor_button->zoom1();//弹跳
        sensor_button->zoom2();
        time1->start(500);
        connect(time1,&QTimer::timeout,[=](){
            time1->stop();
            my_pro->close();
            my_pro->start("./senor");});
        });

    //地图按钮
    MenuButton * map_button=new MenuButton(":/picture/map.png","",240,245);
    map_button->setParent(this);
    map_button->move(400,300);

    //天气按钮
    MenuButton * weather_button=new MenuButton(":/picture/weather.png","",245,245);
    weather_button->setParent(this);
    weather_button->move(700,300);

    //进程结束处理
    connect(my_pro,&QProcess::stateChanged,[=](){

    });
}

operation result:

4. Project effect

4.1 Actual combat video

IMX6ULL smart vehicle terminal

4.2 The author has something to say

This project is a simplified version of a previous project of the author, considering the limited space of the teaching blog. The author shared the "prototype" of the original project with everyone. This project can realize the multi-level function switching of a simple smart vehicle terminal! The overall frame compatibility of the code is very high, and readers and friends can add or modify their own functions based on the author's code!

The follow-up author will teach and share most of the functions and codes of the original smart vehicle terminal when he is free! Including: page switching, map function realization, reversing image design and smooth icon switching process, etc. Thank you for reading, friends can look forward to a wave, I hope this blog will give you something to gain!

5. Code open source

Code address:  IMX6ULL-based intelligent vehicle terminal project code resource - CSDN library

If you don't have enough points, click Bo to follow , leave an email in the comment area , and the author will provide the source code and follow-up questions for free . Please pay attention to a wave! ! !

Guess you like

Origin blog.csdn.net/black_sneak/article/details/131889750