Skip to content

Commit 7bb681d

Browse files
committed
Merge pull request #5168
023e63d qt: Move transaction notification to transaction table model (Wladimir J. van der Laan)
2 parents 8d2396c + 023e63d commit 7bb681d

File tree

5 files changed

+159
-110
lines changed

5 files changed

+159
-110
lines changed

src/qt/transactiontablemodel.cpp

Lines changed: 141 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -91,87 +91,80 @@ class TransactionTablePriv
9191
9292
Call with transaction that was added, removed or changed.
9393
*/
94-
void updateWallet(const uint256 &hash, int status)
94+
void updateWallet(const uint256 &hash, int status, bool showTransaction)
9595
{
9696
qDebug() << "TransactionTablePriv::updateWallet : " + QString::fromStdString(hash.ToString()) + " " + QString::number(status);
97-
{
98-
LOCK2(cs_main, wallet->cs_wallet);
9997

100-
// Find transaction in wallet
101-
std::map<uint256, CWalletTx>::iterator mi = wallet->mapWallet.find(hash);
102-
bool inWallet = mi != wallet->mapWallet.end();
98+
// Find bounds of this transaction in model
99+
QList<TransactionRecord>::iterator lower = qLowerBound(
100+
cachedWallet.begin(), cachedWallet.end(), hash, TxLessThan());
101+
QList<TransactionRecord>::iterator upper = qUpperBound(
102+
cachedWallet.begin(), cachedWallet.end(), hash, TxLessThan());
103+
int lowerIndex = (lower - cachedWallet.begin());
104+
int upperIndex = (upper - cachedWallet.begin());
105+
bool inModel = (lower != upper);
103106

104-
// Find bounds of this transaction in model
105-
QList<TransactionRecord>::iterator lower = qLowerBound(
106-
cachedWallet.begin(), cachedWallet.end(), hash, TxLessThan());
107-
QList<TransactionRecord>::iterator upper = qUpperBound(
108-
cachedWallet.begin(), cachedWallet.end(), hash, TxLessThan());
109-
int lowerIndex = (lower - cachedWallet.begin());
110-
int upperIndex = (upper - cachedWallet.begin());
111-
bool inModel = (lower != upper);
107+
if(status == CT_UPDATED)
108+
{
109+
if(showTransaction && !inModel)
110+
status = CT_NEW; /* Not in model, but want to show, treat as new */
111+
if(!showTransaction && inModel)
112+
status = CT_DELETED; /* In model, but want to hide, treat as deleted */
113+
}
112114

113-
// Determine whether to show transaction or not
114-
bool showTransaction = (inWallet && TransactionRecord::showTransaction(mi->second));
115+
qDebug() << " inModel=" + QString::number(inModel) +
116+
" Index=" + QString::number(lowerIndex) + "-" + QString::number(upperIndex) +
117+
" showTransaction=" + QString::number(showTransaction) + " derivedStatus=" + QString::number(status);
115118

116-
if(status == CT_UPDATED)
119+
switch(status)
120+
{
121+
case CT_NEW:
122+
if(inModel)
117123
{
118-
if(showTransaction && !inModel)
119-
status = CT_NEW; /* Not in model, but want to show, treat as new */
120-
if(!showTransaction && inModel)
121-
status = CT_DELETED; /* In model, but want to hide, treat as deleted */
124+
qWarning() << "TransactionTablePriv::updateWallet : Warning: Got CT_NEW, but transaction is already in model";
125+
break;
122126
}
123-
124-
qDebug() << " inWallet=" + QString::number(inWallet) + " inModel=" + QString::number(inModel) +
125-
" Index=" + QString::number(lowerIndex) + "-" + QString::number(upperIndex) +
126-
" showTransaction=" + QString::number(showTransaction) + " derivedStatus=" + QString::number(status);
127-
128-
switch(status)
127+
if(showTransaction)
129128
{
130-
case CT_NEW:
131-
if(inModel)
132-
{
133-
qWarning() << "TransactionTablePriv::updateWallet : Warning: Got CT_NEW, but transaction is already in model";
134-
break;
135-
}
136-
if(!inWallet)
129+
LOCK2(cs_main, wallet->cs_wallet);
130+
// Find transaction in wallet
131+
std::map<uint256, CWalletTx>::iterator mi = wallet->mapWallet.find(hash);
132+
if(mi == wallet->mapWallet.end())
137133
{
138134
qWarning() << "TransactionTablePriv::updateWallet : Warning: Got CT_NEW, but transaction is not in wallet";
139135
break;
140136
}
141-
if(showTransaction)
137+
// Added -- insert at the right position
138+
QList<TransactionRecord> toInsert =
139+
TransactionRecord::decomposeTransaction(wallet, mi->second);
140+
if(!toInsert.isEmpty()) /* only if something to insert */
142141
{
143-
// Added -- insert at the right position
144-
QList<TransactionRecord> toInsert =
145-
TransactionRecord::decomposeTransaction(wallet, mi->second);
146-
if(!toInsert.isEmpty()) /* only if something to insert */
142+
parent->beginInsertRows(QModelIndex(), lowerIndex, lowerIndex+toInsert.size()-1);
143+
int insert_idx = lowerIndex;
144+
foreach(const TransactionRecord &rec, toInsert)
147145
{
148-
parent->beginInsertRows(QModelIndex(), lowerIndex, lowerIndex+toInsert.size()-1);
149-
int insert_idx = lowerIndex;
150-
foreach(const TransactionRecord &rec, toInsert)
151-
{
152-
cachedWallet.insert(insert_idx, rec);
153-
insert_idx += 1;
154-
}
155-
parent->endInsertRows();
146+
cachedWallet.insert(insert_idx, rec);
147+
insert_idx += 1;
156148
}
149+
parent->endInsertRows();
157150
}
158-
break;
159-
case CT_DELETED:
160-
if(!inModel)
161-
{
162-
qWarning() << "TransactionTablePriv::updateWallet : Warning: Got CT_DELETED, but transaction is not in model";
163-
break;
164-
}
165-
// Removed -- remove entire transaction from table
166-
parent->beginRemoveRows(QModelIndex(), lowerIndex, upperIndex-1);
167-
cachedWallet.erase(lower, upper);
168-
parent->endRemoveRows();
169-
break;
170-
case CT_UPDATED:
171-
// Miscellaneous updates -- nothing to do, status update will take care of this, and is only computed for
172-
// visible transactions.
151+
}
152+
break;
153+
case CT_DELETED:
154+
if(!inModel)
155+
{
156+
qWarning() << "TransactionTablePriv::updateWallet : Warning: Got CT_DELETED, but transaction is not in model";
173157
break;
174158
}
159+
// Removed -- remove entire transaction from table
160+
parent->beginRemoveRows(QModelIndex(), lowerIndex, upperIndex-1);
161+
cachedWallet.erase(lower, upper);
162+
parent->endRemoveRows();
163+
break;
164+
case CT_UPDATED:
165+
// Miscellaneous updates -- nothing to do, status update will take care of this, and is only computed for
166+
// visible transactions.
167+
break;
175168
}
176169
}
177170

@@ -230,16 +223,20 @@ TransactionTableModel::TransactionTableModel(CWallet* wallet, WalletModel *paren
230223
QAbstractTableModel(parent),
231224
wallet(wallet),
232225
walletModel(parent),
233-
priv(new TransactionTablePriv(wallet, this))
226+
priv(new TransactionTablePriv(wallet, this)),
227+
fProcessingQueuedTransactions(false)
234228
{
235229
columns << QString() << QString() << tr("Date") << tr("Type") << tr("Address") << BitcoinUnits::getAmountColumnTitle(walletModel->getOptionsModel()->getDisplayUnit());
236230
priv->refreshWallet();
237231

238232
connect(walletModel->getOptionsModel(), SIGNAL(displayUnitChanged(int)), this, SLOT(updateDisplayUnit()));
233+
234+
subscribeToCoreSignals();
239235
}
240236

241237
TransactionTableModel::~TransactionTableModel()
242238
{
239+
unsubscribeFromCoreSignals();
243240
delete priv;
244241
}
245242

@@ -250,12 +247,12 @@ void TransactionTableModel::updateAmountColumnTitle()
250247
emit headerDataChanged(Qt::Horizontal,Amount,Amount);
251248
}
252249

253-
void TransactionTableModel::updateTransaction(const QString &hash, int status)
250+
void TransactionTableModel::updateTransaction(const QString &hash, int status, bool showTransaction)
254251
{
255252
uint256 updated;
256253
updated.SetHex(hash.toStdString());
257254

258-
priv->updateWallet(updated, status);
255+
priv->updateWallet(updated, status, showTransaction);
259256
}
260257

261258
void TransactionTableModel::updateConfirmations()
@@ -649,3 +646,82 @@ void TransactionTableModel::updateDisplayUnit()
649646
updateAmountColumnTitle();
650647
emit dataChanged(index(0, Amount), index(priv->size()-1, Amount));
651648
}
649+
650+
// queue notifications to show a non freezing progress dialog e.g. for rescan
651+
struct TransactionNotification
652+
{
653+
public:
654+
TransactionNotification() {}
655+
TransactionNotification(uint256 hash, ChangeType status, bool showTransaction):
656+
hash(hash), status(status), showTransaction(showTransaction) {}
657+
658+
void invoke(QObject *ttm)
659+
{
660+
QString strHash = QString::fromStdString(hash.GetHex());
661+
qDebug() << "NotifyTransactionChanged : " + strHash + " status= " + QString::number(status);
662+
QMetaObject::invokeMethod(ttm, "updateTransaction", Qt::QueuedConnection,
663+
Q_ARG(QString, strHash),
664+
Q_ARG(int, status),
665+
Q_ARG(bool, showTransaction));
666+
}
667+
private:
668+
uint256 hash;
669+
ChangeType status;
670+
bool showTransaction;
671+
};
672+
673+
static bool fQueueNotifications = false;
674+
static std::vector< TransactionNotification > vQueueNotifications;
675+
676+
static void NotifyTransactionChanged(TransactionTableModel *ttm, CWallet *wallet, const uint256 &hash, ChangeType status)
677+
{
678+
// Find transaction in wallet
679+
std::map<uint256, CWalletTx>::iterator mi = wallet->mapWallet.find(hash);
680+
// Determine whether to show transaction or not (determine this here so that no relocking is needed in GUI thread)
681+
bool inWallet = mi != wallet->mapWallet.end();
682+
bool showTransaction = (inWallet && TransactionRecord::showTransaction(mi->second));
683+
684+
TransactionNotification notification(hash, status, showTransaction);
685+
686+
if (fQueueNotifications)
687+
{
688+
vQueueNotifications.push_back(notification);
689+
return;
690+
}
691+
notification.invoke(ttm);
692+
}
693+
694+
static void ShowProgress(TransactionTableModel *ttm, const std::string &title, int nProgress)
695+
{
696+
if (nProgress == 0)
697+
fQueueNotifications = true;
698+
699+
if (nProgress == 100)
700+
{
701+
fQueueNotifications = false;
702+
if (vQueueNotifications.size() > 10) // prevent balloon spam, show maximum 10 balloons
703+
QMetaObject::invokeMethod(ttm, "setProcessingQueuedTransactions", Qt::QueuedConnection, Q_ARG(bool, true));
704+
for (unsigned int i = 0; i < vQueueNotifications.size(); ++i)
705+
{
706+
if (vQueueNotifications.size() - i <= 10)
707+
QMetaObject::invokeMethod(ttm, "setProcessingQueuedTransactions", Qt::QueuedConnection, Q_ARG(bool, false));
708+
709+
vQueueNotifications[i].invoke(ttm);
710+
}
711+
std::vector<TransactionNotification >().swap(vQueueNotifications); // clear
712+
}
713+
}
714+
715+
void TransactionTableModel::subscribeToCoreSignals()
716+
{
717+
// Connect signals to wallet
718+
wallet->NotifyTransactionChanged.connect(boost::bind(NotifyTransactionChanged, this, _1, _2, _3));
719+
wallet->ShowProgress.connect(boost::bind(ShowProgress, this, _1, _2));
720+
}
721+
722+
void TransactionTableModel::unsubscribeFromCoreSignals()
723+
{
724+
// Disconnect signals from wallet
725+
wallet->NotifyTransactionChanged.disconnect(boost::bind(NotifyTransactionChanged, this, _1, _2, _3));
726+
wallet->ShowProgress.disconnect(boost::bind(ShowProgress, this, _1, _2));
727+
}

