历届刘雯 维多利亚的秘密密的fancy bar

当前访客身份:游客 [
For opensource software
:还可以!
:引用来自“wang_pu”的评论求完整代码,大神Ope...
:求完整代码,大神
:楼主!!!求代码!!
:这么好的文章竟然没有人顶,希望作者继续出啊.
:引用来自“fsx92”的评论 我现在在看qt creator源...
:我现在在看qt creator源代码,我想在qt creator最...
今日访问:2
昨日访问:3
本周访问:5
本月访问:85
所有访问:6518
列表模式: |
int DotMulti(POINT p1, POINT p2)
return p1.x * p2.x + p1.y * p2.y;
bool IsBetween(POINT ptA, POINT ptB, POINT ptC, POINT ptTest)
POINT v0 = { ptB.x - ptA.x, ptB.y - ptA.y };
POINT v1 = { ptC.x - ptA.x, ptC.y - ptA.y };
POINT v2 = { ptTest.x - ptA.x, ptTest.y - ptA.y };
float iTemp11 = DotMulti(v1, v1);
float iTemp20 = DotMulti(v2, v0);
float iTemp10 = DotMulti(v1, v0);
float iTemp21 = DotMulti(v2, v1);
float iTemp00 = DotMulti(v0, v0);
float m = iTemp00 * iTemp11;
float n = iTemp10 * iTemp10;
if (fabs(m - n) == 0)
float u = iTemp11 * iTemp20 - iTemp10 * iTemp21;
float v = iTemp00 * iTemp21 - iTemp10 * iTemp20;
return (m - n) & 0 ? (u & 0 && v & 0) : (u & 0 && v & 0);
发布于 2年前,
阅读(12) | 评论(0) |
投票(0) | 收藏(0)
DesignNet插件是Totem插件之一,主要用于辅助开发数字图像处理、机器学习、模式识别相关算法。具有快速验证、模块化插件化开发的优点。类似软件包括:Mindjet(思维导图),ImageNet, voreenve等。 效果图如下:
OpenCV2.4.4 设置环境变量OPEN_CV,值为opencv build路径如:F:\SDKs\opencv2.4.4\opencv\build\install
Visual Studio 2010 professional
&!-- lang: shell --& git clone
切换到DesignNet1分支,如下图所示
Root //!& 根目录
|—src //!& 存放所有的源代码
|—app //!& 主应用程序,由该应用程序启动,并加载插件
|—libs //!& 所有的库文件
|—Aggregation //!& 集合工具库(类似于C/C++中的Union)
|—algrithom //!& 通用算法
|—CustomUI //!& UI相关类(包含一些自定义控件)
|—extensionsystem //!& 插件管理器、对象池等
|—GraphicsUI //!& 使用Qt GraphicsView framework 的控件相关类
|—Utils //!& 工具辅助类
|—plugins //!& 所有的插件
|—coreplugin //!& 核心插件
|—designnetplugin //!& DesignNet相关插件
|--designnet_core //!& DesignNet核心插件
|—tools //!& DesignNet子插件
|—welcome //!& 欢迎界面
|—shared //!& 不同项目之间共享的类
|—test //!& 用于测试部分库中的类的功能
|—Win32 //!& binary file 和部分资源文件
|—Debug //!& 可执行文件所在路径
|—libs //!& 库文件生成路径
|—medias //!& 资源路径
|—plugins //!& 插件路径
|—skins //!& 皮肤文件
|—CHANGELOG //!& 变化记录
|—README.md //!& License
|—Totem.sln //!& 项目解决方案
DesignNet相关插件的开发都是在src-&plugins-&designnetplugin-&tools 下进行开发。
DesignNet的结构组成
|—&testblock
|—&Normal
DesignNet的每一个文档都是由一个DesignNetSpace来对应的,每一个DesignNetSpace又是有多个处理器Processor组成,Processor又由端口(Port)和属性Property组成。
具体开发方法
一般开发是在原解决方案中的tool中添加Qt Library工程就可以了,(当然,可以在自己定义的任意路径下开发,设置相关路径,然后将生成的插件拷贝到Win32-&plugins下就可以了)。这里以在原解决方案中添加工程。
1. 添加TestPlugin工程到Tools文件夹(需要安装Qt4Add-In,就会有该向导)。
2. 修改项目属性,并添加XML支持。
如图所示修改相关设置:
添加Include路径,包括:$(SolutionDir)src\shared;$(SolutionDir)src\libs,$(SolutionDir)src\plugins\designnetplugin\designnet_core
&$(ProjectName).pluginspec& &$(SolutionDir)$(Platform)\plugins\&
&$(Configuration)\$(TargetName).dll& &$(SolutionDir)$(Platform)\plugins\&
&$(Configuration)\$(TargetName).lib& &$(SolutionDir)$(Platform)\libs\&
修改Linker-&OutputFile为$(Configuration)\$(TargetName).dll。 添加$(SolutionDir)$(Platform)\libs到Linker-&Additional Library Directories。 在Build Events选项卡中Command Line 添加如下三项:
可根据其他已有的工程里设置工程的Properties。
3. 修改代码
修改TestPlugin继承关系,并实现
virtual bool initialize(const QStringList &arguments, QString *errorString);
virtual void extensionsInitialized();
在TestPlugin.cpp中添加导出宏:Q_EXPORT_PLUGIN2(TestPlugin, TestPlugin)(当然,要包含相应头文件) 4. 拷贝其他Tool中的*.pluginspec到TestPlugin工程的目录下。可以修改*.pluginspec下的相关信息。
5. 编译即可生成可以被Totem插件识别的插件了。
6. 添加处理器类Test,并继承自DesignNet::ProcessorGraphicsBlock。代码如下:
#include &QObject&
#include &designnetplugin/designnet_core/graphicsitem/processorgraphicsblock.h&
#include &designnetplugin/designnet_core/designnetbase/port.h&
#include &designnetplugin/designnet_core/data/imagedata.h&
class Test : public DesignNet::ProcessorGraphicsBlock
Test(DesignNet::DesignNetSpace *space, QGraphicsItem *parent = 0);
virtual Processor* create(DesignNet::DesignNetSpace *space = 0) const;
virtual QString title() const;
virtual QString category() const;//!& 种类
virtual bool process();
//!& 处理函数
protected:
virtual void dataArrived(DesignNet::Port* port);
//!& 数据到达
virtual void propertyChanged(DesignNet::Property *prop);
virtual bool connectionTest(DesignNet::Port* src, DesignNet::Port* target);
DesignNet::Port m_inputP
设置Block图标
在Test构造函数中添加setIcon()即可。
一个处理器的属性将会在属性列表中显示出来。在Test构造函数中,首先构造自己需要的属性如:PathDialogProperty prop(路径选择属性), 添加addProperty(prop)就可以了。
如下代码展示的是构造一个拥有Image类型数据的端口,该Image类型是RGB类型的数据, 端口为输出类型端口。
m_outPort(new ImageData(ImageData::IMAGE_BGR, this), Port::OUT_PORT)调用addPort(&m_outPort)就可以啦。
设置处理器种类
处理器种类是根据字符串中的’/’符号进行解析的,也就是,在处理器列表中将会根据’/’来建立分类层次。如在category()中返回”miao/testblock/Normal”,将会把该处理器放在如下结构中,叶子节点就是处理器:
|—&testblock
|—&Normal
|—&test启动处理器,开始执行
在数据准备好时,也就是在dataArrived()调用了指定次数,指定端口数据都到已经达时,可以调用setDataReady(true);系统将会自动开辟一个线程去执行process()函数。
属性修改事件
当用户在UI上修改某一项属性时,将会调用到函数propertyChanged()当中。
先写这么多,以后还要在修改很多东西呢。
发布于 2年前,
阅读(105) | 评论(0) |
投票(0) | 收藏(0)
真的非常高兴自己也在做开源软件,虽然只是尝试,有很多规范不懂(比如License、github、version、CHANGELOG等)。慢慢来吧,将自己的小项目真正的开源起来。
Totem插件系统实际是使用Qt Creator的插件机制,内部使用了很多的Qt Creator 的源码,并做了部分修改。由于Qt Creator的plugin framework中很多都将编译器的东东加进去了,所以进行了简单的剥离。包含了一个插件系统应该有的基本功能(插件管理器、对象池等),保留了创建文件的向导的功能(个人认为这种向导还是很有用的)。UI上也有不少的Qt Creator的影子,以后再进一步修改。内部注释目前是中文注释,以后会修改为英文注释。遵循GNU LGPL协议。 该插件系统使用Core插件作为整个应用的基础,提供主要的Mode切换、Context等功能(这些概念在《阅读QtCreator》系列博客中提到)。
下载地址:
Totem插件系统的目标是简化应用程序的开发步骤,并提供常用的功能API,让程序员的生活好过些。
1. Michael Miao (:
多提建议哦,同志们。
发布于 2年前,
阅读(119) | 评论(0) |
投票(0) | 收藏(0)
在OpenCV2.4.2中有一个FaceRecognizer类,该类用于完成人脸识别,目前其实现的方法有PCA,LDA,LBP三种方法。这里仅对学习其PCA方法进行一下总结。
简单来说,PCA方法就是将图像投影到训练集经过K-L变换所得到的特征空间,然后在投影空间中计算距离,最近的也就是预测到的class了。PCA可以将高维数据降到低维而保证丢失的信息量最少。低维数据也就方便的计算,加快了计算速度。
1. 数学上的事
所有事情只要一牵扯到数学就变成公式一片片的了,但是不看公式又是心里没底,没有办法。数学就是很重要!这里假设有n个向量,每个向量有m维,现在这么多向量我想用另一个向量t来表示,那么就可以用一个较低维的变换矩阵乘以t来标识,也就是压缩成功了!来上证明!假设误差函数为J(t),那么
最后的结果是通过多步推倒而来。可以看到后项与t没有关系,要想让误差函数最小,t就应该取(平均值)。 现在不用简单的一个向量t来表示原来的那n个向量,令每一个向量都对应一个值,如下:
其中是单位向量,几何意义就是每一个向量都对应直线上的一个点(我感觉就是原来t的元素,其实还是向量t),将作为新的坐标。代到J里得:
对求微分就可以得到,其实就是方向向量,那么新的坐标就是原始向量平移后,然后投影到方向上就得到了新的坐标。那么什么方向是最合适的呢? 再将放到J中,得到
其中就是n个原始向量()的协方差矩阵。让J最小,就是让最大。用拉格朗日乘数法:
得到:。可以看到这个就是求S的特征值与特征向量。 &&&&&&& 到此为止,n个原始向量可以用S的特征向量来表示,当然为了压缩空间,就不用全部的特征向量,用特征值最大(方差最大的)的几个就行了。现在从这么一大堆推倒的最后来看,计算的时候给定原始的n个向量, 1、求出协方差矩阵S 2、对协方差矩阵S求特征向量与特征值。 3、取前q个特征值对应的特征向量 4、求新的向量。
然后求距离就可以了。
2.OpenCV中在人脸识别中的实现分析
FaceRecognizer在OpenCV2.4.2是人脸识别的基类,PCA,LDA,LBP三种不同的方法继承于它。PCA相对应的类是Eigenfaces,在文件modules/contrib/src/facerec.cpp中可以找到他的实现。 一组成员变量:
int _num_//对应“数学上的事”中所提到的q个主成分。
vector&Mat& _//原始向量投影后的坐标
Mat _//每幅图像的标签,用于分类
Mat _//特征向量
Mat _//特征值
Mat _//均值
三个重要的方法:
train 用来对样本进行训练,predict的第一个函数重载是预测给定图像的class,第二个重载会返回相似程度(也就是距离)到dist中。
2.1 train方法
void Eigenfaces::train(InputArray _src, InputArray _local_labels) {
//首先进行了有效性检查,检查_local_labels类型,_src类型等
// get labels
Mat labels = _local_labels.getMat();
// observations in row
Mat data = asRowMatrix(_src, CV_64FC1);//将_src中存放的图像列表中的每幅图像(reshape成1行)作为data的一行
// number of samples
int n = data.
// assert there are as much samples as labels
if(static_cast&int&(labels.total()) != n) {
string error_message = format(&The number of samples (src) must equal the number of labels (labels)! len(src)=%d, len(labels)=%d.&, n, labels.total());
CV_Error(CV_StsBadArg, error_message);
// clip number of components to be valid
if((_num_components &= 0) || (_num_components & n))
_num_components =
// perform the PCA
PCA pca(data, Mat(), CV_PCA_DATA_AS_ROW, _num_components);
// copy the PCA results
_mean = pca.mean.reshape(1,1); // store the mean vector
_eigenvalues = pca.eigenvalues.clone(); // eigenvalues by row
transpose(pca.eigenvectors, _eigenvectors); // eigenvectors by column
labels.copyTo(_labels); // store labels for prediction
// save projections
for(int sampleIdx = 0; sampleIdx & data. sampleIdx++) {
Mat p = subspaceProject(_eigenvectors, _mean, data.row(sampleIdx));
_projections.push_back(p);
可以看到PCA这个类完成计算,输入是一个矩阵和主成分个数,得到均值,特征值和特征向量。然后转置特征向量为列向量,然后使用subspaceProject进行一下投影,对应上一部分的第4步。PCA这个类的实现在modules/core/src/matmul.cpp中,其内部调用normalize对特征向量进行了归一化。 2.2 predict方法
void Eigenfaces::predict(InputArray _src, int &minClass, double &minDist) const {
// get data
Mat src = _src.getMat();
//有效性检查
// 投影到PCA的主成分空间
Mat q = subspaceProject(_eigenvectors, _mean, src.reshape(1,1));
minDist = DBL_MAX;
minClass = -1;
//求L2范数也就是欧式距离
for(size_t sampleIdx = 0; sampleIdx & _projections.size(); sampleIdx++) {
double dist = norm(_projections[sampleIdx], q, NORM_L2);
if((dist & minDist) && (dist & _threshold)) {
minClass = _labels.at&int&((int)sampleIdx);
OK,先写这么多,高手拍砖。
发布于 3年前,
阅读(2287) | 评论(4) |
投票(0) | 收藏(1)
&&&&的文章中谈到,和部门老大一宁出去outing的时候,他给了我相当多的机器学习的建议,里面涉及到很多的算法的意义、学习方法等等。一宁上次给我提到,如果学习分类算法,最好从线性的入手,线性分类器最简单的就是LDA,它可以看做是简化版的SVM,如果想理解SVM这种分类器,那理解LDA就是很有必要的了。
&& 谈到LDA,就不得不谈谈PCA,PCA是一个和LDA非常相关的算法,从推导、求解、到算法最终的结果,都有着相当的相似。
&& 本次的内容主要是以推导数学公式为主,都是从算法的物理意义出发,然后一步一步最终推导到最终的式子,LDA和PCA最终的表现都是解一个矩阵特征值的问题,但是理解了如何推导,才能更深刻的理解其中的含义。本次内容要求读者有一些基本的线性代数基础,比如说特征值、特征向量的概念,空间投影,点乘等的一些基本知识等。除此之外的其他公式、我都尽量讲得更简单清楚。
&&& LDA的全称是Linear Discriminant Analysis(线性判别分析),是一种supervised learning。有些资料上也称为是Fisher’s Linear Discriminant,因为它被Ronald Fisher发明自1936年,Discriminant这次词我个人的理解是,一个模型,不需要去通过概率的方法来训练、预测数据,比如说各种贝叶斯方法,就需要获取数据的先验、后验概率等等。LDA是在目前机器学习、数据挖掘领域经典且热门的一个算法,据我所知,百度的商务搜索部里面就用了不少这方面的算法。
&&& LDA的原理是,将带上标签的数据(点),通过投影的方法,投影到维度更低的空间中,使得投影后的点,会形成按类别区分,一簇一簇的情况,相同类别的点,将会在投影后的空间中更接近。要说明白LDA,首先得弄明白线性分类器():因为LDA是一种线性分类器。对于K-分类的一个分类问题,会有K个线性函数:
&&&& 当满足条件:对于所有的j,都有Yk & Yj,的时候,我们就说x属于类别k。对于每一个分类,都有一个公式去算一个分值,在所有的公式得到的分值中,找一个最大的,就是所属的分类了。
&&& 上式实际上就是一种投影,是将一个高维的点投影到一条高维的直线上,LDA最求的目标是,给出一个标注了类别的数据集,投影到了一条直线之后,能够使得点尽量的按类别区分开,当k=2即二分类问题的时候,如下图所示:
&&&& 红色的方形的点为0类的原始点、蓝色的方形点为1类的原始点,经过原点的那条线就是投影的直线,从图上可以清楚的看到,红色的点和蓝色的点被原点明显的分开了,这个数据只是随便画的,如果在高维的情况下,看起来会更好一点。下面我来推导一下二分类LDA问题的公式:
&&&& 假设用来区分二分类的直线(投影函数)为:
&&& LDA分类的一个目标是使得不同类别之间的距离越远越好,同一类别之中的距离越近越好,所以我们需要定义几个关键的值。
&&& 类别i的原始中心点为:(Di表示属于类别i的点)
&&& 类别i投影后的中心点为:
&&& 衡量类别i投影后,类别点之间的分散程度(方差)为:
&&& 最终我们可以得到一个下面的公式,表示LDA投影到w后的损失函数:
&& 我们分类的目标是,使得类别内的点距离越近越好(集中),类别间的点越远越好。分母表示每一个类别内的方差之和,方差越大表示一个类别内的点越分散,分子为两个类别各自的中心点的距离的平方,我们最大化J(w)就可以求出最优的w了。想要求出最优的w,可以使用拉格朗日乘子法,但是现在我们得到的J(w)里面,w是不能被单独提出来的,我们就得想办法将w单独提出来。
&& 我们定义一个投影前的各类别分散程度的矩阵,这个矩阵看起来有一点麻烦,其实意思是,如果某一个分类的输入点集Di里面的点距离这个分类的中心店mi越近,则Si里面元素的值就越小,如果分类的点都紧紧地围绕着mi,则Si里面的元素值越更接近0.
&& 带入Si,将J(w)分母化为:
&& 同样的将J(w)分子化为:
&& 这样损失函数可以化成下面的形式:
&& 这样就可以用最喜欢的拉格朗日乘子法了,但是还有一个问题,如果分子、分母是都可以取任意值的,那就会使得有无穷解,我们将分母限制为长度为1(这是用拉格朗日乘子法一个很重要的技巧,在下面将说的PCA里面也会用到,如果忘记了,请复习一下高数),并作为拉格朗日乘子法的限制条件,带入得到:
&& 这样的式子就是一个求特征值的问题了。
&& 对于N(N&2)分类的问题,我就直接写出下面的结论了:
&& 这同样是一个求特征值的问题,我们求出的第i大的特征向量,就是对应的Wi了。
&& 这里想多谈谈特征值,特征值在纯数学、量子力学、固体力学、计算机等等领域都有广泛的应用,特征值表示的是矩阵的性质,当我们取到矩阵的前N个最大的特征值的时候,我们可以说提取到的矩阵主要的成分(这个和之后的PCA相关,但是不是完全一样的概念)。在机器学习领域,不少的地方都要用到特征值的计算,比如说图像识别、pagerank、LDA、还有之后将会提到的PCA等等。
&& 下图是图像识别中广泛用到的特征脸(eigen face),提取出特征脸有两个目的,首先是为了压缩数据,对于一张图片,只需要保存其最重要的部分就是了,然后是为了使得程序更容易处理,在提取主要特征的时候,很多的噪声都被过滤掉了。跟下面将谈到的PCA的作用非常相关。
&&& 特征值的求法有很多,求一个D * D的矩阵的时间复杂度是O(D^3), 也有一些求Top M的方法,比如说,它的时间复杂度是O(D^2 * M), 总体来说,求特征值是一个很费时间的操作,如果是单机环境下,是很局限的。
&&& 主成分分析(PCA)与LDA有着非常近似的意思,LDA的输入数据是带标签的,而PCA的输入数据是不带标签的,所以PCA是一种unsupervised learning。LDA通常来说是作为一个独立的算法存在,给定了训练数据后,将会得到一系列的判别函数(discriminate function),之后对于新的输入,就可以进行预测了。而PCA更像是一个预处理的方法,它可以将原本的数据降低维度,而使得降低了维度的数据之间的方差最大(也可以说投影误差最小,具体在之后的推导里面会谈到)。
&&& 方差这个东西是个很有趣的,有些时候我们会考虑减少方差(比如说训练模型的时候,我们会考虑到方差-偏差的均衡),有的时候我们会尽量的增大方差。方差就像是一种信仰(强哥的话),不一定会有很严密的证明,从实践来说,通过尽量增大投影方差的PCA算法,确实可以提高我们的算法质量。
&&& 说了这么多,推推公式可以帮助我们理解。我下面将用两种思路来推导出一个同样的表达式。首先是最大化投影后的方差,其次是最小化投影后的损失(投影产生的损失最小)。
&&& 最大化方差法:
&&& 假设我们还是将一个空间中的点投影到一个向量中去。首先,给出原空间的中心点:
&&& 假设u1为投影向量,投影之后的方差为:
&&& 上面这个式子如果看懂了之前推导LDA的过程,应该比较容易理解,如果线性代数里面的内容忘记了,可以再温习一下,优化上式等号右边的内容,还是用拉格朗日乘子法:
&&& 将上式求导,使之为0,得到:
&&& 这是一个标准的特征值表达式了,λ对应的特征值,u对应的特征向量。上式的左边取得最大值的条件就是λ1最大,也就是取得最大的特征值的时候。假设我们是要将一个D维的数据空间投影到M维的数据空间中(M & D), 那我们取前M个特征向量构成的投影矩阵就是能够使得方差最大的矩阵了。
&&& 最小化损失法:
&&& 假设输入数据x是在D维空间中的点,那么,我们可以用D个正交的D维向量去完全的表示这个空间(这个空间中所有的向量都可以用这D个向量的线性组合得到)。在D维空间中,有无穷多种可能找这D个正交的D维向量,哪个组合是最合适的呢?
&&& 假设我们已经找到了这D个向量,可以得到:
&&& 我们可以用近似法来表示投影后的点:
&&& 上式表示,得到的新的x是由前M 个基的线性组合加上后D - M个基的线性组合,注意这里的z是对于每个x都不同的,而b对于每个x是相同的,这样我们就可以用M个数来表示空间中的一个点,也就是使得数据降维了。但是这样降维后的数据,必然会产生一些扭曲,我们用J描述这种扭曲,我们的目标是,使得J最小:
&&& 上式的意思很直观,就是对于每一个点,将降维后的点与原始的点之间的距离的平方和加起来,求平均值,我们就要使得这个平均值最小。我们令:
&&& 将上面得到的z与b带入降维的表达式:
&&& 将上式带入J的表达式得到:
&&&& 再用上拉普拉斯乘子法(此处略),可以得到,取得我们想要的投影基的表达式为:
&&& 这里又是一个特征值的表达式,我们想要的前M个向量其实就是这里最大的M个特征值所对应的特征向量。证明这个还可以看看,我们J可以化为:
&&& 也就是当误差J是由最小的D - M个特征值组成的时候,J取得最小值。跟上面的意思相同。
&&& 下图是PCA的投影的一个表示,黑色的点是原始的点,带箭头的虚线是投影的向量,Pc1表示特征值最大的特征向量,pc2表示特征值次大的特征向量,两者是彼此正交的,因为这原本是一个2维的空间,所以最多有两个投影的向量,如果空间维度更高,则投影的向量会更多。
&&& 本次主要讲了两种方法,PCA与LDA,两者的思想和计算方法非常类似,但是一个是作为独立的算法存在,另一个更多的用于数据的预处理的工作。另外对于PCA和LDA还有核方法,本次的篇幅比较大了,先不说了,以后有时间再谈:
发布于 3年前,
阅读(1076) | 评论(0) |
投票(0) | 收藏(1)
qmake文档真的不是很全,好多变量都不知道到哪里去找他们的用法,只能暂时copy源码中的部分来用,之后再慢慢学习。这里总结一下查找到资料的qmake知识。
_DATE_& 当前日期和时间
_FILE_&&& 当前qmake正在解析的文件(pri、pro文件等)
QMAKE_HOST&& OS相关信息,
QMAKE_HOST.arch&&& 系统格式(x86或64)
QMAKE_HOST.name& 计算机在网络中的名称
QMAKE_HOST.os&&&&&& 操作系统(Windows or linux)
QMAKE_HOST.version&&&& 系统版本(Win95,WinXP…Win7获得的信息为144.
更多请参考:
比较有用的几个函数:
将路径中反斜杠( \ )转换成斜杠(/ ),
defineReplace(cleanPath) {
win32:1 ~= s|\\\\|/|g
#用/替换掉\,正则表达式当中用四个反斜杠匹配反斜杠, 这里是Perl的正则表达式的用法,|g表示替换全部符合的模式
contains(1, ^/.*):pfx = / #前缀是/的,在最后要加上/
else:pfx =
segs = $$split(1, /)#根据/分开参数1
#去除segs中..和.
for(seg, segs) {
equals(seg, ..):out = $$member(out, 0, -2) #文档中member函数是只有两个参数的,所以这个-2不清楚是做什么的
else:!equals(seg, .):out += $$seg
return($$join(out, /, $$pfx)) ##用/将列表链接起来,并用pfx作为结果的前缀
Debug与Release生成不同名字的文件
很多情况下我们需要根据当前build的mode来决定生成的文件名,比如,如果使用debug模式,就在生成文件后面加d。那么当载入这些文件的时候,我们还需要手动添加这样的话:
&&&& MYFILE = $${NAME}_d.dll
&&&& MYFILE = $${NAME}.dll
每次用到都要写这个,那就来一个函数好了。在QtCreator中的实现是这样的:
defineReplace(qtLibraryName) {
unset(LIBRARY_NAME)
LIBRARY_NAME = $$1
CONFIG(debug, debug|release) {
!debug_and_release|build_pass {
mac:RET = $$member(LIBRARY_NAME, 0)_debug
else:win32:RET = $$member(LIBRARY_NAME, 0)d
isEmpty(RET):RET = $$LIBRARY_NAME
return($$RET)
添加预编译头
isEmpty(PRECOMPILED_HEADER):PRECOMPILED_HEADER = $$PWD/shared/***.h
发布于 3年前,
阅读(974) | 评论(0) |
投票(0) | 收藏(3)
在很多应用中经常都要操作文件的,比如递归/非递归删除文件(夹),拷贝文件夹等,在QtCreator中有一个辅助类完成了这些常用的操作FileUtils,在Utils-libs当中。它实现了以下四个操作
static bool removeRecursively(const QString &filePath, QString *error = 0);
static bool copyRecursively(const QString &srcFilePath,
const QString &tgtFilePath, QString *error = 0);
static bool isFileNewerThan(const QString &filePath,
const QDateTime &timeStamp);
static QString resolveSymlinks(const QString &path);
下面分别解释一下它们的实现。
bool FileUtils::removeRecursively(const QString &filePath, QString *error)
QFileInfo fileInfo(filePath);//提供系统独立的文件信息,文件名、路径、权限、文件夹(或快捷方式)等。
//如果不存在该路径,并且该路径也不是快捷方式,就不用删除了
if (!fileInfo.exists() && !fileInfo.isSymLink())
//设置文件的访问权限(添加用户可写权限)
QFile::setPermissions(filePath, fileInfo.permissions() | QFile::WriteUser);
if (fileInfo.isDir())//指定路径是文件夹(或者通过快捷方式链接到一个文件夹)
QDir dir(filePath);
dir = dir.canonicalPath();//去掉一些不必要的路径信息,返回绝对路径(返回快捷方式的目标路径)
if (dir.isRoot())//不能删除根目录(C:,D:...)
if (error)
*error = QCoreApplication::translate(&Utils::FileUtils&,
&Refusing to remove root directory.&);
//不能删除home(C:/Documents and Settings/Username)
if (dir.path() == QDir::home().canonicalPath())
if (error)
*error = QCoreApplication::translate(&Utils::FileUtils&,
&Refusing to remove your home directory.&);
//除了..和.之外的文件夹,文件、快捷方式、隐藏文件、系统文件,
//这里返回的只有名字,不是完整路径
QStringList fileNames = dir.entryList(QDir::Files
| QDir::Hidden
| QDir::System
| QDir::Dirs
| QDir::NoDotAndDotDot);
foreach (const QString &fileName, fileNames)
//递归删除
if (!removeRecursively(filePath + QLatin1Char('/') + fileName, error))
//删除空的文件夹
if (!QDir::root().rmdir(dir.path()))
if (error)
*error = QCoreApplication::translate(&Utils::FileUtils&, &Failed to remove directory '%1'.&)
.arg(QDir::toNativeSeparators(filePath));
else //说明是文件
if (!QFile::remove(filePath))
if (error)
*error = QCoreApplication::translate(&Utils::FileUtils&, &Failed to remove file '%1'.&)
.arg(QDir::toNativeSeparators(filePath));
\fn resolveSymlinks()
\brief 处理快捷方式,iMaxLevel指定最大链接次数
QString FileUtils::resolveSymlinks(const QString &path, const int &iMaxLevel)
QFileInfo f(path);
int links = iMaxL
while (links-- && f.isSymLink())
f.setFile(f.symLinkTarget());
if (links &= 0)
return QString();
return f.filePath();
bool FileUtils::copyRecursively(const QString &srcFilePath, const QString &tgtFilePath, QString *error)
QFileInfo srcFileInfo(srcFilePath);
if (srcFileInfo.isDir())//是文件夹,应该先创建文件夹,然后拷贝里面文件
QDir targetDir(tgtFilePath);
targetDir.cdUp();
//目标文件夹不能存在
if (!targetDir.mkdir(QFileInfo(tgtFilePath).fileName()))
if (error)
*error = QCoreApplication::translate(&Utils::FileUtils&, &Failed to create directory '%1'.&)
.arg(QDir::toNativeSeparators(tgtFilePath));
QDir sourceDir(srcFilePath);
QStringList fileNames = sourceDir.entryList(QDir::Files | QDir::Dirs | QDir::NoDotAndDotDot | QDir::Hidden | QDir::System);
//深度优先遍历即可
foreach (const QString &fileName, fileNames)
const QString newSrcFilePath
= srcFilePath + QLatin1Char('/') + fileN
const QString newTgtFilePath
= tgtFilePath + QLatin1Char('/') + fileN
if (!copyRecursively(newSrcFilePath, newTgtFilePath, error))
if (!QFile::copy(srcFilePath, tgtFilePath))
if (error)
*error = QCoreApplication::translate(&Utils::FileUtils&, &Could not copy file '%1' to '%2'.&)
.arg(QDir::toNativeSeparators(srcFilePath),
QDir::toNativeSeparators(tgtFilePath));
* \brief FileUtils::isFileNewerThan
* 比较文件或文件夹是否比某个时间新,如果filePath是文件夹,那么它里面有一个文件比timeStamp新,
* 就返回真
bool FileUtils::isFileNewerThan(const QString &filePath, const QDateTime &timeStamp)
QFileInfo fileInfo(filePath);
if (!fileInfo.exists())//如果文件不存在,返回false
if (fileInfo.lastModified() &= timeStamp)//文件修改时间更新
if (fileInfo.isDir())
//这里不包括隐藏文件、快捷方式等系统文件
const QStringList dirContents = QDir(filePath)
.entryList(QDir::Files | QDir::Dirs | QDir::NoDotAndDotDot);
foreach (const QString &curFileName, dirContents)
const QString curFilePath
= filePath + QLatin1Char('/') + curFileN
if (isFileNewerThan(curFilePath, timeStamp))
上边的isFileNewerThan中代码与源码不太相同,因为我认为当文件路径不存在时,应该返回false的。
发布于 3年前,
阅读(250) | 评论(0) |
投票(0) | 收藏(1)
继续阅读QtCreator源码,本文主要分析在QtCreator中Mode的管理方式以及其接口设计方法。
Mode顾名思义叫做模式,在Qt Creator中有欢迎模式,编辑模式(EditMode),设计模式(DesignMode),调试模式,帮助模式。在核心插件中只有两个模式就是EditMode和DesignMode,本文仅分析EditMode,其他可以类似推广开来。在UI上的表示就是IDE最左边的Tab工具栏。如下图所示:
当然从CorePlugin入手,其类中有两个成员
MainWindow *m_mainW
EditMode *m_editM
相关的函数是
bool CorePlugin::initialize(const QStringList &arguments, QString *errorMessage)
parseArguments(arguments);
const bool success = m_mainWindow-&init(errorMessage);
if (success) {
m_editMode = new EditM
addObject(m_editMode);
ModeManager::activateMode(m_editMode-&id());
m_designMode = new DesignM
也就是在初始时的模式应该是m_editMode.将m_editMode放到对象池当中,之后可以随时访问。接下来看MainWindow类的Mode相关内容,它有三个比较重要的成员变量:EditorManager *m_editorManager; ModeManager *m_modeM& FancyTabWidget *m_modeS
在MainWindow的构造函数中创建了这四个变量。
m_modeStack = new FancyTabWidget(this);
m_modeManager = new ModeManager(this, m_modeStack);
m_modeManager-&addWidget(m_progressManager-&progressView());
m_statusBarManager = new StatusBarManager(this);
m_messageManager = new MessageM
m_editorManager = new EditorManager(this);
m_editorManager-&hide();
FancyTabWidget
FancyTabWidget用于创建最左边的工具栏,它包括FancyTabBar和CornerWidget两个部分,同时它还管理着一个StackedLayout和状态栏,用于显示在不同模式下的不同Widget布局。FancyTabWidget的控件布局如下:
树形结构从左到右,从上到下有序的布局。之所以FancyTabBar部分的颜色不同,是FancyTabWidget的paintEvent完成的。
EditorManager
继承于QWidget,在MainWindow的构造函数中创建实例,在extensionInitialized()中初始化。该类完成“文件”菜单项中的保存、另存为等菜单项的添加,并添加关闭按钮以及相应的快捷键。
可以看到“向前/后”、关闭等按钮的插函数都在该类中。在该类中还有一个重要的类是EditorClosingCoreListener,继承与ICoreListener。ICoreListener提供一些钩子(或者说相应函数)响应核心插件发出的事件。任何继承与ICoreListener的类的变量的接口函数返回false,整个过程(如关闭过程)就会终止。在使用时,需要将这些变量添加到对象池当中,并要在析构的时候从对象池移出。
在Qt中Model/View方式被使用的淋漓精致,可以说EditorManager就一个View。OpenEditorsModel继承于QAbstractItemModel,实现相应的虚方法就可以了。这里的columnCount和rowCount分别返回2和editor数量。在这个model中数据存放在Entry列表中,每个Entry存放文件名、id、显示标题等信息。Entry列表中以文件名作为key(也就是说,文件名相同的Editor将会合并,替换掉旧的)。
在OpenEditorModel中可以看到有两个这样的东东:QList&OpenEditorsModel::Entry& m_
QList&IEditor *& m_duplicateE
我暂时还没有理解出这个duplicate有什么作用?望高手解答。
在editormanager.cpp中有一个EditorManagerPlaceHolder(继承与QWidget)是用于根据当前模式进行布局。该类的功能很弱,就是隐藏当前Mode,show新的Mode。一个EditorManagerPlaceHolder对应一个m_mode,Mode发生改变,所有的Holder都将会收到currentModeChanged(newMode)信号。在Holder类中有一个静态变量EditorManagerPlaceHolder m_current,记录当前状态。那么响应信号中要做的就是如果响应函数在m_current中,就把EditorManager先隐藏掉,在Holder的模式是newMode的情况下把EditorManager添加到布局当中去,然后显示(这里显然是EditorManager的布局已经改变了),在EditorManager中有一个自动保存的定时器m_autoSaveTimer,然后调用autoSave()让IDocument类去完成保存。接着看下面的代码:
void EditorManager::init()
d-&m_coreListener = new EditorClosingCoreListener(this);
ExtensionSystem::PluginManager::addObject(d-&m_coreListener);
d-&m_openEditorsFactory = new OpenEditorsViewFactory();
ExtensionSystem::PluginManager::addObject(d-&m_openEditorsFactory);
VariableManager *vm = VariableManager::instance();
vm-&registerVariable(kCurrentDocumentFilePath,
tr(&Full path of the current document including file name.&));
vm-&registerVariable(kCurrentDocumentPath,
tr(&Full path of the current document excluding file name.&));
vm-&registerVariable(kCurrentDocumentXPos,
tr(&X-coordinate of the current editor's upper left corner, relative to screen.&));
vm-&registerVariable(kCurrentDocumentYPos,
tr(&Y-coordinate of the current editor's upper left corner, relative to screen.&));
connect(vm, SIGNAL(variableUpdateRequested(QByteArray)),
this, SLOT(updateVariable(QByteArray)));
这个初始化中完成coreListener的添加,并注册一些变量(其实本质上就是QMap的key-value)。
接下来看setCurrentEditor()。首先应该知道EditorView提供了鼠标的位置历史,那么当设置新的editor时就需要考虑是否保留之前记录的鼠标位置。更新Action,更新windows title。之外还有一个叫setCurrentView的函数。这个就是之前提到的修改布局。
void EditorManager::emptyView(Core::Internal::EditorView *view)
if (!view)
QList&IEditor *& editors = view-&editors();
foreach (IEditor *editor, editors) {
if (!d-&m_editorModel-&isDuplicate(editor)) {
editors.removeAll(editor);
view-&removeEditor(editor);
emit editorAboutToClose(editor);
removeEditor(editor);
view-&removeEditor(editor);
emit editorsClosed(editors);
foreach (IEditor *editor, editors) {
可以看到,清空View的方法是从View中移除所有的Editor。
有了清空View,还有关闭View。关闭View就是关闭该View的所有的IEditor,然后放上Split Window的其他View。
EditorManager中的另一个重要函数是
Core::IEditor *EditorManager::placeEditor(Core::Internal::EditorView *view, Core::IEditor *editor)。将当前分个窗口中有editor的窗口先将editor移除,然后添加到这个view参数当中。当调用activateEditor时就需要先将view和editor结合起来(调用placeEditor)即可,然后设置当前Editor为editor。
一个Mode包含显示名称,图标,优先级,id,类型,状态(Enabled or not).所有的Mode都需要继承于该类。
ModeManager
它的初始化如下:
ModeManager::ModeManager(Internal::MainWindow *mainWindow,
Internal::FancyTabWidget *modeStack)
m_instance =
d = new ModeManagerPrivate();
d-&m_mainWindow = mainW
d-&m_modeStack = modeS
d-&m_signalMapper = new QSignalMapper(this);
d-&m_oldCurrent = -1;
d-&m_actionBar = new Internal::FancyActionBar(modeStack);
d-&m_modeStack-&addCornerWidget(d-&m_actionBar);
connect(d-&m_modeStack, SIGNAL(currentAboutToShow(int)), SLOT(currentTabAboutToChange(int)));
connect(d-&m_modeStack, SIGNAL(currentChanged(int)), SLOT(currentTabChanged(int)));
connect(d-&m_signalMapper, SIGNAL(mapped(int)), this, SLOT(slotActivateMode(int)));
可以看到当FancyTabWidget的Tab发生变化时,就会调用currentTabAboutToChange(int)和currentTabChanged();而这两个函数又会发出
currentModeAboutToChange(mode)和currentModeChanged(mode, oldMode)两个信号,这些信号由所有的Mode来响应,也就是说ModeManager提供了FancyTabWidget和Mode之间的通信桥梁。
发布于 3年前,
阅读(399) | 评论(2) |
投票(0) | 收藏(2)
在QtCreator当中用到了不少的Concurrent(并发),比如编译时,搜索时等。其实在很多场合中都需要用到,一般是CPU去做一项大任务(花费较长时间)时相应用户操作。另一个重要用途就是在当前这个多核,甚至多CPU的年代,并行变成成为一种时尚了,它也确实提高了应用程序的性能。我的电脑是单CPU,2核心4线程,所以相比单应用程序,应该可以将性能提高将近4倍(当然不会是4倍的)。我所听过的有很多库是这方面的,比如CUDA,OpenCL,OpenMP。Qt是怎么做的还真不知道,望高手指教。首先来测试下:
#include &QtCore/QCoreApplication&
#include &QtConcurrentRun&
#include &QtConcurrentMap&
#include &qmath.h&
#include &QFuture&
#include &QVector&
#include &QTime&
#include &QObject&
#include &QFutureWatcher&
#include &myslot.h&
void test(const int &N)
int k = N * 100;
int v = 0;
for(int i = 0; i & ++i)
int main(int argc, char *argv[])
QCoreApplication a(argc, argv);
QVector&int& datas();
t.start();
for(int i = 0; i & 1000; ++i)
test(datas[i]);
printf(&%d ms\n&, t.elapsed());
t.restart();
QFutureWatcher&void& *pW = new QFutureWatcher&void&;
QObject::connect(pW, SIGNAL(finished()), &ms, SLOT(onFinish()));
QFuture&void& future = QtConcurrent::map(datas, test);
pW-&setFuture(future);
return a.exec();
这里的MySlot里面就有一个onFinish()而已,完成时间输出,虽然QTime计时并不准确,但是这里已经可以看出问题了。
提高了将近三倍的速度。
这里分为三个部分来谈Concurrent在Qt中的用法(QThread在这里就不谈了,有点low-level,灵活性虽强,但不好控制,需要较好的多线程经验)。
1. QtConcurrent::run()
QtConcurrent::run()使用Qt的全局线程池,找到一个空闲线程执行函数。在操作系统中,是有流水线技术的,那么IO操作与CPU运算是可以同时进行的,如果我们的操作中有这两种操作,就可以使用QtConcurrent并发完成。看下面的函数原型:
&T& QtConcurrent::run ( Function function, ... )
第一个是函数名,接下来是不定长的参数列表。每个线程就去执行这个函数,直到完成任务线程归还给线程池。这里就有一个问题,假如我现在有很多任务,那么,需要多少个线程来做这些任务呢?当然,并不是开辟的线程越多越好的,开辟过多的线程不仅浪费资源,同时也会使CPU因切换时间片而性能下降。为了减少线程数量,我可以将任务分成N组,这个N的取值可以根据具体的计算机的CPU来确定,我的是4核心,可以使用4个线程4个分组(这个不是最好的,因为没有考虑到IO与CPU并发的问题)。Qt中有一个函数可以返回一个较合适的CPU线程数量QThread::idealThreadCount(),他返回的就是CPU的核心数量。
如果希望在每个组里根据当前处理状态来通知消息,可以使用QApplication::postEvent(), QApplication :: sendEvent()或者QMetaObject::InvokeMethod(),还有signal-slot,他们各有各自的好处。
postEvent()类似与windows中的PostMessage(),使用非阻塞的方式发送消息到消息队列当中,相反SendMessage()就是阻塞的方式了,知道消息被处理之后才会返回。但是Qt中的sendEvent()有些特别,如果在别的线程sendEvent(),你会得到这样的ASSERT:
ASSERT failure in QCoreApplication::sendEvent: &Cannot send events to objects owned by a different thread. Current thread c33f640. Receiver '' (of type 'Widget') was created in thread 277818&。
在Qt高级编程中有这样的一些话,“sendEvent()方法立刻分派消息--但是应该小心使用,或者就不使用,比如在多线程编程中使用sendEvent()会使事件句柄归发送者线程拥有,而不是接收者线程。并且没有事件压缩或者重排序,sendEvent()不会删除事件,因此应该在栈上创建事件。”
所以还是用postEvent比较好,具体用法参考Qt Assistant。
invokeMethod()的函数原型非常的长,如下
bool QMetaObject::invokeMethod (
* obj, const char * member,
type, ret,
val0 = QGenericArgument( 0 ),
val1 = QGenericArgument(),
val2 = QGenericArgument(),
val3 = QGenericArgument(),
val4 = QGenericArgument(),
val5 = QGenericArgument(),
val6 = QGenericArgument(),
val7 = QGenericArgument(),
val8 = QGenericArgument(),
val9 = QGenericArgument() ) [static]。
也就是说invokeMethod最多支持10个参数,该函数还有不少的重载。具体使用方法如下:
QString retV
QMetaObject::invokeMethod(obj, &compute&, Qt::DirectConnection,
Q_RETURN_ARG(QString, retVal),
Q_ARG(QString, &sqrt&),
Q_ARG(int, 42),
Q_ARG(double, 9.7));
第三个参数是链接类型,在多线程中Qt::QueuedConnection是经常使用的。Q_RETURN_ARG只有在使用Qt::DirectConnection才会有效。
2. 使用QRunnable
使用QRunnable就需要派生一个子类了,然后重写虚函数run()。在run函数中完成要做的任务,但是QRunnable不是QObject的子类,那么只能用invoke方法了和自定义事件(custom event)。另外在run结束之后,线程池将会删除这个runnable对象,那么在构造函数里使用setAutoDelete(false)可以将控制权转交给用户。创建任务时,使用的方法是QThreadPool::globalInstance()-&start(MyRunnableObj);
使用QtConcurrent::run()方法可以返回一个QFuture&T&, 我们可以使用QFutureWatcher&T&来跟踪处理的过程,而使用QRunnable类就需要我们自己管理了。
3. 使用QtConcurrent监视进度
有四种方式来监视任务进度,filter,mapper,reducer,function。最后一种是无法跟踪具体进度的,只能监视到任务开始与任务结束。这里分别说一下前三者的用法。
给定一个集合如QList,QVector等,通过一个过滤filter函数的返回值返回一个新的集合;mapper返回新类型的集合,reducer就是将集合中的值merge成一个值(比如求和等,这个值也可以是一个新的集合,那么所谓的值就是集合)。QtConcurrent::run()等方法都会返回一个QFuture&T&类型的对象,直接访问该对象将会阻塞,那么就要使用非阻塞的QFutureWatcher&T&来监视,它所监视的事件有paused, resumed, canceled。其中QFuture&T&中的T需要是filter等操作的结果类型,QFutureWatcher&T&就需要和他监视的QFuture的类型相同才对。来看一下《Qt高级编程》中的示意图:
这里有一个地方要注意:在filtered()之前,我们就应该将watcher的信号链接到相应的槽上去。下面是具体用法(以filtered为例):
&T& QtConcurrent::filtered ( const Sequence & sequence, FilterFunction filterFunction );
&T& QtConcurrent::filtered ( ConstIterator begin, ConstIterator end, FilterFunction filterFunction )
说明前边的参数可以是一个集合,或者是两个const的迭代器,最后一个参数就是相应的过滤函数。过滤函数的圆形需要是这样的:
bool function(const T &t);
用法如下(来自Qt Assistant):
bool allLowerCase(const QString &string)
return string.lowered() ==
QStringList strings = ...;
QFuture&QString& lowerCaseStrings = QtConcurrent::filtered(strings, allLowerCase);
这里学会了一种叫函数对象的东东,上一段代码先看,其实就是重载()操作符,另外typedef 该操作符函数的返回值为result_type。
struct StartsWith
StartsWith(const QString &string)
: m_string(string) { }
typedef bool result_
bool operator()(const QString &testString)
return testString.startsWith(m_string);
QString m_
QList&QString& strings = ...;
QFuture&QString& fooString = QtConcurrent::filtered(images, StartsWith(QLatin1String(&Foo&)));
好,这部分就做这么多记录,然后准备把QtCreator的ProgressManager抠出来用。
发布于 3年前,
阅读(367) | 评论(1) |
投票(0) | 收藏(0)
这里主要分析QtCreator中OutputPane的相关内容,可以看到QtCreator在状态栏有四个Checkable按钮,如下图所示
checkable按钮的实现方式基本一致,这里仅以其中一种General Message来说明问题。在插件源码的coreplugin中,相关类有ModeManager,MessageManager,MessageOutputWindow,OutputPaneManager,OutputPaneToggleButton,OutputPaneManageButton,OutputPanePlaceHolder,MiniSplitterHandle,OutputWindow以及StatusBarManager和StatusBarWidget。这里首先看一下他们之间的关系。
接下来详细解释与分析一下(高手勿喷,请多指教)。
MainWindow的构造函数都做了些什么?
MainWindow应该做的都只是各种管理器的初始化工作,如何布局是由ModeManager来管理的,ModeManager通过各个不同的Mode来呈现不同的界面布局。
IMode就是这些Mode的接口类,在后面会详细介绍。先看MainWindow,初始化分为了三个部分:构造函数、init()、extensionsInitialized()。在Qt Creator中,很多Manager使用单例模式,但是这些单例模式可以说是一种不自动的,没有根据成员m_instance的状态来判断是创建实例还是直接返回,每一个Manager都需要create一下(好处不详,没有分析出来,望高手赐教)。在构造函数中就是调用这些create(),合情合理,构造函数不就是创建嘛。先上段代码看看:
MainWindow::MainWindow() :
Utils::AppMainWindow(),
m_coreImpl(new ICore(this)),
m_actionManager(new ActionManager(this)),
m_editorManager(0),
m_statusBarManager(0),
m_modeManager(0),
m_activeContext(0),
m_generalSettings(new GeneralSettings),
m_shortcutSettings(new ShortcutSettings),
m_toggleSideBarAction(0),
m_toggleSideBarButton(new QToolButton)
(void) new DocumentManager(this);
OutputPaneManager::create();
setWindowTitle(tr(&Qt Creator&));
registerDefaultContainers();
registerDefaultActions();
m_modeStack = new FancyTabWidget(this);
m_modeManager = new ModeManager(this, m_modeStack);
m_modeManager-&addWidget(m_progressManager-&progressView());
m_statusBarManager = new StatusBarManager(this);
m_messageManager = new MessageM
m_editorManager = new EditorManager(this);
m_editorManager-&hide();
m_externalToolManager = new ExternalToolManager();
setCentralWidget(m_modeStack);
statusBar()-&insertPermanentWidget(0, m_toggleSideBarButton);
撇掉与输出窗口无关的东西,上面的代码就是与Output相关的代码。
m_actionManager用于管理菜单,关于Output仅用于注册快捷键。
1. OutputPaneManager::create()
完成OutputPaneManager创建,在其构造函数中添加了“清除action”,“prevAction”,“nextAction”,”最大化最小化”,”关闭“等输出窗口上toolbar的一些action。这几个action是所有输出类窗口的公共action,也就是说,所有的输出类窗口都会有这几个action。
2. registerDefaultContainers() 和 registerDefaultActions()
用于添加主窗口的菜单,具体添加方法参见
3.new FancyTabWidget 和 new ModeManager
FancyTabWidget是Qt Creator窗口最左边的一列,包括欢迎、编辑、调试、设计、项目、分析、帮助,下边还有一个大家都熟悉的cornerWidget(放着构建、构建运行等几个按钮)。ModeManager用于管理各种Mode,比如EditMode,WelcomeMode等等,每一个FancyTabWidget中的Tab对应一个Mode,从这里可以看出,可以通过添加IMode的子类以在FancyTabWidget中添加Tab。ModeManager的构造函数完成FancyTabWidget的Tab改变所引起的信号的槽,并添加cornerWidget(在FancyTabWidget中,并没有添加,只是添加了一个占位的布局而已)。
4. new StatusBarManager(this) 和 new MessageManager
StatusBarManager的构造函数完成根据StatusBarWidget的个数(四个)向StatusBar添加Widget的功能(其实就相当于将状态栏分成了四个部分),并将他们存放在QList&QWidget*&中以便以后使用。来一段代码
StatusBarManager::StatusBarManager(MainWindow *mainWnd)
: QObject(mainWnd),
m_mainWnd(mainWnd)
for (int i = 0; i &= StatusBarWidget::L ++i) {
QWidget *w = new QWidget();
m_mainWnd-&statusBar()-&insertPermanentWidget(i, w);
w-&setLayout(new QHBoxLayout);
w-&setVisible(true);
w-&layout()-&setMargin(0);
m_statusBarWidgets.append(w);
m_mainWnd-&statusBar()-&insertPermanentWidget(StatusBarWidget::Last+1,
new QLabel(), 1);
个人认为这里使得状态栏的分组个数固定,设计并不是很好,插件希望添加按钮到状态栏时就遇到了困难,需要修改StatusBarWidget,并重新编译Core插件,不知道是我理解的不够深入还是怎么。MessageManager的构造函数并没有做什么实质性的东西。
statusBar()-&insertPermanentWidget(0, m_toggleSideBarButton);在状态栏的最左边添加了一个按钮用于显示/隐藏边栏。
MainWindow的init()做了些什么?
先上代码,已经去掉无关代码。
bool MainWindow::init(QString *errorMessage)
Q_UNUSED(errorMessage)
ExtensionSystem::PluginManager::addObject(m_coreImpl);
m_statusBarManager-&init();
m_modeManager-&init();
m_outputView = new Core::StatusBarW
m_outputView-&setWidget(OutputPaneManager::instance()-&buttonsWidget());
m_outputView-&setPosition(Core::StatusBarWidget::Second);
ExtensionSystem::PluginManager::addObject(m_outputView);
m_messageManager-&init();
1. m_statusBarManager-&init()
仅将对象池的objectAdded() 和aboutToRemoveObject()两个信号关联到了相应的槽函数objectAdded(QObject*)和aboutToRemoveObject(QObject*)。当有对象添加到对象池当中时,就使用Aggregation来查询是否是StatusBarWidget类型,如果是该类型,就将相对应的Widget添加到状态栏的相应位置当中,并添加到窗口列表(QMap&QWidget *, IContext *& m_contextWidgets)当中。aboutToRemoveObject就是从窗口列表中删除。
2. m_modeManager-&init()
与StatusBarManager一样,仅将对象池的objectAdded() 和aboutToRemoveObject()两个信号关联到了相应的槽函数objectAdded(QObject*)和aboutToRemoveObject(QObject*)。他查询的是IMode类型,如果是IMode类型,就将其添加到FancyTabWidget当中,并链接相应的槽,注册快捷键。
3.new Core::StatusBarWidget()之后的几句代码
创建了一个Widget,这个Widget由OutputPaneManager来负责填充,并设置这个widget放在状态栏的第2个位置上(有4个位置),添加这个StatusBarWidget到对象池,相应的槽函数完成真正的插入工作。
4. MessageManager::init()
该函数中创建MessageOutputWindow,并将其添加到对象池中。
MainWindow的extensionsInitialized()都做了些什么?
简要代码如下:
void MainWindow::extensionsInitialized()
m_statusBarManager-&extensionsInitalized();
OutputPaneManager::instance()-&init();
// reading the shortcut settings must be done after all shortcuts have been registered
m_actionManager-&d-&initialize();
1. m_statusBarManager-&extensionsInitalized()
该函数为空,可以进行扩展。
2. OutputPaneManager::instance()-&init()
首先在主窗口的menuBar中添加菜单,关联”清空“,”最大化/最小化“操作。然后从对象池当中得到所有的IOutputPane,并将它们根据在状态栏中的位置进行排序。然后将每个IOutputPane的输出窗口outputWidget添加到QStackedWidget当中,将toolBarWidget添加到输出窗口上边的工具栏m_opToolBarWidgets(也是一个StackedWidget)中。
好,先写这么多,以后继续补充。
发布于 3年前,
阅读(198) | 评论(0) |
投票(0) | 收藏(0)}

我要回帖

更多关于 维多利亚的秘密 综艺 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信