Transcript
CZECH TECHNICAL UNIVERSITY IN PRAGUE Faculty of Civil Engineering Department of Mapping and Cartography
BACHELOR’S THESIS OBJECT – ORIENTED GUI FOR GNU GAMA
Author: Thesis supervisor: Date:
Jiří Novák prof. Ing. Aleš Čepek, CSc. January - May 2010
Here will be placed the thesis submission paper. . .
ii
Acknowledgments I would like to thank to all of the people who gave me important pieces of advice while working on my bachelor’s thesis. At the first place it will be prof. Ing. Aleš Čepek, CSc. for his guidance, for his proposals and suggestions, which richened this paper and for his help with resolving some C++ related doubts (especially the line-up to his computational library). Next I have to mention Ing. Jan Pytel, Ph.D., with whom I consulted most of the application design related questions and who helped me a lot with the creation of the back-bone classes in my application and who drove me to the idea of incorporating a XSL transformation for a nicer appearance of the adjustment results. Last, but not least, I have to thank my family for their support and calm environment they supplied me with during the time of my studies and writing of this paper.
Manifestation I declare that I worked out this bachelor’s thesis on my own with the exploitation of the literature mentioned in the Bibliography section. In Prague, 10th of May ..................... Jiří Novák
iii
Name of the paper: Author: Study program: Branch of study: Thesis supervisor: Consultant: Abstract:
Keywords:
Object – oriented GUI for GNU Gama Jiří Novák Geodesy and cartography Geoinformatics prof. Ing. Aleš Čepek, CSc. Implementation of an object oriented graphical user interface (GUI) above the GNU Gama computational library for adjusting geodetic networks. Written in C++ making use of Qt platform version 4.6.0. Objective was to create a base, above which would be possible to extend the functionality with the exploitation of add-ons (plugins manager), create a projects manager and project XML representation (with the possibility of extensions to other tasks like sequential adjustment, etc.) and solve the question of saving and restoring application settings. GNU, Gama, geodesy, networks, adjustment, C++, Qt, XML
iv
Contents Prologue
1
1 GNU Gama 1.1 Basic information . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1.2 Program’s input XML . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1.3 Porting GNU Gama to the Qt framework . . . . . . . . . . . . . . . . . . . . . . .
2 2 3 4
2 Qt 2.1 2.2 2.3 2.4 2.5 2.6 2.7 2.8 2.9
SDK Brief history . . . . . . . . . . . Licensing . . . . . . . . . . . . Supported platforms and ports Installation . . . . . . . . . . . Qt Development Tools . . . . . Qt Modules . . . . . . . . . . . Signals and slots mechanism . . Static build . . . . . . . . . . . Plugins support . . . . . . . . .
. . . . . . . . .
9 9 10 10 11 12 12 13 15 17
3 Design patterns 3.1 Singleton . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
21 21
4 XML 4.1 XML Schema Definition (XSD) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4.2 XSL Transformation (XSLT) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
23 23 25
5 QGama Application 5.1 Basic information . . . . . . 5.2 Application structure . . . . 5.3 Storing Settings . . . . . . . 5.4 Projects Manager . . . . . . 5.5 Plugins Manager . . . . . . 5.6 XML validation plugin . . . 5.7 Adjustment using libqgama 5.8 Internationalization . . . . . 5.9 Future plans . . . . . . . . .
. . . . . . . . .
29 29 30 31 32 33 34 35 36 38
6 Distribution of binaries 6.1 Windows . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6.2 Debian package . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
39 39 41
Epilogue
45
Bibliography
46
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
v
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
A QGama User Guide A.1 Installation . . . . . . . . A.2 Starting QGama . . . . . A.3 New project creation . . . A.4 Opening existing project . A.5 Adding network to project A.6 Adjustment settings . . . A.7 Adjusting the network . . A.8 XML validation plugin . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
B Content of the attached CD
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
I . I . I . I . III . IV . V . VI . VIII X
vi
List of Figures 2.1 2.2 2.3
Qt Software Development Kit Components diagram (source: [8]). . . . . . . . . . . Qt/X11 library dependencies scheme. . . . . . . . . . . . . . . . . . . . . . . . . . . Qt/Windows library dependencies scheme. . . . . . . . . . . . . . . . . . . . . . . .
9 16 17
4.1
XSLT process flow (source: [12]). . . . . . . . . . . . . . . . . . . . . . . . . . . . .
26
5.1
Localization of strings in Qt Linguist. . . . . . . . . . . . . . . . . . . . . . . . . .
37
A.1 Startup of QGama program. . . . . . . . . . . A.2 New project successfully created. . . . . . . . A.3 Opening existing project. . . . . . . . . . . . A.4 New network added into an empty project. . A.5 An example of the HTML adjustment output. A.6 An example of the TXT adjustment output. . A.7 An example of the XML adjustment output. . A.8 Plugin Manager dialog. . . . . . . . . . . . . A.9 XML Validation plugin: an error found. . . . A.10 XML Validation plugin: validation successful.
vii
. . . . . . . . . .
. . . . . . . . . .
. . . . . . . . . .
. . . . . . . . . .
. . . . . . . . . .
. . . . . . . . . .
. . . . . . . . . .
. . . . . . . . . .
. . . . . . . . . .
. . . . . . . . . .
. . . . . . . . . .
. . . . . . . . . .
. . . . . . . . . .
. . . . . . . . . .
. . . . . . . . . .
. . . . . . . . . .
. . . . . . . . . .
. . . . . . . . . .
. . . . . . . . . .
. . . . . . . . . .
. . . . . . . . . .
II III III V VII VII VIII VIII IX IX
Prologue Although there have already been two attempts of creating a GUI above the GNU Gama computational library, none of them was utilized widely in the praxis. I refer to the Rocinante project of Jan Pytel1 and the Gwion project of Jiří Bartoš. Development of the first one ended prematurely at the version 1.0.0., a main disadvantage of the second one was its tight bound to the KDE desktop libraries and therefore impossibility of porting it to Windows, which is still the most frequently used operating system. My project is thus the third one in a row and I hope that this attempt will finally gain the audience, which such a great piece of software as GNU Gama deserves. My goal is to learn the best from both its ancestors and avoid committing the same errors which precluded its expansion. Therefore I tried to plan carefully the objectives of my project. Those were primarily: create it portable, so that none user of today’s most frequent operating systems (Windows, Unix, Linux, MacOS) would be discriminated; create it multilingual, so that there would be also a possibility to offer the resulting program to foreign surveyors; create it easily extendable with add-ons, so that there would be a way how to add new functionalities developed by the third sides without the need to interfere in the main application source code; get rid of the manual XML input data entering, etc. During my studying internship in Valencia, Spain I also came in contact with another adjustment software called RedTop 2 , which was developed at the local university department and which captivated me above all by its user affability. Also from there I tried to learn. A result of my efforts is the origin of the QGama program. Although in the time of writing this thesis not all of the intended objectives are fulfilled (XML input format is still the only way how to enter the measurements), there was created a solid fundamentals for the future project extensibility. I have divided this bachelor thesis into several chapters in which I wanted to lead a reader through the process of QGama development and familiarize him with its usage. I will start with the GNU Gama project description in general, continue with its porting under the Qt platform, next I will explain the basics of the GUI development in this framework (including the signal-slots mechanism, plugins mechanism, etc). Also the design and XML patterns utilized within the code of my application will be covered and a gentle look inside of how the things are done will be provided. Last chapter I dedicated to the distribution of the application binaries, because I thing it’s an important issue if you want to come through to the official distributions. I will demonstrate how to create an installer/uninstaller for Windows and a .deb package for the most commonly used Linux distribution nowadays - Ubuntu 10.4. Lucid Lynx. In appendix I attach the program’s User Guide.
1 Rocinante’s 2 RedTop:
home page: http://roci.sourceforge.net/) http://www.upv.es/miw/infoweb/dicgf/info/Setup-REDTOP.exe
1
Chapter 1
GNU Gama 1.1
Basic information
GNU Gama is a project dedicated to the adjustment of geodetic networks (Gama acronym is created from the words Geodesy and Mapping). ”It is intended for use with traditional geodetic surveyings which are still used and needed in special measurements (e.g., underground or high precision engineering measurements) where the Global Positioning System (GPS) cannot be used.” 1 Project’s home page is available at: http://www.gnu.org/software/gama Documentation with detailed description of installation and gama-local program usage, explanation of the input XML data format, applied theory of adjustment, data structures and algorithms that are used and lots of useful examples and commentaries either of the input data and output protocol, is available in many formats (such as HTML, ASCII text, DVI, PS, PDF, etc.) at: http://www.gnu.org/software/gama/manual/index.html Latest source codes (current version in the time of writing this thesis is 1.9.07) can be downloaded from an anonymous read-only CVS repository: cvs -d:pserver:
[email protected]:/sources/gama co -P gama
At the same place is also available a collection of sample networks, which can serve as a good start point while beginning adjusting with GNU Gama. It can be checkedout with the following command: cvs -d:pserver:
[email protected]:/sources/gama co examples
Project’s maintainer as well the main developer and the guru of whole the idea that firstly emerged back in the year 1998 is Aleš Čepek. Nevertheless project had tens of other contributors among who we can depict those from our faculty: Jiří Veselý who wrote a Median library for calculating the approximate coordinates that enter the calculations, Petr Doubrava or Jan Pytel. Complete list of all project’s contributors is provided within the documentation. GNU Gama is written in C++ and enables both: 1. adjustment in local Cartesian coordinate systems (fully supported, provided by a command line program gama-local ) 2. adjustment in global coordinate systems (partly supported, program gama-g3 ) 1 Cited
from: GNU Gama Project Homepage [6]
2
CHAPTER 1. GNU GAMA
1.2. PROGRAM’S INPUT XML
GNU Gama provides both the ”classical” adjustment and lots of pieces of information for the adjusted network analysis. Default numerical method for solving the system of error equations is an algorithm of singular value decomposition (SVD), but there is a possibility to use also GramSchmidt orthogonalization (GSO), Choleski decomposition or a method of the envelope. Currently supported observation types are: • horizontal directions and distances • horizontal angles • slope distances and zenith angles • height differences • observed coordinates (that is coordinates with given variance-covariance matrix) • observed coordinate differences (vectors)
1.2
Program’s input XML
The input data format for gama-local (local geodetic network adjustment) is defined in accordance with the definition of Extended Markup Language (XML) for the structured data description. XML is a general markup language that was developed and standardized by a W3C consortium.2 Deals of an simplified form of the older SGML. It’s designed primarily for the exchange of data between applications and for publishing of the documents, where it describes the structure from the factual content point of view of the individual parts, it doesn’t bother with the appearance. Document presentation form can be later defined with the usage of cascading style sheets (CSS) or a transformation into the other document type (which I exploited in the XML - HTML conversion which will be described later in the section 4.2). XML enables us to define the set of proper marks, which we would like to use (in the case of GNU Gama project that are marks relating the points, measurements, network parameters, etc). We can also define the set of marks which can appear within the document instance and specify additional rules for them, like: • in which context they can appear • which elements and attributes can each mark include, etc. This structure definition file could be either DTD, which uses GNU Gama or XSD, which I am using in QGama for validating of the input files. More about XML features and patterns used within QGama application will be covered in a separate chapter 4. GNU Gama utilizes for the processing of XML a parser Expat (version 1.1) written by James Clark3 , which comes bundled as a part of the source code and can be utilized if client’s operating system doesn’t have newer version of it installed. An Example of the input XML for GNU Gama For better illustration of how that XML can look like a depicted one of the sample networks propagated within the official set of examples, concretely a file ”skorepa-dusek.gkf”, which is a free local network formed by 4 points and measured horizontal distances and angles between them. 2 XML
specification: http://www.w3.org/TR/xml/ homepage: http://www.jclark.com/xml/expat.html
3 Expat
3
CHAPTER 1. GNU GAMA
1.3. PORTING GNU GAMA TO THE QT FRAMEWORK
Volna sit s merenymi delkami a uhly ----------------------------------Skorepa, Z. - Dusek, R.: Aplikace singularniho rozkladu matice..., GaKO 41/83, 1995, c. 7
x="1118103.84" x="1117697.19" x="1119159.92" x="1119260.13"
y="668559.14" y="667132.98" y="667054.59" y="667932.57"
adj="XY" /> adj="XY" /> adj="XY" /> adj="xy" />
s2 --> w6 -->
val="1464.841" /> fs="1" val="52.2111" /> fs="4" val="33.5127" /> fs="1" val="85.7233" />
val="883.684" /> fs="2" val="57.6310" /> fs="1" val="46.1976" /> fs="2" val="103.8266"/>
--> --> --> -->
1.3
Porting GNU Gama to the Qt framework
So far the only way how GNU Gama could be compiled was with the exploitation of GNU Autotools. Under Unix-like systems it is, without any doubts, the most preferably way, but its practically impossible port the same source code to any other platform. Therefore my first goal in this thesis was to move the compiling of the computational library and its traditional command line utilities: gama-local, gama-g3 and gama-local-xml2txt under Qt
4
CHAPTER 1. GNU GAMA
1.3. PORTING GNU GAMA TO THE QT FRAMEWORK
framework and enable thus the portability of the source code also to Windows and MacOS.
GNU Autotools GNU Autotools (also known as just the GNU build system) is a suite of programming tools produced by the GNU project, which are designed to assist in making source-code packages portable among the Unix-like systems. It consists of various utilities like: Autoconf, Automake, Libtool, etc. which are most of the times utilized together with other GNU’s programs: make gcc (GNU Compiler Collection). For our purpose Autoconf is the most important one. It processes configure.ac file and generates a configure script. This script, if subsequently run, will input other template files like Makefile.in and create a platform specific Makefile as its output. Application should be then compiled successfully with it, because configure script has already ”prepared the environment” (by enforcing a completion of the missing includes, optimizing of the compile process for the computer’s architecture using only available utilities). A typical approach of compiling and installing such application is: [jirka@cohen gama]$ ./configure [jirka@cohen gama]$ make [jirka@cohen gama]$ make install
Compiling GNU Gama with qmake Using qmake (which come as part of the Qt framework, but can be used for any software not dependent whether was written in Qt or not) is another possibility of obtaining automatic generation of the platform specific Makefiles. From my point of view it is even an easier way, because as an input is needed only one single ASCII project file (.pro), where the desired configuration is specified (basics of its format will be covered later in this section). A principal idea was to keep the original source codes intact and provide just an alternative way how to compile them. The presumption was that user would get the GNU Gama source codes and my QGama source codes and place them on the same hierarchy level. In the following example I will download most recent versions of both of them from their version control systems. [jirka@cohen ~]$ cvs -d:pserver:
[email protected]:/sources/gama co -P gama [jirka@cohen ~]$ git clone git://qgama.git.sourceforge.net/gitroot/qgama/qgama [jirka@cohen ~]$ ls gama qgama
If we explore the content of the qgama directory we will see that its separated into several sub-projects. [jirka@cohen qgama]$ ls gama-g3 gama-local-xml2txt gama-local libqgama
options.pri qgama-core
qgama-plugins qgama.pro
scripts
Our concern should be focused now on the file qgama.pro. It is a wrapper where we can decide which parts of the QGama project we desire to compile. Let’s have a look in it. [jirka@cohen qgama]$ cat qgama.pro TEMPLATE=subdirs CONFIG+=ordered all all { SUBDIRS=libqgama \ gama-local \ gama-local-xml2txt \ gama-g3 \
5
CHAPTER 1. GNU GAMA
1.3. PORTING GNU GAMA TO THE QT FRAMEWORK
qgama-plugins \ qgama-core/src/qgama-core.pro } ...
We see that a template is subdirs, that means qmake will generate Makefiles for all of the project files placed in the directories within the SUBDIRS variable and create the rules so that those would be automatically called while running the principal Makefile. If no project file is explicitly mentioned, it’s supposed that it will have the same name as its parent directory. By specifying name-of-the-target {commands} I have created several combinations of projects parts. We can choose which target should be compiled by adding its name to the CONFIG variable. Default option is all (that means QGama GUI and its plugins are also included), nevertheless now we will suffice just with gama option that includes programs: gama-local, gama-g3 and gamalocal-xml2txt. [jirka@cohen ~]$ qmake qgama.pro [jirka@cohen ~]$ make [jirka@cohen ~]$ sudo make install
By default when installing binaries are copied to /usr/local/bin, if you wish to change it, you can specify a different prefix while executing qmake. E.g. if you want to place the result binaries to /usr/bin, execute it like this: [jirka@cohen ~]$ qmake qgama.pro PREFIX=/usr/
Of course we could also compile each sub-project separately - all dependencies are properly resolved. Project file format As was already explained project file is a simple text file with the configuration for qmake. Although it can be generated automatically (by executing qmake -project), in the majority of cases developers edit it by hand to satisfy their needs. Besides variables like HEADERS, SOURCES, FORMS, which define us which files belong to the project, there exist tens of variables through which we can customize the build process of our application. You can add elements to the variable by specifying +=, remove them with -= or do a complete overwrite with =. Detailed variable reference is available within the Qt documentation4 . By default qmake suppose configuration in accordance with the mkspecs file corresponding to the combination of operating system and compiler in use (in my case configuration is taken from /opt/qtsdk-2009.05/qt/mkspecs/linux-g++/qmake.conf). It’s ever a good place to start, when you are in doubts about some configuration related issues. If we look into it: [jirka@cohen qgama]$ cat /opt/qtsdk-2009.05/qt/mkspecs/linux-g++/qmake.conf # # qmake configuration for linux-g++ # MAKEFILE_GENERATOR = UNIX TEMPLATE = app CONFIG += qt warn_on release incremental link_prl QT += core gui QMAKE_INCREMENTAL_STYLE = sublib include(../common/g++.conf) include(../common/linux.conf) load(qt_config) 4 QMake
variable reference: http://doc.trolltech.com/4.6/qmake-variable-reference.html
6
CHAPTER 1. GNU GAMA
1.3. PORTING GNU GAMA TO THE QT FRAMEWORK
We can see for example that by default two Qt modules (QtCore and QtGui ) are included, that default template is application in release mode, etc. In the case of GNU Gama for example I had to disable the automatic include of Qt libraries, deny the threads support (because no threads are used within the code and its support would require unwanted additional run-time library), force static linking with the compiler run-time library. If some condition is destined just for specific family of platforms, we can distinguish it with a prefix (unix:, win32:, mac:). Lines introduced with a hash-mark (#) are commentaries. # don’t include qt support CONFIG -= qt # disable threads support, because we don’t need it win32:QMAKE_LFLAGS_EXCEPTIONS_ON = -Wl win32:QMAKE_CXXFLAGS_EXCEPTIONS_ON = -fexceptions # link statically with the compiler library win32:QMAKE_LFLAGS_RELEASE += -static-libgcc # specify that deals a console application win32:CONFIG+=console
Other important issue was how to resolve dependencies. All of the programs of the GNU Gama set depends on the computational library (gamalib) and a file config.h, which contains information about program versions and that is created automatically by a script when needed. Both of those two dependences had to be resolved, because as my thesis supervisor said: ”There doesn’t exist anything like a proper succession of compilation!” This can be done by creating an extra target, which will be run before the proper project compilation. Unfortunately I haven’t found a better way than explicitly specify the commands which should be executed. This is a limitation because if a user uses a different compiler, the build process will fail at this phase. # generating of config.h file with the version info unix:version.commands = cd ../scripts && g++ -o version version.cpp && ./version win32:version.commands = cd ../scripts && mingw32-g++.exe -o version version.cpp && version.exe QMAKE_EXTRA_TARGETS += version PRE_TARGETDEPS += version # dependency to libqgama unix:libqgama.commands = cd ../libqgama && $$QMAKE_QMAKE libqgama.pro && make win32:libqgama.commands = cd ../libqgama && $$QMAKE_QMAKE libqgama.pro && mingw32-make.exe QMAKE_EXTRA_TARGETS += libqgama PRE_TARGETDEPS+=libqgama
Other thing which deserves to be mentioned is how to customize the classical Makefile targets install and clean. We can add files to the clean target by adding values to the QMAKE CLEAN variable. With the installs target it’s similar, we just need to specify a separate target for adding it to the INSTALLS variable. # add a file to be cleaned QMAKE_CLEAN += ../config.h # initalize the installs target isEmpty(PREFIX) { PREFIX = /usr/local } prefix.path = $$PREFIX target.path = $$prefix.path/bin INSTALLS += target
7
CHAPTER 1. GNU GAMA
1.3. PORTING GNU GAMA TO THE QT FRAMEWORK
We can also define our own variables and use them within the project file’s code or access variables passed as parameters while executing qmake (operator for accessing a variable value is $$). This is the case of the variable PREFIX in the preceding example. Nevertheless qmake also provides us a bunch of built-in functions that enable processing of the variable contents or effectuation of some basic conditional tests. For example we can enforce a successful include of some file and if that is not fulfilled, exit with an error message. # include options !include(../options.pri) : error(Couldn’t find the options.pri file!)
For full qmake functional reference consult Qt documentation5 . GUI related features of qmake and its project file representation will be discussed in following chapter.
5 QMake
functional reference: http://doc.trolltech.com/4.6/qmake-function-reference.html
8
Chapter 2
Qt SDK QGama project was developed with the utilization of Qt – Cross-platform application and UI framework. ”It includes a cross-platform class library, integrated development tools and a crossplatform IDE. Using Qt, you can write web-enabled applications once and deploy them across many desktop and embedded operating systems without rewriting the source code.”[8]
Figure 2.1: Qt Software Development Kit Components diagram (source: [8]).
2.1
Brief history
Qt framework’s development started in 1991 as a project of Haavard Nord and Eirik ChambeEng, two ex-classmates from Norwegian Institute of Technology in Trondheim. ”The letter ’Q’ was chosen as the class prefix because the letter looked beautiful in Haavard’s 9
CHAPTER 2. QT SDK
2.2. LICENSING
Emacs font. The ’t’ was added to stand for ”toolkit”, inspired by Xt, the X Toolkit. The company was incorporated on March 4, 1994, originally as Quasar Technologies, then as Troll Tech, and then as Trolltech.” 1 On May 20, 1995 first publicly available release (Qt 0.90) was introduced for Unix (Qt/X11 ) and Windows (Qt/Windows). It was available under two licenses: commercial edition for commercial development (Unix, Windows) and a free software edition for open source development (Unix). In 2000 Trolltech released Qtopia Core (Qt/Embedded ), which was designed to run on embedded Linux devices and provided is own window system. At the end of 2001, Qt 3.0 with support for Mac OS X, was released. In 2005 emerged Qt 4.0. Besides hundreds of improvements (yet with more than 500 classes and 9000 functions) it was the first Qt edition available for both commercial and open source development on all the supported platforms. In 2008 Nokia acquired Trolltech ASA and changed the name first to Qt Software, then to Qt Development Frameworks. Since then it has focused on Qt development to turn it into the main development platform for its devices, including a port to the Symbian S60 platform. The source code was made available over Gitorious, a community oriented git source code repository, in order to gather an even broader community that is not only using Qt but also helping to improve it.2 Current release, at the time of writing this thesis, is 4.6.2.
2.2
Licensing
From the very beginning Qt was available under a commercial license which justified development of proprietary applications without any licensing restrictions. On the other hand, as time went on, there existed a bunch of free licenses. Until version 1.45, Qt source codes were published under the FreeQt license (source codes were available, but redistribution of its modifications was prohibited). From version 2.0 it changed to Q Public License (QPL), but but was illegal to distribute products derived from both GPL’ed and QPL’ed code. Presently, Qt is available under the GNU Lesser General Public License version 2.1 (LGPL) 3 , making it available making it available in both proprietary and open source software. It therefore implies, that also the GNU General Public License version 3.0 (GPL) 4 can be used (which will be my case). Detailed licenses comparison can be found at: http://qt.nokia.com/products/licensing
2.3
Supported platforms and ports
Qt is available for the following platforms: Linux/X11 (X Window Systems), Mac OS X, Windows (version with minGW or VS 2008 compiler), Embedded Linux, Windows CE, Symbian, Maemo (PDAs, Smartphones, etc.). Also there exist editions, where its commercial support and development has been stopped. This is the case of Qt Jambi – Qt for Java. Since Nokia opened the Qt source code to the community on Gitorious 5 some other ports developed by community appeared. For example: Qt for OpenSolaris, Qt for Haiku (completed) or Qt-iPhone, Android-Lighthouse (experimental). 1 Cited
from: C++ GUI Programming with Qt4 [1], page xv from: Qt (framework) [9] 3 Text of the license available at: http://www.gnu.org/licenses/lgpl.html 4 Text of the license available at: http://www.gnu.org/licenses/gpl.html 5 Open source infrastructure for hosting open source project that use Git version control system http://www. gitorious.org/ 2 Cited
10
CHAPTER 2. QT SDK
2.4
2.4. INSTALLATION
Installation
As we have already said before, Qt is available either under commercial or open source license. As I have utilized the open source edition, whatever I will explain next will be meant regarded to it. If we go to the Qt’s official download page6 and select LGPL tab, we will see that two types of source packages are available: • Qt SDK: Complete Development Environment that includes the tools you need to build cross-platform applications with Qt in a single install (Qt libraries, Qt Creator IDE, Qt development tools). • Qt: Framework Only that includes only the source packages of the Qt framework only (during the installation process can be chosen between LGPL or GPL license).
Qt SDK: Complete Development Environment In my case I have chosen a complete Qt SDK for Linux/X11 32bit. It deals a graphical installer file, only thing you have to do is make it executable and run it. During the installation process you are also prompted to install manually some additional packages, on Debian/Ubuntu you can install them with: sudo apt-get install libglib2.0-dev libSM-dev libxrender-dev libfontconfig1-dev \ libxext-dev libglui-dev
On my Fedora 12 Constantine, I had to search the Internet discussion forums to find the corresponding ones, but thats the deal of using non-mainstream distribution. . . All of the files then will be copied into the specified location and corresponding environment variables (QTDIR, QTINC, QTLIB ) will be set. chmod u+x qt-sdk-linux-x86-opensource-2009.05.bin ./qt-sdk-linux-x86-opensource-2009.05.bin
Qt is already built dynamically under LGPL license, we can start using it. Let’s have look into the Qt SDK directory structure (discussing only the most significant parts): • bin/ - qtcreator and gdb binaries • qt/bin/ - rest of Qt utilities like: designer, assistant, moc, uic, etc. • qt/lib/ - compiled Qt libraries • qt/include/ - Qt header files • qt/mkspecs/ - default configurations for individual platform–compiler pairs • qt/doc/html/ - offline version of Qt online help that visualizes Qt Assistant
Qt: Framework only If you choose a framework only package (which is what I did in the case of a static Qt build), you just need to untar the package into desired location, set environment variables, configure it and run make. Most important step is configuration. You can obtain detailed description of all possible parameters by executing ./configure --help, but I think the most important ones are -static or -dynamic (default), which decides whether a shared libraries (.so, .dll) or static libraries (.a) approach will be used and -prefix, which decides the root destination directory (default is /usr/local/Trolltech/Qt-4.6.0). The rest of them can be left on its default values. A typical process of a successful installation of statically linked Qt can look like this: 6 Qt
official download page: http://qt.nokia.com/downloads
11
CHAPTER 2. QT SDK
[jirka@cohen [jirka@cohen [jirka@cohen [jirka@cohen [jirka@cohen [jirka@cohen [jirka@cohen [jirka@cohen [jirka@cohen
2.5
2.5. QT DEVELOPMENT TOOLS
opt]$ gunzip qt-everywhere-opensource-src-4.6.0.tar.gz opt]$ tar xvf qt-everywhere-opensource-src-4.6.0.tar opt]$ mv qt-everywhere-opensource-src-4.6.0 qt-4.6.0-static && cd qt-4.6.0-static qt-4.6.0-static]$ sudo ./configure -static -prefix /opt/qt-4.6.0-static qt-4.6.0-static]$ make sub-src qt-4.6.0-static]$ export PATH=\$PATH:/opt/qt-4.6.0-static/bin qt-4.6.0-static]$ export QTDIR=\$PATH:/opt/qt-4.6.0-static/ qt-4.6.0-static]$ export QTINC=\$PATH:/opt/qt-4.6.0-static/include/ qt-4.6.0-static]$ export QTLIB=\$PATH:/opt/qt-4.6.0-static/lib/
Qt Development Tools
In this section I just want to provide a a brief description of some essential development tools I used while implementing my application, more information can be found at: http://qt.nokia.com/products/developer-tools
Qt Creator Qt Creator is relatively new cross-platform integrated development environment (IDE) - in the time of writing this thesis current version was 1.3.0. It includes a visual debugger and an integrated GUI designer, with which you can create forms interactively. The editor itself has standard features for auto-completion, syntax highlighting or interconnection with Qt documentation. By default it uses the GNU Compiler Collection on Linux and either MinGW or VS2008 compiler on Windows.
Qt Designer If I quote the words of its developers, Qt Designer enables us to. . .: ”rapidly design and build widgets and dialogs using on-screen forms using the same widgets that will be used in your application. Forms created with Qt Designer are fully-functional, and they can be previewed so that you can ensure that they will look and feel exactly as you intended.” 7 Qt Designer stores the user interface configuration in a special XML and utilizes a separate tool uic (Qt’s User Interface Compiler) for translating it into C++ representation class.
Qt Linguist Qt Linguist is a set of tools for application internationalization. More about that will be covered in the section 5.8.
Qt Assistant Qt Assistant is an HTML documentation reader, which can be customized and redistributed together with our application.
2.6
Qt Modules
Qt framework is divided into several individual modules. If it’s used qmake for building of the projects, only thing you have to do to start using classes from a new module, is to add it to QT variable inside your project file. For example if you want to process xml inputs, you will need QXMLReader class, which is inside xml module, and you can add it by the following line: QT += xml 7 Cited
from: Qt – A cross-platform application and UI framework (section: Qt Development Tools) [8]
12
CHAPTER 2. QT SDK
2.7. SIGNALS AND SLOTS MECHANISM
At this section I am going to focus only on those modules, that I am directly using within my QGama application. To get a complete overview of what more Qt offers, consult the modules section of the official documentation at: http://doc.trolltech.com/4.6/modules.html
QtCore Core non-graphical classes essential to every Qt application and used by other modules. Contains back-bone classes like: QObject, QString, QLocale, QFile, QDir etc.
QtGui Contains graphical user interface (GUI) components. It deals either specialized widgets (e.g. QCalendarWidget, QDockWidget) or containers (e.g. QGroupBox, QStackedWidget) and dialogs (e.g. QFileDialog, QFontDialog, QPrintDialog) or its general alternatives to inherit from (QWidget, QDialog).
QtWebKit Classes for displaying (QWebView ) web content as well as classes to render and interact with it (QWebInspector, QWebPage, QWebElement, etc.).
QtXml Classes for handling XML. It provides a XML stream reader/writer, with the implementation of both SAX8 and DOM9 approach.
QtXmlPatterns Classes providing XPath, XQuery, XSLT and XML Schema validation features.
2.7
Signals and slots mechanism
Signals and slots is a fundamental mechanism of all Qt programs. It enables a communication between objects, without knowing anything about each other. A typical example is when we want to project a change in one widget to a change in other widget (for example we can want to bind a widget visibility onto a button state (dependent if it’s toggled or not)). Only the classes inherited from QObject (no mater if directly or indirectly via some ancestor like for example QWidget) can employ the signal-slots mechanism. Qt Widgets has plenty of predefined signals and slots, but we can also add our customized ones. The concept is that QObject1 can send signals containing an event information about what has changed (in our small example it would be a boolean corresponding to the toggle state) which can be received by other QObject2’s slots. In their body is then implemented the desired ”reaction” to the first object’s change (in our case changing the QObject2’s visible attribute value). Only signals and slots with coincident parameters could be interconnected. There is one exception when we are trying to connect a signal with more parameters than the slot, this is possible - additional parameters are just ignored. Slots can be utilized for receiving a signal, but they are also an ordinary C++ member functions with the same characteristics (that means they can have the visibility specifier, they can be virtual, they can be overloaded, etc.) and thus can be invoked any time. 8 Simple API for XML: enables serial access to the XML document (http://en.wikipedia.org/wiki/Simple_ API_for_XML) 9 Document Object Model: object oriented access and editing of XML document (http://en.wikipedia.org/ wiki/Document_Object_Model)
13
CHAPTER 2. QT SDK
2.7. SIGNALS AND SLOTS MECHANISM
A typical connection statement looks like this: QObject::connect(sender, SIGNAL(signal), receiver, SLOT(slot));
sender and receiver are pointers to QObject, signal and slot are function signatures without the parameter names and SIGNAL() and SLOT() are macros that convert the argument to a string and adds an identifier at the beginning of this string - ”1” for signals, ”2” for slots. We can connect arbitrary number of signals into one slot or on the contrary one signal can be received by more than just a one slot. It’s also possible to connect one signal directly to the second one. Although it’s rarely needed (because it’s done automatically on QObject’s deletion), we can call explicitly a disconnection: QObject::disconnect(sender, SIGNAL(signal), receiver, SLOT(slot));
Practical example As taken over from the book C++ GUI Programming with Qt 4 [1], page 21. class Employee : public QObject { Q_OBJECT public: Employee() { mySalary = 0; } int salary() const { return mySalary; } public slots: void setSalary(int newSalary); signals: void salaryChanged(int newSalary); private: int mySalary; }; void Employee::setSalary(int newSalary) { if (newSalary != mySalary) { mySalary = newSalary; emit salaryChanged(mySalary); } }
It illustrates several things. First that signal–slots mechanism is not limited only to widgets, second that every class containing signals/slots has to have a macro Q OBJECT in its declaration, third that with command emit we can whenever emit a signal informing ”the rest of world” about the change of class’s private member.
Qt’s Meta-Object System An attentive reader could began to hesitate in this phase. How it’s possible that C++ compiler understands to the keywords like: signals, slot, emit, Q OBJECT ? It’s a good question, it does not! All of those are macros. Before supplying our source codes to a compiler a separate Qt tool called moc (Meta Object Compiler) has to ”retranslate it” into pure C++. It also utilizes another key service and that is introspection. ”It allows us to obtain a ”meta-information” about QObject subclasses at run time including the list of signals and slots supported by the object and its class name” 10 10 Cited
from: C++ GUI Programming with Qt4 [1], page 22
14
CHAPTER 2. QT SDK
2.8. STATIC BUILD
At first Q OBJECT macro creates declarations of the basic introspection functions like: metaObject(), tr(), qt metacall(), etc. Subsequently moc generates implementations for all of them and for all of the defined signals. QObject member functions then utilizes those introspection functions. To sum it up: a signal–slots mechanism reminds a design pattern called Observer (http:// en.wikipedia.org/wiki/Observer_pattern), but all of the code for registration/deregistration or invocation is generated automatically by moc. Objects thus doesn’t need to know anything about the underlying communication mechanism and doesn’t need to take care of, if there exists an object which would receive their emitted signal or whether was some object connected to its slots.
2.8
Static build
While working on QGama development I have encountered that my program depends on a significant number of shared libraries. For getting know which, I used a ldd (List Dynamic Dependences) program under Linux and Dependency Walker11 in Windows.
GNU/Linux dependencies A result on Linux scared me in the beginning. Here’s the output: [jirka@cohen bin]$ ldd ./qgama linux-gate.so.1 => (0x009cc000) libQtWebKit.so.4 => /opt/qtsdk-2009.05/qt/lib/libQtWebKit.so.4 (0x009cd000) libQtXmlPatterns.so.4 => /opt/qtsdk-2009.05/qt/lib/libQtXmlPatterns.so.4 (0x00407000) libQtNetwork.so.4 => /opt/qtsdk-2009.05/qt/lib/libQtNetwork.so.4 (0x00110000) libQtXml.so.4 => /opt/qtsdk-2009.05/qt/lib/libQtXml.so.4 (0x00239000) libQtGui.so.4 => /opt/qtsdk-2009.05/qt/lib/libQtGui.so.4 (0x06e07000) libQtCore.so.4 => /opt/qtsdk-2009.05/qt/lib/libQtCore.so.4 (0x05353000) libpthread.so.0 => /lib/libpthread.so.0 (0x00282000) libstdc++.so.6 => /usr/lib/libstdc++.so.6 (0x0029c000) libm.so.6 => /lib/libm.so.6 (0x00385000) libgcc_s.so.1 => /lib/libgcc_s.so.1 (0x003af000) libc.so.6 => /lib/libc.so.6 (0x06265000) libXrender.so.1 => /usr/lib/libXrender.so.1 (0x003cd000) libfontconfig.so.1 => /usr/lib/libfontconfig.so.1 (0x0087f000) libfreetype.so.6 => /usr/lib/freetype-freeworld/libfreetype.so.6 (0x008b4000) libz.so.1 => /lib/libz.so.1 (0x0094c000) libXext.so.6 => /usr/lib/libXext.so.6 (0x003d6000) libX11.so.6 => /usr/lib/libX11.so.6 (0x04aab000) libgthread-2.0.so.0 => /lib/libgthread-2.0.so.0 (0x00986000) librt.so.1 => /lib/librt.so.1 (0x0095f000) libglib-2.0.so.0 => /lib/libglib-2.0.so.0 (0x01b77000) libgobject-2.0.so.0 => /lib/libgobject-2.0.so.0 (0x0506c000) libSM.so.6 => /usr/lib/libSM.so.6 (0x00968000) libICE.so.6 => /usr/lib/libICE.so.6 (0x0098b000) libdl.so.2 => /lib/libdl.so.2 (0x00970000) /lib/ld-linux.so.2 (0x003e7000) libexpat.so.1 => /lib/libexpat.so.1 (0x0523a000) libxcb.so.1 => /usr/lib/libxcb.so.1 (0x009a5000) libuuid.so.1 => /lib/libuuid.so.1 (0x00975000) libXau.so.6 => /usr/lib/libXau.so.6 (0x0097a000)
We can see that there is a dependency to: • 5 Qt modules I am using within the application: libQtWebKit, libQtXmlPatterns, libQtXML, libQtGui, libQtCore • one additional Qt module libQtNetwork : which was brought by including libQtWebKit • libpthread for multithreading 11 Dependency
Walker homepage: http://www.dependencywalker.com/
15
CHAPTER 2. QT SDK
2.8. STATIC BUILD
• dynamic linker/loader libraries: ld-linux, libdl • Expat XML parser library: libexpat • compiler runtime library: libgcc s • open C/C++ libraries: libstdc++, libc, libm (arithmetical and mathematical functions), libz (compression), librt (socket functions, notification subsystem. . .) • other (mostly X11) dependencies: libglib (common event loop handling), libgthread, libgobject (included automatically with the preceding one), libXrender (X rendering extension), libfontconfig (font customization and configuration), libfreetype (font engine), libXext (X extensions), libX11 (X11 client), libICE (X Inter Client Exchange), libSM (X Session Management), libxcb (interface to the X Window system protocol), libuuid (generating unique identifiers), libXau (X authorization) Detail description of Qt/X11 library dependencies (including the following schema), can be found at: http://doc.qt.nokia.com/4.6/requirements-x11.html
Figure 2.2: Qt/X11 library dependencies scheme.
Windows dependencies On Windows, the situation was much more easier - only dependencies to the Qt modules libraries, standard win32 api (kernel32.dll, msvcrt.dll ) and utilized compiler (mingw10.dll, libgcc s dw21.dll ) were found. http://doc.qt.nokia.com/4.6/requirements-x11.html Nevertheless some QtWebKit brought once again two additional dependencies: phonon4.dll and qtnetwork4.dll. 16
CHAPTER 2. QT SDK
2.9. PLUGINS SUPPORT
Figure 2.3: Qt/Windows library dependencies scheme.
Static linking Required presumption for static build is to have Qt built statically (how we can achieve that was discussed in section A.1). On Windows this is basically all we have to do, the rest will solve qmake internally and generates us a binary that depends yet only on the win32 api libraries. On Linux the situation is a bit more complicated. First of all we have to get static builds of all dependent libraries. Those are usually contained in the libraries development -dev packages. Once we have them, only thing which remains is to add few lines into our project file: unix:QMAKE_LFLAGS_SHAPP += -static unix:LIBS += -lXext -lm -ldl -lSM -lICE
First instruction explicitly says, we want to do a static build, in the second one we specify against which libraries.
Conclusihon Although I finally achieved completely static build on Linux: [jirka@cohen bin]$ ldd ./qgama not a dynamic executable
It turned out to be wrong way to go, because Qt, while linked statically, disabled the Plugin mechanism support, which was one of the main goals of my thesis. Therefore I had to proceed the way of distributing the application with dynamic libraries by package on Linux and installer / uninstaller on Windows. More about its creation in the chapter 6.
2.9
Plugins support
Qt has implemented it’s own mechanism to deal with plugins. It’s very simple and I will demonstrate it on the excerpts of an official Qt example called ”Echoplugin”. Full code accessible at: 17
CHAPTER 2. QT SDK
2.9. PLUGINS SUPPORT
http://doc.trolltech.com/4.6/tools-echoplugin.html
Core application part First of all we need an abstract class (or interface) to be defined somewhere in the main application source code. This interface then every plugin, which wishes extending the main application, will have to implement. In this case it consist of just a one pure virtual method called echo and a virtual destructor with empty function body. class EchoInterface { public: virtual ~EchoInterface() {} virtual QString echo(const QString &message) = 0; }; Q_DECLARE_INTERFACE(EchoInterface, "com.trolltech.Plugin.EchoInterface/1.0");
At the end is then called a Qt macro for declaring this abstract class as the plugin interface for the identifier ”com.trolltech.Plugin.EchoInterface”, version ”1.0”. If we have a look into the Qt sources (concretely into the file: corelib/kernel/qobject.h), we can deduct that this macro just declare a couple of inline templates, which serves for getting the interface identifier or performing reinterpret cast to desired interface from QObject. #
define Q_DECLARE_INTERFACE(IFace, IId) \ template <> inline const char *qobject_interface_iid
() \ { return IId; } \ template <> inline IFace *qobject_cast(QObject *object) \ { return reinterpret_cast((object ? object->qt_metacast(IId) : 0)); } \ template <> inline IFace *qobject_cast(const QObject *object) \ { return reinterpret_cast \ ((object ? const_cast(object)->qt_metacast(IId) : 0)); }
Somewhere else in the main application we need to load the plugin. This will be done at run time and for this purpose Qt has a QPluginLoader class. We just need to pass a plugin path to its constructor and call the instance() method, which will try to load the plugin. bool EchoWindow::loadPlugin() { ... QDir pluginsDir(qApp->applicationDirPath()); foreach (QString fileName, pluginsDir.entryList(QDir::Files)) { QPluginLoader pluginLoader(pluginsDir.absoluteFilePath(fileName)); QObject *plugin = pluginLoader.instance(); if (plugin) { echoInterface = qobject_cast(plugin); if (echoInterface) return true; } } return false; }
If the process was unsuccessful, we obtain a 0 pointer. In the other case a QObject* pointer, which is subsequently recasted to the desired interface (EchoInterface in our case), is returned. For the recasting is used a special template function defined in: qobject.h. We can see that several casts from the given QObject pointer (with the exploitation of the metaObject information about it) are realized:
18
CHAPTER 2. QT SDK
2.9. PLUGINS SUPPORT
inline T qobject_cast(QObject *object) { #if !defined(QT_NO_MEMBER_TEMPLATES) && !defined(QT_NO_QOBJECT_CHECK) reinterpret_cast(0)->qt_check_for_QOBJECT_macro(*reinterpret_cast(object)); #endif return static_cast(reinterpret_cast(0)->staticMetaObject.cast(object)); }
After this we can normally access the plugin’s functions via the EchoInterface pointer: void EchoWindow::sendEcho() { QString text = echoInterface->echo(lineEdit->text()); label->setText(text); }
Plugin part Now few words to how the plugin implementation itself should look like. First of all we have to specify in the project file, that deals a plugin library: TEMPLATE CONFIG TARGET
= lib += plugin = $$qtLibraryTarget(echoplugin)
Next step is to create a plugin class that will inherit from both QObject and EchoInterface (as defined in the core application). We must not forget to add Q OBJECT and Q INTERFACES macros to the class definition, implement all of the required methods of EchoInterface and in the end export the newly created plugin with another Q EXPORT PLUGIN2 macro. class EchoPlugin : public QObject, EchoInterface { Q_OBJECT Q_INTERFACES(EchoInterface) public: QString echo(const QString &message) { return message; } }; Q_EXPORT_PLUGIN2(echoplugin, EchoPlugin);
Let’s have a look briefly also on what these macros do. Here are its definitions as taken from qglobal.h, qplugin.h or qobject.h: #
define Q_EXPORT_PLUGIN2(PLUGIN, PLUGINCLASS) \ Q_PLUGIN_VERIFICATION_DATA \ Q_EXTERN_C Q_DECL_EXPORT \ const char * Q_STANDARD_CALL qt_plugin_query_verification_data() \ { return qt_plugin_verification_data; } \ Q_EXTERN_C Q_DECL_EXPORT QT_PREPEND_NAMESPACE(QObject) \ * Q_STANDARD_CALL \qt_plugin_instance() \ Q_PLUGIN_INSTANCE(PLUGINCLASS)
#
define Q_PLUGIN_VERIFICATION_DATA \ static const char *qt_plugin_verification_data = \ "pattern=""QT_PLUGIN_VERIFICATION_DATA""\n" \ "version="QT_VERSION_STR"\n" \ "debug="QPLUGIN_DEBUG_STR"\n" \ "buildkey="QT_BUILD_KEY;
#
define Q_DECL_EXPORT
__declspec(dllexport)
19
CHAPTER 2. QT SDK
2.9. PLUGINS SUPPORT
#define Q_PLUGIN_INSTANCE(IMPLEMENTATION) \ { \ static QT_PREPEND_NAMESPACE(QPointer) _instance; \ if (!_instance) \ _instance = new IMPLEMENTATION; \ return _instance; \ }
We can see that Q EXPORT PLUGIN2 macro defines a method for providing implementation data, declares a shared library export and create another meta function instance(), which is the one that QPluginLoader::instance() tries to call above the given shared library.
20
Chapter 3
Design patterns It would be probably a good grace to start with a description what exactly is a design pattern. Design pattern constitute a general problem solution, which is used while designing new computer programs. It’s not a library or a part of source code, which we could insert directly to our program, it’s just a description of problem solving or a template, which could be utilized in different situations. In object oriented languages, they typically shows relations and interactions between classes, without determining the concrete implementation of the class. As a bible of design patterns serves a book written by the ”Gang of Four” (Erich Gamma, Richard Helm, Ralph Johnson and John Vlissides). More about that available at: http://c2.com/cgi/wiki?DesignPatternsBook At this chapter I want to present a unique design pattern I’ve used so far in my application. It deals a Singleton.
3.1
Singleton
We are using Singleton patterns when we want to achieve of creating just one class instance that would be accessible from many places. The key idea is to decline client programmers the control of given object’s life cycle. This could be achieved by declaring all of the constructors as private (including copy constructor and assignment operator which we also declare private and with an empty implementation), which would prevent compiler of generating explicitly any other constructor. I have used Singleton pattern in the back-bone classes of my program: PluginsManager, ProjectsManager or Settings, from which comes the following example code: class SettingsImpl : public Settings { friend class Factory; private: /// Pointer to the QGamaCore::QGamaSettingsImpl object. static SettingsImpl *self; // singleton SettingsImpl();
// forbidden
/// Private operator= (singleton implementation). SettingsImpl& operator=(SettingsImpl&); // forbidden /// Private copy constructor (singleton implementation) SettingsImpl(const SettingsImpl&); // forbidden ~SettingsImpl();
// forbidden
21
CHAPTER 3. DESIGN PATTERNS
3.1. SINGLETON
/// Counter of the active pointers. static int pointersCount; /// QSettings object used for storing persistent settings. QSettings applicationSettings; // QSettings used for storing values protected: static SettingsImpl* instance(); void release(); void saveValue(const QString &key); void loadValue(const QString &key); void saveAll(); void loadAll(); }; // class SettingsImpl
Next you can see that class contains private pointer to itself. This pointer is static and, as well as another static pointersCount attribute, is initially set to 0 (doesn’t point anywhere): // inicialization of the private instance pointer to null. SettingsImpl* SettingsImpl::self = 0; // inicialization of the private counter of references int SettingsImpl::pointersCount = 0;
Creating of the object instance is postponed to the time of the first calling of the instance() method. Every subsequent calling then just returns a pointer to the instance and increases the ”released pointers counter”. SettingsImpl* SettingsImpl::instance() { if (self == 0) { self = new SettingsImpl(); } pointersCount++; return self; }
While finished using the object release() method must be called. It decreases ”released pointers counter” and deletes class instance if it was the last one. void SettingsImpl::release() { pointersCount--; if (pointersCount == 0) { delete self; self = 0; } }
Thanks to having the release() function I could define also a destructor private. Dynamic approach in the object creation was chosen because I have to utilize one singleton implementation within another one (e.g. Settings class pointer in ProjectsManager class) and static allocation caused me problems, because I wasn’t able to ensure the initialization order.
22
Chapter 4
XML At this chapter I want to familiarize you with two major XML features I am making use of within the QGama application. It deals the XSD for XML document validation and XSLT for transforming XML into HTML.
4.1
XML Schema Definition (XSD)
XML Schema Definition (XSD) is an XML schema1 describing the structure (valid content) of an XML document. It deals an XML-based alternative to, or maybe better say the successor of, older Document Type Definitions (DTDs)2 . It brings some new features which DTDs didn’t have, like: support of data types, support of namespaces, easier extensibility, etc. A typical XML schema defines: • where within the document can appear individual elements • attributes of those elements • which elements are descendants of other elements • the order and number of child elements • whether an element could be empty or has to contain a text • element and attributes data types • default values of elements and attributes In the comparison with DTD, XML schema is written using the same XML syntax as the documents and is thus, at least from my point of view, far more readable. Each XSD file has to start with the declaration of XML version, followed by the root element: ...
This could have several attributes. The first one: xmlns:xsd="http://www.w3.org/2001/XMLSchema indicates that the elements and data types used within the schema come from the namespace at the specified address and that they should be prefixed by xsd: while used. The second one just states that every newly declared elements has to be namespace qualified. There are two principal types of elements: 1 XML 2 DTD
Schema Tutorial: http://www.w3schools.com/schema/default.asp Tutorial: http://www.w3schools.com/dtd/default.asp
23
CHAPTER 4. XML
4.1. XML SCHEMA DEFINITION (XSD)
• simpleType - contains only text. • complexType - contains other elements and/or attributes. Moreover there are disponible attributes to specify: • name of the attribute / element • type of the attribute / element: xsd:string, xsd:decimal, xsd:integer, xsd:boolean, xsd:date, xsd:time, etc. • use of the attribute / element (whether is optional or required ) And many other elements for defining restrictions or conditions, like: • on the attribute value • for listing possible attribute values to choose from • for specifying which and how many elements could be contained in other element
Practical example Let’s comment on a real example from gama-local.xsd, concretely a element definition.
We can see that network element is defined to be a complex type which can contain elements description, parameters and points-observations (they can appear even several times or doesn’t have to appear at all - attributes maxOccurs=’unbounded’, minOccurs=’0’). Next there are definitions of several optional attributes. First of them is axes-xy, which is restricted to one of the specified strings (ne, sw, es, wn, en, nw, se, ws) with the default value 24
CHAPTER 4. XML
4.2. XSL TRANSFORMATION (XSLT)
of ne (north-east). Second is angles, which is similarly restricted to an enumeration of strings (right-handed, left-handed ) with the default value of right-handed. And the last one epoch defined as a name token with the default value of ’0.0’. A corresponding DTD definition looks like:
It’s pretty much shorter, but the syntax is a bit confusing I guess.
Converting DTD to XSD First I would like to clarify what led me to the need of converting existing GNU Gama’s DTD to XSD. The reason was simple, one of the objectives of this thesis was to implement validation of the existing network XMLs and after a closure look to what Qt offers I had found that only features to perform a XML Schema validation were available (QXmlSchemaValidator class in QtXmlPatterns module). More about the Qt implementation will be covered in the section 5.6 about the XML validation plugin. Because I didn’t have enough time to code everything manually, I have utilized one of the free java-based converters between DTD and XSD (dtd2xs)3 to do the rough work and then corrected incurred insufficiencies manually. Complete resulting XSD file gama-local.xsd can be found in the QGama source codes (directory qgama/qgama-core/xml).
4.2
XSL Transformation (XSLT)
XSLT4 is another XML-based language that serves us for generating a new document based on the content of the existing one. In my application I’ve used it for generating the HTML output of the adjustment.
XSLT process The transformation itself is performed by so called processor 5 . It takes two documents as the input: first is an XML source document (in my case the adjustment results XML), second an XSLT stylesheet with the template rules and produces an output document. Each XSL file starts with the declaration of XML version, followed by the root element (where once again the namespace is defined by standard w3 URL address) and