QT菜鸟问题

来源:互联网 发布:手机怎么查看mac地址 编辑:程序博客网 时间:2024/05/17 20:26

1.     在公司XP3上跟家里WIN7上同样VS2008+QT4.8.5+VSAddin1.1.1搭建的环境,这是前提,结果在公司创建的工程用移动硬盘带回来在家里竟然打不开,各种提示没权限,还以为是工程设置的问题或者编译器问题,最后发现在WIN7下要用管理员权限打开才行,各种坑爹(QTCreator同样)。

2.     初用QT首先面临的是工程配置与QT编译链接过程的各种概念。

        (1)QT中的构建相当于VS的Build(编译+链接),qmake完成的只是为编译生成makefile的工作。

        (2)工程配置时如果是使用一个独立工程则此工程pro文件中头文件与源文件配置以工程所在目录为基准进行配置,qmake产生的makefile文件、构建产生的obj文件和最终产生的exe\lib\dll文件则以项目的构建目录为基准进行配置。

        (3)QT中使用附加包含目录的方法是在pro文件中加入“INCLUDEPATH += ../path”,效果同VS种配置工程附加包含目录。

        (4)在工程文件pro中TEMPLATE变量的使用

变量TEMPLATE描述了为建立目标文件而采用何种模板,即生成何种形式的Makefile文件。Qmake
工具定义了5种模板:
1. 应用程序App,为建立一个Qt应用程序创建Makefile文件;
2. 库lib,为建立引用程序库而创建Makefile文件;
3. 子工程 subdirs,为建立子目录下的目标文件创建一个Makefile文件,
子目录通过变量SUBDIRS指定(子目录下的工程文件也需要在各自的pro文件中指出使用何种模板);
4. VC应用程序vcapp,为Visual Studio 生成一个应用程序工程,仅仅用于Windos操作系统.
5. VC库vclib,为Visual Studio生成一个应用程序库工程,仅仅用于Windows操作系统.

