Mercurial > lcfOS
annotate python/ide.py @ 284:05184b95fa16
Moved tests to seperate folder
author | Windel Bouwman |
---|---|
date | Fri, 15 Nov 2013 13:43:22 +0100 |
parents | c137f1fe3e65 |
children | 1c7c1e619be8 |
rev | line source |
---|---|
99 | 1 #!/usr/bin/python |
2 | |
248 | 3 import sys |
4 import os | |
255 | 5 import logging |
64 | 6 |
7 from PyQt4.QtCore import * | |
8 from PyQt4.QtGui import * | |
1 | 9 |
10 # Compiler imports: | |
106 | 11 import ppci |
100 | 12 from astviewer import AstViewer |
162 | 13 from codeedit import CodeEdit |
281 | 14 from logview import LogView as BuildOutput |
131 | 15 stutil = __import__('st-util') |
165 | 16 import c3 |
249 | 17 import zcc |
18 import outstream | |
282 | 19 import traceback |
64 | 20 |
259 | 21 |
282 | 22 def handle_exception(tp, v, tb): |
23 logging.critical(str(v)) | |
24 tb = traceback.format_tb(tb) | |
25 for i in tb: | |
26 logging.critical(i.strip()) | |
27 | |
28 sys.excepthook = handle_exception | |
29 | |
167 | 30 class BuildErrors(QTreeView): |
248 | 31 sigErrorSelected = pyqtSignal(object) |
259 | 32 |
248 | 33 def __init__(self, parent=None): |
34 super(BuildErrors, self).__init__(parent) | |
35 model = QStandardItemModel() | |
36 self.setModel(model) | |
37 self.clicked.connect(self.itemSelected) | |
38 self.errorIcon = QIcon('icons/error.png') | |
39 self.model = QStandardItemModel() | |
40 self.model.setHorizontalHeaderLabels(['Message', 'Row', 'Column']) | |
281 | 41 self.header().setStretchLastSection(True) |
248 | 42 self.setModel(self.model) |
169
ee0d30533dae
Added more tests and improved the diagnostic update
Windel Bouwman
parents:
168
diff
changeset
|
43 |
248 | 44 def setErrorList(self, errorlist): |
259 | 45 c = self.model.rowCount() |
46 self.model.removeRows(0, c) | |
47 for e in errorlist: | |
48 item = QStandardItem(self.errorIcon, str(e.msg)) | |
49 item.setData(e) | |
277 | 50 row = str(e.loc.row) if e.loc else '' |
51 irow = QStandardItem(row) | |
259 | 52 irow.setData(e) |
277 | 53 col = str(e.loc.col) if e.loc else '' |
54 icol = QStandardItem(col) | |
259 | 55 icol.setData(e) |
56 self.model.appendRow([item, irow, icol]) | |
281 | 57 for i in range(3): |
58 self.resizeColumnToContents(i) | |
248 | 59 |
60 def itemSelected(self, index): | |
256 | 61 if not index.isValid(): |
62 return | |
63 item = self.model.itemFromIndex(index) | |
64 err = item.data() | |
65 self.sigErrorSelected.emit(err) | |
64 | 66 |
67 | |
282 | 68 class DisAsmModel(QAbstractTableModel): |
69 def __init__(self): | |
70 super().__init__() | |
71 self.outs = None | |
72 self.instructions = [] | |
73 self.headers = ['Address', 'Bytes', 'Instruction'] | |
74 self.txts = [] | |
75 self.txts.append(lambda i: '0x{:08x}'.format(i.address)) | |
76 self.txts.append(lambda i: str(i.encode())) | |
77 self.txts.append(lambda i: str(i)) | |
78 | |
79 def rowCount(self, parent): | |
80 return len(self.instructions) | |
81 | |
82 def columnCount(self, parent): | |
83 return len(self.headers) | |
84 | |
85 def data(self, index, role): | |
86 if not index.isValid(): | |
87 return | |
88 row, col = index.row(), index.column() | |
89 if role == Qt.DisplayRole: | |
90 i = self.instructions[row] | |
91 return self.txts[col](i) | |
92 | |
93 def headerData(self, section, orientation, role): | |
94 if orientation == Qt.Horizontal and role == Qt.DisplayRole: | |
95 return self.headers[section] | |
96 | |
97 def setInstructions(self, ins): | |
98 self.instructions = ins | |
99 self.modelReset.emit() | |
100 | |
101 | |
102 class Disassembly(QTableView): | |
103 def __init__(self): | |
104 super().__init__() | |
105 self.dm = DisAsmModel() | |
106 self.setModel(self.dm) | |
107 | |
108 def showPos(self, p): | |
109 for i in self.dm.instructions: | |
110 if i.address == p: | |
111 row = self.dm.instructions.index(i) | |
112 self.selectRow(row) | |
113 | |
64 | 114 class AboutDialog(QDialog): |
248 | 115 def __init__(self, parent=None): |
116 super(AboutDialog, self).__init__(parent) | |
117 self.setWindowTitle('About') | |
118 l = QVBoxLayout(self) | |
119 txt = QTextEdit(self) | |
120 txt.setReadOnly(True) | |
282 | 121 with open(os.path.join('..', 'readme.rst'), 'r') as f: |
122 aboutText = f.read() | |
248 | 123 txt.append(aboutText) |
124 l.addWidget(txt) | |
125 but = QPushButton('OK') | |
126 but.setDefault(True) | |
127 but.clicked.connect(self.close) | |
128 l.addWidget(but) | |
129 | |
64 | 130 |
131 class Ide(QMainWindow): | |
248 | 132 def __init__(self, parent=None): |
133 super(Ide, self).__init__(parent) | |
256 | 134 self.to_open_files = [] |
259 | 135 self.logger = logging.getLogger('ide') |
255 | 136 |
248 | 137 self.setWindowTitle('LCFOS IDE') |
138 icon = QIcon('icons/logo.png') | |
139 self.setWindowIcon(icon) | |
64 | 140 |
248 | 141 # Create menus: |
142 mb = self.menuBar() | |
143 self.fileMenu = mb.addMenu('File') | |
144 self.viewMenu = mb.addMenu('View') | |
145 self.helpMenu = mb.addMenu('Help') | |
64 | 146 |
248 | 147 # Create mdi area: |
148 self.mdiArea = QMdiArea() | |
149 self.mdiArea.setViewMode(QMdiArea.TabbedView) | |
249 | 150 self.mdiArea.setTabsClosable(True) |
256 | 151 self.mdiArea.setTabsMovable(True) |
248 | 152 self.setCentralWidget(self.mdiArea) |
64 | 153 |
248 | 154 # Create components: |
155 def addComponent(name, widget): | |
259 | 156 dw = QDockWidget(name) |
157 dw.setWidget(widget) | |
158 dw.setObjectName(name) | |
159 self.addDockWidget(Qt.RightDockWidgetArea, dw) | |
160 self.viewMenu.addAction(dw.toggleViewAction()) | |
161 return widget | |
64 | 162 |
248 | 163 self.buildOutput = addComponent('Build output', BuildOutput()) |
164 self.astViewer = addComponent('AST viewer', AstViewer()) | |
255 | 165 self.astViewer.sigNodeSelected.connect(lambda node: self.showLoc(node.loc)) |
248 | 166 self.builderrors = addComponent('Build errors', BuildErrors()) |
255 | 167 self.builderrors.sigErrorSelected.connect(lambda err: self.showLoc(err.loc)) |
248 | 168 self.devxplr = addComponent('Device explorer', stutil.DeviceExplorer()) |
169 self.regview = addComponent('Registers', stutil.RegisterView()) | |
170 self.memview = addComponent('Memory', stutil.MemoryView()) | |
282 | 171 self.disasm = addComponent('Disasm', Disassembly()) |
248 | 172 self.ctrlToolbar = stutil.DebugToolbar() |
173 self.addToolBar(self.ctrlToolbar) | |
174 self.ctrlToolbar.setObjectName('debugToolbar') | |
175 self.devxplr.deviceSelected.connect(self.regview.mdl.setDevice) | |
176 self.ctrlToolbar.statusChange.connect(self.memview.refresh) | |
177 self.devxplr.deviceSelected.connect(self.memview.setDevice) | |
178 self.devxplr.deviceSelected.connect(self.ctrlToolbar.setDevice) | |
179 self.ctrlToolbar.statusChange.connect(self.regview.refresh) | |
180 self.ctrlToolbar.codePosition.connect(self.pointCode) | |
64 | 181 |
248 | 182 # About dialog: |
183 self.aboutDialog = AboutDialog() | |
184 self.aboutDialog.setWindowIcon(icon) | |
259 | 185 |
248 | 186 # Create actions: |
187 def addMenuEntry(name, menu, callback, shortcut=None): | |
259 | 188 a = QAction(name, self) |
189 menu.addAction(a) | |
190 a.triggered.connect(callback) | |
191 if shortcut: | |
192 a.setShortcut(shortcut) | |
163 | 193 |
248 | 194 addMenuEntry("New", self.fileMenu, self.newFile, shortcut=QKeySequence(QKeySequence.New)) |
195 addMenuEntry("Open", self.fileMenu, self.openFile, shortcut=QKeySequence(QKeySequence.Open)) | |
196 addMenuEntry("Save", self.fileMenu, self.saveFile, shortcut=QKeySequence(QKeySequence.Save)) | |
197 addMenuEntry("Build", self.fileMenu, self.buildFile, shortcut=QKeySequence("F7")) | |
256 | 198 addMenuEntry("Build and flash", self.fileMenu, self.buildFileAndFlash, shortcut=QKeySequence("F8")) |
152 | 199 |
259 | 200 self.helpAction = QAction('Help', self) |
248 | 201 self.helpAction.setShortcut(QKeySequence('F1')) |
202 self.helpMenu.addAction(self.helpAction) | |
203 addMenuEntry('About', self.helpMenu, self.aboutDialog.open) | |
204 | |
205 addMenuEntry('Cascade windows', self.viewMenu, self.mdiArea.cascadeSubWindows) | |
206 addMenuEntry('Tile windows', self.viewMenu, self.mdiArea.tileSubWindows) | |
207 sb = self.statusBar() | |
247 | 208 |
248 | 209 # Load settings: |
210 self.settings = QSettings('windelsoft', 'lcfoside') | |
211 self.loadSettings() | |
212 self.diag = ppci.DiagnosticsManager() | |
213 self.c3front = c3.Builder(self.diag) | |
214 | |
215 # File handling: | |
216 def newFile(self): | |
217 self.newCodeEdit() | |
247 | 218 |
248 | 219 def openFile(self): |
220 filename = QFileDialog.getOpenFileName(self, "Open C3 file...", "*.c3", | |
259 | 221 "C3 source files (*.c3)") |
248 | 222 if filename: |
223 self.loadFile(filename) | |
64 | 224 |
248 | 225 def saveFile(self): |
226 ac = self.activeMdiChild() | |
227 if ac: | |
228 ac.save() | |
101 | 229 |
248 | 230 def loadFile(self, filename): |
231 ce = self.newCodeEdit() | |
232 try: | |
233 with open(filename) as f: | |
234 ce.Source = f.read() | |
235 ce.FileName = filename | |
236 except Exception as e: | |
237 print('exception opening file', e) | |
64 | 238 |
248 | 239 # MDI: |
240 def newCodeEdit(self): | |
241 ce = CodeEdit() | |
249 | 242 ce.textChanged.connect(self.parseFile) |
248 | 243 w = self.mdiArea.addSubWindow(ce) |
249 | 244 self.mdiArea.setActiveSubWindow(w) |
245 ce.showMaximized() | |
248 | 246 return ce |
247 | |
248 def activeMdiChild(self): | |
249 aw = self.mdiArea.activeSubWindow() | |
250 if aw: | |
251 return aw.widget() | |
252 | |
253 def findMdiChild(self, filename): | |
254 for wid in self.allChildren(): | |
255 if wid.filename == filename: | |
256 return wid | |
64 | 257 |
248 | 258 def allChildren(self): |
259 return [w.widget() for w in self.mdiArea.subWindowList()] | |
260 | |
261 # Settings: | |
262 def loadSettings(self): | |
263 if self.settings.contains('mainwindowstate'): | |
264 self.restoreState(self.settings.value('mainwindowstate')) | |
265 if self.settings.contains('mainwindowgeometry'): | |
266 self.restoreGeometry(self.settings.value('mainwindowgeometry')) | |
267 if self.settings.contains('lastfiles'): | |
268 lfs = self.settings.value('lastfiles') | |
277 | 269 if lfs: |
270 self.to_open_files.extend(lfs) | |
256 | 271 |
272 def showEvent(self, ev): | |
273 super().showEvent(ev) | |
274 while self.to_open_files: | |
275 fn = self.to_open_files.pop(0) | |
276 self.loadFile(fn) | |
64 | 277 |
248 | 278 def closeEvent(self, ev): |
279 self.settings.setValue('mainwindowstate', self.saveState()) | |
280 self.settings.setValue('mainwindowgeometry', self.saveGeometry()) | |
281 ac = self.activeMdiChild() | |
282 lfs = [ce.FileName for ce in self.allChildren() if ce.FileName] | |
283 self.settings.setValue('lastfiles', lfs) | |
284 ev.accept() | |
285 | |
286 # Error handling: | |
287 def nodeSelected(self, node): | |
288 self.showLoc(node.loc) | |
289 | |
290 def showLoc(self, loc): | |
291 ce = self.activeMdiChild() | |
292 if not ce: | |
293 return | |
294 if loc: | |
295 ce.setRowCol(loc.row, loc.col) | |
296 ce.setFocus() | |
64 | 297 |
248 | 298 def pointCode(self, p): |
249 | 299 # Lookup pc in debug infos: |
300 loc = None | |
282 | 301 print(p) |
302 self.disasm.showPos(p) | |
249 | 303 if hasattr(self, 'debugInfo'): |
304 for di in self.debugInfo: | |
305 if di.address > p: | |
306 loc = di.info | |
307 break | |
308 if loc: | |
309 ce = self.activeMdiChild() | |
310 if ce: | |
311 ce.ic.arrow = loc | |
312 self.showLoc(loc) | |
247 | 313 |
248 | 314 # Build recepy: |
249 | 315 def parseFile(self): |
282 | 316 self.logger.info('Parsing!') |
248 | 317 ce = self.activeMdiChild() |
318 if not ce: | |
319 return | |
320 self.diag.clear() | |
251
6ed3d3a82a63
Added another c3 example. First import attempt
Windel Bouwman
parents:
250
diff
changeset
|
321 pkg = self.c3front.parse(ce.Source) |
64 | 322 |
248 | 323 # Set errors: |
324 self.builderrors.setErrorList(self.diag.diags) | |
325 ce.setErrors(self.diag.diags) | |
251
6ed3d3a82a63
Added another c3 example. First import attempt
Windel Bouwman
parents:
250
diff
changeset
|
326 self.astViewer.setAst(pkg) |
282 | 327 if pkg: |
328 c3.AstPrinter().printAst(pkg) | |
259 | 329 self.logger.info('Done!') |
64 | 330 |
249 | 331 def buildFile(self): |
332 ce = self.activeMdiChild() | |
333 if not ce: | |
334 return | |
279 | 335 fn = ce.FileName |
336 wd = os.path.dirname(fn) | |
249 | 337 self.diag.clear() |
338 outs = outstream.TextOutputStream() | |
279 | 339 if not zcc.zcc(ce.Source, outs, self.diag, pack_dir=wd): |
249 | 340 # Set errors: |
341 self.builderrors.setErrorList(self.diag.diags) | |
342 ce.setErrors(self.diag.diags) | |
343 return | |
344 | |
256 | 345 def buildFileAndFlash(self): |
346 ce = self.activeMdiChild() | |
347 if not ce: | |
348 return | |
279 | 349 fn = ce.FileName |
350 wd = os.path.dirname(fn) | |
256 | 351 self.diag.clear() |
352 outs = outstream.TextOutputStream() | |
279 | 353 if not zcc.zcc(ce.Source, outs, self.diag, do_optimize=True, pack_dir=wd): |
256 | 354 # Set errors: |
355 self.builderrors.setErrorList(self.diag.diags) | |
356 ce.setErrors(self.diag.diags) | |
357 return | |
358 | |
279 | 359 outs.dump() |
249 | 360 code_s = outs.getSection('code') |
282 | 361 self.disasm.dm.setInstructions(code_s.instructions) |
249 | 362 self.debugInfo = code_s.debugInfos() |
250 | 363 if self.ctrlToolbar.device: |
255 | 364 logging.info('Flashing stm32f4 discovery') |
250 | 365 bts = code_s.to_bytes() |
366 self.ctrlToolbar.device.writeFlash(0x08000000, bts) | |
367 stl = self.ctrlToolbar.device.iface | |
368 stl.reset() | |
369 stl.halt() | |
256 | 370 stl.run() |
259 | 371 logging.info('Done!') |
372 else: | |
373 self.logger.warning('Not connected to device, skipping flash') | |
249 | 374 |
256 | 375 |
1 | 376 if __name__ == '__main__': |
258 | 377 logging.basicConfig(format=zcc.logformat, level=logging.DEBUG) |
240 | 378 app = QApplication(sys.argv) |
379 ide = Ide() | |
380 ide.show() | |
259 | 381 ide.logger.info('IDE started') |
240 | 382 app.exec_() |
1 | 383 |