关闭

Code Project:创建一个媒体播放器

标签: qt音乐编程浏览器工作webkit
563人阅读 评论(0) 收藏 举报
分类:

无论从功能还是大小来讲,Amarok都是一款优秀的KDE音乐播放器。但它很难称作是一款快速点选式的音乐播放器,因为它要通过好几次点击和一些 仔细的GUI导航才能听到音乐收藏中的音乐,这将给我们的CPU和大脑带来一定负担。这里我们将会构建所能想到的最简单和最直观的音乐播放器,给用户提供 另一个选择。

从苹果的新款iPod Shuffle吸取一些灵感,只提供最基本的控制功能。一个按钮用于选择音乐,另一个按钮用于播放和暂停,还有一个按钮用于跳到下一段音乐。对于大多数用 户而言,这些控制功能已经足够。与像Amarok这样臃肿而琐碎的播放器相比,它简直令人耳目一新。

编译环境

在进行任何编程工作之前,必须满足大家的播放器的几个要求。第一个要求是Qt 4.5,需要安装它和与它相关的开发库。开发库包含了导入到我们自己项目中的头文件,这样我们就能在自己的应用程序之外使用Qt功能。

如果你的版本足够新,比如Ubuntu Jaunty,那么会发现Qt 4.5已经包含在内了。如果版本较旧,必须自己添加它。Nokia提供了易于安装的包,而且这些包可以与当前版本的Qt共存,或者也可以更新版本。

新手还需要一个工作开发环境。测试是否安装了工作开发环境的方法是,在命令行中输入“make”。如果提示无法找到该命令,则需要通过所使用版本的 包管理器来安装GNU系列的编译工具。这包括一些基本的编程工具,比如GCC编译器套件、“make”编译系统和GDB调试器。大多数其他的版本都应该包 含此编译环境的默认安装,如果没有也应该能够找到一个名称相似的元包。

Qt 4.5包括Creator IDE,但Kubuntu用户需要把它当作一个单独的包进行安装。因此在Linux和大多数其他操作系统上,当程序员试着猜测所使用的硬件和软件层时,音 频播放是一个问题多发区。幸运的是,Qt给出了我们所见过的最好的解决方案之一,即集成来自KDE的Phonon框架。Phonon位于我们所使用的任何 音频驱动器和路由层之上,并在编程和猜测配置复杂部分之间提供了一个接口。它让播放音频文件变得相对容易,而且是完全跨平台的。

你需要安装Phonon及其开发库。连同其他几个依赖项一起,它很可能还需要GStreamer,这是大多数Linux版本的后端选择。Phonon将直接与GStreamer对话,而我们只要与Phonon对话即可。

1(2)

* Qt Creator通常是标准Qt 4.5安装的一部分,但Ubuntu用户将需要手动抓取这个包。

起步

现在,如你已经安装了所有软件,接下来就可以正式开始了。运行Creator,方法是使用启动菜单或者在命令行上输入“qtcreator”。此应 用程序一开始会显示一个帮助性质的向导画面,在上面可以加载以前的项目或了解关于Qt的更多信息。要开始一个新项目,点击菜单File>New。

这将运行新建项目向导,而我们的首个任务就是选择要创建项目的种类。在“Projects”标题下找到“Qt4 GUI Application”,然后点击OK。现在,需要为项目输入一个名称,并选择创建项目目录的文件夹。

下一个窗口是模块选择页面,在这里可以选择要包含在项目内的其他组件。每个Qt项目都会使用的两个核心模块QtCore和QtGUI已经被选中,只 要选择要使用的任意非核心组件即可。例如,如果要开发浏览器,就需要包含WebKit模块。因为我们只需要与操作系统的音频层进行交互,所以只需要选择 Phonon。

选择Phonon后,最后一个页面将列出我们的项目中文件和类的默认名称,只要保持它们的默认值不变即可。点击“Finish”结束向导,创建文件夹和文件模板,所有这些都将自动被加载到Creator中。

如果你之前使用过任何形式的IDE,就不会对Qt Creator感到陌生。它有好几种不同的操作模式。一开始要花最多时间熟悉的是GUI设计器,点击以“ui”结尾的文件(User Interface设计器文件)时会自动运行它。这些文件之一已经添加到我们的项目中,在主显示区域左边的项目窗格中可以看到这个文件和四个其他文件。

