《C++.GUI.Programming.with.Qt.4》读书笔记Chapter 16~18

QWidget::setToolTip()用于为Widget设置相应的tip文本。

    同样,QAction::setToolTip()为Action设置相应的tip文本;若没有显式的为Action设置tip文本,Action会自动的使用action text。

    setStatusTip(),该函数为Widget和Action添加 status tip。

    QWidget::setWhatsThis()

    QWhatsThis类,静态函数QWhatsThis::createAction()
   
    QTextBrowser类能解析大量HTML标签,可用于显示基于HTML的文本内容

    Qt Assistant支持索引和文本搜索功能,可以很好的用于提供在线帮助

    要使用Qt Assistant,必须在程序中书写必要的代码让Qt Assistant能察觉到文档的所在。

    Qt程序与Qt Assisant之间的通讯是由QAssistantClient这个类来负责的;该类属于一个单独的类库,要使用该类库,需要在.pro中添加下面一行: CONFIG     +=assistant

    QAssitantClient类的构造函数以文件路径作为首个参数,用于确定 Qt Assistant可执行文件的位置。

    创建 Qt Assistant profile,并写.dct文件来提供文档相关信息。

Chapter 17 Internationalization

    Qt4为国际化内置了很多良好的支持:

    1.Qt的API接口和内部实现均是基于Unicode的
    2.Qt的文本引擎支持所有的non-Latin的书写系统,包括阿拉伯,中日韩,希伯来,印度,泰国等。
    3.Qt的layout 引擎为right-to-left风格的阿拉伯文和希伯来文提供了相应的支持。
    4.某些语言在输入文本时需要使用专用的输入法,QLineEdit和QTextEdit可以和系统中安装的任意输入法协调工作。

    Qt提供了用于文本翻译的GUI工具:Qt Linguist,以及两个辅助命令行程序 lupdate和lrelease。

    对于大多数程序,都是在启动阶段就根据用户的locale setting加载合适的translation file,然而某些情况下用户要求能够在运行时实时的切换界面语言。

Section 1 .Working With Unicode

    QString以Unicode来存储字符串,QString中的每个字符都是一个16-bit的QChar而不是8-bit的char。

    对QString中的某个位置进行赋值操作,可以通过字符方式来确定新值,也可以通过数值方式来确定,例如要将类型为QString的str的首字符设为'A',可以有下面两种方式:

    str[0]='A';
    str[0]=QChar(0x41);

    基于QChar之上的编程不同于基于char。要获得一个QChar变量的编码值,对其调用unicode()函数;要获得一个QChar变量对应的ASCII或Latin-1编码值,对其调用toLatin1(),若原来QChar中存放的是non-latin字符,该函数返回'/0'。

    Qt为QChar类提供了基于Unicode的判断函数,如isPrint(),isSpace(),isLetter(),isNumber()等,其工作与C++标准库提供的isalpha(),isdigit(),isspace()类似。

    Qt负责将Unicode编码的QString正常显式,并在需要和其他系统进行通讯时转换为相关的编码格式。

    默认情况下QTextStream使用系统本地的8-bit编码格式(可通过调用QTextCodec::codecForLocale()获得)用于读写文本文件,对于美国和西欧,这通常意味着使用Latin-1。

    可以调用QTextStream::setCodec()来自定义读写文件时所使用的编码,如stream.setCodec("UTF-16");

    UTF-16格式与QString的内存表示一致,因此使用UTF-16读写Unicode字符串速率会很高,缺点是在存储纯ASCII数据时会有较大的开销。

    setCodec()的参数是一个合适的QTextCodec对象,由其负责完成Unicode和对应编码之间的转换。

    QTextCodec::codecForName()
   
    在读文本文件时,默认情况下QTextStream能够自动检测Unicode编码(依据是0xFFFE,这个Unicode byte order mark);可以通过调用setAutoDetectUnicode(false)来关闭该特性。

    默认情况下,Qt将传递给函数tr()的参数视为Latin-1编码的字符串;可以调用静态函数QTextCodec::setCodecForTr()来改变该默认设置,而自定义编码方式。注意,这必须在第一次调用tr()之前就完成。
   
    然而即便如此,代码中的其它显式字符串仍会被解释为Latin-1字符串;解决方法之一是利用QTextCodec对象的toUnicode()函数,也可以调用QText::setCodecForCStrings()来告知Qt在const char*与QString之间进行转换时采用何种编码。



