Export functionality for transaction list
This commit is contained in:
parent
d52a0f3bca
commit
fbaee7a853
13 changed files with 230 additions and 13 deletions
|
@ -77,7 +77,8 @@ HEADERS += src/qt/bitcoingui.h \
|
|||
src/qt/transactionview.h \
|
||||
src/qt/walletmodel.h \
|
||||
src/bitcoinrpc.h \
|
||||
src/qt/overviewpage.h
|
||||
src/qt/overviewpage.h \
|
||||
src/qt/csvmodelwriter.h
|
||||
SOURCES += src/qt/bitcoin.cpp src/qt/bitcoingui.cpp \
|
||||
src/qt/transactiontablemodel.cpp \
|
||||
src/qt/addresstablemodel.cpp \
|
||||
|
@ -114,7 +115,8 @@ SOURCES += src/qt/bitcoin.cpp src/qt/bitcoingui.cpp \
|
|||
src/qt/transactionview.cpp \
|
||||
src/qt/walletmodel.cpp \
|
||||
src/bitcoinrpc.cpp \
|
||||
src/qt/overviewpage.cpp
|
||||
src/qt/overviewpage.cpp \
|
||||
src/qt/csvmodelwriter.cpp
|
||||
|
||||
RESOURCES += \
|
||||
src/qt/bitcoin.qrc
|
||||
|
|
|
@ -34,7 +34,8 @@ Designer: http://www.everaldo.com
|
|||
Icon Pack: Crystal SVG
|
||||
License: LGPL
|
||||
|
||||
Icon: src/qt/res/icons/receive.png, src/qt/res/icons/history.png
|
||||
Icon: src/qt/res/icons/receive.png, src/qt/res/icons/history.png,
|
||||
src/qt/res/icons/export.png
|
||||
Designer: Oxygen team
|
||||
Icon Pack: Oxygen
|
||||
License: Creative Common Attribution-ShareAlike 3.0 License or LGPL
|
||||
|
|
|
@ -28,6 +28,7 @@
|
|||
<file alias="editdelete">res/icons/editdelete.png</file>
|
||||
<file alias="history">res/icons/history.png</file>
|
||||
<file alias="overview">res/icons/overview.png</file>
|
||||
<file alias="export">res/icons/export.png</file>
|
||||
</qresource>
|
||||
<qresource prefix="/images">
|
||||
<file alias="about">res/images/about.png</file>
|
||||
|
|
|
@ -75,8 +75,12 @@ BitcoinGUI::BitcoinGUI(QWidget *parent):
|
|||
toolbar->addAction(receiveCoins);
|
||||
toolbar->addAction(addressbook);
|
||||
|
||||
overviewPage = new OverviewPage();
|
||||
QToolBar *toolbar2 = addToolBar("Transactions toolbar");
|
||||
toolbar2->setToolButtonStyle(Qt::ToolButtonTextBesideIcon);
|
||||
toolbar2->addAction(exportAction);
|
||||
|
||||
// Overview page
|
||||
overviewPage = new OverviewPage();
|
||||
QVBoxLayout *vbox = new QVBoxLayout();
|
||||
|
||||
transactionView = new TransactionView(this);
|
||||
|
@ -146,8 +150,10 @@ void BitcoinGUI::createActions()
|
|||
receiveCoins->setToolTip(tr("Show the list of addresses for receiving payments"));
|
||||
options = new QAction(QIcon(":/icons/options"), tr("&Options..."), this);
|
||||
options->setToolTip(tr("Modify configuration options for bitcoin"));
|
||||
openBitcoin = new QAction(QIcon(":/icons/bitcoin"), "Open &Bitcoin", this);
|
||||
openBitcoin = new QAction(QIcon(":/icons/bitcoin"), tr("Open &Bitcoin"), this);
|
||||
openBitcoin->setToolTip(tr("Show the Bitcoin window"));
|
||||
exportAction = new QAction(QIcon(":/icons/export"), tr("&Export..."), this);
|
||||
exportAction->setToolTip(tr("Export data in current view to a file"));
|
||||
|
||||
connect(quit, SIGNAL(triggered()), qApp, SLOT(quit()));
|
||||
connect(sendCoins, SIGNAL(triggered()), this, SLOT(sendCoinsClicked()));
|
||||
|
@ -156,6 +162,7 @@ void BitcoinGUI::createActions()
|
|||
connect(options, SIGNAL(triggered()), this, SLOT(optionsClicked()));
|
||||
connect(about, SIGNAL(triggered()), this, SLOT(aboutClicked()));
|
||||
connect(openBitcoin, SIGNAL(triggered()), this, SLOT(show()));
|
||||
connect(exportAction, SIGNAL(triggered()), this, SLOT(exportClicked()));
|
||||
}
|
||||
|
||||
void BitcoinGUI::setClientModel(ClientModel *clientModel)
|
||||
|
@ -410,10 +417,20 @@ void BitcoinGUI::gotoOverviewTab()
|
|||
{
|
||||
overviewAction->setChecked(true);
|
||||
centralWidget->setCurrentWidget(overviewPage);
|
||||
exportAction->setEnabled(false);
|
||||
}
|
||||
|
||||
void BitcoinGUI::gotoHistoryTab()
|
||||
{
|
||||
historyAction->setChecked(true);
|
||||
centralWidget->setCurrentWidget(transactionsPage);
|
||||
exportAction->setEnabled(true);
|
||||
}
|
||||
|
||||
void BitcoinGUI::exportClicked()
|
||||
{
|
||||
// Redirect to the right view, as soon as export for other views
|
||||
// (such as address book) is implemented.
|
||||
transactionView->exportClicked();
|
||||
}
|
||||
|
||||
|
|
|
@ -63,6 +63,7 @@ private:
|
|||
QAction *receiveCoins;
|
||||
QAction *options;
|
||||
QAction *openBitcoin;
|
||||
QAction *exportAction;
|
||||
|
||||
QSystemTrayIcon *trayIcon;
|
||||
TransactionView *transactionView;
|
||||
|
@ -92,6 +93,7 @@ private slots:
|
|||
void trayIconActivated(QSystemTrayIcon::ActivationReason reason);
|
||||
void transactionDetails(const QModelIndex& idx);
|
||||
void incomingTransaction(const QModelIndex & parent, int start, int end);
|
||||
void exportClicked();
|
||||
|
||||
void gotoOverviewTab();
|
||||
void gotoHistoryTab();
|
||||
|
|
83
src/qt/csvmodelwriter.cpp
Normal file
83
src/qt/csvmodelwriter.cpp
Normal file
|
@ -0,0 +1,83 @@
|
|||
#include "csvmodelwriter.h"
|
||||
|
||||
#include <QAbstractItemModel>
|
||||
#include <QFile>
|
||||
#include <QTextStream>
|
||||
|
||||
CSVModelWriter::CSVModelWriter(const QString &filename, QObject *parent) :
|
||||
QObject(parent),
|
||||
filename(filename)
|
||||
{
|
||||
}
|
||||
|
||||
void CSVModelWriter::setModel(const QAbstractItemModel *model)
|
||||
{
|
||||
this->model = model;
|
||||
}
|
||||
|
||||
void CSVModelWriter::addColumn(const QString &title, int column, int role)
|
||||
{
|
||||
Column col;
|
||||
col.title = title;
|
||||
col.column = column;
|
||||
col.role = role;
|
||||
|
||||
columns.append(col);
|
||||
}
|
||||
|
||||
static void writeValue(QTextStream &f, const QString &value)
|
||||
{
|
||||
// TODO: quoting if " or \n in string
|
||||
f << "\"" << value << "\"";
|
||||
}
|
||||
|
||||
static void writeSep(QTextStream &f)
|
||||
{
|
||||
f << ",";
|
||||
}
|
||||
|
||||
static void writeNewline(QTextStream &f)
|
||||
{
|
||||
f << "\n";
|
||||
}
|
||||
|
||||
bool CSVModelWriter::write()
|
||||
{
|
||||
QFile file(filename);
|
||||
if(!file.open(QIODevice::WriteOnly | QIODevice::Text))
|
||||
return false;
|
||||
QTextStream out(&file);
|
||||
|
||||
int numRows = model->rowCount();
|
||||
|
||||
// Header row
|
||||
for(int i=0; i<columns.size(); ++i)
|
||||
{
|
||||
if(i!=0)
|
||||
{
|
||||
writeSep(out);
|
||||
}
|
||||
writeValue(out, columns[i].title);
|
||||
}
|
||||
writeNewline(out);
|
||||
|
||||
// Data rows
|
||||
for(int j=0; j<numRows; ++j)
|
||||
{
|
||||
for(int i=0; i<columns.size(); ++i)
|
||||
{
|
||||
if(i!=0)
|
||||
{
|
||||
writeSep(out);
|
||||
}
|
||||
QVariant data = model->index(j, columns[i].column).data(columns[i].role);
|
||||
writeValue(out, data.toString());
|
||||
}
|
||||
writeNewline(out);
|
||||
}
|
||||
|
||||
file.close();
|
||||
|
||||
return file.error() == QFile::NoError;
|
||||
}
|
||||
|
43
src/qt/csvmodelwriter.h
Normal file
43
src/qt/csvmodelwriter.h
Normal file
|
@ -0,0 +1,43 @@
|
|||
#ifndef CSVMODELWRITER_H
|
||||
#define CSVMODELWRITER_H
|
||||
|
||||
#include <QObject>
|
||||
#include <QList>
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
class QAbstractItemModel;
|
||||
QT_END_NAMESPACE
|
||||
|
||||
// Export TableModel to CSV file
|
||||
class CSVModelWriter : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit CSVModelWriter(const QString &filename, QObject *parent = 0);
|
||||
|
||||
void setModel(const QAbstractItemModel *model);
|
||||
void addColumn(const QString &title, int column, int role=Qt::EditRole);
|
||||
|
||||
// Perform write operation
|
||||
// Returns true on success, false otherwise
|
||||
bool write();
|
||||
|
||||
private:
|
||||
QString filename;
|
||||
const QAbstractItemModel *model;
|
||||
|
||||
struct Column
|
||||
{
|
||||
QString title;
|
||||
int column;
|
||||
int role;
|
||||
};
|
||||
QList<Column> columns;
|
||||
|
||||
signals:
|
||||
|
||||
public slots:
|
||||
|
||||
};
|
||||
|
||||
#endif // CSVMODELWRITER_H
|
|
@ -254,3 +254,9 @@ bool TransactionRecord::statusUpdateNeeded()
|
|||
{
|
||||
return status.cur_num_blocks != nBestHeight;
|
||||
}
|
||||
|
||||
std::string TransactionRecord::getTxID()
|
||||
{
|
||||
return hash.ToString() + strprintf("-%03d", idx);
|
||||
}
|
||||
|
||||
|
|
|
@ -103,6 +103,9 @@ public:
|
|||
/* Status: can change with block chain update */
|
||||
TransactionStatus status;
|
||||
|
||||
/* Return the unique identifier for this transaction (part) */
|
||||
std::string getTxID();
|
||||
|
||||
/* Update status from wallet tx.
|
||||
*/
|
||||
void updateStatus(const CWalletTx &wtx);
|
||||
|
|
|
@ -394,13 +394,16 @@ QVariant TransactionTableModel::formatTxToAddress(const TransactionRecord *wtx)
|
|||
return QVariant(description);
|
||||
}
|
||||
|
||||
QVariant TransactionTableModel::formatTxAmount(const TransactionRecord *wtx) const
|
||||
QVariant TransactionTableModel::formatTxAmount(const TransactionRecord *wtx, bool showUnconfirmed) const
|
||||
{
|
||||
QString str = QString::fromStdString(FormatMoney(wtx->credit + wtx->debit));
|
||||
if(showUnconfirmed)
|
||||
{
|
||||
if(!wtx->status.confirmed || wtx->status.maturity != TransactionStatus::Mature)
|
||||
{
|
||||
str = QString("[") + str + QString("]");
|
||||
}
|
||||
}
|
||||
return QVariant(str);
|
||||
}
|
||||
|
||||
|
@ -541,6 +544,18 @@ QVariant TransactionTableModel::data(const QModelIndex &index, int role) const
|
|||
{
|
||||
return llabs(rec->credit + rec->debit);
|
||||
}
|
||||
else if (role == TxIDRole)
|
||||
{
|
||||
return QString::fromStdString(rec->getTxID());
|
||||
}
|
||||
else if (role == ConfirmedRole)
|
||||
{
|
||||
return rec->status.status == TransactionStatus::HaveConfirmations;
|
||||
}
|
||||
else if (role == FormattedAmountRole)
|
||||
{
|
||||
return formatTxAmount(rec, false);
|
||||
}
|
||||
return QVariant();
|
||||
}
|
||||
|
||||
|
|
|
@ -25,6 +25,7 @@ public:
|
|||
} ColumnIndex;
|
||||
|
||||
// Roles to get specific information from a transaction row
|
||||
// These are independent of column
|
||||
enum {
|
||||
// Type of transaction
|
||||
TypeRole = Qt::UserRole,
|
||||
|
@ -36,8 +37,14 @@ public:
|
|||
AddressRole,
|
||||
// Label of address related to transaction
|
||||
LabelRole,
|
||||
// Absolute net amount of transaction
|
||||
AbsoluteAmountRole
|
||||
// Absolute net amount of transaction, for filtering
|
||||
AbsoluteAmountRole,
|
||||
// Unique identifier
|
||||
TxIDRole,
|
||||
// Is transaction confirmed?
|
||||
ConfirmedRole,
|
||||
// Formatted amount, without brackets when unconfirmed
|
||||
FormattedAmountRole
|
||||
} RoleIndex;
|
||||
|
||||
int rowCount(const QModelIndex &parent) const;
|
||||
|
@ -57,7 +64,7 @@ private:
|
|||
QVariant formatTxDate(const TransactionRecord *wtx) const;
|
||||
QVariant formatTxType(const TransactionRecord *wtx) const;
|
||||
QVariant formatTxToAddress(const TransactionRecord *wtx) const;
|
||||
QVariant formatTxAmount(const TransactionRecord *wtx) const;
|
||||
QVariant formatTxAmount(const TransactionRecord *wtx, bool showUnconfirmed=true) const;
|
||||
QVariant formatTxDecoration(const TransactionRecord *wtx) const;
|
||||
|
||||
private slots:
|
||||
|
|
|
@ -1,11 +1,10 @@
|
|||
#include "transactionview.h"
|
||||
|
||||
// Temp includes for filtering prototype
|
||||
// Move to TransactionFilterRow class
|
||||
#include "transactionfilterproxy.h"
|
||||
#include "transactionrecord.h"
|
||||
#include "transactiontablemodel.h"
|
||||
#include "guiutil.h"
|
||||
#include "csvmodelwriter.h"
|
||||
|
||||
#include <QScrollBar>
|
||||
#include <QComboBox>
|
||||
|
@ -15,6 +14,9 @@
|
|||
#include <QLineEdit>
|
||||
#include <QTableView>
|
||||
#include <QHeaderView>
|
||||
#include <QPushButton>
|
||||
#include <QFileDialog>
|
||||
#include <QMessageBox>
|
||||
|
||||
#include <QDebug>
|
||||
|
||||
|
@ -76,6 +78,7 @@ TransactionView::TransactionView(QWidget *parent) :
|
|||
QVBoxLayout *vlayout = new QVBoxLayout(this);
|
||||
vlayout->setContentsMargins(0,0,0,0);
|
||||
vlayout->setSpacing(0);
|
||||
//vlayout->addLayout(hlayout2);
|
||||
|
||||
QTableView *view = new QTableView(this);
|
||||
vlayout->addLayout(hlayout);
|
||||
|
@ -196,3 +199,36 @@ void TransactionView::changedAmount(const QString &amount)
|
|||
transactionProxyModel->setMinAmount(0);
|
||||
}
|
||||
}
|
||||
|
||||
void TransactionView::exportClicked()
|
||||
{
|
||||
// CSV is currently the only supported format
|
||||
QString filename = QFileDialog::getSaveFileName(
|
||||
this,
|
||||
tr("Export Transaction Data"),
|
||||
QDir::currentPath(),
|
||||
tr("Comma separated file (*.csv)"));
|
||||
if(!filename.endsWith(".csv"))
|
||||
{
|
||||
filename += ".csv";
|
||||
}
|
||||
|
||||
CSVModelWriter writer(filename);
|
||||
|
||||
// name, column, role
|
||||
writer.setModel(transactionProxyModel);
|
||||
writer.addColumn("Confirmed", 0, TransactionTableModel::ConfirmedRole);
|
||||
writer.addColumn("Date", 0, TransactionTableModel::DateRole);
|
||||
writer.addColumn("Type", TransactionTableModel::Type, Qt::EditRole);
|
||||
writer.addColumn("Label", 0, TransactionTableModel::LabelRole);
|
||||
writer.addColumn("Address", 0, TransactionTableModel::AddressRole);
|
||||
writer.addColumn("Amount", 0, TransactionTableModel::FormattedAmountRole);
|
||||
writer.addColumn("ID", 0, TransactionTableModel::TxIDRole);
|
||||
|
||||
if(!writer.write())
|
||||
{
|
||||
QMessageBox::critical(this, tr("Error exporting"), tr("Could not write to file %1.").arg(filename),
|
||||
QMessageBox::Abort, QMessageBox::Abort);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -50,6 +50,7 @@ public slots:
|
|||
void chooseType(int idx);
|
||||
void changedPrefix(const QString &prefix);
|
||||
void changedAmount(const QString &amount);
|
||||
void exportClicked();
|
||||
|
||||
};
|
||||
|
||||
|
|
Loading…
Reference in a new issue