Mercurial > lcfOS
annotate python/ide.py @ 192:6cd6260789a1
Added more tests for parser generator
author | Windel Bouwman |
---|---|
date | Sun, 26 May 2013 23:19:27 +0200 |
parents | ee0d30533dae |
children | 8b2f20aae086 |
rev | line source |
---|---|
99 | 1 #!/usr/bin/python |
2 | |
69 | 3 import sys, os, base64 |
131 | 4 assert sys.version_info.major == 3, "Needs to be run in python version 3.x" |
64 | 5 |
6 from PyQt4.QtCore import * | |
7 from PyQt4.QtGui import * | |
1 | 8 |
9 # Compiler imports: | |
64 | 10 from project import Project |
106 | 11 import ppci |
100 | 12 from astviewer import AstViewer |
162 | 13 #from codeeditor import CodeEdit |
14 from codeedit import CodeEdit | |
131 | 15 stutil = __import__('st-util') |
152 | 16 import testc3 |
165 | 17 import c3 |
64 | 18 |
19 lcfospng = base64.decodestring(b'iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAAXNSR0IArs4c6QAAAAZiS0dEAP8A\n/wD/oL2nkwAAAAlwSFlzAAALEwAACxMBAJqcGAAAAAd0SU1FB9sJEhMKBk7B678AAAA/SURBVFjD\n7dbBCQAgDATBi9h/y7EFA4Kf2QLCwH1S6XQu6sqoujublc8BAAAAAAAAAAB8B+zXT6YJAAAAAKYd\nWSgFQNUyijIAAAAASUVORK5CYII=\n') | |
20 | |
21 class BuildOutput(QTextEdit): | |
22 """ Build output component """ | |
23 def __init__(self, parent=None): | |
24 super(BuildOutput, self).__init__(parent) | |
25 self.setCurrentFont(QFont('Courier')) | |
26 self.setReadOnly(True) | |
27 self.append('Build output will appear here!') | |
28 | |
167 | 29 class BuildErrors(QTreeView): |
64 | 30 sigErrorSelected = pyqtSignal(object) |
31 def __init__(self, parent=None): | |
32 super(BuildErrors, self).__init__(parent) | |
33 model = QStandardItemModel() | |
34 self.setModel(model) | |
35 self.clicked.connect(self.itemSelected) | |
154 | 36 self.errorIcon = QIcon('error.png') |
169
ee0d30533dae
Added more tests and improved the diagnostic update
Windel Bouwman
parents:
168
diff
changeset
|
37 self.model = QStandardItemModel() |
ee0d30533dae
Added more tests and improved the diagnostic update
Windel Bouwman
parents:
168
diff
changeset
|
38 self.model.setHorizontalHeaderLabels(['Message', 'Row', 'Column']) |
ee0d30533dae
Added more tests and improved the diagnostic update
Windel Bouwman
parents:
168
diff
changeset
|
39 self.setModel(self.model) |
ee0d30533dae
Added more tests and improved the diagnostic update
Windel Bouwman
parents:
168
diff
changeset
|
40 |
64 | 41 def setErrorList(self, errorlist): |
169
ee0d30533dae
Added more tests and improved the diagnostic update
Windel Bouwman
parents:
168
diff
changeset
|
42 c = self.model.rowCount() |
ee0d30533dae
Added more tests and improved the diagnostic update
Windel Bouwman
parents:
168
diff
changeset
|
43 self.model.removeRows(0, c) |
64 | 44 for e in errorlist: |
167 | 45 item = QStandardItem(self.errorIcon, str(e.msg)) |
64 | 46 item.setData(e) |
167 | 47 irow = QStandardItem(str(e.loc.row)) |
48 irow.setData(e) | |
49 icol = QStandardItem(str(e.loc.col)) | |
50 icol.setData(e) | |
169
ee0d30533dae
Added more tests and improved the diagnostic update
Windel Bouwman
parents:
168
diff
changeset
|
51 self.model.appendRow([item, irow, icol]) |
64 | 52 def itemSelected(self, index): |
53 if not index.isValid(): | |
54 return | |
169
ee0d30533dae
Added more tests and improved the diagnostic update
Windel Bouwman
parents:
168
diff
changeset
|
55 item = self.model.itemFromIndex(index) |
64 | 56 err = item.data() |
57 self.sigErrorSelected.emit(err) | |
58 | |
59 class ProjectView(QWidget): | |
60 sigLoadFile = pyqtSignal(str) | |
61 def __init__(self, parent=None): | |
62 super(ProjectView, self).__init__(parent) | |
63 self.treeview = QTreeView(self) | |
64 self.treeview.setContextMenuPolicy(Qt.CustomContextMenu) | |
65 l = QVBoxLayout(self) | |
66 l.addWidget(self.treeview) | |
67 pm = QPixmap() | |
68 pm.loadFromData(lcfospng) | |
69 self.projectIcon = QIcon(pm) | |
70 # Connect signals: | |
71 self.treeview.activated.connect(self.activate) | |
72 self.treeview.customContextMenuRequested.connect(self.contextMenu) | |
73 def setProject(self, project): | |
74 self.project = project | |
75 model = QStandardItemModel() | |
76 root = model.invisibleRootItem() | |
77 pitem = QStandardItem(self.projectIcon, project.name) | |
78 pitem.setEditable(False) | |
79 pitem.setData(project) | |
80 root.appendRow(pitem) | |
67 | 81 for el in self.project.elements: |
82 fitem = QStandardItem(el) | |
64 | 83 pitem.appendRow(fitem) |
84 fitem.setEditable(False) | |
67 | 85 fitem.setData(el) |
64 | 86 self.treeview.setModel(model) |
87 self.treeview.expandAll() | |
88 def contextMenu(self, pos): | |
89 idx = self.treeview.indexAt(pos) | |
90 if not idx.isValid(): | |
91 return | |
92 item = self.treeview.model().itemFromIndex(idx) | |
93 def activate(self, index): | |
94 if not index.isValid(): | |
95 return | |
96 model = self.treeview.model() | |
97 item = model.itemFromIndex(index) | |
98 fn = item.data() | |
99 if type(fn) is str: | |
100 self.sigLoadFile.emit(fn) | |
101 | |
102 class AboutDialog(QDialog): | |
103 def __init__(self, parent=None): | |
104 super(AboutDialog, self).__init__(parent) | |
105 self.setWindowTitle('About') | |
106 l = QVBoxLayout(self) | |
107 txt = QTextEdit(self) | |
108 txt.setReadOnly(True) | |
109 aboutText = """<h1>lcfOS IDE</h1> | |
110 <p>An all-in-one IDE for OS development.</p> | |
111 <p>https://www.assembla.com/spaces/lcfOS/wiki</p> | |
112 <p>Author: Windel Bouwman</p> | |
113 """ | |
114 txt.append(aboutText) | |
115 l.addWidget(txt) | |
116 but = QPushButton('OK') | |
117 but.setDefault(True) | |
118 but.clicked.connect(self.close) | |
119 l.addWidget(but) | |
120 | |
121 class ProjectOptions(QDialog): | |
122 pass | |
123 # TODO: project options in here | |
124 | |
125 class Ide(QMainWindow): | |
126 def __init__(self, parent=None): | |
127 super(Ide, self).__init__(parent) | |
128 self.setWindowTitle('LCFOS IDE') | |
129 icon = QPixmap() | |
130 icon.loadFromData(lcfospng) | |
131 self.setWindowIcon(QIcon(icon)) | |
132 | |
133 # Create menus: | |
68 | 134 mb = self.menuBar() |
101 | 135 self.fileMenu = mb.addMenu('File') |
68 | 136 self.projectMenu = mb.addMenu('Project') |
137 self.viewMenu = mb.addMenu('View') | |
138 self.helpMenu = mb.addMenu('Help') | |
64 | 139 |
140 # Create mdi area: | |
141 self.mdiArea = QMdiArea() | |
142 self.setCentralWidget(self.mdiArea) | |
143 | |
144 # Create components: | |
68 | 145 def addComponent(name, widget): |
146 dw = QDockWidget(name) | |
147 dw.setWidget(widget) | |
148 dw.setObjectName(name) | |
149 self.addDockWidget(Qt.RightDockWidgetArea, dw) | |
150 self.viewMenu.addAction(dw.toggleViewAction()) | |
151 return widget | |
64 | 152 |
68 | 153 self.buildOutput = addComponent('Build output', BuildOutput()) |
154 self.astViewer = addComponent('AST viewer', AstViewer()) | |
155 self.astViewer.sigNodeSelected.connect(self.nodeSelected) | |
156 self.builderrors = addComponent('Build errors', BuildErrors()) | |
64 | 157 self.builderrors.sigErrorSelected.connect(self.errorSelected) |
68 | 158 self.projectview = addComponent('Project explorer', ProjectView()) |
64 | 159 self.projectview.sigLoadFile.connect(self.loadFile) |
131 | 160 self.devxplr = addComponent('Device explorer', stutil.DeviceExplorer()) |
161 self.regview = addComponent('Registers', stutil.RegisterView()) | |
136 | 162 self.memview = addComponent('Memory', stutil.MemoryView()) |
137 | 163 self.ctrlToolbar = stutil.DebugToolbar() |
164 self.addToolBar(self.ctrlToolbar) | |
165 self.ctrlToolbar.setObjectName('debugToolbar') | |
131 | 166 self.devxplr.deviceSelected.connect(self.regview.mdl.setDevice) |
136 | 167 self.devxplr.deviceSelected.connect(self.memview.setDevice) |
137 | 168 self.devxplr.deviceSelected.connect(self.ctrlToolbar.setDevice) |
143 | 169 self.ctrlToolbar.statusChange.connect(self.regview.refresh) |
64 | 170 |
171 # About dialog: | |
172 self.aboutDialog = AboutDialog() | |
173 self.aboutDialog.setWindowIcon(QIcon(icon)) | |
174 # Create actions: | |
68 | 175 def addMenuEntry(name, menu, callback, shortcut=None): |
176 a = QAction(name, self) | |
177 menu.addAction(a) | |
178 a.triggered.connect(callback) | |
179 if shortcut: | |
180 a.setShortcut(shortcut) | |
67 | 181 |
68 | 182 addMenuEntry("New", self.projectMenu, self.newProject) |
183 addMenuEntry("Open", self.projectMenu, self.openProject) | |
184 addMenuEntry("Save", self.projectMenu, self.saveProject) | |
185 addMenuEntry("Close", self.projectMenu, self.closeProject) | |
186 addMenuEntry("Build", self.projectMenu, self.buildProject, shortcut=QKeySequence('F7')) | |
67 | 187 |
101 | 188 addMenuEntry("New", self.fileMenu, self.newFile, shortcut=QKeySequence(QKeySequence.New)) |
189 addMenuEntry("Open", self.fileMenu, self.openFile, shortcut=QKeySequence(QKeySequence.Open)) | |
190 addMenuEntry("Save", self.fileMenu, self.saveFile, shortcut=QKeySequence(QKeySequence.Save)) | |
191 addMenuEntry("Build", self.fileMenu, self.buildFile, shortcut=QKeySequence("F8")) | |
192 | |
64 | 193 self.helpAction = QAction('Help', self) |
194 self.helpAction.setShortcut(QKeySequence('F1')) | |
195 self.helpMenu.addAction(self.helpAction) | |
68 | 196 addMenuEntry('About', self.helpMenu, self.aboutDialog.open) |
64 | 197 |
68 | 198 addMenuEntry('Cascade windows', self.viewMenu, self.mdiArea.cascadeSubWindows) |
199 addMenuEntry('Tile windows', self.viewMenu, self.mdiArea.tileSubWindows) | |
64 | 200 |
163 | 201 sb = self.statusBar() |
202 | |
64 | 203 # Load settings: |
204 self.settings = QSettings('windelsoft', 'lcfoside') | |
205 self.loadSettings() | |
206 | |
152 | 207 ce = self.newFile() |
162 | 208 ce.Source = testc3.testsrc |
152 | 209 |
210 self.diag = ppci.DiagnosticsManager() | |
165 | 211 self.c3front = c3.Builder(self.diag) |
152 | 212 |
64 | 213 # File handling: |
67 | 214 def newProject(self): |
215 filename = QFileDialog.getSaveFileName(self, \ | |
216 "Select new projectfile", "", "lcfos Project files (*.lcp2)") | |
217 if filename: | |
218 self.project = Project() | |
219 self.project.filename = filename | |
220 self.project.save() | |
64 | 221 |
101 | 222 def newFile(self): |
152 | 223 ce = CodeEdit() |
162 | 224 ce.textChanged.connect(self.buildProject) |
225 w = self.mdiArea.addSubWindow(ce) | |
152 | 226 ce.show() |
162 | 227 w.resize(500, 700) |
152 | 228 return ce |
101 | 229 def openFile(self): |
230 filename = QFileDialog.getOpenFileName(self, "Open K# file...", "*.ks", "K# source files (*.ks)") | |
231 if filename: | |
232 self.loadFile(filename) | |
233 def saveFile(self): | |
234 ac = self.activeMdiChild() | |
235 if ac: | |
236 ac.save() | |
237 | |
67 | 238 def saveProject(self): |
239 self.project.save() | |
64 | 240 |
67 | 241 def closeProject(self): |
64 | 242 ac = self.activeMdiChild() |
243 if ac: | |
244 self.mdiArea.removeSubWindow(ac) | |
245 | |
246 def loadFile(self, filename): | |
247 # Find existing mdi widget: | |
248 wid = self.findMdiChild(filename) | |
249 if wid: | |
250 self.mdiArea.setActiveSubWindow(wid.parent()) | |
251 return wid | |
252 | |
253 # Create a new one: | |
254 ce = CodeEdit() | |
101 | 255 |
256 #source = self.project.loadProjectFile(filename) | |
257 try: | |
258 with open(filename) as f: | |
259 source = f.read() | |
260 ce.setSource(source) | |
261 except Exception as e: | |
262 print('exception opening file', e) | |
64 | 263 self.mdiArea.addSubWindow(ce) |
101 | 264 ce.setFileName(filename) |
64 | 265 ce.show() |
266 return ce | |
267 | |
67 | 268 def loadProject(self, filename): |
269 self.project = Project(filename) | |
270 self.projectview.setProject(self.project) | |
271 | |
272 def openProject(self): | |
273 filename = QFileDialog.getOpenFileName(self, \ | |
274 "Choose project file", "", "lcfos Project files (*.lcp2)") | |
275 if filename: | |
276 self.loadProject(filename) | |
277 | |
64 | 278 # MDI: |
279 def activeMdiChild(self): | |
280 aw = self.mdiArea.activeSubWindow() | |
281 if aw: | |
282 return aw.widget() | |
283 else: | |
284 return None | |
285 | |
286 def findMdiChild(self, filename): | |
287 for window in self.mdiArea.subWindowList(): | |
288 wid = window.widget() | |
289 if wid.filename == filename: | |
290 return wid | |
291 return None | |
292 | |
293 def allChildren(self): | |
294 c = [] | |
295 for window in self.mdiArea.subWindowList(): | |
296 wid = window.widget() | |
297 c.append(wid) | |
298 return c | |
299 | |
300 # Settings: | |
301 def loadSettings(self): | |
302 if self.settings.contains('mainwindowstate'): | |
303 self.restoreState(self.settings.value('mainwindowstate')) | |
304 if self.settings.contains('mainwindowgeometry'): | |
305 self.restoreGeometry(self.settings.value('mainwindowgeometry')) | |
101 | 306 if self.settings.contains('lastfile'): |
307 self.loadFile(self.settings.value('lastfile')) | |
64 | 308 |
309 def closeEvent(self, ev): | |
310 self.settings.setValue('mainwindowstate', self.saveState()) | |
311 self.settings.setValue('mainwindowgeometry', self.saveGeometry()) | |
101 | 312 #if self.project: |
313 # self.settings.setValue('openedproject', self.project.filename) | |
314 # # TODO: ask for save of opened files | |
315 ac = self.activeMdiChild() | |
316 if ac: | |
162 | 317 if hasattr(ac, 'filename') and ac.filename: |
101 | 318 self.settings.setValue('lastfile', ac.filename) |
152 | 319 else: |
320 self.settings.remove('lastfile') | |
321 else: | |
322 self.settings.remove('lastfile') | |
64 | 323 ev.accept() |
324 | |
325 # Error handling: | |
326 def nodeSelected(self, node): | |
327 ce = self.activeMdiChild() | |
328 if not ce: | |
329 return | |
165 | 330 if node.loc: |
331 row, col = node.loc.row, node.loc.col | |
332 ce.setRowCol( row, col ) | |
168 | 333 ce.setFocus() |
64 | 334 else: |
335 ce.clearErrors() | |
336 | |
337 def errorSelected(self, err): | |
338 ce = self.activeMdiChild() | |
339 if not ce: | |
340 return | |
163 | 341 ce.setRowCol(err.loc.row, err.loc.col) |
167 | 342 ce.setFocus() |
64 | 343 |
344 # Project loading: | |
345 | |
346 # Build recepy: | |
101 | 347 def buildFile(self): |
348 ce = self.activeMdiChild() | |
349 if ce: | |
162 | 350 source = ce.Source |
101 | 351 self.buildOutput.clear() |
165 | 352 self.buildOutput.append('Starting build') |
353 ir = self.c3front.build(source) | |
354 ast = self.c3front.pkg | |
102 | 355 self.astViewer.setAst(ast) |
101 | 356 self.buildOutput.append("Done!") |
67 | 357 def buildProject(self): |
64 | 358 """ Build project """ |
359 self.buildOutput.clear() | |
152 | 360 self.diag.diags.clear() |
361 self.buildFile() | |
64 | 362 |
152 | 363 self.builderrors.setErrorList(self.diag.diags) |
364 #self.astViewer.setAst(mods[0]) | |
365 for err in self.diag.diags: | |
366 self.buildOutput.append(str(err)) | |
154 | 367 ce = self.activeMdiChild() |
368 if ce: | |
369 ce.setErrors(self.diag.diags) | |
64 | 370 self.buildOutput.append("Done!") |
371 | |
1 | 372 if __name__ == '__main__': |
373 app = QApplication(sys.argv) | |
374 ide = Ide() | |
375 ide.show() | |
376 app.exec_() | |
377 |