|Home | Tutorial | Classes | Functions | QSA Developer | Language | Library | Qt API | QSA Articles Qt Script for Applications

A scriptable database front-end

This example demonstrates how to use databases within a QSA script. Three small scripts are included: The first creates a SQL query and generates a graphical output from the result. The second provides a small dialog with some information about the people stored in the database. The third demonstrates a simple way to run frequently used queries.

To run this example you will need to have a database running and to have built Qt with the SQL Module. The database should contain the 'people' table. An example SQL script to create this table and populate it with test data is included in the people.sql file.


Header file:

#ifndef DATABASE_H
#define DATABASE_H

#include <qmainwindow.h>
#include <qmap.h>
#include <qaction.h>
#include <qsinterpreter.h>
#include "connect.h"

class QDataTable;
class QLabel;
class QListView;
class QListViewItem;
class QPushButton;
class QTextEdit;

class DataBase : public QMainWindow
{
    Q_OBJECT

public:
    DataBase( QWidget* parent =  0, const char* name = 0,
        WFlags fl = WType_TopLevel );
    ~DataBase();

protected:

protected slots:
    void dbConnect();
    void execQuery();
    void showTable( QListViewItem * item );
    void exit();
    void openIDE();
    void scriptChooser();
    void runScript();

private:
    void setupActions();
    void initQSA();

    QSInterpreter interpreter;
    QListView *listView;
    QDataTable *sqlTable;
    QTextEdit *queryTextEdit;
    QLabel *label;
    QPushButton *submitButton;

    QMap<QAction *, QString> scripts;
    ConnectDialog *conDiag;
};

#endif // DATABASE_H
/****************************************************************************
** Form interface generated from reading ui file '../../examples/database/connect.ui'
**
** Created: Wed Jan 29 16:18:00 2003
**      by: The User Interface Compiler ($Id: $)
**
** WARNING! All changes made in this file will be lost!
****************************************************************************/

#ifndef CONNECTDIALOG_H
#define CONNECTDIALOG_H

#include <qvariant.h>
#include <qdialog.h>

class QVBoxLayout;
class QHBoxLayout;
class QGridLayout;
class QGroupBox;
class QLineEdit;
class QLabel;
class QComboBox;
class QSpinBox;
class QPushButton;

class ConnectDialog : public QDialog
{
    Q_OBJECT

public:
    ConnectDialog( QWidget* parent = 0, const char* name = 0, bool modal = FALSE, WFlags fl = 0 );
    ~ConnectDialog();

    QGroupBox* GroupBox3;
    QLineEdit* editDatabase;
    QLabel* TextLabel3;
    QLineEdit* editPassword;
    QLabel* TextLabel4;
    QLabel* TextLabel4_2;
    QComboBox* comboDriver;
    QLineEdit* editUsername;
    QLineEdit* editHostname;
    QLabel* TextLabel5;
    QLabel* TextLabel5_2;
    QSpinBox* portSpinBox;
    QLabel* TextLabel2;
    QPushButton* PushButton1;
    QPushButton* PushButton2;

public slots:
    virtual void init();
    virtual void destroy();

protected:
    QVBoxLayout* ConnectDialogLayout;
    QGridLayout* GroupBox3Layout;
    QHBoxLayout* Layout25;

protected slots:
    virtual void languageChange();

};

#endif // CONNECTDIALOG_H


Implementation files:

#include "database.h"

#include <qsproject.h>
#include <qvariant.h>
#include <qdatatable.h>
#include <qgroupbox.h>
#include <qheader.h>
#include <qlabel.h>
#include <qlistview.h>
#include <qpushbutton.h>
#include <qsplitter.h>
#include <qtextedit.h>
#include <qlayout.h>
#include <qtooltip.h>
#include <qwhatsthis.h>
#include <qaction.h>
#include <qmenubar.h>
#include <qpopupmenu.h>
#include <qtoolbar.h>
#include <qiconset.h>
#include <qapplication.h>

