Browse Source

better code organization

function parameters should be lowercase

Fix crash on invalid labels import

Added invoice exporting and reduced duplicate code

Better exception handling

removed json module import

some more cleanup

Cleaned up some stuff

Added exporting contacts
3.1
Abdussamad 7 years ago
committed by SomberNight
parent
commit
5997c18aef
  1. 19
      gui/qt/contact_list.py
  2. 15
      gui/qt/invoice_list.py
  3. 40
      gui/qt/main_window.py
  4. 23
      gui/qt/util.py
  5. 22
      lib/contacts.py
  6. 32
      lib/paymentrequest.py
  7. 35
      lib/util.py

19
gui/qt/contact_list.py

@ -23,16 +23,17 @@
# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
import webbrowser
import os
from electrum.i18n import _
from electrum.bitcoin import is_address
from electrum.util import block_explorer_URL, FileImportFailed
from electrum.util import block_explorer_URL
from electrum.plugins import run_hook
from PyQt5.QtGui import *
from PyQt5.QtCore import *
from PyQt5.QtWidgets import (
QAbstractItemView, QFileDialog, QMenu, QTreeWidgetItem)
from .util import MyTreeWidget
from .util import MyTreeWidget, import_meta_gui, export_meta_gui
class ContactList(MyTreeWidget):
@ -53,15 +54,10 @@ class ContactList(MyTreeWidget):
self.parent.set_contact(item.text(0), item.text(1))
def import_contacts(self):
wallet_folder = self.parent.get_wallet_folder()
filename, __ = QFileDialog.getOpenFileName(self.parent, "Select your wallet file", wallet_folder)
if not filename:
return
try:
self.parent.contacts.import_file(filename)
except FileImportFailed as e:
self.parent.show_message(str(e))
self.on_update()
import_meta_gui(self.parent, _('contacts'), self.parent.contacts.import_file, self.on_update)
def export_contacts(self):
export_meta_gui(self.parent, _('contacts'), self.parent.contacts.export_file)
def create_menu(self, position):
menu = QMenu()
@ -69,6 +65,7 @@ class ContactList(MyTreeWidget):
if not selected:
menu.addAction(_("New contact"), lambda: self.parent.new_contact_dialog())
menu.addAction(_("Import file"), lambda: self.import_contacts())
menu.addAction(_("Export file"), lambda: self.export_contacts())
else:
names = [item.text(0) for item in selected]
keys = [item.text(1) for item in selected]

15
gui/qt/invoice_list.py

@ -25,7 +25,7 @@
from .util import *
from electrum.i18n import _
from electrum.util import format_time, FileImportFailed
from electrum.util import format_time
class InvoiceList(MyTreeWidget):
@ -57,15 +57,10 @@ class InvoiceList(MyTreeWidget):
self.parent.invoices_label.setVisible(len(inv_list))
def import_invoices(self):
wallet_folder = self.parent.get_wallet_folder()
filename, __ = QFileDialog.getOpenFileName(self.parent, "Select your wallet file", wallet_folder)
if not filename:
return
try:
self.parent.invoices.import_file(filename)
except FileImportFailed as e:
self.parent.show_message(str(e))
self.on_update()
import_meta_gui(self.parent, _('invoices'), self.parent.invoices.import_file, self.on_update)
def export_invoices(self):
export_meta_gui(self.parent, _('invoices'), self.parent.invoices.export_file)
def create_menu(self, position):
menu = QMenu()

40
gui/qt/main_window.py

