diff --git a/electrum/gui/qt/__init__.py b/electrum/gui/qt/__init__.py
index 8dc7d6b73..c66f48271 100644
--- a/electrum/gui/qt/__init__.py
+++ b/electrum/gui/qt/__init__.py
@@ -38,8 +38,8 @@ except Exception as e:
"Error: Could not import PyQt6. On Linux systems, "
"you may try 'sudo apt-get install python3-pyqt6'") from e
-from PyQt6.QtGui import QGuiApplication
-from PyQt6.QtWidgets import QApplication, QSystemTrayIcon, QWidget, QMenu, QMessageBox, QDialog
+from PyQt6.QtGui import QGuiApplication, QCursor
+from PyQt6.QtWidgets import QApplication, QSystemTrayIcon, QWidget, QMenu, QMessageBox, QDialog, QToolTip
from PyQt6.QtCore import QObject, pyqtSignal, QTimer, Qt
import PyQt6.QtCore as QtCore
@@ -208,6 +208,7 @@ class ElectrumGui(BaseElectrumGui, Logger):
m = self.tray.contextMenu()
m.clear()
network = self.daemon.network
+ m.addAction(_("Plugins"), self.show_plugins_dialog)
if network:
m.addAction(_("Network"), self.show_network_dialog)
if network and network.lngossip:
@@ -293,6 +294,11 @@ class ElectrumGui(BaseElectrumGui, Logger):
self.lightning_dialog = LightningDialog(self)
self.lightning_dialog.bring_to_top()
+ def show_plugins_dialog(self):
+ from .plugins_dialog import PluginsDialog
+ d = PluginsDialog(self)
+ d.exec()
+
def show_network_dialog(self, proxy_tab=False):
if self.network_dialog:
self.network_dialog.show(proxy_tab=proxy_tab)
@@ -546,3 +552,9 @@ class ElectrumGui(BaseElectrumGui, Logger):
if hasattr(PyQt6, "__path__"):
ret["pyqt.path"] = ", ".join(PyQt6.__path__ or [])
return ret
+
+ def do_copy(self, text: str, *, title: str = None) -> None:
+ self.app.clipboard().setText(text)
+ message = _("Text copied to Clipboard") if title is None else _("{} copied to Clipboard").format(title)
+ # tooltip cannot be displayed immediately when called from a menu; wait 200ms
+ self.timer.singleShot(200, lambda: QToolTip.showText(QCursor.pos(), message, None))
diff --git a/electrum/gui/qt/main_window.py b/electrum/gui/qt/main_window.py
index 833896c56..919fea096 100644
--- a/electrum/gui/qt/main_window.py
+++ b/electrum/gui/qt/main_window.py
@@ -759,7 +759,7 @@ class ElectrumWindow(QMainWindow, MessageBoxMixin, Logger, QtEventListener):
tools_menu.addAction(_("Electrum preferences"), self.settings_dialog)
tools_menu.addAction(_("&Network"), self.gui_object.show_network_dialog).setEnabled(bool(self.network))
- tools_menu.addAction(_("&Plugins"), self.plugins_dialog)
+ tools_menu.addAction(_("&Plugins"), self.gui_object.show_plugins_dialog)
tools_menu.addSeparator()
tools_menu.addAction(_("&Sign/verify message"), self.sign_verify_message)
tools_menu.addAction(_("&Encrypt/decrypt message"), self.encrypt_message)
@@ -1131,9 +1131,7 @@ class ElectrumWindow(QMainWindow, MessageBoxMixin, Logger, QtEventListener):
return ReceiveTab(self)
def do_copy(self, text: str, *, title: str = None) -> None:
- self.app.clipboard().setText(text)
- message = _("Text copied to Clipboard") if title is None else _("{} copied to Clipboard").format(title)
- self.show_tooltip_after_delay(message)
+ self.gui_object.do_copy(text, title=title)
def show_tooltip_after_delay(self, message):
# tooltip cannot be displayed immediately when called from a menu; wait 200ms
@@ -2209,12 +2207,6 @@ class ElectrumWindow(QMainWindow, MessageBoxMixin, Logger, QtEventListener):
layout.addLayout(hbox, 4, 1)
d.exec()
- def password_dialog(self, msg=None, parent=None):
- from .password_dialog import PasswordDialog
- parent = parent or self
- d = PasswordDialog(parent, msg)
- return d.run()
-
def tx_from_text(self, data: Union[str, bytes]) -> Union[None, 'PartialTransaction', 'Transaction']:
from electrum.transaction import tx_from_any
try:
@@ -2654,11 +2646,6 @@ class ElectrumWindow(QMainWindow, MessageBoxMixin, Logger, QtEventListener):
self.gui_object.timer.timeout.disconnect(self.timer_actions)
self.gui_object.close_window(self)
- def plugins_dialog(self):
- from .plugins_dialog import PluginsDialog
- d = PluginsDialog(self)
- d.exec()
-
def cpfp_dialog(self, parent_tx: Transaction) -> None:
new_tx = self.wallet.cpfp(parent_tx, 0)
total_size = parent_tx.estimated_size() + new_tx.estimated_size()
diff --git a/electrum/gui/qt/plugins_dialog.py b/electrum/gui/qt/plugins_dialog.py
index 7b9e7081f..f74e1d827 100644
--- a/electrum/gui/qt/plugins_dialog.py
+++ b/electrum/gui/qt/plugins_dialog.py
@@ -6,17 +6,17 @@ from PyQt6.QtWidgets import QLabel, QVBoxLayout, QGridLayout, QPushButton, QWidg
from electrum.i18n import _
from electrum.plugin import run_hook
-from .util import WindowModalDialog, Buttons, CloseButton, WWLabel, insert_spaces
+from .util import WindowModalDialog, Buttons, CloseButton, WWLabel, insert_spaces, MessageBoxMixin
if TYPE_CHECKING:
- from .main_window import ElectrumWindow
+ from . import ElectrumGui
from electrum_cc import ECPrivkey
class PluginDialog(WindowModalDialog):
- def __init__(self, name, metadata, status_button: Optional['PluginStatusButton'], window: 'ElectrumWindow'):
+ def __init__(self, name, metadata, status_button: Optional['PluginStatusButton'], window: 'PluginsDialog'):
display_name = metadata.get('fullname', '')
author = metadata.get('author', '')
description = metadata.get('description', '')
@@ -80,12 +80,12 @@ class PluginDialog(WindowModalDialog):
self.status_button.update()
self.close()
# note: all enabled plugins will receive this hook:
- run_hook('init_qt', self.window.window.gui_object)
+ run_hook('init_qt', self.window.gui_object)
class PluginStatusButton(QPushButton):
- def __init__(self, window, name):
+ def __init__(self, window: 'PluginsDialog', name: str):
QPushButton.__init__(self, '')
self.window = window
self.plugins = window.plugins
@@ -103,7 +103,7 @@ class PluginStatusButton(QPushButton):
p = self.plugins.get(self.name)
plugin_is_loaded = p is not None
enabled = (
- not plugin_is_loaded and self.plugins.is_available(self.name, self.window.wallet)
+ not plugin_is_loaded
or plugin_is_loaded and p.can_user_disable()
)
self.setEnabled(enabled)
@@ -113,14 +113,13 @@ class PluginStatusButton(QPushButton):
self.setText(text)
-class PluginsDialog(WindowModalDialog):
+class PluginsDialog(WindowModalDialog, MessageBoxMixin):
- def __init__(self, window: 'ElectrumWindow'):
- WindowModalDialog.__init__(self, window, _('Electrum Plugins'))
- self.window = window
- self.wallet = self.window.wallet
- self.config = window.config
- self.plugins = self.window.gui_object.plugins
+ def __init__(self, gui_object: 'ElectrumGui'):
+ WindowModalDialog.__init__(self, None, _('Electrum Plugins'))
+ self.gui_object = gui_object
+ self.config = gui_object.config
+ self.plugins = gui_object.plugins
vbox = QVBoxLayout(self)
scroll = QScrollArea()
scroll.setEnabled(True)
@@ -144,7 +143,7 @@ class PluginsDialog(WindowModalDialog):
self.init_plugins_password()
return
# ask for url and password, same window
- pw = self.window.password_dialog(
+ pw = self.password_dialog(
msg=' '.join([
_('Warning: Third-party plugins are not endorsed by Electrum!'),
'
',
@@ -160,7 +159,7 @@ class PluginsDialog(WindowModalDialog):
privkey = self.plugins.derive_privkey(pw, salt)
if pubkey != privkey.get_public_key_bytes():
keyfile_path, keyfile_help = self.plugins.get_keyfile_path()
- self.window.show_error(
+ self.show_error(
''.join([
_('Incorrect password.'), '\n\n',
_('Your plugin authorization password is required to install plugins.'), ' ',
@@ -186,8 +185,8 @@ class PluginsDialog(WindowModalDialog):
_('Your plugins key is:'), '\n\n', key_hex, '\n\n',
_('Please save this key in'), '\n\n' + keyfile_path, '\n\n', keyfile_help
])
- self.window.do_copy(key_hex, title=_('Plugins key'))
- self.window.show_message(msg)
+ self.gui_object.do_copy(key_hex, title=_('Plugins key'))
+ self.show_message(msg)
def download_plugin_dialog(self):
import os
@@ -206,12 +205,12 @@ class PluginsDialog(WindowModalDialog):
except UserCancelled:
return
except Exception as e:
- self.window.show_error(f"{e}")
+ self.show_error(f"{e}")
return
try:
success = self.confirm_add_plugin(path)
except Exception as e:
- self.window.show_error(f"{e}")
+ self.show_error(f"{e}")
success = False
if not success:
os.unlink(path)
@@ -232,7 +231,7 @@ class PluginsDialog(WindowModalDialog):
try:
success = self.confirm_add_plugin(path)
except Exception as e:
- self.window.show_error(f"{e}")
+ self.show_error(f"{e}")
success = False
if not success:
os.unlink(path)
@@ -249,7 +248,7 @@ class PluginsDialog(WindowModalDialog):
return False
self.plugins.external_plugin_metadata[name] = manifest
self.plugins.authorize_plugin(name, path, privkey)
- self.window.show_message(_('Plugin installed successfully.'))
+ self.show_message(_('Plugin installed successfully.'))
self.show_list()
return True
diff --git a/electrum/gui/qt/util.py b/electrum/gui/qt/util.py
index 273f73805..6ee320b1d 100644
--- a/electrum/gui/qt/util.py
+++ b/electrum/gui/qt/util.py
@@ -320,6 +320,13 @@ class MessageBoxMixin(object):
return None
return choice_widget.selected_key
+ def password_dialog(self, msg=None, parent=None):
+ from .password_dialog import PasswordDialog
+ parent = parent or self
+ d = PasswordDialog(parent, msg)
+ return d.run()
+
+
def custom_message_box(*, icon, parent, title, text, buttons=QMessageBox.StandardButton.Ok,
defaultButton=QMessageBox.StandardButton.NoButton, rich_text=False,