張增虎,李 博,郭 銳
(中北大學電子與計算機科學技術學院,山西太原030051)
隨著信息技術的發展,嵌入式設備越來越多地走進人們的日常生活。MPlayer是一款可在各主流操作系統上使用的多媒體播放器,也支持嵌入式Linux平臺。它是一款功能強大的多媒體播放器,支持所有主流的多媒體文件格式。美中不足的是,它的操作是基于命令行界面的,在嵌入式Linux平臺下還沒有一款圖形用戶界面,操作起來十分不方便。針對這個問題,決定為其開發一款基于Qt框架的圖形用戶界面[1-2]。Qt是一個跨平臺的C++應用程序開發框架,廣泛用于開發GUI程序。在基于S3C6410微處理器和嵌入式Linux操作系統的軟硬件平臺上設計和實現了一款GUI軟件,該GUI軟件可以實現對MPlayer常用的控制功能,基本滿足了使用需要。本文即闡述了這款圖形用戶界面的設計和實現過程。
Qt的圖形用戶界面的基礎是QWidget。Qt中所有類型的GUI組件如按鈕、標簽、滑動條等都派生自QWidget,而QWidget本身則為QObject的子類。Widget負責接收鼠標、鍵盤、觸摸屏和來自窗口系統的其他事件,并在屏幕上描繪了自身顯示。每一個GUI組件都是一個Widget,Widget還可以作為容器,在其內包含其他Widget。
QWidget不是一個抽象類型,并且可以被放置在一個已存在的用戶界面中。若是Widget沒有指定父Widget,當它顯示時就是一個獨立的視窗或是一個頂層Widget。Qt提供一種托管機制,當Widget于創建時指定父對象,就可把自己的生命周期交給上層對象管理,當上層對象被釋放時,自己也被釋放,確保對象不再使用時都會被刪除。
信號與槽機制[3]是Qt的核心機制,同時也是Qt特有的機制。Qt利用信號與槽(signals/slots)機制取代傳統的callback來進行對象之間的溝通。當操作事件發生的時候,對象會發提交一個信號(signal);而槽(slot)則是一個函數接受特定信號并且運行槽本身設置的動作。信號與槽之間,則通過QObject的靜態方法connect來鏈接。信號在任何運行點上皆可發射,甚至可以在槽里再發射另一個信號,信號與槽的鏈接不限定為一對一的鏈接,一個信號可以鏈接到多個槽或多個信號鏈接到同一個槽,甚至信號也可連接到信號。
布局管理類型[4]用于描述一個應用程序的用戶界面中的Widget是如何放置的。當視窗縮放時,布局管理器會自動調整Widget的大小、位置或是字號,確保它們相對的排列和用戶界面整體仍然保有可用性。
Qt自帶的布局管理類型有:QHBoxLayout、QVBoxLayout和QGridLayout。這些類型繼承自QLayout,但QLayout非繼承自QWidget而是直接源于QObject。它們負責Widget的幾何管理。想要創建更復雜的布局管理,可以繼承QLayout來自定義布局管理類型。
QHBoxLayout:配置Widget成橫向一列;
QVBoxLayout:配置Widget成垂直一行;
QGridLayout:配置Widget在平面網格。
本文的研究目的是設計實現一款嵌入式Linux平臺[5-6]下的MPlayer圖形用戶界面,需要主機PC平臺和嵌入式開發板平臺。主機平臺上安裝的操作系統是Ubuntu 12.04,這是一款主流的GNU/Linux操作系統。開發軟件為Qt Creator,它是Qt官方提供的一款輕量級的Qt/C++IDE開發環境。嵌入式開發板平臺采用Arm-Linux體系結構,硬件平臺以S3C6410處理器[2]為核心,操作系統版本為Linux-3.0.1。要想在嵌入式平臺上運行Qt程序,必須將Qt庫移植到嵌入式平臺中。在Qt官網上下載Qt libraries源碼包,按如下配置選項對Qt Libraries進行配置:

