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_()