Browse Source

Convert RPC console to QTextEdit instead of QTableView

* This allows copy/pasting whole or partial messages
* Handle output more consistently in console
    * No more scrollbars-in-scrollbars: by setting per-pixel scrolling on the table, cells can have any height
* Decorations for "request" and "reply" are changed to the txin and txout icons instead of colored squares
try
Wladimir J. van der Laan 13 years ago
parent
commit
c6aa86afc2
  1. 22
      src/qt/forms/rpcconsole.ui
  2. 124
      src/qt/rpcconsole.cpp
  3. 5
      src/qt/rpcconsole.h

22
src/qt/forms/rpcconsole.ui

@ -7,7 +7,7 @@
<x>0</x>
<y>0</y>
<width>706</width>
<height>382</height>
<height>446</height>
</rect>
</property>
<property name="windowTitle">
@ -327,30 +327,22 @@
<number>3</number>
</property>
<item>
<widget class="QTableWidget" name="messagesWidget">
<widget class="QTextEdit" name="messagesWidget">
<property name="minimumSize">
<size>
<width>0</width>
<height>100</height>
</size>
</property>
<property name="tabKeyNavigation">
<bool>false</bool>
<property name="readOnly">
<bool>true</bool>
</property>
<property name="selectionBehavior">
<enum>QAbstractItemView::SelectRows</enum>
<property name="tabKeyNavigation" stdset="0">
<bool>false</bool>
</property>
<property name="columnCount">
<property name="columnCount" stdset="0">
<number>2</number>
</property>
<attribute name="horizontalHeaderVisible">
<bool>false</bool>
</attribute>
<attribute name="verticalHeaderVisible">
<bool>false</bool>
</attribute>
<column/>
<column/>
</widget>
</item>
<item>

124
src/qt/rpcconsole.cpp