配置完成后進行編譯,這樣編譯出來的就是Arm-Linux平臺下支持觸摸屏的Qt運行庫。將編譯完成后的Qt庫放入嵌入式開發板的文件系統中,并將Qt的環境變量添加到系統環境變量中。至此,開發平臺搭建成功。
GUI程序啟動后顯示主界面,主界面按功能分為兩個部分:播放顯示區和播放控制區,其中播放顯示區占了屏幕的絕大部分。播放控制區內分布著時間顯示標簽、播放進度條、音量調節條和所有的播放控制按鈕。主界面啟動后,當按下按鈕或拖動滑動條時,就會實現相應的功能。
主界面類mainWidget繼承自QWidget,利用構造函數對類對象進行初始化。主界面的實現原理是在一個父Widget上添加按鈕、標簽、滑動條等部件作為子Widget。類mainWidget的構造函數部分代碼如下:
mainWidget::mainWidget(QWidget*parent)
:QWidget(parent)
{
QWidget::setGeometry(QRect(0,0,800,480));
//設置主界面大小;
……
paceSlider=new QSlider(this);
//添加播放進度條;
paceSlider- > setGeometry(QRect(42,450,716,5));
//設置播放進度條的位置和大小;
paceSlider- >setOrientation(Qt::Horizontal);
//設置進度條為水平方向;
connect(paceSlider,SIGNAL(sliderReleased()),this,SLOT(pacesliderreleased()));
//關聯進度條拖動釋放信號和相應槽函數;
…...
playButton=new QPushButton("",this);
//新建開始按鈕;playButton - > setGeometry(QRect(42,455,25,25));
//設置開始按鈕的位置和大小;
playButton->setIcon(icon_play);
//設置開始按鈕的圖標;
connect(playButton,SIGNAL(clicked()),this,SLOT(play()));
//關聯開始按鈕按下信號和相應槽函數;
……
}
播放開始前必須先選擇播放文件。當還沒有選擇好播放文件時,按下開始按鈕,會打開選擇文件對話框。選擇文件對話框繼承自QDialog,對話框上的主要部件為播放文件列表、“添加”和“移除”按鈕。點擊“添加”按鈕會進入文件目錄選擇文件,選中的文件會添加到播放文件列表中,點擊“移除”按鈕會移除播放列表中被選中的文件。雙擊播放列表中的一個文件或選中一個文件后按下對話框上的播放按鈕,就會開始播放。選擇文件對話框如圖1所示。

