#include <QApplication>
#include <QMainWindow>
#include <QMenuBar>
#include <QLabel>
#include <QPushButton>
#include <QCheckBox>
#include <QFile>
#include <QTimer>
#include <QicsTable.h>
#include <QicsDataModelDefault.h>
#include <QicsComboCellDisplay.h>
#include <QPixmap>
#include <QTextStream>
#include <QMenu>
#include <stdlib.h>
#include <math.h>
#include "high.xpm"
#include "low.xpm"
static const char fname[] = "initial-values.txt";
static int stock_name = 0;
static int day_zero = 1;
static int day_one = 4;
static int day_two = 7;
static int day_three = 10;
static int cur_idx = 0;
static int high_idx = 1;
static int low_idx = 2;
static int nyse_idx = 0;
static int nasdaq_idx = 1;
static int update_period = 2000;
class StockData : public QObject
{
Q_OBJECT
public:
StockData();
~StockData();
QicsDataModel *dataModel(void) const { return myDM; }
public slots:
void updateStocks(void);
signals:
void stockChanged(int row, bool gained);
protected slots:
void dataValuesChanged(QicsRegion reg);
protected:
void loadDT(void);
void addDay(int start_col);
void updateStock(int row);
float getFloatValue(int row, int col);
float newprice(float old_price, float percent_change = (float) 0.02, int updown = 1);
int roundf(float f);
float randf(void);
int posneg(void);
QicsDataModel *myDM;
};
StockData::StockData() :
QObject()
{
myDM = new QicsDataModelDefault();
connect(myDM, SIGNAL(modelChanged(QicsRegion)),
this, SLOT(dataValuesChanged(QicsRegion)));
loadDT();
}
StockData::~StockData()
{
delete myDM;
}
void
StockData::updateStocks(void)
{
for (int i = 0; i <= myDM->lastRow(); ++i)
updateStock(i);
}
void
StockData::loadDT(void)
{
QFile* f;
if ( QFile::exists(fname) )
f = new QFile(fname);
else f = new QFile(QString("..\\")+fname);
f->open(QIODevice::ReadOnly);
QTextStream stream(f);
myDM->readASCII(stream);
f->close();
#ifdef ADD_DATA
myDM->addColumns(6);
#if defined(Q_WS_WIN32)
srand(2332);
#else
srandom(2332);
#endif
addDay(day_two);
addDay(day_three);
QFile out("out.txt");
out.open(QIODevice::WriteOnly);
QTextStream outstream(&out);
myDM->writeASCII(outstream, ';');
out.close();
#endif
}
void
StockData::addDay(int start_col)
{
for (int i = 0; i < myDM->numRows(); ++i)
{
float old_price = getFloatValue(i, day_one+cur_idx);
float new_price = newprice(old_price, (float) 0.05, posneg());
QicsDataFloat fdata(new_price);
QicsDataFloat high(newprice(new_price, (float) 0.05, 1));
QicsDataFloat low(newprice(new_price, (float) 0.05, -1));
myDM->setItem(i, start_col+high_idx, high);
myDM->setItem(i, start_col+low_idx, low);
myDM->setItem(i, start_col+cur_idx, fdata);
}
}
float
StockData::getFloatValue(int row, int col)
{
const QicsDataItem *itm = myDM->item(row, col);
if (itm)
{
if (itm->type() == QicsDataItem_Float)
return ((QicsDataFloat *) itm)->data();
else if (itm->type() == QicsDataItem_Double)
return ((QicsDataDouble *) itm)->data();
else if (itm->type() == QicsDataItem_Int)
return ((QicsDataInt *) itm)->data();
}
return -1;
}
int
StockData::roundf(float f)
{
int base = (int) floor(f);
if ((f - base) < 0.5)
return base;
else
return base + 1;
}
float
StockData::randf(void)
{
#if defined(Q_WS_WIN32)
return (float) rand() / (float) RAND_MAX;
#else
return (float) random() / (float) RAND_MAX;
#endif
}
int
StockData::posneg(void)
{
if (roundf(randf()) < 0.5)
return -1;
else
return 1;
}
float
StockData::newprice(float old_price, float percent_change, int updown)
{
float new_price = old_price + (randf() * percent_change * old_price * updown);
new_price = ((float) (roundf(new_price * 100))) / 100;
return new_price;
}
void
StockData::updateStock(int row)
{
float cur_price;
cur_price = getFloatValue(row, day_zero+cur_idx);
if (cur_price < 0)
cur_price = getFloatValue(row, day_one+cur_idx);
cur_price = newprice(cur_price, (float) 0.02, posneg());
QicsDataFloat fdata(cur_price);
myDM->setItem(row, day_zero+cur_idx, fdata);
float high = getFloatValue(row, day_zero+high_idx);
if ((high < 0) || (cur_price > high))
myDM->setItem(row, day_zero+high_idx, fdata);
float low = getFloatValue(row, day_zero+low_idx);
if ((low < 0) || (cur_price < low))
myDM->setItem(row, day_zero+low_idx, fdata);
}
void
StockData::dataValuesChanged(QicsRegion reg)
{
int row = reg.startCell().row();
int col = reg.startCell().column();
if (col == day_zero+cur_idx)
{
float yesterday = getFloatValue(row, day_one+cur_idx);
float today = getFloatValue(row, day_zero+cur_idx);
emit stockChanged(row, (today > yesterday));
}
}
class StockUI: public QObject
{
Q_OBJECT
public:
StockUI(QicsDataModel *dt);
~StockUI();
inline QWidget *base(void) const { return myMainWindow; }
inline QicsTable *table(void) const { return myTable; }
signals:
void updateToggled(bool on);
protected slots:
void decorateStock(int idx, bool gainer);
protected:
void setupRowHeader(void);
void setupColumnHeader(void);
void setupGrid(void);
void setupDay(int day_idx, QString day);
QMainWindow *myMainWindow;
QicsTable *myTable;
};
StockUI::StockUI(QicsDataModel *dt) :
QObject()
{
myMainWindow = new QMainWindow();
myTable = new QicsTable(dt, myMainWindow);
myMainWindow->setCentralWidget(myTable);
QMenuBar *menu = myMainWindow->menuBar();
QMenu *file = new QMenu("&File",myMainWindow);
QAction *exit = file->addAction("E&xit");
connect(exit,SIGNAL(triggered()), qApp, SLOT(quit()));
menu->addMenu(file);
QLabel *label = new QLabel(QString("Stock Activity"), myTable);
QFont fnt = label->font();
fnt.setPointSize(fnt.pointSize() + 6);
label->setFont(fnt);
label->setAlignment(Qt::AlignCenter);
myTable->setTopTitleWidget(label);
setupRowHeader();
setupColumnHeader();
setupGrid();
QCheckBox *cb = new QCheckBox(QString("Auto\nUpdates"), myTable);
myTable->setTopLeftCornerWidget(cb);
connect(cb, SIGNAL(toggled(bool)),
this, SIGNAL(updateToggled(bool)));
myMainWindow->show();
}
StockUI::~StockUI()
{
delete myTable;
}
void
StockUI::setupRowHeader(void)
{
}
void
StockUI::setupColumnHeader(void)
{
QicsColumnHeader *ch = myTable->columnHeader();
ch->setNumRows(3);
ch->setAlignment(Qt::AlignCenter);
ch->addCellSpan(QicsSpan(0,stock_name, 3, 1));
ch->cellRef(0,stock_name).setLabel(QString("Stock"));
setupDay(day_zero, QString("Today"));
setupDay(day_one, QString("One Day Ago"));
setupDay(day_two, QString("Two Days Ago"));
setupDay(day_three, QString("Three Days Ago"));
delete ch;
}
void
StockUI::setupDay(int day_idx, QString day)
{
QicsColumnHeader *ch = myTable->columnHeader();
ch->addCellSpan(QicsSpan(0,day_idx, 1, 3));
ch->cellRef(0,day_idx).setLabel(QString(day));
ch->cellRef(0,day_idx).setForegroundColor(Qt::white);
ch->cellRef(0,day_idx).setBackgroundColor(Qt::blue);
ch->addCellSpan(QicsSpan(1,day_idx, 1, 2));
ch->cellRef(1,day_idx).setLabel(QString("Show Gains in"));
ch->dataModel()->setItem(1,day_idx+2, QicsDataString("Green"));
QicsComboCellDisplay *cd = new QicsComboCellDisplay();
cd->addItem(QString("Green"));
cd->addItem(QString("Red"));
ch->cellRef(1,day_idx+2).setDisplayer(cd);
QPalette pal = ch->palette();
pal.setColor(QPalette::Base,"white");
ch->cellRef(1,day_idx+2).setPalette(pal);
ch->cellRef(2,day_idx+cur_idx).setLabel(QString("Current"));
ch->cellRef(2,day_idx+high_idx).setLabel(QString("High"));
ch->cellRef(2,day_idx+high_idx).setPixmap(QPixmap(high_xpm));
ch->cellRef(2,day_idx+low_idx).setLabel(QString("Low"));
ch->cellRef(2,day_idx+low_idx).setPixmap(QPixmap(low_xpm));
delete ch;
}
void
StockUI::setupGrid(void)
{
QicsDataItemSprintfFormatter *v = new QicsDataItemSprintfFormatter();
myTable->columnRef(0).setWidthInChars(7);
v->addFormatString(QicsDataItem_Float, "$%.2f");
v->addFormatString(QicsDataItem_Int, "$%d.00");
myTable->mainGridRef().setFormatter(v);
for (int i = day_zero; i <= (day_three + 2); ++i)
{
myTable->columnRef(i).setAlignment(Qt::AlignRight);
}
QFont fnt = myTable->font();
fnt.setPointSize(fnt.pointSize() + 2);
fnt.setBold(true);
myTable->rowRef(nyse_idx).setFont(fnt);
myTable->rowRef(nasdaq_idx).setFont(fnt);
v = new QicsDataItemSprintfFormatter();
v->addFormatString(QicsDataItem_Float, "%.2f");
v->addFormatString(QicsDataItem_Int, "%d.00");
myTable->rowRef(nyse_idx).setFormatter(v);
myTable->rowRef(nasdaq_idx).setFormatter(v);
myTable->freezeTopRows(2);
}
void
StockUI::decorateStock(int idx, bool gainer)
{
const QicsDataItem *itm =
myTable->columnHeaderRef().cellRef(1, day_zero+2).dataValue();
QString gain_color = itm->string();
QColor c;
if (gainer)
c = QColor(gain_color);
else
c = myTable->mainGridRef().foregroundColor();
myTable->cellRef(idx, day_zero+cur_idx).setForegroundColor(c);
}
class StockApp : public QApplication
{
Q_OBJECT
public:
StockApp(int &argc, char **argv);
~StockApp();
protected slots:
void toggleUpdates(bool on);
void updateStocks(void);
protected:
StockData *mySD;
StockUI *myUI;
QTimer *myTimer;
static int SortFloats(const QicsDataItem *a, const QicsDataItem *b);
};
StockApp::StockApp(int &argc, char **argv) :
QApplication(argc, argv)
{
mySD = new StockData();
myUI = new StockUI(mySD->dataModel());
connect(myUI, SIGNAL(updateToggled(bool)),
this, SLOT(toggleUpdates(bool)));
myTimer = new QTimer();
connect(myTimer, SIGNAL(timeout()),
this, SLOT(updateStocks()));
connect(mySD, SIGNAL(stockChanged(int, bool)),
myUI, SLOT(decorateStock(int, bool)));
}
StockApp::~StockApp()
{
delete myUI;
delete mySD;
delete myTimer;
}
void
StockApp::toggleUpdates(bool on)
{
if (on)
myTimer->start(update_period);
else
myTimer->stop();
}
void
StockApp::updateStocks(void)
{
mySD->updateStocks();
myUI->table()->sortRows(day_zero+cur_idx, Qics::Descending, 2, -1, &SortFloats);
}
int
StockApp::SortFloats(const QicsDataItem *a, const QicsDataItem *b)
{
if ((a->type() == QicsDataItem_Float) &&
(b->type() == QicsDataItem_Float))
{
float fa = ((const QicsDataFloat *) a)->data();
float fb = ((const QicsDataFloat *) b)->data();
if (fa < fb)
return -1;
else if (fa == fb)
return 0;
else
return 1;
}
else
{
qDebug("not floats");
QString sa = a->string();
QString sb = b->string();
return sa.compare(sb);
}
}
int main(int argc, char *argv[])
{
StockApp app(argc,argv);
return app.exec();
}
#include "stocks.moc"