后序
最后更新于:2022-04-01 07:11:43
# 【Qt编程】基于Qt的词典开发系列--后序
从去年八月份到现在,总算完成了词典的编写以及相关技术文档的编辑工作。从整个过程来说,文档的编写比程序的实现耗费的时间更多。[基于Qt的词典开发系列文章](http://blog.csdn.net/column/details/tengwei.html),大致包含了在编写词典软件过程中遇到的技术重点与难点。每篇文章都完成了一个小的功能,所给的代码都基本上是可以独立运行的。本系列文章对于想要自己动手完成词典软件的程序员来说具有很好的参考价值,对于想要编写其它软件的人来说也具有参考意义。
## 词典软件制作的初衷
在[2013的年终总结](http://blog.csdn.net/tengweitw/article/details/17880055)中,我提过想要学习一门界面编程语言,后来就选中了Qt。于是在2014年上半年,陆陆续续学了一些,那时候就想要做个东西出来,来巩固学习的东西,当时还不知道做个什么东西。有一次,我用有道词典时,就想到做一个类似的词典软件。当然了,有的人可能会说,不要重复造轮子,即使你写了一个词典软件,也肯定没有有道词典好。好吧,我承认我写的软件没有有道词典好,毕竟人家是一个专业的团队,而我就是一个刚入手没几个月的菜鸟。我做词典的目的也并不是期望大家都能拿来用,只是用作练练手。当然,也不能把自己的作品说得一文不值,毕竟自己也花心血来完成的,所以我也得强行说说我做的词典的优点。
## 词典软件的优点
对比于有道词典,我所做的词典软件的优点如下:
1、没有广告;2、自带8种本地词典;3、可以本地发音;4、换肤功能;5、自动补全;6、单词本也是扁平化(有道词典的单词本不是,不知道为啥还不改正)
对于缺点来说,还比较多,由于没有经过测试,因此还存在一些隐藏的bug,不过对于基本的使用来说影响不大。**大致的优缺点在软件的帮助菜单中已经说明:**
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-01-15_5698c1ca0f650.jpg)
## 词典软件的技术文档
在词典软件完成半年之后,也就是研一下学期初,我开始动笔写关于词典制作过程中的技术要点。由于时间有限,还得给导师干项目,所以直到今天才得以完成所有的技术文章的撰写。**本系列文章共有16篇**,其中第一篇主要介绍了词典的整体框架与功能,分别描述了各个源文件的功能,而最后一篇(即本文)想当于是这一系列文章的后序。
<十五>html特殊字符及正则表达式
最后更新于:2022-04-01 07:11:41
# 【Qt编程】基于Qt的词典开发系列html特殊字符及正则表达式
**1、html特殊字符的显示**
我们知道html语言和C语言一样也有一些特殊字符,它们是不能正常显示的,必须经过转义,在网上可以查到如何显示这些字符,如下图所示:
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-01-15_5698c1c994f19.jpg)
上图给了最常用的特殊字符的显示,下面我们来实验一下:
首先在记事本中写入一句:
~~~
<font color=blue>程序例</font>#include<stdio.h>
~~~
**然后就文本后缀名改为.html后**,用浏览器打开,显示如下:
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-01-15_5698c1c9a97ad.jpg)
我们从上图可以发现include后面没有显示,但是当我们在文本中写入:
~~~
<font color=blue>程序例</font>#include<stdio.h>
或
<font color=blue>程序例</font>#include<stdio.h>
~~~
时就能正常显示了。显示结果如下图:
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-01-15_5698c1c9bb474.jpg)
我们知道Qt能够支持html语言,但是我在编写可以查询c语言函数(肯定有#include后面**。这样就不会当成是特殊字符了,就可以正常显示了。半角和全角的转换只需要点击下图中的第3个按钮:
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-01-15_5698c1c9d359d.jpg)
虽然说,使用全角后看起来不是很协调,但还看得过去,如果读者有什么好的方法,请不吝赐教。
**2、正则表达式**
接着上面的问题继续说,比如我有一个C语言函数词典库,我取其中一段来举例:
~~~
<font color=blue>程序例</font>:<br>#include <span style="color:#ff0000;"><</span>stdio.h <span style="color:#ff0000;">></span><br>#include <span style="color:#ff0000;"><</span> stdlib.h <span style="color:#ff0000;">></span><br>int main(void)<br>
~~~
因为我们需要将上述半角的**,**即字符串中为红色的部分**。当然当文件比较小的时候,可以手动查找替换,但是由于词典一半都有十几M,这时就必须使用正则表达式了。
在Qt中,使用了QRegExp来支持正则表达式。关于正则表达式的语法,网上都有很多教程,我就不细说了。从上面的实例字符串的内容,我们知道包含了几个头文件,所以我们在匹配时,要分别匹配各个头文件两边的半角。我们知道,在正则表达式中,非贪婪模式是由'?'来指定的,但是对于QRegExp类来说,?是不合法的。**在QRegExp中,要使用setMinimal函数来指定贪婪模式**。**setMinimal(true)就是非贪婪模式,setMinimal(false)就是贪婪模式。**
通过上面两部分的讲解,就可以完成C语言函数库词典的显示了。下面通过代码实现,新建Qt Gui应用程序,选择基类为QWidget,其中只需要修改widget.cpp里的内容即可(需要在widget.ui界面中,添加两个label,分别命名为label和label1)。
widget.cpp文件:
~~~
#include "widget.h"
#include "ui_widget.h"
#include<QString>
#include<QRegExp>
#include<QDebug>
#include<QLabel>
Widget::Widget(QWidget *parent) :
QWidget(parent),
ui(new Ui::Widget)
{
ui->setupUi(this);
QString str="<font color=blue>程序例</font>:<br>#include <stdio.h ><br>#include < stdlib.h ><br>int main(void)<br>";
ui->label->setText(str); //由于Qt也能识别html格式,而< >是html的特殊字符,因此不处理的话,就会出现显示出错
QRegExp rx("#include (<(.*.h )>)");//正则表达式里的括号是为了获取匹配的内容
rx.setMinimal(true);//非贪婪模式
int pos=0;//从字符串的开始部分
for(int i=0;i<str.size();i++)
{
pos=rx.indexIn(str,0);//得到匹配字符串的位置
while(pos!=-1)//匹配成功
{
str.replace(rx.cap(1),"<"+rx.cap(2)+">");//这里是进行替换,如果不明白rx.cap是什么,可以qDebug显示内容
pos=rx.indexIn(str,pos+rx.matchedLength());//从当前位置,继续往后进行匹配
}
}
ui->label1->setText(str);
}
Widget::~Widget()
{
delete ui;
}
~~~
**程序运行结果如下:**
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-01-15_5698c1c9e3b08.jpg)
**下面放一张查单词软件用到的特殊字符显示和使用正则表达式后的截图:**
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-01-15_5698c1c3b87cf.jpg)
注意:我使用的平台为QT5,如果转移到Qt4上有错的话,可以反向参考[http://qt-project.org/wiki/Transition_from_Qt_4.x_to_Qt5](http://qt-project.org/wiki/Transition_from_Qt_4.x_to_Qt5)
<十四>自动补全功能
最后更新于:2022-04-01 07:11:39
# 【Qt编程】基于Qt的词典开发系列自动补全功能
最近写了一个查单词的类似有道词典的软件,里面就有一个自动补全功能(即当你输入一个字母时,就会出现几个候选项)。这个自动补全功能十分常见,百度搜索关键词时就会出现。不过它们这些补全功能都是与你输入的进行首字匹配,有时也会不方便。例如,如果我输入一个“好”,如果是首字匹配的话会出现下图:
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-01-15_5698c1c956712.jpg)
如果是句中匹配的话,则是这种情况:
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-01-15_5698c1c968ab2.jpg)
你可以根据自己的要求进行选择哪一种模式。
Qt中自带QCompleter类来实现上面的自动补全功能,读者可以在Qt自带的demo中很容易的学会该类的使用。**下面我要讲的是自己构造一个比QCompleter更强大的类**。有人会说,为什么有现成的不用,要自己写一个类呢?因为,我用QCompleter类的时候发现,它只有句首匹配模式(可能是我没仔细看文档,不知道可以改变模式),其次,当我的词库非常大的时候,有的时候就不会出现下拉自动补全列表,具体原因也不清楚。所以自己写了一个类,来实现QCompleter类所没有功能。废话不多说,直接见代码(代码注解比较详细,就不仔细讲解了,widget.ui文件也不给出了,就是一个空的界面):
1、widget.h
~~~
#ifndef WIDGET_H
#define WIDGET_H
#include <QWidget>
#include<QMouseEvent>
namespace Ui {
class Widget;
}
class Widget : public QWidget
{
Q_OBJECT
public:
explicit Widget(QWidget *parent = 0);
~Widget();
void mousePressEvent(QMouseEvent *event);
private:
Ui::Widget *ui;
signals:
void movesignal();
};
#endif // WIDGET_H
~~~
2.completelineedit.h
~~~
#ifndef COMPLETELINEEDIT_H
#define COMPLETELINEEDIT_H
#include <QLineEdit>
#include <QStringList>
#include<QFile>
#include<QTextCodec>
#include<QDebug>
class QListView;
class QStringListModel;
class QModelIndex;
class CompleteLineEdit : public QLineEdit {
Q_OBJECT
public:
CompleteLineEdit(QStringList words, QWidget *parent = 0);
public slots:
void setCompleter(const QString &text); // 动态的显示完成列表
void completeText(const QModelIndex &index); // 点击完成列表中的项,使用此项自动完成输入的单词
protected:
virtual void keyPressEvent(QKeyEvent *e);
virtual void focusOutEvent(QFocusEvent *e);
private slots:
void replyMoveSignal();
private:
QStringList words; // 整个完成列表的单词
QListView *listView; // 完成列表
QStringListModel *model; // 完成列表的model
};
#endif // COMPLETELINEEDIT_H
~~~
3.widget.cpp
~~~
#include "widget.h"
#include "ui_widget.h"
Widget::Widget(QWidget *parent) :
QWidget(parent),
ui(new Ui::Widget)
{
ui->setupUi(this);
}
Widget::~Widget()
{
delete ui;
}
void Widget::mousePressEvent(QMouseEvent *event)
{
emit movesignal();
}
~~~
4.completelineedit.cpp
~~~
#include "CompleteLineEdit.h"
#include <QKeyEvent>
#include <QListView>
#include <QStringListModel>
#include <QDebug>
CompleteLineEdit::CompleteLineEdit(QStringList words, QWidget *parent)
: QLineEdit(parent), words(words)
{
listView = new QListView(this);//用于显示下拉列表
model = new QStringListModel(this);
listView->setWindowFlags(Qt::ToolTip);//设置下拉列表的样式
connect(this, SIGNAL(textChanged(const QString &)), this, SLOT(setCompleter(const QString &)));
connect(listView, SIGNAL(clicked(const QModelIndex &)), this, SLOT(completeText(const QModelIndex &)));
}
void CompleteLineEdit::focusOutEvent(QFocusEvent *e)
{
// listView->hide();//当输入行不是焦点时,隐藏自动补全的下拉列表
}
void CompleteLineEdit::replyMoveSignal()
{
listView->hide();//当输入行不是焦点时,隐藏自动补全的下拉列表
}
void CompleteLineEdit::keyPressEvent(QKeyEvent *e)
{
if (!listView->isHidden())
{
int key = e->key();
int count = listView->model()->rowCount();
QModelIndex currentIndex = listView->currentIndex();
if (Qt::Key_Down == key)
{
// 按向下方向键时
int row = currentIndex.row() + 1;
if (row >= count)
{
row = 0;
}
QModelIndex index = listView->model()->index(row, 0);
listView->setCurrentIndex(index);
} else if (Qt::Key_Up == key)
{
// 按向下方向键时
int row = currentIndex.row() - 1;
if (row < 0)
{
row = count - 1;
}
QModelIndex index = listView->model()->index(row, 0);
listView->setCurrentIndex(index);
} else if (Qt::Key_Escape == key)
{
// 按下Esc键时隐藏完成列表
listView->hide();
} else if (Qt::Key_Enter == key || Qt::Key_Return == key)
{
// 按下回车键时,使用完成列表中选中的项,并隐藏完成列表
if (currentIndex.isValid())
{
QString text = listView->currentIndex().data().toString();
setText(text);
}
listView->hide();
} else
{
// 其他情况,隐藏完成列表,并使用QLineEdit的键盘按下事件
listView->hide();
QLineEdit::keyPressEvent(e);
}
} else
{
QLineEdit::keyPressEvent(e);
}
}
void CompleteLineEdit::setCompleter(const QString &text)
{
if (text.isEmpty())//没有输入内容的情况
{
listView->hide();
return;
}
if ((text.length() > 1) && (!listView->isHidden()))
{
return;
}
// 如果完整的完成列表中的某个单词包含输入的文本,则加入要显示的完成列表串中
QStringList sl;
foreach(QString word, words)
{
//填充模式一
if (word.contains(text))//只要包含该输入内容就显示,这里也可以设置大小写不敏感
{
sl << word;
}
//填充模式二
// if(word.indexOf(text,0,Qt::CaseInsensitive)==0)//必需与句首内容相同
// sl<<word;
}
model->setStringList(sl);
listView->setModel(model);
if (model->rowCount() == 0)
{
return;
}
// 设置列表的显示位置及大小
listView->setMinimumWidth(width());
listView->setMaximumWidth(width());
QPoint p(0, height());
int x = mapToGlobal(p).x();
int y = mapToGlobal(p).y() + 1;
listView->move(x, y);
listView->show();
}
void CompleteLineEdit::completeText(const QModelIndex &index)
{
QString text = index.data().toString();
setText(text);
listView->hide();
}
~~~
5.main.cpp
~~~
#include <QApplication>
#include "CompleteLineEdit.h"
#include"widget.h"
int main(int argc, char *argv[]) {
QApplication a(argc, argv);
QStringList sl;
QFile *inFile=new QFile ("input.txt");//这个是你自己的词库
if(!inFile->open(QIODevice::ReadOnly|QIODevice::Text))
{
qDebug()<<"cannot read!";
}
while(!inFile->atEnd())
{
QByteArray line = inFile->readLine();
QTextCodec* gbk_codec = QTextCodec::codecForName("GBK");
QString gbk_string = gbk_codec->toUnicode(line);
if (!line.isEmpty())
sl << gbk_string.trimmed();//将文件中的词汇输入到sl中
}
inFile->close();//关闭文件
sl<< "你好" << "好的" << "好吗" << "你的" << "真好啊" << "天真" << "你好吗";
Widget *w= new Widget();
CompleteLineEdit * edit= new CompleteLineEdit(sl,w);
w->show();
// QObject::connect(w,SIGNAL(movesignal()),edit,SLOT(replyMoveSignal()));
return a.exec();
}
~~~
最后放两张查单词软件用到的自动补全功能的截图:
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-01-15_5698c1c36e518.jpg)
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-01-15_5698c1c97de69.jpg)
<十三>音频播放
最后更新于:2022-04-01 07:11:36
# 【Qt编程】基于Qt的词典开发系列音频播放
在上一篇文章中,我是在Qt4平台上调用本地发音的,后来由于用到JSON解析,就将平台转到了Qt5,因为Qt5自带解析JSON的类。然后发现上一篇文章的方法无法运行,当然网上可以找到解决方法,我在这里直接调用Qt自带的类进行网络音频播放和本地播放。**比较适用于单词的网上发音和无网情况下的本地发音,而不用将音频文件下载下来,占用太多内存。**
为了简便起见,工程里只有一个main.cpp文件,不要忘了在.pro文件中加入下面两行:
~~~
QT += multimedia
CONFIG += qaxcontainer
~~~
**下面给出main.cpp文件:**
~~~
#include "widget.h"
#include <QApplication>
#include<QtMultimedia/QMediaPlayer>
#include<QUrl>
#include<ActiveQt/QAxObject>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
QMediaPlayer *player = new QMediaPlayer();
player->setMedia(QUrl("http://media.shanbay.com/audio/us/hello.mp3"));
player->play();
QAxObject *speech= new QAxObject();
speech->setControl("SAPI.SpVoice");
speech->dynamicCall("Speak(QString,uint)","good",1);//这里换成中文也可以
return a.exec();
}
~~~
运行后,你会发现本地发音肯定没网络发音好,本地发音是电脑发音,这也是缺点之一。
<十二>调用讲述人
最后更新于:2022-04-01 07:11:34
# 【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)
}
/***************************/
//}
~~~
**程序结果如下:**
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-01-15_5698c1c93fc22.jpg)
<十一>系统托盘的显示
最后更新于:2022-04-01 07:11:32
# 【Qt编程】基于Qt的词典开发系列系统托盘的显示
本文主要讨论Qt中的系统托盘的设置。系统托盘想必大家都不陌生,最常用的就是QQ。系统托盘以简单、小巧的形式能让人们较快的打开软件。废话不多说,下面开始具体介绍。
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-01-15_5698c1c8eab8d.jpg)
首先,新建一个Qt Gui项目,类型选择QMainWindow,类名也选择默认的mainwindow。这时,**工程项目中就有3个文件(界面文件不用管)**:mainwindow.h mainwindow.cpp main.cpp。然后,**分别修改3个文件如下**:
**1、mainwindow.h**
~~~
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include<QSystemTrayIcon>
#include<QMenu>
#include<QCloseEvent>
namespace Ui {
class MainWindow;
}
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = 0);
~MainWindow();
private:
Ui::MainWindow *ui;
QSystemTrayIcon *trayicon;//将系统托盘及菜单定义为窗口的私有成员
QMenu *trayiconMenu;
signals:
void exitsignal();//发射程序退出信号
private slots:
void onSystemTrayIconClicked(QSystemTrayIcon::ActivationReason reason);
void exit();//与退出按钮对应的槽
protected:
void closeEvent(QCloseEvent *event);
};
#endif // MAINWINDOW_H
~~~
**2、mainwindow.cpp**
~~~
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include<QtGui>
#include<QEvent>
#include<QDebug>
#include<QAction>
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
trayicon=new QSystemTrayIcon(this);
QAction *quit=new QAction(tr("exit"), this);
QIcon icon("D:/studytool/Qt/workspace/myTray/tengweitw.jpg");//注意修改这里的图片目录
setWindowIcon(icon);
// setMinimumSize(200,100);//窗口最大化和最小化一样大小。所以窗口不能放大也不能缩小
// setMaximumSize(200,100);
trayiconMenu=new QMenu(this);
trayiconMenu->addAction(quit);//这里可以添加多个菜单
// trayiconMenu->addSeparator();
trayicon->setIcon(icon);
trayicon->setToolTip(tr("我爱记单词"));
trayicon->setContextMenu(trayiconMenu);
connect(quit,SIGNAL(triggered(bool)),this,SLOT(exit()));
connect(trayicon, SIGNAL(activated(QSystemTrayIcon::ActivationReason)), this, SLOT(onSystemTrayIconClicked(QSystemTrayIcon::ActivationReason)));
trayicon->show();
}
MainWindow::~MainWindow()
{
delete ui;
}
~~~
~~~
//下面处理不同情况下,单击托盘图标的操作
void MainWindow::onSystemTrayIconClicked(QSystemTrayIcon::ActivationReason reason)
{
switch(reason)
{
//单击
case QSystemTrayIcon::Trigger:
// qDebug()<<this->isEnabled();
//双击
// flag=!flag;
// case QSystemTrayIcon::DoubleClick:
//恢复窗口显示
if(this->isVisible())//判断窗口是否可见
{
if(this->isMinimized())//窗口是否是最小化
{
this->setWindowFlags(Qt::WindowStaysOnTopHint);//置顶
// this->setWindowFlags(Qt::Widget);//取消置顶
this->setWindowState(Qt::WindowActive);
this->setGeometry(this->geometry());//使得每次显示窗口为上次的位置
this->show();
}
else
this->hide();
}
else
{
this->setWindowFlags(Qt::WindowStaysOnTopHint);//置顶
// this->setWindowFlags(Qt::Widget);//取消置顶
this->setWindowState(Qt::WindowActive);
this->setGeometry(this->geometry());
this->show();
}
break;
default:
break;
}
}
void MainWindow::exit()
{
emit exitsignal();
}
void MainWindow::closeEvent(QCloseEvent *event)//处理窗口关闭事件
{
if(trayicon->isVisible())
{
hide();
this->close();
event->ignore();
}
}
~~~
**3、main.cpp**
~~~
#include <QtGui/QApplication>
#include "mainwindow.h"
#include<QTextCodec>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
QTextCodec::setCodecForTr(QTextCodec::codecForLocale());//防止中文乱码
MainWindow w;
w.show();
QObject::connect(&w,SIGNAL(exitsignal()),&a,SLOT(quit()));//将退出按钮与退出函数绑定
return a.exec();
}
~~~
~~~
上面程序的主要部分都给了注释,在这就不一一分析了。运行结果如下:
~~~
~~~
1、界面如下(这不是我们关注的地方):
~~~
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-01-15_5698c1c909587.jpg)
~~~
2、托盘显示如下:
~~~
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-01-15_5698c1c91ba8f.jpg)
~~~
3、托盘的菜单显示如下:
~~~
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-01-15_5698c1c92e1e4.jpg)
<十>–国际音标的显示
最后更新于:2022-04-01 07:11:30
# 【Qt编程】基于Qt的词典开发系列--国际音标的显示
在年前的一篇文章中,我提到要学习Qt.于是在这学期看了一个月的qt。现在大致对qt有了一些了解。但是现在导师又把我调到了android应用开发平台,所以说qt的学习要搁置了。本打算这学期做一个单词查询软件的,目测只有在暑假完成了,现在才刚刚完成了本地词库的查询。目前的工作解决了词库的来源、本地词典的查询功能。日后的工作还有访问网络词库、记单词的算法实现等等。下面来说一说我在本地查词功能实现的问题—显示音标!
我们知道,qt中讲的比较多的是如何显示中文。一般情况都可以用下面代码来解决:
~~~
QTextCodec::setCodecForTr(QTextCodec::codecForName("UTF-8"));
QTextCodec::setCodecForTr(QTextCodec::codecForLocale());
~~~
但是,我们**本文解决的是显示国际音标**。因为当我们从词库中搜索到我们查的单词时,便会显示其音标和意思。但是有一些音标却无法显示,举例如下:
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-01-15_5698c1c8b916e.jpg)
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-01-15_5698c1c8c81b4.jpg)
**对比两张图就可以看出有一些音标无法显示。**
**由于Qt可以解析html语言,因此我们可以通过在html语言中设置字体来显示这些特殊的音标**。**一些常用的音标有:**
θ ð ʃ ʒ tʃ dʒ ŋ ɪ æ ɔ ʌ ʊ ə ɛ ɑ: ʊ: ɔ eɪ ɑɪ ɔɪ əʊ oʊ ɑʊ ɪə eə ʊə ɪə
**我们可以通过下面程序来测试:**
` QString str2=" θ ð ʃ ʒ tʃ dʒ ŋ ɪ æ ɔ ʌ ʊ ə ɛ ɑ: ʊ: ɔ eɪ ɑɪ ɔɪ əʊ oʊ ɑʊ ɪə eə ʊə ɪə"; `
然后将上面的字符串放入Qlabel等部件进行显示,在这里就不给出具体程序了。**测试结果如下:**
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-01-15_5698c1c8d9d93.jpg)
经过测试如上图,上述特殊音标都可以正常显示。学习过html语言的可以看出上面的字符串是html语言,并且将字体设置为Time New Roman。
举一反三,如果要显示其它特殊字符,只需找出其所在的字体。当然,如果你学过html,你可以在上面的语句中包含多个字体(有备无患)。
<九>–JSON数据解析
最后更新于:2022-04-01 07:11:27
# 【Qt编程】基于Qt的词典开发系列--JSON数据解析
在上一篇文章《[用户登录及API调用的实现](http://blog.csdn.net/tengweitw/article/details/45932429)》中,我通过程序实现了用户登录及API调用的实现,从而能够实现网络查词、添词的操作。但是,从上文中可以看到,调用API后返回的是JSON格式的数据,如下图所示(在QtCreator中的显示):
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-01-15_5698c1c86f5d8.jpg)
为了更好的观察JSON格式,我整理后显示如下:
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-01-15_5698c1c87fa2a.jpg)
显然,为了显示查词的结果,我们必须在上图中提取有用的信息,并正确的显示出来。上图中每一行内容的意思我已经在文章《[调用网络API](http://blog.csdn.net/tengweitw/article/details/45484803)》中作了解释。我在词典中选择想要显示的内容有:单词本身、单词ID、中文解释、英文解释、音标、发音音频,与之对应上图中的content、learning_id、definition、en_definitions、pron、audio。
为了获取这些内容,我们必须进行对上面的JSON格式数据(关于什么是JSON格式,可以自己百度)进行解析。在Qt 4中,没有对应的模块,必须单独加入相应的函数库,当然也可以自己根据JSON的格式自己写程序提取你想要的内容,只不过比较麻烦而已。在Qt5中,自带有对应的JSON解析模块,因此不用自己来写解析函数了,只需要调用相应的函数。**下面给出具体的程序实现:**
程序的主体框架与文章《[用户登录及API调用的实现](http://blog.csdn.net/tengweitw/article/details/45932429)》中的一样,只是在其中加入了JSON数据解析功能。具体的说,每当发送查词请求后,服务器就会返回JSON格式的单词内容。因此,我们只需要在处理服务器返回数据的函数replyfinished()函数中对应的查词操作QueryWordAction中进行JSON数据解析,将我们需要的内容存储到我们定义的Word类中。其中,word的定义如下:
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-01-15_5698c1c89334c.jpg)
由于只是在上文《[用户登录及API调用的实现](http://blog.csdn.net/tengweitw/article/details/45932429)》的程序中加了JSON解析的部分,其它部分基本没变,所以程序的运行流程及思路可见上文。当然,程序中的注释也算比较详细,因此不作具体说明。只需要将下面五个源文件放入空的Qt项目中即可。注意:是在Qt 5中,且需要在其.pro文件中加入语句:QT +=core gui network webkit multimedia。**具体的源文件如下**:
* * *
**1、word.h文件**
~~~
#ifndef WORD_H
#define WORD_H
#include<QString>
#include<List>
#include <QJsonObject>
#include<QtMultimedia/QMediaPlayer>
#include<QUrl>
class Word
{
public:
explicit Word();
Word(const Word& word);
~Word();
public:
QString name;//单词本身
QString learning_id;//单词ID
QString definition;//单词中文解释
QString en_definitions;//单词英文解释
QString pron;//单词音标
QString audio;//单词的发音音频
void clear();
};
#endif // WORD_H
~~~
* * *
**2、word.cpp文件**
~~~
#include "word.h"
Word::Word()
{
}
Word::Word(const Word &w)
{
name=w.name;
learning_id=w.learning_id;
definition=w.definition;
en_definitions=w.en_definitions;
pron=w.pron;
audio=w.audio;
}
Word::~Word()
{
}
void Word::clear()//清除内容
{
name="";
learning_id="";
definition="";
en_definitions="";
pron="";
audio="";
// this->examples=NULL;
}
~~~
* * *
**3、network.h文件**
~~~
#ifndef NETWORK_H
#define NETWORK_H
#include"word.h"
#include <QObject>
#include <QtNetwork/QNetworkAccessManager>
#include<QtNetwork/QNetworkReply>
#include<QtNetwork/QNetworkRequest>
#include<QtNetwork/QNetworkCookie>
#include<QtNetwork/QNetworkCookieJar>
#include <QJsonDocument>
#include <QJsonObject>
#include <QJsonArray>
#include <QJsonValue>
#include<QString>
#include<QDebug>
#include<QList>
#include<QUrl>
#include<QByteArray>
class netWork : public QObject
{
Q_OBJECT
public:
explicit netWork(QObject *parent = 0);
// ~netWork();
enum HttpAction{NoAction,NetStudy,GetSessionidAction,LoginAction,QueryWordAction,AddWordAction,AddExampleAction,QueryWordExamplesAction};
HttpAction httpAction;
QNetworkAccessManager * http;
QString sessionid;
QString queryword;//要查询的单词
QString nickname;
QString username;
QString password;
bool isBusy;
Word *gotword;//通过访问网络得到的单词信息
QString getCookie(const QString &name);
void loginShanbay();
void queryWord(const QString &word);
void queryExamples(QString learningid);
void connectNet(QString username="nineheadedbird", QString password="123456");
void addWord(const QString &word);
signals:
void signalQueryFinished(Word *word);
void connectSuccess();
void connectFail();
void verifySuccess();
void verifyFail();
void NetState(bool);
public slots:
void replyfinished(QNetworkReply*);
};
#endif // NETWORK_H
~~~
* * *
**4、network.cpp文件**
~~~
#include "network.h"
#include<QList>
#include<QDesktopServices>
netWork::netWork(QObject *parent) :
QObject(parent)
{
http=new QNetworkAccessManager(this);
http->setCookieJar(new QNetworkCookieJar(this));
connect(http,SIGNAL(finished(QNetworkReply*)),this,SLOT(replyfinished(QNetworkReply*)));
isBusy=true;
gotword=new Word();
}
QString netWork::getCookie(const QString &name)
{
foreach(QNetworkCookie cookie , http->cookieJar()->cookiesForUrl(QUrl("http://www.shanbay.com/")))
{
//qDebug()<<cookie.name();
if(cookie.name()==name)
{
return cookie.value();
}
}
return "";
}
void netWork::loginShanbay()
{
QNetworkRequest request;
request.setUrl(QUrl("http://www.shanbay.com/accounts/login/"));
request.setRawHeader("Accept","text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8");
request.setRawHeader("Accept-Charset","GBK,utf-8;q=0.7,*;q=0.3");
request.setRawHeader("Accept-Language","zh-CN,zh;q=0.8");
request.setRawHeader("Cache-Control","max-age=0");
request.setRawHeader("Connection","keep-alive");
request.setRawHeader("Host","www.shanbay.com");
request.setRawHeader("User-Agent","Mozilla/5.0 (Windows NT 6.1) AppleWebKit/535.7 (KHTML, like Gecko) Chrome/16.0.912.63 Safari/535.7");
request.setRawHeader("Origin","http//www.shanbay.com");
request.setRawHeader("Referer","http://www.shanbay.com/accounts/login/");
request.setRawHeader("Host","www.shanbay.com");
request.setRawHeader("Content-Type","application/x-www-form-urlencoded");
QByteArray postData;
postData.append(QString("csrfmiddlewaretoken=%1&").arg(sessionid));
postData.append(QString("username=%1&password=%2&").arg(QUrl::toPercentEncoding(username).constData()).arg(password));
postData.append("login=登录&continue=home&u=1&next=");
request.setHeader(QNetworkRequest::ContentLengthHeader,postData.size());
httpAction=LoginAction;
http->post(request,postData);
}
void netWork::queryWord(const QString &word)
{
QNetworkRequest request;
request.setUrl(QUrl("http://www.shanbay.com/api/word/"+word));
request.setRawHeader("Accept","text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8");
request.setRawHeader("Accept-Charset","GBK,utf-8;q=0.7,*;q=0.3");
request.setRawHeader("Accept-Language","zh-CN,zh;q=0.8");
request.setRawHeader("Cache-Control","max-age=0");
request.setRawHeader("Connection","keep-alive");
request.setRawHeader("Host","www.shanbay.com");
request.setRawHeader("User-Agent","Mozilla/5.0 (Windows NT 6.1) AppleWebKit/535.7 (KHTML, like Gecko) Chrome/16.0.912.63 Safari/535.7");
httpAction=QueryWordAction;
http->get(request);
}
void netWork::queryExamples(QString learningid)
{
if(learningid=="0"){
return;
}
QNetworkRequest request;
request.setUrl(QUrl("http://www.shanbay.com/api/learning/examples/"+learningid));
request.setRawHeader("Accept","text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8");
request.setRawHeader("Accept-Charset","GBK,utf-8;q=0.7,*;q=0.3");
request.setRawHeader("Accept-Language","zh-CN,zh;q=0.8");
request.setRawHeader("Cache-Control","max-age=0");
request.setRawHeader("Connection","keep-alive");
request.setRawHeader("Host","www.shanbay.com");
request.setRawHeader("User-Agent","Mozilla/5.0 (Windows NT 6.1) AppleWebKit/535.7 (KHTML, like Gecko) Chrome/16.0.912.63 Safari/535.7");
httpAction=QueryWordExamplesAction;
http->get(request);
}
void netWork::addWord(const QString &word)
{
if(word.isEmpty())
qDebug()<<"你的输入有误";
else
{
QNetworkRequest request;
request.setUrl(QUrl("http://www.shanbay.com/api/learning/add/"+word));
request.setRawHeader("Accept","text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8");
request.setRawHeader("Accept-Charset","GBK,utf-8;q=0.7,*;q=0.3");
request.setRawHeader("Accept-Language","zh-CN,zh;q=0.8");
request.setRawHeader("Cache-Control","max-age=0");
request.setRawHeader("Connection","keep-alive");
request.setRawHeader("Host","www.shanbay.com");
request.setRawHeader("User-Agent","Mozilla/5.0 (Windows NT 6.1) AppleWebKit/535.7 (KHTML, like Gecko) Chrome/16.0.912.63 Safari/535.7");
httpAction=AddWordAction;
http->get(request);
}
}
void netWork::connectNet(QString username, QString password)
{
this->username=username;
this->password=password;
QNetworkRequest request;
request.setUrl(QUrl("http://www.shanbay.com/accounts/login/"));
request.setRawHeader("Accept","text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8");
request.setRawHeader("Accept-Charset","GBK,utf-8;q=0.7,*;q=0.3");
request.setRawHeader("Accept-Language","zh-CN,zh;q=0.8");
request.setRawHeader("Cache-Control","max-age=0");
request.setRawHeader("Connection","keep-alive");
request.setRawHeader("Host","www.shanbay.com");
request.setRawHeader("User-Agent","Mozilla/5.0 (Windows NT 6.1) AppleWebKit/535.7 (KHTML, like Gecko) Chrome/16.0.912.63 Safari/535.7");
httpAction=GetSessionidAction;
http->get(request);
}
void netWork::replyfinished(QNetworkReply *reply)
{
QByteArray wordInformation;
QVariant status_code=reply->attribute(QNetworkRequest::HttpStatusCodeAttribute);
// qDebug()<<"code_state="<<status_code;
if(status_code==QVariant::Invalid)
emit NetState(false);
else
emit NetState(true);
QJsonDocument jsonDoc;
QJsonObject jsonObj,vocObj,en_definitionsObj;
switch(httpAction)
{
case NoAction:
break;
case GetSessionidAction:
sessionid=getCookie("csrftoken");
if(!sessionid.isEmpty()){
emit connectSuccess();
qDebug()<<("Connect successfully! Verifying username and password...");
loginShanbay();
}else{
emit connectFail();
qDebug()<<("Cannot connect! Please try to check the internet!");
}
break;
case LoginAction:
httpAction=NoAction;
if(0==reply->readAll().size())
{
QString nickname=QUrl::fromPercentEncoding(getCookie("username").toLatin1());
emit verifySuccess();
qDebug()<<"Login successfully!"<<nickname;
queryWord("hello");
}else
{
emit verifyFail();
qDebug()<<"Failed to login! Please check!";
}
break;
case QueryWordAction://是在这里面进行JSON数据解析!
qDebug()<<"----query word----";
wordInformation=reply->readAll();
qDebug()<<"the information of word in JSON format::\n";
qDebug()<< wordInformation;
jsonDoc=QJsonDocument::fromJson( wordInformation);
if(!jsonDoc.isNull())
{
jsonObj=jsonDoc.object();
vocObj=jsonObj.value("voc").toObject();//Qt5 自带JSON解析,因此很容易得到单词的各个部分的内容
en_definitionsObj=vocObj.value("en_definitions").toObject();
gotword->clear();
gotword->name=vocObj.value("content").toString();//单词本身
gotword->learning_id=QString::number(jsonObj.value("learning_id").toDouble(),'g',15);
gotword->definition=vocObj.value("definition").toString();//中文解释
gotword->pron=vocObj.value("pron").toString();//单词音标
gotword->pron=gotword->pron;
gotword->audio=vocObj.value("audio").toString();
//由于单词的英文解释有多项(词性、解释等等),我们将结果用html格式表示,并使得词性颜色设置为黑色
for( QJsonObject::const_iterator it=en_definitionsObj.constBegin();it!=en_definitionsObj.constEnd();it++)
{
gotword->en_definitions+=QString("<br><font color='black'><b>%1\. </b></font>").arg(it.key());
if(it.value().isArray())
{
foreach(QJsonValue jv,it.value().toArray())
{
gotword->en_definitions+=QString("%1; ").arg(jv.toString());
}
}
gotword->en_definitions+="<br>";
}
emit signalQueryFinished(gotword);
//显示JSON解析后的数据
qDebug()<<"name="<<gotword->name;
qDebug()<<"word="<<gotword->learning_id;
qDebug()<<"definition="<<gotword->definition;
qDebug()<<"en_definitions="<<gotword->en_definitions;
qDebug()<<"pron="<<gotword->pron;
qDebug()<<"audio"<<gotword->audio;
//进行网络发音
QMediaPlayer *player = new QMediaPlayer();
player->setMedia(QUrl(gotword->audio));
player->play();
if(gotword->learning_id!="0")//不是新单词
{
qDebug()<<"你学过这个单词";
}
}
break;
case QueryWordExamplesAction:
// qDebug()<<"query word examples";
// qDebug()<<reply->readAll();
break;
case AddWordAction:
// qDebug()<<"add word";
jsonDoc=QJsonDocument::fromJson(reply->readAll());
if(!jsonDoc.isNull())
{
jsonObj=jsonDoc.object();
//qDebug()<<"jsonObj"<<jsonObj;
gotword->learning_id=QString::number(jsonObj.value("id").toDouble(),'g',15);
// qDebug()<<jsonObj.value("id").toDouble()<<"add word result learning id="<<gotword->learning_id<< (gotword->learning_id!="0");
// emit signalAddwordFinished(DICT::word->learning_id!="0");
// queryExamples(DICT::word->learning_id);
}
default:break;
}
}
~~~
* * *
**5、main.cpp文件**
~~~
#include <QApplication>
#include "network.h"
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
netWork *nW = new netWork();
nW->connectNet();
return a.exec();
}
~~~
**运行结果如下图:**
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-01-15_5698c1c8a3912.jpg)
<八>–用户登录及API调用的实现
最后更新于:2022-04-01 07:11:25
# 【Qt编程】基于Qt的词典开发系列--用户登录及API调用的实现
在上一篇文章《[调用网络API](http://blog.csdn.net/tengweitw/article/details/45484803)》中,我只讲述了如何直观的使用API接口以及调用API后返回的结果,本文则从程序实现的角度来实现API的调用,当然本程序的实现也是借助于扇贝网的API接口文档[http://www.shanbay.com/help/developer/api/](http://www.shanbay.com/help/developer/api/)。
由[API文档](http://www.shanbay.com/help/developer/api/)可知,要想调用其API,必须先注册。因此,我就注册了,账户名为nineheadedbird, 密码为123456。显然,我们要查词,首先必须得登录该账户。如果用浏览器,那就很简单,只需单纯的输入用户名和密码就可以了。可实际上,这一操作并不简单,只是浏览器为我们做了这一切。如果我们要通过程序来实现上述功能的话,就需要用到Qt中的get()函数了,而发送请求的内容格式就至关重要了。
##查看请求格式
**我们可以通过浏览器来查看请求格式**:首先用谷歌浏览器(其他浏览器也可以,不过你要百度一下怎么来查看这些格式)打开扇贝网的登录界面[http://www.shanbay.com/accounts/login/](http://www.shanbay.com/accounts/login/) ,在谷歌浏览器的设置中单击开发者选项,然后刷新一下页眉,就会出现如下的界面:
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-01-15_5698c1c803347.jpg)
然后**点击右边的第一个文件login**,就会出现下面的内容:
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-01-15_5698c1c823cc8.jpg)
从上图可以看出,**内容分为三类**:**General、Response Headers、Request Headers**
在**General**中可以看到**Request Method为GET**(一般还有另一种方式POST,这在Qt中都有对应的函数),Status Code为200表示正常。在Response Headers 中**我们关注的是Set-Cookie中的csrftoken的值**,因为这在我们登录时需要这个值。**我们最关心的是Request Headers的内容,这部分就是我们请求函数中内容格式**!参考上述的具体内容如下:
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-01-15_5698c1c84763b.jpg)
**我们的程序可以写成如下的方式:**
~~~
QNetworkRequest request;
request.setUrl(QUrl("http://www.shanbay.com/accounts/login/"));
request.setRawHeader("Accept","text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8");
request.setRawHeader("Accept-Language","zh-CN,zh;q=0.8");
request.setRawHeader("Cache-Control","max-age=0");
request.setRawHeader("Connection","keep-alive");
request.setRawHeader("Host","www.shanbay.com");
request.setRawHeader("User-Agent","Mozilla/5.0 (Windows NT 6.1) AppleWebKit/535.7 (KHTML, like Gecko) Chrome/16.0.912.63 Safari/535.7");
http->get(request);
~~~
当我们执行上述的请求之后,服务器就会作答,作答的内容就是上面的Response Headers,而我们需要的是Set-Cookie中的csrftoken的值。**在Qt中,我们将程序中finished信号与我们定义的槽关联,即每当网络应答结束时,都会发射这个信号,从而触发该槽函数的执行,来处理服务器的应答内容**。在程序中,getCookie函数就是来获取csrftoken的值。
## 用户登录
获得csrftoken的值后,我们就需要实现登录操作了。除了上述的请求格式之外,我们还需要加入csrftoken的值、用户名以及密码。**具体格式可见下述代码:**
~~~
QNetworkRequest request; request.setUrl(QUrl("http://www.shanbay.com/accounts/login/"));
request.setRawHeader("Accept","text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8");
request.setRawHeader("Accept-Language","zh-CN,zh;q=0.8");
request.setRawHeader("Cache-Control","max-age=0");
request.setRawHeader("Connection","keep-alive");
request.setRawHeader("Host","www.shanbay.com");
request.setRawHeader("User-Agent","Mozilla/5.0 (Windows NT 6.1) AppleWebKit/535.7 (KHTML, like Gecko) Chrome/16.0.912.63 Safari/535.7");
request.setRawHeader("Origin","http//www.shanbay.com");
request.setRawHeader("Referer","http://www.shanbay.com/accounts/login/");
request.setRawHeader("Host","www.shanbay.com");
request.setRawHeader("Content-Type","application/x-www-form-urlencoded");
QByteArray postData;
postData.append(QString("csrfmiddlewaretoken=%1&").arg(sessionid));//csrftoken的值
postData.append(QString("username=%1&password=%2&").arg(QUrl::toPercentEncoding(username).constData()).arg(password));//用户名及密码
postData.append("login=登录&continue=home&u=1&next=");
request.setHeader(QNetworkRequest::ContentLengthHeader,postData.size());
httpAction=LoginAction;
http->post(request,postData);
~~~
##调用API
完成登录之后,就可以进行查词和添词操作了。除了上述提到的请求头格式之外,只需要遵守API规范(《[调用网络API](http://blog.csdn.net/tengweitw/article/details/45484803)》中提到请求格式)即可。**查词及添词的程序实现分别如下:**
~~~
void netWork::queryWord(const QString &word)//查词操作
{
QNetworkRequest request;
request.setUrl(QUrl("http://www.shanbay.com/api/word/"+word));
request.setRawHeader("Accept","text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8");
request.setRawHeader("Accept-Charset","GBK,utf-8;q=0.7,*;q=0.3");
request.setRawHeader("Accept-Language","zh-CN,zh;q=0.8");
request.setRawHeader("Cache-Control","max-age=0");
request.setRawHeader("Connection","keep-alive");
request.setRawHeader("Host","www.shanbay.com");
request.setRawHeader("User-Agent","Mozilla/5.0 (Windows NT 6.1) AppleWebKit/535.7 (KHTML, like Gecko) Chrome/16.0.912.63 Safari/535.7");
httpAction=QueryWordAction;
http->get(request);
}
void netWork::addWord(const QString &word)//添词操作
{
if(word.isEmpty())
qDebug()<<"你的输入有误";
else
{
QNetworkRequest request;
request.setUrl(QUrl("http://www.shanbay.com/api/learning/add/"+word));
request.setRawHeader("Accept","text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8");
request.setRawHeader("Accept-Charset","GBK,utf-8;q=0.7,*;q=0.3");
request.setRawHeader("Accept-Language","zh-CN,zh;q=0.8");
request.setRawHeader("Cache-Control","max-age=0");
request.setRawHeader("Connection","keep-alive");
request.setRawHeader("Host","www.shanbay.com");
request.setRawHeader("User-Agent","Mozilla/5.0 (Windows NT 6.1) AppleWebKit/535.7 (KHTML, like Gecko) Chrome/16.0.912.63 Safari/535.7");
httpAction=AddWordAction;
http->get(request);
}
}
~~~
## 完整流程
至此,API调用的各个功能已经实现,**下面给出程序的整体思路**:**首先**获取csrftoken的值(每次都不同);**然后**利用用户名、密码及csrftoken的值来登录;**接着**就可以调用API了。在程序中,每当进行请求,都会在replyfinished函数中用case语句来分别处理这些请求对应的应答。**注意**,不要连续的进行请求,否则可能发生冲突。在程序中,为了防止冲突,我在connectNet请求后,在其应答处理函数中再进行loginShanbay的登录,然后在其应答函数中进行queryWord查词请求,然后在其对应的应答处理函数中进行addWord添词请求。**其结果显示如下:**
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-01-15_5698c1c85d338.jpg)
## 程序实现
**下面我们给出具体的程序实现(qt 5版本,使用到网络类,需要加上QT += network)**:首先建立一个空的qt子项目,然后添加一个名为netWork的类,继承自QObject,然后再添加一个名为main的源文件,这三个文件的内容分别如下:
**1、network.h文件**
~~~
#ifndef NETWORK_H
#define NETWORK_H
#include <QObject>
#include <QtNetwork/QNetworkAccessManager>
#include<QtNetwork/QNetworkReply>
#include<QtNetwork/QNetworkRequest>
#include<QtNetwork/QNetworkCookie>
#include<QtNetwork/QNetworkCookieJar>
#include <QJsonDocument>
#include <QJsonObject>
#include <QJsonArray>
#include <QJsonValue>
#include<QString>
#include<QDebug>
#include<QList>
#include<QUrl>
#include<QByteArray>
class netWork : public QObject //由于程序文件直接摘自整个项目文件,所以程序中有关的定义或函数没有使用,但是这个程序可以单独运行
{
Q_OBJECT
public:
explicit netWork(QObject *parent = 0);
// ~netWork();
enum HttpAction{NoAction,NetStudy,GetSessionidAction,LoginAction,QueryWordAction,AddWordAction,AddExampleAction,QueryWordExamplesAction};
HttpAction httpAction;
QNetworkAccessManager * http;
QString sessionid;
QString queryword;//要查询的单词
QString nickname;
QString username;
QString password;
bool isBusy;
QString getCookie(const QString &name);
void loginShanbay();
void queryWord(const QString &word);
void queryExamples(QString learningid);
void connectNet(QString username="nineheadedbird", QString password="123456");
void addWord(const QString &word);
signals://这里的信号都没有用到
void connectSuccess();
void connectFail();
void verifySuccess();
void verifyFail();
void NetState(bool);
public slots:
void replyfinished(QNetworkReply*);
};
#endif // NETWORK_H
~~~
**2、network.cpp文件**
~~~
#include "network.h"
#include<QList>
#include<QDesktopServices>
netWork::netWork(QObject *parent) :
QObject(parent)
{
http=new QNetworkAccessManager(this);
http->setCookieJar(new QNetworkCookieJar(this));
connect(http,SIGNAL(finished(QNetworkReply*)),this,SLOT(replyfinished(QNetworkReply*)));//将finished信号与我们定义的槽关联,每当网络应答结束时,都会发射这个信号
isBusy=true;
}
QString netWork::getCookie(const QString &name)//用于获得SessionId
{
foreach(QNetworkCookie cookie , http->cookieJar()->cookiesForUrl(QUrl("http://www.shanbay.com/")))
{
if(cookie.name()==name)
{
qDebug()<<"csrftoken:"<<cookie.value();
return cookie.value();
}
}
return "";
}
void netWork::connectNet(QString username, QString password)//连接网络,使用默认的用户名和密码
{
this->username=username;
this->password=password;
QNetworkRequest request;
request.setUrl(QUrl("http://www.shanbay.com/accounts/login/"));
request.setRawHeader("Accept","text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8");
request.setRawHeader("Accept-Language","zh-CN,zh;q=0.8");
request.setRawHeader("Cache-Control","max-age=0");
request.setRawHeader("Connection","keep-alive");
request.setRawHeader("Host","www.shanbay.com");
request.setRawHeader("User-Agent","Mozilla/5.0 (Windows NT 6.1) AppleWebKit/535.7 (KHTML, like Gecko) Chrome/16.0.912.63 Safari/535.7");
httpAction=GetSessionidAction;
http->get(request);
}
void netWork::replyfinished(QNetworkReply *reply)//每当执行网站应答结束后,就会执行该槽函数
{
QVariant status_code=reply->attribute(QNetworkRequest::HttpStatusCodeAttribute);
qDebug()<<"code_state="<<status_code;//网络状态,200代表正常,302代表重定向,404:not found等等
if(status_code==QVariant::Invalid)//判断是否连接到网站,即当前设备能否上网
emit NetState(false);
else
emit NetState(true);
switch(httpAction)//根据我们都进行了什么网络请求
{
case NoAction:
break;
case GetSessionidAction://获取SessionId
sessionid=getCookie("csrftoken");
if(!sessionid.isEmpty())
{
emit connectSuccess();
qDebug()<<("已经连接扇贝网,正在验证用户名密码...");
loginShanbay();
}else
{
emit connectFail();
qDebug()<<("Cannot connect to the website!");
}
break;
case LoginAction: //进行登录操作
httpAction=NoAction;
if(0==reply->readAll().size())
{
QString nickname=QUrl::fromPercentEncoding(getCookie("username").toLatin1());
emit verifySuccess();
qDebug()<<"Successfully Login"<<nickname;
queryWord("hello");
}else
{
emit verifyFail();
qDebug()<<"Failed to login!";
}
break;
case QueryWordAction://查词操作
qDebug()<<"----query word----";
qDebug()<<reply->readAll();//读取查词结果
addWord("hello");//添加单词到单词本
break;
case AddWordAction://添词操作
qDebug()<<"---add word----";
qDebug()<<reply->readAll();//返回添加词语的learning_id
break;
default:break;
}
}
void netWork::loginShanbay()//账户密码的登录操作
{
QNetworkRequest request;
request.setUrl(QUrl("http://www.shanbay.com/accounts/login/"));
request.setRawHeader("Accept","text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8");
request.setRawHeader("Accept-Language","zh-CN,zh;q=0.8");
request.setRawHeader("Cache-Control","max-age=0");
request.setRawHeader("Connection","keep-alive");
request.setRawHeader("Host","www.shanbay.com");
request.setRawHeader("User-Agent","Mozilla/5.0 (Windows NT 6.1) AppleWebKit/535.7 (KHTML, like Gecko) Chrome/16.0.912.63 Safari/535.7");
request.setRawHeader("Origin","http//www.shanbay.com");
request.setRawHeader("Referer","http://www.shanbay.com/accounts/login/");
request.setRawHeader("Host","www.shanbay.com");
request.setRawHeader("Content-Type","application/x-www-form-urlencoded");
QByteArray postData;
postData.append(QString("csrfmiddlewaretoken=%1&").arg(sessionid));
postData.append(QString("username=%1&password=%2&").arg(QUrl::toPercentEncoding(username).constData()).arg(password));
postData.append("login=登录&continue=home&u=1&next=");
request.setHeader(QNetworkRequest::ContentLengthHeader,postData.size());
httpAction=LoginAction;
http->post(request,postData);
}
void netWork::queryWord(const QString &word)//查词操作
{
QNetworkRequest request;
request.setUrl(QUrl("http://www.shanbay.com/api/word/"+word));
request.setRawHeader("Accept","text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8");
request.setRawHeader("Accept-Charset","GBK,utf-8;q=0.7,*;q=0.3");
request.setRawHeader("Accept-Language","zh-CN,zh;q=0.8");
request.setRawHeader("Cache-Control","max-age=0");
request.setRawHeader("Connection","keep-alive");
request.setRawHeader("Host","www.shanbay.com");
request.setRawHeader("User-Agent","Mozilla/5.0 (Windows NT 6.1) AppleWebKit/535.7 (KHTML, like Gecko) Chrome/16.0.912.63 Safari/535.7");
httpAction=QueryWordAction;
http->get(request);
}
void netWork::addWord(const QString &word)//添词操作
{
if(word.isEmpty())
qDebug()<<"你的输入有误";
else
{
QNetworkRequest request;
request.setUrl(QUrl("http://www.shanbay.com/api/learning/add/"+word));
request.setRawHeader("Accept","text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8");
request.setRawHeader("Accept-Charset","GBK,utf-8;q=0.7,*;q=0.3");
request.setRawHeader("Accept-Language","zh-CN,zh;q=0.8");
request.setRawHeader("Cache-Control","max-age=0");
request.setRawHeader("Connection","keep-alive");
request.setRawHeader("Host","www.shanbay.com");
request.setRawHeader("User-Agent","Mozilla/5.0 (Windows NT 6.1) AppleWebKit/535.7 (KHTML, like Gecko) Chrome/16.0.912.63 Safari/535.7");
httpAction=AddWordAction;
http->get(request);
}
}
~~~
**3、main.cpp文件**
~~~
#include <QApplication>
#include "network.h"
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
netWork *nW = new netWork();
//
nW->connectNet();
// nW->loginShanbay();
// nW->queryWord("hello");
return a.exec();
}
~~~
<七>–调用网络API
最后更新于:2022-04-01 07:11:23
# 【Qt编程】基于Qt的词典开发系列--调用网络API
前面文章中我们实现了本地的词库设计,可以完成本地的查词功能,那么这篇文章**主要讲一讲如何通过调用网络的api来实现词典的网络查词功能**。
## 词典API的选择
基本上市面上做词典软件的公司都有对应的api,比如说有道、金山等等,不过一般都需要api key,有点麻烦,于是乎,我就找了个简单好用的扇贝网API:[http://www.shanbay.com/help/developer/api/](http://www.shanbay.com/help/developer/api/) 奇怪的是,该api说要停用了,可以从我去年暑假完成该软件制作,到如今为止,还能正常使用。
##扇贝词典api的使用
进入上面我给的扇贝网的api地址,我们可以看出该api文档还是比较详细。像我这种从未接触过网络编程的人来说,也能比较了解。下面我们来简单说说怎么使用这些api。由于我词典的功能只有查词、添加单词两种功能,因此只需要使用文档中的对应的api接口就可以了。
在api介绍的开始处,有下面的文字如下:
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-01-15_5698c1c794b50.jpg)
因此,在使用该api时,**我们必须首先注册**!完成注册并登录后,我们继续查看api文档,由于我们只实现查询单词和保存单词的功能(当然也可以实现文档中的其它功能,使用方法类似),因此只需关注下面如图所示的内容就够了:
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-01-15_5698c1c7a5fed.jpg)
有可能你看了图中的内容,还是一头雾水,那么我们可以先尝试一下。**首先确保已经登录了扇贝网,然后在浏览器中输入**:[http://www.shanbay.com/api/word/test](http://www.shanbay.com/api/word/test) ,然后我们就可以看到如下的内容:
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-01-15_5698c1c7bae38.jpg)
图中的内容就是我们查询test单词所得到的结果。如果我们想要保存该单词,则只需要在浏览器中输入:[http://www.shanbay.com/api/learning/add/test](http://www.shanbay.com/api/learning/add/test) 我们得到的结果如下:
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-01-15_5698c1c7ce7de.jpg)
上图说明这个单词已经被我们保存了,并且给予了它一个编号。因此,当我们再查询该单词的时候,即在浏览器中输入:[http://www.shanbay.com/api/word/test](http://www.shanbay.com/api/word/test) 会发现id不再是0,而是与上面的编号相同:
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-01-15_5698c1c7dd86b.jpg)
到目前为止,上述的操作都不涉及到编程,都是直接在浏览器中进行的操作。然而,我们想要在词典中实现该功能,则就**不得不面对两个问题:1、编程实现用户的登录及调用api;2、解析调用api后返回的json格式的数据。**
**上述的两个问题都会在后续的系列文章中予以解决,敬请关注。**
<六>–界面美化设计
最后更新于:2022-04-01 07:11:20
# 【Qt编程】基于Qt的词典开发系列--界面美化设计
本文讲一讲界面设计,作品要面向用户,界面设计的好坏直接影响到用户的体验。现在的窗口设计基本都是扁平化的,你可以从window XP与window 8的窗口可以明显感觉出来。当然除了窗口本身的效果,窗口中各种控件的特效也是特别重要的一环。下面讲讲我在词典软件中的一些设计:说到界面美化的设计,不得不提到美工,一个好的美工是相当的重要!软件毕竟少不了图标,而不懂美工的我,也就只能在网上使用别人的图标了。
## 如何得到网上的图标?
直接百度就可以了,当然还有另一种方法:就是从别人的文件中提取这些图标文件。一般来说,图标文件是不会以图片格式存储在软件目录中的,一般都存放在后缀名为.rdb以及.ui文件中。具体的步骤如下:
1、找到要提取软件的安装目录
2、进行搜索:搜索.jpg、.png等图片格式文件(能够搜到最好了),然后搜索.rdb或.ui文件
3、下载RDB打包解包工具,进行解包,就可以得到图片资源了
## 控件间的无缝连接:
所谓无缝连接是指控件与控件之间没有空隙,用Qt Creator打开界面文件,比如我打开这个词典项目,打开searchwindow.ui文件,控件是否有空隙的效果如下所示:
控件间的空隙大小可以由这些子控件所在父控件的Layout属性控制
**1、当Layout属性设置如下时:(有空隙的情况)**
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-01-15_5698c1c6553e5.jpg)
则控件间有空隙,显示效果如下:
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-01-15_5698c1c662add.jpg)
**2当Layout属性设置如下时:(没有空隙的情况)**
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-01-15_5698c1c6778a7.jpg)
则控件间无空隙,显示效果如下:
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-01-15_5698c1c6880bb.jpg)
## 按钮的美化
现在按钮也开始扁平化,例如上图中的所有按钮都是扁平化的,两者的差别如下:
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-01-15_5698c1c69d574.jpg)
显然第一种是常规的按钮,如果我们把ToolButton的autoRise属性书中,就会出现第二种情况,之所以没有使用QPushButton是因为它没有autoRaise属性。
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-01-15_5698c1c6c37f0.jpg)
当选中autoRasie属性后,当鼠标放在该按钮上时效果如下:
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-01-15_5698c1c6d13e1.jpg)
## 动态渲染效果
我们发现很多软件都有动态效果,如鼠标放在按钮上时,会发生变化,点击后又发生变化,这些是怎么做到的呢?Qt中美化界面最好的使用QML,不过由于刚出来不久,网上资料不是很多,我也不是很懂,就不介绍了。学习过网页制作的都知道,网页的渲染效果用到了css,与此类似,Qt使用qss来美化界面。下面介绍一种简单使用的方法来进行渲染操作:
右键单击界面中的控件,选择“更改样式表……”,然后在弹出的窗口中设置渲染效果,**下面以单击按钮来举例说明:**
首先,右击关闭按钮,选择“更改样式表……”:
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-01-15_5698c1c6e2020.jpg)
然后在弹出的“编辑样式表”按钮中写入如下代码:
~~~
QToolButton{
border-image: url(:/searchwindow/searchwindow/close_normal.png);
}
//上面的语句是给“关闭”按钮添加close_normal.png的图标,注意这里需要写你自己图片的路径
QToolButton:hover{
border-image: url(:/searchwindow/searchwindow/close_hover.png);
}
//这条语句的作用是,当鼠标放在“关闭”按钮上时,图标变成close_hover.png的图标,注意这里需要写你自己图片的路径
~~~
**这两条语句实现的效果如下:**
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-01-15_5698c1c706d5b.jpg)
一般情况下,关闭按钮显示如下:
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-01-15_5698c1c71a52e.jpg)
当鼠标放在上面时,效果如下:
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-01-15_5698c1c726fc2.jpg)
我在词典中所有的按钮几乎都是采用了这种效果,如果想使用更多的效果,可以百度qt setstylesheet,可以看到更多的渲染效果。**在软件界面中,listWidget控件中我使用的样式表如下**:
~~~
QListWidget::item
{
width:40px;
height:40px;
font:bold 20px;
}
QListWidget {
background-color: rgb(255, 255, 255);
}
QListWidget::item:selected:!active {
background-color: rgb(98, 93, 255);
}
QListWidget::item:selected:active {
background-color: rgb(98, 93, 255);
}
QListWidget::item:hover {
background-color: rgba(50, 23, 255, 100);
}
~~~
具体含义可以根据效果就可以看出,如下演示效果:
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-01-15_5698c1c733036.jpg)
当前选择项使用深蓝表示,而鼠标停靠的选择项使用浅蓝表示。
**下面是我软件项目中所有的界面,图标都是使用网络上的图标(若有侵权,请告知):**
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-01-15_5698c1c7411b0.jpg)![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-01-15_5698c1c756451.jpg)
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-01-15_5698c1c769804.jpg)![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-01-15_5698c1c77aa16.jpg)
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-01-15_5698c1c786594.jpg)
<五>–无边框窗口的拖动
最后更新于:2022-04-01 07:11:18
# 【Qt编程】基于Qt的词典开发系列--无边框窗口的拖动
在上一篇文章中,我们讲述了如何进行无边框窗口的缩放与拖动,**而在一些情况下,我们的窗口只需要进行拖动也不需要改变其大小**,比如:QQ的登录窗口。本来在上一篇文章中已经讲述了如何进行窗口的拖动,但是却与窗口的缩放相关的程序放在一起,**下面专门单独分离出来。**
窗口的拖放只涉及到鼠标事件:按下操作、释放操作和移动操作,因此只需要重写这三个函数。由于程序比较简单,并且注释也比较详细,就不作过多介绍。新建一个基类为QWidget的Qt Gui应用程序,**只需修改widget.h和widget.cpp文件如下**:
**1、widget.h文件**
~~~
#ifndef WIDGET_H
#define WIDGET_H
#include <QWidget>//注意我使用的是Qt5,Qt4与Qt5的区别可以参考http://qt-project.org/wiki/Transition_from_Qt_4.x_to_Qt5
#include<QMouseEvent>
namespace Ui {
class Widget;
}
class Widget : public QWidget
{
Q_OBJECT
public:
explicit Widget(QWidget *parent = 0);
~Widget();
QPoint move_point; //移动的距离
bool mouse_press; //鼠标按下
//鼠标按下事件
void mousePressEvent(QMouseEvent *event);
//鼠标释放事件
void mouseReleaseEvent(QMouseEvent *event);
//鼠标移动事件
void mouseMoveEvent(QMouseEvent *event);
private:
Ui::Widget *ui;
};
#endif // WIDGET_H
~~~
**2、widget.cpp文件**
~~~
#include "widget.h"
#include "ui_widget.h"
Widget::Widget(QWidget *parent) :
QWidget(parent),
ui(new Ui::Widget)
{
ui->setupUi(this);
this->setMouseTracking(false);
setWindowFlags(Qt::FramelessWindowHint | Qt::WindowStaysOnTopHint);//设置主窗口无边框
}
Widget::~Widget()
{
delete ui;
}
void Widget::mousePressEvent(QMouseEvent *event)
{
if(event->button() == Qt::LeftButton)
{
this->setMouseTracking(true);
mouse_press = true;
//鼠标相对于窗体的位置(或者使用event->globalPos() - this->pos())
move_point = event->pos();;
}
}
void Widget::mouseReleaseEvent(QMouseEvent *event)
{
//设置鼠标为未被按下
mouse_press = false;
}
void Widget::mouseMoveEvent(QMouseEvent *event)
{
//若鼠标左键被按下
// qDebug()<<"mouse_press="<<event->globalPos();
if(mouse_press)
{
//鼠标相对于屏幕的位置
QPoint move_pos = event->globalPos();
//移动主窗体位置
this->move(move_pos - move_point);
}
}
~~~
<四>–无边框窗口的缩放与拖动
最后更新于:2022-04-01 07:11:16
# 【Qt编程】基于Qt的词典开发系列--无边框窗口的缩放与拖动
在现在,绝大多数软件都向着简洁,时尚发展。就拿有道的单词本和我做的单词本来说,绝大多数用户肯定喜欢我所做的单词本(就单单界面,关于颜色搭配和布局问题,大家就不要在意了)。
有道的单词本:
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-01-15_5698c1c5e6c6a.jpg)
我所做的单词本:
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-01-15_5698c1c616a18.jpg)
很明显,两者的主要区别就是周围的边框问题。你可以对比QQ以前的版本和这几年的版本,就会发现都倾向于下面这种窗口模式。下面我们就说说如何用Qt实现无边框窗口的缩放与拖动。
对于无边框窗口的拖动其实很简单,其基本思想是,在鼠标移动前后记录鼠标的坐标,然后将窗口移动这两个坐标之差的距离即可,具体实现可以看代码,就非常清楚了。下面主要讲讲如何实现鼠标改变窗口的大小,首先,我们将一个窗口分为以下9个区域,其中只有鼠标在22区域时无法改变其形状,不能改变窗口大小。当鼠标在其它区域时,鼠标改变形状并可以改变窗口大小。窗口区域分类如下图:
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-01-15_5698c1c62e774.jpg)
**具体实现如下代码(widget.ui未做任何改变):**
**1、widget.h文件**
~~~
#ifndef WIDGET_H
#define WIDGET_H
#include <QWidget>
#define MARGIN 20//四个角的长度
namespace Ui {
class Widget;
}
class Widget : public QWidget
{
Q_OBJECT
public:
explicit Widget(QWidget *parent = 0);
~Widget();
int countFlag(QPoint p, int row);
void setCursorType(int flag);
int countRow(QPoint p);
protected:
void mousePressEvent(QMouseEvent *event);
void mouseReleaseEvent(QMouseEvent *event);
void mouseDoubleClickEvent(QMouseEvent *event);
void mouseMoveEvent(QMouseEvent *event);
private:
Ui::Widget *ui;
bool isLeftPressed;
int curPos;
QPoint pLast;
};
#endif // WIDGET_H
~~~
**2、widget.cpp文件**
~~~
#include "widget.h"
#include "ui_widget.h"
#include<QMouseEvent>
#include<QDebug>
Widget::Widget(QWidget *parent) :
QWidget(parent),
ui(new Ui::Widget)
{
ui->setupUi(this);
this->setMouseTracking(true);
//设置在不按鼠标的情况下也触发鼠标移动事件,注意QMainWindow的情况:centralWidget()->setMouseTracking(true);
isLeftPressed=false;
curPos=0;//标记鼠标左击时的位置
this->setMinimumSize(400,300);//设置最小尺寸
QCursor cursor;
cursor.setShape(Qt::ArrowCursor);//设置鼠标为箭头形状
// ui->pushButton->setCursor(cursor);//当放在按钮上时,为箭头
// cursor.setShape(Qt::OpenHandCursor);
QWidget::setCursor(cursor);//当放在主窗口上时,为手形
qDebug()<<"h="<<this->height();
setWindowFlags(Qt::FramelessWindowHint);//设置主窗口无边框
qDebug()<<this->minimumHeight();
}
Widget::~Widget()
{
delete ui;
}
void Widget::mousePressEvent(QMouseEvent *event)//鼠标按下事件
{
if(event->button()==Qt::LeftButton)
{
this->isLeftPressed=true;
QPoint temp=event->globalPos();
pLast=temp;
curPos=countFlag(event->pos(),countRow(event->pos()));
event->ignore();
}
}
void Widget::mouseReleaseEvent(QMouseEvent *event)//鼠标释放事件
{
if(isLeftPressed)
isLeftPressed=false;
QApplication::restoreOverrideCursor();//恢复鼠标指针性状
event->ignore();
}
void Widget::mouseDoubleClickEvent(QMouseEvent *event)//鼠标双击 全屏
{
if(event->button()==Qt::LeftButton)
{
if(windowState()!=Qt::WindowFullScreen)
setWindowState(Qt::WindowFullScreen);
else setWindowState(Qt::WindowNoState);//恢复正常模式
}
event->ignore();
}
void Widget::mouseMoveEvent(QMouseEvent *event)//鼠标移动事件
{
int poss=countFlag(event->pos(),countRow(event->pos()));
setCursorType(poss);
if(isLeftPressed)//是否左击
{
QPoint ptemp=event->globalPos();
ptemp=ptemp-pLast;
if(curPos==22)//移动窗口
{
ptemp=ptemp+pos();
move(ptemp);
}
else
{
QRect wid=geometry();
switch(curPos)//改变窗口的大小
{
case 11:wid.setTopLeft(wid.topLeft()+ptemp);break;//左上角
case 13:wid.setTopRight(wid.topRight()+ptemp);break;//右上角
case 31:wid.setBottomLeft(wid.bottomLeft()+ptemp);break;//左下角
case 33:wid.setBottomRight(wid.bottomRight()+ptemp);break;//右下角
case 12:wid.setTop(wid.top()+ptemp.y());break;//中上角
case 21:wid.setLeft(wid.left()+ptemp.x());break;//中左角
case 23:wid.setRight(wid.right()+ptemp.x());break;//中右角
case 32:wid.setBottom(wid.bottom()+ptemp.y());break;//中下角
}
setGeometry(wid);
}
pLast=event->globalPos();//更新位置
}
event->ignore();
}
int Widget::countFlag(QPoint p,int row)//计算鼠标在哪一列和哪一行
{
if(p.y()<MARGIN)
return 10+row;
else if(p.y()>this->height()-MARGIN)
return 30+row;
else
return 20+row;
}
void Widget::setCursorType(int flag)//根据鼠标所在位置改变鼠标指针形状
{
Qt::CursorShape cursor;
switch(flag)
{
case 11:
case 33:
cursor=Qt::SizeFDiagCursor;break;
case 13:
case 31:
cursor=Qt::SizeBDiagCursor;break;
case 21:
case 23:
cursor=Qt::SizeHorCursor;break;
case 12:
case 32:
cursor=Qt::SizeVerCursor;break;
case 22:
cursor=Qt::OpenHandCursor;break;
default:
// QApplication::restoreOverrideCursor();//恢复鼠标指针性状
break;
}
setCursor(cursor);
}
int Widget::countRow(QPoint p)//计算在哪一列
{
return (p.x()<MARGIN)?1:(p.x()>(this->width()-MARGIN)?3:2);
}
~~~
**3、main.cpp文件**
~~~
#include<QtWidgets>
#include "widget.h"
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
Widget w;
w.show();
return a.exec();
}
~~~
**程序运行截图如下:**
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-01-15_5698c1c63e21e.jpg)
当你将鼠标放在窗口的边缘时,鼠标会变化形状,表示可以拖动窗口。由于没有关闭窗口,只能在强制关闭窗口。如果想做到和不同窗口实现最小化和关闭窗口的画,我们可以在窗口左上角放置两个ToolButton,并设置autorise属性,加上图片即可。下面给出使用上面的无边框窗口所做的词典软件的主界面:
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-01-15_5698c1c2daaed.jpg)
<三>–开始菜单的设计
最后更新于:2022-04-01 07:11:14
# 【Qt编程】基于Qt的词典开发系列--开始菜单的设计
这篇文章讲讲如何实现开始菜单(或者称为主菜单)的设计。什么是开始菜单呢?我们拿常用的软件来用图例说明,大多数软件的开始菜单在左下角,如下图:
1、window 7的开始菜单
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-01-15_5698c1c5585bc.jpg)
2、有道词典的主菜单
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-01-15_5698c1c573d6e.jpg)
3、QQ的开始菜单
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-01-15_5698c1c5871c6.jpg)
4、我写的词典软件的开始菜单
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-01-15_5698c1c59e35b.jpg)
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-01-15_5698c1c5b1c1c.jpg)
当你左键单击开始菜单时,就会弹出相应的菜单选项,然后你就可以进行相关操作。**本文只讲如何实现点击按钮,弹出菜单功能,至于点击菜单后的事件需要你自己编写**。当然,关于右击按钮出现菜单的方法,则是要重写qt自带的函数,至于具体操作可以百度。
**要想使按钮实现左键单击弹出菜单,我们需要让按钮继承下面的名为QMenuButton类,类文件如下**:
**1、qmenubutton.h**
~~~
#ifndef QMENUBUTTON_H
#define QMENUBUTTON_H
#include <QToolButton>
class QMenu;
class QMenuButton : public QToolButton
{
Q_OBJECT
public:
explicit QMenuButton(QWidget *parent = 0);
QMenu * getmenu();
QMenu * menu;
signals:
public slots:
void popupmenu();
};
#endif // QMENUBUTTON_H
~~~
**2、qmenubutton.cpp**
~~~
#include "qmenubutton.h"
#include <QMenu>
#include<QRect>
QMenuButton::QMenuButton(QWidget *parent) :
QToolButton(parent)
{
menu = new QMenu(this);
connect(this,SIGNAL(clicked()),this,SLOT(popupmenu()));//点击按钮就弹出菜单
}
QMenu *QMenuButton::getmenu()
{
return menu;
}
void QMenuButton::popupmenu()
{ QPoint pos; //获取按键菜单的坐标
// int x = pos.x();
int y = pos.y();
// pos.setX(x + this->geometry().width()/2);//也可以改变出现菜单的窗口的x位置
pos.setY(y-this->geometry().height());
//返回菜单下面的action不管有没被选中,可以算一个小事件循环
//里面参数的意思是在哪个坐标弹出菜单,这里将自定义的pop按键的坐标作参考,并在其下面弹出菜单
menu->exec(this->mapToGlobal(pos));
}
~~~
**好吧,对于不懂得怎么继承,也嫌麻烦,下面给出一个简单的方法**:首先建立一个Qt Widgets Application,假设取项目名为MenuButton,类名取为Widget,选择继承于QWidget,然后将上面的qmenubutton.h和qmenubutton.cpp文件添加到项目中来。然后打开界面文件widgets.ui拖一个PushButton按钮,**将objectName改为MenuButton**。然后右击MenuButton选择“提升为……”选项,然后在提升的类名称中填写上面我所提到的类的名字QMenuButton,然后选择提升,这样就完成了PushButton继承QMenuButton。接着我们就要实现菜单选项的设计,具体需要用到QMenu类中的函数,在程序中,我给出了注释,大家可以依葫芦画瓢来定制自己的菜单内容。**下面给出具体的实现:**
1、widget.h文件:
~~~
#ifndef WIDGET_H
#define WIDGET_H
#include <QWidget>
namespace Ui {
class Widget;
}
class Widget : public QWidget
{
Q_OBJECT
public:
explicit Widget(QWidget *parent = 0);
~Widget();
private:
Ui::Widget *ui;
};
#endif // WIDGET_H
~~~
**2、qmenubutton.h (前面已给出)**
**3、widget.cpp文件:**
~~~
#include "widget.h"
#include "ui_widget.h"
#include<QMenu>
Widget::Widget(QWidget *parent) :
QWidget(parent),
ui(new Ui::Widget)
{
ui->setupUi(this);
QMenu * menu = ui->MenuButton->getmenu();//获得菜单,并向上面添加菜单
QAction* ToTop =menu->addAction("toTop");//一级菜单
//ToTop->setIcon(QIcon(":/searchwindow/searchwindow/topon_normal.png")); 为ToTop菜单添加图片标识
// connect(ToTop, SIGNAL(triggered()), this, SLOT(on_toTopBtn_clicked()));添加事件,当单击ToTop时,产生相应的事件
QMenu *fontMenu=menu->addMenu("Font");
QAction* FontSize1 = fontMenu->addAction("small");//二级菜单
QAction* FontSize2 = fontMenu->addAction("middle");
QAction* FontSize3 = fontMenu->addAction("large");
}
Widget::~Widget()
{
delete ui;
}
~~~
**4、qmenubutton.cpp(前面已给出)**
**5、main.cpp文件:**
~~~
#include "widget.h"
#include <QApplication>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
Widget w;
w.show();
return a.exec();
}
~~~
**运行结果如下:**
**点击前:**
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-01-15_5698c1c5c0957.jpg)
**点击后:**
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-01-15_5698c1c5d1992.jpg)
****
****
****
<二>–本地词典的设计
最后更新于:2022-04-01 07:11:11
# 【Qt编程】基于Qt的词典开发系列--本地词典的设计
我设计的词典不仅可以实现在线查单词,而且一个重大特色就是具有**丰富的本地词典库**:我默认加入了八个类型的词典,如下所示:
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-01-15_5698c1c324591.jpg)
由于是本人是通信专业,因此加入了华为通信词典、电子工程词典,又由于我喜爱编程,也加入了c语言基本函数词典。下面介绍如何设计本地词典:
## 词典类型的选择
当然是txt格式的最好了,因为我们可以用程序直接进行读取。可是网上词典一般都是用mdx格式、ld2格式的,我无法用Qt来直接读取。最终,经过不断摸索,网上查找,发现我们可以将mdx格式的词典通过软件转化为txt格式的!
## mdx词典的下载
如何获取mdx格式的词典呢?直接百度 mdx 词典就可以发现如下搜索结果:
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-01-15_5698c1c482b94.jpg)
从中选择下载你想要的词典,这里我以http://bbs.meizu.cn/thread-3299845-1-1.html中的第一个mdx词典“三合一汉语大辞典”作为例子。
## mdx词典的查看
由于是mdx格式的,你用记事本打开就会显示乱码。那么怎么查看你下载的词典的内容呢?这时我们可以利用一个可以解析mdx格式的**软件MDict([点击下载](http://download.csdn.net/detail/tengweitw/8588165))**来打开该mdx词典。
举例说明:
假设我下载了一个“三合一汉语大辞典.mdx”,我们首先打开MDict软件来查看该词典的内容。首先选择“词库”--“词库管理”--“加入词典”,从而加入我们下载的词典文件,然后点击“词库”,从下拉列表中选择我们刚才加入的“三合一汉语大辞典”。然后单击左侧栏,就会在右侧栏中显示对应的内容。具体操作图解如下:
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-01-15_5698c1c4a1201.jpg)![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-01-15_5698c1c4ba08d.jpg)
## mdx词典的转换
将mdx格式的词典转化为txt格式需要利用工具MDx词典转化工具([点击下载](http://download.csdn.net/detail/tengweitw/8588171)),只需要按照下图即可转化为txt文件:
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-01-15_5698c1c4cf497.jpg)
用记事本打开该txt文件我们就可以发现下图类似的语言格式:
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-01-15_5698c1c4e0be4.jpg)
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-01-15_5698c1c505def.jpg)
我们以图中第二行为例,aadb为我们要查的单词(注意:我们这里的词典是成语词典,即查找aadb四个拼音首字母所组成的成语),而后面中的即为我们要找的成语,学过网页设计的人应该都知道嗷嗷待哺的意思,这其实是html语言,即将“嗷嗷待哺”变为蓝色字体,正如我们在MDict所看到的一样。而恰好Qt可以识别html语言,因此即使像我这种没有学过html语言的人也不用费心思去了解。
## txt词典的处理
前面我们基本上完成了词典的制作,但是我为了简便起见,我将词典中要查的词和内容分开保存。例如,我将上面的txt文件改名为Chinese.txt(方便编程使用),将其拆分如下名为ChineseA和ChineseB文件:
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-01-15_5698c1c5350d3.jpg)
这样做的好处是,每当我在搜索窗口输入我想要查询的内容时(比如输入aadb),我就只需要打开ChineseA文件,在这里寻找是否存在该词,而不需要打开整个词典(词+内容),这样查找速度会快一点,因为很明显ChineseA的文件的大小要明显小于Chinese文件大小,**还有一点好处,会在后面的系列文章中提到**。如下所示:
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-01-15_5698c1c5460c8.jpg)
关于如何将上面的文件拆分成两个文件,我想应该很简单,我就不在这写出具体的实现过程,只说说思路:首先读取整个文件,每次读取一行,在每一行中,遇到第一个空格就提取出来放入ChineseA.txt中,该行剩余部分放入ChineseB.txt。**值得注意的是**,两个子文件中,对应的内容应该在相同行!
好吧,也许说了这么多,你也嫌太复杂,懒得自己操作,觉得我的本地词典库足够了,那好吧,你可以自己下载我已经处理好了的txt格式的词典文件。链接为:[http://download.csdn.net/detail/tengweitw/8588209](http://download.csdn.net/detail/tengweitw/8588209)
<一>–词典框架设计及成品展示
最后更新于:2022-04-01 07:11:09
# 【Qt编程】基于Qt的词典开发系列--词典框架设计及成品展示
去年暑假的时候,作为学习Qt的实战,我写了一个名为《我爱查词典》的词典软件。后来由于导师项目及上课等原因,时间不足,所以该软件的部分功能欠缺,性能有待改善。这学期重新拿出来看时,又有很多东西都不熟悉了。恰逢这学期课比较少,于是乎想写写关于这个软件制作的一系列文章,对日后重拾该软件时能够较快入手。当然,也非常乐意读者们在该软件的基础上添加一些功能,源代码会在该系列文章写完之后公布。可运行的词典软件的下载地址:[http://download.csdn.net/detail/tengweitw/8548767](http://download.csdn.net/detail/tengweitw/8548767)
**下面先给出软件的架构,文章后面会给出各个功能展示图。**
## 词典的功能模块
1、无边框窗口的拖动、放大缩小
2、系统托盘的显示
3、登录管理
4、用户账号显示
5、置顶、换肤、最小化、关闭按钮
6、调用本地浏览器查词
7、查词历史前进后退按钮
8、查词框、搜索按钮
9、三种查词:网络查词、本地查词、网页查词
10、单词信息显示窗口
11、主菜单:置顶、字体大小选择、匹配模式选择、注销、关于、退出
12、单词本
13、本地词典的选择模块
14、网上学堂
15、网络连通状态标志
16、本地词典使用状态
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-01-15_5698c1c294fa7.jpg)
## 工程目录文件
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-01-15_5698c1c2b77c6.jpg)
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-01-15_5698c1c2cf3a7.jpg)
**下面给出各个文件的大概功能说明:**
1、basicwindow.h、basicwindow.cpp和basicwindow.ui文件定义了Basicwindow类,主要实现的是边框窗口的拖动和缩放,软件中其他的窗口设计大都继承该类
2、completelineedit.h和completelineedit.cpp文件定义了CompleteLineEdit类,该类主要是改进QLineEdit类,可以有下拉列表的显示,软件中功能8就用到该类。
3、libmanager.h、libmanager.cpp和libmanager.ui文件用于生成本地词典的选择界面。
4、localdict.h和localdict.cpp文件定义了LocalDict类,用于读取本地的词典库。
5、logindialog.h、logindialog.cpp和logindialog.ui定义了LoginDialog类,用于管理用户帐号的登录。
6、network.h和network.cpp文件定义了netWork类,用于根据扇贝网的公开API,使用帐号登录扇贝网,进行网络查词。
7、qmenubutton.h和qmenubutton.cpp定义了QMenuButton类,功能11继承了该类,使之可以出现下拉菜单。
8、searchwindow.h、searchwindow.cpp和searchwindow.ui定义了SearchWindow类,用于软件主界面的显示,这个是软件的核心部分。
9、word.h和word.cpp文件定义了Word类,主要用于储存从网络查词获得的单词内容。
10、wordbook.h、wordbook.cpp和wordbook.ui文件定义了WordBook类,用于管理单词本,此部分功能尚未实现。
11、wordmodify.h、wordmodify.cpp和wordmodify.ui文件定义了WordModify,用于单词本中单词的修改功能。
## 部分功能的展示图
**1、主界面**
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-01-15_5698c1c2daaed.jpg)
**2、网络查词**
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-01-15_5698c1c2ef432.jpg)
**3、本地查词**
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-01-15_5698c1c30faa9.jpg)
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-01-15_5698c1c324591.jpg)
**4、网络搜索**
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-01-15_5698c1c341333.jpg)
**5、换肤功能**
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-01-15_5698c1c355b65.jpg)
**6、搜索模式:句首匹配、句中匹配**
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-01-15_5698c1c36e518.jpg)
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-01-15_5698c1c36e518.jpg)
**7、主菜单**
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-01-15_5698c1c38fb03.jpg)
**8、托盘显示**
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-01-15_5698c1c3aadae.jpg)
**9、其他一些截图:**
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-01-15_5698c1c3b87cf.jpg)
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-01-15_5698c1c3cac6f.jpg)
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-01-15_5698c1c3e1a6b.jpg)
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-01-15_5698c1c3f0d9c.jpg)
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-01-15_5698c1c41030f.jpg)
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-01-15_5698c1c42d06f.jpg)
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-01-15_5698c1c440f50.jpg)
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-01-15_5698c1c454271.jpg)
****
****
****
****
前言
最后更新于:2022-04-01 07:11:07
> 原文出处:[基于Qt的词典开发](http://blog.csdn.net/column/details/tengwei.html)
作者:[滕伟](http://blog.csdn.net/tengweitw)
**本系列文章经作者授权在看云整理发布,未经作者允许,请勿转载!**
# 基于Qt的词典开发
> 本系列文章讲述了如何用Qt写一个完整的类似有道词典的词典软件,并且该软件有一些特色功能。对于如何写一个词典具有参考意义!