![]() |
| ||
Classes - Annotated - Tree - Functions - Home - Structure |
The following example shows how to manipulate headers of a QTable spreadsheet, prevent columns from being modified by the user and adjust the size of the spreadsheet widget.
To do this we write a small program that converts the native currencies of the member countries deploying the EURO to EURO.
For the widget -- a tiny spreadsheet that we implement in a new class with the name EuroConverter -- we need the following API:
#include <qtable.h>
As we derive the class from QTable, the first thing to do is to include the QTable API.
class EuroConverter: public QTable { Q_OBJECT
To react to user input in the spreadsheet cells the new class gets a slot, thus the Q_OBJECT macro is needed.
public: EuroConverter();
This is our constructor. It is the only function of the EuroConverter class that can be called from outside -- for example from the main program.
private slots: void processValueChange( int, int );
When the user changes figure and currency of what he or she wants to convert to EUR, the table sends a signal that we wish to react to. Therefore we introduce the processValueChange() slot.
private: double calculate( double ); int inputcurrency; QComboTableItem * currencies; };
Last but not least we have one more private function calculate() (the one that actually does the conversion job) and declare two variables used in more than one EuroConverter functions.
Now let's fill this framework with functionality.
#include "converter.h" #include <qstring.h>
First we include the APIs of the classes we use in the EuroConverter implementation.
static struct { const char *currency; double rate;
We need something to store currencies and exchange rates. For this reason we create a struct datatype that files currencies as const char * and the exchange rates in a double. (Usually one would use a QString instead of const char * but for compatibility reasons with the SunPro cc compiler we don't use QString in a struct.)
} rates[] = {
We need this structure only once: for an array that holds the twelve currency-rate pairs. Thus we don't bother with declaring ...
{ "ATS (A)", 13.7603 }, { "BEF (B)", 40.3399 }, { "DEM (D)", 1.95583 }, { "ESP (E)", 166.386 }, { "FIM (FIN)", 5.94573 }, { "FRP (F)", 6.55957 }, { "GRD (GR)", 340.750 }, { "IEP (IR)", 0.78756 }, { "ITL (IT)", 1936.27 }, { "LUF (LUX)", 40.3399 }, { "NLG (NL)", 2.20371 }, { "PTE (PT)", 200.482 } };
... and defining it separately, and simply add Austrian Schilling, Belgian Franc, German Mark, Spanish Peseta, Finnish Finnmark, French Franc, Greek Drachm, Irish Pound, Italian Lira, Luxembourg Franc, Dutch Gulden and Portuguese Escudo each accompanied by the official exchange rate. There is only one official exchange rate based on the 1 EUR, i.e. 1 EUR equals 200.482 PTE.
const int numcurrencies = sizeof( rates ) / sizeof( rates[0] );
This list might be extended when new members enter the EURO community. To avoid changing code at more than one place we calculate each time how many currencies we have to take into consideration, i.e. the length of the rates array.
This is done by dividing the size of the entire array by the size of a single item.
EuroConverter::EuroConverter() : QTable( 1, 5, 0, "euroconverter" ) {
Now let's start with the EuroConverter constructor.
As already mentioned we derive the class from QTable: a EuroConverter object is a spreadsheet with 1 line and 5 columns. As a main widget it will have a 0 parent and the name euroconverter.
By default each QTable object comes with a horizontal header and one to the left of the actual table grid.
setLeftMargin( 0 ); verticalHeader()->hide();
The latter we don't really want to be there -- overall the EURO converter is a one line form.
To safely get rid of the vertical header we first set its width to zero and hide it afterwards. Note that setLeftMargin() is a QTable function whilst for the hiding we have to obtain the topmost QHeader using verticalHeader() and use the QHeader function hide() on the returned header object.
horizontalHeader()->setLabel( 0, "Value" ); horizontalHeader()->setLabel( 1, "Currency (Country)" ); horizontalHeader()->setLabel( 2, "" ); horizontalHeader()->setLabel( 3, "Value in Euro" ); horizontalHeader()->setLabel( 4, "" );
The horizontal header on top of the table is more valuable for us but we better change the column titles to something more descriptive. This is done by changing the label of each QHeader. Thus we obtain a pointer to the horizontal QHeader and apply QHeader::setLabel() on it.
This way we end up with a first column titled Value, a second column with the title Currency (Country), and a fourth named Value in Euro. For the third and the last column we change the label to an empty string.
Note that -- contrary to what the default labels suggest -- the first column (and of course the first row as well) are addressed by 0.
QStringList currencylist;
To give the user a choice between the various national currencies we use a combo box. Fortunately the Qt table module provides us with a class named QComboTableItem to easily fill a cell with a combo box.
The QComboTableItem constructor expects us to provide it with all combo box menu entries in one string list.
for ( int i = 0; i < numcurrencies; i++ ) currencylist << rates[i].currency;
This currencylist variable we fill with the currency part of the rates[] structure. In the first round of the for loop we add the text denoting Austrian Schilling; PTE (PT) comes as the last entry of the string list.
Note how easy it is to add a string to a QStringList: simply use the << operator.
currencies = new QComboTableItem( this, currencylist );
Now we create the combo box table item with the menu entries from currencylist as a child of the respective EuroConverter object.
setItem( 0, 1, currencies );
To place the combo box in the second cell of the one and only row of our EuroConverter table we explicitly make currencies the table item of this cell.
inputcurrency = 0;
The inputcurrency variable holds an internal counter indicating the currency to be used for the calculation. To begin with this is -- by QComboTableItem default -- the first item of the rates[] list.
setText( 0, 2, "equals" ); setText( 0, 4, "EUR" );
Apart from this the initial form has a blank first column (the user should define a value not we) and consequently a blank result in the fourth column.
In column 2 we place the string equals and in the last column the string EUR.
setColumnReadOnly( 2, TRUE ); setColumnReadOnly( 3, TRUE ); setColumnReadOnly( 4, TRUE );
Neither of them is to be changed by the user -- and we will of course not provide the user with an opportunity to manipulate the result of the calculation in column 3 either. That's why we set these three columns read-only.
adjustColumn( 1 ); adjustColumn( 2 ); adjustColumn( 3 ); adjustColumn( 4 );
Finally we adjust the size of all columns apart from the first one according to their longest entry -- which in case of column 1 and 3 is the respective header title.
The first column we don't adjust: its headline Value is very short, and the user might easily decide to enter a longer number.
adjustSize();
A table widget with plenty of whitespace to the right of and below the table grid is not very nice in our case of a table main widget. Therefore we adjust the size of the EuroConverter widget to the size of the table.
connect( this, SIGNAL( valueChanged( int, int ) ), this, SLOT( processValueChange( int, int ) ) ); }
Last but not least we want an EuroConverter object to react to user input. Therefore we connect the table's valueChanged() signal to our custom slot processValueChange().
The signal carries two integer arguments denoting the row and the column of the cell and is issued when the user is done with editing the cell in question.
void EuroConverter::processValueChange( int, int col ) {
processValueChange() is the slot we use to react to changes the user applies to the spreadsheet. As we have one row only we can forget about the row information and simply deal with the column number the slot gets from the valueChanged signal.
if ( col == 0 ){
As we set the last three columns read-only it is getting even easier: we have to take care of only column 0 and column 1. Column 0 is the one where the user enters a numerical value.
bool ok; double value;
The local variable value we use to store the value the user entered. With the help of ok we check whether the user entered a numerical value indeed.
value = text( 0, col ).toDouble( &ok );
To find out what exactly the user has entered we obtain the text() of cell 0,0 and convert this QString to a double value.
If the cell contains letters or other non-numerical characters which don't make sense in a number ok is set to FALSE.
if ( ok ){
Thus we can check whether the user typed in something meaningful. If this is the case...
double euro = calculate( value );
... we use our own calculate() function to convert the value to EUR...
setText( 0, 3, QString::number( euro ) );
... and set the fourth cell to display exactly this result. As setText() expects a QString as its third argument we transform the double value euro to a QString using QString::number().
} else {
If the user entered something non-numerical...
setText( 0, 0, "" ); setText( 0, 3, "" ); }
... we wipe out the content of the first and the fourth column so that he or she realizes that the entry wasn't something that can be converted to EUR.
} else if ( col == 1 ){
If the user changes the active entry of the combo box in column 1 ...
inputcurrency = currencies->currentItem();
... we set the intern variable inputcurrency to the number of the entry. To find out which entry the user switched to we use QComboTableItem::currentItem().
emit valueChanged( 0, 0 ); } }
Additionally we fake a value change in cell 0,0 to make the EuroConverter recalculate the EUR value in column 3. Note that because the signal valueChanged() is connected to processValueChange() this causes a recursive function call.
double EuroConverter::calculate( const double value ) {
To actually calculate the appropriate EUR equivalent of the numerical value the user enters we take the respective double value, ...
return ( value / rates[inputcurrency].rate ); }
... devide it by the rate the inputcurrency variable points out in the rates[] array, and return the result.
For those who have already written GUI programs with Qt the main program does not bear any news.
#include "converter.h" #include <qapplication.h>
First we include the API of the EuroConverter and the QApplication class.
int main( int argc, char ** argv ) { QApplication app( argc, argv );
The command line arguments (even if not used in this example) are handed over to the new application object app.
EuroConverter * euroconvert = new EuroConverter();
We create an instance of the EuroConverter class,
app.setMainWidget( euroconvert );
make it the main widget of the application,
euroconvert->show();
make it visible for the user,
return app.exec(); }
and start the execution loop of the application.
Copyright © 2000 Trolltech | Trademarks | Qt version main-beta1
|