# 【Qt編程】基于Qt的詞典開發系列調用講述人
我們知道,win7系統自帶有講述人,即可以機器讀出當前內容,具體可以將電腦鎖定,然后點擊左下角的按鈕即可。之前在用Matlab寫掃雷游戲的時候,也曾經調用過講述人來進行游戲的語音提示。**具體的Matlab腳本文件如下:**
` sp=actxserver('SAPI.SpVoice');sp.Speak('你好,歡迎來到西安電子科技大學!Hello,Welcome?to?XD?University!')??`
Qt調用講述人,需要使用專門的類,具體可以參考[http://lynxline.com/qtspeech-say-hello-world ?](http://lynxline.com/qtspeech-say-hello-world%E4%B8%80%E6%96%87%EF%BC%8C%E6%96%87%E4%B8%AD%E5%A4%A7%E8%87%B4%E4%BB%8B%E7%BB%8D%E4%BA%86%E8%AF%A5%E7%B1%BB%E7%9A%84%E4%BD%BF%E7%94%A8%E6%96%B9%E6%B3%95%E3%80%82%E4%B8%8B%E9%9D%A2%E6%88%91%E5%B0%B1%E9%80%9A%E8%BF%87%E4%BD%BF%E7%94%A8%E8%AF%A5%E7%B1%BB%E6%9D%A5%E5%AE%9E%E7%8E%B0%E8%AE%B2%E8%BF%B0%E4%BA%BA%E7%9A%84%E8%B0%83%E7%94%A8%E3%80%82)??一文,文中大致介紹了該類的使用方法。下面我就通過使用該類來實現講述人的調用。
首先建立一個dialog類型的gui項目,將上面所說的類QtSpeech類的頭文件speech.h和源文件speech.cpp添加到工程中,**這樣項目中就有5個文件**:dialog.h、speech.h、main.cpp、dialog.cpp、speech.cpp。當然還有界面文件dialog.ui。在界面文件中**添加QTextEdit控件**用于輸入你要讀取的文字,然后在其槽函數中添加QtSpeech的發音功能,**添加QPushButton控件**來控制發音。**具體的各個文件源代碼如下:**
**1、dialog.h**
~~~
#ifndef DIALOG_H
#define DIALOG_H
#include <QDialog>
#include"speech.h"
namespace Ui {
class Dialog;
}
class Dialog : public QDialog
{
Q_OBJECT
public:
explicit Dialog(QWidget *parent = 0);
~Dialog();
private slots:
void on_pushButton_clicked();
private:
Ui::Dialog *ui;
};
#endif // DIALOG_H
~~~
**2、speech.h**
~~~
#ifndef SPEECH_H
#define SPEECH_H
#include <QObject>
class QtSpeech : public QObject {
Q_OBJECT
public:
// 處理異常情況
struct Error { QString msg; Error(QString s):msg(s) {} };
struct InitError : Error { InitError(QString s):Error(s) {} };
struct LogicError : Error { LogicError(QString s):Error(s) {} };
struct CloseError : Error { CloseError(QString s):Error(s) {} };
//定義數據類型
struct VoiceName { QString id; QString name; };
typedef QList<VoiceName> VoiceNames;
//定義構造函數
QtSpeech(QObject * parent);
QtSpeech(VoiceName n = VoiceName(), QObject * parent =0L);
virtual ~QtSpeech();
const VoiceName & name() const; //要讀的內容
static VoiceNames voices(); //要讀的內容
void say(QString) const; //同步發音
void tell(QString) const; //異步發音
void tell(QString, QObject * obj, const char * slot) const; //發音結束時,有停頓
/*******************/
void pause(void) const;//暫停
void resume(void) const;//從暫停中恢復
void stop(void) const;//停止發音
/******************/
signals:
void finished();
protected:
virtual void timerEvent(QTimerEvent *);
private:
class Private;
Private * d;
};
//}
#endif // SPEECH_H
~~~
**3、main.cpp**
~~~
#include <QApplication>
#include"dialog.h"
int main(int argc, char *argv[]){
QApplication app(argc, argv);
Dialog dlg;
dlg.show();
return app.exec();
}
~~~
**4、dialog.cpp**
~~~
#include "dialog.h"
#include "ui_dialog.h"
Dialog::Dialog(QWidget *parent) :
QDialog(parent),
ui(new Ui::Dialog)
{
ui->setupUi(this);
}
Dialog::~Dialog()
{
delete ui;
}
void Dialog::on_pushButton_clicked()
{
QtSpeech *speaker = new QtSpeech(this);
speaker->tell(ui->textEdit->toPlainText(),speaker,SLOT(onSpeechFinished()));
// speaker.stop();
}
~~~
**5、speech.cpp**
~~~
#include "speech.h"
#include <QString>
#include <QPointer>
#include <QList>
#include <QTimerEvent>
#undef UNICODE
#include <sapi.h>
#include <sphelper.h>
#include <comdef.h>
#define UNICODE
#include <windows.h>
#include <windowsx.h>
#include <commctrl.h>
// some defines for throwing exceptions
#define Where QString("%1:%2:").arg(__FILE__).arg(__LINE__)
#define SysCall(x,e) {\
HRESULT hr = x;\
if (FAILED(hr)) {\
QString msg = #e;\
msg += ":"+QString(__FILE__);\
msg += ":"+QString::number(__LINE__)+":"+#x+":";\
msg += _com_error(hr).ErrorMessage();\
throw e(msg);\
}\
}
// internal data
class QtSpeech::Private {
public:
Private()
:onFinishSlot(0L),waitingFinish(false) {}
VoiceName name;
static const QString VoiceId;
typedef QPointer<QtSpeech> Ptr;
static QList<Ptr> ptrs;
CComPtr<ISpVoice> voice;
const char * onFinishSlot;
QPointer<QObject> onFinishObj;
bool waitingFinish;
class WCHAR_Holder {
public:
WCHAR * w;
WCHAR_Holder(QString s)
:w(0) {
w = new WCHAR[s.length()+1];
s.toWCharArray(w);
w[s.length()] =0;
}
~WCHAR_Holder() { delete[] w; }
};
};
const QString QtSpeech::Private::VoiceId = QString("win:%1");
QList<QtSpeech::Private::Ptr> QtSpeech::Private::ptrs = QList<QtSpeech::Private::Ptr>();
//類的定義
QtSpeech::QtSpeech(QObject * parent)
:QObject(parent), d(new Private)
{
CoInitialize(NULL);
SysCall( d->voice.CoCreateInstance( CLSID_SpVoice ), InitError);
VoiceName n;
WCHAR * w_id = 0L;
WCHAR * w_name = 0L;
CComPtr<ISpObjectToken> voice;
SysCall( d->voice->GetVoice(&voice), InitError);
SysCall( SpGetDescription(voice, &w_name), InitError);
SysCall( voice->GetId(&w_id), InitError);
n.name = QString::fromWCharArray(w_name);
n.id = QString::fromWCharArray(w_id);
voice.Release();
if (n.id.isEmpty())
throw InitError(Where+"No default voice in system");
d->name = n;
d->ptrs << this;
}
QtSpeech::QtSpeech(VoiceName n, QObject * parent)
:QObject(parent), d(new Private)
{
ULONG count = 0;
CComPtr<IEnumSpObjectTokens> voices;
CoInitialize(NULL);
SysCall( d->voice.CoCreateInstance( CLSID_SpVoice ), InitError);
if (n.id.isEmpty()) {
WCHAR * w_id = 0L;
WCHAR * w_name = 0L;
CComPtr<ISpObjectToken> voice;
SysCall( d->voice->GetVoice(&voice), InitError);
SysCall( SpGetDescription(voice, &w_name), InitError);
SysCall( voice->GetId(&w_id), InitError);
n.name = QString::fromWCharArray(w_name);
n.id = QString::fromWCharArray(w_id);
voice.Release();
}
else {
SysCall( SpEnumTokens(SPCAT_VOICES, NULL, NULL, &voices), InitError);
SysCall( voices->GetCount(&count), InitError);
for (int i =0; i< count; ++i) {
WCHAR * w_id = 0L;
CComPtr<ISpObjectToken> voice;
SysCall( voices->Next( 1, &voice, NULL ), InitError);
SysCall( voice->GetId(&w_id), InitError);
QString id = QString::fromWCharArray(w_id);
if (id == n.id) d->voice->SetVoice(voice);
voice.Release();
}
}
if (n.id.isEmpty())
throw InitError(Where+"No default voice in system");
d->name = n;
d->ptrs << this;
}
QtSpeech::~QtSpeech()
{
d->ptrs.removeAll(this);
delete d;
}
const QtSpeech::VoiceName & QtSpeech::name() const {
return d->name;
}
QtSpeech::VoiceNames QtSpeech::voices()
{
VoiceNames vs;
ULONG count = 0;
CComPtr<IEnumSpObjectTokens> voices;
CoInitialize(NULL);
SysCall( SpEnumTokens(SPCAT_VOICES, NULL, NULL, &voices), LogicError);
SysCall( voices->GetCount(&count), LogicError);
for(int i=0; i< count; ++i) {
WCHAR * w_id = 0L;
WCHAR * w_name = 0L;
CComPtr<ISpObjectToken> voice;
SysCall( voices->Next( 1, &voice, NULL ), LogicError);
SysCall( SpGetDescription(voice, &w_name), LogicError);
SysCall( voice->GetId(&w_id), LogicError);
QString id = QString::fromWCharArray(w_id);
QString name = QString::fromWCharArray(w_name);
VoiceName n = { id, name };
vs << n;
voice.Release();
}
return vs;
}
void QtSpeech::tell(QString text) const {
tell(text, 0L,0L);
}
void QtSpeech::tell(QString text, QObject * obj, const char * slot) const
{
if (d->waitingFinish)
throw LogicError(Where+"Already waiting to finish speech");
d->onFinishObj = obj;
d->onFinishSlot = slot;
if (obj && slot)
connect(const_cast<QtSpeech *>(this), SIGNAL(finished()), obj, slot);
d->waitingFinish = true;
const_cast<QtSpeech *>(this)->startTimer(100);
Private::WCHAR_Holder w_text(text);
SysCall( d->voice->Speak( w_text.w, SPF_ASYNC | SPF_IS_NOT_XML, 0), LogicError);
}
void QtSpeech::say(QString text) const
{
Private::WCHAR_Holder w_text(text);
SysCall( d->voice->Speak( w_text.w, SPF_IS_NOT_XML, 0), LogicError);
}
void QtSpeech::timerEvent(QTimerEvent * te)
{
QObject::timerEvent(te);
if (d->waitingFinish) {
SPVOICESTATUS es;
d->voice->GetStatus( &es, NULL );
if (es.dwRunningState == SPRS_DONE) {
d->waitingFinish = false;
killTimer(te->timerId());
finished();
}
}
}
/************************/
void QtSpeech::pause(void) const{//暫停
SysCall( d->voice->Pause(), LogicError);
}
void QtSpeech::resume() const{//恢復
SysCall(d->voice->Resume(), LogicError);
}
void QtSpeech::stop() const{//停止
SysCall(d->voice->Speak(NULL, SPF_PURGEBEFORESPEAK, 0), LogicError)
}
/***************************/
//}
~~~
**程序結果如下:**

- 前言
- <一>--詞典框架設計及成品展示
- <二>--本地詞典的設計
- <三>--開始菜單的設計
- <四>--無邊框窗口的縮放與拖動
- <五>--無邊框窗口的拖動
- <六>--界面美化設計
- <七>--調用網絡API
- <八>--用戶登錄及API調用的實現
- <九>--JSON數據解析
- <十>--國際音標的顯示
- <十一>系統托盤的顯示
- <十二>調用講述人
- <十三>音頻播放
- <十四>自動補全功能
- <十五>html特殊字符及正則表達式
- 后序