Section 2 Making Applications Translation-Aware

    要实现程序的多语言化,需要完成两件事情:
        a.确保程序中每个用户可见的字符串都被tr()处理。
        b.确保程序启动时加载translation file(.qm)。

    tr()是在QObject中定义的静态函数,并且在每个使用了Q_OBJECT宏的子类中都被overridden。

    在一个QObject子类代码中,可以不加任何限定符的调用属于该类的tr()。

    tr()返回字符串的一个翻译版本,如果存在的话;否则返回输入文本。

    要准备transaltion file,需要使用Qt提供的lupdate,该工具将代码中所有出现在tr()的可见字符串提取出来并生成待翻译的translation file,这样的translation发送给翻译者来完成翻译工作。

    调用tr()函数的一般形式为

    Context::tr(sourceText,comment);

    其中Context是tr()所属的类,comment是可选参数,用于为翻译者提供附加信息。

    不同Context下的文本串的翻译是独立进行的。

    当在一个全局函数中调用tr()时,必须显式的确定所使用的Context(类)。

    QApplication::translate() 函数完成与tr()函数相同的工作。

    tr()和QApplication::translate()完成双重工作:一方面作为lupdate从中提取用户可见字符串,另一方面又是从translation file中提取对应文本完成转换的C++函数。

    尽管对一个字符串变量而不是字符串调用tr()并不是个好主意,然而实际上也是可以作到的,这需要在将字符串常值赋值给某个字符串变量时调用QT_TR_NOOP()宏,该宏不进行任何操作,只是为lupdate提供标识。

    宏QT_TRANSLATE_NOOP完成同样的工作,不同之处在于参数中可以指定context,这对于初始化类外变量很有用。

    如何能确保程序员在编写代码时将所有用户可见字符串用tr()包裹起来而不出现遗漏呢?可以通过在包含任何Qt头文件之前定义预定义符号QT_NO_CAST_FROM_ASCII这个来告诉Qt禁止从const chat * 到QString的自动转换;通常在.pro文件中添加下面一行:
“DEFINES    +=QT_NO_CAST_FROM_ASCII"来实现这一定义。

    这样就强迫每个字符串常量在使用时必须被tr()或QLatin1String()函数包裹从而转换为QString,这取决于该字符串常量是否需要被翻译。

    静态函数QLocale::system()返回一个QLocale对象,以提供用户的locale信息。

    QTranslator对象只能一次加载一个translation file;通过使用多个QTranslator对象,Qt程序可以安装任意数量的translator,QApplication在寻找合适的翻译时会使用所有已安装的translator。

    QTranslator::load()用于加载translation file(.qm)

    QApplication::setLayoutDirection(),该函数可以改变文本的书写方向。

    QLocale类提供本地化的数字和日期/时间格式。

Section 3 Dynamic Language Switching
   
    当Qt检测到环境变量中的locale设置发生变化时,会创建一个LocalChange event;若要处理该event,需要重新实现QWidget::changeEvent()。

    当QApplication上已安装的QTranslator的内容发生变化时,Qt会创建一个LanguageChange event。

    不要将localChange和LanguageChange两个event混淆,前者的发生是由系统变化导致的,所通知的对象是Qt应用程序;后者的产设姑娘是由Qt自身导致的,所通知的对象是程序的widgets。

Section 4 Translating Applications

    three-step progress:

    a.开发人员运行lupdate程序从源代码中提取用户可见字符串,并创建XML格式的.ts文件

    b.翻译人员运行Qt linguist这个GUI工具,完成文本的翻译工作

    c.开发人员运行lrealease这个工具,声称应用程序使用QTranslator可加载的二进制.qm文件
   
    lrelease的工作就是将可读的.ts文件转换为二进制的.qm文件。
   
    lupdate默认情况下假设所有tr()中的字符串都采用Latin-1编码;若实际情况并非如此,需要在.pro文件中添加CODECFORTR这一项;该工作和在程序中调用QTextCodec::setCodecForTr()二者是缺一不可的。
   
   
Chapter 18 Multithreading


Section 1 Creaing Threads

    Qt中提供多线程的机制很简单:创建QThread的派生类,并重新实现其保护成员函数run()。
   
    QThread::run(),被调用来开始线程的执行,在run()结束时线程终止。
   
    QThread::terminate(),被调用来终止线程的执行,非阻塞操作,并不保证线程的立即终止;可以在调用QThread::terminate()之后调用QThread::wait()来实现同步等待。
   
    terminate()并不是值得推荐结束线程的方法,因为它强制线程终止而不给线程任何情场的机会。
   