src/qt/transactiontablemodel.h

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,12 +72,17 @@ class TransactionTableModel : public QAbstractTableModel
7272
QVariant data(const QModelIndex &index, int role) const;
7373
QVariant headerData(int section, Qt::Orientation orientation, int role) const;
7474
QModelIndex index(int row, int column, const QModelIndex & parent = QModelIndex()) const;
75+
bool processingQueuedTransactions() { return fProcessingQueuedTransactions; }
7576

7677
private:
7778
CWallet* wallet;
7879
WalletModel *walletModel;
7980
QStringList columns;
8081
TransactionTablePriv *priv;
82+
bool fProcessingQueuedTransactions;
83+
84+
void subscribeToCoreSignals();
85+
void unsubscribeFromCoreSignals();
8186

8287
QString lookupAddress(const std::string &address, bool tooltip) const;
8388
QVariant addressColor(const TransactionRecord *wtx) const;
@@ -92,11 +97,14 @@ class TransactionTableModel : public QAbstractTableModel
9297
QVariant txAddressDecoration(const TransactionRecord *wtx) const;
9398

9499
public slots:
95-
void updateTransaction(const QString &hash, int status);
100+
/* New transaction, or transaction changed status */
101+
void updateTransaction(const QString &hash, int status, bool showTransaction);
96102
void updateConfirmations();
97103
void updateDisplayUnit();
98104
/** Updates the column title to "Amount (DisplayUnit)" and emits headerDataChanged() signal for table headers to react. */
99105
void updateAmountColumnTitle();
106+
/* Needed to update fProcessingQueuedTransactions through a QueuedConnection */
107+
void setProcessingQueuedTransactions(bool value) { fProcessingQueuedTransactions = value; }
100108

