An Introduction to Plugin Development for the Electrum Bitcoin Wallet
By Rich Apodaca | Updated
Electrum was created with extensibility in mind. Not only is the source code released under a liberal open source license, but the wallet itself supports third-party plugins. This article shows how to create a simple plugin, and documents the full plugin API. A previous article covered the Electrum Console API.
Step 1: Download and Run Electrum from Source
Clone the Electrum source repository, check out the release you’ll be using, then install the system dependencies needed to run it. The dependencies you’ll need to install will vary depending on your operating system and past development activities.
After dependencies have been installed, you’ll need to generate the Electrum icons. Do so with:
pyrcc5 icons.qrc -o gui/qt/icons_rc.py
If you’re currently using a wallet on your development system, it may be a good idea to keep it separate. To start Electrum in Testnet mode using a clean wallet:
./electrum -w /path/to/dev/wallet --testnet
After creating the dev wallet, close Electrum.
You may get an error along the lines of “Unexpected magic characters”. The following command will clear it:
find . -name "*.pyc" -delete
Step 2: Create a Plugin Directory
Your plugin will reside inside Electrum’s plugins
directory:
mkdir plugins/test
touch plugins/test/__init__.py
touch plugins/test/qt.py
Step 3: Add an Initialization File
Open __init__.py
in the plugins/test
directory. Give it the following content:
from electrum.i18n import _
fullname = _('Test') # label displayed under the "Plugins" menu
description = 'This is a test!' # description shown when "?" button pressed
available_for = ['qt'] # available for the Qt interface only
Step 4: Add a UI File
A plugin’s main entry point is the qt.py
file. At a minimum, this file must contain a class that inherits from Electrum’s BasePlugin
. Add the following to the file at plugins/test/qt.py
.
from electrum.plugins import BasePlugin
class Plugin(BasePlugin):
def __init__(self, parent, config, name):
BasePlugin.__init__(self, parent, config, name)
Step 5: Test the Plugin
Restarting Electrum should put the Test plugin under the dialog available from Tools>Plugins
.
Option: Enable Settings
Most Electrum plugins allow end-user configuration through the GUI. Add this capability to your plugin by overriding three methods: requires_settings
; settings_widget
; and settings_dialog
:
from electrum.plugins import BasePlugin
from electrum_gui.qt.util import (EnterButton, Buttons, CloseButton, OkButton, WindowModalDialog)
from PyQt5.QtWidgets import (QVBoxLayout)
class Plugin(BasePlugin):
def __init__(self, parent, config, name):
BasePlugin.__init__(self, parent, config, name)
def requires_settings(self):
# Return True to add a Settings button.
return True
def settings_widget(self, window):
# Return a button that when pressed presents a settings dialog.
return EnterButton(_('Settings'), partial(self.settings_dialog, window))
def settings_dialog(self, window):
# Return a settings dialog.
d = WindowModalDialog(window, _("Email settings"))
vbox = QVBoxLayout(d)
d.setMinimumSize(500, 200)
vbox.addStretch()
vbox.addLayout(Buttons(CloseButton(d), OkButton(d)))
d.show()
This plugin will add a Settings
button that when pressed produces and empty configuration dialog.
Option: Add Hooks
Your plugin can respond to events triggered by the Electrum GUI. The general mechanism for doing so uses the @hook
mechanism. Import hook
, then implement the hook of interest. All, some, or no hooks may be used.
from electrum.plugins import BasePlugin, hook
class Plugin(BasePlugin):
def __init__(self, parent, config, name):
BasePlugin.__init__(self, parent, config, name)
def load_wallet(self, wallet, main_window):
print("found the wallet", wallet)
Hooks API
create_send_tab
def create_send_tab(self, grid):
""" Called after sending a payment
Args:
grid: QGridLayout containing the Send tab UI
"""
pass
set_seed
def set_seed(self, seed, parent):
""" Called when the seed dialog is shown
Args:
seed: The string representation of the seed
parent: ButtonsTextEdit containing the seed
"""
pass
transaction_dialog_update
def transaction_dialog_update(self, dialog):
""" Called when the transaction dialog is displayed or updated
Args:
dialog: electrum_gui.qt.transaction_dialog.TxDialog representing the transaction
"""
pass
transaction_dialog
# transaction dialog shown
def transaction_dialog(self, dialog):
""" Called when the transaction dialog is displayed
Args:
dialog: electrum_gui.qt.transaction_dialog.TxDialog representing the transaction
"""
pass
This dialog can be decorated as follows:
def transaction_dialog(self, dialog):
button = QPushButton(_("Test Button"))
button.clicked.connect(lambda: print("button clicked..."))
dialog.buttons.insert(0, button)
button.show()
This modification places a button labeled “Test Button” onto the Transaction detail dialog.
password_dialog
def password_dialog(self, edit, parent, position):
""" Called when the user is prompted to enter a wallet password
Args:
edit PyQt5.QtWidgets.QLineEdit
parent PyQt5.QtWidgets.QGridLayout
position
"""
pass
init_qt
def init_qt(self, gui):
""" Called after plugin is activated by ticking box, or at startup if already active.
Args:
gui: electrum_gui.qt.ElectrumGui
"""
pass
load_wallet
def load_wallet(self, wallet, window):
""" Called after wallet is loaded
Args:
wallet:
window: electrum_gui.qt.main_window.ElectrumWindow
"""
pass
close_wallet
# wallet closed
def close_wallet(self, wallet):
""" Called when a wallet is closed.
Args:
wallet: the wallet that was closed
"""
pass
receive_list_menu
def receive_list_menu(self, menu, address):
""" Called after right-clicking on a receive payment request
Args:
menu: PyQt5.QtWidgets.QMenu
address: string address for the request
"""
pass
on_new_window
def on_new_window(self, window):
""" Called after a new main window is created.
Args:
window: electrum_gui.qt.main_window.ElectrumWindow
"""
pass
on_close_window
def on_close_window(self, window):
""" Called after the main window closes.
Args:
window: electrum_gui.qt.main_window.ElectrumWindow
"""
pass
show_text_edit
def show_text_edit(self, edit):
""" Called when an xpub or address private key is shown
Args:
edit: electrum_gui.qt.qrtextedit.ShowQRTextEdit
"""
pass
init_menubar_tools
def init_menubar_tools(self, window, menu):
""" Called when the Tools menu is initialized
Args:
window: electrum_gui.qt.main_window.ElectrumWindow
menu: PyQt5.QtWidgets.QMenu
"""
pass
This hook can be used to add an item to the Tools menu:
def init_menubar_tools(self, window, menu):
menu.addSeparator()
menu.addAction(_("&Testing"), lambda: print("item selected..."))
Doing so updates the Tools menu accordingly:
abort_send
def abort_send(self, window):
""" Called when the abort dialog is displayed prior to broadcasting a transaction.
Args:
window: electrum_gui.qt.main_window.ElectrumWindow
"""
pass
scan_text_edit
def scan_text_edit(self, parent):
""" Called a camera scans a QR code.
Args:
parent:
"""
pass
receive_menu
def receive_menu(self, menu, addresses, wallet):
""" Called when one or more addresses are right-clicked from the Addresses tab
Args:
menu:
addresses: an array containing the selected addresses
wallet: PyQt5.QtWidgets.QMenu
"""
pass
create_history_tab
def create_history_tab(self):
""" Unknown functionality.
"""
pass