spot
This session will detail in a step-by-step fashion how to write an application leveraging the Qt framework and then how to modify a graphical user interface such as DrawQt in order to add buttons which select various and different graphical shapes.
You should strive for systematically using all the tools you know, such as:
This first exercize is a variation over hello world.
We start with configuring the svn environment (replace the ens<n> with your team id. e.g. ens2)
$> cd ~/Project
$> svn mkdir https://svn.lal.in2p3.fr/projects/Etudiants/ens<n>/TpQt
$> svn mkdir https://svn.lal.in2p3.fr/projects/Etudiants/ens<n>/TpQt/trunk \
https://svn.lal.in2p3.fr/projects/Etudiants/ens<n>/TpQt/branches \
https://svn.lal.in2p3.fr/projects/Etudiants/ens<n>/TpQt/tags \
-m "Create TpQt project"
$> svn co https://svn.lal.in2p3.fr/projects/Etudiants/ens<n>/TpQt/trunk TpQt
For this exercize, we’ll need the Interfaces package to gain access to the system libraries as well as the Qt environment. If the directory Project/Interfaces isn’t already present, create it like so:
$> cd ~/Project
$> svn export https://svn.lal.in2p3.fr/projects/Enseignement/LAL-Info/tags/head/Interfaces \
Interfaces
Then, create a new project with CMT:
$> cd ~/Project
$> cmt create TpQt v1
$> cd TpQt
$> svn add cmt src
The last piece of boilerplate is the cmt/requirements file which should look like:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 | package TpQt
# to gain access to doxygen
use Platform v1r* ${HOME}/Project/Interfaces
# for the Qt environment
use Qt v2r* ${HOME}/Project/Interfaces
# tools for the build (shared libraries, rules, ...)
use dld v2r* ${HOME}/Project/Interfaces
# add our directory of headers for the compiler
include_dirs $(TPQTROOT)/include
# rule to create a 'moc' file (needed for the code generation for
# the signal/slots events)
document moc moc_myWindow \
FROM=../include/myWindow.h \
TO=../src/moc_myWindow.cpp
# describe how the library holding our class shall be built
library TpLib \
../src/myWindow.cpp \
../src/moc_myWindow.cpp
# arguments and options for the compilation and link of 'myWindow' class
macro lib_TpLib_cflags " ${Qt_cflags}"
macro lib_TpLib_cppflags " ${lib_TpLib_cflags}"
macro TpLib_shlibflags " ${Qt_linkopts} ${dld_linkopts}"
macro TpLib_linkopts " -L${TPQTROOT}/$(Platform_bin) -lTpLib" \
WIN32 " ${TPQTROOT}\$(Platform_bin)\TpLib.lib"
macro_append QtTestlinkopts " ${TpLib_linkopts}"
# describe how the QtTest.exe application shall be built
application QtTest QtTest.cpp
# boilerplate needed to package the application for MacOS
document darwin_app TpQt FROM=QtTest TO=../app/QtTest
# update the DYLD_LIBRARY_PATH environment variable
# (needed by the MacOS dynamic linker to find our new shared library)
path_append DYLD_LIBRARY_PATH "" \
Darwin "${TPQTROOT}/$(Platform_bin)"
# rule to create the documentation
document doxygen doc -group=documentation TO=../doc
## EOF ##
|
Note
Notice the line:
use Qt v2r* Interfaces
at the beginning of the requirements file. This line tells CMT to retrieve (and use) the definitions and configurations relevant for the Qt/v2r* package which can be located thanks to the $CMTPATH environment variable. (this env. variable has been automatically defined for you when the Terminal is launched – thanks to the configuration put in the .zshrc profile file)
Now comes the task of creating the myWindow class. First the header file ../include/myWindow.h:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 | #ifndef TPQT_MYWINDOW_H
#define TPQT_MYWINDOW_H 1
// qt related includes
#include <QtGui/QMainWindow>
#include <QtGui/QPushButton>
/**
* @file myWindow.h
* @author LAL ens <ens@lal.in2p3.fr>
* @date March 2007
*
* @brief first class HelloWorld in Qt
*
*
*/
class myWindow : public QMainWindow
{
// the macro Q_OBJECT is mandatory to hint Qt with the signal/slot
// communication between (instances of) classes.
Q_OBJECT
public:
/** @brief Constructor of our main class
* @param parent : Parent widget of the class. In our case this will be the
* main window (so the parent will be "NULL") "NULL"
* @param fl : Creation flags for the window. This is useful to create a
* window which can't be resized, or without a 'quit' button, etc...
*/
myWindow( QMainWindow* parent = 0, Qt::WFlags fl = Qt::Window );
/** @brief Destructor.
*/
virtual ~myWindow();
private:
/** @brief The 'hello' PushButton
*/
QPushButton* m_hello;
};
#endif // !TPQT_MYWINDOW_H
|
Then the implementation, which we save under ../src/myWindow.cpp:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 | /**
* @file myWindow.cpp
* @author LAL ens <ens@lal.in2p3.fr>
* @date March 2007
*
* @brief first class HelloWorld in Qt
*
*
*/
// Interface
#include <myWindow.h>
/** @brief Constructor for the myWindow class
*
* Our class, in order to be a display window, needs to inherit from the
* @c QMainWindow class of the Qt framework.
* @param parent : Parent widget of the class. In our case, this will be the
* main window, so the parent is actually a NULL pointer.
* @param fl : Creation flags for the window. This is useful to create a
* window which can't be resized, or without a 'quit' button, etc...
*/
myWindow::myWindow( QMainWindow* parent, Qt::WFlags fl )
: QMainWindow( parent, fl )
{
// Create the push button
m_hello = new QPushButton(...);
// To complete the above line, heed towards the Qt documentation
// the documentation of QPushButton is available here:
// http://doc.qt.digia.com/4.0/qpushbutton.html
// display our button (in a central position)
setCentralWidget( m_hello );
}
/**
* @brief Destroy an instance of myWindow (and all the objects it may hold)
* i.e. the button we created previously.
*/
myWindow::~myWindow()
{
delete m_hello;
}
|
Eventually comes the main function, which is put in QtTest.cpp:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | /**
* @file QtTest.cpp
* @author LAL ens <ens@lal.in2p3.fr>
* @date March 2007
*
* @brief first HelloWorld en Qt
*
*
*/
// qt-related includes
#include <QtGui/QApplication>
#include <QtGui/QPushButton>
// local includes
#include "myWindow.h"
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
myWindow * mw = new myWindow;
mw->show();
return app.exec();
}
|
Before being able to build and test our new application, we need to execute the cmt/setup.sh script which will correctly configure a few environment variables (Qt environment, libraries handling, ...) Heed towards the cmt directory and issue:
$> cmt config
$> source ./setup.sh
Now you can modify the myWindow.cpp file to make it compilable... Then:
$> cmt make
$> cmt make doc
Important
During the execution of these 2 commands and during this whole session (and all the following ones!) do NOT just look at their output without trying to analyze the messages logged on the screen. Do not forget that if a syntax error was inserted in your code or if a file is missing, these commands won’t clobber (nor replace) the previously built executable (or documentation), thus one might (wrongly!) be led to believe no modifications were taken into account by the build system...
Finally, we can test the application (via 2 methods):
$> open ../app/QtTest.app
Warning
The text output from the standard output std::cout are not shown in a graphical application. Indeed, such an application works in a completely decoupled fashion from the Terminal. However, it is possible to retrieve these outputs via the system console:
$> open /Applications/Utilities/Console.app
This console is used for all the system outputs so it is hardly surprising if you find a great number of messages coming from other applications. It is possible to filter the messages (top-right), though. (beware: it isn’t case sensitive.)
We can then save and commit this first and initial version in our svn repository, after having removed the files we don’t care to track and version:
$> rm -r ../Darwin ../doc ../app
$> rm Darwin.make Makefile *.csh *.sh
$> svn add cmt src
$> svn commit -m "initial version" .
We can then go back under the src directory where all the source files we modify are located:
$> cd ./src
As you may have noticed, no action was associated with the button of the previous application. In order to fix this issue, we’ll use the signal/slot mechanism of Qt.
To leverage all of this machinery, it’s sufficient to add the following line, just after the creation of the button, in myWindow.cpp:
1 2 3 | // Connection of the signal 'clicked()' (from the button)
// to the slot 'close()' (of the QMainWindow)
connect( m_hello, SIGNAL(clicked()), this, SLOT(close()) );
|
As usual, rebuild, test and then commit in the svn repository:
$> cmt make
$> cmt make doc
$> open ../app/QtTest.app
$> cd ..
$> svn commit -m "adding a signal/slot communication"
Note
It sometimes so happens that the compiler doesn’t completely take into account your last modification. In this case, the application will compile successfully but will crash at runtime (when you execute it.) This is because the automatically generated moc_myWindow file hasn’t been regenerated nor recompiled. To fix this, just delete it (in src) and recompile (it should be regenerated anew.)
We wish now to add our own action when someone clicks on a menu.
Thus, we’ll first start by adding an action to our menu Open which we’ll connect to a method modify() (defined later on.)
After the creation of the menu Open, add the following lines:
1 2 | // add a new item "Modify" which we connect to the method modify()
fileMenu->addAction( "Modify", this, SLOT(modify()) );
|
Then, we add the method modify() which will be called whenever the menu Modify is clicked on. This method will be called by an event (a click on the menu), so it isn’t a regular C++ method but really a new slot which we need to define.
In the myWindow.h header file, add the following lines in the body of the class:
1 2 | public slots:
void modify();
|
Now, we need to implement it, in the myWindow.cpp source file:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | /** @brief This method modifies the text of the 'hello' button.
* Documentation about @c QPushButton is available here:
* http://doc.qt.digia.com/4.0/qpushbutton.html
* @c QPushButton is a special kind of button.
* Indeed, buttons can be PushButtons, CheckButtons, RadioButtons, etc...
* It inherits the properties of an abstract button type: QAbstractButton.
* Documentation about @c QAbstractButton is available here:
* http://doc.qt.digia.com/4.0/qabstractbutton.html
*/
void myWindow::modify()
{
// Complete this line according to the documentation
m_hello->setText(...);
}
|
Rebuild, test and commit in svn.