Section 2 Synchronizing Threads

    Qt提供的用于线程同步的类包括QMutex,QReadWriteLock,QSemaphore和QWaitCondition
   
    QMutex::lock()        阻塞操作
    QMutex::trylock()    非阻塞操作
    QMutex::unlock()   
   
    QMutexLocker是Qt提供的用于简化Mutex操作的一个类;QMutexLocker的构造函数以一个QMutex为对象,并对其进行lock操作,而析构函数则对其进行unlock操作。
   
    QReadWriteLock可以允许同时进行多个读操作或一个写操作。
   
    QReadWriteLock::lockForRead()
    QReadWriteLock::lockForWrite()
    QReadWriteLock::unlock()
   
    QSemaphore是对Mutex的扩展,与读写锁不同的是,信号量可以用来保护一批相同的资源,而不只是一个。
   
    QSemaphore:acquire(int n=1)
    QSemaphore::release(int n=1)
    QSemaphore::available()
   
    QWaitCondition和QMutex联合使用,可以允许一个线程在某个条件满足时唤醒其他线程,比起单独使用QMutex能实现更精确的控制。
   
    QWaitCondition::wait()的参数是一个状态为locked的QMutex,该函数在阻塞本线程前会将这个QMutex解锁,并在函数返回前对其lock。
   
    thread-local storage (thread-specific data):这里的意思是多个线程都要访问同一个全局变量,然而对于不同线程,全局变量中的值是不同的。
    较好的实现方法是使用QThreadStorage类,该类常用来实现cache,这样可以避免使用mutex时lock,unlock以及等待带来的开销。
   
    由于某些编译器的问题,QThreadStorage中只能存放指针。
    QThreadStorage::hasLocalData()
    QThreadStorage::setLocalData()
   
Section 3.Communicating with MainThread
   
    当Qt程序运行时,主线程是唯一的线程,并且是唯一允许创建QApplication或QCoreApplication对象并对其调用exec()的线程。在调用exec()之后,主线程要么是在等待event的发生,要么是在处理一个event。
   
    主线程可以通过创建QThread的子类来开始新线程。
   
    之前介绍的mutex,read/write lock,semaphore等均可用于新线程之间的通讯,但是却不能用于和主线程的通讯,因为这会导致主循环的event loop被阻塞并"冻结"UI。
   
    解决方案是在主线程与新线程之间跨线程的使用signal-slot机制。
   
    通常情况下,signal-slot机制是同步工作的,这意味着当signal被emit时,与之想联系的slot会被立即调用。
   
    然而,当该机制用于将不同线程中的object连接起来时,则变为异步机制。这样的连接是在底层是通过创建并传递event来实现的;slot被signal的接受对象所在的线程的event loop所调用。
   
    默认情况下,一个QObject对象存在于其被创建的线程之中;这可以在任何时候调用QObject::moveToThread()被改变。
   
Section 4 Using Qt's Classess in Secondary Threads

    Thread-safe & Reentrant
   
    注意留意这两个概念应用在函数和类之上的不同。
   
    对于类,如果它的所有函数都可以被不同线程同时调用而不相互影响——即使这些调用是针对同一个类对象,那么该类被定义为thread-safe。
    对于类,如果其不同实例可以在不同线程中被同时使用而不相互影响,那么该类被定义为reentrant;然而,不同线程中同时访问同一个reentrant类对象,并不是安全的,这样的访问需要用mutex进行保护。
   
    在Qt的定义中,对于类这个层次,thread-safe是比reentrant更严格的要求,这和在函数层次上的关系正好相反。
   
    通常情况下,C++的类,只要不使用全局或其他共享变量,就是reentrant的。
    Qt中大多数non-GUI的类,是属于reentrant的。
   
    QObject是reentrant的,但是需要注意以下几点:
   
    a.子QObject必须在父QObject所属的线程中被创建,这意味着在非主线程中的对象在创建是不允许以QThread作为parent,因为后者是在主线程或另外一个非主线程中被创建的)。
    b.在删除一个QThread对象前,必须将对应线程中创建的所有对象都销毁。
    c.对象必须在其被创建的线程中被删除。

     如果需要删除存在于另一个线程中的对象,必须调用线程安全的QObject::deleteLater()函数,该函数会发送一个"defered delete" event。
    
     QWidget及其子类不是reentrant的。

你可能感兴趣的:(c++,读书,qt,translation,multithreading)