#include <qsqldriver.h>
#include <qmessagebox.h>
#include <qsqldatabase.h>
#include <qlineedit.h>
#include <qcombobox.h>
#include <qspinbox.h>
#include <qsqlerror.h>
#include <qinputdialog.h>

#include "connect.h"

static const char* const chart_data[] = {
"22 22 5 1",
"       c None",
"+      c #F4F11A",
"@      c #F21515",
"#      c #079913",
"$      c #1F3DD3",
"                      ",
"                      ",
"      ++++@@@@@       ",
"     +++++@@@@@@      ",
"   ##+++++@@@@@@@@    ",
"   ###++++@@@@@@@@    ",
"  ####++++@@@@@@@$$   ",
" ######+++@@@@@@$$$$  ",
" ######+++@@@@@$$$$$  ",
" #######++@@@@$$$$$$  ",
" #######++@@@$$$$$$$  ",
" ########+@@$$$$$$$$  ",
" #########$$$$$$$$$$  ",
" ##########$$$$$$$$$  ",
" ###########$$$$$$$$  ",
" ###########$$$$$$$$  ",
"  ###########$$$$$$   ",
"   ##########$$$$$    ",
"   ###########$$$$    ",
"     #########$$      ",
"      ########$       ",
"                      "
};

static const char* const address_data[] = {
"22 22 4 1",
"       c None",
".      c #724309",
"+      c #E8BF86",
"@      c #000000",
"                      ",
"              ...... ",
"             ..++++..",
"...............++++++.",
".++++++++++++++++++++.",
".++@++++++@@++@+@@@++.",
".++@@@@+@+@+++@++@@++.",
".++@@+@+@@@+@@@+@++++.",
".++@@+@++@+++@++@@@++.",
".++++++++++++++++++++.",
".++++++++++++++++++++.",
".++++@+++++++++++++++.",
".++++@+@+@@+@++@++@++.",
".+++@@@@@@+@@+@@@@@++.",
".++++++++++++++++++++.",
".++++@+++++++@+++++++.",
".+++@@@+++@++@+@@+@++.",
".++++@+@+@@@@++@+@+++.",
".++++++++++++++++++++.",
".++++++++++++++++++++.",
"......................",
"                      "
};

static const char* const query_data[] = {
"22 22 2 1",
"       c None",
"@      c #F21515",
"                      ",
"        @@@@@@        ",
"      @@@@@@@@@@      ",
"     @@        @@     ",
"     @@        @@     ",
"    @@          @@    ",
"   @@            @@   ",
"   @@            @@   ",
"   @@            @@   ",
"   @@            @@   ",
"   @@            @@   ",
"   @@            @@   ",
"   @@            @@   ",
"   @@     @@     @@   ",
"   @@      @@    @@   ",
"    @@      @@  @@    ",
"     @@      @@@@     ",
"     @@       @@@     ",
"      @@@@@@@@@@@     ",
"        @@@@@@  @@    ",
"                      ",
"                      "
};



class QCustomSqlCursor : public QSqlCursor
{
public:
    QCustomSqlCursor( const QString & query = QString::null, bool autopopulate = TRUE,
                      QSqlDatabase* db = 0 )
        : QSqlCursor( QString::null, autopopulate, db )
    {

        exec( query );
        if ( isSelect() && autopopulate ) {
            QSqlRecordInfo inf = db->recordInfo( *(QSqlQuery*)this );
            if ( !inf.isEmpty() ) {
                for ( QSqlRecordInfo::iterator it = inf.begin(); it != inf.end(); ++it ) {
                    append( *it );
                }
            }
        }
        setMode( QSqlCursor::ReadOnly );
    }
    QCustomSqlCursor( const QCustomSqlCursor & other ): QSqlCursor( other ) {}
    bool select( const QString & /*filter*/, const QSqlIndex & /*sort*/ = QSqlIndex() )
        { return exec( lastQuery() ); }
    QSqlIndex primaryIndex( bool /*prime*/ = TRUE ) const
        { return QSqlIndex(); }
    int insert( bool /*invalidate*/ = TRUE )
        { return FALSE; }
    int update( bool /*invalidate*/ = TRUE )
        { return FALSE; }
    int del( bool /*invalidate*/ = TRUE )
        { return FALSE; }
    void setName( const QString& /*name*/, bool /*autopopulate*/ = TRUE ) {}
};

