make plugins dialog available in tray
This makes it possible to install a third-party plugin from the wizard, before creating a wallet, e.g. for a hardware wallet.
This commit is contained in:
@@ -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))
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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([
|
||||
_('<b>Warning</b>: Third-party plugins are not endorsed by Electrum!'),
|
||||
'<br/><br/>',
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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,
|
||||
|
||||
Reference in New Issue
Block a user