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