view python/ide.py @ 248:b10d46e5c8dd

ide refactor
author Windel Bouwman
date Fri, 26 Jul 2013 16:46:02 +0200
parents dd8bbb963458
children e41e4109addd
line wrap: on
line source

#!/usr/bin/python

import sys
import os

from PyQt4.QtCore import *
from PyQt4.QtGui import *

# Compiler imports:
import ppci
from astviewer import AstViewer
from codeedit import CodeEdit
stutil = __import__('st-util')
import c3


class BuildOutput(QTextEdit):
    """ Build output component """
    def __init__(self, parent=None):
      super(BuildOutput, self).__init__(parent)
      self.setCurrentFont(QFont('Courier'))
      self.setReadOnly(True)
      self.append('Build output will appear here!')


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.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)
         irow = QStandardItem(str(e.loc.row))
         irow.setData(e)
         icol = QStandardItem(str(e.loc.col))
         icol.setData(e)
         self.model.appendRow([item, irow, icol])

    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)
        aboutText = """<h1>lcfOS IDE</h1>
        <p>An all-in-one IDE for OS development.</p>
        <p>https://www.assembla.com/spaces/lcfOS/wiki</p>
        <p>Author: Windel Bouwman</p>
        """
        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.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.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(self.nodeSelected)
        self.builderrors = addComponent('Build errors', BuildErrors())
        self.builderrors.sigErrorSelected.connect(self.errorSelected)
        self.devxplr = addComponent('Device explorer', stutil.DeviceExplorer())
        self.regview = addComponent('Registers', stutil.RegisterView())
        self.memview = addComponent('Memory', stutil.MemoryView())
        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"))

        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.buildFile)
        w = self.mdiArea.addSubWindow(ce)
        ce.show()
        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')
            for lf in lfs:
                self.loadFile(lf)

    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 errorSelected(self, err):
        self.showLoc(err.loc)

    def showLoc(self, loc):
        ce = self.activeMdiChild()
        if not ce:
            return
        if loc:
            ce.setRowCol(loc.row, loc.col)
            ce.setFocus()
        else:
            ce.clearErrors()

    def pointCode(self, p):
        print('PC', p)

    # Build recepy:
    def buildFile(self):
        ce = self.activeMdiChild()
        if not ce:
            return
        self.buildOutput.clear()
        self.diag.clear()
        self.buildOutput.append('Starting build')
        ir = self.c3front.build(ce.Source)

        # Set errors:
        for err in self.diag.diags:
            self.buildOutput.append(str(err))
        self.builderrors.setErrorList(self.diag.diags)
        ce.setErrors(self.diag.diags)
        ast = self.c3front.pkg
        self.astViewer.setAst(ast)
        self.buildOutput.append("Done!")

if __name__ == '__main__':
    app = QApplication(sys.argv)
    ide = Ide()
    ide.show()
    app.exec_()