前言:本文為手把手教學(xué)智能車載終端項(xiàng)目(Linux+QT),該項(xiàng)目是綜合性非常強(qiáng)的 Linux 系列項(xiàng)目!項(xiàng)目核心板使用 NXP 的 IMX6ULL 作為 CPU,整體實(shí)現(xiàn)了簡化版本的車載終端功能需求。項(xiàng)目可以學(xué)習(xí)的點(diǎn)非常多,包含:IMX6ULL 的硬件驅(qū)動,QT 的移植與 Linux 多進(jìn)程操作等。該項(xiàng)目的源代碼適用于正點(diǎn)原子的出廠鏡像,只學(xué)習(xí)應(yīng)用層開發(fā)的也可以嘗試學(xué)習(xí)該項(xiàng)目!希望該篇博客可以給諸位些許收獲,博客篇尾代碼開源!
硬件實(shí)物圖:
效果圖:
考慮到該項(xiàng)目整體流程過于復(fù)雜化,作者將重點(diǎn)側(cè)重軟件即 Linux+QT 的框架實(shí)現(xiàn)!該項(xiàng)目的好處是:源代碼可以直接在正點(diǎn)原子 IMX6ULL 開發(fā)板上使用!如果只學(xué)習(xí)應(yīng)用層開發(fā)的讀者朋友可以直接用正點(diǎn)原子的出廠鏡像即可!
一、智能車載終端概述
智能車載終端是一種用于對運(yùn)輸車輛進(jìn)行現(xiàn)代化管理的設(shè)備。它融合了 GPS 技術(shù)、里程定位技術(shù)以及汽車黑匣技術(shù),可以用于多種用途,包括 DVR 行車記錄儀、智能車載后視鏡、網(wǎng)約車運(yùn)營終端、兩客一危車隊(duì)管理終端等。智能車載終端開始普及,使得汽車行業(yè)進(jìn)入智能化加速階段。相較于傳統(tǒng)終端產(chǎn)品,智能車載終端能夠減少平臺許可費(fèi)用、減少研發(fā)費(fèi)用、縮短研發(fā)與生產(chǎn)時間、覆蓋終端知識產(chǎn)權(quán)、快速產(chǎn)品規(guī)劃。智能車載終端往往需要覆蓋大量商用車輛,企業(yè)對性價比存在一定需求,而智能模組產(chǎn)品能夠助力企業(yè)快速進(jìn)行產(chǎn)品迭代,實(shí)現(xiàn)更高效地研發(fā)。
隨著科技與電車技術(shù)發(fā)展,智能車載終端的功能和要求越來越復(fù)雜化和高級化。作者博客篇幅有限,所以這次僅給諸位讀者教學(xué)簡單的 3 個功能的實(shí)現(xiàn)和功能切換方法!?
1.1 車載終端:影音播放器
目前汽車終端上,車主使用最多的應(yīng)該還是音頻類設(shè)備。比如:收音機(jī)、電臺、音樂播放器、聽書等等。所以,車載終端上必須使用成功驅(qū)動音頻類芯片(I2S等)。該音頻設(shè)備無論是借助第 3 方音樂播放器還是自主設(shè)計(jì)一款音樂播放器,都是在車載終端上必不可少的!
1.2 車載終端:地圖功能
車載終端上的地圖功能同樣是不可或缺的,車主往往需要進(jìn)行地圖導(dǎo)航(手機(jī)導(dǎo)航可能用得更多,但是功能你必須要有)。地圖導(dǎo)航依賴的功能實(shí)現(xiàn)就比較復(fù)雜,需要驅(qū)動 GPS 等,同時,根據(jù) GPS 等信息結(jié)合地圖軟件進(jìn)行定位導(dǎo)航!
1.3 車載終端:倒車影像
如自動駕駛一般,科技的進(jìn)步往往是降低人類的駕駛難度。倒車入庫等操作屬于有難度的駕駛技術(shù),結(jié)合現(xiàn)代科技的倒車影像等技術(shù)??梢苑奖丬囍骺焖偻瓿刹僮?,享受愉快生活!
1.4 車載終端:雜項(xiàng)功能
車載終端是一個非常復(fù)雜且常用的裝置,隨著電車與自動駕駛技術(shù)的不斷升級迭代,需求也與日俱增!我們往往需要在終端上附加許許多多其他的功能,比如:智能家居聯(lián)動、車內(nèi)環(huán)境監(jiān)測、天氣預(yù)報與網(wǎng)上瀏覽等!
二、IMX6ULL車載項(xiàng)目的驅(qū)動
作者強(qiáng)調(diào):考慮到篇幅有限,作者本篇博客僅進(jìn)行部分功能實(shí)現(xiàn)教學(xué),并側(cè)重應(yīng)用層的實(shí)現(xiàn)!基礎(chǔ)薄弱的朋友,可以直接使用正點(diǎn)原子出廠的鏡像程序進(jìn)行項(xiàng)目復(fù)現(xiàn)!
2.1 音頻設(shè)備驅(qū)動
音頻 CODEC 支持 I2S 協(xié)議,那么主控制器也必須支持 I2S 協(xié)議,I.MX6ULL 也提供了一個叫做 SAI 的外設(shè),全稱為 SynchronousAudio Interface,翻譯過來就是同步音頻接口。
I.MX6ULL 的 SAI 是一個全雙工、支持幀同步的串行接口,支持I2S、AC97、TDM和音頻DSP。
正點(diǎn)原子 ALPHA 開發(fā)板音頻原理圖如圖所示:
NXP 官方已經(jīng)寫好了 WM8960 驅(qū)動,因此我們直接配置內(nèi)核使能 WM8960 驅(qū)動即可,按照如下所示步驟使能 WM8960 驅(qū)動。
1、根據(jù)IMX6ULL的引腳原理圖修改設(shè)備樹;
2、使能內(nèi)核的 WM8960 驅(qū)動;
2.1、取消 ALSA 模擬 OSS API
2.2、使能 I.MX6ULL 的 WM8960 驅(qū)動
完整可見:《【正點(diǎn)原子】I.MX6U嵌入式Linux驅(qū)動開發(fā)指南V1.6》 第 1544 頁
2.2 LCD驅(qū)動
正點(diǎn)原子的 IMX6ULL 與 NXP 官方的 “藍(lán)版”相似度極高,就是仿照著進(jìn)行制作的(一般情況下,大都公司產(chǎn)品的項(xiàng)目板也是如此)
6ULL 的 eLCDIF 接口驅(qū)動程序 NXP 已經(jīng)編寫好了,因此 LCD 驅(qū)動部分我們不需要去修改。我們需要做的就是按照所使用的 LCD 來修改設(shè)備樹。重點(diǎn)要注意三個地方:
①、LCD 所使用的 IO 配置。
②、LCD 屏幕節(jié)點(diǎn)修改,修改相應(yīng)的屬性值,換成我們所使用的 LCD 屏幕參數(shù)。
③、LCD 背光節(jié)點(diǎn)信息修改,要根據(jù)實(shí)際所使用的背光 IO 來修改相應(yīng)的設(shè)備節(jié)點(diǎn)信息。
1、修改 LCD 所使用的 IO 配置;
檢查一下設(shè)備樹中 LCD 所使用的 IO 配置,這個其實(shí) NXP 都已經(jīng)給我們寫好了,不需要修改!
2、LCD 屏幕參數(shù)節(jié)點(diǎn)信息修改
在 imx6ull-alientek-emmc.dts 文件中找到 lcdif 節(jié)點(diǎn),節(jié)點(diǎn)內(nèi)容如下所示:
&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屏幕數(shù)據(jù)線有多少 */
display-timings {
native-mode = <&timing0>;
timing0: timing0 {
clock-frequency = <51200000>; /* 時鐘頻率51.2MHZ */
hactive = <1024>; /* 水平像素點(diǎn) */
vactive = <600>; /* 垂直像素點(diǎn) */
hfront-porch = <160>;
hback-porch = <140>;
hsync-len = <20>;
vback-porch = <20>;
vfront-porch = <12>;
vsync-len = <3>;
/* 像素點(diǎn)有效電平值 */
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>;
};
};
};*/
};
根據(jù)自己使用 LCD 的實(shí)際參數(shù)修改紅色框中的各個參數(shù)信息!
3、LCD 屏幕背光節(jié)點(diǎn)信息
正點(diǎn)原子的 LCD 接口背光控制 IO 連接到了 I.MX6U 的 GPIO1_IO08 引腳上,GPIO1_IO08復(fù)用為 PWM1_OUT,通過 PWM 信號來控制 LCD 屏幕背光的亮度。正點(diǎn)原子 I.MX6U-ALPHA 開發(fā)板的 LCD 背光引腳和 NXP 官方 EVK 開發(fā)板的背光引腳一樣,因此背光的設(shè)備樹節(jié)點(diǎn)是不需要修改的。
設(shè)置 backlight 節(jié)點(diǎn),這個 NXP 已經(jīng)給我們設(shè)置好了,大家在 imx6ull-alientek-emmc.dts 文件中找到如下內(nèi)容:
backlight {
compatible = "pwm-backlight";
pwms = <&pwm1 0 5000000>;
brightness-levels = <0 4 8 16 32 64 128 255>;
default-brightness-level = <7>;
status = "okay";
};
第 3 行,設(shè)置背光使用 pwm1,PWM 頻率為 200Hz。
第 4 行,設(shè)置背 8 級背光(0~7),分別為 0、4、8、16、32、64、128、255,對應(yīng)占空比為0%、1.57%、3.13%、6.27%、12.55%、25.1%、50.19%、100%,如果嫌少的話可以自行添加一些其他的背光等級值。
第 5 行,設(shè)置默認(rèn)背光等級為 6,也就是 50.19%?的亮度。
完整可見:《【正點(diǎn)原子】I.MX6U嵌入式Linux驅(qū)動開發(fā)指南V1.6》 第 1386 頁
2.3 AP3216C驅(qū)動
本項(xiàng)目作者替換的智能家居 APP 的功能頁為 AP3216C 的車載內(nèi)室監(jiān)測功能!
篇幅有限,AP3216的使用和 QT 功能頁設(shè)計(jì)博客:http://t.csdn.cn/cN6p1
三、QT智能車載裝置
3.1 QMusic音樂播放器
音樂播放器是各家嵌入式機(jī)構(gòu) QT 練習(xí)的常規(guī)項(xiàng)目,QMediaPlayer 類是一個高級媒體播放類。它可以用來播放歌曲、電影和網(wǎng)絡(luò)廣播等內(nèi)容。一般用于播放 mp3 和 mp4 等等媒體文件。QMediaPlayer 類常常與 QMediaPlaylist 類一起使用。可以很輕松的設(shè)計(jì)一個自己喜歡的音樂播放器與視頻播放器。
QMediaPlayer 提供了很多信號,我們可以使用這些信號來完成音樂播放器的一系列操作,比如媒體狀態(tài)改變的信號 stateChanged(QMediaPlayer::State state),判斷這個 state 的狀態(tài)就可以知道什么時候媒體暫停、播放、停止了。
★在項(xiàng)目文件 14_musicplayer.pro 文件第一行添加的代碼:multimedia
在頭文件 “mainwindow.h” 具體代碼如下:
/******************************************************************
* @projectName musicplayer
* @brief mainwindow.h
* @author 混分巨獸龍某某
* @email 1178305328@qq.com
*******************************************************************/
#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>
/* 媒體信息結(jié)構(gòu)體 */
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;
/* 播放進(jìn)度條 */
QSlider *durationSlider;
/* 音樂播放器按鈕 */
QPushButton *pushButton[7];
QPushButton *exit_button;
/* 垂直布局 */
QVBoxLayout *vBoxLayout[3];
/* 水平布局 */
QHBoxLayout *hBoxLayout[4];
/* 垂直容器 */
QWidget *vWidget[3];
/* 水平容器 */
QWidget *hWidget[4];
/* 標(biāo)簽文本 */
QLabel *label[4];
/* 用于遮罩 */
QWidget *listMask;
/* 音樂布局函數(shù) */
void musicLayout();
/* 主窗體大小重設(shè)大小函數(shù)重寫 */
void resizeEvent(QResizeEvent *event);
/* 媒體信息存儲 */
QVector<MediaObjectInfo> mediaObjectInfo;
/* 掃描歌曲 */
void scanSongs();
/* 媒體播放器類初始化 */
void mediaPlayerInit();
private slots:
/* 播放按鈕點(diǎn)擊 */
void btn_play_clicked();
/* 下一曲按鈕點(diǎn)擊*/
void btn_next_clicked();
/* 上一曲按鈕點(diǎn)擊 */
void btn_previous_clicked();
/* 媒體狀態(tài)改變 */
void mediaPlayerStateChanged(QMediaPlayer::State);
/* 列表單擊 */
void listWidgetCliked(QListWidgetItem*);
/* 媒體列表項(xiàng)改變 */
void mediaPlaylistCurrentIndexChanged(int);
/* 媒體總長度改變 */
void musicPlayerDurationChanged(qint64);
/* 媒體播放位置改變 */
void mediaPlayerPositionChanged(qint64);
/* 播放進(jìn)度條松開 */
void durationSliderReleased();
};
#endif // MAINWINDOW_H
在源文件 “mainwindow.cpp” 具體代碼如下:
#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()));
/* 失去焦點(diǎn) */
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()
{
/* 設(shè)置位置與大小,這里固定為800, 480 */
this->setGeometry(0, 0, 800, 480);
QPalette pal;
/* 按鈕 */
for (int i = 0; i < 7; i++)
pushButton[i] = new QPushButton();
/* 標(biāo)簽 */
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();
}
/* 播放進(jìn)度條 */
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);
/* 設(shè)置對象名稱 */
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");
/* 設(shè)置按鈕屬性 */
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);
/* 設(shè)置標(biāo)簽文本 */
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;
/* 設(shè)置列表正在播放的項(xiàng) */
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);
/* 顯示現(xiàn)在播放的時間 */
label[2]->setText(mediaPosition);
}
void MainWindow::resizeEvent(QResizeEvent *event)
{
Q_UNUSED(event);
listMask->setGeometry(0,
listWidget->height() - 50,
310,
50);
}
void MainWindow::durationSliderReleased()
{
/* 設(shè)置媒體播放的位置 */
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))) {
/* 添加到容器數(shù)組里儲存 */
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();
/* 設(shè)置音樂播放器的列表為mediaPlaylist */
musicPlayer->setPlaylist(mediaPlaylist);
/* 設(shè)置播放模式,Loop是列循環(huán) */
mediaPlaylist->setPlaybackMode(QMediaPlaylist::Loop);
}
上述代碼就是常規(guī)的 QT 頁面布局以及操作函數(shù)和信號,代碼參考了正點(diǎn)原子的教程。特別需要注意的地方是需要在 QMusic 的功能頁中加入退出按鈕,即切換至其父進(jìn)程!具體如下圖:
運(yùn)行結(jié)果:
3.2 QVideo視頻播放器
視頻播放器依舊是各家嵌入式機(jī)構(gòu)QT練習(xí)的常規(guī)項(xiàng)目,與音樂播放器一樣使用 QMediaPlayer 類,不同的是需要使用 setVideoOutput(QVideoWidget*) 設(shè)置一個視頻輸出窗口,好讓視頻在此窗口顯示,其他步驟基本都一樣。
★在項(xiàng)目文件 15_videoplayer.pro 文件第一行添加的代碼:multimedia multimediawidgets
在頭文件 “mainwindow.h” 具體代碼如下
#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>
/* 媒體信息結(jié)構(gòu)體 */
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;
/* 播放進(jìn)度條 */
QSlider *durationSlider;
/* 音量條 */
QSlider *volumeSlider;
/* 視頻播放器按鈕 */
QPushButton *pushButton[5];
QPushButton *exit_button;
/* 水平布局 */
QHBoxLayout *hBoxLayout[3];
/* 水平容器 */
QWidget *hWidget[3];
/* 標(biāo)簽文本 */
QLabel *label[2];
/* 垂直容器 */
QWidget *vWidget[2];
/* 垂直界面 */
QVBoxLayout *vBoxLayout[2];
/* 視頻布局函數(shù) */
void videoLayout();
/* 主窗體大小重設(shè)大小函數(shù)重寫 */
void resizeEvent(QResizeEvent *event);
/* 媒體信息存儲 */
QVector<MediaObjectInfo> mediaObjectInfo;
/* 掃描本地視頻文件 */
void scanVideoFiles();
/* 媒體初始化 */
void mediaPlayerInit();
private slots:
/* 播放按鈕點(diǎn)擊 */
void btn_play_clicked();
/* 下一個視頻按鈕點(diǎn)擊 */
void btn_next_clicked();
/* 音量加 */
void btn_volmeup_clicked();
/* 音量減 */
void btn_volmedown_clicked();
/* 全屏 */
void btn_fullscreen_clicked();
/* 媒體狀態(tài)改變 */
void mediaPlayerStateChanged(QMediaPlayer::State);
/* 列表單擊 */
void listWidgetCliked(QListWidgetItem*);
/* 媒體列表項(xiàng)改變 */
void mediaPlaylistCurrentIndexChanged(int);
/* 媒體總長度改變 */
void musicPlayerDurationChanged(qint64);
/* 媒體播放位置改變 */
void mediaPlayerPositionChanged(qint64);
/* 播放進(jìn)度條松開 */
void durationSliderReleased();
/* 音量條松開 */
void volumeSliderReleased();
};
#endif // MAINWINDOW_H
在源文件?“mainwindow.cpp”?具體代碼如下:
#include "mainwindow.h"
#include <QCoreApplication>
#include <QFileInfoList>
#include <QDir>
#include <QProcess>
QProcess * mypro;
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
{
/* 視頻播放器布局初始化 */
videoLayout();
/* 媒體初始化 */
mediaPlayerInit();
/* 掃描本地視頻 */
scanVideoFiles();
/* 設(shè)置按鈕的屬性 */
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()
{
/* 設(shè)置位置與大小,這里固定為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);
}
/* 設(shè)置 */
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();
/* 設(shè)置視頻播放器的列表為mediaPlaylist */
videoPlayer->setPlaylist(mediaPlaylist);
/* 設(shè)置視頻輸出窗口 */
videoPlayer->setVideoOutput(videoWidget);
/* 設(shè)置播放模式,Loop是列循環(huán) */
mediaPlaylist->setPlaybackMode(QMediaPlaylist::Loop);
/* 設(shè)置默認(rèn)軟件音量為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:
/* 設(shè)置視頻輸出窗口 */
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()
{
/* 點(diǎn)擊每次音量+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()
{
/* 點(diǎn)擊每次音量-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;
/* 設(shè)置列表正在播放的項(xiàng) */
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);
/* 顯示現(xiàn)在播放的時間 */
label[0]->setText(mediaPosition);
}
void MainWindow::durationSliderReleased()
{
/* 設(shè)置媒體播放的位置 */
videoPlayer->setPosition(durationSlider->value() * 1000);
}
void MainWindow::volumeSliderReleased()
{
/* 設(shè)置音量 */
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))) {
/* 添加到容器數(shù)組里儲存 */
mediaObjectInfo.append(info);
/* 添加視頻名字至列表 */
listWidget->addItem(info.fileName);
} else {
qDebug()<<
mediaPlaylist->errorString()
.toUtf8().data()
<< endl;
qDebug()<< " Error number:"
<< mediaPlaylist->error()
<< endl;
}
}
}
}
與上一小節(jié)音樂播放器的一樣,在構(gòu)造函數(shù)里布局初始化,然后執(zhí)行掃描本地視頻文件。之后就是一些信號槽的連接,基本上就是這么一個流程了。特別注意:IMX6ULL因?yàn)椴荒苓M(jìn)行漢字解碼,所以漢字名的 mp4 等格式文件名會變成???,但是不影響程序運(yùn)行!
運(yùn)行結(jié)果:
3.3 AP3216C監(jiān)測
本項(xiàng)目作者替換的智能家居 APP 的功能頁為 AP3216C 的車載內(nèi)室監(jiān)測功能!
篇幅有限,AP3216的使用和 QT 功能頁設(shè)計(jì)博客:http://t.csdn.cn/cN6p1
運(yùn)行結(jié)果:
3.4 終端APP切換實(shí)現(xiàn)
★該部分為本篇博客核心部分,希望讀者朋友可以通過這里掌握 Linux+QT 下的多進(jìn)程操作!
3.4.1 Picture制作
作者一直認(rèn)為 QT 的靈魂是設(shè)計(jì)美學(xué),優(yōu)化流程絲滑方面,功能的實(shí)現(xiàn)是非?;A(chǔ)的部分!各別作者可能嘗試自己制作一些產(chǎn)品可能沒有美工,作者這邊提供一種方法!
通過網(wǎng)絡(luò)選擇我們需要的圖標(biāo)和圖片信息,使用去在線去背景網(wǎng)站進(jìn)行去背景操作!
將去背景后的圖片導(dǎo)入到 Ubuntu 中,圖標(biāo)大小盡量保持一致(允許存在些許偏差?。?/p>
3.4.2 APP功能頁制作
考慮到這些 APP 圖標(biāo)的功能實(shí)現(xiàn)和框架是相似的,作者這里僅以 QMusic 為例進(jìn)行教學(xué)!
通過添加?Qt Resource File 資源放置我們之前準(zhǔn)備好的 APP 圖標(biāo)!
1、智能車載終端的時間顯示(掃描檢測):
/* 獲取屏幕的分辨率,Qt官方建議使用這
* 種方法獲取屏幕分辨率,防上多屏設(shè)備導(dǎo)致對應(yīng)不上
* 注意,這是獲取整個桌面系統(tǒng)的分辨率
*/
QList <QScreen *> list_screen = QGuiApplication::screens();
/* 如果是ARM平臺,直接設(shè)置大小為屏幕的大小 */
#if __arm__
/* 重設(shè)大小 */
this->resize(list_screen.at(0)->geometry().width(),
list_screen.at(0)->geometry().height());
#else
/* 否則則設(shè)置主窗體大小為800x480 */
this->resize(800,400);
#endif
/* 構(gòu)建布局標(biāo)題和背景色 */
this->setWindowTitle("智能車載系統(tǒng)");
this->setStyleSheet("background-color: rgb(240, 255, 255);"); //QT顏色標(biāo):http://t.csdn.cn/SF7Rc
//給進(jìn)程分配空間
my_pro = new QProcess(this);
//時間顯示label
QLabel *time_label = new QLabel(this);
time_label->setGeometry(10,20,240,100);//設(shè)置坐標(biāo)
//time_label->setStyleSheet("font-size:55px;color:black");//設(shè)置大小顏色
time_label->setFont(QFont("Helvetica", 72));
//time_label->ssetFont(QFont("Helvetica", 30)); // 設(shè)置字體為Arial,大小為16
//日期顯示label
QLabel *date_label = new QLabel(this);
date_label->setGeometry(200,100,240,100);//設(shè)置坐標(biāo)
date_label->setStyleSheet("font-size:25px;color:green");//設(shè)置大小顏色
//智能車載裝置
QFont font("Arial", 30, QFont::Normal);
QLabel *title_label = new QLabel(this);
title_label->setGeometry(40,180,260,50);//設(shè)置坐標(biāo)
title_label->setFont(font);
title_label->setText("智能車載終端");
m_pLCD = new QLCDNumber(this);
// 設(shè)置能顯示的位數(shù)
m_pLCD->setDigitCount(8);
m_pLCD->setGeometry(20,30,230,100);
// 設(shè)置顯示的模式為十進(jìn)制
m_pLCD->setMode(QLCDNumber::Dec);
// 設(shè)置樣式
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,[=](){
/* 獲取當(dāng)前日期 */
QDate date = QDate::currentDate();//獲取當(dāng)前日期
QString date_msg = QString("%1-%2-%3").arg(date.year()).arg(date.month()).arg(date.day());
date_label->setText(date_msg);
// 獲取系統(tǒng)當(dāng)前時間
QDateTime dateTime = QDateTime::currentDateTime();
// 顯示的內(nèi)容
m_pLCD->display(dateTime.toString("HH:mm:ss"));
/* 判斷進(jìn)程狀態(tài) */
if(my_pro->state() == QProcess::NotRunning)
{
this->show();//重新顯示窗口
}
else
{
this->hide();
}
});
上述代碼通過 Q_Label 和?QLCDNumber 分別創(chuàng)造了普通字體的日期和 “LED數(shù)碼管” 字體,創(chuàng)造一個 timer,每 200ms 觸發(fā)一次信號,信號中函數(shù)進(jìn)行更新時間!同時關(guān)鍵的是進(jìn)行檢測 my_pro 進(jìn)程是否停止運(yùn)行,如果停止運(yùn)行就顯示當(dāng)前 this 進(jìn)程(主菜單進(jìn)程),否則就隱藏 this 進(jìn)程!
2、QMusic 功能按鈕操作:
menubutton.h:
#ifndef MENUBUTTON_H
#define MENUBUTTON_H
#include<QPushButton>
#include<QPropertyAnimation>
#include<QString>
#include<QEvent>
#include<QMouseEvent>
#include <QObject>
#include <QWidget>
/*
* 作者:混分巨獸龍某某
* csdn:混分巨獸龍某某
* 郵箱:1178305328@qq.com
* 嵌入式技術(shù)交流群:958820627
*/
/* 創(chuàng)建了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
通過上述?menubutton.h文件,我們創(chuàng)建了一個 MenuButton 的類,該類繼承了 QPushButton 可以作為一個按鈕(核心技巧)。我們通過讀取?Qt Resource File 文件中的 APP 圖標(biāo)作為按鈕!隨后,對 APP 按鈕進(jìn)行布局和設(shè)置大??!
/* 菜單按鈕切屏定時器 */
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,[=](){
/* 設(shè)計(jì)動作圖標(biāo) */
music_button->zoom1();//彈跳
music_button->zoom2();
/* 延遲500ms后啟動播放器進(jìn)程 */
time1->start(500);
connect(time1,&QTimer::timeout,[=](){
time1->stop();
my_pro->close();
my_pro->start("./QMusicPlayer");});
});
當(dāng)用?MenuButton 類創(chuàng)建了一個?music_button 對象,即 APP 按鈕被按下之后觸發(fā)設(shè)定好的函數(shù),music_button->zoom1() 和?music_button->zoom2() 其實(shí)就是 APP 按鈕的跳動動畫(使得車載終端更美化)。開啟 time1,500ms 之后觸發(fā)信號,關(guān)閉 time1 ,關(guān)閉當(dāng)前my_pro 進(jìn)程,并將該進(jìn)程開啟成 QMusicPlayer 進(jìn)程!其余的以此類推!
我們需要注意的是,start 函數(shù)后面的進(jìn)程名一定要存在當(dāng)前目錄中,且可以在當(dāng)前設(shè)備環(huán)境下運(yùn)行才可以
3.4. 3 完整代碼
menubutton.h:
#ifndef MENUBUTTON_H
#define MENUBUTTON_H
#include<QPushButton>
#include<QPropertyAnimation>
#include<QString>
#include<QEvent>
#include<QMouseEvent>
#include <QObject>
#include <QWidget>
/*
* 作者:混分巨獸龍某某
* csdn:混分巨獸龍某某
* 郵箱:1178305328@qq.com
* 嵌入式技術(shù)交流群:958820627
*/
/* 創(chuàng)建了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:混分巨獸龍某某
* 郵箱:1178305328@qq.com
* 嵌入式技術(shù)交流群: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 ;
}
//設(shè)置圖片固定大小
this->setFixedSize(pixwidth,pixheight);
//設(shè)置不規(guī)則圖片樣式
this->setStyleSheet( "QPushButton{border:0px;}" );
//設(shè)置圖標(biāo)
this->setIcon(pix);
//設(shè)置圖標(biāo)大小
this->setIconSize(QSize(pixwidth,pixheight));
this->setFocusPolicy(Qt::NoFocus); //去除虛線邊框
animation = new QPropertyAnimation(this,"geometry");
}
void MenuButton::zoom1()
{
//設(shè)置動畫時間間隔
animation->setDuration(200);
//設(shè)置起始位置
animation->setStartValue(QRect(this->x(),this->y()+10,this->width(),this->height()));
//設(shè)置結(jié)束位置
animation->setEndValue(QRect(this->x(),this->y(),this->width(),this->height()));
//設(shè)置彈跳曲線
animation->setEasingCurve(QEasingCurve::OutBounce);
//執(zhí)行動畫
animation->start();
}
void MenuButton::zoom2()
{
//設(shè)置動畫時間間隔
animation->setDuration(200);
//設(shè)置起始位置
animation->setStartValue(QRect(this->x(),this->y(),this->width(),this->height()));
//設(shè)置結(jié)束位置
animation->setEndValue(QRect(this->x(),this->y()-10,this->width(),this->height()));
//設(shè)置彈跳曲線
animation->setEasingCurve(QEasingCurve::InElastic);
//執(zhí)行動畫
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:混分巨獸龍某某
* 郵箱:1178305328@qq.com
* 嵌入式技術(shù)交流群:958820627
*/
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
{
/* 布局初始化 */
layoutInit();
}
/* 析構(gòu)函數(shù) */
MainWindow::~MainWindow()
{
}
/* 屏幕布局初始化 */
void MainWindow::layoutInit()
{
/* 獲取屏幕的分辨率,Qt官方建議使用這
* 種方法獲取屏幕分辨率,防上多屏設(shè)備導(dǎo)致對應(yīng)不上
* 注意,這是獲取整個桌面系統(tǒng)的分辨率
*/
QList <QScreen *> list_screen = QGuiApplication::screens();
/* 如果是ARM平臺,直接設(shè)置大小為屏幕的大小 */
#if __arm__
/* 重設(shè)大小 */
this->resize(list_screen.at(0)->geometry().width(),
list_screen.at(0)->geometry().height());
#else
/* 否則則設(shè)置主窗體大小為800x480 */
this->resize(800,400);
#endif
/* 構(gòu)建布局標(biāo)題和背景色 */
this->setWindowTitle("智能車載系統(tǒng)");
this->setStyleSheet("background-color: rgb(240, 255, 255);"); //QT顏色標(biāo):http://t.csdn.cn/SF7Rc
//給進(jìn)程分配空間
my_pro = new QProcess(this);
//時間顯示label
QLabel *time_label = new QLabel(this);
time_label->setGeometry(10,20,240,100);//設(shè)置坐標(biāo)
//time_label->setStyleSheet("font-size:55px;color:black");//設(shè)置大小顏色
time_label->setFont(QFont("Helvetica", 72));
//time_label->ssetFont(QFont("Helvetica", 30)); // 設(shè)置字體為Arial,大小為16
//日期顯示label
QLabel *date_label = new QLabel(this);
date_label->setGeometry(200,100,240,100);//設(shè)置坐標(biāo)
date_label->setStyleSheet("font-size:25px;color:green");//設(shè)置大小顏色
//智能車載裝置
QFont font("Arial", 30, QFont::Normal);
QLabel *title_label = new QLabel(this);
title_label->setGeometry(40,180,260,50);//設(shè)置坐標(biāo)
title_label->setFont(font);
title_label->setText("智能車載終端");
m_pLCD = new QLCDNumber(this);
// 設(shè)置能顯示的位數(shù)
m_pLCD->setDigitCount(8);
m_pLCD->setGeometry(20,30,230,100);
// 設(shè)置顯示的模式為十進(jìn)制
m_pLCD->setMode(QLCDNumber::Dec);
// 設(shè)置樣式
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,[=](){
/* 獲取當(dāng)前日期 */
QDate date = QDate::currentDate();//獲取當(dāng)前日期
QString date_msg = QString("%1-%2-%3").arg(date.year()).arg(date.month()).arg(date.day());
date_label->setText(date_msg);
// 獲取系統(tǒng)當(dāng)前時間
QDateTime dateTime = QDateTime::currentDateTime();
// 顯示的內(nèi)容
m_pLCD->display(dateTime.toString("HH:mm:ss"));
/* 判斷進(jìn)程狀態(tài) */
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,[=](){
/* 設(shè)計(jì)動作圖標(biāo) */
music_button->zoom1();//彈跳
music_button->zoom2();
/* 延遲500ms后啟動播放器進(jìn)程 */
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(); //關(guān)閉定時器
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);
//硬件數(shù)據(jù)按下處理
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);
//進(jìn)程結(jié)束處理
connect(my_pro,&QProcess::stateChanged,[=](){
});
}
運(yùn)行結(jié)果:
四、項(xiàng)目效果
4.1 實(shí)戰(zhàn)視頻
IMX6ULL智能車載終端
4.2 作者有話
該項(xiàng)目為作者之前某成品級項(xiàng)目的簡化版本,考慮到教學(xué)博客篇幅有限。作者將當(dāng)初項(xiàng)目的 “雛形”分享給大家,該項(xiàng)目可以實(shí)現(xiàn)簡單的智能車載終端的多級功能切換!代碼的整體框架兼容性很高,讀者朋友可以根據(jù)在作者的代碼基礎(chǔ)上進(jìn)行增加或者修改自己的功能!
后續(xù)作者空閑之后會將當(dāng)初智能車載終端的大部分功能和代碼都教學(xué)與分享給大家!包括:頁面切換,地圖功能實(shí)現(xiàn),倒車影像設(shè)計(jì)與流程絲滑的圖標(biāo)切換等。感謝的讀取朋友可以期待一波,希望該博客讓大家有所收獲!
五、代碼開源
代碼地址:?基于IMX6ULL的智能車載終端項(xiàng)目代碼資源-CSDN文庫文章來源:http://www.zghlxwxcb.cn/news/detail-605006.html
如果積分不夠的朋友,點(diǎn)波關(guān)注,評論區(qū)留下郵箱,作者無償提供源碼和后續(xù)問題解答。求求啦關(guān)注一波吧?!?。?/strong>文章來源地址http://www.zghlxwxcb.cn/news/detail-605006.html
到了這里,關(guān)于基于IMX6ULL的智能車載終端項(xiàng)目(代碼開源)的文章就介紹完了。如果您還想了解更多內(nèi)容,請?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!