use QStackedLayout in install wizard
[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(_("Restore wallet from master public key"))
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
240         vbox = make_seed_dialog(wallet.get_mnemonic(None), wallet.imported_keys)
241         vbox.addLayout(ok_cancel_buttons(self, _("Next")))
242
243         self.set_layout(vbox)
244         if not self.exec_():
245             exit()
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
254         run_password_dialog(self, wallet, self)
255
256
257     def run(self):
258
259         action = self.restore_or_create()
260         if not action: exit()
261
262         wallet = Wallet(self.storage)
263         gap = self.config.get('gap_limit', 5)
264         if gap != 5:
265             wallet.gap_limit = gap
266             wallet.storage.put('gap_limit', gap, True)
267
268         if action == 'create':
269             wallet.init_seed(None)
270             self.show_seed(wallet)
271             if self.verify_seed(wallet):
272                 def create():
273                     wallet.save_seed()
274                     wallet.create_accounts()
275                     wallet.synchronize()  # generate first addresses offline
276                 self.waiting_dialog(create)
277             else:
278                 return
279                 
280         elif action == 'restore':
281             # ask for seed and gap.
282             seed = self.seed_dialog()
283             if not seed:
284                 return
285             try:
286                 wallet.init_seed(seed)
287             except:
288                 import traceback
289                 traceback.print_exc(file=sys.stdout)
290                 QMessageBox.warning(None, _('Error'), _('Incorrect seed'), _('OK'))
291                 return
292
293             wallet.save_seed()
294
295         elif action == 'watching':
296             # ask for seed and gap.
297             K, chain = self.mpk_dialog()
298             if not K or not chain:
299                 return
300             wallet.seed = ''
301             wallet.create_watching_only_wallet(chain,K)
302
303
304         else: raise
305                 
306         #if not self.config.get('server'):
307         self.network_dialog()
308
309         # start wallet threads
310         wallet.start_threads(self.network)
311
312         if action == 'restore':
313
314             self.waiting_dialog(lambda: wallet.restore(self.waiting_label.setText))
315
316             if wallet.is_found():
317                 QMessageBox.information(None, _('Information'), _("Recovery successful"), _('OK'))
318             else:
319                 QMessageBox.information(None, _('Information'), _("No transactions found for this seed"), _('OK'))
320             
321             wallet.fill_addressbook()
322
323         self.password_dialog(wallet)
324
325         return wallet