static void showError( const QSqlError& err, QWidget* parent = 0 )
{
   QString errStr ( "The database reported an error\n" );
    if ( !err.databaseText().isEmpty() )
        errStr += err.databaseText();
    if ( !err.driverText().isEmpty() )
        errStr += err.driverText();
    QMessageBox::warning( parent, "Error", errStr );
}

// -------------------------------------------------------------------------


DataBase::DataBase( QWidget* parent,  const char* name, WFlags fl )
    : QMainWindow( parent, name, fl )
{
    (void)statusBar();
    if ( !name )
        setName( "DataBase" );
    resize( 660, 530 );
    setCaption( tr( "SQL-QSA-Example" ) );
    setCentralWidget( new QWidget( this, "qt_central_widget" ) );
    QHBoxLayout *DataBaseLayout = new QHBoxLayout( centralWidget(), 11, 6, "DataBaseLayout");

    QVBoxLayout *Layout1 = new QVBoxLayout( 0, 0, 6, "Layout1");

    QSplitter *vsplit = new QSplitter( centralWidget(), "vsplit" );
    vsplit->setOrientation( QSplitter::Vertical );

    QSplitter *hsplit = new QSplitter( vsplit, "hsplit" );
    hsplit->setOrientation( QSplitter::Horizontal );

    listView = new QListView( hsplit, "listView" );
    listView->addColumn( tr( "Tables" ) );
    listView->addColumn( tr( "Type" ) );
    listView->addColumn( tr( "Required" ) );
    listView->header()->setLabel( 0, tr( "Tables" ) );
    listView->header()->setLabel( 1, tr( "Type" ) );
    listView->header()->setLabel( 2, tr( "Required" ) );
    listView->setMargin( 0 );
    listView->setAllColumnsShowFocus( TRUE );
    listView->setRootIsDecorated( TRUE );
    listView->setResizeMode( QListView::LastColumn );

    sqlTable = new QDataTable( hsplit, "sqlTable" );

    QGroupBox *groupBox = new QGroupBox( vsplit, "groupBox" );
    groupBox->setTitle( tr( "SQL Query" ) );
    groupBox->setColumnLayout(0, Qt::Vertical );
    groupBox->layout()->setSpacing( 6 );
    groupBox->layout()->setMargin( 11 );
    QGridLayout *groupBoxLayout = new QGridLayout( groupBox->layout() );
    groupBoxLayout->setAlignment( Qt::AlignTop );

    QVBoxLayout *Layout2 = new QVBoxLayout( 0, 0, 6, "Layout2");

    queryTextEdit = new QTextEdit( groupBox, "queryTextEdit" );
    Layout2->addWidget( queryTextEdit );

    QHBoxLayout *Layout3 = new QHBoxLayout( 0, 0, 6, "Layout3");
    QSpacerItem* spacer = new QSpacerItem( 20, 20, QSizePolicy::Expanding,
                                           QSizePolicy::Minimum );
    Layout3->addItem( spacer );

    submitButton = new QPushButton( groupBox, "submitButton" );
    submitButton->setText( tr( "&Submit" ) );
    submitButton->setSizePolicy( QSizePolicy( (QSizePolicy::SizeType)5,
            (QSizePolicy::SizeType)0, 0, 0, submitButton->sizePolicy().hasHeightForWidth() ) );
    submitButton->setMinimumSize( QSize( 80, 0 ) );
    submitButton->setEnabled( FALSE );
    Layout3->addWidget( submitButton );

    QPushButton *clearButton = new QPushButton( groupBox, "clearButton" );
    clearButton->setText( tr( "C&lear" ) );
    clearButton->setSizePolicy( QSizePolicy( (QSizePolicy::SizeType)5,
            (QSizePolicy::SizeType)0, 0, 0, clearButton->sizePolicy().hasHeightForWidth() ) );
    clearButton->setMinimumSize( QSize( 80, 0 ) );
    Layout3->addWidget( clearButton );
    Layout2->addLayout( Layout3 );

    groupBoxLayout->addLayout( Layout2, 0, 0 );
    Layout1->addWidget( vsplit );

    QHBoxLayout *Layout4 = new QHBoxLayout( 0, 0, 6, "Layout4");

    label = new QLabel( centralWidget(), "label" );
    label->setText( tr( "Press \"Connect\" to open a database" ) );
    Layout4->addWidget( label );

    QPushButton *connectButton = new QPushButton( centralWidget(), "connectButton" );
    connectButton->setText( tr( "&Connect..." ) );
    connectButton->setSizePolicy( QSizePolicy( (QSizePolicy::SizeType)0,
            (QSizePolicy::SizeType)0, 0, 0, connectButton->sizePolicy().hasHeightForWidth() ) );
    connectButton->setMinimumSize( QSize( 80, 0 ) );
    Layout4->addWidget( connectButton );
    Layout1->addLayout( Layout4 );
    DataBaseLayout->addLayout( Layout1 );

    connect( clearButton, SIGNAL( clicked() ), queryTextEdit, SLOT( clear() ) );
    connect( connectButton, SIGNAL( clicked() ), this, SLOT( dbConnect() ) );
    connect( submitButton, SIGNAL( clicked() ), this, SLOT( execQuery() ) );
    connect( listView, SIGNAL( returnPressed(QListViewItem*) ),
             this, SLOT( showTable(QListViewItem*) ) );
    connect( listView, SIGNAL( doubleClicked(QListViewItem*) ),
             this, SLOT( showTable(QListViewItem*) ) );

    conDiag = new ConnectDialog( this, "Connection Dialog", TRUE );

    initQSA();

    setupActions();

    hsplit->setResizeMode( listView, QSplitter::KeepSize );
    vsplit->setResizeMode( groupBox, QSplitter::KeepSize );
}