@ -10,6 +10,7 @@
#include <QThread>
#include <QTextEdit>
#include <QKeyEvent>
#include <QUrl>
#include <boost/tokenizer.hpp>
@ -19,6 +20,19 @@
const int CONSOLE_SCROLLBACK = 50;
const int CONSOLE_HISTORY = 50;
const QSize ICON_SIZE(24, 24);
const struct {
const char *url;
const char *source;
} ICON_MAPPING[] = {
{"cmd-request", ":/icons/tx_input"},
{"cmd-reply", ":/icons/tx_output"},
{"cmd-error", ":/icons/tx_output"},
{"misc", ":/icons/tx_inout"},
{NULL, NULL}
};
/* Object for executing console RPC commands in a separate thread.
*/
class RPCExecutor: public QObject
@ -83,12 +97,9 @@ void RPCExecutor::request(const QString &command)
RPCConsole::RPCConsole(QWidget *parent) :
QDialog(parent),
ui(new Ui::RPCConsole),
firstLayout(true),
historyPtr(0)
{
ui->setupUi(this);
ui->messagesWidget->horizontalHeader()->setResizeMode(1, QHeaderView::Stretch);
ui->messagesWidget->setContextMenuPolicy(Qt::ActionsContextMenu);
#ifndef WIN32
// Show Debug logfile label and Open button only for Windows
@ -99,13 +110,6 @@ RPCConsole::RPCConsole(QWidget *parent) :
// Install event filter for up and down arrow
ui->lineEdit->installEventFilter(this);
// Add "Copy message" to context menu explicitly
QAction *copyMessageAction = new QAction(tr("&Copy"), this);
copyMessageAction->setShortcut(QKeySequence(Qt::CTRL | Qt::Key_C));
copyMessageAction->setShortcutContext(Qt::WidgetShortcut);
connect(copyMessageAction, SIGNAL(triggered()), this, SLOT(copyMessage()));
ui->messagesWidget->addAction(copyMessageAction);
connect(ui->clearButton, SIGNAL(clicked()), this, SLOT(clear()));
connect(ui->openDebugLogfileButton, SIGNAL(clicked()), this, SLOT(on_openDebugLogfileButton_clicked()));
@ -159,68 +163,62 @@ void RPCConsole::setClientModel(ClientModel *model)
}
}
static QColor categoryColor(int category)
static QString categoryClass(int category)
{
switch(category)
{
case RPCConsole::MC_ERROR: return QColor(255,0,0); break;
case RPCConsole::MC_DEBUG: return QColor(192,192,192); break;
case RPCConsole::CMD_REQUEST: return QColor(128,128,128); break;
case RPCConsole::CMD_REPLY: return QColor(128,255,128); break;
case RPCConsole::CMD_ERROR: return QColor(255,128,128); break;
default: return QColor(0,0,0);
case RPCConsole::CMD_REQUEST: return "cmd-request"; break;
case RPCConsole::CMD_REPLY: return "cmd-reply"; break;
case RPCConsole::CMD_ERROR: return "cmd-error"; break;
default: return "misc";
}
}
void RPCConsole::clear()
{
ui->messagesWidget->clear();
ui->messagesWidget->setRowCount(0);
ui->lineEdit->clear();
ui->lineEdit->setFocus();
message(CMD_REPLY, tr("Welcome to the bitcoin RPC console.")+"\n"+
tr("Use up and down arrows to navigate history, and Ctrl-L to clear screen.")+"\n"+
tr("Type \"help\" for an overview of available commands."));
// Add smoothly scaled icon images.
// (when using width/height on an img, Qt uses nearest instead of linear interpolation)
for(int i=0; ICON_MAPPING[i].url; ++i)
{
ui->messagesWidget->document()->addResource(
QTextDocument::ImageResource,
QUrl(ICON_MAPPING[i].url),
QImage(ICON_MAPPING[i].source).scaled(ICON_SIZE, Qt::IgnoreAspectRatio, Qt::SmoothTransformation));
}
// Set default style sheet
ui->messagesWidget->document()->setDefaultStyleSheet(
"table { }"
"td.time { color: #808080; padding-top: 3px; } "
"td.message { font-family: Monospace; font-size: 12px; } "
"td.cmd-request { color: #006060; } "
"td.cmd-error { color: red; } "
"b { color: #006060; } "
);
message(CMD_REPLY, tr("Welcome to the Bitcoin RPC console.<br>"
"Use up and down arrows to navigate history, and <b>Ctrl-L</b> to clear screen.<br>"
"Type <b>help</b> for an overview of available commands."), true);
}
void RPCConsole::message(int category, const QString &message)
void RPCConsole::message(int category, const QString &message, bool html)
{
// Add row to messages widget
int row = ui->messagesWidget->rowCount();
ui->messagesWidget->setRowCount(row+1);
QTime time = QTime::currentTime();
QTableWidgetItem *newTime = new QTableWidgetItem(time.toString());
newTime->setData(Qt::DecorationRole, categoryColor(category));
newTime->setForeground(QColor(128,128,128));
newTime->setFlags(Qt::ItemIsSelectable|Qt::ItemIsEnabled); // make non-editable
int numLines = message.count("\n") + 1;
// As Qt doesn't like very tall cells (they break scrolling) keep only short messages in
// the cell text, longer messages trigger a display widget with scroll bar
if(numLines < 5)
{
QTableWidgetItem *newItem = new QTableWidgetItem(message);
newItem->setFlags(Qt::ItemIsSelectable|Qt::ItemIsEnabled); // make non-editable
if(category == CMD_ERROR) // Coloring error messages in red
newItem->setForeground(QColor(255,16,16));
ui->messagesWidget->setItem(row, 1, newItem);
} else {
QTextEdit *newWidget = new QTextEdit;
newWidget->setText(message);
newWidget->setMaximumHeight(100);
newWidget->setReadOnly(true);
ui->messagesWidget->setCellWidget(row, 1, newWidget);
}
ui->messagesWidget->setItem(row, 0, newTime);
ui->messagesWidget->resizeRowToContents(row);
// Preserve only limited scrollback buffer
while(ui->messagesWidget->rowCount() > CONSOLE_SCROLLBACK)
ui->messagesWidget->removeRow(0);
// Scroll to bottom after table is updated
QTimer::singleShot(0, ui->messagesWidget, SLOT(scrollToBottom()));
QString timeString = time.toString();
QString out;
out += "<table><tr><td class=\"time\" width=\"65\">" + timeString + "</td>";
out += "<td class=\"icon\" width=\"32\"><img src=\"" + categoryClass(category) + "\"></td>";
out += "<td class=\"message " + categoryClass(category) + "\" valign=\"middle\">";
if(html)
out += message;
else
out += GUIUtil::HtmlEscape(message, true);
out += "</td></tr></table>";
ui->messagesWidget->append(out);
}
void RPCConsole::setNumConnections(int count)
@ -298,24 +296,10 @@ void RPCConsole::startExecutor()
thread->start();
}
void RPCConsole::copyMessage()
{
GUIUtil::copyEntryData(ui->messagesWidget, 1, Qt::EditRole);
}
void RPCConsole::on_tabWidget_currentChanged(int index)
{
if(ui->tabWidget->widget(index) == ui->tab_console)
{
if(firstLayout)
{
// Work around QTableWidget issue:
// Call resizeRowsToContents on first Layout request with widget visible,
// to make sure multiline messages that were added before the console was shown
// have the right height.
firstLayout = false;
ui->messagesWidget->resizeRowsToContents();
}
ui->lineEdit->setFocus();
}
}

5
src/qt/rpcconsole.h

@ -37,15 +37,13 @@ private slots:
public slots:
void clear();
void message(int category, const QString &message);
void message(int category, const QString &message, bool html = false);
/** Set number of connections shown in the UI */
void setNumConnections(int count);
/** Set number of blocks shown in the UI */
void setNumBlocks(int count);
/** Go forward or back in history */
void browseHistory(int offset);
/** Copy currently selected message to clipboard */
void copyMessage();
signals:
// For RPC command executor
@ -55,7 +53,6 @@ signals:
private:
Ui::RPCConsole *ui;
ClientModel *clientModel;
bool firstLayout;
QStringList history;
int historyPtr;

Loading…
Cancel
Save