| Tutorial | Classes | Functions | QSA Developer | Language | Library | Qt API | Qt Script for Applications | ![]() |
This example is a spreadsheet written in C++ which provides interfaces for QSA. The end user can write macros in QSA Developer and execute them from the spreadsheet's toolbar and menu. The macros, which are script functions, can read and modify the spreadsheet's data.
In addition, QSA's scripting engine is used for performing cell calculations.
Header file:
#ifndef SHEETINTERFACE_H #define SHEETINTERFACE_H #include <qobject.h> class QTable; class SpreadSheet; class SheetInterface : public QObject { Q_OBJECT Q_PROPERTY( int numSelections READ numSelections ) Q_PROPERTY( int currentRow READ currentRow WRITE setCurrentRow ) Q_PROPERTY( int currentColumn READ currentColumn WRITE setCurrentColumn ) Q_PROPERTY( int numRows READ numRows ) Q_PROPERTY( int numCols READ numCols ) public: SheetInterface( QTable *t, SpreadSheet *s, const char *name ); int numSelections() const; void setCurrentRow( int r ); void setCurrentColumn( int c ); int currentRow() const; int currentColumn() const; int numRows() const; int numCols() const; public slots: void setText( int row, int col, const QString &val ); QString text( int row, int col ) const; QRect selection( int num ) const; signals: void currentChanged( int row, int col ); private: QTable *sheet; SpreadSheet *spreadSheet; }; #endif
Implementation files:
#include "sheetinterface.h" #include <qtable.h> #include "spreadsheet.h" SheetInterface::SheetInterface( QTable *t, SpreadSheet *s, const char *name ) : QObject( t, name ), sheet( t ), spreadSheet( s ) { connect( t, SIGNAL( currentChanged( int, int ) ), this, SIGNAL( currentChanged( int, int ) ) ); } int SheetInterface::numSelections() const { return sheet->numSelections(); } void SheetInterface::setText( int row, int col, const QString &val ) { sheet->setText( row, col, val ); } QString SheetInterface::text( int row, int col ) const { return sheet->text( row, col ); } QRect SheetInterface::selection( int num ) const { QTableSelection s( sheet->selection( num ) ); QRect r; r.setLeft( s.leftCol() ); r.setTop( s.topRow() ); r.setRight( s.rightCol() ); r.setBottom( s.bottomRow() ); return r; } void SheetInterface::setCurrentRow( int r ) { sheet->setCurrentCell( r, sheet->currentColumn() ); } void SheetInterface::setCurrentColumn( int c ) { sheet->setCurrentCell( sheet->currentRow(), c ); } int SheetInterface::currentRow() const { return sheet->currentRow(); } int SheetInterface::currentColumn() const { return sheet->currentColumn(); } int SheetInterface::numRows() const { return sheet->numRows(); } int SheetInterface::numCols() const { return sheet->numCols(); }
/**************************************************************************** ** ui.h extension file, included from the uic-generated form implementation. ** ** If you wish to add, delete or rename slots use Qt Designer which will ** update this file, preserving your code. Create an init() slot in place of ** a constructor, and a destroy() slot in place of a destructor. *****************************************************************************/ #include <qfiledialog.h> #include <qpixmap.h> #include <qsinterpreter.h> #include <qsproject.h> #include <qmessagebox.h> void AddScriptDialog::init() { // List all global functions of the project comboFunction->insertStringList( QSInterpreter::defaultInterpreter()-> globalFunctions() ); } void AddScriptDialog::choosePixmap() { QString f = QFileDialog::getOpenFileName(); if ( f.isEmpty() ) return; QPixmap pix( f ); labelPixmap->setPixmap( pix ); } void AddScriptDialog::addScript() { QSInterpreter *script = QSInterpreter::defaultInterpreter(); QString func = comboFunction->currentText(); if ( script->globalFunctions().findIndex( func ) == -1 ) { QString msg = tr( "The function <b>%1</b> doesn't exist. " "Do you want to add it?" ).arg( func ); if ( QMessageBox::information( 0, tr( "Add Function" ), msg, tr( "&Yes" ), tr( "&No" ), "", 0, 1 ) == 0 ) { // if the function doesn't exist yet, add it to the // project and open the IDE with it script->currentProject()->addFunction( func ); } } emit newScript( func, editName->text(), *labelPixmap->pixmap() ); accept(); }
/**************************************************************************** ** ui.h extension file, included from the uic-generated form implementation. ** ** If you wish to add, delete or rename slots use Qt Designer which will ** update this file, preserving your code. Create an init() slot in place of ** a constructor, and a destroy() slot in place of a destructor. *****************************************************************************/ #include <qsproject.h> #include <qheader.h> #include <qapplication.h> #include "addscriptdialog.h" #include "sheetinterface.h" void SpreadSheet::init() { currentSheet = sheet1; // create a sheet interface object for each sheet, and add it as // application object to the engine interpreter.addObject( new SheetInterface( sheet1, this, "sheet1" ) ); interpreter.addObject( new SheetInterface( sheet2, this, "sheet2" ) ); setupSheet( sheet1 ); setupSheet( sheet2 ); // open the project which the user will work in all the time interpreter.currentProject()->open( "sheet.qsa" ); } void SpreadSheet::setupSheet( QTable *t ) { int num = -1; static QString letters = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; int ch = 0; for ( int i = 0; i < (int)t->numCols(); ++i ) { if ( ch == 26 ) { ch = 0; num++; } QString s = QString( letters[ ch ] ); if ( num != -1 ) s.prepend( letters[ num ] ); ch++; t->horizontalHeader()->setLabel( i, s ); } } void SpreadSheet::fileExit() { qApp->quit(); } void SpreadSheet::currentCellChanged( int row, int col ) { currentCell->setText( QString( currentSheet->name() ) + ":" + currentSheet->horizontalHeader()->label( col ) + QString::number( row + 1 ) ); QMap<QPair<int, int>, QString> ¤tMap = ( currentSheet == sheet1 ) ? sheet1Map : sheet2Map; QMap<QPair<int, int>, QString>::ConstIterator it = currentMap.find( QPair<int, int>( row, col ) ); if ( it == currentMap.end() ) formulaEdit->setText( currentSheet->text( row, col ) ); else formulaEdit->setText( *it ); } void SpreadSheet::sheetChanged( QWidget *page ) { if ( page == tabSheet1 ) currentSheet = sheet1; else currentSheet = sheet2; currentCellChanged( currentSheet->currentRow(), currentSheet->currentColumn() ); } void SpreadSheet::currentValueChanged( int row, int col ) { formulaEdit->setText( currentSheet->text( row, col ) ); evaluate(); } QString SpreadSheet::cellName( int row, int col ) { QString s; int i = col / 26; static QString letters = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; if ( i > 0 ) { --i; s = QString( letters[i] ); } s += letters[col % 26]; s += QString::number( row + 1 ); return s; } void SpreadSheet::evaluate() { QMap<QPair<int, int>, QString> ¤tMap = ( currentSheet == sheet1 ) ? sheet1Map : sheet2Map; currentMap.replace( QPair<int, int>( currentSheet->currentRow(), currentSheet->currentColumn() ), formulaEdit->text() ); QMap<QPair<int, int>, QString>::ConstIterator it; QString vars; for ( it = currentMap.begin(); it != currentMap.end(); ++it ) { QTableItem *item = currentSheet->item( it.key().first, it.key().second ); if ( !item ) continue; bool ok = FALSE; item->text().toInt( &ok ); if ( !ok ) continue; vars += "var " + cellName( item->row(), item->col() ) + "=" + item->text() + ";\n"; } for ( it = currentMap.begin(); it != currentMap.end(); ++it ) { QTableItem *item = currentSheet->item( it.key().first, it.key().second ); if ( !item ) continue; evaluateCell( item, *it, vars ); } } void SpreadSheet::evaluateCell( QTableItem *item, const QString &f, const QString &vars ) { QString formula( f ); int row = item->row(); int col = item->col(); if ( formula[0] != '=' ) return; QString cn = cellName( row, col ); formula = vars +"\n" + cn + "=" + formula.mid( 1 ) + ";\n" + "Application." + QString( currentSheet->name() ) + ".setText( " + QString::number( row ) + ", " + QString::number( col ) + ", String( " + cn + " ) );"; interpreter.evaluate( formula ); } void SpreadSheet::formulaEdited() { currentSheet->setText( currentSheet->currentRow(), currentSheet->currentColumn(), formulaEdit->text() ); evaluate(); currentSheet->setFocus(); } void SpreadSheet::openDeveloper() { // open the Qt Script for Applications Developer interpreter.currentProject()->openDeveloper(); } void SpreadSheet::addScript() { // Let the user add a script AddScriptDialog dia( this, 0, TRUE ); connect( &dia, SIGNAL( newScript( const QString &, const QString &, const QPixmap & ) ), this, SLOT( addScript( const QString &, const QString &, const QPixmap & ) ) ); dia.exec(); } void SpreadSheet::addScript( const QString &function, const QString &name, const QPixmap &pixmap ) { // Add a new action for the script QAction *a = new QAction( name, pixmap, name, 0, this, name.latin1() ); a->addTo( scriptsToolbar ); a->addTo( scriptsMenu ); // associate the action with the function name scripts.insert( a, function ); connect( a, SIGNAL( activated() ), this, SLOT( runScript() ) ); } void SpreadSheet::runScript() { // find the function which has been associated with the activated // action (the action is the sender()) QString s = *scripts.find( (QAction*)sender() ); // and call that function interpreter.call( s, QValueList<QVariant>() ); }
Main:
#include <qapplication.h> #include "spreadsheet.h" int main( int argc, char ** argv ) { QApplication a( argc, argv ); SpreadSheet *w = new SpreadSheet; w->show(); a.connect( &a, SIGNAL( lastWindowClosed() ), &a, SLOT( quit() ) ); return a.exec(); }
The sheet.qsa file contains:
EuroSettings.ui.qs
connect( EuroSettings, "shown()", this, "init" ); connect( EuroSettings, "destroyed()", this, "destroy" ); connect( buttonCalculate, "clicked()", this, "buttonCalculate_clicked" ); connect( buttonCancel, "clicked()", this, "buttonCancel_clicked" ); function init() { if ( Application.sheet1.numSelections > 0 ) { var r = Application.sheet1.selection( 0 ); spinStartRow.value = r.y + 1; spinEndRow.value = r.y + r.height; spinColumn.value = r.x + 1; spinOutputColumn.value = r.x + 2; } } function destroy() { } function buttonCancel_clicked() { reject(); } function buttonCalculate_clicked() { var divisor; switch ( comboCurrency.currentText ) { case "NOK": divisor = 7.8680; break; case "DM": divisor = 1.95583; break; case "ATS": divisor = 13.7603; break; } const inputColumn = spinColumn.value - 1; const outputColumn = spinOutputColumn.value - 1; const startRow = spinStartRow.value - 1; const endRow = spinEndRow.value - 1; if ( endRow < startRow ) { MessageBox.critical( "Cannot calculate!", "Error" ); accept(); return; } for ( var i = startRow; i <= endRow; ++i ) Application.sheet1.setText( i, outputColumn, Application.sheet1.text( i, inputColumn ) / divisor ); accept(); }
main.qs
// Put all global functions here function convertToEuro() { Application.EuroSettings.exec(); }
See also QSA Examples.
Copyright © 2001-2002 Trolltech | Trademarks | QSA version 1.0.0-beta1
|