{

app – 建立一个应用程序的makefile。这是默认值,所以如果模板没有被指定,这个将被使用。

lib – 建立一个库的makefile。(结合CONFIG  += staticlib/dll/plugin等使用

vcapp – 建立一个应用程序的Visual Studio项目文件。

vclib – 建立一个库的Visual Studio项目文件。

subdirs – 这是一个特殊的模板,它可以创建一个能够进入特定目录并且为一个项目文件生成makefile并且为它调用make的makefile。(用subdirs组织在一起的子项目将在subdirs项目指定的构建目录的子目录下生成各自makefile文件)

“app”模板
“app”模板告诉qmake为建立一个应用程序生成一个makefile。当使用这个模板时,下面这些qmake系统变量是被承认的。你应该在你的.pro文件中使用它们来为你的应用程序指定特定信息。

}

       (5)pro文件中变量要点:

DEFINES – 应用程序所需的额外的预处理程序定义的列表,用来添加程序需要的预定义宏命令。

INCLUDEPATH - 应用程序所需的额外的包含路径的列表(include文件路径列表)。

DEPENDPATH – 应用程序所依赖的搜索路径(描述了建立应用程序所依赖的其他文件所在的路 径)。

    (6)qmake -project 可以生成pro文件(可以根据项目需要,手动去编辑修改文件)
qmake 可以生成Makefile文件
用make命令 编译(linux下用make,windows下用nmake

使用qmake -project时,会把本目录及其子目录内所有.cpp .h文件加入到项目输入文件中,使用时注意移去其他无用的文件。
qmake生成的Makefile文件,可以根据需要做相应修改。

 

 (7)在工程文件中使用UI_DIR = path来指定uic工具产生ui文件的目录,并且要注意编译时的包含路径包含此目录。

 (8)菜单设计时保证同一层菜单具有同一个加速键即可。注意菜单使用的图标最好检查好不带背景色的(在PS中打开无背景,否则影响效果)。对图标资源最好按菜单进行分类管理。

(9)QT中的QString内部使用Unicode编码,但如果程序源文件不是采用Unicode编码的话直接使用QString str = “中文”就会产生乱码,因为这事“中文”被按照Unicode编码写入QString(QString)默认这样做,但实际上“中文”并不是Unicode编码的char类型。

处理方法是为QString设置默认的编码模式:

QTextCodec::setCodecForCStrings(QTextCodec::codecForName("GB2312"));
QTextCodec::setCodecForCStrings(QTextCodec::codecForName("UTF-8"))

或在位QString赋值时直接指定编码模式:

QString QString::fromAscii ( const char * str, int size = -1 )
QString QString::fromLatin1 ( const char * str, int size = -1 ) 
QString QString::fromLocal8Bit ( const char * str, int size = -1 )
QString QString::fromUtf8 ( const char * str, int size = -1 )

(10)QT中用于国际化的tr并不是按QT文档中所写定义为QObject的静态函数,而是放在Q_OBJECT宏定义中,因此要使用tr要在文件中引入该宏定义。注意这里对于tr的使用同样存在unicode编码问题,因此可能需要为tr设置默认编码模式:

    QTextCodec::setCodecForTr(QTextCodec::codecForName("utf-8"));    QTextCodec::setCodecForTr(QTextCodec::codecForName("GB2312"));

       (11)在pro工程文件中设置源码使用的编码方式及TR使用的默认编码:

                  CODECFORSRC = UTF-8    #在MSVC编译环境下源码默认不使用UTF-8,所以在MSVC环境下需要设定源码使用UTF-8,与TR保持一致

                  CODECFORTR = UTF-8

             在pro中设置源文件的编码格式为UTF-8则uic生成的ui_*.h文件为UTF-8编码,如果设置源文件编码为GBK则生成文件使用GBK编码。

         (12)对使用lrelease发布产生侧.qm文件将与工程设置中指定的ts文件处于同一路径下.

对于不是QObject的类中,可以使用QObject类前缀QObject::tr()或添加Q_DECLARE_TR_FUNCTIONS()宏定义的方式把tr()函数添加到该类中。

下面是几种tr()的错误使用方法:

         const char *appName = "OpenDrawer 2D";

         QString translated = tr(appName);//不能提取

   

         statusBar()->showMessage(tr("Host" + hostName + "found"));//传递给tr的值会有变化,不能提取

          解决方法是:使用QString::arg()

          statusBar()->showMessage(tr("Host %1 found").arg(hostName));//正确提取

 

          (13)尽管对变量调用tr()通常是非常不明智的,但的确可以让它正常工作。在把一个字符串文字赋值给一个变量之前,为了能够翻译它,必须使用QT_TR_NOOP()宏先标记它。对于静态字符串数组来说,这样做非常有用。

eg:

        void OrderForm::init()

       {

          static const char *const flowers[] = {

          QT_TR_NOOP("Medium Stem Pink Roses"),//QT_TR_NOOP宏标识lupdate将会提取包含其中的字符串。

          QT_TR_NOOP("One Dozen Boxed Roses"),

          0

           }

        for (int i=0;flowers[i];++i)

             comboBox->addItem(tr(flowers[i]));

       }

       (在类之外初始化这样的变量应使用QT_TR_NOOP()宏,这个宏带有上下文作为参数,这里上下文应与后面使用tr或translate时使用的上下文保持一致。)

          (14)在.pro文件中添加DEFINES += QT_NO_CASE_FROM_ASCII可以强制每一个字符串文字都需要使用tr()或者QLatin1String等封装,否则产生编译错误,从而迫使我们添加被遗漏的tr()或者QLatin1String()等调用

            在QT5中已经去掉了QTextCodec::SetCodecFroTr()等方法,因此应尽量使用UTF-8的编码方法来保持向后兼容。

            (15)文件中对资源引用时应使用":/frefix/srcPath"的方法(冒号不能丢,先加上在qrc文件中定义的前缀prefix,再加上资源相对qrc文件的路径srcPath)


              (16) 使用信号槽机制要求类具有从QObject的继承关系,可以考虑使用多重继承,但多重继承时需要QObject类在第一个父类位置。新增信号与槽之后最好重新执行qmake,否则可能出现连接错误。

              (17)通常使用的connect,实际上最后一个参数使用的是Qt::AutoConnection类型:
Qt支持6种连接方式,其中3中最主要:
Qt::DirectConnection(直连方式)
当信号发出后,相应的槽函数将立即被调用。emit语句后的代码将在所有槽函数执行完毕后被执行。(信号与槽函数关系类似于
函数调用,同步执行)
Qt::QueuedConnection(排队方式)
当信号发出后,排队到信号
队列中,需等到接收对象所属线程的事件循环取得控制权时才取得该信号,调用相应的槽函数。emit语句后的代码将在发出信号后立即被执行,无需等待槽函数执行完毕。(此时信号被塞到信号队列里了,信号与槽函数关系类似于消息通信,异步执行)
Qt::AutoConnection(自动方式)
Qt的默认连接方式,如果信号的发出和接收这个信号的对象同属一个线程,那个工作方式与直连方式相同;否则工作方式与排队方式相同。

           (18)在QT5.1.1中使用QMap的迭代器iterator与const_iterator时应注意对非const类型QMap获取迭代器应使用iterator,对const类型QMap获取迭代器应使用const_iterator,否则编译不通过。

        (19)对于信号槽机制要注意,多次执行对同一对信号与槽的connect而不进行disconnect会导致槽函数多次执行,连接多少次则槽函数将执行多少次。

        (20)对于信号槽机制,如果槽函数所在的对象在信号发射时已经销毁,则槽函数不能得到执行;并且,即时槽函数所在对象有多次的销毁与重新生成,只要在信号发射时槽函数对象有效,那么槽函数执行次数将与连接次数相同。

        (21)QThread::start()函数用于启动线程,但注意,手册上有一句“If the thread is already running, this function does nothing.”

       (22)Qt中变相获取主窗口指针的方法:在自己的界面主类中调用setAccessibleName(const QString & name),然后在需要界面主类指针的地方调用QWidgetList QApplication::allWidgets()返回QWidgetList对象,遍历QWidgetList对象,调用accessibleName()方法得到QString值,并用主类中设置的name值进行比较久得到主类的指针了。

      (23)Qt中使用Model/View结构显示数据时要注意,如果模型未变,重新设置模型中数据导致现在的行列数等发生变化后如果不调用beginResetModel()和endResetModel()的话界面是不会自动更新的。必须如下做:

void ServerTableModel::SetRootNode(ServerNode *rootNode)
{
    Q_CHECK_PTR(rootNode);
beginResetModel();//更新模型,导致视图更新,不更新模型视图不会更新
    m_rootNode = rootNode;
endResetModel();
}

      (24)Qt中的QList<Type>在进行插入与删除时不要使用迭代器进行,因为使用迭代器进行插入删除后不能保证迭代器为有效值,因为QList的内部实现是一个高效的Vector而不是链表;如果要用迭代器进行插入删除操作可以使用QLinkedList<Type>容器,其内部实现为链表结构,可以保证迭代器插入删除后仍然后效。例如:

QList<QPointer<ServerNode> >::iterator itor = m_parent->m_childList.begin();
while(itor != m_parent->m_childList.end())
{
QPointer<ServerNode>node = *itor;
if (node == this || node == 0)
{
//Removes the item associated with the iterator pos from the list, 
//and returns an iterator to the next item in the list (which may be end()).
itor = m_parent->m_childList.erase(itor);
continue;
}
else
{
itor++;
}
}

当然,更好的方法是不使用迭代器进行插入与删除:

int i = 0;
while (i < m_parent->m_childList.count())
{
QPointer<ServerNode> node = m_parent->m_childList.at(i);
if (node == this || node == 0)
{
m_parent->m_childList.removeAt(i);
i = 0;
}
else
{
i++;
}
}

       (25)使用Qt信号槽控制连接类型时要注意Qt::DirectConnection会导致槽函数在信号发送的线程中得到调用,如果在子线程中发送信号而在槽函数中包含了创建GUI元素(例如创建对话框等)则会引起在“非GUI线程中创建GUI元素的错误”。为了避免这种错误,对跨线程的信号槽通常使用Qt::AutoConnection方法连接。


待续ing...