DataBase::~DataBase()
{

}

void DataBase::setupActions()
{
    QToolBar *toolBar = new QToolBar( "", this, DockTop );
    toolBar->setLabel( tr( "Tools" ) );

    QAction *fileExitAction = new QAction( this, "fileExitAction" );
    fileExitAction->setText( tr( "Exit" ) );
    fileExitAction->setMenuText( tr( "E&xit" ) );
    connect( fileExitAction, SIGNAL( activated() ), this, SLOT( exit() ) );

    QAction *scriptOpenDevAction = new QAction( this, "scriptsOpenDevAction" );
    scriptOpenDevAction->setText( tr( "Open Qt Scripter..." ) );
    scriptOpenDevAction->setMenuText( tr( "Open Qt Scripter..." ) );
    connect( scriptOpenDevAction, SIGNAL( activated() ), this, SLOT( openIDE() ) );

    QAction *scriptRunAction = new QAction( this, "scriptsRunAction" );
    scriptRunAction->setText( tr( "Run Script..." ) );
    scriptRunAction->setMenuText( tr( "Run Script..." ) );

    QString s = "show age statistcs";
    QAction *a;
    a = new QAction( tr( s ), QPixmap( (const char**)chart_data ), tr( s ), 0, this, s.latin1() );
    scripts.insert( a, "AgeStatistics" );
    a->addTo( toolBar );
    connect( a, SIGNAL( activated() ), this, SLOT( runScript() ) );

    s = "show all addresses";
    a = new QAction( tr( s ), QPixmap( ( const char**)address_data ), tr( s ), 0, this, s.latin1() );
    scripts.insert( a, "Addresses" );
    a->addTo( toolBar );
    connect( a, SIGNAL( activated() ), this, SLOT( runScript() ) );

    s = "run a certain query";
    a = new QAction( tr( s ), QPixmap( ( const char**)query_data ), tr( s ), 0, this, s.latin1() );
    scripts.insert( a, "Query" );
    a->addTo( toolBar );
    connect( a, SIGNAL( activated() ), this, SLOT( runScript() ) );

    QMenuBar *menubar = new QMenuBar( this, "menubar" );

    QPopupMenu *fileMenu = new QPopupMenu( this );
    fileExitAction->addTo( fileMenu );

    QPopupMenu *scriptMenu = new QPopupMenu( this );
    scriptOpenDevAction->addTo( scriptMenu );
    scriptRunAction->addTo( scriptMenu );

    menubar->insertItem( tr( "&File" ), fileMenu );
    menubar->insertItem( tr( "&Scripts" ), scriptMenu );

    connect( fileExitAction, SIGNAL( activated() ), this, SLOT( exit() ) );
    connect( scriptOpenDevAction, SIGNAL( activated() ), this, SLOT( openIDE() ) );
    connect( scriptRunAction, SIGNAL( activated() ), this, SLOT( scriptChooser() ) );
}

