Preview only show first 10 pages with watermark. For full document please download

Bachelor`s Thesis

   EMBED


Share

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 tag specifying the method and encoding: ... A stylesheet itself consists especially from templates that define, how individual parts of XML document will be transformed into the output document shape. For the document parts selection a simple query language XPath is used. 3 Software homepage: http://www.lumrix.de/dtd2xs.php Tutorial: http://www.w3schools.com/xsl/default.asp 5 For example: The XSLT C library for GNOME: http://xmlsoft.org/XSLT/xsltproc2.html 4 XSLT 25 CHAPTER 4. XML 4.2. XSL TRANSFORMATION (XSLT) Figure 4.1: XSLT process flow (source: [12]). ”The processor follows a fixed algorithm: Assuming a stylesheet has already been read and prepared, the processor builds a source tree from the input XML document. It then starts by processing the source tree’s root node, finding in the stylesheet the best-matching template for that node, and evaluating the template’s contents. Instructions in each template generally direct the processor to either create nodes in the result tree, or process more nodes in the source tree in the same way as the root node. Output is derived from the result tree.” 6 Practical example Let’s have a look on an example taken from qgama/qgama-core/xml/gama-local-adjustment.xsl stylesheet for converting the XML with adjustment results to HTML. We can see that each template is defined by the attribute match, which is an XPath 7 expression defining a node in the document object tree. In this case deals the root node, which will create the main structure of the outputting HTML (including a simple javascript definition), that is, all the descendant templates will be evaluated inside the tag , which is the place where the tag is ”called”. Templates can be also ”called” explicitly (using the tag ) and serve us thus as ”function” equivalents. Let’s demonstrate it. In the input XML document we have a 6 Cited 7A from: XSLT [11] good tutorial on XPath available at: http://www.zvon.org/xxl/XPathTutorial/General/examples.html 26 CHAPTER 4. XML 4.2. XSL TRANSFORMATION (XSLT) tag containing network’s description and we want to display the lines wrapped in the same way, that means we need to create a template which would convert \n to
tag. Such template can have several input parameters (), in this case the text in which the conversion should be made.

1. Network’s description:

If we inspect the custom template definition, we will notice that in XSL there is also possible to: • declare constants: ) • use conditional expressions: , , , • use plenty of build-in functions for processing strings, numbers, etc.: substring-before(), substring-after(), string-lenght(), format-number() (depends on the processor in use) • generate the value of some XPath expression: • exploit recursion approach while ”calling” the templates • utilize instruction for iterative processing

Template declares at the beginning the parameter name (so that we could access its content) and one constant (values of both could be accessed from the code by prefixing ’$’). Next it’s checked whether the parameter where contains any text. If it’s empty, no action is performed, if it’s the end of the line,
tag is inserted, if it’s just one line, it’s copied. In the rest of the cases, the part of the text before the first line wrap is taken and inserted followed by the
tag. Then the same template is called recurrently with the input parameter’s value containing only the currently unprocessed part of the text. Complete resulting XSL file gama-local-adjustment.xsl can be found in the QGama source codes (directory qgama/qgama-core/xml). 27 CHAPTER 4. XML 4.2. XSL TRANSFORMATION (XSLT) Qt implementation Implementation of whole the described process in Qt is very simple. An QXmlQuery object is created with the constructor specifying we want to use its XSLT20 features, XSL file is entered using the setQuery() method, XML file using the setFocus() one and the final output is created while executing the method evaluateTo(). QXmlQuery query(QXmlQuery::XSLT20); query.setFocus(outputXmlStream); QFile xslFile(QString(":/xml/gama-local-adjustment.xsl")); xslFile.open(QIODevice::ReadOnly); const QString xslText(QString::fromUtf8(xslFile.readAll())); query.setQuery(xslText); query.evaluateTo(&outputHtmlStream); 28 Chapter 5 QGama Application 5.1 Basic information QGama is a graphical user interface above GNU Gama. Current version, in the time of writing this thesis, is 0.2.4 and although plenty of the features still waits for its implementation (e.g. complete shielding of the network’s XML representation, graphical module for network’s preview, etc.)1 , the fundamentals for subsequent development has already been laid (which was the objective of this thesis). In concrete dealt the following issues: 1. plugins management - implementation of a mechanism that would enable extending of the main application’s functionality with third party add-ons 2. projects management - implementation of classes which would assure the coexistence of different project types and standard functions for project handling 3. persistent storage of settings - both application and project related 4. producing of more user friendly structure of output protocol (HMTL) 5. validation process of the existing network’s XMLs Beside that, QGama is: • written in C++, based on Qt 2 platform • released under the open source GNU GPL v3 3 license • easily portable to other platforms (on the source code level) • written with the internationalization support • supporting the major part of GNU Gama’s functionality • documented with Doxygen 4 • hosted on SourceForge 5 : http://sourceforge.net/projects/qgama • distributed in the form of Windows installer or Debian package for Ubuntu GNU/Linux 6 Project was developed under: Fedora 12 Constantine GNU/Linux - QtCreator 1.3.0 (Qt 4.6.0 ). 1 More about the future plans in the section 5.9 home page: http://qt.nokia.com 3 Text of the license available at: http://www.gnu.org/licenses/gpl.html 4 Source code documentation generator tool: http://www.stack.nl/~dimitri/doxygen/ 5 Web-based source code repository for Open Source projects 6 More about the distribution of binaries is covered in separate chapter 6 2 Qt 29 CHAPTER 5. QGAMA APPLICATION 5.2 5.2. APPLICATION STRUCTURE Application structure In the following sections of this chapter I would like to focus on the implementation details of the back-bone structures of QGama application. I was trying to design everything as general as possible, so that any later interference into the code would be painless. Therefore in the parts where I thought, that may be changed in the future (Settings, Projects Manager, Plugins Manager, etc.) I’ve proceeded with the approach of interfaces and corresponding implementations. I contrive thus a possibility to change e.g. the way how to store data (we can for example want to store it into the database in future) with the reimplementation of just a single class. All of the manager classes are created as Singletons 7 (so that there exists just a one instance of such an object although its accessed from numerous places at the same time) and there is also a factory class used for getting and releasing their instances (it’s the only place in the application where is written which implementation will actually be in use, so if you create for example an alternative implementation for storing settings, just redefine the getSettings() method and you can start using it). class Factory { private: Factory(); public: // settings static Settings* getSettings() { return SettingsImpl::instance(); } static void releaseSettings(Settings* &settings) { if (settings!=0) { settings->release(); settings=0; } } // projects manager static ProjectsManager* getProjectsManager() { return ProjectsManagerImpl::instance(); } static void releaseProjectsManager(ProjectsManager* &projectsManager) { if (projectsManager!=0) { projectsManager->release(); projectsManager=0; } } // plugins manager static PluginsManager* getPluginsManager() { return PluginsManagerImpl::instance(); } static void releasePluginsManager(PluginsManager* &pluginsManager) { if (pluginsManager!=0) { pluginsManager->release(); pluginsManager=0; } } }; A correct way of using such objects it’s to have a pointer to its interface as a private attribute of class (QGamaCore::Settings *settings), initialize it in constructor with Factory::getSettings() method and ”clean up” everything by calling Factory::releaseSettings(settings) in destructor. Unfortunately I haven’t found the way how to create a class both Singleton and QObject (so that would be possible to utilize signals–slots mechanism), therefore I encountered a problem of how to interconnect some widgets from the manager classes. Finally I solved it by introducing an ApplicationComponentProvider class. It provides pointers to the most important widgets of 7 More about this design pattern can be found in chapter 3 30 CHAPTER 5. QGAMA APPLICATION 5.3. STORING SETTINGS the main application (MainWindow, PluginsManagerDialog, ProjectsTreeWidget, MDI subwindow, etc.). The trick is done by using the global qApp pointer (which provides an access to our application) and its methods topLevelWidgets() or widgets() (which return the QObject pointers to all of those widgets). Nevertheless it’s not a very favorable solution, because the pointers have to be searched through some unique identifier (which in most of the cases is only the objectName), but I haven’t found a better one. So at least I concentrated the constant declaration with those names into the same class. ApplicationComponentProvider is also transfered to plugins on its initialization. QGama is also written as MDI (Multiple Document Interface) application, that means user can work with plenty of documents opened at the same time. So far there exists four different types of documents that could be opened with QGama (network definition’s XML and adjustment solution in XML / TXT / HTML) and two possible viewers (editors). For text based documents (XML, TXT) it is a text editor with the syntax highlighter and for HTML documents an integrated browser. So that all of the documents could be handled in the same way, I’ve created a general ancestor of both, which provides the standard functions like loading file, saving it, getting its content, etc. There is functionality for storing the information about which projects or files were recently opened and all of your opened projects and tabs with which you were working before the application’s shutdown will be restored automatically when run next time. All of the core application classes are nested within its own namespace QGamaCore, so that would be avoided of the possible name collisions with the plugins extensions (different namespace QGamaPlugins). I also want to mention that I have been strictly using Q ASSERT macros for every substantial condition (including the checks whether the returned pointers weren’t 0, etc.) and with the explanation what went wrong. Which, together with the qDebug() infos, should provide sufficient information on a possible application crash. Let’s have a closer look on each of those discussed parts. . . 5.3 Storing Settings Persistent storage of application data is solved with the exploitation of QSettings. It provides an interface over the individual platform implementations which enables us to store key–value pairs, without the need to bother of how in the background are the data actually stored (on Windows it is into the system registry, on Mac OS into the XML preferences file and on Unix-like systems into the INI text files). In the case of running QGama under Linux, everything will be stored within a single file ~/.config/Jiri_Novak/QGamaCore.conf. An example of how its content can look like: [plugins] directory=/usr/local/lib/qgama/ enabledPlugins=libxmlvalidation.so [MainWindow] pos=@Point(0 0) size=@Size(924 683) [preferences] font="Sans,10,-1,5,50,0,0,0,0,0" [projects] activeProject=QGamaProject2, /home/jirka/QGamaProjects/ openedProjects=/home/jirka/QGamaProjects/QGamaProject2/QGamaProject2.qgp recentlyOpenedFiles=/home/jirka/QGamaProjects/QGamaProject1/Network/gama-xml.gkf|network, /home/jirka/QGamaProjects/QGamaProject1/Solutions/gama-xml__Default.html|solution-html, /home/jirka/QGamaProjects/QGamaProject2/Solutions/gama-xml__Default.xml|solution-xml, /home/jirka/QGamaProjects/QGamaProject2/Solutions/gama-xml__Default.txt|txt, /home/jirka/QGamaProjects/QGamaProject2/Solutions/gama-xml__Default.html|solution-html 31 CHAPTER 5. QGAMA APPLICATION 5.4. PROJECTS MANAGER recentlyOpenedProjects=/home/jirka/QGamaProjects/QGamaProject1/QGamaProject1.qgp, /home/jirka/QGamaProjects/QGamaProject3/QGamaProject3.qgp QSettings stores keys as QString and values as QVariant. QVariant acts like a union for the most common Qt data types, we can store there for example: QString, QStringList, QRect, QImage, QSize, etc. It provides methods for: getting know the stored type (type()), converting content into another type (convert()) or getting the value (toString(), toStringList(), etc.). In my attitude, I do not use directly the QSettings class for storing the data. As was already discussed, I tried to prepare the application for the situation that the storing system could change in future. Therefore there is general class which includes its own inner map for storing the values and provides the interface to set, get and delete them. class Settings { friend class Factory; protected: QMap settings; virtual virtual virtual virtual virtual void void void void void loadValue(const QString &key) = 0; saveValue(const QString &key) = 0; loadAll() = 0; saveAll() = 0; release() = 0; public: QVariant get(const QString &key) { return settings.value(key); } void set(const QString &key, const QVariant &value) { settings.insert(key,value); } void del(const QString &key) { settings.remove(key); } }; // class Settings The subsequent system of ”committing” the changes into the persistent storage, reminds transactions. loadValue(), saveValue(), loadAll() and saveAll() methods are implemented in accordance with the needs of QSettings (or database). Constructor of the implementation class then calls the loadAll() method to initialize the inner map with the data from persistent storage. Application works all the time above the inner map and if it’s properly ended, the destructor of the same class will commit all the content of the inner map back into the persistent storage (if it crashes, none is saved). 5.4 Projects Manager ProjectsManager is also implemented as Singleton. It principally takes care of: • holding the up-to-date list of opened project’s pointers • providing those pointers if requested so • creating a new project • opening or closing the existing one • setting a project active • updating the project file entries It also cooperates with a specialized ProjectsTreeWidget, so that its every action is projected also there and vice versa. So far there exists only one project type: SingleNetworkProject. It consist of one network in XML format, several possible adjustment settings and its corresponding solutions. All the 32 CHAPTER 5. QGAMA APPLICATION 5.5. PLUGINS MANAGER information about project is stored in a XML file. It’s structure is really simple (it basically says only of what is the type and name of project and which files belongs to it), let’s have a look on an example: We can see, that there is a root element, where the project type and name are specified and then the 3 possible file type elements: , , which includes individual file entries. In the case of network and solution is stored its: id within the project, settingsId of the applied solution, format type, path, name and whether it should be opened in a subwindow next time the project is opened. In the case of adjustment setting I store: all of the adjustment parameters, format types to be generated, encoding of this output and its id and name. Each project (after its opening) is mapped in the memory as DOM8 (all the changes that occurred are stored there) and the final commit into the project file is once again done once the MainWindow is about to close (MainWindow::closeEvent()). 5.5 Plugins Manager PluginsManager works on a similar principle. It’s also implemented as Singleton and: • holds the list of all currently loaded plugins • provides access to them through the general plugin interface • provides methods for loading / unloading and enabling / disabling specified plugins It’s tightly bounded with PluginsManagerDialog, where the user can decide which plugin should be loaded or enabled and which not. The mechanism of Qt plugins was already discussed in section 2.9, so I am going to focus only on the specific aspects of my implementation. I declared a general interface that every possible plugin has to implement. There are few descriptive methods (icon(), name(), description(), authors()) which serves for plugin identification within the PluginsManagerDialog. configuration() method should serve for enabling plugin to provide its own configuration widget. By default it’s expected that plugin needs no such special configuration. items() method is there to provide a way how to insert items into main application ”Tools” menu and connect it to desired slot (it will be covered more in detail in the following section). class PluginInterface : public QObject { protected: ApplicationComponentProvider *acp; 8 Document Object Model, more info at: http://en.wikipedia.org/wiki/Document_Object_Model 33 CHAPTER 5. QGAMA APPLICATION 5.6. XML VALIDATION PLUGIN public: virtual ~PluginInterface() {} virtual virtual virtual virtual virtual virtual QIcon icon() const = 0; QString name() const = 0; QString description() const = 0; QStringList authors() const = 0; QStringList items() const = 0; QWidget* configuration() const { return 0; }; void init(ApplicationComponentProvider *apc) { this->acp = acp; } }; // class PluginInterface A pointer to ApplicationComponentProvider is there because I wanted somehow facilitate the plugin an access to some main application widgets. It’s set at the same place where plugin is loaded into the memory (that is within the PluginsManager::loadPlugin()) method. 5.6 XML validation plugin XML Validation plugin is the only plugin I have implemented so far. It serves for validating the XML representation of GNU Gama’s network against its XSD schema9 . Basically there are just two issues to discuss. First one is how the incorporation to the main application menu is ensured. It is once again very simple: QStringList XMLValidationPlugin::items() const { return QStringList() << tr("Va&lidate GNU Gama XML Input/Output|XML|1|validate()|Ctrl+L"); } items() function returns a special formatted (’—’ is a separator) strings where is stated information about every item to add in the following order: • name of the item • item category • item priority • name of the slot to connect • desired shortcut Items then will be added into the main application’s ”Tools” menu following these rules: • individual categories will be separated with a horizontal line • items in each category will be ordered based on the priority number value (higher number, higher priority) • if the same priority is entered, the order will be alphabetical All of the ”interesting” work then does the plugin validate() function. It gets the active textEditor class pointer via its ApplicationComponentProvider attribute and acquire its content (network’s XML representation). This content is then passed to QXmlSchema instance and validated against the specified XSD. MessageHandler class processes the error information provided by QXmlSchema. I took it over from the official Qt XML Schema Validation Example10 . The excerpt of the function definition follows. 9 This topic was covered in detail in section 4.1. at: http://doc.qt.nokia.com/4.6/xmlpatterns-schema.html 10 Available 34 CHAPTER 5. QGAMA APPLICATION 5.7. ADJUSTMENT USING LIBQGAMA void XMLValidationPlugin::validate() { // get the active text editor pointer TextEditor *textEditor = acp->getActiveTextEditor(); Q_ASSERT(textEditor!=0 && "textEditor pointer is 0!"); // acquire its content instanceData = textEditor->getContent().toAscii(); // create the message handler and XML schema class MessageHandler messageHandler; QXmlSchema schema; schema.setMessageHandler(&messageHandler); // load the XSD file schema.load(schemaData); ... // check the document validity bool errorOccurred = false; QXmlSchemaValidator validator(schema); if (!validator.validate(instanceData)) errorOccurred = true; } ... } While compiling a plugin I also encounter a linkage problem, because when I am using some method from the main application and that method is not defined directly in the header file I am including, compiler automatically discards such a definition. This is a problem to which I’have found a solution only on the Linux platform (gcc has a switcher -rdynamic which will force the linker not to discard those symbols). On Windows platform and minGW compiler, doesn’t exist such a switcher (hopefully in VC2008, which is the second Qt alternative compiler for Windows, there will be). Because I have found this issue recently and haven’t time to think up a better solution, I have just did an workaround by moving the critical function definition into the header file. It will be fixed in some future release of QGama. 5.7 Adjustment using libqgama For the proper network adjustment I am making use of the GNU Gama computational library, which I compiled as a static library and linked together with my program. Calculations are performed on a separate thread in background so that I would avoid ”freezing” the main application during the adjustment. Implementation of a thread is very simple in Qt. You just need to inherit from QThread and re-implement the protected run() method. My run method in the case of SolveNetwork class looks the following way: void SolveNetwork::run() { QString outputXmlStream; QString outputTxtStream; QString outputHtmlStream; try { solveNetwork_(outputXmlStream, outputTxtStream, outputHtmlStream); emit solved(outputXmlStream, outputTxtStream, outputHtmlStream, document, as); } catch (Exception e) { emit solvingFailed(e.text); } 35 CHAPTER 5. QGAMA APPLICATION 5.8. INTERNATIONALIZATION exit(); } We can see, that I am just calling the main computational method (solveNetwork_), emit corresponding signal depending whether the adjustment succeeded or failed and exit. solveNetwork_() function is basically overtaken from GNU Gama’s gama-local-main.h, I have just rewrote it to work with QStrings, throw QGama specific exceptions and generate the HTML output from the XSL Transformation11 . In the corresponding dialog where the adjustment process should be started, I just dynamically create an instance of SolveNetwork class, connect corresponding slots and start the thread execution (solver->start()). SolveNetwork *solver = new SolveNetwork(inputXmlStream, as, document); connect(solver, SIGNAL(solved(QString,QString,QString,Document*,AdjustmentSetting*)), mw, SLOT(onAdjustmentSuccess(QString,QString,QString,Document*,AdjustmentSetting*))); connect(solver, SIGNAL(solvingFailed(QString)), mw, SLOT(onAdjustmentFailure(QString))); solver->start(); Thread will be automatically destroyed when the adjustment finishes. This is achieved by connecting a terminate() signal with onTerminate() slot in the SolveNetwork class. connect(this, SIGNAL(terminated()), this, SLOT(onTerminate())); void SolveNetwork::onTerminate() { delete this; } 5.8 Internationalization All the QGama application is coded with the consideration of that it should be internationalized. Therefore every single text, which a user can come in contact with, is enclosed into QObject::tr() macro. This macro means that such a text will be identified by Qt internationalization tools and offered for localization. tr() macro can be utilized with the additional parameter which will provide more information to the translator, e.g. we can use it as tr("Letter", "US paper size"). When we need to translate a sentence including some variable that should not be localized, we should proceed like this: tr("Host %1 found").arg(hostName) (sentence is first translated, then the argument is inserted). Translation process The translation process in Qt is very easy and consist basically just of four steps: 1. Specify in the project file which languages we want to support: TRANSLATIONS = ../translations/qgamacore_cs_CZ.ts 2. Extract all translatable strings from application’s source code with lupdate. [jirka@cohen src]$ lupdate qgama-core.pro Updating ’../translations/qgamacore_cs_CZ.ts’... Found 312 source text(s) (312 new and 0 already existing) 3. Translate the application using Qt Linguist. [jirka@cohen src]$ linguist ../translations/qgamacore_cs_CZ.ts 11 As it was described in detail in section 4.2. 36 CHAPTER 5. QGAMA APPLICATION 5.8. INTERNATIONALIZATION It is a graphic program that will offer you well-arranged environment for localization (including the preview of where, in dialogs or source code, the original text was located)12 . Figure 5.1: Localization of strings in Qt Linguist. 4. Once you are done, run lrelease to generate the binary .qm file that the application can load. [jirka@cohen translations]$ lrelease qgamacore_cs_CZ.ts Updating ’qgamacore_cs_CZ.qm’... Generated 31 translation(s) (30 finished and 1 unfinished) Ignored 281 untranslated source text(s) Loading corresponding translation file Typically we are loading a corresponding translation file in the application’s main() function depending on the current user’s locale. int main(int argc, char *argv[]) { QApplication app(argc, argv); // load qt translator QTranslator qtTranslator; qtTranslator.load("qt_" + QLocale::system().name(), QLibraryInfo::location(QLibraryInfo::TranslationsPath)); app.installTranslator(&qtTranslator); // load QGamaCore application translator QTranslator myappTranslator; 12 More about Qt Linguist can be found at: http://doc.trolltech.com/4.6/linguist-manual.html 37 CHAPTER 5. QGAMA APPLICATION 5.9. FUTURE PLANS myappTranslator.load(":/translations/qgamacore_"+QLocale::system().name()+".qm"); app.installTranslator(&myappTranslator); // create application’s main window and show it QGamaCore::MainWindow w; w.show(); // start the event loop return app.exec(); } We can also dynamically switch the language on run-time. Doing this assume that for every widget or dialog, all of the translatable strings are set from one separate function retranslateUi(), which is called when language change is requested. So far I am not using it within QGama, but maybe I will implement it in future. Qt Resource System Within the code above I used Qt Resource System 13 . It is a simple mechanism which serves for storing binary files into the application’s executable. It uses a XML-based representation (qgama-core.qrc) of the list of files to be added: ... ../translations/qgamacore_cs_CZ.qm All you have to do, for successful incorporating those files in the application binary is add the following line into the application project file: RESOURCES += qgama-core.qrc Rest of the work will do qmake in the cooperation with rcc (Qt’s Resources Compiler). I have used it QGama for distribution of all icons, images, xmls and translations. 5.9 Future plans Besides the obvious facts like to start functional and localization testings, there is also plenty of other things to be implemented in future QGama releases. I will just pick up the most important ones we have discussed with professor Čepek: • the XML representation of network should be completely covered up - user should not have to bother about it and everything should be made accessible through dialogs • embedded SQL database (SQLite14 ) should be introduced to hold data during the networks creation / edition process • graphical module for network preview should be present • there should be a possibility to create new project based on the database query • new project types like (Sequential Adjustment Project) should be introduced 13 More informationa available at: http://doc.trolltech.com/4.6/resources.html home page: http://www.sqlite.org/ 14 SQLite 38 Chapter 6 Distribution of binaries As already discussed in the section 2.8 about the static build, shared libraries approach had to be chosen in the case of QGama to provide the plugins mechanism working. Other condition is that the version of Qt libraries has to be at least 4.6.0 (because I am utilizing QtWebKit module within QGama application and that was first implemented in that release). The easiest way to comply these conditions seemed to me: 1. creation of an installer / uninstaller for Windows system that would bring all necessary runtime libraries within the installation package 2. creation of a Debian based package (.deb) which will force installing all of the dependent libraries on the system 6.1 Windows For creation of QGama installer / uninstaller in Windows I decided to utilize Inno Settup by Jordan Russel’s software- a free of charge installer for Windows programs available at: http://www.jrsoftware.org/isinfo.php Among the tens of different functions it: • supports all of the Windows versions that are currently in use • creates a single EXE setup wizard as the output (with uninstall capabilities included) • automatically invokes file compression • creates shortcuts at desired places • manages registry entries • provides localized environments I have started with downloading a self installing Quick Start Pack which brings bundled, among the others, an ISTool - a GUI front-end for creating and editing Inno Setup scripts (written by Bjørnar Henden): http://www.jrsoftware.org/download.php/ispack.exe?site=2 ISTool itself than has a great wizard tool, that enables you to create whole installer in a graphical way by filling several consequent informative forms about your program (name, description, license, installation folder, which files should be copied and where, etc.). As a result of running this wizard an Inno Setup Script is generated: 39 CHAPTER 6. DISTRIBUTION OF BINARIES 6.1. WINDOWS ; Script generated by the Inno Setup Script Wizard. ; SEE THE DOCUMENTATION FOR DETAILS ON CREATING INNO SETUP SCRIPT FILES! [Setup] ; NOTE: The value of AppId uniquely identifies this application. ; Do not use the same AppId value in installers for other applications. ; (To generate a new GUID, click Tools | Generate GUID inside the IDE.) AppId={{24C1609A-1644-445A-889B-135CB6C61E43} AppName=QGama AppVerName=QGama 0.2.2. AppPublisher=Jiří Novák AppPublisherURL=http://sourceforge.net/projects/qgama/ AppSupportURL=http://sourceforge.net/projects/qgama/ AppUpdatesURL=http://sourceforge.net/projects/qgama/ DefaultDirName={pf}\QGama DefaultGroupName=QGama AllowNoIcons=yes LicenseFile=C:\Documents and Settings\jirka\Desktop\qgama_0.2.2-win32-instalator\LICENCE.txt OutputBaseFilename=setup Compression=lzma SolidCompression=yes [Languages] Name: english; MessagesFile: compiler:Default.isl Name: czech; MessagesFile: compiler:Languages\Czech.isl [Tasks] Name: desktopicon; Description: {cm:CreateDesktopIcon}; \ GroupDescription: {cm:AdditionalIcons}; Flags: unchecked Name: quicklaunchicon; Description: {cm:CreateQuickLaunchIcon}; \ GroupDescription: {cm:AdditionalIcons}; Flags: unchecked; OnlyBelowVersion: 0,6.1 [Files] Source: Source: Source: Source: Source: Source: Source: Source: Source: Source: Source: Source: ; NOTE: C:\temp\qgama\qgama.exe; DestDir: {app}; Flags: ignoreversion C:\temp\qgama\libgcc_s_dw2-1.dll; DestDir: {app}; Flags: ignoreversion C:\temp\qgama\LICENCE.txt; DestDir: {app}; Flags: ignoreversion C:\temp\qgama\mingwm10.dll; DestDir: {app}; Flags: ignoreversion C:\temp\qgama\phonon4.dll; DestDir: {app}; Flags: ignoreversion C:\temp\qgama\QtCore4.dll; DestDir: {app}; Flags: ignoreversion C:\temp\qgama\QtGui4.dll; DestDir: {app}; Flags: ignoreversion C:\temp\qgama\QtNetwork4.dll; DestDir: {app}; Flags: ignoreversion C:\temp\qgama\QtWebKit4.dll; DestDir: {app}; Flags: ignoreversion C:\temp\qgama\QtXml4.dll; DestDir: {app}; Flags: ignoreversion C:\temp\qgama\QtXmlPatterns4.dll; DestDir: {app}; Flags: ignoreversion C:\temp\qgama\plugins\xmlvalidation.dll; DestDir: {app}\plugins; Flags: ignoreversion Don’t use "Flags: ignoreversion" on any shared system files [Icons] Name: {group}\QGama; Filename: {app}\qgama.exe Name: {group}\{cm:ProgramOnTheWeb,QGama}; Filename: http://sourceforge.net/projects/qgama/ Name: {group}\{cm:UninstallProgram,QGama}; Filename: {uninstallexe} Name: {commondesktop}\QGama; Filename: {app}\qgama.exe; Tasks: desktopicon Name: {userappdata}\Microsoft\Internet Explorer\Quick Launch\QGama; \ Filename: {app}\qgama.exe; Tasks: quicklaunchicon [Run] Filename: {app}\qgama.exe; Description: {cm:LaunchProgram,QGama}; \ Flags: nowait postinstall skipifsilent We can see, that even the code is very readable and easy to edit. For example I had to add an instruction of deleting the program corresponding registry key while uninstalling. It was very easy, I consulted an official documentation placed at: http://www.jrsoftware.org/ishelp/ And added one line, which solved it: 40 CHAPTER 6. DISTRIBUTION OF BINARIES 6.2. DEBIAN PACKAGE [Registry] Root: HKCU; Subkey: Software\Jiri_Novak; Flags: uninsdeletekey Then remains only to compile the script with a special compiler provided also within the quick start package and accessible from ISTool and that’s all, we have portable setup.exe installer (with uninstaller capabilities included) done. 6.2 Debian package Although a user of Fedora/GNU Linux I respected the majority of Linux users and decided to create a package for outnumbered community of Debian based distributions. Concretely (because of the need of the newest qt packages) for Ubuntu 10.4 Lucid Lynx. In comparison with how easy everything was on Windows, I have to say that creation of a Debian package turned out to be much more complicated. First of all I was looking for some similar GUI tool, which would do quickly all the stuff for me. I have found two principal tools: • DebCreator (http://debcreator.cmsoft.net/) • DebianPackageMaker (http://code.google.com/p/debianpackagemaker/) The first one turned out to be full of bugs and I wasn’t able to make it working correctly on my system, every time I workaround one problem, second one revealed. On the other hand, there was DebianPackageMaker. My sympathies to this software expired during the installation process: it wanted me to install about 150 Mbytes of dependency libraries headed by libmono. This wasn’t the tool I had been looking for. I hadn’t got a choice, but implement it manually. I started with the official Debian New Maintainers’ Guide 1 , but it was excessively complicated for my simple intended binary package. Finally I’ve found a page that I am going to qoute several times in the following text, it deals the Chr. Clemens Lee’s Debian Binary Package Building Howto [10]. Package structure Each Debian binary package consists of three principal files: • debian-binary - including only a version line • control.tar.gz - general information about the package, (md5sums of files, scripts which should be run after installation or before uninstallation) • data.tar.gz - all the package data in the root tree structure corresponding to where the files should be copied Creating the QGama package I started from the scratch having just the binaries of my program, that is: qgama and libxmlvalidation.so as the only currently implemented plugin. The working process then was the following: 1. Create a local qgama directory, usr/bin and usr/lib/qgama subdirectories in it and copy my binaries there. 1 Debian New Maintainer’s Guide: http://www.debian.org/doc/maint-guide/ 41 CHAPTER 6. DISTRIBUTION OF BINARIES [jirka@cohen [jirka@cohen [jirka@cohen [jirka@cohen package]$ package]$ package]$ package]$ 6.2. DEBIAN PACKAGE mkdir -p ./qgama-0.2.4-1_i386/usr/bin mkdir -p ./qgama-0.2.4-1_i386/usr/lib/qgama cp qgama ./qgama-0.2.4-1_i386/usr/bin cp plugins/libvalidation.so ./qgama-0.2.4-1_i386/usr/lib/qgama 2. Create the control file. It was the most elaborate part, as a prerequisite, I had to find a deb package name of each dependent library and find out its version. Dependent libraries were determined with the ldd command, package name and version with: [jirka@cohen package]$ dpkg -S /usr/lib/libXdmcp.so.6 libxdmcp6: /usr/lib/libXdmcp.so.6 [jirka@cohen package]$ apt-cache showpkg libxdmcp6 | head -n3 Package: libxdmcp6 Versions: 1:1.0.2-3 ... Beside the depends sections (where we specify the dependent libraries and its minimal versions), also some package, architecture and maintainer sections have to be filled. Result, in my case, looked like this: jirka@cohen:~/Desktop/balicek/qgama$ cat control Package: qgama Version: 0.2.4-1 Section: graphics Priority: optional Architecture: i386 Depends: libqt4-webkit (>= 4:4.6.2-0ubuntu5), libqt4-xmlpatterns (>= 4:4.6.2-0ubuntu5), \ libqt4-network (>= 4:4.6.2-0ubuntu5), libqt4-xml (>= 4:4.6.2-0ubuntu5), \ libqtgui4 (>= 4:4.6.2-0ubuntu5), libqtcore4 (>= 4:4.6.2-0ubuntu5), \ libc6-i686 (>= 2.10.1-0ubuntu16), libstdc++6 (>= 4.4.1-4ubuntu9), \ libgcc1 (>= 1:4.4.1-4ubuntu9), libxrender1 (>= 1:0.9.4-2ubuntu1), \ libfontconfig1 (>= 2.6.0-1ubuntu12), libfreetype6 (>= 2.3.9-5), \ zlib1g (>= 1:1.2.3.3.dfsg-13ubuntu3), libxext6 (>= 2:1.0.99.1-0ubuntu4), \ libx11-6 (>= 2:1.2.2-1ubuntu1), libglib2.0-0 (>= 2.22.3-0ubuntu1), libsm6 (>= 2:1.1.0-2), \ libice6 (>= 2:1.0.5-1), libc6 (>= 2.10.1-0ubuntu16), libexpat1 (>= 2.0.1-4ubuntu1.1), \ libxau6 (>= 1:1.0.4-2), libxcb1 (>= 1.4-1), libpcre3 (>= 7.8-3), \ libuuid1 (>= 2.16-1ubuntu5), libxdmcp6 (>= 1:1.0.2-3) Maintainer: Jiri Novak Description: Graphical user interface for the GNU GaMa library. Dedicated to the adjusting of geodetical networks. This control file then have to be copied inside the DEBIAN subdirectory within the package root. All subdirectories must have 755 permissions set, all non-executable files 644. [jirka@cohen package]$ mkdir -p ./qgama-0.2.4-1_i386/DEBIAN [jirka@cohen package]$ find ./qgama-0.2.4-1_i386 -type d | xargs chmod 755 [jirka@cohen package]$ cp control ./qgama-0.2.4-1_i386/DEBIAN 3. Create the package with: [jirka@cohen package]$ dpkg --build qgama-0.2.4-1_i386 dpkg-deb: building package ‘qgama’ in ‘qgama-0.2.4-1_i386.deb’. 4. Check its validity with lintian tool: [jirka@cohen package]$ lintian qgama-0.2.4-1_i386.deb E: qgama: unstripped-binary-or-object ./usr/bin/qgama E: qgama: unstripped-binary-or-object ./usr/lib/qgama/libxmlvalidation.so E: qgama: no-copyright-file E: qgama: wrong-file-owner-uid-or-gid usr/ 1000/1000 E: qgama: wrong-file-owner-uid-or-gid usr/bin/ 1000/1000 42 CHAPTER 6. DISTRIBUTION OF BINARIES E: E: E: E: W: qgama: qgama: qgama: qgama: qgama: 6.2. DEBIAN PACKAGE wrong-file-owner-uid-or-gid usr/bin/qgama 1000/1000 wrong-file-owner-uid-or-gid usr/lib/ 1000/1000 wrong-file-owner-uid-or-gid usr/lib/qgama/ 1000/1000 wrong-file-owner-uid-or-gid usr/lib/qgama/libxmlvalidation.so 1000/1000 binary-without-manpage usr/bin/qgama It lists us plenty of errors and warnings. Many of them are just related to wrong-file-owner (it should be owned by user root, which will perform the actual installation of package) or unstripped-binary-or-object (symbols from object files have to be discarted). Those can be easily corrected by: [jirka@cohen package]$ strip ./qgama-0.2.4-1_i386/usr/bin/qgama [jirka@cohen package]$ strip ./qgama-0.2.4-1_i386/usr/lib/qgama/libxmlvalidation.so [jirka@cohen package]$ fakeroot dpkg --build qgama-0.2.4-1_i386 dpkg-deb: building package ‘qgama’ in ‘qgama-0.2.4-1_i386.deb’. 5. Now it complaints only about a missing manpage and copyright file. So I had to work them out also. [jirka@cohen package]$ lintian qgama-0.2.4-1_i386.deb E: qgama: no-copyright-file W: qgama: binary-without-manpage usr/bin/qgama The simpliest copyright file has to be placed at qgama-0.2.4-1_i386/usr/share/doc/qgama and it can look like that2 : qgama Copyright: 2010 Jiri Novak 2010-04-24 The home page of qgama is at: http://sourceforge.net/projects/qgama The entire code base may be distributed under the terms of the GNU General Public License (GPL), which appears immediately below. Alternatively, all of the source code as any code derived from that code may instead be distributed under the GNU Lesser General Public License (LGPL), at the choice of the distributor. The complete text of the LGPL appears at the bottom of this file. See /usr/share/common-licenses/(GPL|LGPL) A man page is more complicated stuff. Detailed information about its creation can be found at Linux Man Page HOWTO 3 I’ve just followed the way of editing some existing man page from other package. . . Compressed man page (qgama.1.gz ) should be placed into the usr/share/man/man1 subdirectory. 6. While performing another validation, we see that just remains a complaint about missing changelog file. [jirka@cohen package$] lintian qgama-0.2.4-1_i386.deb E: qgama: debian-changelog-file-missing It has to be created in usr/share/doc/qgama directory (compressed with gzip --best) and its content can look like the following excerpt4 : 2 based on a template from Chr. Clemens Lee’s Debian Binary Package Building HOWTO [10] Man Page HOWTO: http://www.schweikhardt.net/man_page_howto.html 4 based on a template from Chr. Clemens Lee’s Debian Binary Package Building HOWTO [10] 3 Linux 43 CHAPTER 6. DISTRIBUTION OF BINARIES 6.2. DEBIAN PACKAGE linuxstatus (0.2.4-1) unstable; urgency=low * Made Debian package QGama clean. First attemt to create valid debian package. -- Jiri Novak Sat, 24 Apr 2010 12:58:00 +0000 The same for file called changelog.Debian. 7. Now lintian complaints only about ”not closing any itp bugs”. [jirka@cohen package$] lintian qgama-0.2.4-1_i386.deb W: qgama: new-package-should-close-itp-bug It’s just fine, because our package is first of its type and it’s not intended for infiltrating into the official Debian distribution5 8. We are done, the package can be distributed. 5 More about this error warning at new-package-should-close-itp-bug.html Lintian 44 reports page: http://lintian.debian.org/tags/ Epilogue Objective of this bachelor thesis was to create the fundamentals of an object–oriented graphical user interface for GNU Gama. I believe that it was fulfilled - QGama program was created. And although it is still in its pre-alpha phase (it was not properly tested, many of the functions still wait for its implementation), a significant number of problematic issues had been solved. First of all, there is finally a way how to compile GNU Gama under non Unix-like platforms - application is portable on the level of source codes. Also the question of a comfort integration into the user desktops was solved - an installer / uninstaller for Windows and a Debian package for Linux were created. Moreover to that, QGama was written to be easily extensible with third party libraries (plugins). There is a sophisticated projects management system which can also be complemented. All the program is localized (currently only into Czech and English), but new languages will appear if there will be demand for that. There are also few functionalities which GNU Gama itself did not offer. It is above all the HTML output, which brings a new light into the presentation of the adjustment results because it is far more synoptical. To sum it up, strong fundamentals were laid for the new rising application and I cannot choose but to express a hope that it would be used once in both: a common surveyor’s praxis and the educational process at our faculty. I believe it has such a potential if the development will continue. I wish my example was followed and that maybe some other student will join the QGama development, because there is still lots of work to do. I was trying to write this bachelor thesis as a handbook for beginning Qt application developers, the idea was to explain step-by-step every aspect I had to solve on my way from nothing to the complete open source software product. I wanted to cover the Qt’s key features and difficulties, make the reader familiar with its substantial principles and mechanisms, provide a detailed information on the topics which, based on my research, had been poorly documented (including tens of useful complementary sources). I strongly hope these efforts were also fulfilled. 45 Bibliography [1] BLANCHETTE, Jasmin; SUMMERFIELD, Mark: C++ GUI Programming with Qt4, 1st edition, Prentice Hall (2006) [2] ECKEL, Bruce: Myslíme v jazyku C++, knihovna programátora, Grada Publishing (2000) [3] ECKEL, Bruce; ALLISON, Chuck: Myslíme v jazyku C++, 2. díl knihovna zkušeného programátora, Grada Publishing (2006) [4] BRADLEY, Neil: XML, kompletní průvodce, Grada Publishing (2000) [5] HOLZNER, Steven: XSLT, příručka internetového vývojáře, Computer Press (2002) [6] ČEPEK, Aleš: GNU Gama Project Homepage [online], accessible from WWW: http://www. gnu.org/software/gama/, last update January 20, 2009 [7] WIKIPEDIA, The Free Encyclopedia: GNU build system [online], accessible from WWW: http://en.wikipedia.org/wiki/GNU_build_system, last update March 23, 2010 [8] NOKIA CORPORATION: Qt – A cross-platform application and UI framework [online], accessible from WWW: http://qt.nokia.com, last update April 27, 2010 [9] WIKIPEDIA, The Free Encyclopedia: Qt (framework) [online], accessible from WWW: http: //en.wikipedia.org/wiki/Qt_%28framework%29, last update April 28, 2010 [10] LEE, Chr. Clemens: Debian Binary Package Building HOWTO [online], accessible from WWW: http://tldp.org/HOWTO/html_single/ Debian-Binary-Package-Building-HOWTO/ last update August 09, 2005 [11] WIKIPEDIA, The Free Encyclopedia: XSLT [online], accessible from WWW: http://en. wikipedia.org/wiki/XSLT, last update March 31, 2010 [12] KOSEK, Jiří: XSLT v příkladech [online], accessible from WWW: http://www.kosek.cz/ xml/xslt/, last update 2004 46 Appendix A QGama User Guide A.1 Installation GNU/Linux Users of Ubuntu can exploit the existence of a prepared Debian package for their distribution (it’s disponible on the project home page at the Downloads section1 ) and it can be installed with as any other .deb package (second line is to correct possible missing dependencies): [jirka@cohen package$] sudo dpkg -i ./qgama-0.2.4-1_i386.deb [jirka@cohen package$] sudo apt-get install -f Windows For Windows there is prepared an installer available at the same place2 . Installation is very simple, user just accepts the license, selects a destination directory and chooses whether he want to create Start menu / Quick lunch / Desktop icons. A.2 Starting QGama QGama can be executed like any other program by executing qgama (on Linux) or by clicking the appropriate item within the Start menu (on Windows). In the figure A.1 you can see the program’s initial state. QGama is designed for working with multiple projects and networks simultaneously (MDI - Multiple Document Interface). Projects are displayed as the tree structure in the left dock, network then in the larger area on right. Nevertheless on the first startup we don’t have any project active, so predominant majority of functions is disabled. A.3 New project creation So that we could start working with the program, we have to create a new project. This can be done by three different ways: • by pressing the shortcut Ctrl+Shift+N • by clicking on the New Project icon ( ) in the toolbar 1 Donwload link of QGama’s debian package for Ubuntu: https://sourceforge.net/projects/qgama/files/ qgama/qgama-0.2.4-ubuntu/qgama-0.2.4-1_i386.deb/download 2 Donwload link of QGama’s Windows installer: https://sourceforge.net/projects/qgama/files/qgama/ qgama-0.2.4-win32/qgama-0.2.4-win32-installer.exe/download I APPENDIX A. QGAMA USER GUIDE A.3. NEW PROJECT CREATION Figure A.1: Startup of QGama program. • through the menu - item File|New Project Dialog has the following appearance: You have to choose the project type (currently the only available option is Single Network Project), project name and location (by default a subfolder QGamaProjects within the user’s home is selected). Confirm the action by clicking the OK button. New project is created (figure A.2), you can see it in the Projects dock together with its corresponding structure: • Network - contains all of the project’s networks • Settings - contains different sets of adjustment parameters • Solutions - contains all of the adjustment outputs II APPENDIX A. QGAMA USER GUIDE A.4. OPENING EXISTING PROJECT Figure A.2: New project successfully created. A.4 Opening existing project An existing project can be opened by one of the following ways: • by pressing the shortcut Ctrl+Shift+O • by clicking on the Open Project icon ( ) in the toolbar • through the menu - item File|Open Project Then you just choose the appropriate project file (.qgp) in the dialog and click the OK button. Figure A.3: Opening existing project. III APPENDIX A. QGAMA USER GUIDE A.5 A.5. ADDING NETWORK TO PROJECT Adding network to project If you have an empty project opened, you can add a new network to it. You can do it: • by pressing the shortcut Ctrl+N • by clicking on the New Network icon ( ) in the toolbar • through the menu - item File|New Network Dialog has the following appearance: You can choose the project to which you want to add the network (if you have more projects opened) and the network creation type. You can: • create an empty network file • import (copy) an existing network file into the project • link (create shortcut to) an existing network file into the project If you proceed to the next page of the wizard you will be prompted to specify the file. Click on Browse. . . button and select it. Confirm the New Network wizard by clicking on Finish button. New network is added into the project and opened (figure A.4). IV APPENDIX A. QGAMA USER GUIDE A.6. ADJUSTMENT SETTINGS Figure A.4: New network added into an empty project. A.6 Adjustment settings QGama offers us a possibility to store different adjustment settings (parameters). There is one default implicitly created with the new project. We can either modify it by double clicking the settings item in the project tree or we can create a new one by invoking the New Adjustment Setting icon ( ) or its equivalent entry in menu Network|Adjustment Settings|New Adjustment Setting. Dialog has the following appearance: You can specify: • the algorithm with which you wish to be calculated V APPENDIX A. QGAMA USER GUIDE A.7. ADJUSTING THE NETWORK • whether angles are introduced in gons (400) or degrees (360) • bandwidth of the variance-covariance matrix to be listed in the output (-1 = full matrix, 0 = only principal diagonal, 1 = bandwidth of 2, etc.) • if you wish to apply corrections to measured data (ellipsoid, latitude) • language and encoding of the TXT output • generated output formats (XML, TXT, HTML) Changes are saved by clicking on the OK button. A.7 Adjusting the network If you have a network opened, you can adjust it. Invoke the Network adjustment dialog by one of the following ways: • by clicking on the Solve icon ( ) in the toolbar • through the menu - item Network|Solve Dialog has the following appearance: Just select your desired adjustment setting and click on OK. Adjustment will start. If some error occurs, you will be informed by a similar message box: If everything is ok, you will see just an advancing progress bar. Once the network is calculated, the outputs you have specified in the applied adjustment setting will be opened in individual subwindows. In the figures A.5, A.6, A.7 you can see examples of the HTML, TXT and XML formats of the output. VI APPENDIX A. QGAMA USER GUIDE A.7. ADJUSTING THE NETWORK Figure A.5: An example of the HTML adjustment output. Figure A.6: An example of the TXT adjustment output. VII APPENDIX A. QGAMA USER GUIDE A.8. XML VALIDATION PLUGIN Figure A.7: An example of the XML adjustment output. A.8 XML validation plugin If you wish to take use of a XML validation plugin, first you have to do is, to enable it in the Plugins Manager. You can invoke it by either: • by pressing the shortcut Ctrl+Shift+P • through the menu - item Edit|Plugins You should see there a list of available plugins (if not, the plugins directory is probably not properly set, correct it with the clicking on . . . button and specifying the appropriate location). Figure A.8: Plugin Manager dialog. VIII APPENDIX A. QGAMA USER GUIDE A.8. XML VALIDATION PLUGIN Select the one named ”GNU Gama Network XML Validator ” and enable it by clicking on the corresponding checkbox. Confirm it all by clicking on OK button. Once you have XML validator plugin enabled, you should see a new action under the Tools menu. If you execute it over your opened network, you will see the result of validation process in the bottom of the network’s window. In the case of revealing some error, this will be highlighted in red and cursor will be moved there. Figure A.9: XML Validation plugin: an error found. If no error is found, you will just see a green label with ”validation successful” text. Figure A.10: XML Validation plugin: validation successful. IX Appendix B Content of the attached CD • bt/ - text of the bachelor thesis – LATEX source code – PDF format – PS format • src/gama - source codes of GNU Gama (version 1.9.07) • src/qgama - source codes of QGama (version 0.2.4) • src/examples - sample network inputs • deb/ - Debian package of QGama (version 0.2.4) for Ubuntu 10.4 Lucid Lynx • win/ - Windows installer / uninstaller of QGama (version 0.2.4) X