Qt programs are capable of loading Qt Designer .ui files and instantiating the forms represented by the .ui files. Since the .ui file is not compiled it cannot include any C++ code, (e.g. slot implementations). In this section we will explain how to load a dynamic dialog and how to create a class that can be used to implement the dynamic dialog's custom slots.
We will use the credit form that we created in the subclassing section as our example form. We will start by simply instantiating and running the form and then we'll cover how to implement custom slots.
We'll create a main.cpp file to use as a test harness, and manually create a project file.
The project file qt/tools/designer/eg/receiver1/receiver.pro looks like this:
TEMPLATE = app CONFIG = qt warn_on release TARGET = receiver SOURCES += main.cpp unix:LIBS += -lqresource win32:LIBS += $(QTDIR)/lib/qresource.lib INTERFACES = mainform.ui PROJECTNAME = receiver |
The main.cpp is quite standard. It will invoke the form we're going to create in Qt Designer as its main form. This form will then load and execute the dynamic dialog.
#include <qapplication.h> #include "mainform.h" int main( int argc, char *argv[] ) { QApplication app( argc, argv ); MainForm *mainForm = new MainForm; app.setMainWidget( mainForm ); mainForm->show(); return app.exec(); } |
Open the receiver.pro project file in Qt Designer. We'll create a dialog as our main window which we'll use to invoke the dynamic dialog. Press Ctrl+N to launch the New Form dialog and click OK to get the default which is a dialog. Change the dialog's name to 'MainForm' and its caption to 'Main Form'. Add two buttons, one called 'creditPushButton' with the text '&Credit Dialog', and the other called 'quitPushButton' with the text '&Quit'. (For each button click the Push Button toolbar button, then click the form. Change the properties in the property window to those we've just described.)
We will now add a couple of labels so that we can show the settings the user chose in the dynamic dialog. Click the Text Label toolbar button, then click the form below the Credit Dialog button. Change the label's text to 'Credit Rating'. Add another text label below the Quit button. Change its name to 'ratingTextLabel' and its text to 'Unrated'.
We'll now lay out the widgets. Click the form then press Ctrl+G (lay out in a grid).
We'll now handle the signals and slots connections. Press F3 (connect signals/slots). Click the Credit Dialog button, drag to the form and release. Click the clicked() signal. We'll need to implement a custom slot. Click Edit Slots to invoke the Edit Slots dialog. Click New Slot and type in the Slot name 'creditDialog()'. Click OK. The new slot is now in the list of slots; click the creditDialog() slot to make the connection then click OK. Connect the Quit button's clicked() signal to the dialog's accept() function. (Press F3. Click the Quit button and drag to the form; release. Click the clicked() signal and the accept() slot, then click OK.)
Save the form and call it mainform.ui. (Press Ctrl+S and enter the filename.) In the next section we'll write the code for loading and launching the dynamic dialog directly in Qt Designer.
We'll now add the code to invoke the credit dialog. Before we can do this we need to add the widget factory's header file to the form. Click the Source tab in the Object Hierarchy. Right click Included (in Implementation), then click New. Type in '<qwidgetfactory.h>', then press Return. Because we will need to access the spin box in the dynamic dialog we must add its header file. Right click Included (in Implmentation), then click New. Type in '<qspinbox.h>', then press Return.
In our main form we created a slot called creditDialog(). We will implement this slot directly in Qt Designer and use it to load and execute the dynamic dialog. The code is taken from qt/tools/designer/eg/receiver1/mainform.cpp which is generated from mainform.ui.
void MainForm::creditDialog() { QDialog *creditForm = (QDialog *) QWidgetFactory::create( "../credit/creditformbase.ui" ); // Set up the dynamic dialog here if ( creditForm->exec() ) { // The user accepted, act accordingly QSpinBox *amount = (QSpinBox *) creditForm->child( "amountSpinBox", "QSpinBox" ); if ( amount ) ratingTextLabel->setText( amount->text() ); } delete creditForm; } |
We used the child() to gain access to a widget within the dynamic dialog, passing it the name of the widget we were interested in. In some situations we might not know what a widget is called. We can access the first widget of a specified class by calling child() with a null widget name and a classname, e.g. child(0,"QPushButton"). This will return a pointer to the first QPushButton it finds (or 0 if there isn't one). If you want pointers to all the widgets of a given class you can call the QObject::queryList() function, passing it the name of the class. It returns a QObjectList pointer which points to every object in the dialog that is derived from the given class. See the online QObject documentation for further details.
There is one outstanding issue that we haven't addressed: the dynamic dialog does not have the behaviour of the original credit dialog because we have not implemented the setAmount() slot. We can implement slots for dynamic dialogs by creating a QObject subclass. We then create an instance of this subclass and pass a pointer to it to the QWidgetFactory::create() function which will connect the dynamic dialog's signals to the slots implemented in our subclass.
We need to create a QObject subclass and change our creditDialog() to create an instance of our subclass that can be passed to the QWidgetFactory::create() function. Here is the modified creditDialog() function from the qt/tools/designer/eg/receiver2/mainform.cpp file generated by mainform.ui.
void MainForm::creditDialog() { Receiver *receiver = new Receiver; QDialog *creditForm = (QDialog *) QWidgetFactory::create( "../credit/creditformbase.ui", receiver ); receiver->setParent( creditForm ); // Set up the dynamic dialog here if ( creditForm->exec() ) { // The user accepted, act accordingly QSpinBox *amount = (QSpinBox *) creditForm->child( "amountSpinBox", "QSpinBox" ); if ( amount ) ratingTextLabel->setText( amount->text() ); } delete receiver; delete creditForm; } |
We'll now look at the implementation of our 'Receiver' subclass. The code is taken from qt/tools/designer/eg/receiver2/receiver.h and the corresponding receiver.cpp file. We'll start with the header file.
#include <qobject.h> #include <qdialog.h> class Receiver : public QObject { Q_OBJECT public: void setParent( QDialog *parent ); public slots: void setAmount(); private: QDialog *p; }; |
We'll look at the implementation of each function separately.
void Receiver::setParent( QDialog *parent ) { p = parent; setAmount(); } |
void Receiver::setAmount() { QSpinBox *amount = (QSpinBox *) p->child( "amountSpinBox", "QSpinBox" ); QRadioButton *radio = (QRadioButton *) p->child( "stdRadioButton", "QRadioButton" ); if ( radio && radio->isChecked() ) { if ( amount ) amount->setValue( amount->maxValue() / 2 ); return; } radio = (QRadioButton *) p->child( "noneRadioButton", "QRadioButton" ); if ( radio && radio->isChecked() ) if ( amount ) amount->setValue( amount->minValue() ); } |