其中两个文件的名称以“.cpp”结尾,用于保存我们的项目的源代码,但“main.cpp”实际上只能用于运行应用程序,因此我们能进行编辑的只 有“mainwindow.cpp”文件。这个文件有一个对应的头文件,用于描述我们在mainwinodw.cpp中使用的对象和开发文件。最后还有一 个“.pro”文件,实际上这个文件是禁止可见的,因为它是Qt和Creator用于编译项目的项目文件。

2(2)

* 如果你忘记在Creator的启动向导中包含所需模块,可以自己把它们添加到项目的“.pro”文件中。

GUI设计

在项目窗格中双击“ui”文件,嵌入的设计器视图就会出现。如果你过去曾经尝试过一些Qt编程,就会察觉布局与过去的Qt Designer应用程序有所不同,它已经更加无缝地嵌入到了Creator中。没有人曾经说过Designer使用方便,但它功能强大,而且在创建动态 和易扩展的Qt GUI应用程序方面比使用纯编程方法更加简便。

由于Creator中包含了Designer,现在我们可以在GUI修改和编程之间轻松来回切换,这可以显著提高开发速度。

这个应用程序只有一个十分简单的GUI。向下滚动可用小部件的列表,从Buttons区将三个“Push Buttons”拖动到灰色的应用程序画布上。或者,也可以先拖一个按钮,然后再复制/粘贴两个。这些按钮是应用程序所拥有的全部控件。一个按钮将用于选 择要播放的文件,另一个按钮将用于播放和暂停音乐,而最后一个按钮将用于跳到下一首音乐。这与Amarok的20多个按钮形成了鲜明对比。

现在, 来注释用户每个按钮的功能。为此,可以双击每个按钮并使用一个词来描述其功能,或者在按钮所在位置放置一个图标。如果要使用图标,选中按钮,然后在 Creator画面的左下区中找到值面板。此面板中保存了GUI中当前选中小部件的大多数属性。向下滚动,直到在列表中看到“icon”为止。

选择该项,然后点击右侧的“…”按钮,以打开一个文件请求器。现在可以为按钮选择并使用任意图像文件,但如果安装了标准的KDE,可以看一看 “/usr/share/icons/oxygen/32×32/actions”目录。此目录包含大多数常用功能的图标,“media-skip- forward”、“media-skip-backward”和“media-playback-start”特别适合我们要实现的功能。

现在,我们需要垂直排列这三个按钮,同时限制窗口的大小。按下左Ctrl键,然后分别选中每个按钮。它们现在应该都有突出显示的缩放边界。现在点击 工具栏中的“Lay Out Vertically”按钮(或者右键单击菜单)。这将把三个按钮绑定在一起,并由上至下依次排列。

现在点击应用程序背景,并选择“Layout in a Grid”。这将把三个按钮拉伸至充满所有可用空白,无论窗口大小如何。但我们还想让窗口变小。拖动背景左下方的锚点,直到创建出一个小矩形,或者使用属 性面板的“Geometry”区手动输入大小。最后我们获得了一个115像素宽和162像素高的窗口。也可以选择固定窗口的大小,方法是将 Horizontal和Vertical参数的最大值设为和最小尺寸相同的数字。

3(2)

* 使用Creator构建GUI需要一个熟悉的过程,但是基本上只要从左侧拖动组件,并将它们固定在布局引擎的合适位置上即可。

编写

Creator中的Designer视图还有一个好处,即不用编写一行代码便可创建大量编程逻辑。这要感谢Qt的Signals and Slots机制。它们基本上是一种远程过程调用,但由Qt自动处理。在web浏览器中,例如当用户点击刷新按钮时,浏览器接口可能“发出”一个信号以刷新 显示器。

在这个应用程序中,我们要发出三种信号,分别对应于我们的三个按钮——播放/暂停、快进和添加文件。我们需要编写函数来处理每种信号,但在 Designer中,我以把应用程序配置为当特定操作发生时调用特定函数。为此,需要切换到“Signals/Slots”编辑模式,方法是使用工具栏中 的图标或者按下F4键。

