installwizard: set network to None if offline
[electrum-nvc.git] / gui / qt / installwizard.py
1 from PyQt4.QtGui import *
2 from PyQt4.QtCore import *
3 import PyQt4.QtCore as QtCore
4
5 from electrum.i18n import _
6 from electrum import Wallet, mnemonic
7
8 from seed_dialog import SeedDialog
9 from network_dialog import NetworkDialog
10 from util import *
11 from amountedit import AmountEdit
12
13 import sys
14 import threading
15
16 class InstallWizard(QDialog):
17
18     def __init__(self, config, network, storage):
19         QDialog.__init__(self)
20         self.config = config
21         self.network = network
22         self.storage = storage
23         self.setMinimumSize(575, 400)
24         self.setWindowTitle('Electrum')
25         self.connect(self, QtCore.SIGNAL('accept'), self.accept)
26
27         self.stack = QStackedLayout()
28         self.setLayout(self.stack)
29
30
31     def set_layout(self, layout):
32         w = QWidget()
33         w.setLayout(layout)
34         self.stack.setCurrentIndex(self.stack.addWidget(w))
35
36
37     def restore_or_create(self):
38
39         grid = QGridLayout()
40         grid.setSpacing(5)
41
42         msg = _("Electrum could not find an existing wallet.") + "\n\n" \
43             + _("What do you want to do?") + "\n"
44         label = QLabel(msg)
45         label.setWordWrap(True)
46         grid.addWidget(label, 0, 0)
47
48         gb = QGroupBox()
49
50         b1 = QRadioButton(gb)
51         b1.setText(_("Create new wallet"))
52         b1.setChecked(True)
53
54         b2 = QRadioButton(gb)
55         b2.setText(_("Restore an existing wallet from its seed"))
56
57         b3 = QRadioButton(gb)
58         b3.setText(_("Create a watching-only version of an existing wallet"))
59
60         grid.addWidget(b1,1,0)
61         grid.addWidget(b2,2,0)
62         grid.addWidget(b3,3,0)
63
64         vbox = QVBoxLayout()
65         self.set_layout(vbox)
66
67         vbox.addLayout(grid)
68         vbox.addStretch(1)
69         vbox.addLayout(ok_cancel_buttons(self, _('Next')))
70
71         if not self.exec_():
72             return
73         
74         if b1.isChecked():
75             answer = 'create'
76         elif b2.isChecked():
77             answer = 'restore'
78         else:
79             answer = 'watching'
80
81         return answer
82
83
84     def verify_seed(self, wallet):
85         r = self.seed_dialog(False)
86         if not r:
87             return
88
89         if r != wallet.get_mnemonic(None):
90             QMessageBox.warning(None, _('Error'), _('Incorrect seed'), _('OK'))
91             return False
92         else:
93             return True
94
95
96     def seed_dialog(self, is_restore=True):
97
98         vbox = QVBoxLayout()
99         if is_restore:
100             msg = _("Please enter your wallet seed.") + "\n"
101         else:
102             msg = _("Your seed is important!") \
103                 + "\n" + _("To make sure that you have properly saved your seed, please retype it here.")
104         
105         logo = QLabel()
106         logo.setPixmap(QPixmap(":icons/seed.png").scaledToWidth(56))
107         logo.setMaximumWidth(60)
108
109         label = QLabel(msg)
110         label.setWordWrap(True)
111
112         seed_e = QTextEdit()
113         seed_e.setMaximumHeight(100)
114
115         vbox.addWidget(label)
116
117         grid = QGridLayout()
118         grid.addWidget(logo, 0, 0)
119         grid.addWidget(seed_e, 0, 1)
120
121         vbox.addLayout(grid)
122
123         vbox.addStretch(1)
124         vbox.addLayout(ok_cancel_buttons(self, _('Next')))
125
126         self.set_layout(vbox)
127         if not self.exec_():
128             return
129
130         seed = unicode(seed_e.toPlainText())
131
132         if not seed:
133             QMessageBox.warning(None, _('Error'), _('No seed'), _('OK'))
134             return
135
136         return seed
137
138
139
140     def waiting_dialog(self, task, msg= _("Electrum is generating your addresses, please wait.")):
141         def target():
142             task()
143             self.emit(QtCore.SIGNAL('accept'))
144
145         vbox = QVBoxLayout()
146         self.waiting_label = QLabel(msg)
147         vbox.addWidget(self.waiting_label)
148         self.set_layout(vbox)
149         t = threading.Thread(target = target)
150         t.start()
151         self.exec_()
152
153
154
155     def mpk_dialog(self):
156
157         vbox = QVBoxLayout()
158         vbox.addWidget(QLabel(_("Please enter your master public key.")))
159
160         grid = QGridLayout()
161         grid.setSpacing(8)
162
163         label = QLabel(_("Key")) 
164         grid.addWidget(label, 0, 0)
165         mpk_e = QTextEdit()
166         mpk_e.setMaximumHeight(100)
167         grid.addWidget(mpk_e, 0, 1)
168
169         label = QLabel(_("Chain")) 
170         #grid.addWidget(label, 1, 0)
171         chain_e = QTextEdit()
172         chain_e.setMaximumHeight(100)
173         #grid.addWidget(chain_e, 1, 1)
174
175         vbox.addLayout(grid)
176
177         vbox.addStretch(1)
178         vbox.addLayout(ok_cancel_buttons(self, _('Next')))
179
180         self.set_layout(vbox)
181         if not self.exec_(): 
182             return None
183
184         mpk = str(mpk_e.toPlainText()).strip()
185         chain = str(chain_e.toPlainText()).strip()
186         return mpk, chain
187
188
189     def network_dialog(self):
190         
191         grid = QGridLayout()
192         grid.setSpacing(5)
193
194         label = QLabel(_("Electrum communicates with remote servers to get information about your transactions and addresses. The servers all fulfil the same purpose only differing in hardware. In most cases you simply want to let Electrum pick one at random if you have a preference though feel free to select a server manually.") + "\n\n" \
195                       + _("How do you want to connect to a server:")+" ")
196         label.setWordWrap(True)
197         grid.addWidget(label, 0, 0)
198
199         gb = QGroupBox()
200
201         b1 = QRadioButton(gb)
202         b1.setText(_("Auto connect"))
203         b1.setChecked(True)
204
205         b2 = QRadioButton(gb)
206         b2.setText(_("Select server manually"))
207
208         #b3 = QRadioButton(gb)
209         #b3.setText(_("Stay offline"))
210
211         grid.addWidget(b1,1,0)
212         grid.addWidget(b2,2,0)
213         #grid.addWidget(b3,3,0)
214
215         vbox = QVBoxLayout()
216         vbox.addLayout(grid)
217
218         vbox.addStretch(1)
219         vbox.addLayout(ok_cancel_buttons(self, _('Next')))
220
221         self.set_layout(vbox)
222         if not self.exec_():
223             return
224         
225         if b2.isChecked():
226             return NetworkDialog(self.network, self.config, None).do_exec()
227
228         elif b1.isChecked():
229             self.config.set_key('auto_cycle', True, True)
230             return
231
232         else:
233             self.config.set_key("server", None, True)
234             self.config.set_key('auto_cycle', False, True)
235             return
236         
237
238
239     def show_seed(self, wallet):
240         from seed_dialog import make_seed_dialog
241         vbox = make_seed_dialog(wallet.get_mnemonic(None), wallet.imported_keys)
242         vbox.addLayout(ok_cancel_buttons(self, _("Next")))
243         self.set_layout(vbox)
244         return self.exec_()
245
246
247     def password_dialog(self, wallet):
248         msg = _("Please choose a password to encrypt your wallet keys.")+'\n'\
249               +_("Leave these fields empty if you want to disable encryption.")
250         from password_dialog import make_password_dialog, run_password_dialog
251         self.set_layout( make_password_dialog(self, wallet, msg) )
252         return run_password_dialog(self, wallet, self)
253
254
255     def run(self):
256
257         action = self.restore_or_create()
258         if not action: 
259             return
260
261         wallet = Wallet(self.storage)
262         gap = self.config.get('gap_limit', 5)
263         if gap != 5:
264             wallet.gap_limit = gap
265             wallet.storage.put('gap_limit', gap, True)
266
267         if action == 'create':
268             wallet.init_seed(None)
269             if not self.show_seed(wallet):
270                 return
271             if not self.verify_seed(wallet):
272                 return
273             ok, old_password, password = self.password_dialog(wallet)
274             def create():
275                 wallet.save_seed(password)
276                 wallet.synchronize()  # generate first addresses offline
277             self.waiting_dialog(create)
278
279
280         elif action == 'restore':
281             seed = self.seed_dialog()
282             if not seed:
283                 return
284             try:
285                 wallet.init_seed(seed)
286             except Exception:
287                 import traceback
288                 traceback.print_exc(file=sys.stdout)
289                 QMessageBox.warning(None, _('Error'), _('Incorrect seed'), _('OK'))
290                 return
291
292             ok, old_password, password = self.password_dialog(wallet)
293             wallet.save_seed(password)
294
295
296         elif action == 'watching':
297             mpk = self.mpk_dialog()
298             if not mpk:
299                 return
300             wallet.seed = ''
301             wallet.create_watching_only_wallet(mpk)
302
303         else: raise
304                 
305         #if not self.config.get('server'):
306         if self.network:
307             if self.network.interfaces:
308                 self.network_dialog()
309             else:
310                 QMessageBox.information(None, _('Warning'), _('You are offline'), _('OK'))
311                 self.network.stop()
312                 self.network = None
313
314         # start wallet threads
315         wallet.start_threads(self.network)
316
317         if action == 'restore':
318
319             self.waiting_dialog(lambda: wallet.restore(self.waiting_label.setText))
320
321             if self.network:
322                 if wallet.is_found():
323                     QMessageBox.information(None, _('Information'), _("Recovery successful"), _('OK'))
324                 else:
325                     QMessageBox.information(None, _('Information'), _("No transactions found for this seed"), _('OK'))
326             else:
327                 QMessageBox.information(None, _('Information'), _("This wallet was restored offline. It may contain more addresses than displayed."), _('OK'))
328
329         return wallet