|
||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||
|
Announcements
Want a new Job?
Chapters
Services
Feature Zones
|
IntroductionIn this article, we are going to look at the Simple Designer based KDE Application template that many people will use for their first venture into KDE programming with KDevelop. As with any other programming environment, it helps you to write your own programs if you understand the structure of the application that you are dealing with. So, with that in mind, this article will explain not only the files generated by the project wizard when you first create a project, but what files are generated when you compile a project and how they all fit together. We will, of course, be concentrating only on files that are directly relevant to programming your project, and not list the contents of configuration files etc. Creating the ProjectOpen up KDevelop and go to Projects/New Project/C++/KDE and select Simple Designer Based KDE Application.
You then set the application name and location, and click on the Next button. You will then get the Options dialog that allows you to set user preferences such as your name and email address. The Next dialog will be for the source control which we will not be using for this project. And finally, you'll be given the templates for the file headers to review. Just click through these until you get the Finish button. When you click on the Finish button, the wizard will go away and KDevelop will open your project. The main window will contain the project main file: in this case, the AnatomyDemo.cpp file. On the right of your screen will be the project information which looks like:
As you can see, the Automake Manager is just a different name for the project/solution window. The Document View ArchitectureThe Simple Designer Based KDE Application has a document view architecture in that you have a There are two header files in the project; these are under the heading (header in noinst) in the Automake Manager. The first is the AnatomyDemo.h file, which reads: class AnatomyDemo : public KMainWindow { Q_OBJECT public: /** * Default Constructor */ AnatomyDemo(); /** * Default Destructor */ virtual ~AnatomyDemo(); }; All we have here is a constructor and destructor, but it is this class that controls the layout of the application and the application peripherals such as status bar, menu, and tool bars. This is more apparent when we look at the CPP file: AnatomyDemo::AnatomyDemo() : KMainWindow( 0, "AnatomyDemo" ) { setCentralWidget( new AnatomyDemoWidget( this ) ); } AnatomyDemo::~AnatomyDemo() { } Although all we have is the constructor and destructor, you can clearly see that the only function call in the constructor is the call to In the AnatomyDemoWidget.h file, we see: class AnatomyDemoWidget : public AnatomyDemoWidgetBase { Q_OBJECT public: AnatomyDemoWidget(QWidget* parent = 0, const char* name = 0, WFlags fl = 0 ); ~AnatomyDemoWidget(); /*$PUBLIC_FUNCTIONS$*/ public slots: /*$PUBLIC_SLOTS$*/ virtual void button_clicked(); protected: /*$PROTECTED_FUNCTIONS$*/ protected slots: /*$PROTECTED_SLOTS$*/ }; As with the above, this class at the moment is nothing more than a constructor and destructor with one function (it is called a slot here, but we'll get to that, and from this class, perspective, it is a standard function), the AnatomyDemoWidget::AnatomyDemoWidget(QWidget* parent, const char* name, WFlags fl) : AnatomyDemoWidgetBase(parent,name,fl) {} AnatomyDemoWidget::~AnatomyDemoWidget() {} /*$SPECIALIZATION$*/ void AnatomyDemoWidget::button_clicked() { if ( label->text().isEmpty() ) { label->setText( "Hello World!" ); } else { label->clear(); } } As well as the constructor and destructor, we can see the implementation of the Before seeing it in action though, there are a couple of other files we haven't looked at. The main.cpp file, which is, static const char description[] = I18N_NOOP("A KDE KPart Application"); static const char version[] = "0.1"; static KCmdLineOptions options[] = { // { "+[URL]", I18N_NOOP( "Document to open" ), 0 }, KCmdLineLastOption }; int main(int argc, char **argv) { KAboutData about("anatomydemo", I18N_NOOP("AnatomyDemo"), version, description, KAboutData::License_GPL, "(C) 2008 pseudonym67", 0, 0, "pseudonym67@hotmail.com"); about.addAuthor( "pseudonym67", 0, "pseudonym67@hotmail.com" ); KCmdLineArgs::init(argc, argv, &about); KCmdLineArgs::addCmdLineOptions( options ); KApplication app; AnatomyDemo *mainWin = 0; if (app.isRestored()) { RESTORE(AnatomyDemo); } else { // no session.. just start up normally KCmdLineArgs *args = KCmdLineArgs::parsedArgs(); /// @todo do something with the command line args here mainWin = new AnatomyDemo(); app.setMainWidget( mainWin ); mainWin->show(); args->clear(); } // mainWin has WDestructiveClose flag by default, so it will delete itself. return app.exec(); } As you would expect, The final file is the anatomydemowidgetbase.ui file, which is an XML file that is used by the form/widget designer. If we open it, we get:
which is your standard drag and drop form/widget designer interface. If you want to see the XML, put the KXML Editor on your system if it's not already, and use it to open the .ui file.
Building the ApplicationIn order to build the application, go to the Build menu and select the Build Project option. You will then see a dialog that tells you that there are no make files or configuration files. These are files required for the build, and are generated automatically by KDevelop. Unless you are experienced in Linux command line programming, let KDevelop handle them for you. Run them and wait. Then, select Start in the Debug menu and you'll get:
If you click on the "Click Me!" button, the Hello World text will show on the dialog. So, what just happened? You may have noticed that while we have a file called anatomydemowidgetbase.ui, we don't have a class implementation of the same name, but we do have: class AnatomyDemoWidget : public AnatomyDemoWidgetBase { Q_OBJECT in our anatomydemowidget.h file. Also, what is a slot and how did the "Click Me!" button work? The answers to these questions are things that the Qt library does in the background, so in order to answer these questions, we need to know about some things called the Meta Object Compiler and the User Interface Compiler. First, we'll look at the User Interface Compiler, User Interface CompilerThe User Interface Compiler is fairly straightforward in that it takes the .ui file, in this case, the anatomydemowidgetbase.ui file, and generates a C++ class that implements the form exactly as you have defined it in the GUI. So, in the debug/src directory, we will now have the files anatomydemowidgetbase.h and anatomydemowidgetbase.cpp. The header file reads: #ifndef ANATOMYDEMOWIDGETBASE_H #define ANATOMYDEMOWIDGETBASE_H #include <qvariant.h> #include <qwidget.h> class QVBoxLayout; class QHBoxLayout; class QGridLayout; class QSpacerItem; class QPushButton; class QLabel; class AnatomyDemoWidgetBase : public QWidget { Q_OBJECT public: AnatomyDemoWidgetBase( QWidget* parent = 0, const char* name = 0, WFlags fl = 0 ); ~AnatomyDemoWidgetBase(); QPushButton* button; QLabel* label; public slots: virtual void button_clicked(); protected: QGridLayout* anatomydemowidgetbaseLayout; protected slots: virtual void languageChange(); }; #endif // ANATOMYDEMOWIDGETBASE_H And, this is the class that we inherit from the While the header file reads: #include "anatomydemowidgetbase.h" #include <qvariant.h> #include <qpushbutton.h> #include <qlabel.h> #include <qlayout.h> #include <qtooltip.h> #include <qwhatsthis.h> /* * Constructs a AnatomyDemoWidgetBase as a child of 'parent', with the * name 'name' and widget flags set to 'f'. */ AnatomyDemoWidgetBase::AnatomyDemoWidgetBase( QWidget* parent, const char* name, WFlags fl ) : QWidget( parent, name, fl ) { if ( !name ) setName( "anatomydemowidgetbase" ); anatomydemowidgetbaseLayout = new QGridLayout( this, 1, 1, 11, 6, "anatomydemowidgetbaseLayout"); button = new QPushButton( this, "button" ); anatomydemowidgetbaseLayout->addWidget( button, 1, 0 ); label = new QLabel( this, "label" ); anatomydemowidgetbaseLayout->addWidget( label, 0, 0 ); languageChange(); resize( QSize(220, 133).expandedTo(minimumSizeHint()) ); clearWState( WState_Polished ); // signals and slots connections connect( button, SIGNAL( clicked() ), this, SLOT( button_clicked() ) ); } /* * Destroys the object and frees any allocated resources */ AnatomyDemoWidgetBase::~AnatomyDemoWidgetBase() { // no need to delete child widgets, Qt does it all for us } /* * Sets the strings of the subwidgets using the current * language. */ void AnatomyDemoWidgetBase::languageChange() { setCaption( QString::null ); button->setText( tr2i18n( "Click Me!" ) ); label->setText( QString::null ); } void AnatomyDemoWidgetBase::button_clicked() { qWarning( "AnatomyDemoWidgetBase::button_clicked(): Not implemented yet" ); } #include "anatomydemowidgetbase.moc" As you can see, this is pretty much what you would expect from a file that implements a form. The things of note are the Meta Object CompilerThe Meta Object Compiler is the Qt type management system that provides a few benefits to the developer. Its main function is to read the C++ source files before they are compiled, and if the class declaration contains the const char *AnatomyDemoWidget::className() const { return "AnatomyDemoWidget"; } QMetaObject *AnatomyDemoWidget::metaObj = 0; static QMetaObjectCleanUp cleanUp_AnatomyDemoWidget( "AnatomyDemoWidget", &AnatomyDemoWidget::staticMetaObject ); #ifndef QT_NO_TRANSLATION QString AnatomyDemoWidget::tr( const char *s, const char *c ) { if ( qApp ) return qApp->translate( "AnatomyDemoWidget", s, c, QApplication::DefaultCodec ); else return QString::fromLatin1( s ); } #ifndef QT_NO_TRANSLATION_UTF8 QString AnatomyDemoWidget::trUtf8( const char *s, const char *c ) { if ( qApp ) return qApp->translate( "AnatomyDemoWidget", s, c, QApplication::UnicodeUTF8 ); else return QString::fromUtf8( s ); } #endif // QT_NO_TRANSLATION_UTF8 #endif // QT_NO_TRANSLATION QMetaObject* AnatomyDemoWidget::staticMetaObject() { if ( metaObj ) return metaObj; QMetaObject* parentObject = AnatomyDemoWidgetBase::staticMetaObject(); static const QUMethod slot_0 = {"button_clicked", 0, 0 }; static const QMetaData slot_tbl[] = { { "button_clicked()", &slot_0, QMetaData::Public } }; metaObj = QMetaObject::new_metaobject( "AnatomyDemoWidget", parentObject, slot_tbl, 1, 0, 0, #ifndef QT_NO_PROPERTIES 0, 0, 0, 0, #endif // QT_NO_PROPERTIES 0, 0 ); cleanUp_AnatomyDemoWidget.setMetaObject( metaObj ); return metaObj; } void* AnatomyDemoWidget::qt_cast( const char* clname ) { if ( !qstrcmp( clname, "AnatomyDemoWidget" ) ) return this; return AnatomyDemoWidgetBase::qt_cast( clname ); } bool AnatomyDemoWidget::qt_invoke( int _id, QUObject* _o ) { switch ( _id - staticMetaObject()->slotOffset() ) { case 0: button_clicked(); break; default: return AnatomyDemoWidgetBase::qt_invoke( _id, _o ); } return TRUE; } bool AnatomyDemoWidget::qt_emit( int _id, QUObject* _o ) { return AnatomyDemoWidgetBase::qt_emit(_id,_o); } #ifndef QT_NO_PROPERTIES bool AnatomyDemoWidget::qt_property( int id, int f, QVariant* v) { return AnatomyDemoWidgetBase::qt_property( id, f, v); } bool AnatomyDemoWidget::qt_static_property( QObject* , int , int , QVariant* ){ return FALSE; } #endif // QT_NO_PROPERTIES Once #include "filename".moc The main points of interest in the .moc are the The Using the Q_OBJECT
Q_ENUMS( ToggleType ToggleState )
Q_PROPERTY( QString text READ text WRITE setText )
Q_PROPERTY( QPixmap pixmap READ pixmap WRITE setPixmap )
Q_PROPERTY( QKeySequence accel READ accel WRITE setAccel )
Q_PROPERTY( bool toggleButton READ isToggleButton )
Q_PROPERTY( ToggleType toggleType READ toggleType )
Q_PROPERTY( bool down READ isDown WRITE setDown
DESIGNABLE false )
Q_PROPERTY( bool on READ isOn )
Q_PROPERTY( ToggleState toggleState READ state )
Q_PROPERTY( bool autoResize READ autoResize WRITE setAutoResize
DESIGNABLE false )
Q_PROPERTY( bool autoRepeat READ autoRepeat WRITE setAutoRepeat )
Q_PROPERTY( bool exclusiveToggle READ isExclusiveToggle )
where you can see that it is also possible to explicitly exclude certain items from the designer. The .moc file also sets up your signals and slots. Signals and SlotsSignals and slots are Qt's way of communicating between classes. Signals are a type safe method of sending a signal/message from one class to any class that is interested. No class will automatically receive a signal from another class. Each class must request a connection to a specific signal by using the In order to achieve this, there are two new keywords that we use in our class headers. These are the public slots: /*$PUBLIC_SLOTS$*/ virtual void button_clicked(); This is simply a place setting that declares what at the class level is a standard function. The slot implementation is in the CPP file: void AnatomyDemoWidget::button_clicked() { if ( label->text().isEmpty() ) { label->setText( "Hello World!" ); } else { label->clear(); } } If we look at the UI for a second, specifically if we click on the button and look at the properties, we see there is a section called Signal Handlers:
This section will show the available signals for any object on the form, and here we can set up a response slot/function. These are defined in QButton.h as: signals:
void pressed();
void released();
void clicked();
void toggled( bool );
void stateChanged( int );
Then, if we go back to the constructor of the above function, we see the connect( button, SIGNAL( clicked() ), this, SLOT( button_clicked() ) );
The SummaryThis has been a quick guide to the anatomy of a Simple KDE application, focusing on what is generated by a simple project template, and the files that are created when you build it. For this reason, there is no source code to download as all the code is generated automatically by KDevelop and the associated Qt tools. The aim of this article is to enable people new to Qt and KDevelop programming to get started with some understanding of what is happening so they can get on with their own projects and not be sitting there scratching their heads wondering just what is going on.
|
|||||||||||||||||||||||||||||||||||||