圖1 選擇文件對話框示意圖(截圖)
播放文件時,圖形用戶界面程序通過新建一個QProcess進程來調用作為后端的MPlayer進行播放。QProcess類可以啟動一個外部程序并與之進行通信。MPlayer[7]啟動后,QProcess進程通過標準輸入輸出與之進行通信交互。播放文件功能的實現代碼為
QString program;//聲明一個外部程序;
QStringList args;//聲明程序的運行參數;
playProcess=new QProcess(this);
//新建一個QProcess進程來調用MPlayer;
program="/usr/local/mplayer/bin/mplayer";
//指定MPlayer程序路徑;
args<<"-slave"<<"-quiet";
//使MPlayer運行于slave模式;
args<<"-x"<<"800"<<"-y"<<"450";
//指定MPlayer播放窗口的大小;
args< <"-vo"< <"fbdev";
//指定視頻輸出驅動為FrameBuffer;
args< <fileName;
//指定播放文件;
playProcess- >start(program,args);
//啟動MPlayer開始播放;
在播放進行中,QProcess可以通過標準輸入對MPlayer發出指令,也可以通過標準輸出獲取播放文件的信息。用戶可以通過圖形用戶界面上的各個功能按鈕實現暫停/播放、停止、快進(退)、上(下)一個、播放列表的顯示(隱藏)和音量調節等功能。按鈕控制的實現原理是信號與槽機制,點擊按鈕會觸發此按鈕的clicked()信號,clicked()信號被觸發后,與之鏈接的槽函數會被執行,只要在槽函數中加入相應的功能代碼,就會實現相應的控制功能。下面以快進功能為例,介紹按鈕功能的實現方法。
在定義快進按鈕的時候鏈接快進按鈕的clicked()信號和快進功能槽函數forward():
connect(forwardButton,SIGNAL(clicked()),this,SLOT(forward()));
MPlayer快進10 s的命令為“seek 10 0”,要想實現快進10 s功能,就要在槽函數forward()中通過QProcess將這條命令發送給MPlayer。QProcess通過write()函數在標準輸入發送命令。forward()函數源碼如下:
void mainWidget::forward()
{
playProcess- >write("seek 10 0n");
}
GUI程序不僅能通過QProcess向MPlayer寫入命令,還能通過QProcess獲取播放文件的媒體信息。在創建播放進程playProcess的同時,將playProcess的readyRead-StandardOutput()信號與槽函數dataRecieve()鏈接。play-Process進程通過標準輸入向MPlayer發送獲取媒體信息的命令,MPlayer接收命令后會將媒體信息返回到標準輸出中,當標準輸出中的數據信息可讀時,會觸發playProcess的readyReadStandardOutput()信號,與其關聯的槽函數dataRecieve()會被執行。dataRecieve()讀取標準輸出中的數據信息后,經過轉化顯示在GUI界面上。利用這個原理可以實現當前播放時間和文件時間長度的顯示。
獲取文件時長的命令為“get_time_length”,當play-Process啟動MPlayer以后,隨即通過playProcess->write("get_time_lengthn")指令向MPlayer發送命令獲取時長。MPlayer返回的時長信息以“ANS_LENGTH”開頭,隨后為以秒為單位的時間數據。槽函數dataRecieve()讀取信息后會把以秒為單位的時間數據轉化成“分:秒”的形式顯示在GUI界面上。
當前時間的顯示需要每秒更新1次。通過在MPlayer啟動時建立1個QTimer類定時器,定時時間為1 s。每隔1 s定時器就會觸發一次時間截止信號timeout(),與timeout()信號鏈接的槽函數timeok()會讓playProcess發送一條獲取當前時間的指令“get_time_pos”給MPlayer,返回的當前時間以“ANS_TIME_POSITION”開頭。這樣,dataRecieve()就能每隔1秒更新1次GUI界面上當前時間標簽。
首先將文泉驛字體wenquanyi_120_50.qpf放到開發板上Qt目錄中的lib/fonts文件夾下,“120”表示是12號字體,“50”表示細體。有了字體文件后,就可以在GUI界面的主程序中選擇使用中文字體,其實現代碼為:
QApplication app(argc,argv);//聲明程序
QFont font0;//聲明一個字體font0
font.setFamily(("wenquanyi"));//設置font0字體家族為文泉驛
font.setPointSize(12);//設置font0字體字號為12
font.setBold(false);
app.setFont(font);//設置程序所用字體為font0
全部程序編寫完成以后,首先進行qmake讓其自動生成Makefile文件,再進行make就生成了開發板上的可執行文件,將可執行文件和圖標文件一起放到開發板的相應文件夾下。經過在開發板平臺上的運行測試,結果表明,該圖形用戶界面在嵌入式Linux平臺上能穩定運行,各項設計的功能都能實現且性能良好。運行測試效果圖如圖2所示。

圖2 播放文件示意圖(截圖)
本文設計并實現了一款嵌入式Linux平臺下的MPlayer圖形用戶界面,該圖形用戶界面基于Qt框架開發,利用了MPlayer的slave模式并通過QProcess進程控制MPlayer,實現了選擇文件、播放控制等常用功能,成功地解決了MPlayer在嵌入式Linux平臺下沒有圖形用戶界面的問題,具有很高的實用性。經過測試,該界面程序運行穩定、操作良好,滿足了設計要求。
[1]于幫偉,鄧華秋.基于Qt/Embedded的嵌入式數字監控系統控制界面的實現[J].電視技術,2011,35(24):25-28.
[2]雷豐中,劉鵬.基于S3C6410的多媒體系統設計[J].電視技術,2011,35(11):24-27.
[3] Qt Project Group.Qt開發官方參考文檔[EB/OL].[2013-05-25].http://qt-project.org/doc/qt-4.8/.
[4]蔡志明,盧傳富,李立夏.精通Qt4編程[M].北京:電子工業出版社,2011.
[5]吳燕燕.基于ARM9平臺上Qt/Embedded的移植與開發[J].液晶與顯示,2013,28(2):261-264.
[6]閔華松,王娜,譚金鑫.嵌入式多媒體播放器通用控制系統設計與實現[J].計算機工程與科學,2009,31(2):153-155.
[7] MPlayer-電影播放器[EB/OL].[2013-05-25].http://www.mplayerhq.hu.