Mercurial > lcfOS
annotate python/ide.py @ 256:225f444019b1
Added build and flash menu option
author | Windel Bouwman |
---|---|
date | Sun, 04 Aug 2013 18:32:04 +0200 |
parents | 7416c923a02a |
children | 04c19282a5aa |
rev | line source |
---|---|
99 | 1 #!/usr/bin/python |
2 | |
248 | 3 import sys |
4 import os | |
255 | 5 import logging |
64 | 6 |
7 from PyQt4.QtCore import * | |
8 from PyQt4.QtGui import * | |
1 | 9 |
10 # Compiler imports: | |
106 | 11 import ppci |
100 | 12 from astviewer import AstViewer |
162 | 13 from codeedit import CodeEdit |
131 | 14 stutil = __import__('st-util') |
165 | 15 import c3 |
249 | 16 import zcc |
17 import outstream | |
64 | 18 |
256 | 19 logformat='%(asctime)s|%(levelname)s|%(name)s|%(msg)s' |
248 | 20 |
64 | 21 class BuildOutput(QTextEdit): |
248 | 22 """ Build output component """ |
23 def __init__(self, parent=None): | |
255 | 24 super(BuildOutput, self).__init__(parent) |
256 | 25 fmt = logging.Formatter(fmt=logformat) |
255 | 26 class MyHandler(logging.Handler): |
27 def emit(self2, x): | |
28 self.append(str(fmt.format(x))) | |
29 | |
30 logging.getLogger().addHandler(MyHandler()) | |
31 self.setCurrentFont(QFont('Courier')) | |
32 self.setReadOnly(True) | |
33 logging.info('Build output will appear here!') | |
64 | 34 |
248 | 35 |
167 | 36 class BuildErrors(QTreeView): |
248 | 37 sigErrorSelected = pyqtSignal(object) |
38 def __init__(self, parent=None): | |
39 super(BuildErrors, self).__init__(parent) | |
40 model = QStandardItemModel() | |
41 self.setModel(model) | |
42 self.clicked.connect(self.itemSelected) | |
43 self.errorIcon = QIcon('icons/error.png') | |
44 self.model = QStandardItemModel() | |
45 self.model.setHorizontalHeaderLabels(['Message', 'Row', 'Column']) | |
46 self.setModel(self.model) | |
169
ee0d30533dae
Added more tests and improved the diagnostic update
Windel Bouwman
parents:
168
diff
changeset
|
47 |
248 | 48 def setErrorList(self, errorlist): |
169
ee0d30533dae
Added more tests and improved the diagnostic update
Windel Bouwman
parents:
168
diff
changeset
|
49 c = self.model.rowCount() |
ee0d30533dae
Added more tests and improved the diagnostic update
Windel Bouwman
parents:
168
diff
changeset
|
50 self.model.removeRows(0, c) |
64 | 51 for e in errorlist: |
167 | 52 item = QStandardItem(self.errorIcon, str(e.msg)) |
64 | 53 item.setData(e) |
167 | 54 irow = QStandardItem(str(e.loc.row)) |
55 irow.setData(e) | |
56 icol = QStandardItem(str(e.loc.col)) | |
57 icol.setData(e) | |
169
ee0d30533dae
Added more tests and improved the diagnostic update
Windel Bouwman
parents:
168
diff
changeset
|
58 self.model.appendRow([item, irow, icol]) |
248 | 59 |
60 def itemSelected(self, index): | |
256 | 61 if not index.isValid(): |
62 return | |
63 item = self.model.itemFromIndex(index) | |
64 err = item.data() | |
65 self.sigErrorSelected.emit(err) | |
64 | 66 |
67 | |
68 class AboutDialog(QDialog): | |
248 | 69 def __init__(self, parent=None): |
70 super(AboutDialog, self).__init__(parent) | |
71 self.setWindowTitle('About') | |
72 l = QVBoxLayout(self) | |
73 txt = QTextEdit(self) | |
74 txt.setReadOnly(True) | |
75 aboutText = """<h1>lcfOS IDE</h1> | |
76 <p>An all-in-one IDE for OS development.</p> | |
77 <p>https://www.assembla.com/spaces/lcfOS/wiki</p> | |
78 <p>Author: Windel Bouwman</p> | |
79 """ | |
80 txt.append(aboutText) | |
81 l.addWidget(txt) | |
82 but = QPushButton('OK') | |
83 but.setDefault(True) | |
84 but.clicked.connect(self.close) | |
85 l.addWidget(but) | |
86 | |
64 | 87 |
88 class Ide(QMainWindow): | |
248 | 89 def __init__(self, parent=None): |
90 super(Ide, self).__init__(parent) | |
256 | 91 self.to_open_files = [] |
255 | 92 |
248 | 93 self.setWindowTitle('LCFOS IDE') |
94 icon = QIcon('icons/logo.png') | |
95 self.setWindowIcon(icon) | |
64 | 96 |
248 | 97 # Create menus: |
98 mb = self.menuBar() | |
99 self.fileMenu = mb.addMenu('File') | |
100 self.viewMenu = mb.addMenu('View') | |
101 self.helpMenu = mb.addMenu('Help') | |
64 | 102 |
248 | 103 # Create mdi area: |
104 self.mdiArea = QMdiArea() | |
105 self.mdiArea.setViewMode(QMdiArea.TabbedView) | |
249 | 106 self.mdiArea.setTabsClosable(True) |
256 | 107 self.mdiArea.setTabsMovable(True) |
248 | 108 self.setCentralWidget(self.mdiArea) |
64 | 109 |
248 | 110 # Create components: |
111 def addComponent(name, widget): | |
112 dw = QDockWidget(name) | |
113 dw.setWidget(widget) | |
114 dw.setObjectName(name) | |
115 self.addDockWidget(Qt.RightDockWidgetArea, dw) | |
116 self.viewMenu.addAction(dw.toggleViewAction()) | |
117 return widget | |
64 | 118 |
248 | 119 self.buildOutput = addComponent('Build output', BuildOutput()) |
120 self.astViewer = addComponent('AST viewer', AstViewer()) | |
255 | 121 self.astViewer.sigNodeSelected.connect(lambda node: self.showLoc(node.loc)) |
248 | 122 self.builderrors = addComponent('Build errors', BuildErrors()) |
255 | 123 self.builderrors.sigErrorSelected.connect(lambda err: self.showLoc(err.loc)) |
248 | 124 self.devxplr = addComponent('Device explorer', stutil.DeviceExplorer()) |
125 self.regview = addComponent('Registers', stutil.RegisterView()) | |
126 self.memview = addComponent('Memory', stutil.MemoryView()) | |
127 self.ctrlToolbar = stutil.DebugToolbar() | |
128 self.addToolBar(self.ctrlToolbar) | |
129 self.ctrlToolbar.setObjectName('debugToolbar') | |
130 self.devxplr.deviceSelected.connect(self.regview.mdl.setDevice) | |
131 self.ctrlToolbar.statusChange.connect(self.memview.refresh) | |
132 self.devxplr.deviceSelected.connect(self.memview.setDevice) | |
133 self.devxplr.deviceSelected.connect(self.ctrlToolbar.setDevice) | |
134 self.ctrlToolbar.statusChange.connect(self.regview.refresh) | |
135 self.ctrlToolbar.codePosition.connect(self.pointCode) | |
64 | 136 |
248 | 137 # About dialog: |
138 self.aboutDialog = AboutDialog() | |
139 self.aboutDialog.setWindowIcon(icon) | |
140 # Create actions: | |
141 def addMenuEntry(name, menu, callback, shortcut=None): | |
142 a = QAction(name, self) | |
143 menu.addAction(a) | |
144 a.triggered.connect(callback) | |
145 if shortcut: | |
146 a.setShortcut(shortcut) | |
163 | 147 |
248 | 148 addMenuEntry("New", self.fileMenu, self.newFile, shortcut=QKeySequence(QKeySequence.New)) |
149 addMenuEntry("Open", self.fileMenu, self.openFile, shortcut=QKeySequence(QKeySequence.Open)) | |
150 addMenuEntry("Save", self.fileMenu, self.saveFile, shortcut=QKeySequence(QKeySequence.Save)) | |
151 addMenuEntry("Build", self.fileMenu, self.buildFile, shortcut=QKeySequence("F7")) | |
256 | 152 addMenuEntry("Build and flash", self.fileMenu, self.buildFileAndFlash, shortcut=QKeySequence("F8")) |
152 | 153 |
248 | 154 self.helpAction = QAction('Help', self) |
155 self.helpAction.setShortcut(QKeySequence('F1')) | |
156 self.helpMenu.addAction(self.helpAction) | |
157 addMenuEntry('About', self.helpMenu, self.aboutDialog.open) | |
158 | |
159 addMenuEntry('Cascade windows', self.viewMenu, self.mdiArea.cascadeSubWindows) | |
160 addMenuEntry('Tile windows', self.viewMenu, self.mdiArea.tileSubWindows) | |
161 sb = self.statusBar() | |
247 | 162 |
248 | 163 # Load settings: |
164 self.settings = QSettings('windelsoft', 'lcfoside') | |
165 self.loadSettings() | |
166 self.diag = ppci.DiagnosticsManager() | |
167 self.c3front = c3.Builder(self.diag) | |
168 | |
169 # File handling: | |
170 def newFile(self): | |
171 self.newCodeEdit() | |
247 | 172 |
248 | 173 def openFile(self): |
174 filename = QFileDialog.getOpenFileName(self, "Open C3 file...", "*.c3", | |
175 "C3 source files (*.c3)") | |
176 if filename: | |
177 self.loadFile(filename) | |
64 | 178 |
248 | 179 def saveFile(self): |
180 ac = self.activeMdiChild() | |
181 if ac: | |
182 ac.save() | |
101 | 183 |
248 | 184 def loadFile(self, filename): |
185 ce = self.newCodeEdit() | |
186 try: | |
187 with open(filename) as f: | |
188 ce.Source = f.read() | |
189 ce.FileName = filename | |
190 except Exception as e: | |
191 print('exception opening file', e) | |
64 | 192 |
248 | 193 # MDI: |
194 def newCodeEdit(self): | |
195 ce = CodeEdit() | |
249 | 196 ce.textChanged.connect(self.parseFile) |
248 | 197 w = self.mdiArea.addSubWindow(ce) |
249 | 198 self.mdiArea.setActiveSubWindow(w) |
199 ce.showMaximized() | |
248 | 200 return ce |
201 | |
202 def activeMdiChild(self): | |
203 aw = self.mdiArea.activeSubWindow() | |
204 if aw: | |
205 return aw.widget() | |
206 | |
207 def findMdiChild(self, filename): | |
208 for wid in self.allChildren(): | |
209 if wid.filename == filename: | |
210 return wid | |
64 | 211 |
248 | 212 def allChildren(self): |
213 return [w.widget() for w in self.mdiArea.subWindowList()] | |
214 | |
215 # Settings: | |
216 def loadSettings(self): | |
217 if self.settings.contains('mainwindowstate'): | |
218 self.restoreState(self.settings.value('mainwindowstate')) | |
219 if self.settings.contains('mainwindowgeometry'): | |
220 self.restoreGeometry(self.settings.value('mainwindowgeometry')) | |
221 if self.settings.contains('lastfiles'): | |
222 lfs = self.settings.value('lastfiles') | |
256 | 223 self.to_open_files.extend(lfs) |
224 | |
225 def showEvent(self, ev): | |
226 super().showEvent(ev) | |
227 while self.to_open_files: | |
228 fn = self.to_open_files.pop(0) | |
229 self.loadFile(fn) | |
64 | 230 |
248 | 231 def closeEvent(self, ev): |
232 self.settings.setValue('mainwindowstate', self.saveState()) | |
233 self.settings.setValue('mainwindowgeometry', self.saveGeometry()) | |
234 ac = self.activeMdiChild() | |
235 lfs = [ce.FileName for ce in self.allChildren() if ce.FileName] | |
236 self.settings.setValue('lastfiles', lfs) | |
237 ev.accept() | |
238 | |
239 # Error handling: | |
240 def nodeSelected(self, node): | |
241 self.showLoc(node.loc) | |
242 | |
243 def showLoc(self, loc): | |
244 ce = self.activeMdiChild() | |
245 if not ce: | |
246 return | |
247 if loc: | |
248 ce.setRowCol(loc.row, loc.col) | |
249 ce.setFocus() | |
152 | 250 else: |
248 | 251 ce.clearErrors() |
64 | 252 |
248 | 253 def pointCode(self, p): |
249 | 254 # Lookup pc in debug infos: |
255 loc = None | |
256 if hasattr(self, 'debugInfo'): | |
257 for di in self.debugInfo: | |
258 if di.address > p: | |
259 loc = di.info | |
260 break | |
261 if loc: | |
262 ce = self.activeMdiChild() | |
263 if ce: | |
264 ce.ic.arrow = loc | |
265 self.showLoc(loc) | |
247 | 266 |
248 | 267 # Build recepy: |
249 | 268 def parseFile(self): |
248 | 269 ce = self.activeMdiChild() |
270 if not ce: | |
271 return | |
272 self.diag.clear() | |
251
6ed3d3a82a63
Added another c3 example. First import attempt
Windel Bouwman
parents:
250
diff
changeset
|
273 pkg = self.c3front.parse(ce.Source) |
64 | 274 |
248 | 275 # Set errors: |
276 self.builderrors.setErrorList(self.diag.diags) | |
277 ce.setErrors(self.diag.diags) | |
251
6ed3d3a82a63
Added another c3 example. First import attempt
Windel Bouwman
parents:
250
diff
changeset
|
278 self.astViewer.setAst(pkg) |
255 | 279 logging.info('Done!') |
64 | 280 |
249 | 281 def buildFile(self): |
282 ce = self.activeMdiChild() | |
283 if not ce: | |
284 return | |
285 self.diag.clear() | |
286 outs = outstream.TextOutputStream() | |
287 if not zcc.zcc(ce.Source, outs, self.diag): | |
288 # Set errors: | |
289 self.builderrors.setErrorList(self.diag.diags) | |
290 ce.setErrors(self.diag.diags) | |
291 return | |
292 | |
256 | 293 def buildFileAndFlash(self): |
294 ce = self.activeMdiChild() | |
295 if not ce: | |
296 return | |
297 self.diag.clear() | |
298 outs = outstream.TextOutputStream() | |
299 if not zcc.zcc(ce.Source, outs, self.diag, do_optimize=True): | |
300 # Set errors: | |
301 self.builderrors.setErrorList(self.diag.diags) | |
302 ce.setErrors(self.diag.diags) | |
303 return | |
304 | |
249 | 305 code_s = outs.getSection('code') |
306 self.debugInfo = code_s.debugInfos() | |
250 | 307 if self.ctrlToolbar.device: |
255 | 308 logging.info('Flashing stm32f4 discovery') |
250 | 309 bts = code_s.to_bytes() |
310 self.ctrlToolbar.device.writeFlash(0x08000000, bts) | |
311 stl = self.ctrlToolbar.device.iface | |
312 stl.reset() | |
313 stl.halt() | |
256 | 314 stl.run() |
255 | 315 logging.info('Done!') |
249 | 316 |
256 | 317 |
1 | 318 if __name__ == '__main__': |
256 | 319 logging.basicConfig(format=logformat, level=logging.DEBUG) |
240 | 320 app = QApplication(sys.argv) |
321 ide = Ide() | |
322 ide.show() | |
255 | 323 logging.info('IDE started') |
240 | 324 app.exec_() |
1 | 325 |