转载时请注明出处和作者联系方式
作者联系方式:Lutx [email protected]
在Qt 4.6.0和Qt Creator 1.3.0正式发布不久后, 就将原来的Qt开发平台升级到新的平台上来了. 安装的是Qt 4.6.0 msvc版本, 而且在Qt Creator中也可以用MSVC的编译器来编译程序, 使用了一个jom.exe的工具[http://labs.trolltech.com/blogs/2009/03/27/speeding-up-visual-c-qt-builds/], 那编译速度比Visual Studio快的多了. 用了没多久出来一个问题:
用Qt Creator 1.3.0打开以前创建的一个项目文件, 发现Qt Creato就停止响应了, 根本无法加载这个项目. 真郁闷, 难道又回到Visual Studio去吗? 还是去Qt Creator的源码中找找原因吧, 看看能不能解决.
1. 编译生成 Qt Creator
首先从qt网站下载Qt Creator 1.3的源码包, 解压到本地目录(不同于安装的Creator目录以免混淆)下. 用已安装的Qt Creator打开源代码包根目录下的qtcreator.pro项目, 因为需要调试, 设置Build Configuration为Debug模式, 然后Build All生成整个项目. 生成后会在/bin目录下以及/lib/qtcreator/plugins/Nokia目录下生成一组exe和dll文件.
2. 调试查找原因
在Projects页面的第一个选择项Active run configuration中选择"app", [这个不选择则无法启动qtcreator.exe来调试, 默认会是bin.exe, 实际根本没有这个文件], 启动调试程序运行重新编译的qtcreator.exe, 打开刚才那个无法加载的项目文件...... 经过十几次的断点重复调试运行, 找到了导致无响应的程序位置在/src/plugins/qt4projectmanager/qtuicodemodelsupport.cpp中Qt4UiCodeModelSupport::runUic(const QString &ui) const这个函数中. 原始代码为:
void Qt4UiCodeModelSupport::init() { QDateTime sourceTime = QFileInfo(m_sourceName).lastModified(); QFileInfo uiHeaderFileInfo(m_fileName); QDateTime uiHeaderTime = uiHeaderFileInfo.exists() ? uiHeaderFileInfo.lastModified() : QDateTime(); if (uiHeaderTime.isValid() && (uiHeaderTime > sourceTime)) { QFile file(m_fileName); if (file.open(QFile::ReadOnly)) { // qDebug()<<"ui*h file is more recent then source file, using information from ui*h file"<<m_fileName; QTextStream stream(&file); m_contents = stream.readAll().toUtf8(); m_cacheTime = uiHeaderTime; return; } } // qDebug()<<"ui*h file not found, or not recent enough, trying to create it on the fly"; QFile file(m_sourceName); if (file.open(QFile::ReadOnly)) { QTextStream stream(&file); const QString contents = stream.readAll(); if (runUic(contents)) { // qDebug()<<"created on the fly"; return; } else { // uic run was unsuccesfull // qDebug()<<"uic run wasn't succesfull"; m_cacheTime = QDateTime(); m_contents = QByteArray(); // and if the header file wasn't there, next time we need to update // all of the files that include this header if (!uiHeaderFileInfo.exists()) m_updateIncludingFiles = true; return; } } else { // qDebug()<<"Could open "<<m_sourceName<<"needed for the cpp model"; m_contents = QByteArray(); } } bool Qt4UiCodeModelSupport::runUic(const QString &ui) const { QProcess uic; uic.setEnvironment(m_project->environment(m_project->activeBuildConfiguration()).toStringList()); uic.start(m_project->qtVersion(m_project->activeBuildConfiguration())->uicCommand(), QStringList(), QIODevice::ReadWrite); uic.waitForStarted(); uic.write(ui.toUtf8()); uic.closeWriteChannel(); if (uic.waitForFinished()) { m_contents = uic.readAllStandardOutput(); m_cacheTime = QDateTime::currentDateTime(); return true; } else { // qDebug()<<"running uic failed"<<" using uic: "<<m_project->qtVersion(m_project->activeBuildConfiguration())->uicCommand(); // qDebug()<<uic.readAllStandardError(); // qDebug()<<uic.readAllStandardOutput(); // qDebug()<<uic.errorString(); // qDebug()<<uic.error(); uic.kill(); } return false; }
这段代码是处理qt项目中的窗口设计文件(*.ui)的, 大致过程是首先判断ui_*.h文件和ui文件的时间, 如果ui_*.h的时间要比ui文件的时间晚, 则不需要重新生成ui*.h; 如果还没有对应的ui_*.h文件或者是ui_*.h文件的时间比ui文件的时间早, 则需要调用uic来重新生成ui_*.h文件. 停止响应是在runUic的uic.waitForFinished()这行.
开始以为是这几个ui文件的问题导致uic.exe程序运行出错, 因为在qtcreator 1.2.1中加载这个项目时总是出现uic.exe出错的信息, 但当时忽略错误信息后可以继续, 编译也正常就没有深究下去. 于是吧出错的几个ui文件在命令行执行uic.exe来编译, 结果发现都没有问题. 而且几次调试停止响应时正在处理的ui文件不同, 有时候是a.ui, 有时候又是另一个b.ui, 由此觉得应该不是ui文件或uic.exe文件的问题.
然后就发现在调用uic.exe这个进程方式是将ui文件的内容先读出来[runUic中的参数ui], 作为输入流发送给uic.exe进程, 然后读出uic.exe进程的输出流存到m_contents中. 这个处理方式和命令行执行的方式不一样, 命令行的方式是运行"uic.exe -o ui_a.h a.ui"这个命令来生成的.
正是这里进程输入参数流和输出结果流也不知道是哪里没处理好, 导致uic.exe进程死锁.
3. 解决问题
找到了原因, 就有了解决办法. 要从根本上解决输入输出流的死锁问题还是有些麻烦, 况且我还不知道真正的死锁原因. 那么就换一个方法来执行uic.exe进程来避开死锁的问题. 也就是在执行uic.exe进程时用文件方式而不是流的方式, 等执行成功后在从文件中把结果读到m_contents中. 这里增加了一个函数runUicOnEarth(), 代码如下:
// run Uic create ui_*.h file first, and then read it to m_contents // 2009-12-10 Lutx bool Qt4UiCodeModelSupport::runUicOnEarth() const { // prepare parameter list for uic command // uic -o outputfilename uifilename QStringList params; params << "-o"; params << m_fileName; params << m_sourceName; QProcess uic; uic.setEnvironment(m_project->environment(m_project->activeBuildConfiguration()).toStringList()); uic.start(m_project->qtVersion(m_project->activeBuildConfiguration())->uicCommand(), params, QIODevice::ReadWrite); uic.waitForStarted(); if (uic.waitForFinished()) { // uic finished, open ui_*.h and read content to m_contents QFile file(m_fileName); if (file.open(QFile::ReadOnly)) { QTextStream stream(&file); m_contents = stream.readAll().toUtf8(); m_cacheTime = QDateTime::currentDateTime(); return true; } else { qDebug()<<"running uic finished, "<<" using uic: "<<m_project->qtVersion(m_project->activeBuildConfiguration())->uicCommand(); qDebug()<<" but can not open result file: " << m_fileName; } } else { // qDebug()<<"running uic failed"<<" using uic: "<<m_project->qtVersion(m_project->activeBuildConfiguration())->uicCommand(); // qDebug()<<uic.readAllStandardError(); // qDebug()<<uic.readAllStandardOutput(); // qDebug()<<uic.errorString(); // qDebug()<<uic.error(); uic.kill(); } return false; }
同时在qtuicodemodelsupport.h文件中加入函数声明:
bool runUicOnEarth() const;
然后在init函数中调用runUic这段改为:
if (runUicOnEarth()) { // using runUicOnEarth instead // qDebug()<<"created on the fly"; return; } else { // uic run was unsuccesfull // qDebug()<<"uic run wasn't succesfull"; m_cacheTime = QDateTime(); m_contents = QByteArray(); // and if the header file wasn't there, next time we need to update // all of the files that include this header if (!uiHeaderFileInfo.exists()) m_updateIncludingFiles = true; return; }
然后重新编译项目(或只需重新生成Qt4ProjectManager.dll), 载入方才无法打开的项目文件, 一切OK.
转载时请注明出处和作者联系方式
作者联系方式:Lutx <[email protected]>