void DataBase::initQSA()
{
    interpreter.addObject( sqlTable );
    interpreter.addObject( queryTextEdit );
    interpreter.addObject( submitButton );
    interpreter.addObject( label );
    interpreter.addObject( conDiag->editHostname );
    interpreter.addObject( conDiag->editDatabase );
    interpreter.addObject( conDiag->portSpinBox );
    interpreter.addObject( conDiag->editUsername );
    interpreter.addObject( conDiag->editPassword );
    interpreter.addObject( conDiag->comboDriver );
    interpreter.project()->open( "database.qsa" );
}

void DataBase::dbConnect()
{
    if ( conDiag->exec() != QDialog::Accepted )
        return;
    if ( sqlTable->sqlCursor() ) {
        sqlTable->setSqlCursor( 0 );
    }

    if ( QSqlDatabase::contains( "DemoDB" ) ) {
        QSqlDatabase* oldDb = QSqlDatabase::database( "DemoDB" );
        oldDb->close();
        QSqlDatabase::removeDatabase( "DemoDB" );
    }

    QSqlDatabase *db = QSqlDatabase::addDatabase( conDiag->comboDriver->currentText(), "DemoDB" );
    if ( !db ) {
        QMessageBox::warning( this, "Error", "Could not open database" );
        return;
    }
    db->setHostName( conDiag->editHostname->text() );
    db->setDatabaseName( conDiag->editDatabase->text() );
    db->setPort( conDiag->portSpinBox->value() );
    db->setUserName( conDiag->editUsername->text() );
    db->setPassword( conDiag->editPassword->text() );
    if ( !db->open() ) {
        showError( db->lastError(), this );
        return;
    }

    label->setText( "Double-Click on a table-name to view the contents" );
    listView->clear();

    QStringList tables = db->tables();
    for ( QStringList::Iterator it = tables.begin(); it != tables.end(); ++it ) {
        QListViewItem* listItem = new QListViewItem( listView, *it );
        QSqlRecordInfo ri = db->recordInfo ( *it );
        for ( QSqlRecordInfo::Iterator it = ri.begin(); it != ri.end(); ++it ) {
            QString req;
            if ( (*it).isRequired() > 0 ) {
                req = "Yes";
            } else if ( (*it).isRequired() == 0 ) {
                req = "No";
            } else {
                req = "?";
            }
            QListViewItem* fi = new QListViewItem( listItem, (*it).name(),
                                + QVariant::typeToName( (*it).type() ), req );
            listItem->insertItem( fi );
        }
        listView->insertItem( listItem );
    }
    submitButton->setEnabled( TRUE );
}