101109
friend class TransactionTablePriv;
102110
};

src/qt/walletmodel.cpp

Lines changed: 5 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,6 @@ WalletModel::WalletModel(CWallet *wallet, OptionsModel *optionsModel, QObject *p
3434
cachedEncryptionStatus(Unencrypted),
3535
cachedNumBlocks(0)
3636
{
37-
fProcessingQueuedTransactions = false;
3837
fHaveWatchOnly = wallet->HaveWatchOnly();
3938
fForceCheckBalanceChanged = false;
4039

@@ -164,11 +163,8 @@ void WalletModel::checkBalanceChanged()
164163
}
165164
}
166165

167-
void WalletModel::updateTransaction(const QString &hash, int status)
166+
void WalletModel::updateTransaction()
168167
{
169-
if(transactionTableModel)
170-
transactionTableModel->updateTransaction(hash, status);
171-
172168
// Balance and number of transactions might have changed
173169
fForceCheckBalanceChanged = true;
174170
}
@@ -455,45 +451,16 @@ static void NotifyAddressBookChanged(WalletModel *walletmodel, CWallet *wallet,
455451
Q_ARG(int, status));
456452
}
457453

458-
// queue notifications to show a non freezing progress dialog e.g. for rescan
459-
static bool fQueueNotifications = false;
460-
static std::vector<std::pair<uint256, ChangeType> > vQueueNotifications;
461454
static void NotifyTransactionChanged(WalletModel *walletmodel, CWallet *wallet, const uint256 &hash, ChangeType status)
462455
{
463-
if (fQueueNotifications)
464-
{
465-
vQueueNotifications.push_back(make_pair(hash, status));
466-
return;
467-
}
468-
469-
QString strHash = QString::fromStdString(hash.GetHex());
470-
471-
qDebug() << "NotifyTransactionChanged : " + strHash + " status= " + QString::number(status);
472-
QMetaObject::invokeMethod(walletmodel, "updateTransaction", Qt::QueuedConnection,
473-
Q_ARG(QString, strHash),
474-
Q_ARG(int, status));
456+
Q_UNUSED(wallet);
457+
Q_UNUSED(hash);
458+
Q_UNUSED(status);
459+
QMetaObject::invokeMethod(walletmodel, "updateTransaction", Qt::QueuedConnection);
475460
}
476461

