Mercurial > lcfOS
diff python/ide.py @ 95:4a37d6992bd3
movage
author | windel |
---|---|
date | Mon, 24 Dec 2012 13:24:59 +0100 |
parents | python/apps/ide.py@60cc36ef5a50 |
children | 3f772feb12ef |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/python/ide.py Mon Dec 24 13:24:59 2012 +0100 @@ -0,0 +1,299 @@ +import sys, os, base64 +if sys.version_info.major != 3: + print("Needs to be run in python version 3.x") + sys.exit(1) + +from PyQt4.QtCore import * +from PyQt4.QtGui import * + +# Compiler imports: +sys.path.insert(0, os.path.join('..','libs')) +from project import Project +from compiler import Compiler +from widgets import CodeEdit, AstViewer + +lcfospng = base64.decodestring(b'iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAAXNSR0IArs4c6QAAAAZiS0dEAP8A\n/wD/oL2nkwAAAAlwSFlzAAALEwAACxMBAJqcGAAAAAd0SU1FB9sJEhMKBk7B678AAAA/SURBVFjD\n7dbBCQAgDATBi9h/y7EFA4Kf2QLCwH1S6XQu6sqoujublc8BAAAAAAAAAAB8B+zXT6YJAAAAAKYd\nWSgFQNUyijIAAAAASUVORK5CYII=\n') + +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(QListView): + sigErrorSelected = pyqtSignal(object) + def __init__(self, parent=None): + super(BuildErrors, self).__init__(parent) + model = QStandardItemModel() + self.setModel(model) + self.clicked.connect(self.itemSelected) + def setErrorList(self, errorlist): + model = QStandardItemModel() + for e in errorlist: + row, col, msg = e + item = QStandardItem(str(msg)) + item.setData(e) + model.appendRow(item) + self.setModel(model) + def itemSelected(self, index): + if not index.isValid(): + return + model = self.model() + item = model.itemFromIndex(index) + err = item.data() + self.sigErrorSelected.emit(err) + +class ProjectView(QWidget): + sigLoadFile = pyqtSignal(str) + def __init__(self, parent=None): + super(ProjectView, self).__init__(parent) + self.treeview = QTreeView(self) + self.treeview.setContextMenuPolicy(Qt.CustomContextMenu) + l = QVBoxLayout(self) + l.addWidget(self.treeview) + pm = QPixmap() + pm.loadFromData(lcfospng) + self.projectIcon = QIcon(pm) + # Connect signals: + self.treeview.activated.connect(self.activate) + self.treeview.customContextMenuRequested.connect(self.contextMenu) + def setProject(self, project): + self.project = project + model = QStandardItemModel() + root = model.invisibleRootItem() + pitem = QStandardItem(self.projectIcon, project.name) + pitem.setEditable(False) + pitem.setData(project) + root.appendRow(pitem) + for el in self.project.elements: + fitem = QStandardItem(el) + pitem.appendRow(fitem) + fitem.setEditable(False) + fitem.setData(el) + self.treeview.setModel(model) + self.treeview.expandAll() + def contextMenu(self, pos): + idx = self.treeview.indexAt(pos) + if not idx.isValid(): + return + item = self.treeview.model().itemFromIndex(idx) + def activate(self, index): + if not index.isValid(): + return + model = self.treeview.model() + item = model.itemFromIndex(index) + fn = item.data() + if type(fn) is str: + self.sigLoadFile.emit(fn) + +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 ProjectOptions(QDialog): + pass + # TODO: project options in here + +class Ide(QMainWindow): + def __init__(self, parent=None): + super(Ide, self).__init__(parent) + self.setWindowTitle('LCFOS IDE') + self.compiler = Compiler() + icon = QPixmap() + icon.loadFromData(lcfospng) + self.setWindowIcon(QIcon(icon)) + + # Create menus: + mb = self.menuBar() + self.projectMenu = mb.addMenu('Project') + self.viewMenu = mb.addMenu('View') + self.helpMenu = mb.addMenu('Help') + + # Create mdi area: + self.mdiArea = QMdiArea() + 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.projectview = addComponent('Project explorer', ProjectView()) + self.projectview.sigLoadFile.connect(self.loadFile) + + # About dialog: + self.aboutDialog = AboutDialog() + self.aboutDialog.setWindowIcon(QIcon(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.projectMenu, self.newProject) + addMenuEntry("Open", self.projectMenu, self.openProject) + addMenuEntry("Save", self.projectMenu, self.saveProject) + addMenuEntry("Close", self.projectMenu, self.closeProject) + addMenuEntry("Build", self.projectMenu, self.buildProject, 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) + + # Load settings: + self.settings = QSettings('windelsoft', 'lcfoside') + self.loadSettings() + + # File handling: + def newProject(self): + filename = QFileDialog.getSaveFileName(self, \ + "Select new projectfile", "", "lcfos Project files (*.lcp2)") + if filename: + self.project = Project() + self.project.filename = filename + self.project.save() + + def saveProject(self): + self.project.save() + + def closeProject(self): + ac = self.activeMdiChild() + if ac: + self.mdiArea.removeSubWindow(ac) + + def loadFile(self, filename): + # Find existing mdi widget: + wid = self.findMdiChild(filename) + if wid: + self.mdiArea.setActiveSubWindow(wid.parent()) + return wid + + # Create a new one: + ce = CodeEdit() + source = self.project.loadProjectFile(filename) + ce.setSource(source) + self.mdiArea.addSubWindow(ce) + ce.show() + return ce + + def loadProject(self, filename): + self.project = Project(filename) + self.projectview.setProject(self.project) + + def openProject(self): + filename = QFileDialog.getOpenFileName(self, \ + "Choose project file", "", "lcfos Project files (*.lcp2)") + if filename: + self.loadProject(filename) + + # MDI: + def activeMdiChild(self): + aw = self.mdiArea.activeSubWindow() + if aw: + return aw.widget() + else: + return None + + def findMdiChild(self, filename): + for window in self.mdiArea.subWindowList(): + wid = window.widget() + if wid.filename == filename: + return wid + return None + + def allChildren(self): + c = [] + for window in self.mdiArea.subWindowList(): + wid = window.widget() + c.append(wid) + return c + + # 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('openedproject'): + projectfile = self.settings.value('openedproject') + #self.loadProject(projectfile) + + def closeEvent(self, ev): + self.settings.setValue('mainwindowstate', self.saveState()) + self.settings.setValue('mainwindowgeometry', self.saveGeometry()) + if self.project: + self.settings.setValue('openedproject', self.project.filename) + # TODO: ask for save of opened files + ev.accept() + + # Error handling: + def nodeSelected(self, node): + ce = self.activeMdiChild() + if not ce: + return + if node.location: + row, col = node.location + ce.highlightErrorLocation( row, col ) + else: + ce.clearErrors() + + def errorSelected(self, err): + row, col, msg = err + ce = self.activeMdiChild() + if not ce: + return + ce.highlightErrorLocation(row, col) + + # Project loading: + + # Build recepy: + def buildProject(self): + """ Build project """ + self.buildOutput.clear() + self.buildOutput.append(str(self.compiler)) + mods = self.compiler.compileProject(self.project) + + self.builderrors.setErrorList(self.compiler.errorlist) + self.astViewer.setAst(mods[0]) + for err in self.compiler.errorlist: + self.buildOutput.append(str(err)) + self.buildOutput.append("Done!") + +if __name__ == '__main__': + app = QApplication(sys.argv) + ide = Ide() + ide.show() + app.exec_() +