这种模式的原理是,首先选择要发出信号的小部件,然后将光标拖动到其槽将对信号做出反应的小部件。例如,通过web浏览器可以在刷新按钮和 WebKit浏览器小部件之间建立连接。然后就可以刷新显示器,同时无需编写一行代码。如果没有特定小部件接收信号,就像在我们的例子中一样,那么就将连 接拖动到窗口背景上。我们需要为每个按钮做这项工作。

首次将连接放到背景上时,会出现一个“Configure Connection”窗口。在右侧面板上点击“Edit”按钮。这将打开另一个窗口,其中列出了作为应用程序背景的MainWindowClass的信 号和槽。我们需要添加三个槽,以便接受三种不同的信号,而且这些槽将直接对应于我们将要在主应用程序中编写的函数。

按下“+”号,然后添加“playPause()”、“addFiles()”和“nextFile()”函数。点击OK,使用Edit Signals/Slots模式,将来自每个按钮的“clicked()”信号连接到我们刚刚创建的正确槽。例如对于“Add Files”按钮,需将“clicked()”信号连接到“addFiles()”函数。在编辑器窗口中可以看到所有连接,其中蓝色的直线和正方形用于指 示来自某些小部件的信号与各个槽的对应关系。这是我们所需要进行的GUI编辑的最后一部分,保存工作进展,让我们开始下一个步骤。

3(3)

* 了解信号和槽是Qt编程最重要的方面。

编码:mainwindow.h

我们已经为我们的应用程序创建了框架,现在只要添加功能即可。点击“mainwindow.h”,并在顶部添加如下代码行:

#include <QList>

#include <QFileDialog>
#include <QDesktopServices>
#include <Phonon>

以上代码的作用是,通过头文件导入我们要在代码中使用的Qt函数。现在我们需要添加我们的槽,它们在我们前面编辑过的ui文件中已经定义好了。在“public:”部分中的“~MainWindow();”行正下方,添加如下代码:

private
 slots:

void playPause( ) ;
void addFiles( ) ;
void nextFile( ) ;
void aboutToFinish( ) ;
void finished( ) ;

可以看到,这些槽对应于我们在用户界面中创建的名称,而且我们还添加了更多的槽来处理内部通信。最后,在“private”部分中添加如下变量,我们将在应用程序的主要逻辑中用到这些变量:

    QList<
Phonon::
MediaSource
>
 sources;

Phonon:: MediaObject * mediaObject;
Phonon:: AudioOutput * audioOutput;
Phonon:: MediaObject * metaInformationResolver;

编码:mainwindow.cpp

现在,我们需要做的第一件事情是初始化Phonon,并建立内部的信号和槽。这可以通过标准的初始化方法 MainWindow::MainWindow来完成。如果你看一看这个方法的内容,就会发现应用程序的GUI是由 “ui->setupUi(this);”行来运行的。这意味着,我们需要在这之前加入我们的预运行代码。我们将从设置Phonon开始:

     audioOutput =
 new
 Phonon::
AudioOutput
(
Phonon::
MusicCategory
, this
)
;

mediaObject = new Phonon:: MediaObject ( this ) ;
metaInformationResolver = new Phonon:: MediaObject ( this ) ;
Phonon:: createPath ( mediaObject, audioOutput) ;

在Phonon术语中,我们要创建的audioOutput对象叫做音频接收槽。它是直接与音频驱动器通信的层的组成部分,并充当 MediaObject的虚拟音频设备。MediaObject位于这一层的上层,增加了诸如暂停、播放和倒带之类的功能。顺便提一 句,MusicCategory不一定是必需的,但它可以对未来发展起到作用,比如可以根据正在收听的内容自动变化的KDE均衡器。

我们将使用“metaInformationResolver”来指向当前音频文件,而最后一行在接收槽和媒体对象之间建立了连接。Phonon使 用了一种叫做“graph”的框架,这意味着对象就像是一幅图上的节点,需要连接起来才能创建流向。这正是以上代码的最后一行的作用。

现在添加如下用于处理playPause()槽的新函数:

void
 MainWindow::
playPause
(
)