@ -61,7 +61,7 @@ from .fee_slider import FeeSlider
from .util import *
from electrum.util import profiler
from electrum.util import profiler, export_meta, import_meta
class StatusBarButton(QPushButton):
def __init__(self, icon, tooltip, func):
@ -484,8 +484,10 @@ class ElectrumWindow(QMainWindow, MessageBoxMixin, PrintError):
contacts_menu = wallet_menu.addMenu(_("Contacts"))
contacts_menu.addAction(_("&New"), self.new_contact_dialog)
contacts_menu.addAction(_("Import"), lambda: self.contact_list.import_contacts())
contacts_menu.addAction(_("Export"), lambda: self.contact_list.export_contacts())
invoices_menu = wallet_menu.addMenu(_("Invoices"))
invoices_menu.addAction(_("Import"), lambda: self.invoice_list.import_invoices())
invoices_menu.addAction(_("Export"), lambda: self.invoice_list.export_invoices())
wallet_menu.addSeparator()
wallet_menu.addAction(_("Find"), self.toggle_search).setShortcut(QKeySequence("Ctrl+F"))
@ -2417,29 +2419,23 @@ class ElectrumWindow(QMainWindow, MessageBoxMixin, PrintError):
f.write(json.dumps(pklist, indent = 4))
def do_import_labels(self):
labelsFile = self.getOpenFileName(_("Open labels file"), "*.json")
if not labelsFile: return
try:
with open(labelsFile, 'r') as f:
data = f.read()
for key, value in json.loads(data).items():
self.wallet.set_label(key, value)
self.show_message(_("Your labels were imported from") + " '%s'" % str(labelsFile))
except (IOError, os.error) as reason:
self.show_critical(_("Electrum was unable to import your labels.") + "\n" + str(reason))
self.address_list.update()
self.history_list.update()
def import_labels(path):
#TODO: Import labels validation
def import_labels_validate(data):
return data
def import_labels_assign(data):
for key, value in data.items():
self.wallet.set_label(key, value)
import_meta(path, import_labels_validate, import_labels_assign)
def on_import():
self.address_list.update()
self.history_list.update()
import_meta_gui(self, _('labels'), import_labels, on_import)
def do_export_labels(self):
labels = self.wallet.labels
try:
fileName = self.getSaveFileName(_("Select file to save your labels"), 'electrum_labels.json', "*.json")
if fileName:
with open(fileName, 'w+') as f:
json.dump(labels, f, indent=4, sort_keys=True)
self.show_message(_("Your labels were exported to") + " '%s'" % str(fileName))
except (IOError, os.error) as reason:
self.show_critical(_("Electrum was unable to export your labels.") + "\n" + str(reason))
def export_labels(filename):
export_meta(self.wallet.labels, filename)
export_meta_gui(self, _('labels'), export_labels)
def sweep_key_dialog(self):
d = WindowModalDialog(self, title=_('Sweep private keys'))

23
gui/qt/util.py

@ -11,6 +11,7 @@ from PyQt5.QtGui import *
from PyQt5.QtCore import *
from PyQt5.QtWidgets import *
from electrum.util import FileImportFailed, FileExportFailed
if platform.system() == 'Windows':
MONOSPACE_FONT = 'Lucida Console'
elif platform.system() == 'Darwin':
@ -674,6 +675,28 @@ class AcceptFileDragDrop:
def onFileAdded(self, fn):
raise NotImplementedError()
def import_meta_gui(electrum_window, title, importer, on_success):
filename = electrum_window.getOpenFileName(_("Open {} file").format(title) , "*.json")
if not filename:
return
try:
importer(filename)
except FileImportFailed as e:
electrum_window.show_critical(str(e))
else:
electrum_window.show_message(_("Your {} were successfully imported" ).format(title))
on_success()
def export_meta_gui(electrum_window, title, exporter):
filename = electrum_window.getSaveFileName(_("Select file to save your {}").format(title), 'electrum_{}.json'.format(title), "*.json")
if not filename:
return
try:
exporter(filename)
except FileExportFailed as e:
electrum_window.show_critical(str(e))
else:
electrum_window.show_message(_("Your {0} were exported to '{1}'").format(title,str(filename)))
if __name__ == "__main__":
app = QApplication([])

22
lib/contacts.py

