Browse Source

[Qt] Add optional update notifications

3.3.3.1
Johann Bauer 6 years ago
committed by ThomasV
parent
commit
34c99c3b36
  1. 43
      electrum/gui/qt/main_window.py
  2. 97
      electrum/gui/qt/util.py
  3. 1
      icons.qrc
  4. BIN
      icons/update.png

43
electrum/gui/qt/main_window.py

@ -226,6 +226,28 @@ class ElectrumWindow(QMainWindow, MessageBoxMixin, PrintError):
gui_object.timer.timeout.connect(self.timer_actions) gui_object.timer.timeout.connect(self.timer_actions)
self.fetch_alias() self.fetch_alias()
# If the option hasn't been set yet
if config.get('check_updates') is None:
choice = QMessageBox.question(self,
"Electrum - " + _("Enable update check"),
_("For security reasons we advise that you always use the latest version of Electrum.") + " " +
_("Would you like to be notified when there is a newer version of Electrum available?"),
QMessageBox.Yes,
QMessageBox.No)
config.set_key('check_updates', choice == QMessageBox.Yes, save=True)
if config.get('check_updates', False):
# The references to both the thread and the window need to be stored somewhere
# to prevent GC from getting in our way.
def on_version_received(v):
if UpdateCheck.is_newer(v):
self.update_check_button.setText(_("Update to Electrum {} is available").format(v))
self.update_check_button.clicked.connect(lambda: self.show_update_check(v))
self.update_check_button.show()
self._update_check_thread = UpdateCheckThread(self)
self._update_check_thread.checked.connect(on_version_received)
self._update_check_thread.start()
def on_history(self, b): def on_history(self, b):
self.wallet.clear_coin_price_cache() self.wallet.clear_coin_price_cache()
self.new_fx_history_signal.emit() self.new_fx_history_signal.emit()
@ -577,6 +599,7 @@ class ElectrumWindow(QMainWindow, MessageBoxMixin, PrintError):
help_menu = menubar.addMenu(_("&Help")) help_menu = menubar.addMenu(_("&Help"))
help_menu.addAction(_("&About"), self.show_about) help_menu.addAction(_("&About"), self.show_about)
help_menu.addAction(_("&Check for updates"), self.show_update_check)
help_menu.addAction(_("&Official website"), lambda: webbrowser.open("https://electrum.org")) help_menu.addAction(_("&Official website"), lambda: webbrowser.open("https://electrum.org"))
help_menu.addSeparator() help_menu.addSeparator()
help_menu.addAction(_("&Documentation"), lambda: webbrowser.open("http://docs.electrum.org/")).setShortcut(QKeySequence.HelpContents) help_menu.addAction(_("&Documentation"), lambda: webbrowser.open("http://docs.electrum.org/")).setShortcut(QKeySequence.HelpContents)
@ -604,6 +627,9 @@ class ElectrumWindow(QMainWindow, MessageBoxMixin, PrintError):
"servers that handle the most complicated parts of the Bitcoin system.") + "\n\n" + "servers that handle the most complicated parts of the Bitcoin system.") + "\n\n" +
_("Uses icons from the Icons8 icon pack (icons8.com)."))) _("Uses icons from the Icons8 icon pack (icons8.com).")))
def show_update_check(self, version=None):
self._update_check = UpdateCheck(self, version)
def show_report_bug(self): def show_report_bug(self):
msg = ' '.join([ msg = ' '.join([
_("Please report any bugs as issues on github:<br/>"), _("Please report any bugs as issues on github:<br/>"),
@ -1998,7 +2024,6 @@ class ElectrumWindow(QMainWindow, MessageBoxMixin, PrintError):
sb = QStatusBar() sb = QStatusBar()
sb.setFixedHeight(35) sb.setFixedHeight(35)
qtVersion = qVersion()
self.balance_label = QLabel("Loading wallet...") self.balance_label = QLabel("Loading wallet...")
self.balance_label.setTextInteractionFlags(Qt.TextSelectableByMouse) self.balance_label.setTextInteractionFlags(Qt.TextSelectableByMouse)
@ -2010,6 +2035,13 @@ class ElectrumWindow(QMainWindow, MessageBoxMixin, PrintError):
self.search_box.hide() self.search_box.hide()
sb.addPermanentWidget(self.search_box) sb.addPermanentWidget(self.search_box)
self.update_check_button = QPushButton("")
self.update_check_button.setFlat(True)
self.update_check_button.setCursor(QCursor(Qt.PointingHandCursor))
self.update_check_button.setIcon(QIcon(":icons/update.png"))
self.update_check_button.hide()
sb.addPermanentWidget(self.update_check_button)
self.lock_icon = QIcon() self.lock_icon = QIcon()
self.password_button = StatusBarButton(self.lock_icon, _("Password"), self.change_password_dialog ) self.password_button = StatusBarButton(self.lock_icon, _("Password"), self.change_password_dialog )
sb.addPermanentWidget(self.password_button) sb.addPermanentWidget(self.password_button)
@ -2905,6 +2937,13 @@ class ElectrumWindow(QMainWindow, MessageBoxMixin, PrintError):
colortheme_combo.currentIndexChanged.connect(on_colortheme) colortheme_combo.currentIndexChanged.connect(on_colortheme)
gui_widgets.append((colortheme_label, colortheme_combo)) gui_widgets.append((colortheme_label, colortheme_combo))
updatecheck_cb = QCheckBox(_("Automatically check for software updates"))
updatecheck_cb.setChecked(self.config.get('check_updates', False))
def on_set_updatecheck(v):
self.config.set_key('check_updates', v == Qt.Checked, save=True)
updatecheck_cb.stateChanged.connect(on_set_updatecheck)
gui_widgets.append((updatecheck_cb, None))
usechange_cb = QCheckBox(_('Use change addresses')) usechange_cb = QCheckBox(_('Use change addresses'))
usechange_cb.setChecked(self.wallet.use_change) usechange_cb.setChecked(self.wallet.use_change)
if not self.config.is_modifiable('use_change'): usechange_cb.setEnabled(False) if not self.config.is_modifiable('use_change'): usechange_cb.setEnabled(False)
@ -3078,7 +3117,7 @@ class ElectrumWindow(QMainWindow, MessageBoxMixin, PrintError):
tabs_info = [ tabs_info = [
(fee_widgets, _('Fees')), (fee_widgets, _('Fees')),
(tx_widgets, _('Transactions')), (tx_widgets, _('Transactions')),
(gui_widgets, _('Appearance')), (gui_widgets, _('General')),
(fiat_widgets, _('Fiat')), (fiat_widgets, _('Fiat')),
(id_widgets, _('Identity')), (id_widgets, _('Identity')),
] ]

97
electrum/gui/qt/util.py

@ -1,8 +1,11 @@
import asyncio
import os.path import os.path
import time import time
import sys import sys
import platform import platform
import queue import queue
import traceback
from distutils.version import StrictVersion
from functools import partial from functools import partial
from typing import NamedTuple, Callable, Optional, TYPE_CHECKING from typing import NamedTuple, Callable, Optional, TYPE_CHECKING
@ -10,8 +13,9 @@ from PyQt5.QtGui import *
from PyQt5.QtCore import * from PyQt5.QtCore import *
from PyQt5.QtWidgets import * from PyQt5.QtWidgets import *
from electrum import version
from electrum.i18n import _, languages from electrum.i18n import _, languages
from electrum.util import FileImportFailed, FileExportFailed from electrum.util import FileImportFailed, FileExportFailed, make_aiohttp_session, PrintError
from electrum.paymentrequest import PR_UNPAID, PR_PAID, PR_EXPIRED from electrum.paymentrequest import PR_UNPAID, PR_PAID, PR_EXPIRED
if TYPE_CHECKING: if TYPE_CHECKING:
@ -819,6 +823,97 @@ class FromList(QTreeWidget):
self.header().setSectionResizeMode(0, sm) self.header().setSectionResizeMode(0, sm)
self.header().setSectionResizeMode(1, sm) self.header().setSectionResizeMode(1, sm)
class UpdateCheck(QWidget, PrintError):
url = "https://electrum.org/version"
download_url = "https://electrum.org/#download"
def __init__(self, main_window, latest_version=None):
self.main_window = main_window
QWidget.__init__(self)
self.setWindowTitle('Electrum - ' + _('Update Check'))
self.content = QVBoxLayout()
self.content.setContentsMargins(*[10]*4)
self.heading_label = QLabel()
self.content.addWidget(self.heading_label)
self.detail_label = QLabel()
self.content.addWidget(self.detail_label)
self.pb = QProgressBar()
self.pb.setMaximum(0)
self.pb.setMinimum(0)
self.content.addWidget(self.pb)
versions = QHBoxLayout()
versions.addWidget(QLabel(_("Current version: {}".format(version.ELECTRUM_VERSION))))
self.latest_version_label = QLabel(_("Latest version: {}".format(" ")))
versions.addWidget(self.latest_version_label)
self.content.addLayout(versions)
self.update_view(latest_version)
self.update_check_thread = UpdateCheckThread(self.main_window)
self.update_check_thread.checked.connect(self.on_version_retrieved)
self.update_check_thread.failed.connect(self.on_retrieval_failed)
self.update_check_thread.start()
close_button = QPushButton(_("Close"))
close_button.clicked.connect(self.close)
self.content.addWidget(close_button)
self.setLayout(self.content)
self.show()
def on_version_retrieved(self, version):
self.update_view(version)
def on_retrieval_failed(self):
self.heading_label.setText('<h2>' + _("Update check failed") + '</h2>')
self.detail_label.setText(_("Sorry, but we were unable to check for updates. Please try again later."))
self.pb.hide()
@staticmethod
def is_newer(latest_version):
return latest_version > StrictVersion(version.ELECTRUM_VERSION)
def update_view(self, latest_version=None):
if latest_version:
self.pb.hide()
self.latest_version_label.setText(_("Latest version: {}".format(latest_version)))
if self.is_newer(latest_version):
self.heading_label.setText('<h2>' + _("There is a new update available") + '</h2>')
url = "<a href='{u}'>{u}</a>".format(u=UpdateCheck.download_url)
self.detail_label.setText(_("You can download the new version from {}.").format(url))
else:
self.heading_label.setText('<h2>' + _("Already up to date") + '</h2>')
self.detail_label.setText(_("You are already on the latest version of Electrum."))
else:
self.heading_label.setText('<h2>' + _("Checking for updates...") + '</h2>')
self.detail_label.setText(_("Please wait while Electrum checks for available updates."))
class UpdateCheckThread(QThread, PrintError):
checked = pyqtSignal(object)
failed = pyqtSignal()
def __init__(self, main_window):
super().__init__()
self.main_window = main_window
async def get_update_info(self):
async with make_aiohttp_session(proxy=self.main_window.network.proxy) as session:
async with session.get(UpdateCheck.url) as result:
return StrictVersion((await result.text()).strip())
def run(self):
try:
self.checked.emit(asyncio.run_coroutine_threadsafe(self.get_update_info(), self.main_window.network.asyncio_loop).result())
except Exception:
self.print_error(traceback.format_exc())
self.failed.emit()
if __name__ == "__main__": if __name__ == "__main__":
app = QApplication([]) app = QApplication([])
t = WaitingDialog(None, 'testing ...', lambda: [time.sleep(1)], lambda x: QMessageBox.information(None, 'done', "done")) t = WaitingDialog(None, 'testing ...', lambda: [time.sleep(1)], lambda x: QMessageBox.information(None, 'done', "done"))

1
icons.qrc

@ -63,6 +63,7 @@
<file>icons/unconfirmed.png</file> <file>icons/unconfirmed.png</file>
<file>icons/unpaid.png</file> <file>icons/unpaid.png</file>
<file>icons/unlock.png</file> <file>icons/unlock.png</file>
<file>icons/update.png</file>
<file>icons/warning.png</file> <file>icons/warning.png</file>
<file>icons/zoom.png</file> <file>icons/zoom.png</file>
</qresource> </qresource>

BIN
icons/update.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Loading…
Cancel
Save