tMerge pull request #2911 from SomberNight/pyqt5 - electrum - Electrum Bitcoin wallet (HTM) git clone https://git.parazyd.org/electrum (DIR) Log (DIR) Files (DIR) Refs (DIR) Submodules --- (DIR) commit 99bc43d8db3879b1118ca8b3db44dbb9152c8da8 (DIR) parent c43b48f4f59d3214bbb3fa851d6e039f086adb45 (HTM) Author: ThomasV <thomasv@electrum.org> Date: Sun, 24 Sep 2017 09:42:32 +0200 Merge pull request #2911 from SomberNight/pyqt5 migration to PyQt5 Diffstat: M README.rst | 6 +++--- M gui/qt/__init__.py | 30 ++++++++++++++++++++---------- M gui/qt/address_dialog.py | 6 +++--- M gui/qt/amountedit.py | 7 ++++--- M gui/qt/console.py | 13 +++++++------ M gui/qt/contact_list.py | 8 +++++--- M gui/qt/fee_slider.py | 9 +++++---- M gui/qt/installwizard.py | 23 +++++++++++++---------- M gui/qt/invoice_list.py | 4 ++-- M gui/qt/main_window.py | 70 ++++++++++++++++++------------- M gui/qt/network_dialog.py | 38 ++++++++++++++++--------------- M gui/qt/password_dialog.py | 4 ++-- M gui/qt/paytoedit.py | 5 +++-- M gui/qt/qrcodewidget.py | 13 ++++++++----- M gui/qt/qrtextedit.py | 7 ++++--- M gui/qt/qrwindow.py | 9 +++++---- M gui/qt/request_list.py | 7 ++++--- M gui/qt/seed_dialog.py | 4 ++-- M gui/qt/transaction_dialog.py | 8 ++++---- M gui/qt/util.py | 23 ++++++++++++----------- M lib/plot.py | 4 ++-- M plugins/audio_modem/qt.py | 5 +++-- M plugins/cosigner_pool/qt.py | 17 +++++++++++------ M plugins/digitalbitbox/qt.py | 2 +- M plugins/email_requests/qt.py | 19 ++++++++++++------- M plugins/greenaddress_instant/qt.py | 2 +- M plugins/hw_wallet/qt.py | 2 +- M plugins/labels/qt.py | 15 ++++++++++----- M plugins/ledger/auth2fa.py | 5 +++-- M plugins/ledger/qt.py | 6 +++--- M plugins/trezor/qt_generic.py | 8 ++++---- M plugins/trustedcoin/qt.py | 16 ++++++++++++---- M plugins/virtualkeyboard/qt.py | 3 ++- M setup-release.py | 2 +- 34 files changed, 232 insertions(+), 168 deletions(-) --- (DIR) diff --git a/README.rst b/README.rst t@@ -23,7 +23,7 @@ Getting started Electrum is a pure python application. If you want to use the Qt interface, install the Qt dependencies:: - sudo apt-get install python3-pyqt4 + sudo apt-get install python3-pyqt5 If you downloaded the official package (tar.gz), you can run Electrum from its root directory, without installing it on your t@@ -60,8 +60,8 @@ Run install (this should install dependencies):: Compile the icons file for Qt:: - sudo apt-get install pyqt4-dev-tools - pyrcc4 icons.qrc -o gui/qt/icons_rc.py -py3 + sudo apt-get install pyqt5-dev-tools + pyrcc5 icons.qrc -o gui/qt/icons_rc.py Compile the protobuf description file:: (DIR) diff --git a/gui/qt/__init__.py b/gui/qt/__init__.py t@@ -28,13 +28,14 @@ import os import signal try: - import PyQt4 + import PyQt5 except Exception: - sys.exit("Error: Could not import PyQt4 on Linux systems, you may try 'sudo apt-get install python3-pyqt4'") + sys.exit("Error: Could not import PyQt5 on Linux systems, you may try 'sudo apt-get install python3-pyqt5'") -from PyQt4.QtGui import * -from PyQt4.QtCore import * -import PyQt4.QtCore as QtCore +from PyQt5.QtGui import * +from PyQt5.QtWidgets import * +from PyQt5.QtCore import * +import PyQt5.QtCore as QtCore from electrum.i18n import _, set_language from electrum.plugins import run_hook t@@ -52,7 +53,7 @@ try: except Exception as e: print(e) print("Error: Could not find icons file.") - print("Please run 'pyrcc4 icons.qrc -o gui/qt/icons_rc.py -py3', and reinstall Electrum") + print("Please run 'pyrcc5 icons.qrc -o gui/qt/icons_rc.py', and reinstall Electrum") sys.exit(1) from .util import * # * needed for plugins t@@ -73,6 +74,13 @@ class OpenFileEventFilter(QObject): return False +class QElectrumApplication(QApplication): + new_window_signal = pyqtSignal(str, object) + + +class QNetworkUpdatedSignalObject(QObject): + network_updated_signal = pyqtSignal(str, object) + class ElectrumGui: t@@ -88,10 +96,11 @@ class ElectrumGui: self.plugins = plugins self.windows = [] self.efilter = OpenFileEventFilter(self.windows) - self.app = QApplication(sys.argv) + self.app = QElectrumApplication(sys.argv) self.app.installEventFilter(self.efilter) self.timer = Timer() self.nd = None + self.network_updated_signal_obj = QNetworkUpdatedSignalObject() # init tray self.dark_icon = self.config.get("dark_icon", False) self.tray = QSystemTrayIcon(self.tray_icon(), None) t@@ -99,7 +108,7 @@ class ElectrumGui: self.tray.activated.connect(self.tray_activated) self.build_tray_menu() self.tray.show() - self.app.connect(self.app, QtCore.SIGNAL('new_window'), self.start_new_window) + self.app.new_window_signal.connect(self.start_new_window) run_hook('init_qt', self) def build_tray_menu(self): t@@ -141,7 +150,7 @@ class ElectrumGui: def new_window(self, path, uri=None): # Use a signal as can be called from daemon thread - self.app.emit(SIGNAL('new_window'), path, uri) + self.app.new_window_signal.emit(path, uri) def show_network_dialog(self, parent): if not self.daemon.network: t@@ -152,7 +161,8 @@ class ElectrumGui: self.nd.show() self.nd.raise_() return - self.nd = NetworkDialog(self.daemon.network, self.config) + self.nd = NetworkDialog(self.daemon.network, self.config, + self.network_updated_signal_obj) self.nd.show() def create_window_for_wallet(self, wallet): (DIR) diff --git a/gui/qt/address_dialog.py b/gui/qt/address_dialog.py t@@ -30,9 +30,9 @@ from __future__ import unicode_literals import six from electrum.i18n import _ -import PyQt4 -from PyQt4.QtGui import * -from PyQt4.QtCore import * +import PyQt5 +from PyQt5.QtGui import * +from PyQt5.QtCore import * from .util import * from .history_list import HistoryList (DIR) diff --git a/gui/qt/amountedit.py b/gui/qt/amountedit.py t@@ -5,8 +5,9 @@ from __future__ import print_function from __future__ import unicode_literals import six -from PyQt4.QtCore import * -from PyQt4.QtGui import * +from PyQt5.QtCore import * +from PyQt5.QtGui import * +from PyQt5.QtWidgets import (QLineEdit, QStyle, QStyleOptionFrame) from decimal import Decimal from electrum.util import format_satoshis_plain t@@ -59,7 +60,7 @@ class AmountEdit(MyLineEdit): def paintEvent(self, event): QLineEdit.paintEvent(self, event) if self.base_unit: - panel = QStyleOptionFrameV2() + panel = QStyleOptionFrame() self.initStyleOption(panel) textRect = self.style().subElementRect(QStyle.SE_LineEditContents, panel, self) textRect.adjust(2, 0, -10, 0) (DIR) diff --git a/gui/qt/console.py b/gui/qt/console.py t@@ -8,8 +8,9 @@ import six import sys, os, re import traceback, platform -from PyQt4 import QtCore -from PyQt4 import QtGui +from PyQt5 import QtCore +from PyQt5 import QtGui +from PyQt5 import QtWidgets from electrum import util t@@ -21,9 +22,9 @@ else: MONOSPACE_FONT = 'monospace' -class Console(QtGui.QPlainTextEdit): +class Console(QtWidgets.QPlainTextEdit): def __init__(self, prompt='>> ', startup_message='', parent=None): - QtGui.QPlainTextEdit.__init__(self, parent) + QtWidgets.QPlainTextEdit.__init__(self, parent) self.prompt = prompt self.history = [] t@@ -315,8 +316,8 @@ welcome_message = ''' ''' if __name__ == '__main__': - app = QtGui.QApplication(sys.argv) + app = QtWidgets.QApplication(sys.argv) console = Console(startup_message=welcome_message) console.updateNamespace({'myVar1' : app, 'myVar2' : 1234}) - console.show(); + console.show() sys.exit(app.exec_()) (DIR) diff --git a/gui/qt/contact_list.py b/gui/qt/contact_list.py t@@ -35,8 +35,10 @@ from electrum.bitcoin import is_address from electrum.util import block_explorer_URL, format_satoshis, format_time, age from electrum.plugins import run_hook from electrum.paymentrequest import PR_UNPAID, PR_PAID, PR_UNKNOWN, PR_EXPIRED -from PyQt4.QtGui import * -from PyQt4.QtCore import * +from PyQt5.QtGui import * +from PyQt5.QtCore import * +from PyQt5.QtWidgets import ( + QAbstractItemView, QFileDialog, QMenu, QTreeWidgetItem) from .util import MyTreeWidget, pr_tooltips, pr_icons t@@ -59,7 +61,7 @@ class ContactList(MyTreeWidget): def import_contacts(self): wallet_folder = self.parent.get_wallet_folder() - filename = QFileDialog.getOpenFileName(self.parent, "Select your wallet file", wallet_folder) + filename, __ = QFileDialog.getOpenFileName(self.parent, "Select your wallet file", wallet_folder) if not filename: return self.parent.contacts.import_file(filename) (DIR) diff --git a/gui/qt/fee_slider.py b/gui/qt/fee_slider.py t@@ -6,10 +6,11 @@ from __future__ import unicode_literals import six from electrum.i18n import _ -import PyQt4 -from PyQt4.QtGui import * -from PyQt4.QtCore import * -import PyQt4.QtCore as QtCore +import PyQt5 +from PyQt5.QtGui import * +from PyQt5.QtCore import * +import PyQt5.QtCore as QtCore +from PyQt5.QtWidgets import QSlider, QToolTip import threading (DIR) diff --git a/gui/qt/installwizard.py b/gui/qt/installwizard.py t@@ -7,9 +7,9 @@ import six import sys import os -from PyQt4.QtGui import * -from PyQt4.QtCore import * -import PyQt4.QtCore as QtCore +from PyQt5.QtGui import * +from PyQt5.QtCore import * +import PyQt5.QtCore as QtCore import electrum from electrum import Wallet, WalletStorage t@@ -101,6 +101,9 @@ def wizard_dialog(func): # WindowModalDialog must come first as it overrides show_error class InstallWizard(QDialog, MessageBoxMixin, BaseWizard): + accept_signal = pyqtSignal() + synchronized_signal = pyqtSignal(str) + def __init__(self, config, app, plugins, storage): BaseWizard.__init__(self, config, storage) QDialog.__init__(self, None) t@@ -111,7 +114,7 @@ class InstallWizard(QDialog, MessageBoxMixin, BaseWizard): self.plugins = plugins self.language_for_seed = config.get('language') self.setMinimumSize(600, 400) - self.connect(self, QtCore.SIGNAL('accept'), self.accept) + self.accept_signal.connect(self.accept) self.title = QLabel() self.main_widget = QWidget() self.back_button = QPushButton(_("Back"), self) t@@ -176,7 +179,7 @@ class InstallWizard(QDialog, MessageBoxMixin, BaseWizard): wallet_folder = os.path.dirname(self.storage.path) def on_choose(): - path = QFileDialog.getOpenFileName(self, "Select your wallet file", wallet_folder) + path, __ = QFileDialog.getOpenFileName(self, "Select your wallet file", wallet_folder) if path: self.name_e.setText(path) t@@ -227,11 +230,11 @@ class InstallWizard(QDialog, MessageBoxMixin, BaseWizard): self.storage.decrypt(password) break except InvalidPassword as e: - QMessageBox.information(None, _('Error'), str(e), _('OK')) + QMessageBox.information(None, _('Error'), str(e)) continue except BaseException as e: traceback.print_exc(file=sys.stdout) - QMessageBox.information(None, _('Error'), str(e), _('OK')) + QMessageBox.information(None, _('Error'), str(e)) return path = self.storage.path t@@ -408,8 +411,8 @@ class InstallWizard(QDialog, MessageBoxMixin, BaseWizard): msg = _("Recovery successful") else: msg = _("No transactions found for this seed") - self.emit(QtCore.SIGNAL('synchronized'), msg) - self.connect(self, QtCore.SIGNAL('synchronized'), self.show_message) + self.synchronized_signal.emit(msg) + self.synchronized_signal.connect(self.show_message) t = threading.Thread(target = task) t.daemon = True t.start() t@@ -436,7 +439,7 @@ class InstallWizard(QDialog, MessageBoxMixin, BaseWizard): self.run(action) def terminate(self): - self.emit(QtCore.SIGNAL('accept')) + self.accept_signal.emit() def waiting_dialog(self, task, msg): self.please_wait.setText(MSG_GENERATING_WAIT) (DIR) diff --git a/gui/qt/invoice_list.py b/gui/qt/invoice_list.py t@@ -40,7 +40,7 @@ class InvoiceList(MyTreeWidget): def __init__(self, parent): MyTreeWidget.__init__(self, parent, self.create_menu, [_('Expires'), _('Requestor'), _('Description'), _('Amount'), _('Status')], 2) self.setSortingEnabled(True) - self.header().setResizeMode(1, QHeaderView.Interactive) + self.header().setSectionResizeMode(1, QHeaderView.Interactive) self.setColumnWidth(1, 200) def on_update(self): t@@ -64,7 +64,7 @@ class InvoiceList(MyTreeWidget): def import_invoices(self): wallet_folder = self.parent.get_wallet_folder() - filename = QFileDialog.getOpenFileName(self.parent, "Select your wallet file", wallet_folder) + filename, __ = QFileDialog.getOpenFileName(self.parent, "Select your wallet file", wallet_folder) if not filename: return self.parent.invoices.import_file(filename) (DIR) diff --git a/gui/qt/main_window.py b/gui/qt/main_window.py t@@ -33,10 +33,10 @@ from decimal import Decimal import base64 from functools import partial -import PyQt4 -from PyQt4.QtGui import * -from PyQt4.QtCore import * -import PyQt4.QtCore as QtCore +import PyQt5 +from PyQt5.QtGui import * +from PyQt5.QtCore import * +import PyQt5.QtCore as QtCore from electrum.util import bh2u, bfh from . import icons_rc t@@ -81,7 +81,7 @@ class StatusBarButton(QPushButton): self.setIconSize(QSize(25,25)) def onPress(self, checked=False): - '''Drops the unwanted PyQt4 "checked" argument''' + '''Drops the unwanted PyQt5 "checked" argument''' self.func() def keyPressEvent(self, e): t@@ -94,6 +94,15 @@ from electrum.paymentrequest import PR_UNPAID, PR_PAID, PR_UNKNOWN, PR_EXPIRED class ElectrumWindow(QMainWindow, MessageBoxMixin, PrintError): + payment_request_ok_signal = pyqtSignal() + payment_request_error_signal = pyqtSignal() + new_fx_quotes_signal = pyqtSignal() + new_fx_history_signal = pyqtSignal() + network_signal = pyqtSignal(str, object) + alias_received_signal = pyqtSignal() + computing_privkeys_signal = pyqtSignal() + show_privkeys_signal = pyqtSignal() + def __init__(self, gui_object, wallet): QMainWindow.__init__(self) t@@ -167,13 +176,13 @@ class ElectrumWindow(QMainWindow, MessageBoxMixin, PrintError): for i in range(wrtabs.count()): QShortcut(QKeySequence("Alt+" + str(i + 1)), self, lambda i=i: wrtabs.setCurrentIndex(i)) - self.connect(self, QtCore.SIGNAL('payment_request_ok'), self.payment_request_ok) - self.connect(self, QtCore.SIGNAL('payment_request_error'), self.payment_request_error) + self.payment_request_ok_signal.connect(self.payment_request_ok) + self.payment_request_error_signal.connect(self.payment_request_error) self.history_list.setFocus(True) # network callbacks if self.network: - self.connect(self, QtCore.SIGNAL('network'), self.on_network_qt) + self.network_signal.connect(self.on_network_qt) interests = ['updated', 'new_transaction', 'status', 'banner', 'verified', 'fee'] # To avoid leaking references to "self" that prevent the t@@ -185,8 +194,8 @@ class ElectrumWindow(QMainWindow, MessageBoxMixin, PrintError): self.console.showMessage(self.network.banner) self.network.register_callback(self.on_quotes, ['on_quotes']) self.network.register_callback(self.on_history, ['on_history']) - self.connect(self, SIGNAL('new_fx_quotes'), self.on_fx_quotes) - self.connect(self, SIGNAL('new_fx_history'), self.on_fx_history) + self.new_fx_quotes_signal.connect(self.on_fx_quotes) + self.new_fx_history_signal.connect(self.on_fx_history) # update fee slider in case we missed the callback self.fee_slider.update() t@@ -195,7 +204,7 @@ class ElectrumWindow(QMainWindow, MessageBoxMixin, PrintError): self.fetch_alias() def on_history(self, b): - self.emit(SIGNAL('new_fx_history')) + self.new_fx_history_signal.emit() def on_fx_history(self): self.history_list.refresh_headers() t@@ -203,7 +212,7 @@ class ElectrumWindow(QMainWindow, MessageBoxMixin, PrintError): self.address_list.update() def on_quotes(self, b): - self.emit(SIGNAL('new_fx_quotes')) + self.new_fx_quotes_signal.emit() def on_fx_quotes(self): self.update_status() t@@ -275,17 +284,18 @@ class ElectrumWindow(QMainWindow, MessageBoxMixin, PrintError): def on_network(self, event, *args): if event == 'updated': self.need_update.set() - self.emit(QtCore.SIGNAL('updated'), event, *args) + self.gui_object.network_updated_signal_obj.network_updated_signal \ + .emit(event, args) elif event == 'new_transaction': self.tx_notifications.append(args[0]) elif event in ['status', 'banner', 'verified', 'fee']: # Handle in GUI thread - self.emit(QtCore.SIGNAL('network'), event, *args) + self.network_signal.emit(event, args) else: self.print_error("unexpected network message:", event, args) - def on_network_qt(self, event, *args): + def on_network_qt(self, event, args=None): # Handle a network message in the GUI thread if event == 'status': self.update_status() t@@ -307,7 +317,7 @@ class ElectrumWindow(QMainWindow, MessageBoxMixin, PrintError): alias = str(alias) def f(): self.alias_info = self.contacts.resolve_openalias(alias) - self.emit(SIGNAL('alias_received')) + self.alias_received_signal.emit() t = threading.Thread(target=f) t.setDaemon(True) t.start() t@@ -380,7 +390,7 @@ class ElectrumWindow(QMainWindow, MessageBoxMixin, PrintError): def open_wallet(self): wallet_folder = self.get_wallet_folder() - filename = QFileDialog.getOpenFileName(self, "Select your wallet file", wallet_folder) + filename, __ = QFileDialog.getOpenFileName(self, "Select your wallet file", wallet_folder) if not filename: return self.gui_object.new_window(filename) t@@ -389,7 +399,7 @@ class ElectrumWindow(QMainWindow, MessageBoxMixin, PrintError): def backup_wallet(self): path = self.wallet.storage.path wallet_folder = os.path.dirname(path) - filename = QFileDialog.getSaveFileName(self, _('Enter a filename for the copy of your wallet'), wallet_folder) + filename, __ = QFileDialog.getSaveFileName(self, _('Enter a filename for the copy of your wallet'), wallet_folder) if not filename: return t@@ -574,7 +584,7 @@ class ElectrumWindow(QMainWindow, MessageBoxMixin, PrintError): # custom wrappers for getOpenFileName and getSaveFileName, that remember the path selected by the user def getOpenFileName(self, title, filter = ""): directory = self.config.get('io_dir', os.path.expanduser('~')) - fileName = QFileDialog.getOpenFileName(self, title, directory, filter) + fileName, __ = QFileDialog.getOpenFileName(self, title, directory, filter) if fileName and directory != os.path.dirname(fileName): self.config.set_key('io_dir', os.path.dirname(fileName), True) return fileName t@@ -582,13 +592,13 @@ class ElectrumWindow(QMainWindow, MessageBoxMixin, PrintError): def getSaveFileName(self, title, filename, filter = ""): directory = self.config.get('io_dir', os.path.expanduser('~')) path = os.path.join( directory, filename ) - fileName = QFileDialog.getSaveFileName(self, title, path, filter) + fileName, __ = QFileDialog.getSaveFileName(self, title, path, filter) if fileName and directory != os.path.dirname(fileName): self.config.set_key('io_dir', os.path.dirname(fileName), True) return fileName def connect_slots(self, sender): - self.connect(sender, QtCore.SIGNAL('timersignal'), self.timer_actions) + sender.timer_signal.connect(self.timer_actions) def timer_actions(self): # Note this runs in the GUI thread t@@ -1499,9 +1509,9 @@ class ElectrumWindow(QMainWindow, MessageBoxMixin, PrintError): def on_pr(self, request): self.payment_request = request if self.payment_request.verify(self.contacts): - self.emit(SIGNAL('payment_request_ok')) + self.payment_request_ok_signal.emit() else: - self.emit(SIGNAL('payment_request_error')) + self.payment_request_error_signal.emit() def pay_to_URI(self, URI): if not URI: t@@ -1558,7 +1568,7 @@ class ElectrumWindow(QMainWindow, MessageBoxMixin, PrintError): w.searchable_list = l vbox = QVBoxLayout() w.setLayout(vbox) - vbox.setMargin(0) + vbox.setContentsMargins(0, 0, 0, 0) vbox.setSpacing(0) vbox.addWidget(l) buttons = QWidget() t@@ -2121,16 +2131,16 @@ class ElectrumWindow(QMainWindow, MessageBoxMixin, PrintError): if done: break private_keys[addr] = "\n".join(self.wallet.get_private_key(addr, password)) - d.emit(SIGNAL('computing_privkeys')) - d.emit(SIGNAL('show_privkeys')) + self.computing_privkeys_signal.emit() + self.show_privkeys_signal.emit() def show_privkeys(): s = "\n".join( map( lambda x: x[0] + "\t"+ x[1], private_keys.items())) e.setText(s) b.setEnabled(True) - d.connect(d, QtCore.SIGNAL('computing_privkeys'), lambda: e.setText("Please wait... %d/%d"%(len(private_keys),len(addresses)))) - d.connect(d, QtCore.SIGNAL('show_privkeys'), show_privkeys) + self.computing_privkeys_signal.connect(lambda: e.setText("Please wait... %d/%d"%(len(private_keys),len(addresses)))) + self.show_privkeys_signal.connect(show_privkeys) threading.Thread(target=privkeys_thread).start() if not d.exec_(): t@@ -2470,7 +2480,7 @@ class ElectrumWindow(QMainWindow, MessageBoxMixin, PrintError): if alias: self.fetch_alias() set_alias_color() - self.connect(self, SIGNAL('alias_received'), set_alias_color) + self.alias_received_signal.connect(set_alias_color) alias_e.editingFinished.connect(on_alias_edit) id_widgets.append((alias_label, alias_e)) t@@ -2730,7 +2740,7 @@ class ElectrumWindow(QMainWindow, MessageBoxMixin, PrintError): if self.fx: self.fx.timeout = 0 - self.disconnect(self, SIGNAL('alias_received'), set_alias_color) + self.alias_received_signal.disconnect(set_alias_color) run_hook('close_settings_dialog') if self.need_restart: (DIR) diff --git a/gui/qt/network_dialog.py b/gui/qt/network_dialog.py t@@ -31,9 +31,9 @@ from __future__ import unicode_literals import socket import six -from PyQt4.QtGui import * -from PyQt4.QtCore import * -import PyQt4.QtCore as QtCore +from PyQt5.QtGui import * +from PyQt5.QtCore import * +import PyQt5.QtCore as QtCore from electrum.i18n import _ from electrum.network import DEFAULT_PORTS t@@ -45,19 +45,21 @@ protocol_names = ['TCP', 'SSL'] protocol_letters = 'ts' class NetworkDialog(QDialog): - def __init__(self, network, config): + def __init__(self, network, config, network_updated_signal_obj): QDialog.__init__(self) self.setWindowTitle(_('Network')) self.setMinimumSize(500, 20) self.nlayout = NetworkChoiceLayout(network, config) + self.network_updated_signal_obj = network_updated_signal_obj vbox = QVBoxLayout(self) vbox.addLayout(self.nlayout.layout()) vbox.addLayout(Buttons(CloseButton(self))) - self.connect(self, QtCore.SIGNAL('updated'), self.on_update) + self.network_updated_signal_obj.network_updated_signal.connect( + self.on_update) network.register_callback(self.on_network, ['updated', 'interfaces']) def on_network(self, event, *args): - self.emit(QtCore.SIGNAL('updated'), event, *args) + self.network_updated_signal_obj.network_updated_signal.emit(event, args) def on_update(self): self.nlayout.update() t@@ -97,7 +99,7 @@ class NodesListWidget(QTreeWidget): # on 'enter' we show the menu pt = self.visualItemRect(item).bottomLeft() pt.setX(50) - self.emit(SIGNAL('customContextMenuRequested(const QPoint&)'), pt) + self.customContextMenuRequested.emit(pt) def update(self, network): self.clear() t@@ -125,8 +127,8 @@ class NodesListWidget(QTreeWidget): h = self.header() h.setStretchLastSection(False) - h.setResizeMode(0, QHeaderView.Stretch) - h.setResizeMode(1, QHeaderView.ResizeToContents) + h.setSectionResizeMode(0, QHeaderView.Stretch) + h.setSectionResizeMode(1, QHeaderView.ResizeToContents) class ServerListWidget(QTreeWidget): t@@ -163,7 +165,7 @@ class ServerListWidget(QTreeWidget): # on 'enter' we show the menu pt = self.visualItemRect(item).bottomLeft() pt.setX(50) - self.emit(SIGNAL('customContextMenuRequested(const QPoint&)'), pt) + self.customContextMenuRequested.emit(pt) def update(self, servers, protocol, use_tor): self.clear() t@@ -179,8 +181,8 @@ class ServerListWidget(QTreeWidget): h = self.header() h.setStretchLastSection(False) - h.setResizeMode(0, QHeaderView.Stretch) - h.setResizeMode(1, QHeaderView.ResizeToContents) + h.setSectionResizeMode(0, QHeaderView.Stretch) + h.setSectionResizeMode(1, QHeaderView.ResizeToContents) class NetworkChoiceLayout(object): t@@ -251,13 +253,13 @@ class NetworkChoiceLayout(object): self.proxy_password.editingFinished.connect(self.set_proxy) self.check_disable_proxy() - self.proxy_mode.connect(self.proxy_mode, SIGNAL('currentIndexChanged(int)'), self.check_disable_proxy) + self.proxy_mode.currentIndexChanged.connect(self.check_disable_proxy) - self.proxy_mode.connect(self.proxy_mode, SIGNAL('currentIndexChanged(int)'), self.proxy_settings_changed) - self.proxy_host.connect(self.proxy_host, SIGNAL('textEdited(QString)'), self.proxy_settings_changed) - self.proxy_port.connect(self.proxy_port, SIGNAL('textEdited(QString)'), self.proxy_settings_changed) - self.proxy_user.connect(self.proxy_user, SIGNAL('textEdited(QString)'), self.proxy_settings_changed) - self.proxy_password.connect(self.proxy_password, SIGNAL('textEdited(QString)'), self.proxy_settings_changed) + self.proxy_mode.currentIndexChanged.connect(self.proxy_settings_changed) + self.proxy_host.textEdited.connect(self.proxy_settings_changed) + self.proxy_port.textEdited.connect(self.proxy_settings_changed) + self.proxy_user.textEdited.connect(self.proxy_settings_changed) + self.proxy_password.textEdited.connect(self.proxy_settings_changed) self.tor_cb = QCheckBox(_("Use Tor Proxy")) self.tor_cb.setIcon(QIcon(":icons/tor_logo.png")) (DIR) diff --git a/gui/qt/password_dialog.py b/gui/qt/password_dialog.py t@@ -28,8 +28,8 @@ from __future__ import print_function from __future__ import unicode_literals import six -from PyQt4.QtGui import * -from PyQt4.QtCore import * +from PyQt5.QtGui import * +from PyQt5.QtCore import * from electrum.i18n import _ from .util import * import re (DIR) diff --git a/gui/qt/paytoedit.py b/gui/qt/paytoedit.py t@@ -28,8 +28,9 @@ from __future__ import print_function from __future__ import unicode_literals import six -from PyQt4.QtCore import * -from PyQt4.QtGui import * +from PyQt5.QtCore import * +from PyQt5.QtGui import * +from PyQt5.QtWidgets import QCompleter, QPlainTextEdit from .qrtextedit import ScanQRTextEdit import re (DIR) diff --git a/gui/qt/qrcodewidget.py b/gui/qt/qrcodewidget.py t@@ -4,9 +4,11 @@ from __future__ import print_function from __future__ import unicode_literals import six -from PyQt4.QtGui import * -from PyQt4.QtCore import * -import PyQt4.QtGui as QtGui +from PyQt5.QtGui import * +from PyQt5.QtCore import * +import PyQt5.QtGui as QtGui +from PyQt5.QtWidgets import ( + QApplication, QVBoxLayout, QTextEdit, QHBoxLayout, QPushButton, QWidget) import os import qrcode t@@ -95,6 +97,7 @@ class QRDialog(WindowModalDialog): vbox = QVBoxLayout() qrw = QRCodeWidget(data) + qscreen = QApplication.primaryScreen() vbox.addWidget(qrw, 1) if show_text: text = QTextEdit() t@@ -109,12 +112,12 @@ class QRDialog(WindowModalDialog): filename = os.path.join(config.path, "qrcode.png") def print_qr(): - p = QPixmap.grabWindow(qrw.winId()) + p = qscreen.grabWindow(qrw.winId()) p.save(filename, 'png') self.show_message(_("QR code saved to file") + " " + filename) def copy_to_clipboard(): - p = QPixmap.grabWindow(qrw.winId()) + p = qscreen.grabWindow(qrw.winId()) p.save(filename, 'png') QApplication.clipboard().setImage(QImage(filename)) self.show_message(_("QR code copied to clipboard")) (DIR) diff --git a/gui/qt/qrtextedit.py b/gui/qt/qrtextedit.py t@@ -6,8 +6,9 @@ from __future__ import unicode_literals import six from electrum.i18n import _ from electrum.plugins import run_hook -from PyQt4.QtGui import * -from PyQt4.QtCore import * +from PyQt5.QtGui import * +from PyQt5.QtCore import * +from PyQt5.QtWidgets import QFileDialog from .util import ButtonsTextEdit, MessageBoxMixin t@@ -45,7 +46,7 @@ class ScanQRTextEdit(ButtonsTextEdit, MessageBoxMixin): run_hook('scan_text_edit', self) def file_input(self): - fileName = QFileDialog.getOpenFileName(self, 'select file') + fileName, __ = QFileDialog.getOpenFileName(self, 'select file') if not fileName: return with open(fileName, "r") as f: (DIR) diff --git a/gui/qt/qrwindow.py b/gui/qt/qrwindow.py t@@ -32,10 +32,11 @@ import re import platform from decimal import Decimal -from PyQt4.QtGui import * -from PyQt4.QtCore import * -import PyQt4.QtCore as QtCore -import PyQt4.QtGui as QtGui +from PyQt5.QtGui import * +from PyQt5.QtCore import * +import PyQt5.QtCore as QtCore +import PyQt5.QtGui as QtGui +from PyQt5.QtWidgets import (QHBoxLayout, QVBoxLayout, QLabel, QWidget) from electrum_gui.qt.qrcodewidget import QRCodeWidget from electrum.i18n import _ (DIR) diff --git a/gui/qt/request_list.py b/gui/qt/request_list.py t@@ -33,8 +33,9 @@ from electrum.i18n import _ from electrum.util import block_explorer_URL, format_satoshis, format_time, age from electrum.plugins import run_hook from electrum.paymentrequest import PR_UNPAID, PR_PAID, PR_UNKNOWN, PR_EXPIRED -from PyQt4.QtGui import * -from PyQt4.QtCore import * +from PyQt5.QtGui import * +from PyQt5.QtCore import * +from PyQt5.QtWidgets import QTreeWidgetItem, QMenu from .util import MyTreeWidget, pr_tooltips, pr_icons t@@ -53,7 +54,7 @@ class RequestList(MyTreeWidget): def item_changed(self, item): if item is None: return - if not self.isItemSelected(item): + if not item.isSelected(): return addr = str(item.text(1)) req = self.wallet.receive_requests[addr] (DIR) diff --git a/gui/qt/seed_dialog.py b/gui/qt/seed_dialog.py t@@ -28,8 +28,8 @@ from __future__ import print_function from __future__ import unicode_literals import six -from PyQt4.QtGui import * -from PyQt4.QtCore import * +from PyQt5.QtGui import * +from PyQt5.QtCore import * from electrum.i18n import _ from .util import * (DIR) diff --git a/gui/qt/transaction_dialog.py b/gui/qt/transaction_dialog.py t@@ -32,10 +32,10 @@ import copy import datetime import json -import PyQt4 -from PyQt4.QtGui import * -from PyQt4.QtCore import * -import PyQt4.QtCore as QtCore +import PyQt5 +from PyQt5.QtGui import * +from PyQt5.QtCore import * +import PyQt5.QtCore as QtCore from electrum import transaction from electrum.bitcoin import base_encode (DIR) diff --git a/gui/qt/util.py b/gui/qt/util.py t@@ -15,8 +15,9 @@ from collections import namedtuple from functools import partial from electrum.i18n import _ -from PyQt4.QtGui import * -from PyQt4.QtCore import * +from PyQt5.QtGui import * +from PyQt5.QtCore import * +from PyQt5.QtWidgets import * if platform.system() == 'Windows': MONOSPACE_FONT = 'Lucida Console' t@@ -57,10 +58,11 @@ expiration_values = [ class Timer(QThread): stopped = False + timer_signal = pyqtSignal() def run(self): while not self.stopped: - self.emit(SIGNAL('timersignal')) + self.timer_signal.emit() time.sleep(0.5) def stop(self): t@@ -111,7 +113,7 @@ class HelpLabel(QLabel): self.font = QFont() def mouseReleaseEvent(self, x): - QMessageBox.information(self, 'Help', self.help_text, 'OK') + QMessageBox.information(self, 'Help', self.help_text) def enterEvent(self, event): self.font.setUnderline(True) t@@ -135,7 +137,7 @@ class HelpButton(QPushButton): self.clicked.connect(self.onclick) def onclick(self): - QMessageBox.information(self, 'Help', self.help_text, 'OK') + QMessageBox.information(self, 'Help', self.help_text) class Buttons(QHBoxLayout): def __init__(self, *buttons): t@@ -349,7 +351,7 @@ def filename_field(parent, config, defaultname, select_msg): def func(): text = filename_e.text() _filter = "*.csv" if text.endswith(".csv") else "*.json" if text.endswith(".json") else None - p = QFileDialog.getSaveFileName(None, select_msg, text, _filter) + p, __ = QFileDialog.getSaveFileName(None, select_msg, text, _filter) if p: filename_e.setText(p) t@@ -405,7 +407,7 @@ class MyTreeWidget(QTreeWidget): self.header().setStretchLastSection(False) for col in range(len(headers)): sm = QHeaderView.Stretch if col == self.stretch_column else QHeaderView.ResizeToContents - self.header().setResizeMode(col, sm) + self.header().setSectionResizeMode(col, sm) def editItem(self, item, column): if column in self.editable_columns: t@@ -436,13 +438,12 @@ class MyTreeWidget(QTreeWidget): # on 'enter' we show the menu pt = self.visualItemRect(item).bottomLeft() pt.setX(50) - self.emit(SIGNAL('customContextMenuRequested(const QPoint&)'), pt) + self.customContextMenuRequested.emit(pt) def createEditor(self, parent, option, index): self.editor = QStyledItemDelegate.createEditor(self.itemDelegate(), parent, option, index) - self.editor.connect(self.editor, SIGNAL("editingFinished()"), - self.editing_finished) + self.editor.editingFinished.connect(self.editing_finished) return self.editor def editing_finished(self): t@@ -603,6 +604,6 @@ class TaskThread(QThread): if __name__ == "__main__": app = QApplication([]) - t = WaitingDialog(None, 'testing ...', lambda: [time.sleep(1)], lambda x: QMessageBox.information(None, 'done', "done", _('OK'))) + t = WaitingDialog(None, 'testing ...', lambda: [time.sleep(1)], lambda x: QMessageBox.information(None, 'done', "done")) t.start() app.exec_() (DIR) diff --git a/lib/plot.py b/lib/plot.py t@@ -1,4 +1,4 @@ -from PyQt4.QtGui import * +from PyQt5.QtGui import * from electrum.i18n import _ t@@ -9,7 +9,7 @@ from electrum.util import format_satoshis from electrum.bitcoin import COIN import matplotlib -matplotlib.use('Qt4Agg') +matplotlib.use('Qt5Agg') import matplotlib.pyplot as plt import matplotlib.dates as md from matplotlib.patches import Ellipse (DIR) diff --git a/plugins/audio_modem/qt.py b/plugins/audio_modem/qt.py t@@ -10,8 +10,9 @@ from electrum_gui.qt.util import WaitingDialog, EnterButton, WindowModalDialog from electrum.util import print_msg, print_error from electrum.i18n import _ -from PyQt4.QtGui import * -from PyQt4.QtCore import * +from PyQt5.QtGui import * +from PyQt5.QtCore import * +from PyQt5.QtWidgets import (QComboBox, QGridLayout, QLabel, QPushButton) try: import amodem.audio (DIR) diff --git a/plugins/cosigner_pool/qt.py b/plugins/cosigner_pool/qt.py t@@ -28,8 +28,9 @@ import threading import time from xmlrpc.client import ServerProxy -from PyQt4.QtGui import * -from PyQt4.QtCore import * +from PyQt5.QtGui import * +from PyQt5.QtCore import * +from PyQt5.QtWidgets import QPushButton from electrum import bitcoin, util from electrum import transaction t@@ -82,19 +83,23 @@ class Listener(util.DaemonThread): if message: self.received.add(keyhash) self.print_error("received message for", keyhash) - self.parent.obj.emit(SIGNAL("cosigner:receive"), keyhash, - message) + self.parent.obj.cosigner_receive_signal.emit( + keyhash, message) # poll every 30 seconds time.sleep(30) +class QReceiveSignalObject(QObject): + cosigner_receive_signal = pyqtSignal(object, object) + + class Plugin(BasePlugin): def __init__(self, parent, config, name): BasePlugin.__init__(self, parent, config, name) self.listener = None - self.obj = QObject() - self.obj.connect(self.obj, SIGNAL('cosigner:receive'), self.on_receive) + self.obj = QReceiveSignalObject() + self.obj.cosigner_receive_signal.connect(self.on_receive) self.keys = [] self.cosigner_list = [] (DIR) diff --git a/plugins/digitalbitbox/qt.py b/plugins/digitalbitbox/qt.py t@@ -1,4 +1,4 @@ -from PyQt4.Qt import (QInputDialog, QLineEdit) +from PyQt5.QtWidgets import (QInputDialog, QLineEdit) from ..hw_wallet.qt import QtHandlerBase, QtPluginBase from .digitalbitbox import DigitalBitboxPlugin (DIR) diff --git a/plugins/email_requests/qt.py b/plugins/email_requests/qt.py t@@ -35,10 +35,11 @@ from email.mime.multipart import MIMEMultipart from email.mime.base import MIMEBase from email.encoders import encode_base64 -from PyQt4.QtGui import * -from PyQt4.QtCore import * -import PyQt4.QtCore as QtCore -import PyQt4.QtGui as QtGui +from PyQt5.QtGui import * +from PyQt5.QtCore import * +import PyQt5.QtCore as QtCore +import PyQt5.QtGui as QtGui +from PyQt5.QtWidgets import (QVBoxLayout, QLabel, QGridLayout, QLineEdit) from electrum.plugins import BasePlugin, hook from electrum.paymentrequest import PaymentRequest t@@ -102,6 +103,10 @@ class Processor(threading.Thread): s.quit() +class QEmailSignalObject(QObject): + email_new_invoice_signal = pyqtSignal() + + class Plugin(BasePlugin): def fullname(self): t@@ -121,13 +126,13 @@ class Plugin(BasePlugin): if self.imap_server and self.username and self.password: self.processor = Processor(self.imap_server, self.username, self.password, self.on_receive) self.processor.start() - self.obj = QObject() - self.obj.connect(self.obj, SIGNAL('email:new_invoice'), self.new_invoice) + self.obj = QEmailSignalObject() + self.obj.email_new_invoice_signal.connect(self.new_invoice) def on_receive(self, pr_str): self.print_error('received payment request') self.pr = PaymentRequest(pr_str) - self.obj.emit(SIGNAL('email:new_invoice')) + self.obj.email_new_invoice_signal.emit() def new_invoice(self): self.parent.invoices.add(self.pr) (DIR) diff --git a/plugins/greenaddress_instant/qt.py b/plugins/greenaddress_instant/qt.py t@@ -28,7 +28,7 @@ import urllib import sys import requests -from PyQt4.QtGui import QApplication, QPushButton +from PyQt5.QtWidgets import QApplication, QPushButton from electrum.plugins import BasePlugin, hook from electrum.i18n import _ (DIR) diff --git a/plugins/hw_wallet/qt.py b/plugins/hw_wallet/qt.py t@@ -26,7 +26,7 @@ import threading -from PyQt4.Qt import QVBoxLayout, QLabel, SIGNAL +from PyQt5.Qt import QVBoxLayout, QLabel from electrum_gui.qt.password_dialog import PasswordDialog, PW_PASSPHRASE from electrum_gui.qt.util import * (DIR) diff --git a/plugins/labels/qt.py b/plugins/labels/qt.py t@@ -1,7 +1,8 @@ from functools import partial -from PyQt4.QtGui import * -from PyQt4.QtCore import * +from PyQt5.QtGui import * +from PyQt5.QtCore import * +from PyQt5.QtWidgets import (QHBoxLayout, QLabel, QVBoxLayout) from electrum.plugins import hook from electrum.i18n import _ t@@ -12,11 +13,15 @@ from electrum_gui.qt.util import WindowModalDialog, OkButton from .labels import LabelsPlugin +class QLabelsSignalObject(QObject): + labels_changed_signal = pyqtSignal(object) + + class Plugin(LabelsPlugin): def __init__(self, *args): LabelsPlugin.__init__(self, *args) - self.obj = QObject() + self.obj = QLabelsSignalObject() def requires_settings(self): return True t@@ -47,14 +52,14 @@ class Plugin(LabelsPlugin): return bool(d.exec_()) def on_pulled(self, wallet): - self.obj.emit(SIGNAL('labels_changed'), wallet) + self.obj.labels_changed_signal.emit(wallet) def done_processing(self, dialog, result): dialog.show_message(_("Your labels have been synchronised.")) @hook def on_new_window(self, window): - window.connect(window.app, SIGNAL('labels_changed'), window.update_tabs) + self.obj.labels_changed_signal.connect(window.update_tabs) self.start_wallet(window.wallet) @hook (DIR) diff --git a/plugins/ledger/auth2fa.py b/plugins/ledger/auth2fa.py t@@ -1,8 +1,9 @@ from binascii import hexlify, unhexlify import threading -from PyQt4.Qt import (QDialog, QInputDialog, QLineEdit, QTextEdit, QVBoxLayout, QLabel, SIGNAL) -import PyQt4.QtCore as QtCore +from PyQt5.Qt import (QDialog, QInputDialog, QLineEdit, QTextEdit, QVBoxLayout, QLabel) +import PyQt5.QtCore as QtCore +from PyQt5.QtWidgets import * from electrum.i18n import _ from electrum_gui.qt.util import * (DIR) diff --git a/plugins/ledger/qt.py b/plugins/ledger/qt.py t@@ -1,8 +1,8 @@ import threading -from PyQt4.Qt import (QDialog, QInputDialog, QLineEdit, - QVBoxLayout, QLabel, SIGNAL) -import PyQt4.QtCore as QtCore +from PyQt5.Qt import (QDialog, QInputDialog, QLineEdit, + QVBoxLayout, QLabel) +import PyQt5.QtCore as QtCore from electrum.i18n import _ from .ledger import LedgerPlugin (DIR) diff --git a/plugins/trezor/qt_generic.py b/plugins/trezor/qt_generic.py t@@ -1,9 +1,9 @@ from functools import partial import threading -from PyQt4.Qt import Qt -from PyQt4.Qt import QGridLayout, QInputDialog, QPushButton -from PyQt4.Qt import QVBoxLayout, QLabel, SIGNAL +from PyQt5.Qt import Qt +from PyQt5.Qt import QGridLayout, QInputDialog, QPushButton +from PyQt5.Qt import QVBoxLayout, QLabel from electrum_gui.qt.util import * from .plugin import TIM_NEW, TIM_RECOVER, TIM_MNEMONIC from ..hw_wallet.qt import QtHandlerBase, QtPluginBase t@@ -377,7 +377,7 @@ class SettingsDialog(WindowModalDialog): def change_homescreen(): from PIL import Image # FIXME dialog = QFileDialog(self, _("Choose Homescreen")) - filename = dialog.getOpenFileName() + filename, __ = dialog.getOpenFileName() if filename: im = Image.open(str(filename)) if im.size != (hs_cols, hs_rows): (DIR) diff --git a/plugins/trustedcoin/qt.py b/plugins/trustedcoin/qt.py t@@ -28,8 +28,8 @@ from threading import Thread import re from decimal import Decimal -from PyQt4.QtGui import * -from PyQt4.QtCore import * +from PyQt5.QtGui import * +from PyQt5.QtCore import * from electrum_gui.qt.util import * from electrum_gui.qt.qrcodewidget import QRCodeWidget t@@ -40,8 +40,16 @@ from electrum.plugins import hook from .trustedcoin import TrustedCoinPlugin, server +class QTOSSignalObject(QObject): + two_factor_tos_signal = pyqtSignal() + + class Plugin(TrustedCoinPlugin): + def __init__(self, parent, config, name): + super().__init__(parent, config, name) + self.tos_signal_obj = QTOSSignalObject() + @hook def on_new_window(self, window): wallet = window.wallet t@@ -196,7 +204,7 @@ class Plugin(TrustedCoinPlugin): def request_TOS(): tos = server.get_terms_of_service() self.TOS = tos - window.emit(SIGNAL('twofactor:TOS')) + self.tos_signal_obj.two_factor_tos_signal.emit() def on_result(): tos_e.setText(self.TOS) t@@ -204,7 +212,7 @@ class Plugin(TrustedCoinPlugin): def set_enabled(): next_button.setEnabled(re.match(regexp,email_e.text()) is not None) - window.connect(window, SIGNAL('twofactor:TOS'), on_result) + self.tos_signal_obj.two_factor_tos_signal.connect(on_result) t = Thread(target=request_TOS) t.setDaemon(True) t.start() (DIR) diff --git a/plugins/virtualkeyboard/qt.py b/plugins/virtualkeyboard/qt.py t@@ -1,4 +1,5 @@ -from PyQt4.QtGui import * +from PyQt5.QtGui import * +from PyQt5.QtWidgets import (QVBoxLayout, QGridLayout, QPushButton) from electrum.plugins import BasePlugin, hook from electrum.i18n import _ import random (DIR) diff --git a/setup-release.py b/setup-release.py t@@ -34,7 +34,7 @@ if sys.platform == 'darwin': setup_requires=['py2app'], app=[mainscript], options=dict(py2app=dict(argv_emulation=False, - includes=['PyQt4.QtCore', 'PyQt4.QtGui', 'PyQt4.QtWebKit', 'PyQt4.QtNetwork', 'sip'], + includes=['PyQt5.QtCore', 'PyQt5.QtGui', 'PyQt5.QtWebKit', 'PyQt5.QtNetwork', 'sip'], packages=['lib', 'gui', 'plugins'], iconfile='electrum.icns', plist=plist,