{
switch ( mediaObject- > state( ) ) {
case Phonon:: PlayingState :
mediaObject- > pause( ) ;
ui- > pushButtonPlay- > setChecked( false ) ;
break ;
case Phonon:: PausedState :
mediaObject- > play( ) ;
break ;
case Phonon:: StoppedState :
mediaObject- > play( ) ;
break ;
case Phonon:: LoadingState :
ui- > pushButtonPlay- > setChecked( false ) ;
break ;
}
}

以上代码应该不难理解。我们使用一条Case语句检查播放的当前状态。Phonon通过MediaObject为我们提供了这种功能。它实际上就是 一个整数,但是每个值代表着特定的状态。例如,如果应用程序是首次启动,播放列表中尚未加入任何文件,就会返回LoadingState值。播放和暂停由 媒体对象进行处理,在“mediaObject->pause();”命令之后将自动从当前位置继续。

4
* 在Creator中输入代码时,可使用自动完成功能找到所需函数。

我们还尝试给用户提供一些关于当前播放状态的反馈。如果音乐正在播放,我们会让“Play”按钮凹下去,使其看起来好像是被按下一样。如果音乐处于 任意其他状态,我们会让“Play”按钮恢复原样,使其看起来处于停止状态。为了实现这一点,需要将播放按钮取名为“pushButtonPlay”。可 以在Designer视图中修改它的名称,方法是选择该按钮并修改“objectName”值。

void
 MainWindow::
addFiles
(
)

{
QStringList files = QFileDialog:: getOpenFileNames ( this , tr( "Select Music Files" ) ,
QDesktopServices:: storageLocation ( QDesktopServices:: MusicLocation ) ) ;
 
ui- > pushButtonPlay- > setChecked( false ) ;
if ( files.isEmpty ( ) )
return ;
int index = sources.size ( ) ;
foreach ( QString string, files) {
Phonon:: MediaSource source( string) ;
sources.append ( source) ;
}
if ( ! sources.isEmpty ( ) ) {
metaInformationResolver- > setCurrentSource( sources.at ( index) ) ;
mediaObject- > setCurrentSource( metaInformationResolver- > currentSource( ) ) ;
 
}
}

现在,让我们开始实现添加文件的部分。创建文件请求器几乎是自动的,我们可以把结果放到一个字符串列表中。 “QdesktopServices::MusicLocation”返回一个通常用于保存音乐文件的特定操作系统位置,而我们使用它作为请求器的起始位 置。接下来,我们将把已经选择的每个音乐文件添加到我们早先创建的“metaInformationResolver”中,并使用它告诉 mediaObject接下来播放哪个文件。

在所有这些工作间隙,我们进行一点清理工作,以确保队列中有文件,而且当处于播放状态时出现播放按钮。添加文件将自动停止播放。

void
 MainWindow::
nextFile
(
)

{
 
int index = sources.indexOf ( mediaObject- > currentSource( ) ) + 1 ;
 
if ( sources.size ( ) > index) {
mediaObject- > stop( ) ;
mediaObject- > setCurrentSource( sources.at ( index) ) ;
mediaObject- > play( ) ;
}
}
 
void MainWindow:: aboutToFinish ( )
{
 
int index = sources.indexOf ( mediaObject- > currentSource( ) ) + 1 ;
 
if ( sources.size ( ) > index) {
mediaObject- > enqueue( sources.at ( index) ) ;
} else {
ui- > pushButtonPlay- > setChecked( false ) ;
}
 
}
 
void MainWindow:: finished ( )
{
 
ui- > pushButtonPlay- > setChecked( false ) ;
 
}

下载此项目的代码

5

它看起来可能不起眼,但我们的音乐播放应用程序具备了一款音乐播放器所需要的大部分功能。

原文链接:http://www.tuxradar.com/content/code-project-create-media-player
翻译CSDN:http://qt.csdn.net/articles.aspx?pointid=174

0
0

查看评论
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
    个人资料
    • 访问:227275次
    • 积分:2889
    • 等级:
    • 排名:第12349名
    • 原创:44篇
    • 转载:82篇
    • 译文:0篇
    • 评论:50条
    文章分类
    最新评论