Add try/finally for every setOverrideCursor/restoreOverrideCursor.
[bertos.git] / wizard / BToolchainPage.py
1 #!/usr/bin/env python
2 # encoding: utf-8
3 #
4 # This file is part of BeRTOS.
5 #
6 # Bertos is free software; you can redistribute it and/or modify
7 # it under the terms of the GNU General Public License as published by
8 # the Free Software Foundation; either version 2 of the License, or
9 # (at your option) any later version.
10 #
11 # This program is distributed in the hope that it will be useful,
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14 # GNU General Public License for more details.
15 #
16 # You should have received a copy of the GNU General Public License
17 # along with this program; if not, write to the Free Software
18 # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
19 #
20 # As a special exception, you may use this file as part of a free software
21 # library without restriction.  Specifically, if other files instantiate
22 # templates or use macros or inline functions from this file, or you compile
23 # this file and link it with other files to produce an executable, this
24 # file does not by itself cause the resulting executable to be covered by
25 # the GNU General Public License.  This exception does not however
26 # invalidate any other reasons why the executable file might be covered by
27 # the GNU General Public License.
28 #
29 # Copyright 2008 Develer S.r.l. (http://www.develer.com/)
30 #
31 # $Id$
32 #
33 # Author: Lorenzo Berni <duplo@develer.com>
34 #
35
36 import os
37 import collections
38
39 from BWizardPage import *
40 import BToolchainSearch
41 import bertos_utils
42 import qvariant_converter
43
44 from const import *
45
46 class BToolchainPage(BWizardPage):
47     """
48     Page of the wizard that permits to choose the toolchain to use for the
49     project.
50     """
51
52     def __init__(self):
53         BWizardPage.__init__(self, UI_LOCATION + "/toolchain_select.ui")
54         self.setTitle(self.tr("Select toolchain"))
55         self._validation_process = None
56         self._valid_items = []
57
58     ## Overloaded QWizardPage methods. ##
59
60     def isComplete(self):
61         """
62         Overload of the QWizard isComplete method.
63         """
64         if self.pageContent.toolchainList.currentRow() != -1:
65             self.setProjectInfo("TOOLCHAIN",
66                 qvariant_converter.getStringDict(self.pageContent.toolchainList.currentItem().data(Qt.UserRole)))
67             return True
68         else:
69             return False
70
71     ####
72
73     ## Overloaded BWizardPage methods. ##
74
75     def setupUi(self):
76         """
77         Sets up the user interface.
78         """
79         self.pageContent.infoLabel.setVisible(False)
80
81     def connectSignals(self):
82         """
83         Connects the signals with the related slots.
84         """
85         self.connect(self.pageContent.toolchainList, SIGNAL("itemSelectionChanged()"), self.selectionChanged)
86         self.connect(self.pageContent.addButton, SIGNAL("clicked()"), self.addToolchain)
87         self.connect(self.pageContent.removeButton, SIGNAL("clicked()"), self.removeToolchain)
88         self.connect(self.pageContent.searchButton, SIGNAL("clicked()"), self.searchToolchain)
89         self.connect(self.pageContent.checkButton, SIGNAL("clicked()"), self.validateAllToolchains)
90
91     def reloadData(self):
92         """
93         Overload of the BWizard reloadData method.
94         """
95         self._clearList()
96         self.setupUi()
97         self._populateToolchainList()
98         if len(self._valid_items) == 1:
99             self.pageContent.toolchainList.setCurrentItem(self._valid_items[0])
100
101     ####
102
103     ## Slots ##
104
105     def selectionChanged(self):
106         """
107         Slot called when the user click on an entry of the toolchain list.
108         """
109         if self.pageContent.toolchainList.currentRow() != -1:
110             infos = collections.defaultdict(lambda: unicode("not defined"))
111             infos.update(qvariant_converter.getStringDict(self.pageContent.toolchainList.currentItem().data(Qt.UserRole)))
112             self.pageContent.infoLabel.setText("GCC " + infos["version"] + " (" + infos["build"] + ")\nTarget: " + infos["target"] + "\nPath: " + os.path.normpath(infos["path"]))
113             self.pageContent.infoLabel.setVisible(True)
114             if self.isDefaultToolchain(infos):
115                 self.disableRemoveButton()
116             else:
117                 self.enableRemoveButton()
118             self.emit(SIGNAL("completeChanged()"))
119
120     def addToolchain(self):
121         """
122         Slot called when the user adds manually a toolchain.
123         """
124         sel_toolchain = unicode(QFileDialog.getOpenFileName(self, self.tr("Choose the toolchain"), ""))
125         if sel_toolchain != "":
126             item = QListWidgetItem(sel_toolchain)
127             item.setData(Qt.UserRole, qvariant_converter.convertStringDict({"path": sel_toolchain}))
128             self.pageContent.toolchainList.addItem(item)
129             toolchains = self.toolchains()
130             toolchains[sel_toolchain] = False
131             self.setToolchains(toolchains)
132
133     def removeToolchain(self):
134         """
135         Slot called when the user removes manually a toolchain.
136         """
137         if self.pageContent.toolchainList.currentRow() != -1:
138             item = self.pageContent.toolchainList.takeItem(self.pageContent.toolchainList.currentRow())
139             toolchain = qvariant_converter.getStringDict(item.data(Qt.UserRole))["path"]
140             toolchains = self.toolchains()
141             del toolchains[toolchain]
142             self.setToolchains(toolchains)
143
144     def searchToolchain(self):
145         """
146         Slot called when the user clicks on the 'search' button. It opens the
147         toolchain search dialog.
148         """
149         search = BToolchainSearch.BToolchainSearch()
150         self.connect(search, SIGNAL("accepted()"), self._search)
151         search.exec_()
152
153     def validateAllToolchains(self):
154         """
155         Slot called when the user clicks on the validate button. It starts the
156         toolchain validation procedure for all the toolchains.
157         """
158         try:
159             QApplication.instance().setOverrideCursor(Qt.WaitCursor)
160             for i in range(self.pageContent.toolchainList.count()):
161                 self.validateToolchain(i)
162         finally:
163             QApplication.instance().restoreOverrideCursor()
164
165     ####
166
167     def _populateToolchainList(self):
168         """
169         Fills the toolchain list with the toolchains stored in the QSettings.
170         """
171         toolchains = self.toolchains()
172         if os.name == "nt":
173             import winreg_importer
174             stored_toolchains = winreg_importer.getBertosToolchains()
175             for toolchain in stored_toolchains:
176                 toolchains[toolchain] = True
177         sel_toolchain = self.projectInfo("TOOLCHAIN")
178         for key, value in toolchains.items():
179             if os.path.exists(key):
180                 item = QListWidgetItem(key)
181                 item.setData(Qt.UserRole, qvariant_converter.convertStringDict({"path": key}))
182                 self.pageContent.toolchainList.addItem(item)
183                 if sel_toolchain and sel_toolchain["path"] == key:
184                     self.pageContent.toolchainList.setCurrentItem(item)
185                 if value:
186                     self.validateToolchain(self.pageContent.toolchainList.row(item))
187
188     def currentToolchain(self):
189         selected_toolchain = qvariant_converter.getStringDict(self.pageContent.toolchainList.currentItem().data(Qt.UserRole))
190         return selected_toolchain
191
192     def _clearList(self):
193         """
194         Removes all the toolchain from the list.
195         """
196         self.pageContent.toolchainList.clear()
197
198     def _search(self):
199         """
200         Searches for toolchains in the stored directories, and stores them in the
201         QSettings.
202         """
203         dir_list = self.searchDirList()
204         if self.pathSearch():
205             dir_list += [element for element in bertos_utils.getSystemPath()]
206         toolchain_list = bertos_utils.findToolchains(dir_list)
207         stored_toolchains = self.toolchains()
208         for element in toolchain_list:
209             if not element in stored_toolchains:
210                 item = QListWidgetItem(element)
211                 item.setData(Qt.UserRole, qvariant_converter.convertStringDict({"path": element}))
212                 self.pageContent.toolchainList.addItem(item)
213                 stored_toolchains[element] = False
214         self.setToolchains(stored_toolchains)
215         self.showMessage(self.tr("Toolchain search result."), self.tr("%1 toolchains founded").arg(len(stored_toolchains)))
216
217     def _validItem(self, index, infos):
218         """
219         Sets the item at index as a valid item and associates the given info to it.
220         """
221         item = self.pageContent.toolchainList.item(index)
222         new_data = qvariant_converter.getStringDict(self.pageContent.toolchainList.item(index).data(Qt.UserRole))
223         new_data.update(infos)
224         item.setData(Qt.UserRole, qvariant_converter.convertStringDict(new_data))
225         needed = self.projectInfo("CPU_INFOS")
226         if "target" in infos and infos["target"].find(needed["TOOLCHAIN"]) != -1:
227             item.setIcon(QIcon(":/images/ok.png"))
228             self._valid_items.append(item)
229         else:
230             item.setIcon(QIcon(":/images/warning.png"))
231         if "version" in infos and "target" in infos:
232             item.setText("GCC " + infos["version"] + " - " + infos["target"].strip())
233
234     def _invalidItem(self, index):
235         """
236         Sets the item at index as an invalid item.
237         """
238         item = self.pageContent.toolchainList.item(index)
239         item.setIcon(QIcon(":/images/error.png"))
240
241     def validateToolchain(self, i):
242         """
243         Toolchain validation procedure.
244         """
245         filename = qvariant_converter.getStringDict(self.pageContent.toolchainList.item(i).data(Qt.UserRole))["path"]
246         valid = False
247         info = {}
248         # Check for the other tools of the toolchain
249         for tool in TOOLCHAIN_ITEMS:
250             if os.path.exists(filename.replace("gcc", tool)):
251                 valid = True
252             else:
253                 valid = False
254                 break
255         # Try to retrieve the informations about the toolchain only for the valid toolchains
256         if valid:
257             self._validation_process = QProcess()
258             self._validation_process.start(filename, ["-v"])
259             self._validation_process.waitForStarted(1000)
260             if self._validation_process.waitForFinished(200):
261                 description = unicode(self._validation_process.readAllStandardError())
262                 info = bertos_utils.getToolchainInfo(description)
263                 if len(info) >= 4:
264                     valid = True
265             else:
266                 self._validation_process.kill()
267         # Add the item in the list with the appropriate associate data.
268         if valid:
269             self._validItem(i, info)
270         else:
271             self._invalidItem(i)
272         toolchains = self.toolchains()
273         toolchains[filename] = True
274         self.setToolchains(toolchains)
275     
276     def isDefaultToolchain(self, toolchain):
277         """
278         Returns True if the given toolchain is one of the default toolchains.
279         """
280         if os.name == "nt":
281             import winreg_importer
282             stored_toolchains = winreg_importer.getBertosToolchains()
283             if toolchain["path"] in stored_toolchains:
284                 return True
285         return False
286     
287     def disableRemoveButton(self):
288         """
289         Disable the remove button.
290         """
291         self.pageContent.removeButton.setEnabled(False)
292     
293     def enableRemoveButton(self):
294         """
295         Enable the remove button.
296         """
297         self.pageContent.removeButton.setEnabled(True)
298         
299     def currentItem(self):
300         return self.pageContent.toolchainList.currentItem()