QT 现在被越来越多的人所喜爱.也有了越来越多的人选择 QT 作为开发Xwindow 下面 GUI 环境的语言. 在 Linux 中,绚丽的 KDE 桌面环境就是用 QT 开发出来的. 现在来看看如何在 QT 下面处理中文.
本文中全部在 QT 3 的环境下运作.如果您用的是比较旧的 QT 版本, 请注意:
Tips: 如果您的 Linux 系统中找不到 findtr3 那么请尝试找 findtr2 或者 findtr
Tips: 如果您的 Linux 系统中找不到 msg2qm2 那么请尝试找 msg2qm
Tips: 可以用 which findtr3 或者 whereis msg2qm2 来寻找您的系统
Tips: 如果您希望在自己的计算机中编译本文中的范例, 您需要安装 g++, qt, qt-devel, make 这些 套件.
下面我们来看一个简单的 QT 程序:
代码:
/* chinese.h */
#include <qapplication.h>
#include <qwidget.h>
#include <qlabel.h>
#include <qlineedit.h>
class Chinese: public QWidget
{
Q_OBJECT
public:
Chinese();
private:
QLabel *label;
QLineEdit *input;
private slots:
void display();
};
这里我们简单的定义了一个基于 QWidget 的 class, 命名为 Chinese 然后我们定义了一个 Label *label 和一个 LineEdit input 并且定义一个
代码:
void slot display()
/* chinese.cpp */
#include "chinese.moc"
#include <iostream.h>
Chinese::Chinese()
{
resize(200,100);
label=new QLabel( "Input Line:", this);
label->setGeometry(10,10,90,30);
input=new QLineEdit(this);
input->setGeometry(10, 40, 180, 30);
input->setFocus();
connect(input, SIGNAL(returnPressed()), this, SLOT(display()));
}
void Chinese::display()
{
QCString string;
string=input->text();
cout<<string<<endl;
}
在 .cpp 文件中. Chinese::Chinese() 中,我们只做了四件最简单的事情 1. 显示一个叫做 Input Line: 的 label 在 10,10 这个位置上. 大小为 90 30. 然后显示一个可以用来输入的 LineEdit 在 10, 40, 大小为 180, 30. 然后告诉程序,当程序打开的时侯,将 focus 用 setFocus() 放在 input ( QLineEdit )上面.也就是说,程序一打开,我们打 keyboard 就可以直接输入到 input 这个 QLineEdit 中. 然后我们告诉程序,当在 input 中接收到 Enter 键的时侯,去呼叫 display() 这个 slot Chinese::display() 中.我们有三个动作. 1. 定义一个 QCString 用作 QT 与 C++ 之间的沟通. string=input-text() 把我们输入在 input 中的 string 抓下来,放到 string 中.然后用 cout 把 string 显示在您的 X 终端模拟中. (rxvt, qterm, xter Konsole 之类的程序)
代码:
/* main.cpp */
#include <qapplication.h>
#include "chinese.h"
main (int argc, char **argv)
{
QApplication a(argc, argv);
Chinese w;
a.setMainWidget (&w);
w.show();
return a.exec();
}
这个就不多说了.是 chinese 的 main 程序.用来显示 chinese 的.
然后建立一个如下的 Makefile:
代码:
INCL= -I$(QTDIR)/include -I/usr/include/kde
CFLAGS= -pipe -O2 -fno-strength-reduce
LFLAGS= -L$(QTDIR)/lib -L$(KDEDIR)/lib -L/usr/X11R6/lib
LIBS= -lkdecore -lkdeui -lqt -lX11 -lXext -ldl
CC=g++
MOC=moc
chinese: chinese.moc chinese.o main.o
$(CC) $(LFLAGS) -o chinese chinese.o main.o $(LIBS)
chinese.moc: chinese.h
$(MOC) chinese.h -o chinese.moc
main.o: main.cpp
chinese.o: chinese.cpp
clean:
rm -f *.o
rm -f *.bak
rm -f *.moc
rm -f chinese
.SUFFIXES: .cpp .h
.cpp.o:
$(CC) -c $(CFLAGS) $(INCL) -o $@ $<
通过 make 指令.我们就可以 build 出一个可执行的文件 chinese 执行 ./chinese 可以看到我们的小程序. 我们在程序中那个可以输入的地方,随便打些东西进去(请输入英文)然后按 enter 键. 我们就会看到我们在程序中输入的那些东西会被显示在我们的 X 终端模拟中.
但是目前这个程序既不能显示中文,同样的也不能接受中文输入. 不相信的话,您可以尝试在那个输入文字的地方打入随便几个中文. 例如 "中文测试" 然后按 Enter 键.您会发现 X 终端模拟中显示的只有 "??" 之类的东西. 原因后面会讲到.
首先,我们第一部需要的是让我门的程序显示中文.相信大家一定都知道, QT 中已经帮我们做好了 i18n 了.只要去用就好. 首先我们需要在 chinese.h 的文件中加入:
#include <qtranslator.h> qtranslator.h 是给 QTranslator 用的 include 文件. 然后在 chinese.cpp 中 Chinese::Chinese 需要改成下面的样子:
代码:
Chinese::Chinese()
{
resize(200,100);
QTranslator translator(this);
translator.load("chinese", ".");
qApp->installTranslator(&translator);
label=new QLabel(tr( "Input Line:"), this);
label->setGeometry(10,10,90,30);
input=new QLineEdit(this);
input->setGeometry(10, 40, 180, 30);
input->setFocus();
connect(input, SIGNAL(returnPressed()), this, SLOT(display()));
}
首先,我们定义了 QTranslator translator 在 this. 然后 translator.load 去载入 chinese.qm 这个文件.这里 .qm 这个全名称我们是不需要的. QT default 就会去找 .qm 了. 所以虽然我们的中文对应的翻译文件全名是 chinese.qm ,但是我们这里只需要打入 chinese 就好了.
qApp->installlTranslator(&translator)就会帮我们把翻译成中文的信息, 加载我们的程序中.
同时, label=new QLabel("Input Line:", this) 要改成下面这样 label=new QLabel(tr( "Input Line:"), this) 这里的 tr 是代表信息需要翻译 (translator)的意思. tr 也是给 findtr 来寻找哪里需要信息翻译的一个标志 (flag)
现在用 make 重新编译我们的程序. 编译完后,我们还需要做出 qm 文件来给 QTranslator 用.
首先,我们需要用到 findtr3 这个程序. findtr3 会帮我们找出程序中所有用到 tr 的地方.同时帮我们自动产生 po 文件.
findtr3 chinese.cpp > chinese.po
现在用您最喜爱的文字编辑器(vim, emacs, joe or other)打开 chinese.po 这个文件,我们会看到:
代码:
# This is a Qt message file in .po format. Each msgid starts with
# a scope. This scope should *NOT* be translated - eg
# from French to English, "Foo::Bar" would be translated to "Pub",
# not "Foo::Pub".
msgid ""
msgstr ""
"Project-Id-Version: PROJECT VERSION/n"
"POT-Creation-Date: 2002-06-22 20时09分00秒 EDT/n"
"PO-Revision-Date: YYYY-MM-DD/n"
"Last-Translator: FULLNAME <EMAIL@ADDRESS>/n"
"Content-Type: text/plain; charset=iso-8859-1/n"
#: chinese.cpp:15
msgid "Chinese::Input Line:"
msgstr ""
这里 findtr3 帮我们找到,在 chinese.cpp 这个程序中的第 11 行,有一处用到 tr 的地方. 而 tr 后面的内容是 Input Line: 还记得我们的 label 是怎么写的吗? label=new QLabel(tr( "Input Line:"), this); 没错, 这里找到的就是 "Input Line:" 这几个字. 下面我们开始翻译 po 文件了
我们真正需要动到的只有两个地方,就是:
"Content-Type: text/plain; charset=iso-8859-1/n"
和下面的 msgstr "" 就足够了.
代码:
#: chinese.cpp:15
msgid "Chinese::Input Line:"
msgstr ""
首先我们需要设定我们是用哪种于言编码来翻译 po 的.同时 qt 也会用这种语言编码来显示我们所翻译的信息.所以我们需要改成:
代码:
"Content-Type: text/plain; charset=big5/n"
或者:
代码:
"Content-Type: text/plain; charset=gb2312/n"
其中 big5 是繁体中文所以用的编码, gb2312 (GBK) 则是简体中文所使用的.
接下来,我就需要更改 msgstr 了:
msgid "Chinese::Input Line:"
msgstr "请输入中文:"
Tips: 注意,如果您是用 gb2312 简体来输入的那么 您需要在 charset 中选择 gb2312. 同样的, big5 繁体输入,就需要选择 charset=big5 您不能用同一种编码的输入法,同时去写两 个 po 文件. 也就是说,不能用 xcin 的繁 体输入,同时去翻译 gb2312 的 po. 也不 能用 chinput 的简体输入来翻译 big5 的 po. 如果您需要同时翻译繁体,简体的 po 那么建议您用您所熟习的语言环境翻译好 一份出来,然后用 big5<=>gb 的转码工具 转出另外一个编码的po文件来.同时记得更 改所转出来的文件中的 charset
当然,如果您愿意,也可以把
"PO-Revision-Date: YYYY-MM-DD/n"
"Last-Translator: FULLNAME <EMAIL@ADDRESS>/n"
加入适当的信息
PO-Revision-Date: 加入您翻译的信息
Last-Translator: 加入您的个人信息以及 e-mail
"PO-Revision-Date: 2002-06-22/n"
"Last-Translator: Goldencat <ruili@worldnet.att.net>/n"
然后存盘,退出.先在我们还需要最后一步,就是把 chinese.po 文件转成 chinese.qm 的格式给程序来读.这时侯我们就用到了一只叫做 msg2qm2 的程序.
msg2qm2 chinese.po chinese.qm
现在我们就算是完成了. 执行我们的文件 ./chinese 看看,那个 "Input Line:" 已经变成中文的 "请输入中文:" 了.
下一步,我们来解决在 QT 中输入中文,然后把中文显示出来的问题.
Tips: QT 中的中文输入也很重要.写一个程序,不光是 表面上看起来信息都变成中文的,就叫做中文化 了.举一个最简单的例子:现在有一个中文的 MySql 数据库.我们的 QT 程序需要正确的输入 查询中文的数据.这就需要 QT 可以正确无误的 接受并且处理我们的中文输入才可以.
在 QT 中,我们有一个 QTextCodec 可以用. QTextCodec 就是专门来为我们处理不同编码的.
为了使用 QTextCodec 我们首先需要在 chinese.cpp 中,把 qtextcodec.h 给 include 进来.
include <qtextcodec.h>
现在来看看我们的 void Chinese::display() 吧.
代码:
void Chinese::display()
{
QString string;
string=input->text();
QTextCodec *codec=QTextCodec::codecForName("big5");
QCString chinese_string=codec->fromUnicode(string);
cout<<chinese_string<<endl;
}
这里我们看到,同样的, 把 input 中我们输入的 string 扔给 string. 下面就用到了 QTextCodec 了.我们可以把 QTextCodec *codec=QTextCodec::codecForName("big5"); 写成两行,这样比较方便看:
代码:
QTextCodec *codec;
codec=QTextCodec::codecForName("big5");
第一行是通过 QTextCodec 建立 codec 第二行呢,告诉 QT 我们的 codec 是用 big5 编码的. (简体中文就要用 gb2312)
最后,我们通过 codec->formUnicode(string) 把 Unicode 转换成我们需要用的 big5 码. ( QT default 全部是用 Unicode,这也就是为什么开始的时侯,如果我们没有通过 QTextCodec 转码,看到的中文都是 ?? 了.因为 Unicode 的标准就是不认得的 code 一律用 ?? 来表示)最后我们定义一个 QCString chinese_string,
代码:
QCString chinese_string=codec->fromUnicode(string);
而 chinese_string就是已经被 codec 转码过的 big5 code 的了. codec->fromUnicode(string) 这里是做转码的动作.
现在重新 make 所 build 出来的 chinese 就已经具备了最基本的中文程序的要求了.现在在输入中文到程序中,您就可以在 X 终端模拟中看到中文了.
那么这个程序已经具备了中文处理能力,为什么还被叫做最基本的呢中文处理呢?因为我们的程序目前并不会判断 locale, 而自动设定相应的编码.现在我们把这个 chinese 的程序作成一个相对完整的中文程序.
QTextCodec 中还给我们提供了一个读取当然于言环境的 function 叫做 locale. QTextCodec::locale() 就会返回当前使用者的 locale. 现在让我们来看看一个相对完整的中文程序:
Tips: 这个程序中对 locale 名称的定义 是按照 RedHat 7.3 中对中文 locale 的定义为标准的,也就是说: 繁体中文 : zh_TW.Big5 简体中文 : zh_CN.GB2312
代码:
/* chinese.h */
#include <qapplication.h>
#include <qwidget.h>
#include <qlabel.h>
#include <qlineedit.h>
#include <qtranslator.h>
class Chinese: public QWidget
{
Q_OBJECT
public:
Chinese();
private:
QLabel *label;
QLineEdit *input;
QString locale;
private slots:
void display();
};
我们这里加入了一个新的 QString locale, 用来做 locale 的判断.
代码:
/* chinese.cpp */
#include "chinese.moc"
#include <iostream.h>
#include <qtextcodec.h>
Chinese::Chinese()
{
resize(200,100);
QTranslator translator(this);
locale=QTextCodec::locale();
translator.load("chinese."+locale, ".");
qApp->installTranslator(&translator);
label=new QLabel(tr( "Input Line:"), this);
label->setGeometry(10,10,90,30);
input=new QLineEdit(this);
input->setGeometry(10, 40, 180, 30);
input->setFocus();
connect(input, SIGNAL(returnPressed()), this, SLOT(display()));
}
void Chinese::display()
{
QString string;
string=input->text();
QTextCodec *codec;
if (locale == "zh_TW.Big5")
codec=QTextCodec::codecForName("big5");
if (locale == "zh_CN.GB2312")
codec=QTextCodec::codecForName("gb2312");
QCString encoded_string=codec->fromUnicode(string);
cout<<encoded_string<<endl;
}
在 chinese.cpp 里面, Chinese::Chinese 我们加入 locale 的判断. 从而自动加载相应的 .qm 文件.
locale=QTextCodec::locale() 就把 QTextCodec 返回的值,也就是当前使用的 locale 传给了 locale. (locale 是一个 QString)然后我们用 translator.load("chinese."+locale, "."); 实际上就是说 chinese.+当然 locale (zh_TW.Big5 或者 zh_CN.GB2312)也就是说,最后我们加载的 locale 是 chinese.zh_TW.Big5 或者是 chinese.zh_CN.GB2312. 这样只要对应不同的 locale 环境翻译不同的 .po 然后制作成基于 locale 名称的 qm, QT 就可以帮我们自动判断在哪个locale 要加载那个文件了. (chinese.zh_TW.Big5.qm 或者 chinese.zh_CN.GB2312.qm)
Tips: QT 在载入 .qm 文件的时侯,如果该文件不存在.那么 就会用程序中的语言来显示.所以不需要为了程序是否 可以找到该 .qm 文件而担心.(不用去写 这方面的 error handle)
而在 void Chinese::display() 中,我们也做一个自动的判断,用来判断使用者是在哪个中文环境中输入中文的. 如果是 zh_TW.Big5 我们就把转码设定为 big5, 如果是 GB2312 就设定为 gb2312. 而如果是其它的,就忽略不管了.
同时在修改一下我们的 Makefile
代码:
INCL= -I$(QTDIR)/include -I/usr/include/kde
CFLAGS= -pipe -O2 -fno-strength-reduce
LFLAGS= -L$(QTDIR)/lib -L$(KDEDIR)/lib -L/usr/X11R6/lib
LIBS= -lkdecore -lkdeui -lqt -lX11 -lXext -ldl
CC=g++
MOC=moc
chinese: chinese.moc chinese.o main.o
$(CC) $(LFLAGS) -o chinese chinese.o main.o $(LIBS)
chinese.moc: chinese.h
$(MOC) chinese.h -o chinese.moc
main.o: main.cpp
chinese.o: chinese.cpp
po:
findtr3 chinese.cpp > chinese.zh_TW.Big5.po
findtr3 chinese.cpp > chinese.zh_CN.GB2312.po
qm:
msg2qm2 chinese.zh_TW.Big5.po chinese.zh_TW.Big5.qm
msg2qm2 chinese.zh_CN.GB2312.po chinese.zh_CN.GB2312.qm
clean:
rm -f *.o
rm -f *.bak
rm -f *.moc
rm -f chinese
.SUFFIXES: .cpp .h
.cpp.o:
$(CC) -c $(CFLAGS) $(INCL) -o $@ $<
在 Makefile 中加入 po 和 qm. 这样我们用 make po 就会产生 chinese.zh_TW.Big5.po 和 chinese.zh_CN.GB2312.po 了.把这两个 po 翻译好以后.用 make qm 就会做出 chinese.zh_TW.Big5.qm 和 chinese.zh_CN.GB2312.qm 这两个 qm 文件.现在试试看.在简体或者繁体的 X 终端模拟环境下.我们的程序会自动的显示相应的翻译信息.也会自动接受相应 locale 下面的中文输入法了.
原文来自:http://laputan2005.spaces.live.com/blog/cns!67c9641c6d6e8ca7!141.entry