Mercurial > lcfOS
diff python/ide/ide.py @ 290:7b38782ed496
File moves
author | Windel Bouwman |
---|---|
date | Sun, 24 Nov 2013 11:24:15 +0100 |
parents | python/ide.py@bd2593de3ff8 |
children | 534b94b40aa8 |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/python/ide/ide.py Sun Nov 24 11:24:15 2013 +0100 @@ -0,0 +1,338 @@ +#!/usr/bin/python + +import sys +import os +import logging +import traceback +import io + +from PyQt4.QtCore import * +from PyQt4.QtGui import * + +# Compiler imports: +import ppci +from astviewer import AstViewer +from codeedit import CodeEdit +from logview import LogView as BuildOutput +from disasm import Disassembly +stutil = __import__('st-util') +import c3 +import zcc +import outstream + + +def handle_exception(tp, v, tb): + logging.critical(str(v)) + tb = traceback.format_tb(tb) + for i in tb: + logging.critical(i.strip()) + +sys.excepthook = handle_exception + +class BuildErrors(QTreeView): + sigErrorSelected = pyqtSignal(object) + + def __init__(self, parent=None): + super(BuildErrors, self).__init__(parent) + model = QStandardItemModel() + self.setModel(model) + self.clicked.connect(self.itemSelected) + self.errorIcon = QIcon('icons/error.png') + self.model = QStandardItemModel() + self.model.setHorizontalHeaderLabels(['Message', 'Row', 'Column']) + self.header().setStretchLastSection(True) + self.setModel(self.model) + + def setErrorList(self, errorlist): + c = self.model.rowCount() + self.model.removeRows(0, c) + for e in errorlist: + item = QStandardItem(self.errorIcon, str(e.msg)) + item.setData(e) + row = str(e.loc.row) if e.loc else '' + irow = QStandardItem(row) + irow.setData(e) + col = str(e.loc.col) if e.loc else '' + icol = QStandardItem(col) + icol.setData(e) + self.model.appendRow([item, irow, icol]) + for i in range(3): + self.resizeColumnToContents(i) + + def itemSelected(self, index): + if not index.isValid(): + return + item = self.model.itemFromIndex(index) + err = item.data() + self.sigErrorSelected.emit(err) + + + +class AboutDialog(QDialog): + def __init__(self, parent=None): + super(AboutDialog, self).__init__(parent) + self.setWindowTitle('About') + l = QVBoxLayout(self) + txt = QTextEdit(self) + txt.setReadOnly(True) + with open(os.path.join('..', 'readme.rst'), 'r') as f: + aboutText = f.read() + txt.append(aboutText) + l.addWidget(txt) + but = QPushButton('OK') + but.setDefault(True) + but.clicked.connect(self.close) + l.addWidget(but) + + +class Ide(QMainWindow): + def __init__(self, parent=None): + super(Ide, self).__init__(parent) + self.to_open_files = [] + self.logger = logging.getLogger('ide') + + self.setWindowTitle('LCFOS IDE') + icon = QIcon('icons/logo.png') + self.setWindowIcon(icon) + + # Create menus: + mb = self.menuBar() + self.fileMenu = mb.addMenu('File') + self.viewMenu = mb.addMenu('View') + self.helpMenu = mb.addMenu('Help') + + # Create mdi area: + self.mdiArea = QMdiArea() + self.mdiArea.setViewMode(QMdiArea.TabbedView) + self.mdiArea.setTabsClosable(True) + self.mdiArea.setTabsMovable(True) + self.setCentralWidget(self.mdiArea) + + # Create components: + def addComponent(name, widget): + dw = QDockWidget(name) + dw.setWidget(widget) + dw.setObjectName(name) + self.addDockWidget(Qt.RightDockWidgetArea, dw) + self.viewMenu.addAction(dw.toggleViewAction()) + return widget + + self.buildOutput = addComponent('Build output', BuildOutput()) + self.astViewer = addComponent('AST viewer', AstViewer()) + self.astViewer.sigNodeSelected.connect(lambda node: self.showLoc(node.loc)) + self.builderrors = addComponent('Build errors', BuildErrors()) + self.builderrors.sigErrorSelected.connect(lambda err: self.showLoc(err.loc)) + self.devxplr = addComponent('Device explorer', stutil.DeviceExplorer()) + self.regview = addComponent('Registers', stutil.RegisterView()) + self.memview = addComponent('Memory', stutil.MemoryView()) + self.disasm = addComponent('Disasm', Disassembly()) + self.ctrlToolbar = stutil.DebugToolbar() + self.addToolBar(self.ctrlToolbar) + self.ctrlToolbar.setObjectName('debugToolbar') + self.devxplr.deviceSelected.connect(self.regview.mdl.setDevice) + self.ctrlToolbar.statusChange.connect(self.memview.refresh) + self.devxplr.deviceSelected.connect(self.memview.setDevice) + self.devxplr.deviceSelected.connect(self.ctrlToolbar.setDevice) + self.ctrlToolbar.statusChange.connect(self.regview.refresh) + self.ctrlToolbar.codePosition.connect(self.pointCode) + + # About dialog: + self.aboutDialog = AboutDialog() + self.aboutDialog.setWindowIcon(icon) + + # Create actions: + def addMenuEntry(name, menu, callback, shortcut=None): + a = QAction(name, self) + menu.addAction(a) + a.triggered.connect(callback) + if shortcut: + a.setShortcut(shortcut) + + addMenuEntry("New", self.fileMenu, self.newFile, shortcut=QKeySequence(QKeySequence.New)) + addMenuEntry("Open", self.fileMenu, self.openFile, shortcut=QKeySequence(QKeySequence.Open)) + addMenuEntry("Save", self.fileMenu, self.saveFile, shortcut=QKeySequence(QKeySequence.Save)) + addMenuEntry("Build", self.fileMenu, self.buildFile, shortcut=QKeySequence("F7")) + addMenuEntry("Build and flash", self.fileMenu, self.buildFileAndFlash, shortcut=QKeySequence("F8")) + + self.helpAction = QAction('Help', self) + self.helpAction.setShortcut(QKeySequence('F1')) + self.helpMenu.addAction(self.helpAction) + addMenuEntry('About', self.helpMenu, self.aboutDialog.open) + + addMenuEntry('Cascade windows', self.viewMenu, self.mdiArea.cascadeSubWindows) + addMenuEntry('Tile windows', self.viewMenu, self.mdiArea.tileSubWindows) + sb = self.statusBar() + + # Load settings: + self.settings = QSettings('windelsoft', 'lcfoside') + self.loadSettings() + self.diag = ppci.DiagnosticsManager() + self.c3front = c3.Builder(self.diag) + + # File handling: + def newFile(self): + self.newCodeEdit() + + def openFile(self): + filename = QFileDialog.getOpenFileName(self, "Open C3 file...", "*.c3", + "C3 source files (*.c3)") + if filename: + self.loadFile(filename) + + def saveFile(self): + ac = self.activeMdiChild() + if ac: + ac.save() + + def loadFile(self, filename): + ce = self.newCodeEdit() + try: + with open(filename) as f: + ce.Source = f.read() + ce.FileName = filename + except Exception as e: + print('exception opening file', e) + + # MDI: + def newCodeEdit(self): + ce = CodeEdit() + ce.textChanged.connect(self.parseFile) + w = self.mdiArea.addSubWindow(ce) + self.mdiArea.setActiveSubWindow(w) + ce.showMaximized() + return ce + + def activeMdiChild(self): + aw = self.mdiArea.activeSubWindow() + if aw: + return aw.widget() + + def findMdiChild(self, filename): + for wid in self.allChildren(): + if wid.filename == filename: + return wid + + def allChildren(self): + return [w.widget() for w in self.mdiArea.subWindowList()] + + # Settings: + def loadSettings(self): + if self.settings.contains('mainwindowstate'): + self.restoreState(self.settings.value('mainwindowstate')) + if self.settings.contains('mainwindowgeometry'): + self.restoreGeometry(self.settings.value('mainwindowgeometry')) + if self.settings.contains('lastfiles'): + lfs = self.settings.value('lastfiles') + if lfs: + self.to_open_files.extend(lfs) + + def showEvent(self, ev): + super().showEvent(ev) + while self.to_open_files: + fn = self.to_open_files.pop(0) + self.loadFile(fn) + + def closeEvent(self, ev): + self.settings.setValue('mainwindowstate', self.saveState()) + self.settings.setValue('mainwindowgeometry', self.saveGeometry()) + ac = self.activeMdiChild() + lfs = [ce.FileName for ce in self.allChildren() if ce.FileName] + self.settings.setValue('lastfiles', lfs) + ev.accept() + + # Error handling: + def nodeSelected(self, node): + self.showLoc(node.loc) + + def showLoc(self, loc): + ce = self.activeMdiChild() + if not ce: + return + if loc: + ce.setRowCol(loc.row, loc.col) + ce.setFocus() + + def pointCode(self, p): + # Lookup pc in debug infos: + loc = None + print(p) + self.disasm.showPos(p) + if hasattr(self, 'debugInfo'): + for di in self.debugInfo: + if di.address > p: + loc = di.info + break + if loc: + ce = self.activeMdiChild() + if ce: + ce.ic.arrow = loc + self.showLoc(loc) + + # Build recepy: + def parseFile(self): + self.logger.info('Parsing!') + ce = self.activeMdiChild() + if not ce: + return + self.diag.clear() + pkg = self.c3front.parse(ce.Source) + + # Set errors: + self.builderrors.setErrorList(self.diag.diags) + ce.setErrors(self.diag.diags) + self.astViewer.setAst(pkg) + if pkg: + c3.AstPrinter().printAst(pkg) + self.logger.info('Done!') + + def buildFile(self): + ce = self.activeMdiChild() + if not ce: + return + fn = ce.FileName + self.diag.clear() + outs = outstream.TextOutputStream() + if not zcc.zcc([io.StringIO(ce.Source)], [], outs, self.diag): + # Set errors: + self.builderrors.setErrorList(self.diag.diags) + ce.setErrors(self.diag.diags) + return + + def buildFileAndFlash(self): + ce = self.activeMdiChild() + if not ce: + return + fn = ce.FileName + self.diag.clear() + outs = outstream.TextOutputStream() + imps = [open(ce.FileName, 'r') for ce in self.allChildren() if ce.FileName and ce.FileName != fn] + if not zcc.zcc([open(fn, 'r')], imps, outs, self.diag): + # Set errors: + self.builderrors.setErrorList(self.diag.diags) + ce.setErrors(self.diag.diags) + return + + outs.dump() + code_s = outs.getSection('code') + self.disasm.dm.setInstructions(code_s.instructions) + self.debugInfo = code_s.debugInfos() + if self.ctrlToolbar.device: + logging.info('Flashing stm32f4 discovery') + bts = code_s.to_bytes() + self.ctrlToolbar.device.writeFlash(0x08000000, bts) + stl = self.ctrlToolbar.device.iface + stl.reset() + stl.halt() + stl.run() + logging.info('Done!') + else: + self.logger.warning('Not connected to device, skipping flash') + + +if __name__ == '__main__': + logging.basicConfig(format=zcc.logformat, level=logging.DEBUG) + app = QApplication(sys.argv) + ide = Ide() + ide.show() + ide.logger.info('IDE started') + app.exec_()