1、北京邮电大学毕业设计多文本编辑器设计与实现摘 要在深入研究当前各种文本编辑软件优劣的前提上,分析软件开发中程序员的实际需求,发现他们有时候并不需要打开系统庞大、启动较慢的集成开发环境来阅读改写代码,只需要通过小型的文本编辑器即可。然而大多数小型文本编辑器并不能高亮显示代码中的关键字,或者软件本身闭源,不能根据需要改变高亮显示关键字列表,或者软件只有windows版或Linux版平台移植性较差,这并不能满足经常需要在各个软件系统平台之间交叉开发的程序员的需求,甚至影响了他们的工作效率。本系统运用了平台移植性较好的Qt语言来开发,不同平台之间只需要重新编译即可,且代码易读可重用性良好,程序员只需要
2、修改代码中关键字列表或根据需要自定义自己的高亮显示关键字列表即可,这一软件为程序员阅读改写代码提供了便利。关键词 自定义;多文本编辑器;Qt;高亮显示The Design and Implementation of Multiple Document EditorAbstractIn the further study of the current various text editing software quality on the premise of the analysis of the actual demand of software development programmer
3、s, find that they sometimes dont need to open the large system, start the slower integrated development environment to read rewrite code, you just need to through the small text editor. But most small text editor is not highlighted keywords in your code, or closed source software itself, not accordi
4、ng to the need to change the highlighted keywords list, or software version only Windows or Linux version of the moving platform plant sex is bad, it doesnt meet often need to cross between different software system platform development demand for programmers, and even affect their work efficiency.
5、This system USES the platform portability better Qt to develop language, you just need to recompile, between the platform and code easy to read good reusability, programmers only need to modify the code in the keywords list or according to the need to customize your highlighted keywords list, this s
6、oftware provides convenience for programmers to read rewriting code.Key words: multiple document editor; plugins; custom highlight目录摘要IAbstractII1绪论1 11现实背景和理论背景1 12研究目的和意义1 121目的1 122意义12软件需求分析23开发模型的确定3 31软件工程及其开发模型概述3 311软件工程3 312开发模型概述3 32确定增量模型64QT简介7 41 QT支持的平台7 42 QT的不同发行版本75多文本编辑器的设计和实现8 51信
7、号/槽机制8 511信号/槽机制概述8 512一个小例子9 513信号11 514槽11 515元对象信息12 516一个实际应用的例子13 52 QMDIAREA类14 521多文档界面程序14 522 QMDIAREA类1453 QSYNTAXHIGHLIGHTER类1654插件的实现机制1755插件的优点18 551插件机制加快运行速度18 552插件机制使软件的后期维护变得简单1856编写QT插件18 561较高级的API18 562较低级的API1957 QT插件分类19 571动态加载的插件19 572静态插件206运行测试及界面217关键代码及注释27结论45参考文献46致谢47
8、641 绪论1.1 现实背景和理论背景虽然目前国内外的多文本编辑器种类已经不胜其数,在实际应用中,根据自己的需求找到一款很顺手的编辑器却不是每个人都能做到的.windows下绝大多数用户不二的选择的microsoft office尽管强大,但它所有的功能并不被人们所一一掌握,程序的庞大对于不需要它额外功能的用户来说,装载如此庞杂的程序到内存并不是必须的,无形中浪费了用户的时间.而Linux下最受欢迎的编辑器非Emacs莫属, 通过加载各种插件它的功能已经不再局限于文本编辑器,完全可以胜任编译与测试方面的工作,深受程序员的青睐.然而作为程序员在实际工作中,有时候并不需要那些诸如编译之类的功能,有
9、时候他们只是需要阅读学习或者稍微改动一些代码.这时候,他们需要的是一个轻便、无需浪费过多内存的多文本编辑器,便于多个程序代码之间参照读写。本课题的实践完成了这一需求,并借助于Qt语言“编码一次,多次部署,多平台运行”的宗旨,成功的实现了一个软件的多平台运行。目前,该文本编辑器支持windows、linux和Android三个平台。1.2 研究目的和意义1.2.1目的满足广大程序员的现实需求,实现一个可以多平台运行、自定义高亮显示方案的轻便多文本编辑器。1.2.2意义 通过该课题的研究,我们能更好地了解在实践中,针对用户分别定制软件需求的重要性。通过在实践中努力贯彻软件工程的思想,可以体会软件工
10、程带给研发工作的高效性。培养自己自主学习的能力,尝试独立完成项目,并总结经验,积累成长。2 软件需求分析根据以上在绪论中的分析,我们将满足广大程序员的现实需求,实现一个可以多平台运行、自定义高亮显示方案的轻便多文本编辑器。该软件要实现的具体功能如下所列:多文本编辑器的功能:1.维护各个编辑器的独立状态。2.可以打开多个编辑器,保证各个编辑器简单的复制、粘贴、删除等功能的实现。3.通过主窗口的菜单实现对各个编辑器状态的改变。4.帮助菜单选项显示软件帮助信息。5.实现各个编辑窗口的整理排序,以级联的方式排序各个窗口,方便用户快速文本概要信息。6.通过菜单随时调用显示当前编辑窗体之前之后的相邻窗体。
11、高亮显示端功能:1.设定的搞定显示关键字实现高亮显示。2.保证高亮显示这一功能在编辑器中的正确调用。插件功能:根据当前编辑文件的类型正确的加载相应的自定义插件。根据插件加载情况,实时地在终端打印,方便用户根据需要修改插件。3 开发模型的确定3.1软件工程及其开发模型概述3.1.1软件工程该软件的开发完全应用软件工程的思想-将工程化应用于软件来提高软件开发的效率。目前,业界对于软件工程这一概念有以下观点:1.软件工程遵循创立与使用健全的工程原则,以便经济地获得可靠且高效率的软件。2.软件工程需应用系统化,遵从原则,可被计量的方法来发展、操作及维护软件;也就是把工程应用到软件上。3.软件工程是与开
12、发、管理及更新软件产品有关的理论、方法及工具。4.软件工程一种知识或学科,目标是生产质量良好、准时交货、符合预算,并满足用户所需的软件。5.软件工程实际应用科学知识在设计、建构计算机程序,与相伴而来所产生的文件,以及后续的操作和维护上。6.软件工程使用与系统化生产和维护软件产品有关之技术与管理的知识,使软件开发与修改可在有限的时间与费用下进行。7.软件工程是建造由工程师团队所开发之大型软件系统有关的知识学科。8.软件工程是对软件分析、设计、实施及维护的一种系统化方法。9.软件工程是以系统化地应用工具和技术于开发以计算机为主的应用。10.软件工程是关于设计和开发优质软件3.1.2开发模型概述软件
13、工程中,常用的开发模型有四种:瀑布模型、原型模型、增量模型和螺旋模型。瀑布模型包括计划,需求分析、设计、编码、测试、运行维护六个阶段,如图1所示,阶段自上而下,相互衔接,次序固定。瀑布模型具有很强的顺序性和依赖性,即:下一阶段必须以上一阶段的完成为前提。测试工作是在处于开发后期的测试阶段集中进行的,测试中可能发现大量的错误,因此必须返回需求分析、设计或编著码中定位问题,而以前认为已完成的各阶段必须修改,从而代价昂贵。 图1 瀑布模型原型模型的主要思想是先通过需求分析并建立一个软件系统的概貌原型(如图2),通过用户与开发者的评价和判断,不断的对原型进行反复的扩充、改进和求精,最终建立符合用户需求
14、的目标系统。开发过程中可能会多次生成更完善的原型,测试人员要对每个原型进行测试,并且在每个原型中投入的工作量及测试目标也将有所不同。图2 原型模型增量模型(如图3)与原型实现模型和其他演化方法一样,本质上是迭代的,但与原型实现不一样的是其强调每一个增量均发布一个可操作产品。早期的增量是最终产品的“可拆卸”版本,但提供了为用户服务的功能,并且为用户提供了评估的平台。图3 增量模型螺旋型开发模型结合了瀑布模型和原型模型的特点,并且加入了二者所忽略的风险分析。螺旋模型的每个周期都包含制定计划,风险分析、实施工程和评审四个阶段,如图4所示。开发过程每迭代一次,螺旋线就增加一周,软件开发前进一个层次,系
15、统生成一个新版本,软件开发时间与成本又有新投入,最后得到 一个用户满意的软件版本。图4 增量模型3.2确定增量模型基于实际分析后,我认为既然不能一次实现所有的软件框架,那么把需求分解,一点点的累加实现是可行的,这比较贴近增量模型的概念。于是,我把项目的需求分为多文本编辑器、文本高亮显示、插件加载实现附加功能三大块来逐步累加实现。4 Qt简介Qt是一个多平台的C+图形用户界面应用程序框架。它提供给应用程序开发者建立艺术级的图形用户界面所需的所用功能。Qt是完全面向对象的很容易扩展,并且允许真正地组件编程。 自从1996年早些时候,Qt进入商业领域,它已经成为全世界范围内数千种成功的应用程序的基础
16、。Qt也是流行的Linux桌面环境KDE 的基础,KDE是所有主要的Linux发行版的一个标准组件。 4.1 Qt支持的平台1. MS/Windows - 95、98、NT 4.0、ME、和2000 2. Unix/X11 - Linux、Sun Solaris、HP-UX、Compaq Tru64 UNIX、IBM AIX、SGI IRIX和其它很多X11平台 3. Macintosh - Mac OS X 4. Embedded - 有帧缓冲(framebuffer)支持的Linux平台。 Qt是最初奇趣科技的一个产品,是一个跨平台C+图形用户界面应用程序开发框架,2012年该公司被,Qt
17、被Digia收购,现由其维护运营。 4.2 Qt的不同发行版本Qt企业版和Qt专业版: 提供给商业软件开发。它们提供传统商业软件发行版并且提供免费升级和技术支持服务。企业版比专业版多一些扩展模块。 Qt自由版:是Qt仅仅为了开发自由和开放源码软件 提供的Unix/X11版本。在Qt公共许可证和GNU通用公共许可证 下,它是免费的。Qt/嵌入式自由版:是Qt为了开发自由软件提供的嵌入式版本。在GNU通用公共许可证下,它是免费的。5 多文本编辑器的设计和实现在多文本编辑器的实现中不得不提的是Qt用于对象间的通讯的信号/槽机制和用来实现多文档界面(Multiple Document Interfac
18、e)的widget类QMdiArea。5.1 信号/槽机制5.1.1 信号/槽机制概述信号/槽机制是Qt的一个中心特征并且也许是Qt与其它工具包的最不相同的部分。 在图形用户界面编程中,我们经常希望一个窗口部件的一个变化被通知给另一个窗口部件。更一般地,我们希望任何一类的对象可以和其它对象进行通讯。例如,如果我们正在解析一个XML文件,当我们遇到一个新的标签时,我们也许希望通知列表视图我们正在用来表达XML文件的结构。 较老的工具包使用一种被称作回调的通讯方式来实现同一目的。回调是指一个函数的指针,所以如果你希望一个处理函数通知你一些事件,你可以把另一个函数(回调)的指针传递给处理函数。处理函
19、数在适当的时候调用回调。回调有两个主要缺点。首先他们不是类型安全的。我们从来都不能确定处理函数使用了正确的参数来调用回调。其次回调和处理函数是非常强有力地联系在一起的,因为处理函数必须知道要调用哪个回调。 在Qt中我们有一种可以替代回调的技术。我们使用信号和槽。当一个特定事件发生的时候,一个信号被发射。Qt的窗口部件有很多预定义的信号,但是我们总是可以通过继承来加入我们自己的信号。槽就是一个可以被调用处理特定信号的函数。Qt的窗口部件又很多预定义的槽,但是通常的习惯是你可以加入自己的槽,这样你就可以处理你所感兴趣的信号。 信号和槽的机制是类型安全的:一个信号的签名必须与它的接收槽的签名相匹配。
20、(实际上一个槽的签名可以比它接收的信号的签名少,因为它可以忽略额外的签名。)因为签名是一致的,编译器就可以帮助我们检测类型不匹配。信号和槽是宽松地联系在一起的:一个发射信号的类不用知道也不用注意哪个槽要接收这个信号。Qt的信号和槽的机制可以保证如果你把一个信号和一个槽连接起来,槽会在正确的时间使用信号的参数而被调用。信号和槽可以使用任何数量、任何类型的参数。它们是完全类型安全的:不会再有回调核心转储(core dump)。 从QObject类或者它的一个子类(比如QWidget类)继承的所有类可以包含信号和槽。当对象改变它们的状态的时候,信号被发送,从某种意义上讲,它们也许对外面的世界感兴趣。
21、这就是所有的对象通讯时所做的一切。它不知道也不注意无论有没有东西接收它所发射的信号。这就是真正的信息封装,并且确保对象可以用作一个软件组件。 槽可以用来接收信号,但它们是正常的成员函数。一个槽不知道它是否被任意信号连接。此外,对象不知道关于这种通讯机制和能够被用作一个真正的软件组件。 我们可以把许多信号和你所希望的单一槽相连,并且一个信号也可以和你所期望的许多槽相连。把一个信号和另一个信号直接相连也是可以的。(这时,只要第一个信号被发射时,第二个信号立刻就被发射。)图5是一个关于一些信号和槽连接的摘要图。总体来看,信号和槽构成了一个强有力的组件编程机制。 图5 一个关于一些信号和槽连接的摘要图
22、5.1.2一个小例子一个最小的C+类声明如下: class Foo public: Foo(); int value() const return val; void setValue( int ); private: int val; ;一个小的Qt类如下: class Foo : public QObject Q_OBJECT public: Foo(); int value() const return val; public slots: void setValue( int ); signals: void valueChanged( int ); private: int val;
23、 ;这个类有同样的内部状态,和公有方法来访问状态,但是另外它也支持使用信号和槽的组件编程:这个类可以通过发射一个信号,valueChanged(),来告诉外面的世界它的状态发生了变化,并且它有一个槽,其它对象可以发送信号给这个槽。 所有包含信号和/或者槽的类必须在它们的声明中提到Q_OBJECT。 槽可以由应用程序的编写者来实现。这里是Foo:setValue()的一个可能的实现: void Foo:setValue( int v ) if ( v != val ) val = v; emit valueChanged(v); emit valueChanged(v)这一行从对象中发射valu
24、eChanged信号。正如你所能看到的,你通过使用emit signal(arguments)来发射信号。 下面是把两个对象连接在一起的一种方法: Foo a, b; connect(&a, SIGNAL(valueChanged(int), &b, SLOT(setValue(int); b.setValue( 11 ); / a = undefined b = 11 a.setValue( 79 ); / a = 79 b = 79 b.value(); 调用a.setValue(79)会使a发射一个valueChanged() 信号,b将会在它的setValue()槽中接收这个信号,也就
25、是b.setValue(79) 被调用。接下来b会发射同样的valueChanged()信号,但是因为没有槽被连接到b的valueChanged()信号,所以没有发生任何事(信号消失了)。 注意只有当v != val的时候setValue()函数才会设置这个值并且发射信号。这样就避免了在循环连接的情况下(比如b.valueChanged() 和a.setValue()连接在一起)出现无休止的循环的情况。 这个例子说明了对象之间可以在互相不知道的情况下一起工作,只要在最初的时在它们中间建立连接。 预处理程序改变或者移除了signals、slots和emit 这些关键字,这样就可以使用标准的C+编
26、译器。 在一个定义有信号和槽的类上运行moc。这样就会生成一个可以和其它对象文件编译和连接成引用程序的C+源文件。 5.1.3信号 当对象的内部状态发生改变,信号就被发射,在某些方面对于对象代理或者所有者也许是很有趣的。只有定义了一个信号的类和它的子类才能发射这个信号。 例如,一个列表框同时发射highlighted()和activated()这两个信号。绝大多数对象也许只对activated()这个信号感兴趣,但是有时想知道列表框中的哪个条目在当前是高亮的。如果两个不同的类对同一个信号感兴趣,你可以把这个信号和这两个对象连接起来。 当一个信号被发射,它所连接的槽会被立即执行,就像一个普通函数
27、调用一样。信号/槽机制完全不依赖于任何一种图形用户界面的事件回路。当所有的槽都返回后 emit也将返回。 如果几个槽被连接到一个信号,当信号被发射时,这些槽就会被按任意顺序一个接一个地执行。 信号会由moc自动生成并且一定不要在.cpp文件中实现。它们也不能有任何返回类型(比如使用void)。 关于参数需要注意。我们的经验显示如果信号和槽不使用特殊的类型,它们都可以多次使用。如果QScrollBar:valueChanged() 使用了一个特殊的类型,比如hypothetical QRangeControl:Range,它就只能被连接到被设计成可以处理QRangeControl的槽。简单的和教
28、程1的第5部分一样的程序将是不可能的。 5.1.4槽 当一个和槽连接的信号被发射的时候,这个操被调用。槽也是普通的C+函数并且可以像它们一样被调用;它们唯一的特点就是它们可以被信号连接。槽的参数不能含有默认值,并且和信号一样,为了槽的参数而使用自己特定的类型是很不明智的。 因为槽就是普通成员函数,但却有一点非常有意思的东西,它们也和普通成员函数一样有访问权限。一个槽的访问权限决定了谁可以和它相连: 一个public slots:区包含了任何信号都可以相连的槽。这对于组件编程来说非常有用:你生成了许多对象,它们互相并不知道,把它们的信号和槽连接起来,这样信息就可以正确地传递,并且就像一个铁路模型
29、,把它打开然后让它跑起来。 一个protected slots:区包含了之后这个类和它的子类的信号才能连接的槽。这就是说这些槽只是类的实现的一部分,而不是它和外界的接口。 一个private slots:区包含了之后这个类本身的信号可以连接的槽。这就是说它和这个类是非常紧密的,甚至它的子类都没有获得连接权利这样的信任。 我们也可以把槽定义为虚的,这在实践中被发现也是非常有用的。 信号和槽的机制是非常有效的,但是它不像“真正的”回调那样快。信号和槽稍微有些慢,这是因为它们所提供的灵活性,尽管在实际应用中这些不同可以被忽略。通常,发射一个和槽相连的信号,大约只比直接调用那些非虚函数调用的接收器慢十
30、倍。这是定位连接对象所需的开销,可以安全地重复所有地连接(例如在发射期间检查并发接收器是否被破坏)并且可以按一般的方式安排任何参数。当十个非虚函数调用听起来很多时,举个例子来说,时间开销只不过比任何一个“new”或者 “delete”操作要少些。当你执行一个字符串、矢量或者列表操作时,需要“new”或者 “delete”,信号和槽仅对一个完整函数调用地时间开销中的一个非常小的部分负责。无论何时你在一个槽中使用一个系统调用和间接地调用超过十个函数的时间是相同的。在一台i585-500机器上,你每秒钟可以发射2,000,000个左右连接到一个接收器上的信号,或者发射1,200,000个左右连接到两
31、个接收器的信号。信号和槽机制的简单性和灵活性对于时间的开销来说是非常值得的,你的用户甚至察觉不出来。 图6是一个一个信号和槽连接的例子。图6 一个信号和槽连接的例子5.1.5元对象信息 元对象编译器(moc)解析一个C+文件中的类声明并且生成初始化元对象的C+代码。元对象包括所有信号和槽函数的名称,还有这些函数的指针。元对象包括一些额外的信息,比如对象的类名称。你也可以检查一个对象是否继承了一个特定的类,比如: if ( widget-inherits(QButton) ) / 是的,它是一个Push Button、Radio Button或者其它按钮。 5.1.6一个实际应用的例子 这是一个
32、注释过的简单的例子(代码片断选自qlcdnumber.h)。 #include qframe.h #include qbitarray.h class QLCDNumber : public QFrameQLCDNumber通过QFrame和QWidget,还有#include这样的相关声明继承了含有绝大多数信号/槽知识的QObject。 Q_OBJECTQ_OBJECT是由预处理器展开声明几个由moc来实现的成员函数,如果你得到了几行 “virtual function QButton:className not defined”这样的编译器错误信息,你也许忘记运行moc或者忘记在连接命令中
33、包含moc输出。 public: QLCDNumber( QWidget *parent=0, const char *name=0 ); QLCDNumber( uint numDigits, QWidget *parent=0, const char *name=0 );它并不和moc直接相关,但是如果你继承了QWidget,你当然想在你的构造器中获得parent和name这两个参数,而且把它们传递到父类的构造器中。 一些解析器和成员函数在这里省略掉了,moc忽略了这些成员函数。 signals: void overflow();当QLCDNumber被请求显示一个不可能值时,它发射一个信
34、号。 如果你没有留意溢出,或者你认为溢出不会发生,你可以忽略overflow()信号,也就是说你可以不把它连接到任何一个槽上。 另一方面如果当数字溢出时,你想调用两个不同的错误函数,很简单地你可以把这个信号和两个不同的槽连接起来。Qt将会两个都调用(按任意顺序)。 public slots: void display( int num ); void display( double num ); void display( const char *str ); void setHexMode(); void setDecMode(); void setOctMode(); void setBi
35、nMode(); void smallDecimalPoint( bool );一个槽就是一个接收函数,用来获得其它窗口部件状态变或的信息。QLCDNumber 使用它,就像上面的代码一样,来设置显示的数字。因为display()是这个类和程序的其它的部分的一个接口,所以这个槽是公有的。 几个例程把QScrollBar的newValue信号连接到display槽,所以LCD数字可以继续显示滚动条的值。 请注意display()被重载了,当你把一个信号和这个槽相连的时候,Qt将会选择适当的版本。如果使用回调,你会发现五个不同的名字并且自己来跟踪类型。 一些不相关的成员函数已经从例子中省略了。 5
36、.2 QMdiArea类5.2.1多文档界面程序在主窗口的中央区域能够提供多个文档的应用程序就被称作多文档界面应用程序(Multiple Document Interface)。Qt中的QMidArea类为Qt程序实现多文档界面提供了途径。5.2.2 QMdiArea类QMdiArea类,本质上更像一个MDI窗口的窗口管理器。例如,它绘出了它管理的窗口本身,并把它们组织成级联模式或砖模式。QMdiArea通常被用作QMainWindow的中心部件来创建MDI应用程序,但是也可以放置在任何布局中。下面的代码说明了如何添加了一个QMdiArea到主窗口QMainWindow:QMainWindow
37、 *mainWindow = new QMainWindow; mainWindow-setCentralWidget (mdiArea);QMdiArea中的子窗口是QMdiSubWindow.类的实例,它们通过addSubWindow()函数被:加到多文本界面区域。子窗口获得键盘焦点或它的setFocus()函数被调用后可以变得活跃,用户通常可以将光标移到子窗口上来使它被激活。当活动窗口变化时多文本界面区域发出subWindowActivated()信号, activeSubWindow()函数返回当前活动的窗口对象。调用subWindowList()函数,程序可以方便的得到一个所有子窗口
38、的列表。子窗口可以按当前预定义的排序方案WindowOrder进行排序。子窗口部件 QMdiArea提供了两个内置的布局策略:cascadeSubWindows()和tileSubWindows()来对所有级联排序(如图7)和砖排序窗口(如图8)进行。两者通过信号和槽机制都是很容易和菜单条目相关联的。图7 级联排序图8 砖排序5.3 QSyntaxHighlighter类要自定义Highlighter每个Editor 中的文本语法高亮显示方案,必须对Qt中文本语法高亮显示类-QSyntaxHighlighter类进行特化。因此,我们定义了一个继承自QSyntaxHighlighter类High
39、lighter 来实现特定关键字的高亮显示。这里,对QSyntaxHighlighter进行简单介绍:QSyntaxHighlighter允许你定义语法高亮显示规则,也可以通过这个类来查询当前文本的格式和用户数据。它是一个用来实现QTextDocument语法高亮显示的基类,他可以自动高亮显示QTextDocument的特定文本。语法高亮显示常用于当用户输入文本在一个特定的格式(例如源代码)时,帮助用户阅读文本,找出语法错误。图9是一个应用QSyntaxHighlighter的例子。自定义文本高亮显示方案,我们必须子类化QSyntaxHighlighter并且重写highlightBlock(
40、)方法。当你构造QSyntaxHighlighter子类的实例时,需要传入一个QTextDocument类型的参数其告知编译器你希望高亮显示方案的应用文本区域,例如:QTextEdit *editor = new QTextEdit;MyHighlight *highlight = new MyHighlight(editor-document();之后,当程序需要时你定义的highlightBlock()方法会被自动调用,用你自定义的highlightBlock()函数应用格式于部分文本(例如,设置字体和颜色)。QSyntaxHighlighter提供setFormat()函数来给定QTex
41、tCharFormat并将其应用于当前的文本块。例如:void MyHighlighter:highlightBlock(const QString &text) QTextCharFormat myClassFormat; myClassFormat.setFontWeight(QFont:Bold); myClassFormat.setForeground(Qt:darkMagenta); QString pattern = bMyA-Za-z+b; QRegExp expression(pattern); int index = text.indexOf(expression); whi
42、le (index = 0) int length = expression.matchedLength(); setFormat(index, length, myClassFormat); index = text.indexOf(expression, index + length); 图9 应用QSyntaxHighlighter的例子5.4插件的实现机制Qt 插件由共享库(Unix/Linux 上的 .so 以及 Windows 上的 .dll)实现。简单来说,一切关于共享库的知识,在 Qt 插件上同样适用。Qt 插件根本上说就是符合某些规定的共享库。从另外一方面说,插件架构是不同于
43、普通的链接的。我们可以将插件理解成一种动态链接技术,而非插件则是静态链接。插件系统机制是,核心系统指定接口和交换的数据格式(通常是以 API 的形式给出)。这种接口和特定的数据格式形成一种协议,不同模块通过这种协议与核心系统进行交互,或者是能够通过核心系统将自己的某些功能暴露出来(也就是为其它模块提供服务)。仅仅将所需要的功能放到动态链接库里面并不能满足一些特定的需求,比如,我们希望应用程序能够自动发现某一特定文件夹下的动态链接库,能够知道这些动态链接库内部的逻辑是怎样的,我们能够如何使用它们。这些需求,对于一个普通的动态链接库模块是不能实现的(或者说,不是自动提供的功能,而需要编写额外的代码
44、)。于是,我们需要一个插件架构。一般来说,动态链接库能够对外暴露出的是一系列函数。对于链接库内部的变量、类以及对象则不能直接访问。编写插件则可以绕过这个限制,插件内部的变量、对象都可以直接对应用程序暴露出来。5.5插件的优点5.5.1插件机制加快运行速度在Qt中,在插件的运行采用的是一种叫延时加载的模式,也就是只有在平台或是其他插件需要调用某一插件时,操作系统才将该插件调入到内存,但在内存紧张时操作系统可将部分插件从内存中清除掉,这样就明显提高了内存的利用率,正因为此延时加载机制,在Qt住主程序平台启动时,会发现采用插件机制的启动速度较不采用插件机制的平台相对的明显加快。5.5.2插件机制使软
45、件的后期维护变得简单当我们针对于插件的客户需求有变化时,我们只需要重新编译更新插件即可,没必要完全更新软件系统,这使得软件更新的成本变得简单,容易,使客户更易于接受。5.6编写Qt插件Qt提供了两种API创建插件: 较高级的API用来编写扩展Qt本身:自定义数据库驱动程序,图像格式,文本编解码器,自定义风格等。较低级别的API扩展Qt应用程序。例如,如果您想要编写一个定制的QStyle子类,Qt应用程序动态地加载它,您将使用更高级的API。因为高级API是建立在低级API之上,所以有一些问题是互通的。如果你想提供插件使用Qt设计器,请参考Qt设计器模块的文档。5.6.1较高级的API 较高级的API: 扩展Qt本身:编写一个插件,扩展了Qt本身的插件,是通过子类化适当的插件基类,实现一些功能,并添加一个宏。Qt有几个插件基类。子类插件默认存储在父类插件目录的子目录。,如果按此规则他们不存储在正确的目录。Qt将会找不到插件,导致插件无法运行。写一个插件通过