def __init__(self, _config, _network): global wallet, network, contacts network = _network config = _config network.register_callback('updated', update_callback) network.register_callback('connected', update_callback) network.register_callback('disconnected', update_callback) network.register_callback('disconnecting', update_callback) contacts = util.StoreDict(config, 'contacts') storage = WalletStorage(config.get_wallet_path()) if not storage.file_exists: action = self.restore_or_create() if not action: exit() password = droid.dialogGetPassword('Choose a password').result if password: password2 = droid.dialogGetPassword('Confirm password').result if password != password2: modal_dialog('Error','passwords do not match') exit() else: # set to None if it's an empty string password = None if action == 'create': wallet = Wallet(storage) seed = wallet.make_seed() modal_dialog('Your seed is:', seed) wallet.add_seed(seed, password) wallet.create_master_keys(password) wallet.create_main_account(password) elif action == 'restore': seed = self.seed_dialog() if not seed: exit() if not Wallet.is_seed(seed): exit() wallet = Wallet.from_seed(seed, password, storage) else: exit() msg = "Creating wallet" if action == 'create' else "Restoring wallet" droid.dialogCreateSpinnerProgress("Electrum", msg) droid.dialogShow() wallet.start_threads(network) if action == 'restore': wallet.restore(lambda x: None) else: wallet.synchronize() droid.dialogDismiss() droid.vibrate() else: wallet = Wallet(storage) wallet.start_threads(network)
def main(self, url): last_wallet = self.config.get('gui_last_wallet') if last_wallet is not None and self.config.get('wallet_path') is None: if os.path.exists(last_wallet): self.config.cmdline_options['default_wallet_path'] = last_wallet try: storage = WalletStorage(self.config.get_wallet_path()) except BaseException as e: QMessageBox.warning(None, _('Warning'), str(e), _('OK')) self.config.set_key('gui_last_wallet', None) return if storage.file_exists: try: wallet = Wallet(storage) except BaseException as e: QMessageBox.warning(None, _('Warning'), str(e), _('OK')) return action = wallet.get_action() else: action = 'new' if action is not None: wallet = self.run_wizard(storage, action) if not wallet: return else: wallet.start_threads(self.network) # init tray self.dark_icon = self.config.get("dark_icon", False) icon = QIcon(":icons/electrum_dark_icon.png") if self.dark_icon else QIcon(':icons/electrum_light_icon.png') self.tray = QSystemTrayIcon(icon, None) self.tray.setToolTip('Electrum-XMC') self.tray.activated.connect(self.tray_activated) self.build_tray_menu() self.tray.show() # main window self.main_window = w = ElectrumWindow(self.config, self.network, self) self.current_window = self.main_window #lite window self.init_lite() # plugins interact with main window run_hook('init_qt', self) w.load_wallet(wallet) # initial configuration if self.config.get('hide_gui') is True and self.tray.isVisible(): self.main_window.hide() self.lite_window.hide() else: if self.config.get('lite_mode') is True: self.go_lite() else: self.go_full() s = Timer() s.start() self.windows.append(w) if url: self.set_url(url) w.connect_slots(s) signal.signal(signal.SIGINT, lambda *args: self.app.quit()) self.app.exec_() if self.tray: self.tray.hide() # clipboard persistence # see http://www.mail-archive.com/[email protected]/msg17328.html event = QtCore.QEvent(QtCore.QEvent.Clipboard) self.app.sendEvent(self.app.clipboard(), event) w.close_wallet()
class ElectrumGui: def __init__(self, config, network): self.config = config self.network = network storage = WalletStorage(config.get_wallet_path()) if not storage.file_exists: print "Wallet not found. try 'electrum create'" exit() self.wallet = Wallet(storage) self.wallet.start_threads(self.network) self.contacts = StoreDict(self.config, 'contacts') locale.setlocale(locale.LC_ALL, '') self.encoding = locale.getpreferredencoding() self.stdscr = curses.initscr() curses.noecho() curses.cbreak() curses.start_color() curses.use_default_colors() curses.init_pair(1, curses.COLOR_WHITE, curses.COLOR_BLUE) curses.init_pair(2, curses.COLOR_WHITE, curses.COLOR_CYAN) self.stdscr.keypad(1) self.stdscr.border(0) self.maxy, self.maxx = self.stdscr.getmaxyx() self.set_cursor(0) self.w = curses.newwin(10, 50, 5, 5) set_verbosity(False) self.tab = 0 self.pos = 0 self.popup_pos = 0 self.str_recipient = "" self.str_description = "" self.str_amount = "" self.str_fee = "" self.history = None if self.network: self.network.register_callback('updated', self.update) self.network.register_callback('connected', self.refresh) self.network.register_callback('disconnected', self.refresh) self.network.register_callback('disconnecting', self.refresh) self.tab_names = [_("History"), _("Send"), _("Receive"), _("Contacts"), _("Wall")] self.num_tabs = len(self.tab_names) def set_cursor(self, x): try: curses.curs_set(x) except Exception: pass def restore_or_create(self): pass def verify_seed(self): pass def get_string(self, y, x): self.set_cursor(1) curses.echo() self.stdscr.addstr( y, x, " "*20, curses.A_REVERSE) s = self.stdscr.getstr(y,x) curses.noecho() self.set_cursor(0) return s def update(self): self.update_history() if self.tab == 0: self.print_history() self.refresh() def print_history(self): width = [20, 40, 14, 14] delta = (self.maxx - sum(width) - 4)/3 format_str = "%"+"%d"%width[0]+"s"+"%"+"%d"%(width[1]+delta)+"s"+"%"+"%d"%(width[2]+delta)+"s"+"%"+"%d"%(width[3]+delta)+"s" if self.history is None: self.update_history() self.print_list(self.history[::-1], format_str%( _("Date"), _("Description"), _("Amount"), _("Balance"))) def update_history(self): width = [20, 40, 14, 14] delta = (self.maxx - sum(width) - 4)/3 format_str = "%"+"%d"%width[0]+"s"+"%"+"%d"%(width[1]+delta)+"s"+"%"+"%d"%(width[2]+delta)+"s"+"%"+"%d"%(width[3]+delta)+"s" b = 0 self.history = [] for item in self.wallet.get_history(): tx_hash, conf, value, timestamp, balance = item if conf: try: time_str = datetime.datetime.fromtimestamp(timestamp).isoformat(' ')[:-3] except Exception: time_str = "------" else: time_str = 'pending' label, is_default_label = self.wallet.get_label(tx_hash) if len(label) > 40: label = label[0:37] + '...' self.history.append( format_str%( time_str, label, format_satoshis(value, whitespaces=True), format_satoshis(balance, whitespaces=True) ) ) def print_balance(self): if not self.network: msg = _("Offline") elif self.network.is_connected(): if not self.wallet.up_to_date: msg = _("Synchronizing...") else: c, u, x = self.wallet.get_balance() msg = _("Balance")+": %f "%(Decimal(c) / COIN) if u: msg += " [%f unconfirmed]"%(Decimal(u) / COIN) if x: msg += " [%f unmatured]"%(Decimal(x) / COIN) else: msg = _("Not connected") self.stdscr.addstr( self.maxy -1, 3, msg) for i in range(self.num_tabs): self.stdscr.addstr( 0, 2 + 2*i + len(''.join(self.tab_names[0:i])), ' '+self.tab_names[i]+' ', curses.A_BOLD if self.tab == i else 0) self.stdscr.addstr( self.maxy -1, self.maxx-30, ' '.join([_("Settings"), _("Network"), _("Quit")])) def print_contacts(self): messages = map(lambda x: "%20s %45s "%(x[0], x[1][1]), self.contacts.items()) self.print_list(messages, "%19s %15s "%("Key", "Value")) def print_receive(self): fmt = "%-35s %-30s" messages = map(lambda addr: fmt % (addr, self.wallet.labels.get(addr,"")), self.wallet.addresses()) self.print_list(messages, fmt % ("Address", "Label")) def print_edit_line(self, y, label, text, index, size): text += " "*(size - len(text) ) self.stdscr.addstr( y, 2, label) self.stdscr.addstr( y, 15, text, curses.A_REVERSE if self.pos%6==index else curses.color_pair(1)) def print_send_tab(self): self.stdscr.clear() self.print_edit_line(3, _("Pay to"), self.str_recipient, 0, 40) self.print_edit_line(5, _("Description"), self.str_description, 1, 40) self.print_edit_line(7, _("Amount"), self.str_amount, 2, 15) self.print_edit_line(9, _("Fee"), self.str_fee, 3, 15) self.stdscr.addstr( 12, 15, _("[Send]"), curses.A_REVERSE if self.pos%6==4 else curses.color_pair(2)) self.stdscr.addstr( 12, 25, _("[Clear]"), curses.A_REVERSE if self.pos%6==5 else curses.color_pair(2)) def print_banner(self): if self.network: self.print_list( self.network.banner.split('\n')) def print_list(self, list, firstline = None): self.maxpos = len(list) if not self.maxpos: return if firstline: firstline += " "*(self.maxx -2 - len(firstline)) self.stdscr.addstr( 1, 1, firstline ) for i in range(self.maxy-4): msg = list[i] if i < len(list) else "" msg += " "*(self.maxx - 2 - len(msg)) m = msg[0:self.maxx - 2] m = m.encode(self.encoding) self.stdscr.addstr( i+2, 1, m, curses.A_REVERSE if i == (self.pos % self.maxpos) else 0) def refresh(self): if self.tab == -1: return self.stdscr.border(0) self.print_balance() self.stdscr.refresh() def main_command(self): c = self.stdscr.getch() print c if c == curses.KEY_RIGHT: self.tab = (self.tab + 1)%self.num_tabs elif c == curses.KEY_LEFT: self.tab = (self.tab - 1)%self.num_tabs elif c == curses.KEY_DOWN: self.pos +=1 elif c == curses.KEY_UP: self.pos -= 1 elif c == 9: self.pos +=1 # tab elif curses.unctrl(c) in ['^W', '^C', '^X', '^Q']: self.tab = -1 elif curses.unctrl(c) in ['^N']: self.network_dialog() elif curses.unctrl(c) == '^S': self.settings_dialog() else: return c if self.pos<0: self.pos=0 if self.pos>=self.maxpos: self.pos=self.maxpos - 1 def run_tab(self, i, print_func, exec_func): while self.tab == i: self.stdscr.clear() print_func() self.refresh() c = self.main_command() if c: exec_func(c) def run_history_tab(self, c): if c == 10: out = self.run_popup('',["blah","foo"]) def edit_str(self, target, c, is_num=False): # detect backspace if c in [8, 127, 263] and target: target = target[:-1] elif not is_num or curses.unctrl(c) in '0123456789.': target += curses.unctrl(c) return target def run_send_tab(self, c): if self.pos%6 == 0: self.str_recipient = self.edit_str(self.str_recipient, c) if self.pos%6 == 1: self.str_description = self.edit_str(self.str_description, c) if self.pos%6 == 2: self.str_amount = self.edit_str(self.str_amount, c, True) elif self.pos%6 == 3: self.str_fee = self.edit_str(self.str_fee, c, True) elif self.pos%6==4: if c == 10: self.do_send() elif self.pos%6==5: if c == 10: self.do_clear() def run_receive_tab(self, c): if c == 10: out = self.run_popup('Address', ["Edit label", "Freeze", "Prioritize"]) def run_contacts_tab(self, c): if c == 10 and self.contacts: out = self.run_popup('Adress', ["Copy", "Pay to", "Edit label", "Delete"]).get('button') key = self.contacts.keys()[self.pos%len(self.contacts.keys())] if out == "Pay to": self.tab = 1 self.str_recipient = key self.pos = 2 elif out == "Edit label": s = self.get_string(6 + self.pos, 18) if s: self.wallet.labels[address] = s def run_banner_tab(self, c): self.show_message(repr(c)) pass def main(self,url): tty.setraw(sys.stdin) while self.tab != -1: self.run_tab(0, self.print_history, self.run_history_tab) self.run_tab(1, self.print_send_tab, self.run_send_tab) self.run_tab(2, self.print_receive, self.run_receive_tab) self.run_tab(3, self.print_contacts, self.run_contacts_tab) self.run_tab(4, self.print_banner, self.run_banner_tab) tty.setcbreak(sys.stdin) curses.nocbreak() self.stdscr.keypad(0) curses.echo() curses.endwin() def do_clear(self): self.str_amount = '' self.str_recipient = '' self.str_fee = '' self.str_description = '' def do_send(self): if not is_valid(self.str_recipient): self.show_message(_('Invalid xmc address')) return try: amount = int(Decimal(self.str_amount) * COIN) except Exception: self.show_message(_('Invalid Amount')) return try: fee = int(Decimal(self.str_fee) * COIN) except Exception: self.show_message(_('Invalid Fee')) return if self.wallet.use_encryption: password = self.password_dialog() if not password: return else: password = None try: tx = self.wallet.mktx( [(self.str_recipient, amount)], password, self.config, fee) except Exception as e: self.show_message(str(e)) return if self.str_description: self.wallet.labels[tx.hash()] = self.str_description h = self.wallet.send_tx(tx) self.show_message(_("Please wait..."), getchar=False) self.wallet.tx_event.wait() status, msg = self.wallet.receive_tx( h, tx ) if status: self.show_message(_('Payment sent.')) self.do_clear() #self.update_contacts_tab() else: self.show_message(_('Error')) def show_message(self, message, getchar = True): w = self.w w.clear() w.border(0) for i, line in enumerate(message.split('\n')): w.addstr(2+i,2,line) w.refresh() if getchar: c = self.stdscr.getch() def run_popup(self, title, items): return self.run_dialog(title, map(lambda x: {'type':'button','label':x}, items), interval=1, y_pos = self.pos+3) def network_dialog(self): if not self.network: return host, port, protocol, proxy_config, auto_connect = self.network.get_parameters() srv = 'auto-connect' if auto_connect else self.network.default_server out = self.run_dialog('Network', [ {'label':'server', 'type':'str', 'value':srv}, {'label':'proxy', 'type':'str', 'value':self.config.get('proxy', '')}, ], buttons = 1) if out: if out.get('server'): server = out.get('server') auto_connect = server == 'auto-connect' if not auto_connect: try: host, port, protocol = server.split(':') except Exception: self.show_message("Error:" + server + "\nIn doubt, type \"auto-connect\"") return False if out.get('proxy'): proxy = self.parse_proxy_options(out.get('proxy')) else: proxy = None self.network.set_parameters(host, port, protocol, proxy, auto_connect) def settings_dialog(self): out = self.run_dialog('Settings', [ {'label':'Default GUI', 'type':'list', 'choices':['classic','lite','gtk','text'], 'value':self.config.get('gui')}, {'label':'Default fee', 'type':'satoshis', 'value': format_satoshis(self.wallet.fee_per_kb).strip() } ], buttons = 1) if out: if out.get('Default GUI'): self.config.set_key('gui', out['Default GUI'], True) if out.get('Default fee'): fee = int(Decimal(out['Default fee']) * COIN) self.config.set_key('fee_per_kb', fee, True) def password_dialog(self): out = self.run_dialog('Password', [ {'label':'Password', 'type':'password', 'value':''} ], buttons = 1) return out.get('Password') def run_dialog(self, title, items, interval=2, buttons=None, y_pos=3): self.popup_pos = 0 self.w = curses.newwin( 5 + len(items)*interval + (2 if buttons else 0), 50, y_pos, 5) w = self.w out = {} while True: w.clear() w.border(0) w.addstr( 0, 2, title) num = len(items) numpos = num if buttons: numpos += 2 for i in range(num): item = items[i] label = item.get('label') if item.get('type') == 'list': value = item.get('value','') elif item.get('type') == 'satoshis': value = item.get('value','') elif item.get('type') == 'str': value = item.get('value','') elif item.get('type') == 'password': value = '*'*len(item.get('value','')) else: value = '' if value is None: value = '' if len(value)<20: value += ' '*(20-len(value)) if item.has_key('value'): w.addstr( 2+interval*i, 2, label) w.addstr( 2+interval*i, 15, value, curses.A_REVERSE if self.popup_pos%numpos==i else curses.color_pair(1) ) else: w.addstr( 2+interval*i, 2, label, curses.A_REVERSE if self.popup_pos%numpos==i else 0) if buttons: w.addstr( 5+interval*i, 10, "[ ok ]", curses.A_REVERSE if self.popup_pos%numpos==(numpos-2) else curses.color_pair(2)) w.addstr( 5+interval*i, 25, "[cancel]", curses.A_REVERSE if self.popup_pos%numpos==(numpos-1) else curses.color_pair(2)) w.refresh() c = self.stdscr.getch() if c in [ord('q'), 27]: break elif c in [curses.KEY_LEFT, curses.KEY_UP]: self.popup_pos -= 1 elif c in [curses.KEY_RIGHT, curses.KEY_DOWN]: self.popup_pos +=1 else: i = self.popup_pos%numpos if buttons and c==10: if i == numpos-2: return out elif i == numpos -1: return {} item = items[i] _type = item.get('type') if _type == 'str': item['value'] = self.edit_str(item['value'], c) out[item.get('label')] = item.get('value') elif _type == 'password': item['value'] = self.edit_str(item['value'], c) out[item.get('label')] = item ['value'] elif _type == 'satoshis': item['value'] = self.edit_str(item['value'], c, True) out[item.get('label')] = item.get('value') elif _type == 'list': choices = item.get('choices') try: j = choices.index(item.get('value')) except Exception: j = 0 new_choice = choices[(j + 1)% len(choices)] item['value'] = new_choice out[item.get('label')] = item.get('value') elif _type == 'button': out['button'] = item.get('label') break return out
class ElectrumGui(): def __init__(self, config, network): self.network = network self.config = config def main(self, url=None): storage = WalletStorage(self.config.get_wallet_path()) if not storage.file_exists: action = self.restore_or_create() if not action: exit() self.wallet = wallet = Wallet(storage) gap = self.config.get('gap_limit', 5) if gap != 5: wallet.gap_limit = gap wallet.storage.put('gap_limit', gap, True) if action == 'create': seed = wallet.make_seed() show_seed_dialog(seed, None) r = change_password_dialog(False, None) password = r[2] if r else None wallet.add_seed(seed, password) wallet.create_master_keys(password) wallet.create_main_account(password) wallet.synchronize() # generate first addresses offline elif action == 'restore': seed = self.seed_dialog() if not seed: exit() r = change_password_dialog(False, None) password = r[2] if r else None wallet.add_seed(seed, password) wallet.create_master_keys(password) wallet.create_main_account(password) else: exit() else: self.wallet = Wallet(storage) action = None self.wallet.start_threads(self.network) if action == 'restore': self.restore_wallet(wallet) w = ElectrumWindow(self.wallet, self.config, self.network) if url: w.set_url(url) Gtk.main() def restore_or_create(self): return restore_create_dialog() def seed_dialog(self): return run_recovery_dialog() def network_dialog(self): return run_network_dialog( self.network, parent=None ) def restore_wallet(self, wallet): dialog = Gtk.MessageDialog( parent = None, flags = Gtk.DialogFlags.MODAL, buttons = Gtk.ButtonsType.CANCEL, message_format = "Please wait..." ) dialog.show() def recover_thread( wallet, dialog ): wallet.restore(lambda x:x) GObject.idle_add( dialog.destroy ) thread.start_new_thread( recover_thread, ( wallet, dialog ) ) r = dialog.run() dialog.destroy() if r==Gtk.ResponseType.CANCEL: return False if not wallet.is_found(): show_message("No transactions found for this seed") return True