@ -25,10 +25,11 @@ import dns
import json
import traceback
import sys
import os
from . import bitcoin
from . import dnssec
from .util import FileImportFailed, FileImportFailedEncrypted
from .util import export_meta, import_meta
class Contacts(dict):
@ -51,18 +52,15 @@ class Contacts(dict):
self.storage.put('contacts', dict(self))
def import_file(self, path):
try:
with open(path, 'r') as f:
d = self._validate(json.loads(f.read()))
except json.decoder.JSONDecodeError:
traceback.print_exc(file=sys.stderr)
raise FileImportFailedEncrypted()
except BaseException:
traceback.print_exc(file=sys.stdout)
raise FileImportFailed()
self.update(d)
import_meta(path, self.validate, self.load_meta)
def load_meta(self, data):
self.update(data)
self.save()
def export_file(self, fileName):
export_meta(self, fileName)
def __setitem__(self, key, value):
dict.__setitem__(self, key, value)
self.save()
@ -119,7 +117,7 @@ class Contacts(dict):
except AttributeError:
return None
def _validate(self, data):
def validate(self, data):
for k,v in list(data.items()):
if k == 'contacts':
return self._validate(v)

32
lib/paymentrequest.py

@ -40,7 +40,7 @@ except ImportError:
from . import bitcoin
from . import util
from .util import print_error, bh2u, bfh
from .util import FileImportFailed, FileImportFailedEncrypted
from .util import export_meta, import_meta
from . import transaction
from . import x509
from . import rsakey
@ -468,27 +468,31 @@ class InvoiceStore(object):
continue
def import_file(self, path):
try:
with open(path, 'r') as f:
d = json.loads(f.read())
self.load(d)
except json.decoder.JSONDecodeError:
traceback.print_exc(file=sys.stderr)
raise FileImportFailedEncrypted()
except BaseException:
traceback.print_exc(file=sys.stdout)
raise FileImportFailed()
import_meta(path, self.validate, self.on_import)
#TODO: Invoice import validation
def validate(self, data):
return data
def on_import(self, data):
self.load(data)
self.save()
def save(self):
l = {}
def export_file(self, fileName):
export_meta(self.before_save(), fileName)
def before_save(self):
l= {}
for k, pr in self.invoices.items():
l[k] = {
'hex': bh2u(pr.raw),
'requestor': pr.requestor,
'txid': pr.tx
}
self.storage.put('invoices', l)
return l
def save(self):
self.storage.put('invoices', self.before_save())
def get_status(self, key):
pr = self.get(key)

35
lib/util.py

@ -60,16 +60,18 @@ class InvalidPassword(Exception):
class FileImportFailed(Exception):
def __init__(self, message=''):
self.message = str(message)
def __str__(self):
return _("Failed to import file.")
return _("Failed to import from file.") + "\n" + self.message
class FileExportFailed(Exception):
def __init__(self, reason=''):
self.message = str(reason)
class FileImportFailedEncrypted(FileImportFailed):
def __str__(self):
return (_('Failed to import file.') + ' ' +
_('Perhaps it is encrypted...') + '\n' +
_('Importing encrypted files is not supported.'))
return( _("Failed to export to file.") + "\n" + self.message )
# Throw this exception to unwind the stack like when an error occurs.
# However unlike other exceptions the user won't be informed.
@ -785,3 +787,24 @@ def setup_thread_excepthook():
def versiontuple(v):
return tuple(map(int, (v.split("."))))
def import_meta(path, validater, load_meta):
try:
with open(path, 'r') as f:
d = validater(json.loads(f.read()))
load_meta(d)
#backwards compatibility for JSONDecodeError
except ValueError:
traceback.print_exc(file=sys.stderr)
raise FileImportFailed(_("Invalid JSON code."))
except BaseException as e:
traceback.print_exc(file=sys.stdout)
raise FileImportFailed(e)
def export_meta(meta, fileName):
try:
with open(fileName, 'w+') as f:
json.dump(meta, f, indent=4, sort_keys=True)
except (IOError, os.error) as reason:
traceback.print_exc(file=sys.stderr)
raise FileExportFailed(str(reason))

Loading…
Cancel
Save