Merge branch '2.0'
[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
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 = seed_e.toPlainText()
131         seed = unicode(seed.toLower())
132
133         if not seed:
134             QMessageBox.warning(None, _('Error'), _('No seed'), _('OK'))
135             return
136
137         return seed
138
139
140
141     def waiting_dialog(self, task, msg= _("Electrum is generating your addresses, please wait.")):
142         def target():
143             task()
144             self.emit(QtCore.SIGNAL('accept'))
145
146         vbox = QVBoxLayout()
147         self.waiting_label = QLabel(msg)
148         vbox.addWidget(self.waiting_label)
149         self.set_layout(vbox)
150         t = threading.Thread(target = target)
151         t.start()
152         self.exec_()
153
154
155
156     def mpk_dialog(self):
157
158         vbox = QVBoxLayout()
159         vbox.addWidget(QLabel(_("Please enter your master public key.")))
160
161         grid = QGridLayout()
162         grid.setSpacing(8)
163
164         label = QLabel(_("Key")) 
165         grid.addWidget(label, 0, 0)
166         mpk_e = QTextEdit()
167         mpk_e.setMaximumHeight(100)
168         grid.addWidget(mpk_e, 0, 1)
169
170         label = QLabel(_("Chain")) 
171         #grid.addWidget(label, 1, 0)
172         chain_e = QTextEdit()
173         chain_e.setMaximumHeight(100)
174         #grid.addWidget(chain_e, 1, 1)
175
176         vbox.addLayout(grid)
177
178         vbox.addStretch(1)
179         vbox.addLayout(ok_cancel_buttons(self, _('Next')))
180
181         self.set_layout(vbox)
182         if not self.exec_(): 
183             return None
184
185         mpk = str(mpk_e.toPlainText()).strip()
186         chain = str(chain_e.toPlainText()).strip()
187         return mpk, chain
188
189
190     def network_dialog(self):
191         
192         grid = QGridLayout()
193         grid.setSpacing(5)
194
195         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" \
196                       + _("How do you want to connect to a server:")+" ")
197         label.setWordWrap(True)
198         grid.addWidget(label, 0, 0)
199
200         gb = QGroupBox()
201
202         b1 = QRadioButton(gb)
203         b1.setText(_("Auto connect"))
204         b1.setChecked(True)
205
206         b2 = QRadioButton(gb)
207         b2.setText(_("Select server manually"))
208
209         #b3 = QRadioButton(gb)
210         #b3.setText(_("Stay offline"))
211
212         grid.addWidget(b1,1,0)
213         grid.addWidget(b2,2,0)
214         #grid.addWidget(b3,3,0)
215
216         vbox = QVBoxLayout()
217         vbox.addLayout(grid)
218
219         vbox.addStretch(1)
220         vbox.addLayout(ok_cancel_buttons(self, _('Next')))
221
222         self.set_layout(vbox)
223         if not self.exec_():
224             return
225         
226         if b2.isChecked():
227             return NetworkDialog(self.network, self.config, None).do_exec()
228
229         elif b1.isChecked():
230             self.config.set_key('auto_cycle', True, True)
231             return
232
233         else:
234             self.config.set_key("server", None, True)
235             self.config.set_key('auto_cycle', False, True)
236             return
237         
238
239
240     def show_seed(self, wallet):
241         from seed_dialog import make_seed_dialog
242         vbox = make_seed_dialog(wallet.get_mnemonic(None), wallet.imported_keys)
243         vbox.addLayout(ok_cancel_buttons(self, _("Next")))
244         self.set_layout(vbox)
245         return self.exec_()
246
247
248     def password_dialog(self, wallet):
249         msg = _("Please choose a password to encrypt your wallet keys.")+'\n'\
250               +_("Leave these fields empty if you want to disable encryption.")
251         from password_dialog import make_password_dialog, run_password_dialog
252         self.set_layout( make_password_dialog(self, wallet, msg) )
253         return run_password_dialog(self, wallet, self)
254
255
256     def run(self):
257
258         action = self.restore_or_create()
259         if not action: 
260             return
261
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 = Wallet(self.storage)
269             wallet.init_seed(None)
270             if not self.show_seed(wallet):
271                 return
272             if not self.verify_seed(wallet):
273                 return
274             ok, old_password, password = self.password_dialog(wallet)
275             def create():
276                 wallet.save_seed(password)
277                 wallet.synchronize()  # generate first addresses offline
278             self.waiting_dialog(create)
279
280         elif action == 'restore':
281             seed = self.seed_dialog()
282             if not seed:
283                 return
284             wallet = Wallet.from_seed(seed, self.storage)
285             ok, old_password, password = self.password_dialog(wallet)
286             wallet.save_seed(password)
287
288         elif action == 'watching':
289             mpk = self.mpk_dialog()
290             if not mpk:
291                 return
292             wallet.seed = ''
293             wallet.create_watching_only_wallet(mpk)
294
295         else: raise
296                 
297         #if not self.config.get('server'):
298         if self.network:
299             if self.network.interfaces:
300                 self.network_dialog()
301             else:
302                 QMessageBox.information(None, _('Warning'), _('You are offline'), _('OK'))
303                 self.network.stop()
304                 self.network = None
305
306         # start wallet threads
307         wallet.start_threads(self.network)
308
309         if action == 'restore':
310
311             self.waiting_dialog(lambda: wallet.restore(self.waiting_label.setText))
312
313             if self.network:
314                 if wallet.is_found():
315                     QMessageBox.information(None, _('Information'), _("Recovery successful"), _('OK'))
316                 else:
317                     QMessageBox.information(None, _('Information'), _("No transactions found for this seed"), _('OK'))
318             else:
319                 QMessageBox.information(None, _('Information'), _("This wallet was restored offline. It may contain more addresses than displayed."), _('OK'))
320
321         return wallet