void DataBase::execQuery()
{
    QCustomSqlCursor *cursor = new QCustomSqlCursor( queryTextEdit->text(),
                               TRUE, QSqlDatabase::database( "DemoDB", TRUE ) );

    if ( cursor->isSelect() ) {
        sqlTable->setSqlCursor( cursor, TRUE, TRUE );
        sqlTable->refresh( QDataTable::RefreshAll );
        QString txt( "Query OK" );
        if ( cursor->size() >= 0 )
            txt += ", returned rows: " + QString::number( cursor->size() );
        label->setText( txt );
    } else {
        if ( !cursor->isActive() ) {
            showError( cursor->lastError(), this );
        } else {
            label->setText( QString("Query OK, affected rows: %1")
                            .arg( cursor->numRowsAffected() ) );
        }
    }
}

void DataBase::showTable( QListViewItem *item )
{
    QListViewItem *i = item->parent();
    if ( !i ) {
        i = item;
    }

    QSqlCursor *cursor = new QSqlCursor( i->text( 0 ), TRUE,
                         QSqlDatabase::database( "DemoDB", TRUE ) );
    sqlTable->setSqlCursor( cursor, TRUE, TRUE );
    sqlTable->setSort( cursor->primaryIndex() );
    sqlTable->refresh( QDataTable::RefreshAll );
    label->setText( "Displaying table " + i->text( 0 ) );
}

void DataBase::exit()
{
    qApp->quit();
}

void DataBase::openIDE()
{
    interpreter.project()->openIDE();
}

void DataBase::scriptChooser()
{
    bool ok = FALSE;
    QString func = QInputDialog::getItem( "Choose a function", "Choose a function",
                        interpreter.globalFunctions(), 0, FALSE, &ok );
    if ( !ok || func.isEmpty() )
        return;
    interpreter.call( func, QValueList<QVariant>() );
}

void DataBase::runScript()
{
    QString s = *scripts.find( (QAction*)sender() );
    interpreter.call( s, QValueList<QVariant>() );
}



Main:

#include <qapplication.h>
#include "database.h"

int main( int argc, char **argv )
{
    QApplication a( argc, argv );

    DataBase db;

    db.resize( 600, 450 );
    a.setMainWidget( &db );
    db.show();
    a.connect( &a, SIGNAL( lastWindowClosed() ), &a, SLOT( quit() ) );
    return a.exec();
}


SQL:

drop table people;

create table people (
id int primary key,
name char( 50 ),
city char( 30 ),
birthday date
);


-- This is just needed for LocalSql:

-- create index people_idx_1 on people( name );
-- create index people_idx_2 on people( city );
-- create index people_idx_3 on people( birthday );


insert into people values( 1, 'Alexander', 'Paris', '1971-11-03' );
insert into people values( 2, 'Johannes', 'Paris', '1968-03-06' );
insert into people values( 3, 'Martin', 'New York', '1923-09-22' );
insert into people values( 4, 'Matthias', 'Los Angeles', '1932-01-14' );
insert into people values( 5, 'Andreas', 'Santa Fe', '1958-12-03' );
insert into people values( 6, 'Hugo', 'Oslo', '1961-06-19' );
insert into people values( 7, 'Theodor', 'Nice', '1972-07-23' );
insert into people values( 8, 'Egon', 'Berlin', '1975-08-01' );
insert into people values( 9, 'Helmut', 'London', '1980-03-03' );
insert into people values( 10, 'Franz', 'Vienna', '1940-11-07' );
insert into people values( 11, 'Markus', 'Berne', '1964-12-06' );
insert into people values( 12, 'Tim', 'Madrid', '1979-08-27' );
insert into people values( 13, 'Josef', 'Munich', '1963-04-17' );

See also QSA Examples.


Copyright © 2001-2003 TrolltechTrademarks
QSA version 1.0.0-beta2