477462
static void ShowProgress(WalletModel *walletmodel, const std::string &title, int nProgress)
478463
{
479-
if (nProgress == 0)
480-
fQueueNotifications = true;
481-
482-
if (nProgress == 100)
483-
{
484-
fQueueNotifications = false;
485-
if (vQueueNotifications.size() > 10) // prevent balloon spam, show maximum 10 balloons
486-
QMetaObject::invokeMethod(walletmodel, "setProcessingQueuedTransactions", Qt::QueuedConnection, Q_ARG(bool, true));
487-
for (unsigned int i = 0; i < vQueueNotifications.size(); ++i)
488-
{
489-
if (vQueueNotifications.size() - i <= 10)
490-
QMetaObject::invokeMethod(walletmodel, "setProcessingQueuedTransactions", Qt::QueuedConnection, Q_ARG(bool, false));
491-
492-
NotifyTransactionChanged(walletmodel, NULL, vQueueNotifications[i].first, vQueueNotifications[i].second);
493-
}
494-
std::vector<std::pair<uint256, ChangeType> >().swap(vQueueNotifications); // clear
495-
}
496-
497464
// emits signal "showProgress"
498465
QMetaObject::invokeMethod(walletmodel, "showProgress", Qt::QueuedConnection,
499466
Q_ARG(QString, QString::fromStdString(title)),

0 commit comments

Comments
 (0)