# HG changeset patch # User windel # Date 1356351899 -3600 # Node ID 4a37d6992bd3a3688f49ee51485cfcd7eb91f5c1 # Parent 1be00bcfaabb95d376b112092e135e8bc52d9c0c movage diff -r 1be00bcfaabb -r 4a37d6992bd3 python/apps/bouncing_cube.py --- a/python/apps/bouncing_cube.py Mon Dec 24 13:21:13 2012 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,406 +0,0 @@ -from PyQt4.QtGui import * -from PyQt4.QtCore import * -from PyQt4.QtOpenGL import QGLWidget -from OpenGL.GL import * -from OpenGL.GLU import gluPerspective -import sys -from random import random -from math import pi, cos, sin, fabs, sqrt -from numpy import mat, array, ones, zeros, eye -from numpy.linalg import norm -import numpy as np -import time -import scipy.integrate -#import pyopencl - -""" - Test script that lets a dice bounce. - Converted from 20-sim equations into python code. - - 20-sim website: - http://www.20sim.com - -""" -def drawCube(w): - glBegin(GL_QUADS) # Start Drawing The Cube - glColor3f(0.0,1.0,0.0) # Set The Color To Blue - glVertex3f( w, w,-w) # Top Right Of The Quad (Top) - glVertex3f(-w, w,-w) # Top Left Of The Quad (Top) - glVertex3f(-w, w, w) # Bottom Left Of The Quad (Top) - glVertex3f( w, w, w) # Bottom Right Of The Quad (Top) - - glColor3f(1.0,0.5,0.0) # Set The Color To Orange - glVertex3f( w,-w, w) # Top Right Of The Quad (Bottom) - glVertex3f(-w,-w, w) # Top Left Of The Quad (Bottom) - glVertex3f(-w,-w,-w) # Bottom Left Of The Quad (Bottom) - glVertex3f( w,-w,-w) # Bottom Right Of The Quad (Bottom) - - glColor3f(1.0,0.0,0.0) # Set The Color To Red - glVertex3f( w, w, w) # Top Right Of The Quad (Front) - glVertex3f(-w, w, w) # Top Left Of The Quad (Front) - glVertex3f(-w,-w, w) # Bottom Left Of The Quad (Front) - glVertex3f( w,-w, w) # Bottom Right Of The Quad (Front) - - glColor3f(1.0,1.0,0.0) # Set The Color To Yellow - glVertex3f( w,-w,-w) # Bottom Left Of The Quad (Back) - glVertex3f(-w,-w,-w) # Bottom Right Of The Quad (Back) - glVertex3f(-w, w,-w) # Top Right Of The Quad (Back) - glVertex3f( w, w,-w) # Top Left Of The Quad (Back) - - glColor3f(0.0,0.0,1.0) # Set The Color To Blue - glVertex3f(-w, w, w) # Top Right Of The Quad (Left) - glVertex3f(-w, w,-w) # Top Left Of The Quad (Left) - glVertex3f(-w,-w,-w) # Bottom Left Of The Quad (Left) - glVertex3f(-w,-w, w) # Bottom Right Of The Quad (Left) - - glColor3f(1.0,0.0,1.0) # Set The Color To Violet - glVertex3f( w, w,-w) # Top Right Of The Quad (Right) - glVertex3f( w, w, w) # Top Left Of The Quad (Right) - glVertex3f( w,-w, w) # Bottom Left Of The Quad (Right) - glVertex3f( w,-w,-w) # Bottom Right Of The Quad (Right) - glEnd() # Done Drawing The Quad - -def drawFloor(w, h): - glBegin(GL_QUADS) # Start Drawing The Cube - - glColor3f(1.0,0.5,0.0) # Set The Color To Orange - glVertex3f( w,-w,h)# Top Right Of The Quad (Bottom) - glVertex3f(-w,-w,h)# Top Left Of The Quad (Bottom) - glVertex3f(-w,w,h)# Bottom Left Of The Quad (Bottom) - glVertex3f( w,w,h)# Bottom Right Of The Quad (Bottom) - glEnd() # Done Drawing The Quad - -def drawAxis(): - glLineWidth(0.5) - glBegin(GL_LINES) - glColor3f(1.0, 0.0, 0.0) - glVertex3f(0,0,0) - glVertex3f(1,0,0) - glColor3f(0.0, 1.0, 0.0) - glVertex3f(0,0,0) - glVertex3f(0,1,0) - glColor3f(0.0, 0.0, 1.0) - glVertex3f(0,0,0) - glVertex3f(0,0,1) - glEnd() - - -def cross(A, B): - a = A.A1 - b = B.A1 - return mat(np.cross(a, b)).T - -def skew(X): - Y = mat(zeros( (3, 3) )) - a,b,c = X.A1 - Y[0,1] = -c - Y[0,2] = b - Y[1,0] = c - Y[1,2] = -a - Y[2,0] = -b - Y[2,1] = a - return Y - -def adjoint(T): - W = T[0:3, 0] - V = T[3:6, 0] - a = mat(zeros( (6,6) ) ) - a[0:3, 0:3] = skew(W) - a[3:6, 0:3] = skew(V) - a[3:6, 3:6] = skew(W) - return a - -def Adjoint(H): - R = H[0:3, 0:3] - P = H[0:3, 3] - a = mat(zeros( (6,6) ) ) - a[0:3, 0:3] = R - a[3:6, 3:6] = R - a[3:6, 0:3] = skew(P) * R - return a - -def quatToR(q): - x, y, z, w = q.A1 - r = mat(eye(3)) - r[0,0] = 1 - (2*y**2+2*z**2) - r[0,1] = 2*x*y+2*z*w - r[0,2] = 2*x*z - 2*y*w - r[1,0] = 2*x*y-2*z*w - r[1,1] = 1 - (2*x**2 + 2*z**2) - r[1,2] = 2*y*z + 2*x*w - r[2,0] = 2*x*z+2*y*w - r[2,1] = 2*y*z - 2*x*w - r[2,2] = 1 - (2*x**2+2*y**2) - return r - -def rotateAbout(axis, angle): - ax, ay, az = (axis/norm(axis)).A1 - qx = ax*sin(angle/2.0) - qy = ay*sin(angle/2.0) - qz = az*sin(angle/2.0) - qw = cos(angle/2.0) - q = mat(array([qx,qy,qz,qw])).T - return q - -def normalizeQuaternion(quat): - x,y,z,w = quat.A1 - magnitude = sqrt(x*x + y*y + z*z + w*w) - x = x / magnitude - y = y / magnitude - z = z / magnitude - w = w / magnitude - quat[0, 0] = x - quat[1, 0] = y - quat[2, 0] = z - quat[3, 0] = w - return quat - -def VTo4x4(V): - v1, v2, v3 = V.A1 - return mat(array( \ - [[0.0, -v3, v2, -v1], \ - [ v3, 0.0, -v1, -v2], \ - [-v2, v1, 0.0, -v3], \ - [v1, v2, v3, 0.0] ])) - -def homogeneous(R,p): - H = mat(eye(4)) - H[0:3, 0:3] = R - H[0:3, 3] = p - return H - -def rateOfChange(states, thetime, parameters): - quat = states[0:4, 0] # Orientation (4) - pos = states[4:7, 0] # Position (3) - P = states[7:13, 0] # Momentum (6) - massI, gravity = parameters - # Rigid body parts: - # Forward Kinematic chain: - H = homogeneous(quatToR(quat), pos) # Forward kinematics - - AdjX2 = mat(eye(6)) # The connectionpoint in the real world - adjAdjX2_1 = adjoint(AdjX2[0:6,0]) - adjAdjX2_2 = adjoint(AdjX2[0:6,1]) - adjAdjX2_3 = adjoint(AdjX2[0:6,2]) - adjAdjX2_4 = adjoint(AdjX2[0:6,3]) - adjAdjX2_5 = adjoint(AdjX2[0:6,4]) - adjAdjX2_6 = adjoint(AdjX2[0:6,5]) - AdjInv2 = Adjoint(H.I) - M2 = AdjInv2.T * (massI * AdjInv2) # Transfor mass to base frame - MassMatrix = M2 - - wrenchGrav2 = mat( zeros((1,6)) ) - wrenchGrav2[0, 0:3] = -cross(gravity, pos).T - wrenchGrav2[0, 3:6] = gravity.T - - Bk = mat( zeros( (6,6) )) - Bk[0:3, 0:3] = skew(P[0:3, 0]) - Bk[3:6, 0:3] = skew(P[3:6, 0]) - Bk[0:3, 3:6] = skew(P[3:6, 0]) - - # TODO: do this a cholesky: - v = np.linalg.solve(MassMatrix, P) # Matrix inverse like thingy ! - - T2_00 = v # Calculate the relative twist! - TM2 = T2_00.T * M2 - twistExternal = T2_00 # Twist van het blokje - TMSum2 = TM2 - - PDotBodies = mat( zeros( (6,1)) ) - PDotBodies[0,0] = TMSum2 * (adjAdjX2_1 * T2_00) - PDotBodies[1,0] = TMSum2 * (adjAdjX2_2 * T2_00) - PDotBodies[2,0] = TMSum2 * (adjAdjX2_3 * T2_00) - PDotBodies[3,0] = TMSum2 * (adjAdjX2_4 * T2_00) - PDotBodies[4,0] = TMSum2 * (adjAdjX2_5 * T2_00) - PDotBodies[5,0] = TMSum2 * (adjAdjX2_6 * T2_00) - - PDot = -PDotBodies - Bk * v - PDot += wrenchGrav2.T - - ##### Contact wrench part: - HB_W = H # Is H-matrix van het blokje - WrenchB = mat(zeros( (1,6) )) - for px in [-0.5, 0.5]: - for py in [-0.5, 0.5]: - for pz in [-0.5, 0.5]: - HB1_B = homogeneous(mat(eye(3)), mat([px,py,pz]).T) - HB1_W = HB_W * HB1_B - HW1_W = homogeneous(mat(eye(3)), HB1_W[0:3,3]) - HW_W1 = HW1_W.I - HB_W1 = HW_W1 * HB_W - - AdjHB_W1 = Adjoint(HB_W1) - TB_W1_W1 = AdjHB_W1 * twistExternal - z = HB1_W[2, 3] - vx, vy, vz = TB_W1_W1[3:6, 0].A1 - if z < 0: - # Contact forces: - Fx = -50.0*vx - Fy = -50.0*vy - Fz = -z*50000.0 - else: - Fx = 0.0 - Fy = 0.0 - Fz = 0.0 - # TODO: reflect impulse - WrenchW1 = mat([0,0,0,0,0,Fz]) - # Transform it back: - WrenchB += (AdjHB_W1.T * WrenchW1.T).T - ##### End of contact wrench - - PDot += (WrenchB * AdjInv2).T - - # Position and orientation rates: - QOmega = VTo4x4(v[0:3, 0]) - quatDot = 0.5 * QOmega * quat - vel = v[3:6, 0] - posDot = skew(v[0:3]) * pos + vel - # The rate vector: - rates = mat(zeros( (13,1) )) - rates[0:4, 0] = quatDot - rates[4:7, 0] = posDot - rates[7:13, 0] = PDot - return rates - -def fWrapper(y, t, parameters): - y = mat(y).T - dy = rateOfChange(y, t, parameters) - return dy.T.A1 - -def SCIPY(endtime, dt, state, parameters): - times = np.arange(0.0, endtime, dt) - y0 = state.T.A1 - res = scipy.integrate.odeint(fWrapper, y0, times, args=(parameters,)) - states = [] - res = res.T - r,c = res.shape - for ci in range(0,c): - states.append(mat(res[:,ci]).T) - return states - -def RK4(endtime, dt, state, parameters): - t = 0.0 - states = [] - while t < endtime: - newstate = mat (zeros( state.shape )) # Create a new object - - #### Runge Kutta integration: - k1 = rateOfChange(state, t, parameters) - k2 = rateOfChange(state + 0.5*dt*k1, t, parameters) - k3 = rateOfChange(state + 0.5*dt*k1, t, parameters) - k4 = rateOfChange(state + dt*k3, t, parameters) - newstate = state + (dt/6.0)*(k1+2*k2+2*k3+k4) - - # Normalize quat: - newstate[0:4, 0] = normalizeQuaternion(newstate[0:4, 0]) - states.append(newstate) - - state = newstate - - t += dt - print state[6,0], t, ' ', (t/endtime)*100.0, '%' - return states - - -def simulate(endtime, dt): - PInitial = mat( zeros((6,1)) ) - posInitial = mat(array([-1.2, -1.3, 2.8])).T - quatInitial = rotateAbout(mat(array([0.2, 1.0, 0.4])).T, 0.5) - # Parameters: - gravity = mat( array([0,0,-9.81]) ).T - massI = mat(eye(6)) * 1.01 # Mass matrix - parameters = (massI, gravity) - - # The state vector! - state = mat(zeros( (13,1) )) - state[0:4, 0] = quatInitial - state[4:7, 0] = posInitial - state[7:13, 0] = PInitial - - return SCIPY(endtime, dt, state, parameters) - -class W(QGLWidget): - time = 0.0 - index = 0 - def __init__(self, states, dt, parent=None): - super(W, self).__init__(parent) - self.firstRun = True - self.savePNGS = False - self.dt = dt - self.resize(500,500) - self.states = states - self.UP() - t = QTimer(self) - t.timeout.connect(self.UP) - t.start(self.dt*1000.0) - - def UP(self): - self.time += self.dt - if self.states: - state = self.states[self.index] - self.Dicequat = state[0:4, 0] - self.Dicepos = state[4:7, 0] - self.index += 1 - if self.index == len(self.states): - self.index = 0 - self.firstRun = False - # Paint: - self.update() - if self.firstRun: - # Create png images for the movie: - if self.savePNGS: - pm = self.renderPixmap() - pm.save('image'+str(self.index)+'.png') - - def initializeGL(self): - glClearColor(0.0, 0.5, 0.0, 1.0) - glEnable(GL_DEPTH_TEST) - glDepthFunc(GL_LESS) - glShadeModel(GL_SMOOTH) - def resizeGL(self,w,h): - glViewport(0, 0, w, h) - glMatrixMode(GL_PROJECTION) - glLoadIdentity() - gluPerspective(45.0, float(w)/float(h), 0.1, 100.0) - glMatrixMode(GL_MODELVIEW) - def paintGL(self): - glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT) # Clear buffers - glLoadIdentity() # Reset The View - - glLoadIdentity() - glTranslatef(0.0,-2.0,-10.0) # Move Left And Into The Screen - glRotatef(-90.0, 1.0, 0.0, 0.0) - drawFloor(2.0, 0.0) - drawAxis() - self.renderText(1.0, 0.0, 0.0, 'X') - self.renderText(0.0, 1.0, 0.0, 'Y') - self.renderText(0.0, 0.0, 1.0, 'Z') - - self.renderText(0.0,0.0,1.2,str(self.time)) - - x,y,z = self.Dicepos.A1 - R = quatToR(self.Dicequat) - - glTranslatef(x, y, z) - # Trick to rotate the openGL matrix: - r = R.A1 - rotR = (r[0], r[3], r[6], 0.0, - r[1], r[4], r[7], 0.0, - r[2], r[5], r[8], 0.0, - 0.0, 0.0, 0.0, 1.0) - glMultMatrixd(rotR) - - drawCube(0.6) - -et = 20.0 -dt = 0.04 -print 'starting integration... endtime =', et, ' stepsize =', dt -t0 = time.time() -states = simulate(et, dt) -t1 = time.time() -print 'That was heavy, it took me ', t1-t0, ' seconds!' -app = QApplication(sys.argv) -w = W(states, dt) -w.show() -sys.exit(app.exec_()) - diff -r 1be00bcfaabb -r 4a37d6992bd3 python/apps/build.py --- a/python/apps/build.py Mon Dec 24 13:21:13 2012 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,20 +0,0 @@ -import sys, os, argparse -sys.path.insert(0, os.path.join('..','libs')) - -# Compiler imports: -from compiler import Compiler -from project import Project - -if __name__ == '__main__': - parser = argparse.ArgumentParser(description='Build tool to build projects') - parser.add_argument('project', type=str, help='the project to be build') - args = parser.parse_args() - - try: - project = Project(args.project) - except IOError: - print('Failed to load {0}'.format(args.project)) - sys.exit(3) - pc = Compiler() - pc.compileProject(project) - diff -r 1be00bcfaabb -r 4a37d6992bd3 python/apps/diagrameditor.py --- a/python/apps/diagrameditor.py Mon Dec 24 13:21:13 2012 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,314 +0,0 @@ -#!/usr/bin/python - -from PyQt4.QtGui import * -from PyQt4.QtCore import * -import sys, json, base64 - -from diagramitems import Connection, ResizeSelectionHandle, Block, DiagramScene, CodeBlock -from icons import newicon, saveicon, loadicon -import diagramitems - -""" - Author: Windel Bouwman - Year: 2012 - Description: This script implements a diagram editor. - run with python 3.x as: - $ python [thisfile.py] -""" -def indent(lines): - return [' ' + line for line in lines] - -class ParameterDialog(QDialog): - def __init__(self, block, parent = None): - super(ParameterDialog, self).__init__(parent) - self.block = block - self.button = QPushButton('Ok', self) - self.nameEdit = QLineEdit(self.block.name) - self.codeEdit = QTextEdit(self) - self.codeEdit.setPlainText(self.block.code) - l = QFormLayout(self) - l.addRow('Name:', self.nameEdit) - l.addRow('Code:', self.codeEdit) - l.addWidget(self.button) - self.button.clicked.connect(self.OK) - def OK(self): - self.block.name = self.nameEdit.text() - self.block.code = self.codeEdit.toPlainText() - self.close() - -class EditorGraphicsView(QGraphicsView): - def __init__(self, parent=None): - QGraphicsView.__init__(self, parent) - self.setDragMode(QGraphicsView.RubberBandDrag) - self.delShort = QShortcut(QKeySequence.Delete, self) - self._model = None - self.treeView = QTreeView() - self.treeView.activated.connect(self.itemActivated) - def itemActivated(self, idx): - b = idx.internalPointer() - s = b.scene() - s.clearSelection() - b.setSelected(True) - def setDiagram(self, d): - self.setScene(d) - self.delShort.activated.connect(d.deleteItems) - def getModel(self): - return self._model - def setModel(self, m): - self._model = m - if m: - self.treeView.setModel(m) - self.diagram = m.rootDiagram - self.model.modelReset.connect(self.treeView.expandAll) - model = property(getModel, setModel) - diagram = property(lambda s: s.scene(), setDiagram) - def save(self): - if self.model: - if not self.model.filename: - self.model.filename = QFileDialog.getSaveFileName(self) - if self.model.filename: - with open(self.model.filename, 'w') as f: - f.write(json.dumps(self.model.Dict, indent=2)) - def load(self): - filename = QFileDialog.getOpenFileName(self) - if filename: - self.model = loadModel(filename) - def newModel(self): - self.model = ModelHierarchyModel() - def goUp(self): - if hasattr(self.diagram, 'containingBlock'): - self.diagram = self.diagram.containingBlock.scene() - self.zoomAll() - def showCode(self): - if self.model: - c = self.model.gencode() - c = '\n'.join(c) - d = QDialog() - l = QFormLayout(d) - codeview = QTextEdit() - codeview.setPlainText(c) - l.addRow('code', codeview) - runButton = QPushButton('Run') - outputview = QTextEdit() - l.addRow('Output', outputview) - l.addWidget(runButton) - def print2(txt): - txt2 = outputview.toPlainText() - outputview.setPlainText(txt2 + '\n' + txt) - def runIt(): - outputview.clear() - globs = {'print': print2} - exec(codeview.toPlainText(), globs) - runButton.clicked.connect(runIt) - d.exec_() - def zoomAll(self): - """ zoom to fit all items """ - rect = self.diagram.itemsBoundingRect() - self.fitInView(rect, Qt.KeepAspectRatio) - def wheelEvent(self, event): - pos = event.pos() - posbefore = self.mapToScene(pos) - degrees = event.delta() / 8.0 - sx = (100.0 + degrees) / 100.0 - self.scale(sx, sx) - event.accept() - def dragEnterEvent(self, event): - if event.mimeData().hasFormat('component/name'): - event.accept() - def dragMoveEvent(self, event): - if event.mimeData().hasFormat('component/name'): event.accept() - def dropEvent(self, event): - if event.mimeData().hasFormat('component/name'): - name = bytes(event.mimeData().data('component/name')).decode() - kind, name = name.split(':') - pos = self.mapToScene(event.pos()) - s = self.scene() - print(kind, 'name:', name) - kind = getattr(diagramitems, kind) - print(kind) - b = kind(s.uniqify(name)) - b.setPos(pos) - s.addItem(b) - -class LibraryModel(QStandardItemModel): - mimeTypes = lambda self: ['component/name'] - def mimeData(self, idxs): - mimedata = QMimeData() - for idx in idxs: - if idx.isValid(): - txt = self.data(idx, Qt.DisplayRole) - mimedata.setData('component/name', txt) - return mimedata - -class ModelHierarchyModel(QAbstractItemModel): - def __init__(self): - super(ModelHierarchyModel, self).__init__() - self.rootDiagram = DiagramScene() - self.rootDiagram.structureChanged.connect(self.handlechange) - self.filename = None - def handlechange(self): - self.modelReset.emit() - def setDict(self, d): - self.rootDiagram.Dict = d - self.modelReset.emit() - def getDict(self): - return self.rootDiagram.Dict - Dict = property(getDict, setDict) - def gencode(self): - c = ['def topLevel():'] - c += indent(self.rootDiagram.gencode()) - c.append('print("Running model")') - c.append('topLevel()') - c.append('print("Done")') - return c - def index(self, row, column, parent=None): - if parent.isValid(): - parent = parent.internalPointer().subModel - else: - parent = self.rootDiagram - blocks = sorted(parent.blocks, key=lambda b: b.name) - block = blocks[row] - # Store the index to retrieve it later in the parent function. - # TODO: solve this in a better way. - block.index = self.createIndex(row, column, block) - return block.index - def parent(self, index): - if index.isValid(): - block = index.internalPointer() - if block.scene() == self.rootDiagram: - return QModelIndex() - else: - print(block) - outerBlock = block.scene().containingBlock - return outerBlock.index - print('parent: No valid index') - def data(self, index, role): - if index.isValid() and role == Qt.DisplayRole: - b = index.internalPointer() - if index.column() == 0: - return b.name - elif index.column() == 1: - return str(type(b)) - def headerData(self, section, orientation, role): - if orientation == Qt.Horizontal and role == Qt.DisplayRole: - if section == 0: - return "Element" - elif section == 1: - return "Type" - else: - return "x" - def rowCount(self, parent): - if parent.column() > 0: - return 0 - if parent.isValid(): - block = parent.internalPointer() - if hasattr(block, 'subModel'): - return len(block.subModel.blocks) - else: - return 0 - else: - return len(self.rootDiagram.blocks) - def columnCount(self, parent): - return 2 - -class LibraryWidget(QListView): - def __init__(self): - super(LibraryWidget, self).__init__(None) - self.libraryModel = LibraryModel(self) - self.libraryModel.setColumnCount(1) - # Create an icon with an icon: - pixmap = QPixmap(60, 60) - pixmap.fill() - painter = QPainter(pixmap) - painter.fillRect(10, 10, 40, 40, Qt.blue) - painter.setBrush(Qt.yellow) - painter.drawEllipse(20, 20, 20, 20) - painter.end() - # Fill library: - for name in ['CodeBlock:codeBlock', 'DiagramBlock:submod', 'Block:blk']: - self.libraryModel.appendRow(QStandardItem(QIcon(pixmap), name)) - self.setModel(self.libraryModel) - self.setViewMode(self.IconMode) - self.setDragDropMode(self.DragOnly) - -def warning(txt): - QMessageBox.warning(None, "Warning", txt) - -def loadModel(filename): - try: - m = ModelHierarchyModel() - with open(filename, 'r') as f: data = f.read() - m.filename = filename - m.Dict = json.loads(data) - return m - except KeyError: - warning('Corrupt model: {0}'.format(filename)) - except ValueError: - warning('Corrupt model: {0}'.format(filename)) - except FileNotFoundError: - warning('File [{0}] not found'.format(filename)) - -class Main(QMainWindow): - def __init__(self): - super(Main, self).__init__(None) - self.editor = EditorGraphicsView() - self.setCentralWidget(self.editor) - self.setWindowTitle("Diagram editor") - def buildIcon(b64): - icon = base64.decodestring(b64) - pm = QPixmap() - pm.loadFromData(icon) - return QIcon(pm) - toolbar = self.addToolBar('Tools') - toolbar.setObjectName('Tools') - def act(name, shortcut, callback, icon=None): - a = QAction(icon, name, self) if icon else QAction(name, self) - a.setShortcuts(shortcut) - a.triggered.connect(callback) - toolbar.addAction(a) - act('New', QKeySequence.New, self.editor.newModel, buildIcon(newicon)) - act('Save', QKeySequence.Save, self.editor.save, buildIcon(saveicon)) - act('Load', QKeySequence.Open, self.editor.load, buildIcon(loadicon)) - act('Full screen', QKeySequence("F11"), self.toggleFullScreen) - act('Fit in view', QKeySequence("F8"), self.editor.zoomAll) - act('Go up', QKeySequence(Qt.Key_Up), self.editor.goUp) - act('Model code', QKeySequence("F7"), self.editor.showCode) - def addDock(name, widget): - dock = QDockWidget(name, self) - dock.setObjectName(name) - dock.setWidget(widget) - self.addDockWidget(Qt.LeftDockWidgetArea, dock) - addDock('Library', LibraryWidget()) - addDock('Model tree', self.editor.treeView) - self.settings = QSettings('windelsoft', 'diagrameditor') - self.loadSettings() - def toggleFullScreen(self): - self.setWindowState(self.windowState() ^ Qt.WindowFullScreen) - self.editor.zoomAll() - 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('openedmodel'): - modelfile = self.settings.value('openedmodel') - self.editor.model = loadModel(modelfile) - def closeEvent(self, ev): - self.settings.setValue('mainwindowstate', self.saveState()) - self.settings.setValue('mainwindowgeometry', self.saveGeometry()) - if self.editor.model and self.editor.model.filename: - self.settings.setValue('openedmodel', self.editor.model.filename) - # TODO: ask for save of opened files - else: - self.settings.remove('openedmodel') - ev.accept() - -if __name__ == '__main__': - if sys.version_info.major != 3: - print('Please use python 3.x') - sys.exit(1) - app = QApplication(sys.argv) - main = Main() - main.show() - app.exec_() - diff -r 1be00bcfaabb -r 4a37d6992bd3 python/apps/diagramitems.py --- a/python/apps/diagramitems.py Mon Dec 24 13:21:13 2012 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,435 +0,0 @@ -""" - Contains all blocks that can be used to build models. -""" - -from PyQt4.QtGui import * -from PyQt4.QtCore import * - -def uniqify(name, names): - newname, i = name, 1 - while newname in names: newname, i = name + str(i), i + 1 - return newname - -def enum(**enums): - return type('Enum', (), enums) - -Position = enum(TOP=0, TOP_RIGHT=1, RIGHT=2, BOTTOM_RIGHT=3, BOTTOM=4, BOTTOM_LEFT=5, LEFT=6, TOP_LEFT=7) - -def buildPath(pts): - path = QPainterPath(pts[0]) - for pt in pts[1:]: path.lineTo(pt) - return path - -def equalSpace(n, l, offset=15): - if n == 1: - return [l / 2] - elif n > 1: - return [offset + (l - offset*2)/(n - 1)*i for i in range(n)] - return [] - -class Connection(QGraphicsPathItem): - """ A connection between blocks """ - def __init__(self, fromPort=None, toPort=None): - super(Connection, self).__init__() - self.pos2 = self.fromPort = self.toPort = None - self.setFlags(self.ItemIsSelectable | self.ItemClipsToShape) - pen = QPen(Qt.blue, 2, cap=Qt.RoundCap) - self.setPen(pen) - self.arrowhead = QGraphicsPathItem(self) - self.arrowhead.setPath(buildPath([QPointF(0.0, 0.0), QPointF(-6.0, 10.0), QPointF(6.0, 10.0), QPointF(0.0, 0.0)])) - self.arrowhead.setPen(pen) - self.arrowhead.setBrush(QBrush(pen.color())) - self.vias = [] - self.setFromPort(fromPort) - self.setToPort(toPort) - def getDict(self): - d = {} - d['fromBlock'] = self.fromPort.block.name - d['fromPort'] = self.fromPort.name - d['toBlock'] = self.toPort.block.name - d['toPort'] = self.toPort.name - return d - Dict = property(getDict) - def myDelete(self): - scene = self.scene() - if scene: - self.setFromPort(None) - self.setToPort(None) - scene.removeItem(self) - def setFromPort(self, fromPort): - if self.fromPort: - self.fromPort.posCallbacks.remove(self.setBeginPos) - self.fromPort.connection = None - self.fromPort = fromPort - if self.fromPort: - self.fromPort.connection = self - self.updateLineStukken() - self.fromPort.posCallbacks.append(self.setBeginPos) - def setToPort(self, toPort): - if self.toPort: - self.toPort.posCallbacks.remove(self.setEndPos) - self.toPort.connection = None - self.toPort = toPort - if self.toPort: - self.setEndPos(toPort.scenePos()) - self.toPort.connection = self - self.toPort.posCallbacks.append(self.setEndPos) - def getPos1(self): - if self.fromPort: - return self.fromPort.scenePos() - def setBeginPos(self, pos1): self.updateLineStukken() - def setEndPos(self, endpos): - self.pos2 = endpos - self.updateLineStukken() - def itemChange(self, change, value): - if change == self.ItemSelectedHasChanged: - for via in self.vias: - via.setVisible(value) - return super(Connection, self).itemChange(change, value) - def shape(self): return self.myshape - def updateLineStukken(self): - """ - This algorithm determines the optimal routing of all signals. - TODO: implement nice automatic line router - """ - pos1 = self.getPos1() - pos2 = self.pos2 - if pos1 is None or pos2 is None: - return - scene = self.scene() - vias = [pos1 + QPointF(20, 0)] + self.vias + [pos2 + QPointF(-20, 0)] - if scene: - litem = QGraphicsLineItem() - litem.setFlags(self.ItemIsSelectable) - scene.addItem(litem) - for p1, p2 in zip(vias[:-1], vias[1:]): - line = QLineF(p1, p2) - litem.setLine(line) - citems = scene.collidingItems(litem) - citems = [i for i in citems if type(i) is Block] - scene.removeItem(litem) - pts = [pos1] + vias + [pos2] - self.arrowhead.setPos(pos2) - self.arrowhead.setRotation(90) - p = buildPath(pts) - self.setPath(p) - """ Create a shape outline using the path stroker """ - s = super(Connection, self).shape() - pps = QPainterPathStroker() - pps.setWidth(10) - self.myshape = pps.createStroke(s).simplified() - -class PortItem(QGraphicsPathItem): - """ Represents a port to a subsystem """ - def __init__(self, name, block): - super(PortItem, self).__init__(block) - self.textItem = QGraphicsTextItem(self) - self.connection = None - self.block = block - self.setCursor(QCursor(Qt.CrossCursor)) - self.setPen(QPen(Qt.blue, 2, cap=Qt.RoundCap)) - self.name = name - self.posCallbacks = [] - self.setFlag(self.ItemSendsScenePositionChanges, True) - def getName(self): return self.textItem.toPlainText() - def setName(self, name): - self.textItem.setPlainText(name) - rect = self.textItem.boundingRect() - lw, lh = rect.width(), rect.height() - lx = 3 if type(self) is InputPort else -3 - lw - self.textItem.setPos(lx, -lh / 2) - name = property(getName, setName) - def getDict(self): - return {'name': self.name} - Dict = property(getDict) - def itemChange(self, change, value): - if change == self.ItemScenePositionHasChanged: - for cb in self.posCallbacks: cb(value) - return value - return super(PortItem, self).itemChange(change, value) - -class OutputPort(PortItem): - def __init__(self, name, block, d=10.0): - super(OutputPort, self).__init__(name, block) - self.setPath(buildPath([QPointF(0.0, -d), QPointF(d, 0), QPointF(0.0, d)])) - def mousePressEvent(self, event): - self.scene().startConnection(self) - -class InputPort(PortItem): - def __init__(self, name, block, d=10.0): - super(InputPort, self).__init__(name, block) - self.setPath(buildPath([QPointF(-d, -d), QPointF(0, 0), QPointF(-d, d)])) - -class Handle(QGraphicsEllipseItem): - """ A handle that can be moved by the mouse """ - def __init__(self, dx=10.0, parent=None): - super(Handle, self).__init__(QRectF(-0.5*dx,-0.5*dx,dx,dx), parent) - self.setBrush(QBrush(Qt.white)) - self.setFlags(self.ItemIsMovable) - self.setZValue(1) - self.setVisible(False) - self.setCursor(QCursor(Qt.SizeFDiagCursor)) - def mouseMoveEvent(self, event): - """ Move function without moving the other selected elements """ - p = self.mapToParent(event.pos()) - self.setPos(p) - -class ResizeSelectionHandle(Handle): - def __init__(self, position, block): - super(ResizeSelectionHandle, self).__init__(dx=12, parent=block) - self.position = position - self.block = block - if position in [Position.TOP_LEFT, Position.BOTTOM_RIGHT]: - self.setCursor(QCursor(Qt.SizeFDiagCursor)) - elif position in [Position.TOP_RIGHT, Position.BOTTOM_LEFT]: - self.setCursor(QCursor(Qt.SizeBDiagCursor)) - elif position in [Position.TOP, Position.BOTTOM]: - self.setCursor(QCursor(Qt.SizeVerCursor)) - elif position in [Position.LEFT, Position.RIGHT]: - self.setCursor(QCursor(Qt.SizeHorCursor)) - def mouseMoveEvent(self, event): - self.block.sizerMoveEvent(self, event.scenePos()) - -class Block(QGraphicsRectItem): - """ Represents a block in the diagram. """ - def __init__(self, name='Untitled', parent=None): - super(Block, self).__init__(parent) - self.selectionHandles = [ResizeSelectionHandle(i, self) for i in range(8)] - # Properties of the rectangle: - self.setPen(QPen(Qt.blue, 2)) - self.setBrush(QBrush(Qt.lightGray)) - self.setFlags(self.ItemIsSelectable | self.ItemIsMovable | self.ItemSendsScenePositionChanges) - self.setCursor(QCursor(Qt.PointingHandCursor)) - self.setAcceptHoverEvents(True) - self.label = QGraphicsTextItem(name, self) - self.name = name - # Create corner for resize: - button = QPushButton('+in') - button.clicked.connect(self.newInputPort) - self.buttonItemAddInput = QGraphicsProxyWidget(self) - self.buttonItemAddInput.setWidget(button) - self.buttonItemAddInput.setVisible(False) - button = QPushButton('+out') - button.clicked.connect(self.newOutputPort) - self.buttonItemAddOutput = QGraphicsProxyWidget(self) - self.buttonItemAddOutput.setWidget(button) - self.buttonItemAddOutput.setVisible(False) - # Inputs and outputs of the block: - self.inputs = [] - self.outputs = [] - self.changeSize(2,2) - def editParameters(self): - pd = ParameterDialog(self, self.window()) - pd.exec_() - def newInputPort(self): - names = [i.name for i in self.inputs + self.outputs] - self.addInput(InputPort(uniqify('in', names), self)) - def newOutputPort(self): - names = [i.name for i in self.inputs + self.outputs] - self.addOutput(OutputPort(uniqify('out', names), self)) - def setName(self, name): self.label.setPlainText(name) - def getName(self): return self.label.toPlainText() - name = property(getName, setName) - def getDict(self): - d = {'x': self.scenePos().x(), 'y': self.scenePos().y()} - rect = self.rect() - d.update({'width': rect.width(), 'height': rect.height()}) - d['name'] = self.name - d['inputs'] = [inp.Dict for inp in self.inputs] - d['outputs'] = [outp.Dict for outp in self.outputs] - return d - def setDict(self, d): - self.name = d['name'] - self.setPos(d['x'], d['y']) - self.changeSize(d['width'], d['height']) - for inp in d['inputs']: - self.addInput(InputPort(inp['name'], self)) - for outp in d['outputs']: - self.addOutput(OutputPort(outp['name'], self)) - Dict = property(getDict, setDict) - def addInput(self, i): - self.inputs.append(i) - self.updateSize() - def addOutput(self, o): - self.outputs.append(o) - self.updateSize() - def contextMenuEvent(self, event): - menu = QMenu() - pa = menu.addAction('Parameters') - pa.triggered.connect(self.editParameters) - menu.exec_(event.screenPos()) - def itemChange(self, change, value): - if change == self.ItemSelectedHasChanged: - for child in [self.buttonItemAddInput, self.buttonItemAddOutput]: - child.setVisible(value) - if value: - self.repositionAndShowHandles() - else: - [h.setVisible(False) for h in self.selectionHandles] - - return super(Block, self).itemChange(change, value) - def hoverEnterEvent(self, event): - if not self.isSelected(): - self.repositionAndShowHandles() - super(Block, self).hoverEnterEvent(event) - def hoverLeaveEvent(self, event): - if not self.isSelected(): - [h.setVisible(False) for h in self.selectionHandles] - super(Block, self).hoverLeaveEvent(event) - def myDelete(self): - for p in self.inputs + self.outputs: - if p.connection: p.connection.myDelete() - self.scene().removeItem(self) - def repositionAndShowHandles(self): - r = self.rect() - self.selectionHandles[Position.TOP_LEFT].setPos(r.topLeft()) - self.selectionHandles[Position.TOP].setPos(r.center().x(), r.top()) - self.selectionHandles[Position.TOP_RIGHT].setPos(r.topRight()) - self.selectionHandles[Position.RIGHT].setPos(r.right(), r.center().y()) - self.selectionHandles[Position.BOTTOM_RIGHT].setPos(r.bottomRight()) - self.selectionHandles[Position.BOTTOM].setPos(r.center().x(), r.bottom()) - self.selectionHandles[Position.BOTTOM_LEFT].setPos(r.bottomLeft()) - self.selectionHandles[Position.LEFT].setPos(r.left(), r.center().y()) - for h in self.selectionHandles: - h.setVisible(True) - def sizerMoveEvent(self, handle, pos): - r = self.rect().translated(self.pos()) - if handle.position == Position.TOP_LEFT: r.setTopLeft(pos) - elif handle.position == Position.TOP: r.setTop(pos.y()) - elif handle.position == Position.TOP_RIGHT: r.setTopRight(pos) - elif handle.position == Position.RIGHT: r.setRight(pos.x()) - elif handle.position == Position.BOTTOM_RIGHT: r.setBottomRight(pos) - elif handle.position == Position.BOTTOM: r.setBottom(pos.y()) - elif handle.position == Position.BOTTOM_LEFT: r.setBottomLeft(pos) - elif handle.position == Position.LEFT: r.setLeft(pos.x()) - else: - print('invalid position') - self.setCenterAndSize(r.center(), r.size()) - self.repositionAndShowHandles() - def updateSize(self): - rect = self.rect() - h, w = rect.height(), rect.width() - self.buttonItemAddInput.setPos(0, h + 4) - self.buttonItemAddOutput.setPos(w+10, h+4) - for inp, y in zip(self.inputs, equalSpace(len(self.inputs), h)): - inp.setPos(0.0, y) - for outp, y in zip(self.outputs, equalSpace(len(self.outputs), h)): - outp.setPos(w, y) - def setCenterAndSize(self, center, size): - self.changeSize(size.width(), size.height()) - p = QPointF(size.width(), size.height()) - self.setPos(center - p / 2) - def changeSize(self, w, h): - h = 20 if h < 20 else h - w = 40 if w < 40 else w - self.setRect(0.0, 0.0, w, h) - rect = self.label.boundingRect() - self.label.setPos((w - rect.width()) / 2, (h - rect.height()) / 2) - self.updateSize() - -class CodeBlock(Block): - def __init__(self, name='Untitled', parent=None): - super(CodeBlock, self).__init__(name, parent) - self.code = '' - def setDict(self, d): - super(CodeBlock, self).setDict(d) - self.code = d['code'] - def getDict(self): - d = super(CodeBlock, self).getDict() - d['code'] = self.code - return d - def gencode(self): - c = ['def {0}():'.format(self.name)] - if self.code: - c += indent(self.code.split('\n')) - else: - c += indent(['pass']) - return c - -class DiagramBlock(Block): - def __init__(self, name='Untitled', parent=None): - super(DiagramBlock, self).__init__(name, parent) - self.subModel = DiagramScene() - self.subModel.containingBlock = self - def setDict(self, d): - self.subModel.Dict = d['submodel'] - def mouseDoubleClickEvent(self, event): - # descent into child diagram - #self.editParameters() - print('descent') - scene = self.scene() - if scene: - for view in scene.views(): - view.diagram = self.subModel - view.zoomAll() - -class DiagramScene(QGraphicsScene): - """ A diagram scene consisting of blocks and connections """ - structureChanged = pyqtSignal() - def __init__(self): - super(DiagramScene, self).__init__() - self.startedConnection = None - - blocks = property(lambda sel: [i for i in sel.items() if isinstance(i, Block)]) - connections = property(lambda sel: [i for i in sel.items() if type(i) is Connection]) - def addItem(self, item): - super(DiagramScene, self).addItem(item) - if isinstance(item, Block): - self.structureChanged.emit() - def removeItem(self, item): - super(DiagramScene, self).removeItem(item) - if isinstance(item, Block): - self.structureChanged.emit() - def setDict(self, d): - for block in d['blocks']: - b = Block() - self.addItem(b) - b.Dict = block - for con in d['connections']: - fromPort = self.findPort(con['fromBlock'], con['fromPort']) - toPort = self.findPort(con['toBlock'], con['toPort']) - self.addItem(Connection(fromPort, toPort)) - def getDict(self): - return {'blocks': [b.Dict for b in self.blocks], 'connections': [c.Dict for c in self.connections]} - Dict = property(getDict, setDict) - def gencode(self): - c = [] - for b in self.blocks: - c += b.gencode() - for b in self.blocks: - c.append('{0}()'.format(b.name)) - return c - def findPort(self, blockname, portname): - block = self.findBlock(blockname) - if block: - for port in block.inputs + block.outputs: - if port.name == portname: return port - def findBlock(self, blockname): - for block in self.blocks: - if block.name == blockname: return block - def uniqify(self, name): - blocknames = [item.name for item in self.blocks] - return uniqify(name, blocknames) - def mouseMoveEvent(self, event): - if self.startedConnection: - pos = event.scenePos() - self.startedConnection.setEndPos(pos) - super(DiagramScene, self).mouseMoveEvent(event) - def mouseReleaseEvent(self, event): - if self.startedConnection: - for item in self.items(event.scenePos()): - if type(item) is InputPort and item.connection == None: - self.startedConnection.setToPort(item) - self.startedConnection = None - return - self.startedConnection.myDelete() - self.startedConnection = None - super(DiagramScene, self).mouseReleaseEvent(event) - def startConnection(self, port): - self.startedConnection = Connection(port, None) - pos = port.scenePos() - self.startedConnection.setEndPos(pos) - self.addItem(self.startedConnection) - def deleteItems(self): - for item in list(self.selectedItems()): item.myDelete() - diff -r 1be00bcfaabb -r 4a37d6992bd3 python/apps/icons.py --- a/python/apps/icons.py Mon Dec 24 13:21:13 2012 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,8 +0,0 @@ - - -saveicon = b'iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAGGklEQVR42s2XeWxURRjAv7d3L9ay\nlJBCbAx/SIIpCWilhNLSFsrR0qUgyCmnnKVFa/xDjNbUKGIUSrlaLhEREWlZaQoC24VymBivoGJB\n/MuEtEpjoNt2z/H7Zt+8fdPdGk2MOu23MzvvzXy/+Y6ZWQX+46L8bwC2vLTlZte9e6MMRiMYjQYY\nPWYsZGXngM1iBkUxoERej1RYayJmYGrF1Dbj/31+P1xtuwQ3vvmaP6M/h8Nx69Xq6kclgDWrn2W3\nbt+G5KQkSE9Ph/E5eTB1xixIQID+yIraEMoVJYLGay4KGNS2ty8Aza4muHqpVZvClmCDHbW1igSw\neuUq1tPTA2EWRohkmJCbD9OLS+Fu14M4ZlP6M8lwSvSdEUNTwNXYCFc87iiAzQY7d9XJACuXr2Dd\n3d1gNpvBarXCxMmFUDzLCUk2S3zfKTrlqmIlYgqtTfWDXh80NZ6CNvdFnQUSYNfuXTLAimXLmV5B\nTn4hlDrLICUxHoAC+tHSTNz1jIcCw48HvX60AAK0RgGsaIE9e/fIAMuWPiMB5BZMAWfZHBiUaI1r\nAaZ+Mi32VKVqX5h/ZzwGzpxuRAtckFywt36fDLB08RIZoHAqlDhnQ0eXdwDlosFAP1APRGVoahK0\nYBBedp+XLNCwv0EGWLxwUT+AIphZ6ozNghhlEQCtrZqeqRC+QAjOftqEFogCkAX2HzwgAyxasFAC\nmFQwFaaVEIAJ5P2K6T5ln2uu0LnEHwzBZ2ea4Epr1AUU5AcPH5IBFsx/Wg5CBCgqdoLVbBxg5Xov\nDGyFQCgMF5tPI8B5CeDwkfdkgPlPzYsBmDLTCffu98b3f78OzS4s+g7Vg5Ot4G5xwdV+AEeOvi8D\nzJszV5p7IgLkT3equ9zAAPo4EMpFAIpnnrMuuKYDsCDA0WMfyABzy+bIAPlTIG9aKfzZecV0y5dc\nIdosMvzyORdc90RjwGKxwLHjH8oAZc7ZEoBj6DAYOeoxPJiMYKIDymTkbYPBoB5CkRpABByDcDjM\nJUQSDEIwFIIwyk83b0DXrx0SwPETH8kAs2eVxrhXKsrfPLnZwNOZEeDEyY9lgNLiEvYXpv1HCgGc\nPPWJDFAys/jfA8AD71RTowxQPH3GgAAK+t2IQv7X4oC+q5cSKiIORCyEyP9qLfr1AI2u0zLAjKJp\ncQHWblgPGRkZ0Q7G4uwFch8d6xXlm3jw0qEUCARgOF5yRDGZTOBqPiMDFBUUxsxLh8aa9evAbrfz\nVYuVRvVG2uKZ6COrvFj1Ao92fiL2eME+yK4pM6EFms+2yAAFeZNjAawWWF9eDqmpqVBT/ZqWZn1+\nH7y5dat2LxRmp1qY/pUtL/NgY9ju6e3lVz29Bc5dOC8D5OVMigNghfLKCg7wXOVm3kd53e31Qv3+\nBq6clAUx5/v7/I2a17mvOXBfHyTgLUgDQNdc8LTKADnZE2IAiHRz1fPgGDIEKjeW8z7aZPwBP9TW\n1WlK9QAC4p1tb2sAPp8PbLgYunFb8HgPBILgabssA0zIejIGgHy4uaoKARywacPGCABO7sOr9rs7\ntsdEOwnBkNRu34EAJnQBAz++TwB8V0WoIAZl2/VrMkDWuMdjAJISE2EjuoCCsBKjOuJrVISTbt32\nlqZMrJyiXQDt2VnHldGYYDCA7rSBGS0awvEmowmufH5dBhibOeYu+nSYHiABAdZhGg4igIoKbkqa\nnLKjpqZGWrneErTihn31XGEEIAgJOIZixqim5hdffRkFSElOUfDX0LgR6cNbMECGaBbAyF21dg2v\n+UBdkFGu6wH0z0jhgfp6vlIeuPiMrmGiUNr+0P5jcp/P5xUWoDoN/bT8kYczqjH4rCILFi5ZAvaH\n7JKPRZtMLtJPCE1Oz44cOsSh1X0KrBaLHiD47fffjcTmL/ojLhFlNCovtJjNDmRi2dnjk57IyhqM\nsWBBf1pxQjMJzmgke6rKmULJwVgQm36E8nd2dNxv9Xh+//nOHR9Ivxj4WjGRQu24+mb88psegNqE\nSQlrJH9lZmYacnNzjWlpaQpmBBdKLXXHU9QNiIkdEa0T9nq94c7OzpDb7Q63t7crTD6WFRXEj0J3\nveAfetNmUUsM6bsAAAAASUVORK5CYII=\n' - -loadicon = b'iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAACsElEQVR42u2Xf0hTURTHv++ZczlD\n6SeUREVRiRT9gFWM1rbAwr+iYAathPqjMosIQQqJQAhLLDI0CMwflRa1vyQst0oNLKxhCGWR9UfB\nwFCRaqC2vc6d7+1tr/7YnuwW5IEv95y79875vHvP7t4E/GUTpgGmAaL8vaRzpDlx3iuRXpEOkz5N\nFWDhrHR8bL+LtGXLKUqRJcoKyeUk1Q8Fges3gLIKeGlm+1QBHA4rPJ7WxG4eHqLlWoJRcvfJaPHY\nV1LPbwB5Dnja7if+BFUVQEdX7OqETdL48vD+C/DODze5e9iUCmAjgDt6FzJ+m/gG5GyD9OEz1lPY\nqwLQFrQ1JR+Amc0JPO0O941XBbAQQB0nABcBvNACbCGAWk4ABwngpRbATACXEksU7rsJGlnzUSZB\nkEdAbW9lXgnJtx0jAJ8WYCMBnE8MIESFx7+rRQRRLiaPESANmL2EAF6jgaLSCIA9Bx53sZokJpEY\nm4zFzP85BgTn0mUZUL9y7CPpz8CCvAJdA0DrE6D6NnxsLp9UaV2KVfW7Jy8QWcGUyUJhP0piFJA0\nmw6iAn19EAgAmdkICpRw5EEdsjabgXQDYo9gRZENVxVeIJO8Gjqs7y2wxoJ+IXUGxkcHkDrTqC+R\nXrt5D3AVoYU9yJs+L1bnruQLUFJO+34NpxlAc2MVCly7+ALkHQAedSKfAZwq3o/KK2X8ikvUT4ss\nkPyDWMwAtm5ai47uZn4Ag8PAAgvoxxzzGUCGMQ1DI89gMBr4ADymtwHHIXSSa1UOIt/zBqwz5/IB\nqG4Bjl9EDblFCkDN5ZM4csLJB+DoBaDWDTp3cVUBcDntaLx1hg/AzlKgvQc7yH2oAGSbjOg/WwjT\nvMzkFvdT65U34UdgDCtYGP1avoFUSMpK8gKw7q8n9bLgn/pj8n8C/ALihrxpNKi7hQAAAABJRU5E\nrkJggg==\n' - -newicon = b'iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAADSUlEQVR42r2XO0xaURjHLzYmdXRi\nYXBwdDScJl3djQNLw0TSAR88FR9JU6P4QKMCaTqQdGgXsQQMQXFwcGXhCCG+4iNEo6mDMZL45kK/\n76THUO+9Qi/efskJcm4O3+/+v8f51Al/LBaLmTs6Or43gAkq7eHhoXh2dmZva2v7WusZHf9jZGTE\nPz4+blfjuFwuC6IoCsViUbi8vCwDhM1oNH75V4AAANjUAJRKJeYc183NjXB1dVU+Pj7+CIp+qxlg\neHg44PV6VQHg2z8+PjKAu7s7oampSQAVxL29PUtnZ+eP/waA6/r6WtDr9UyJ09NTcXd319LV1aUI\n8QQwNDQUmJiYqBsA5BcMBgPLC4Q4OTkRt7e3LSaTSRbiCWBwcDAwOTmpOgfQOVSBcHFxIbS0tLB9\nDgH5IGYyGYvZbJZAvAoAOsL4IwTEngGxH9fp2MJnhUJBTKfT5t7e3rAsgMfjYQB4oB4V+OJAuI+A\naGtra6nz8/P3o6OjJQnAwMBAYGpqSjUAh8Aw8JLE3OBqIMTGxgbNZrPE5/MVFQFUe6+A4M5xoWMe\nIg4ASksB+vv761aAG3eKMFwBBFhfX6ebm5sEyl0eAHOgjqvgRSBUA3KAghHouFIAt9vNFNACgIci\nmUxSKEcyNjYmBXC5XK8Wgudvz8OAAJgDigD1lGE155UAsiFwOp2vUgVKzisBZJMQAdR2wlqco62s\nrNBcLkfgzpECOByOuhXg5cc733NLJBIMQLYP2O12BqB0uJrjyk8li8fjdGtri4AfeQAMAUpXayJW\ne2MlANlWbLPZGAB2LPagCoQapZaXl+nOzg6ZmZmRB8CBhANoYTB5U5iQyOzsrDwAjmR4hWpl0WiU\nwpxI5ubmpAAwKLAQ4HWqlUUiEbq/v0/m5+flATAEWiqwtLREDw4OyMLCgjyA1iEIh8P08PCQ+P1+\nKUBPTw8D0DIEi4uL9OjoiASDQSlAd3c3A7i/v9cUAKsgFApJAaxWa2B6etp2e3v71yGlen++X60v\n4EwAAOlUKvUO+oEUoLW19UNfX9/nxsbGhsoOxy+Wl75X2+drdXX1J/zX9AkmI+lU3N7e/hYSxAAT\n0Rs+FdX69rXsA1y5ubn5Vz6fL1Q++w30VO4/0/9IewAAAABJRU5ErkJggg==\n' - diff -r 1be00bcfaabb -r 4a37d6992bd3 python/apps/ide.py --- a/python/apps/ide.py Mon Dec 24 13:21:13 2012 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,299 +0,0 @@ -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 = """
An all-in-one IDE for OS development.
-https://www.assembla.com/spaces/lcfOS/wiki
-Author: Windel Bouwman
- """ - 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_() - diff -r 1be00bcfaabb -r 4a37d6992bd3 python/bouncing_cube.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/python/bouncing_cube.py Mon Dec 24 13:24:59 2012 +0100 @@ -0,0 +1,406 @@ +from PyQt4.QtGui import * +from PyQt4.QtCore import * +from PyQt4.QtOpenGL import QGLWidget +from OpenGL.GL import * +from OpenGL.GLU import gluPerspective +import sys +from random import random +from math import pi, cos, sin, fabs, sqrt +from numpy import mat, array, ones, zeros, eye +from numpy.linalg import norm +import numpy as np +import time +import scipy.integrate +#import pyopencl + +""" + Test script that lets a dice bounce. + Converted from 20-sim equations into python code. + + 20-sim website: + http://www.20sim.com + +""" +def drawCube(w): + glBegin(GL_QUADS) # Start Drawing The Cube + glColor3f(0.0,1.0,0.0) # Set The Color To Blue + glVertex3f( w, w,-w) # Top Right Of The Quad (Top) + glVertex3f(-w, w,-w) # Top Left Of The Quad (Top) + glVertex3f(-w, w, w) # Bottom Left Of The Quad (Top) + glVertex3f( w, w, w) # Bottom Right Of The Quad (Top) + + glColor3f(1.0,0.5,0.0) # Set The Color To Orange + glVertex3f( w,-w, w) # Top Right Of The Quad (Bottom) + glVertex3f(-w,-w, w) # Top Left Of The Quad (Bottom) + glVertex3f(-w,-w,-w) # Bottom Left Of The Quad (Bottom) + glVertex3f( w,-w,-w) # Bottom Right Of The Quad (Bottom) + + glColor3f(1.0,0.0,0.0) # Set The Color To Red + glVertex3f( w, w, w) # Top Right Of The Quad (Front) + glVertex3f(-w, w, w) # Top Left Of The Quad (Front) + glVertex3f(-w,-w, w) # Bottom Left Of The Quad (Front) + glVertex3f( w,-w, w) # Bottom Right Of The Quad (Front) + + glColor3f(1.0,1.0,0.0) # Set The Color To Yellow + glVertex3f( w,-w,-w) # Bottom Left Of The Quad (Back) + glVertex3f(-w,-w,-w) # Bottom Right Of The Quad (Back) + glVertex3f(-w, w,-w) # Top Right Of The Quad (Back) + glVertex3f( w, w,-w) # Top Left Of The Quad (Back) + + glColor3f(0.0,0.0,1.0) # Set The Color To Blue + glVertex3f(-w, w, w) # Top Right Of The Quad (Left) + glVertex3f(-w, w,-w) # Top Left Of The Quad (Left) + glVertex3f(-w,-w,-w) # Bottom Left Of The Quad (Left) + glVertex3f(-w,-w, w) # Bottom Right Of The Quad (Left) + + glColor3f(1.0,0.0,1.0) # Set The Color To Violet + glVertex3f( w, w,-w) # Top Right Of The Quad (Right) + glVertex3f( w, w, w) # Top Left Of The Quad (Right) + glVertex3f( w,-w, w) # Bottom Left Of The Quad (Right) + glVertex3f( w,-w,-w) # Bottom Right Of The Quad (Right) + glEnd() # Done Drawing The Quad + +def drawFloor(w, h): + glBegin(GL_QUADS) # Start Drawing The Cube + + glColor3f(1.0,0.5,0.0) # Set The Color To Orange + glVertex3f( w,-w,h)# Top Right Of The Quad (Bottom) + glVertex3f(-w,-w,h)# Top Left Of The Quad (Bottom) + glVertex3f(-w,w,h)# Bottom Left Of The Quad (Bottom) + glVertex3f( w,w,h)# Bottom Right Of The Quad (Bottom) + glEnd() # Done Drawing The Quad + +def drawAxis(): + glLineWidth(0.5) + glBegin(GL_LINES) + glColor3f(1.0, 0.0, 0.0) + glVertex3f(0,0,0) + glVertex3f(1,0,0) + glColor3f(0.0, 1.0, 0.0) + glVertex3f(0,0,0) + glVertex3f(0,1,0) + glColor3f(0.0, 0.0, 1.0) + glVertex3f(0,0,0) + glVertex3f(0,0,1) + glEnd() + + +def cross(A, B): + a = A.A1 + b = B.A1 + return mat(np.cross(a, b)).T + +def skew(X): + Y = mat(zeros( (3, 3) )) + a,b,c = X.A1 + Y[0,1] = -c + Y[0,2] = b + Y[1,0] = c + Y[1,2] = -a + Y[2,0] = -b + Y[2,1] = a + return Y + +def adjoint(T): + W = T[0:3, 0] + V = T[3:6, 0] + a = mat(zeros( (6,6) ) ) + a[0:3, 0:3] = skew(W) + a[3:6, 0:3] = skew(V) + a[3:6, 3:6] = skew(W) + return a + +def Adjoint(H): + R = H[0:3, 0:3] + P = H[0:3, 3] + a = mat(zeros( (6,6) ) ) + a[0:3, 0:3] = R + a[3:6, 3:6] = R + a[3:6, 0:3] = skew(P) * R + return a + +def quatToR(q): + x, y, z, w = q.A1 + r = mat(eye(3)) + r[0,0] = 1 - (2*y**2+2*z**2) + r[0,1] = 2*x*y+2*z*w + r[0,2] = 2*x*z - 2*y*w + r[1,0] = 2*x*y-2*z*w + r[1,1] = 1 - (2*x**2 + 2*z**2) + r[1,2] = 2*y*z + 2*x*w + r[2,0] = 2*x*z+2*y*w + r[2,1] = 2*y*z - 2*x*w + r[2,2] = 1 - (2*x**2+2*y**2) + return r + +def rotateAbout(axis, angle): + ax, ay, az = (axis/norm(axis)).A1 + qx = ax*sin(angle/2.0) + qy = ay*sin(angle/2.0) + qz = az*sin(angle/2.0) + qw = cos(angle/2.0) + q = mat(array([qx,qy,qz,qw])).T + return q + +def normalizeQuaternion(quat): + x,y,z,w = quat.A1 + magnitude = sqrt(x*x + y*y + z*z + w*w) + x = x / magnitude + y = y / magnitude + z = z / magnitude + w = w / magnitude + quat[0, 0] = x + quat[1, 0] = y + quat[2, 0] = z + quat[3, 0] = w + return quat + +def VTo4x4(V): + v1, v2, v3 = V.A1 + return mat(array( \ + [[0.0, -v3, v2, -v1], \ + [ v3, 0.0, -v1, -v2], \ + [-v2, v1, 0.0, -v3], \ + [v1, v2, v3, 0.0] ])) + +def homogeneous(R,p): + H = mat(eye(4)) + H[0:3, 0:3] = R + H[0:3, 3] = p + return H + +def rateOfChange(states, thetime, parameters): + quat = states[0:4, 0] # Orientation (4) + pos = states[4:7, 0] # Position (3) + P = states[7:13, 0] # Momentum (6) + massI, gravity = parameters + # Rigid body parts: + # Forward Kinematic chain: + H = homogeneous(quatToR(quat), pos) # Forward kinematics + + AdjX2 = mat(eye(6)) # The connectionpoint in the real world + adjAdjX2_1 = adjoint(AdjX2[0:6,0]) + adjAdjX2_2 = adjoint(AdjX2[0:6,1]) + adjAdjX2_3 = adjoint(AdjX2[0:6,2]) + adjAdjX2_4 = adjoint(AdjX2[0:6,3]) + adjAdjX2_5 = adjoint(AdjX2[0:6,4]) + adjAdjX2_6 = adjoint(AdjX2[0:6,5]) + AdjInv2 = Adjoint(H.I) + M2 = AdjInv2.T * (massI * AdjInv2) # Transfor mass to base frame + MassMatrix = M2 + + wrenchGrav2 = mat( zeros((1,6)) ) + wrenchGrav2[0, 0:3] = -cross(gravity, pos).T + wrenchGrav2[0, 3:6] = gravity.T + + Bk = mat( zeros( (6,6) )) + Bk[0:3, 0:3] = skew(P[0:3, 0]) + Bk[3:6, 0:3] = skew(P[3:6, 0]) + Bk[0:3, 3:6] = skew(P[3:6, 0]) + + # TODO: do this a cholesky: + v = np.linalg.solve(MassMatrix, P) # Matrix inverse like thingy ! + + T2_00 = v # Calculate the relative twist! + TM2 = T2_00.T * M2 + twistExternal = T2_00 # Twist van het blokje + TMSum2 = TM2 + + PDotBodies = mat( zeros( (6,1)) ) + PDotBodies[0,0] = TMSum2 * (adjAdjX2_1 * T2_00) + PDotBodies[1,0] = TMSum2 * (adjAdjX2_2 * T2_00) + PDotBodies[2,0] = TMSum2 * (adjAdjX2_3 * T2_00) + PDotBodies[3,0] = TMSum2 * (adjAdjX2_4 * T2_00) + PDotBodies[4,0] = TMSum2 * (adjAdjX2_5 * T2_00) + PDotBodies[5,0] = TMSum2 * (adjAdjX2_6 * T2_00) + + PDot = -PDotBodies - Bk * v + PDot += wrenchGrav2.T + + ##### Contact wrench part: + HB_W = H # Is H-matrix van het blokje + WrenchB = mat(zeros( (1,6) )) + for px in [-0.5, 0.5]: + for py in [-0.5, 0.5]: + for pz in [-0.5, 0.5]: + HB1_B = homogeneous(mat(eye(3)), mat([px,py,pz]).T) + HB1_W = HB_W * HB1_B + HW1_W = homogeneous(mat(eye(3)), HB1_W[0:3,3]) + HW_W1 = HW1_W.I + HB_W1 = HW_W1 * HB_W + + AdjHB_W1 = Adjoint(HB_W1) + TB_W1_W1 = AdjHB_W1 * twistExternal + z = HB1_W[2, 3] + vx, vy, vz = TB_W1_W1[3:6, 0].A1 + if z < 0: + # Contact forces: + Fx = -50.0*vx + Fy = -50.0*vy + Fz = -z*50000.0 + else: + Fx = 0.0 + Fy = 0.0 + Fz = 0.0 + # TODO: reflect impulse + WrenchW1 = mat([0,0,0,0,0,Fz]) + # Transform it back: + WrenchB += (AdjHB_W1.T * WrenchW1.T).T + ##### End of contact wrench + + PDot += (WrenchB * AdjInv2).T + + # Position and orientation rates: + QOmega = VTo4x4(v[0:3, 0]) + quatDot = 0.5 * QOmega * quat + vel = v[3:6, 0] + posDot = skew(v[0:3]) * pos + vel + # The rate vector: + rates = mat(zeros( (13,1) )) + rates[0:4, 0] = quatDot + rates[4:7, 0] = posDot + rates[7:13, 0] = PDot + return rates + +def fWrapper(y, t, parameters): + y = mat(y).T + dy = rateOfChange(y, t, parameters) + return dy.T.A1 + +def SCIPY(endtime, dt, state, parameters): + times = np.arange(0.0, endtime, dt) + y0 = state.T.A1 + res = scipy.integrate.odeint(fWrapper, y0, times, args=(parameters,)) + states = [] + res = res.T + r,c = res.shape + for ci in range(0,c): + states.append(mat(res[:,ci]).T) + return states + +def RK4(endtime, dt, state, parameters): + t = 0.0 + states = [] + while t < endtime: + newstate = mat (zeros( state.shape )) # Create a new object + + #### Runge Kutta integration: + k1 = rateOfChange(state, t, parameters) + k2 = rateOfChange(state + 0.5*dt*k1, t, parameters) + k3 = rateOfChange(state + 0.5*dt*k1, t, parameters) + k4 = rateOfChange(state + dt*k3, t, parameters) + newstate = state + (dt/6.0)*(k1+2*k2+2*k3+k4) + + # Normalize quat: + newstate[0:4, 0] = normalizeQuaternion(newstate[0:4, 0]) + states.append(newstate) + + state = newstate + + t += dt + print state[6,0], t, ' ', (t/endtime)*100.0, '%' + return states + + +def simulate(endtime, dt): + PInitial = mat( zeros((6,1)) ) + posInitial = mat(array([-1.2, -1.3, 2.8])).T + quatInitial = rotateAbout(mat(array([0.2, 1.0, 0.4])).T, 0.5) + # Parameters: + gravity = mat( array([0,0,-9.81]) ).T + massI = mat(eye(6)) * 1.01 # Mass matrix + parameters = (massI, gravity) + + # The state vector! + state = mat(zeros( (13,1) )) + state[0:4, 0] = quatInitial + state[4:7, 0] = posInitial + state[7:13, 0] = PInitial + + return SCIPY(endtime, dt, state, parameters) + +class W(QGLWidget): + time = 0.0 + index = 0 + def __init__(self, states, dt, parent=None): + super(W, self).__init__(parent) + self.firstRun = True + self.savePNGS = False + self.dt = dt + self.resize(500,500) + self.states = states + self.UP() + t = QTimer(self) + t.timeout.connect(self.UP) + t.start(self.dt*1000.0) + + def UP(self): + self.time += self.dt + if self.states: + state = self.states[self.index] + self.Dicequat = state[0:4, 0] + self.Dicepos = state[4:7, 0] + self.index += 1 + if self.index == len(self.states): + self.index = 0 + self.firstRun = False + # Paint: + self.update() + if self.firstRun: + # Create png images for the movie: + if self.savePNGS: + pm = self.renderPixmap() + pm.save('image'+str(self.index)+'.png') + + def initializeGL(self): + glClearColor(0.0, 0.5, 0.0, 1.0) + glEnable(GL_DEPTH_TEST) + glDepthFunc(GL_LESS) + glShadeModel(GL_SMOOTH) + def resizeGL(self,w,h): + glViewport(0, 0, w, h) + glMatrixMode(GL_PROJECTION) + glLoadIdentity() + gluPerspective(45.0, float(w)/float(h), 0.1, 100.0) + glMatrixMode(GL_MODELVIEW) + def paintGL(self): + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT) # Clear buffers + glLoadIdentity() # Reset The View + + glLoadIdentity() + glTranslatef(0.0,-2.0,-10.0) # Move Left And Into The Screen + glRotatef(-90.0, 1.0, 0.0, 0.0) + drawFloor(2.0, 0.0) + drawAxis() + self.renderText(1.0, 0.0, 0.0, 'X') + self.renderText(0.0, 1.0, 0.0, 'Y') + self.renderText(0.0, 0.0, 1.0, 'Z') + + self.renderText(0.0,0.0,1.2,str(self.time)) + + x,y,z = self.Dicepos.A1 + R = quatToR(self.Dicequat) + + glTranslatef(x, y, z) + # Trick to rotate the openGL matrix: + r = R.A1 + rotR = (r[0], r[3], r[6], 0.0, + r[1], r[4], r[7], 0.0, + r[2], r[5], r[8], 0.0, + 0.0, 0.0, 0.0, 1.0) + glMultMatrixd(rotR) + + drawCube(0.6) + +et = 20.0 +dt = 0.04 +print 'starting integration... endtime =', et, ' stepsize =', dt +t0 = time.time() +states = simulate(et, dt) +t1 = time.time() +print 'That was heavy, it took me ', t1-t0, ' seconds!' +app = QApplication(sys.argv) +w = W(states, dt) +w.show() +sys.exit(app.exec_()) + diff -r 1be00bcfaabb -r 4a37d6992bd3 python/build.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/python/build.py Mon Dec 24 13:24:59 2012 +0100 @@ -0,0 +1,20 @@ +import sys, os, argparse +sys.path.insert(0, os.path.join('..','libs')) + +# Compiler imports: +from compiler import Compiler +from project import Project + +if __name__ == '__main__': + parser = argparse.ArgumentParser(description='Build tool to build projects') + parser.add_argument('project', type=str, help='the project to be build') + args = parser.parse_args() + + try: + project = Project(args.project) + except IOError: + print('Failed to load {0}'.format(args.project)) + sys.exit(3) + pc = Compiler() + pc.compileProject(project) + diff -r 1be00bcfaabb -r 4a37d6992bd3 python/diagrameditor.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/python/diagrameditor.py Mon Dec 24 13:24:59 2012 +0100 @@ -0,0 +1,314 @@ +#!/usr/bin/python + +from PyQt4.QtGui import * +from PyQt4.QtCore import * +import sys, json, base64 + +from diagramitems import Connection, ResizeSelectionHandle, Block, DiagramScene, CodeBlock +from icons import newicon, saveicon, loadicon +import diagramitems + +""" + Author: Windel Bouwman + Year: 2012 + Description: This script implements a diagram editor. + run with python 3.x as: + $ python [thisfile.py] +""" +def indent(lines): + return [' ' + line for line in lines] + +class ParameterDialog(QDialog): + def __init__(self, block, parent = None): + super(ParameterDialog, self).__init__(parent) + self.block = block + self.button = QPushButton('Ok', self) + self.nameEdit = QLineEdit(self.block.name) + self.codeEdit = QTextEdit(self) + self.codeEdit.setPlainText(self.block.code) + l = QFormLayout(self) + l.addRow('Name:', self.nameEdit) + l.addRow('Code:', self.codeEdit) + l.addWidget(self.button) + self.button.clicked.connect(self.OK) + def OK(self): + self.block.name = self.nameEdit.text() + self.block.code = self.codeEdit.toPlainText() + self.close() + +class EditorGraphicsView(QGraphicsView): + def __init__(self, parent=None): + QGraphicsView.__init__(self, parent) + self.setDragMode(QGraphicsView.RubberBandDrag) + self.delShort = QShortcut(QKeySequence.Delete, self) + self._model = None + self.treeView = QTreeView() + self.treeView.activated.connect(self.itemActivated) + def itemActivated(self, idx): + b = idx.internalPointer() + s = b.scene() + s.clearSelection() + b.setSelected(True) + def setDiagram(self, d): + self.setScene(d) + self.delShort.activated.connect(d.deleteItems) + def getModel(self): + return self._model + def setModel(self, m): + self._model = m + if m: + self.treeView.setModel(m) + self.diagram = m.rootDiagram + self.model.modelReset.connect(self.treeView.expandAll) + model = property(getModel, setModel) + diagram = property(lambda s: s.scene(), setDiagram) + def save(self): + if self.model: + if not self.model.filename: + self.model.filename = QFileDialog.getSaveFileName(self) + if self.model.filename: + with open(self.model.filename, 'w') as f: + f.write(json.dumps(self.model.Dict, indent=2)) + def load(self): + filename = QFileDialog.getOpenFileName(self) + if filename: + self.model = loadModel(filename) + def newModel(self): + self.model = ModelHierarchyModel() + def goUp(self): + if hasattr(self.diagram, 'containingBlock'): + self.diagram = self.diagram.containingBlock.scene() + self.zoomAll() + def showCode(self): + if self.model: + c = self.model.gencode() + c = '\n'.join(c) + d = QDialog() + l = QFormLayout(d) + codeview = QTextEdit() + codeview.setPlainText(c) + l.addRow('code', codeview) + runButton = QPushButton('Run') + outputview = QTextEdit() + l.addRow('Output', outputview) + l.addWidget(runButton) + def print2(txt): + txt2 = outputview.toPlainText() + outputview.setPlainText(txt2 + '\n' + txt) + def runIt(): + outputview.clear() + globs = {'print': print2} + exec(codeview.toPlainText(), globs) + runButton.clicked.connect(runIt) + d.exec_() + def zoomAll(self): + """ zoom to fit all items """ + rect = self.diagram.itemsBoundingRect() + self.fitInView(rect, Qt.KeepAspectRatio) + def wheelEvent(self, event): + pos = event.pos() + posbefore = self.mapToScene(pos) + degrees = event.delta() / 8.0 + sx = (100.0 + degrees) / 100.0 + self.scale(sx, sx) + event.accept() + def dragEnterEvent(self, event): + if event.mimeData().hasFormat('component/name'): + event.accept() + def dragMoveEvent(self, event): + if event.mimeData().hasFormat('component/name'): event.accept() + def dropEvent(self, event): + if event.mimeData().hasFormat('component/name'): + name = bytes(event.mimeData().data('component/name')).decode() + kind, name = name.split(':') + pos = self.mapToScene(event.pos()) + s = self.scene() + print(kind, 'name:', name) + kind = getattr(diagramitems, kind) + print(kind) + b = kind(s.uniqify(name)) + b.setPos(pos) + s.addItem(b) + +class LibraryModel(QStandardItemModel): + mimeTypes = lambda self: ['component/name'] + def mimeData(self, idxs): + mimedata = QMimeData() + for idx in idxs: + if idx.isValid(): + txt = self.data(idx, Qt.DisplayRole) + mimedata.setData('component/name', txt) + return mimedata + +class ModelHierarchyModel(QAbstractItemModel): + def __init__(self): + super(ModelHierarchyModel, self).__init__() + self.rootDiagram = DiagramScene() + self.rootDiagram.structureChanged.connect(self.handlechange) + self.filename = None + def handlechange(self): + self.modelReset.emit() + def setDict(self, d): + self.rootDiagram.Dict = d + self.modelReset.emit() + def getDict(self): + return self.rootDiagram.Dict + Dict = property(getDict, setDict) + def gencode(self): + c = ['def topLevel():'] + c += indent(self.rootDiagram.gencode()) + c.append('print("Running model")') + c.append('topLevel()') + c.append('print("Done")') + return c + def index(self, row, column, parent=None): + if parent.isValid(): + parent = parent.internalPointer().subModel + else: + parent = self.rootDiagram + blocks = sorted(parent.blocks, key=lambda b: b.name) + block = blocks[row] + # Store the index to retrieve it later in the parent function. + # TODO: solve this in a better way. + block.index = self.createIndex(row, column, block) + return block.index + def parent(self, index): + if index.isValid(): + block = index.internalPointer() + if block.scene() == self.rootDiagram: + return QModelIndex() + else: + print(block) + outerBlock = block.scene().containingBlock + return outerBlock.index + print('parent: No valid index') + def data(self, index, role): + if index.isValid() and role == Qt.DisplayRole: + b = index.internalPointer() + if index.column() == 0: + return b.name + elif index.column() == 1: + return str(type(b)) + def headerData(self, section, orientation, role): + if orientation == Qt.Horizontal and role == Qt.DisplayRole: + if section == 0: + return "Element" + elif section == 1: + return "Type" + else: + return "x" + def rowCount(self, parent): + if parent.column() > 0: + return 0 + if parent.isValid(): + block = parent.internalPointer() + if hasattr(block, 'subModel'): + return len(block.subModel.blocks) + else: + return 0 + else: + return len(self.rootDiagram.blocks) + def columnCount(self, parent): + return 2 + +class LibraryWidget(QListView): + def __init__(self): + super(LibraryWidget, self).__init__(None) + self.libraryModel = LibraryModel(self) + self.libraryModel.setColumnCount(1) + # Create an icon with an icon: + pixmap = QPixmap(60, 60) + pixmap.fill() + painter = QPainter(pixmap) + painter.fillRect(10, 10, 40, 40, Qt.blue) + painter.setBrush(Qt.yellow) + painter.drawEllipse(20, 20, 20, 20) + painter.end() + # Fill library: + for name in ['CodeBlock:codeBlock', 'DiagramBlock:submod', 'Block:blk']: + self.libraryModel.appendRow(QStandardItem(QIcon(pixmap), name)) + self.setModel(self.libraryModel) + self.setViewMode(self.IconMode) + self.setDragDropMode(self.DragOnly) + +def warning(txt): + QMessageBox.warning(None, "Warning", txt) + +def loadModel(filename): + try: + m = ModelHierarchyModel() + with open(filename, 'r') as f: data = f.read() + m.filename = filename + m.Dict = json.loads(data) + return m + except KeyError: + warning('Corrupt model: {0}'.format(filename)) + except ValueError: + warning('Corrupt model: {0}'.format(filename)) + except FileNotFoundError: + warning('File [{0}] not found'.format(filename)) + +class Main(QMainWindow): + def __init__(self): + super(Main, self).__init__(None) + self.editor = EditorGraphicsView() + self.setCentralWidget(self.editor) + self.setWindowTitle("Diagram editor") + def buildIcon(b64): + icon = base64.decodestring(b64) + pm = QPixmap() + pm.loadFromData(icon) + return QIcon(pm) + toolbar = self.addToolBar('Tools') + toolbar.setObjectName('Tools') + def act(name, shortcut, callback, icon=None): + a = QAction(icon, name, self) if icon else QAction(name, self) + a.setShortcuts(shortcut) + a.triggered.connect(callback) + toolbar.addAction(a) + act('New', QKeySequence.New, self.editor.newModel, buildIcon(newicon)) + act('Save', QKeySequence.Save, self.editor.save, buildIcon(saveicon)) + act('Load', QKeySequence.Open, self.editor.load, buildIcon(loadicon)) + act('Full screen', QKeySequence("F11"), self.toggleFullScreen) + act('Fit in view', QKeySequence("F8"), self.editor.zoomAll) + act('Go up', QKeySequence(Qt.Key_Up), self.editor.goUp) + act('Model code', QKeySequence("F7"), self.editor.showCode) + def addDock(name, widget): + dock = QDockWidget(name, self) + dock.setObjectName(name) + dock.setWidget(widget) + self.addDockWidget(Qt.LeftDockWidgetArea, dock) + addDock('Library', LibraryWidget()) + addDock('Model tree', self.editor.treeView) + self.settings = QSettings('windelsoft', 'diagrameditor') + self.loadSettings() + def toggleFullScreen(self): + self.setWindowState(self.windowState() ^ Qt.WindowFullScreen) + self.editor.zoomAll() + 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('openedmodel'): + modelfile = self.settings.value('openedmodel') + self.editor.model = loadModel(modelfile) + def closeEvent(self, ev): + self.settings.setValue('mainwindowstate', self.saveState()) + self.settings.setValue('mainwindowgeometry', self.saveGeometry()) + if self.editor.model and self.editor.model.filename: + self.settings.setValue('openedmodel', self.editor.model.filename) + # TODO: ask for save of opened files + else: + self.settings.remove('openedmodel') + ev.accept() + +if __name__ == '__main__': + if sys.version_info.major != 3: + print('Please use python 3.x') + sys.exit(1) + app = QApplication(sys.argv) + main = Main() + main.show() + app.exec_() + diff -r 1be00bcfaabb -r 4a37d6992bd3 python/diagramitems.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/python/diagramitems.py Mon Dec 24 13:24:59 2012 +0100 @@ -0,0 +1,435 @@ +""" + Contains all blocks that can be used to build models. +""" + +from PyQt4.QtGui import * +from PyQt4.QtCore import * + +def uniqify(name, names): + newname, i = name, 1 + while newname in names: newname, i = name + str(i), i + 1 + return newname + +def enum(**enums): + return type('Enum', (), enums) + +Position = enum(TOP=0, TOP_RIGHT=1, RIGHT=2, BOTTOM_RIGHT=3, BOTTOM=4, BOTTOM_LEFT=5, LEFT=6, TOP_LEFT=7) + +def buildPath(pts): + path = QPainterPath(pts[0]) + for pt in pts[1:]: path.lineTo(pt) + return path + +def equalSpace(n, l, offset=15): + if n == 1: + return [l / 2] + elif n > 1: + return [offset + (l - offset*2)/(n - 1)*i for i in range(n)] + return [] + +class Connection(QGraphicsPathItem): + """ A connection between blocks """ + def __init__(self, fromPort=None, toPort=None): + super(Connection, self).__init__() + self.pos2 = self.fromPort = self.toPort = None + self.setFlags(self.ItemIsSelectable | self.ItemClipsToShape) + pen = QPen(Qt.blue, 2, cap=Qt.RoundCap) + self.setPen(pen) + self.arrowhead = QGraphicsPathItem(self) + self.arrowhead.setPath(buildPath([QPointF(0.0, 0.0), QPointF(-6.0, 10.0), QPointF(6.0, 10.0), QPointF(0.0, 0.0)])) + self.arrowhead.setPen(pen) + self.arrowhead.setBrush(QBrush(pen.color())) + self.vias = [] + self.setFromPort(fromPort) + self.setToPort(toPort) + def getDict(self): + d = {} + d['fromBlock'] = self.fromPort.block.name + d['fromPort'] = self.fromPort.name + d['toBlock'] = self.toPort.block.name + d['toPort'] = self.toPort.name + return d + Dict = property(getDict) + def myDelete(self): + scene = self.scene() + if scene: + self.setFromPort(None) + self.setToPort(None) + scene.removeItem(self) + def setFromPort(self, fromPort): + if self.fromPort: + self.fromPort.posCallbacks.remove(self.setBeginPos) + self.fromPort.connection = None + self.fromPort = fromPort + if self.fromPort: + self.fromPort.connection = self + self.updateLineStukken() + self.fromPort.posCallbacks.append(self.setBeginPos) + def setToPort(self, toPort): + if self.toPort: + self.toPort.posCallbacks.remove(self.setEndPos) + self.toPort.connection = None + self.toPort = toPort + if self.toPort: + self.setEndPos(toPort.scenePos()) + self.toPort.connection = self + self.toPort.posCallbacks.append(self.setEndPos) + def getPos1(self): + if self.fromPort: + return self.fromPort.scenePos() + def setBeginPos(self, pos1): self.updateLineStukken() + def setEndPos(self, endpos): + self.pos2 = endpos + self.updateLineStukken() + def itemChange(self, change, value): + if change == self.ItemSelectedHasChanged: + for via in self.vias: + via.setVisible(value) + return super(Connection, self).itemChange(change, value) + def shape(self): return self.myshape + def updateLineStukken(self): + """ + This algorithm determines the optimal routing of all signals. + TODO: implement nice automatic line router + """ + pos1 = self.getPos1() + pos2 = self.pos2 + if pos1 is None or pos2 is None: + return + scene = self.scene() + vias = [pos1 + QPointF(20, 0)] + self.vias + [pos2 + QPointF(-20, 0)] + if scene: + litem = QGraphicsLineItem() + litem.setFlags(self.ItemIsSelectable) + scene.addItem(litem) + for p1, p2 in zip(vias[:-1], vias[1:]): + line = QLineF(p1, p2) + litem.setLine(line) + citems = scene.collidingItems(litem) + citems = [i for i in citems if type(i) is Block] + scene.removeItem(litem) + pts = [pos1] + vias + [pos2] + self.arrowhead.setPos(pos2) + self.arrowhead.setRotation(90) + p = buildPath(pts) + self.setPath(p) + """ Create a shape outline using the path stroker """ + s = super(Connection, self).shape() + pps = QPainterPathStroker() + pps.setWidth(10) + self.myshape = pps.createStroke(s).simplified() + +class PortItem(QGraphicsPathItem): + """ Represents a port to a subsystem """ + def __init__(self, name, block): + super(PortItem, self).__init__(block) + self.textItem = QGraphicsTextItem(self) + self.connection = None + self.block = block + self.setCursor(QCursor(Qt.CrossCursor)) + self.setPen(QPen(Qt.blue, 2, cap=Qt.RoundCap)) + self.name = name + self.posCallbacks = [] + self.setFlag(self.ItemSendsScenePositionChanges, True) + def getName(self): return self.textItem.toPlainText() + def setName(self, name): + self.textItem.setPlainText(name) + rect = self.textItem.boundingRect() + lw, lh = rect.width(), rect.height() + lx = 3 if type(self) is InputPort else -3 - lw + self.textItem.setPos(lx, -lh / 2) + name = property(getName, setName) + def getDict(self): + return {'name': self.name} + Dict = property(getDict) + def itemChange(self, change, value): + if change == self.ItemScenePositionHasChanged: + for cb in self.posCallbacks: cb(value) + return value + return super(PortItem, self).itemChange(change, value) + +class OutputPort(PortItem): + def __init__(self, name, block, d=10.0): + super(OutputPort, self).__init__(name, block) + self.setPath(buildPath([QPointF(0.0, -d), QPointF(d, 0), QPointF(0.0, d)])) + def mousePressEvent(self, event): + self.scene().startConnection(self) + +class InputPort(PortItem): + def __init__(self, name, block, d=10.0): + super(InputPort, self).__init__(name, block) + self.setPath(buildPath([QPointF(-d, -d), QPointF(0, 0), QPointF(-d, d)])) + +class Handle(QGraphicsEllipseItem): + """ A handle that can be moved by the mouse """ + def __init__(self, dx=10.0, parent=None): + super(Handle, self).__init__(QRectF(-0.5*dx,-0.5*dx,dx,dx), parent) + self.setBrush(QBrush(Qt.white)) + self.setFlags(self.ItemIsMovable) + self.setZValue(1) + self.setVisible(False) + self.setCursor(QCursor(Qt.SizeFDiagCursor)) + def mouseMoveEvent(self, event): + """ Move function without moving the other selected elements """ + p = self.mapToParent(event.pos()) + self.setPos(p) + +class ResizeSelectionHandle(Handle): + def __init__(self, position, block): + super(ResizeSelectionHandle, self).__init__(dx=12, parent=block) + self.position = position + self.block = block + if position in [Position.TOP_LEFT, Position.BOTTOM_RIGHT]: + self.setCursor(QCursor(Qt.SizeFDiagCursor)) + elif position in [Position.TOP_RIGHT, Position.BOTTOM_LEFT]: + self.setCursor(QCursor(Qt.SizeBDiagCursor)) + elif position in [Position.TOP, Position.BOTTOM]: + self.setCursor(QCursor(Qt.SizeVerCursor)) + elif position in [Position.LEFT, Position.RIGHT]: + self.setCursor(QCursor(Qt.SizeHorCursor)) + def mouseMoveEvent(self, event): + self.block.sizerMoveEvent(self, event.scenePos()) + +class Block(QGraphicsRectItem): + """ Represents a block in the diagram. """ + def __init__(self, name='Untitled', parent=None): + super(Block, self).__init__(parent) + self.selectionHandles = [ResizeSelectionHandle(i, self) for i in range(8)] + # Properties of the rectangle: + self.setPen(QPen(Qt.blue, 2)) + self.setBrush(QBrush(Qt.lightGray)) + self.setFlags(self.ItemIsSelectable | self.ItemIsMovable | self.ItemSendsScenePositionChanges) + self.setCursor(QCursor(Qt.PointingHandCursor)) + self.setAcceptHoverEvents(True) + self.label = QGraphicsTextItem(name, self) + self.name = name + # Create corner for resize: + button = QPushButton('+in') + button.clicked.connect(self.newInputPort) + self.buttonItemAddInput = QGraphicsProxyWidget(self) + self.buttonItemAddInput.setWidget(button) + self.buttonItemAddInput.setVisible(False) + button = QPushButton('+out') + button.clicked.connect(self.newOutputPort) + self.buttonItemAddOutput = QGraphicsProxyWidget(self) + self.buttonItemAddOutput.setWidget(button) + self.buttonItemAddOutput.setVisible(False) + # Inputs and outputs of the block: + self.inputs = [] + self.outputs = [] + self.changeSize(2,2) + def editParameters(self): + pd = ParameterDialog(self, self.window()) + pd.exec_() + def newInputPort(self): + names = [i.name for i in self.inputs + self.outputs] + self.addInput(InputPort(uniqify('in', names), self)) + def newOutputPort(self): + names = [i.name for i in self.inputs + self.outputs] + self.addOutput(OutputPort(uniqify('out', names), self)) + def setName(self, name): self.label.setPlainText(name) + def getName(self): return self.label.toPlainText() + name = property(getName, setName) + def getDict(self): + d = {'x': self.scenePos().x(), 'y': self.scenePos().y()} + rect = self.rect() + d.update({'width': rect.width(), 'height': rect.height()}) + d['name'] = self.name + d['inputs'] = [inp.Dict for inp in self.inputs] + d['outputs'] = [outp.Dict for outp in self.outputs] + return d + def setDict(self, d): + self.name = d['name'] + self.setPos(d['x'], d['y']) + self.changeSize(d['width'], d['height']) + for inp in d['inputs']: + self.addInput(InputPort(inp['name'], self)) + for outp in d['outputs']: + self.addOutput(OutputPort(outp['name'], self)) + Dict = property(getDict, setDict) + def addInput(self, i): + self.inputs.append(i) + self.updateSize() + def addOutput(self, o): + self.outputs.append(o) + self.updateSize() + def contextMenuEvent(self, event): + menu = QMenu() + pa = menu.addAction('Parameters') + pa.triggered.connect(self.editParameters) + menu.exec_(event.screenPos()) + def itemChange(self, change, value): + if change == self.ItemSelectedHasChanged: + for child in [self.buttonItemAddInput, self.buttonItemAddOutput]: + child.setVisible(value) + if value: + self.repositionAndShowHandles() + else: + [h.setVisible(False) for h in self.selectionHandles] + + return super(Block, self).itemChange(change, value) + def hoverEnterEvent(self, event): + if not self.isSelected(): + self.repositionAndShowHandles() + super(Block, self).hoverEnterEvent(event) + def hoverLeaveEvent(self, event): + if not self.isSelected(): + [h.setVisible(False) for h in self.selectionHandles] + super(Block, self).hoverLeaveEvent(event) + def myDelete(self): + for p in self.inputs + self.outputs: + if p.connection: p.connection.myDelete() + self.scene().removeItem(self) + def repositionAndShowHandles(self): + r = self.rect() + self.selectionHandles[Position.TOP_LEFT].setPos(r.topLeft()) + self.selectionHandles[Position.TOP].setPos(r.center().x(), r.top()) + self.selectionHandles[Position.TOP_RIGHT].setPos(r.topRight()) + self.selectionHandles[Position.RIGHT].setPos(r.right(), r.center().y()) + self.selectionHandles[Position.BOTTOM_RIGHT].setPos(r.bottomRight()) + self.selectionHandles[Position.BOTTOM].setPos(r.center().x(), r.bottom()) + self.selectionHandles[Position.BOTTOM_LEFT].setPos(r.bottomLeft()) + self.selectionHandles[Position.LEFT].setPos(r.left(), r.center().y()) + for h in self.selectionHandles: + h.setVisible(True) + def sizerMoveEvent(self, handle, pos): + r = self.rect().translated(self.pos()) + if handle.position == Position.TOP_LEFT: r.setTopLeft(pos) + elif handle.position == Position.TOP: r.setTop(pos.y()) + elif handle.position == Position.TOP_RIGHT: r.setTopRight(pos) + elif handle.position == Position.RIGHT: r.setRight(pos.x()) + elif handle.position == Position.BOTTOM_RIGHT: r.setBottomRight(pos) + elif handle.position == Position.BOTTOM: r.setBottom(pos.y()) + elif handle.position == Position.BOTTOM_LEFT: r.setBottomLeft(pos) + elif handle.position == Position.LEFT: r.setLeft(pos.x()) + else: + print('invalid position') + self.setCenterAndSize(r.center(), r.size()) + self.repositionAndShowHandles() + def updateSize(self): + rect = self.rect() + h, w = rect.height(), rect.width() + self.buttonItemAddInput.setPos(0, h + 4) + self.buttonItemAddOutput.setPos(w+10, h+4) + for inp, y in zip(self.inputs, equalSpace(len(self.inputs), h)): + inp.setPos(0.0, y) + for outp, y in zip(self.outputs, equalSpace(len(self.outputs), h)): + outp.setPos(w, y) + def setCenterAndSize(self, center, size): + self.changeSize(size.width(), size.height()) + p = QPointF(size.width(), size.height()) + self.setPos(center - p / 2) + def changeSize(self, w, h): + h = 20 if h < 20 else h + w = 40 if w < 40 else w + self.setRect(0.0, 0.0, w, h) + rect = self.label.boundingRect() + self.label.setPos((w - rect.width()) / 2, (h - rect.height()) / 2) + self.updateSize() + +class CodeBlock(Block): + def __init__(self, name='Untitled', parent=None): + super(CodeBlock, self).__init__(name, parent) + self.code = '' + def setDict(self, d): + super(CodeBlock, self).setDict(d) + self.code = d['code'] + def getDict(self): + d = super(CodeBlock, self).getDict() + d['code'] = self.code + return d + def gencode(self): + c = ['def {0}():'.format(self.name)] + if self.code: + c += indent(self.code.split('\n')) + else: + c += indent(['pass']) + return c + +class DiagramBlock(Block): + def __init__(self, name='Untitled', parent=None): + super(DiagramBlock, self).__init__(name, parent) + self.subModel = DiagramScene() + self.subModel.containingBlock = self + def setDict(self, d): + self.subModel.Dict = d['submodel'] + def mouseDoubleClickEvent(self, event): + # descent into child diagram + #self.editParameters() + print('descent') + scene = self.scene() + if scene: + for view in scene.views(): + view.diagram = self.subModel + view.zoomAll() + +class DiagramScene(QGraphicsScene): + """ A diagram scene consisting of blocks and connections """ + structureChanged = pyqtSignal() + def __init__(self): + super(DiagramScene, self).__init__() + self.startedConnection = None + + blocks = property(lambda sel: [i for i in sel.items() if isinstance(i, Block)]) + connections = property(lambda sel: [i for i in sel.items() if type(i) is Connection]) + def addItem(self, item): + super(DiagramScene, self).addItem(item) + if isinstance(item, Block): + self.structureChanged.emit() + def removeItem(self, item): + super(DiagramScene, self).removeItem(item) + if isinstance(item, Block): + self.structureChanged.emit() + def setDict(self, d): + for block in d['blocks']: + b = Block() + self.addItem(b) + b.Dict = block + for con in d['connections']: + fromPort = self.findPort(con['fromBlock'], con['fromPort']) + toPort = self.findPort(con['toBlock'], con['toPort']) + self.addItem(Connection(fromPort, toPort)) + def getDict(self): + return {'blocks': [b.Dict for b in self.blocks], 'connections': [c.Dict for c in self.connections]} + Dict = property(getDict, setDict) + def gencode(self): + c = [] + for b in self.blocks: + c += b.gencode() + for b in self.blocks: + c.append('{0}()'.format(b.name)) + return c + def findPort(self, blockname, portname): + block = self.findBlock(blockname) + if block: + for port in block.inputs + block.outputs: + if port.name == portname: return port + def findBlock(self, blockname): + for block in self.blocks: + if block.name == blockname: return block + def uniqify(self, name): + blocknames = [item.name for item in self.blocks] + return uniqify(name, blocknames) + def mouseMoveEvent(self, event): + if self.startedConnection: + pos = event.scenePos() + self.startedConnection.setEndPos(pos) + super(DiagramScene, self).mouseMoveEvent(event) + def mouseReleaseEvent(self, event): + if self.startedConnection: + for item in self.items(event.scenePos()): + if type(item) is InputPort and item.connection == None: + self.startedConnection.setToPort(item) + self.startedConnection = None + return + self.startedConnection.myDelete() + self.startedConnection = None + super(DiagramScene, self).mouseReleaseEvent(event) + def startConnection(self, port): + self.startedConnection = Connection(port, None) + pos = port.scenePos() + self.startedConnection.setEndPos(pos) + self.addItem(self.startedConnection) + def deleteItems(self): + for item in list(self.selectedItems()): item.myDelete() + diff -r 1be00bcfaabb -r 4a37d6992bd3 python/icons.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/python/icons.py Mon Dec 24 13:24:59 2012 +0100 @@ -0,0 +1,8 @@ + + +saveicon = b'iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAGGklEQVR42s2XeWxURRjAv7d3L9ay\nlJBCbAx/SIIpCWilhNLSFsrR0qUgyCmnnKVFa/xDjNbUKGIUSrlaLhEREWlZaQoC24VymBivoGJB\n/MuEtEpjoNt2z/H7Zt+8fdPdGk2MOu23MzvvzXy/+Y6ZWQX+46L8bwC2vLTlZte9e6MMRiMYjQYY\nPWYsZGXngM1iBkUxoERej1RYayJmYGrF1Dbj/31+P1xtuwQ3vvmaP6M/h8Nx69Xq6kclgDWrn2W3\nbt+G5KQkSE9Ph/E5eTB1xixIQID+yIraEMoVJYLGay4KGNS2ty8Aza4muHqpVZvClmCDHbW1igSw\neuUq1tPTA2EWRohkmJCbD9OLS+Fu14M4ZlP6M8lwSvSdEUNTwNXYCFc87iiAzQY7d9XJACuXr2Dd\n3d1gNpvBarXCxMmFUDzLCUk2S3zfKTrlqmIlYgqtTfWDXh80NZ6CNvdFnQUSYNfuXTLAimXLmV5B\nTn4hlDrLICUxHoAC+tHSTNz1jIcCw48HvX60AAK0RgGsaIE9e/fIAMuWPiMB5BZMAWfZHBiUaI1r\nAaZ+Mi32VKVqX5h/ZzwGzpxuRAtckFywt36fDLB08RIZoHAqlDhnQ0eXdwDlosFAP1APRGVoahK0\nYBBedp+XLNCwv0EGWLxwUT+AIphZ6ozNghhlEQCtrZqeqRC+QAjOftqEFogCkAX2HzwgAyxasFAC\nmFQwFaaVEIAJ5P2K6T5ln2uu0LnEHwzBZ2ea4Epr1AUU5AcPH5IBFsx/Wg5CBCgqdoLVbBxg5Xov\nDGyFQCgMF5tPI8B5CeDwkfdkgPlPzYsBmDLTCffu98b3f78OzS4s+g7Vg5Ot4G5xwdV+AEeOvi8D\nzJszV5p7IgLkT3equ9zAAPo4EMpFAIpnnrMuuKYDsCDA0WMfyABzy+bIAPlTIG9aKfzZecV0y5dc\nIdosMvzyORdc90RjwGKxwLHjH8oAZc7ZEoBj6DAYOeoxPJiMYKIDymTkbYPBoB5CkRpABByDcDjM\nJUQSDEIwFIIwyk83b0DXrx0SwPETH8kAs2eVxrhXKsrfPLnZwNOZEeDEyY9lgNLiEvYXpv1HCgGc\nPPWJDFAys/jfA8AD71RTowxQPH3GgAAK+t2IQv7X4oC+q5cSKiIORCyEyP9qLfr1AI2u0zLAjKJp\ncQHWblgPGRkZ0Q7G4uwFch8d6xXlm3jw0qEUCARgOF5yRDGZTOBqPiMDFBUUxsxLh8aa9evAbrfz\nVYuVRvVG2uKZ6COrvFj1Ao92fiL2eME+yK4pM6EFms+2yAAFeZNjAawWWF9eDqmpqVBT/ZqWZn1+\nH7y5dat2LxRmp1qY/pUtL/NgY9ju6e3lVz29Bc5dOC8D5OVMigNghfLKCg7wXOVm3kd53e31Qv3+\nBq6clAUx5/v7/I2a17mvOXBfHyTgLUgDQNdc8LTKADnZE2IAiHRz1fPgGDIEKjeW8z7aZPwBP9TW\n1WlK9QAC4p1tb2sAPp8PbLgYunFb8HgPBILgabssA0zIejIGgHy4uaoKARywacPGCABO7sOr9rs7\ntsdEOwnBkNRu34EAJnQBAz++TwB8V0WoIAZl2/VrMkDWuMdjAJISE2EjuoCCsBKjOuJrVISTbt32\nlqZMrJyiXQDt2VnHldGYYDCA7rSBGS0awvEmowmufH5dBhibOeYu+nSYHiABAdZhGg4igIoKbkqa\nnLKjpqZGWrneErTihn31XGEEIAgJOIZixqim5hdffRkFSElOUfDX0LgR6cNbMECGaBbAyF21dg2v\n+UBdkFGu6wH0z0jhgfp6vlIeuPiMrmGiUNr+0P5jcp/P5xUWoDoN/bT8kYczqjH4rCILFi5ZAvaH\n7JKPRZtMLtJPCE1Oz44cOsSh1X0KrBaLHiD47fffjcTmL/ojLhFlNCovtJjNDmRi2dnjk57IyhqM\nsWBBf1pxQjMJzmgke6rKmULJwVgQm36E8nd2dNxv9Xh+//nOHR9Ivxj4WjGRQu24+mb88psegNqE\nSQlrJH9lZmYacnNzjWlpaQpmBBdKLXXHU9QNiIkdEa0T9nq94c7OzpDb7Q63t7crTD6WFRXEj0J3\nveAfetNmUUsM6bsAAAAASUVORK5CYII=\n' + +loadicon = b'iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAACsElEQVR42u2Xf0hTURTHv++ZczlD\n6SeUREVRiRT9gFWM1rbAwr+iYAathPqjMosIQQqJQAhLLDI0CMwflRa1vyQst0oNLKxhCGWR9UfB\nwFCRaqC2vc6d7+1tr/7YnuwW5IEv95y79875vHvP7t4E/GUTpgGmAaL8vaRzpDlx3iuRXpEOkz5N\nFWDhrHR8bL+LtGXLKUqRJcoKyeUk1Q8Fges3gLIKeGlm+1QBHA4rPJ7WxG4eHqLlWoJRcvfJaPHY\nV1LPbwB5Dnja7if+BFUVQEdX7OqETdL48vD+C/DODze5e9iUCmAjgDt6FzJ+m/gG5GyD9OEz1lPY\nqwLQFrQ1JR+Amc0JPO0O941XBbAQQB0nABcBvNACbCGAWk4ABwngpRbATACXEksU7rsJGlnzUSZB\nkEdAbW9lXgnJtx0jAJ8WYCMBnE8MIESFx7+rRQRRLiaPESANmL2EAF6jgaLSCIA9Bx53sZokJpEY\nm4zFzP85BgTn0mUZUL9y7CPpz8CCvAJdA0DrE6D6NnxsLp9UaV2KVfW7Jy8QWcGUyUJhP0piFJA0\nmw6iAn19EAgAmdkICpRw5EEdsjabgXQDYo9gRZENVxVeIJO8Gjqs7y2wxoJ+IXUGxkcHkDrTqC+R\nXrt5D3AVoYU9yJs+L1bnruQLUFJO+34NpxlAc2MVCly7+ALkHQAedSKfAZwq3o/KK2X8ikvUT4ss\nkPyDWMwAtm5ai47uZn4Ag8PAAgvoxxzzGUCGMQ1DI89gMBr4ADymtwHHIXSSa1UOIt/zBqwz5/IB\nqG4Bjl9EDblFCkDN5ZM4csLJB+DoBaDWDTp3cVUBcDntaLx1hg/AzlKgvQc7yH2oAGSbjOg/WwjT\nvMzkFvdT65U34UdgDCtYGP1avoFUSMpK8gKw7q8n9bLgn/pj8n8C/ALihrxpNKi7hQAAAABJRU5E\nrkJggg==\n' + +newicon = b'iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAADSUlEQVR42r2XO0xaURjHLzYmdXRi\nYXBwdDScJl3djQNLw0TSAR88FR9JU6P4QKMCaTqQdGgXsQQMQXFwcGXhCCG+4iNEo6mDMZL45kK/\n76THUO+9Qi/efskJcm4O3+/+v8f51Al/LBaLmTs6Or43gAkq7eHhoXh2dmZva2v7WusZHf9jZGTE\nPz4+blfjuFwuC6IoCsViUbi8vCwDhM1oNH75V4AAANjUAJRKJeYc183NjXB1dVU+Pj7+CIp+qxlg\neHg44PV6VQHg2z8+PjKAu7s7oampSQAVxL29PUtnZ+eP/waA6/r6WtDr9UyJ09NTcXd319LV1aUI\n8QQwNDQUmJiYqBsA5BcMBgPLC4Q4OTkRt7e3LSaTSRbiCWBwcDAwOTmpOgfQOVSBcHFxIbS0tLB9\nDgH5IGYyGYvZbJZAvAoAOsL4IwTEngGxH9fp2MJnhUJBTKfT5t7e3rAsgMfjYQB4oB4V+OJAuI+A\naGtra6nz8/P3o6OjJQnAwMBAYGpqSjUAh8Aw8JLE3OBqIMTGxgbNZrPE5/MVFQFUe6+A4M5xoWMe\nIg4ASksB+vv761aAG3eKMFwBBFhfX6ebm5sEyl0eAHOgjqvgRSBUA3KAghHouFIAt9vNFNACgIci\nmUxSKEcyNjYmBXC5XK8Wgudvz8OAAJgDigD1lGE155UAsiFwOp2vUgVKzisBZJMQAdR2wlqco62s\nrNBcLkfgzpECOByOuhXg5cc733NLJBIMQLYP2O12BqB0uJrjyk8li8fjdGtri4AfeQAMAUpXayJW\ne2MlANlWbLPZGAB2LPagCoQapZaXl+nOzg6ZmZmRB8CBhANoYTB5U5iQyOzsrDwAjmR4hWpl0WiU\nwpxI5ubmpAAwKLAQ4HWqlUUiEbq/v0/m5+flATAEWiqwtLREDw4OyMLCgjyA1iEIh8P08PCQ+P1+\nKUBPTw8D0DIEi4uL9OjoiASDQSlAd3c3A7i/v9cUAKsgFApJAaxWa2B6etp2e3v71yGlen++X60v\n4EwAAOlUKvUO+oEUoLW19UNfX9/nxsbGhsoOxy+Wl75X2+drdXX1J/zX9AkmI+lU3N7e/hYSxAAT\n0Rs+FdX69rXsA1y5ubn5Vz6fL1Q++w30VO4/0/9IewAAAABJRU5ErkJggg==\n' + diff -r 1be00bcfaabb -r 4a37d6992bd3 python/ide.py --- /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 = """An all-in-one IDE for OS development.
+https://www.assembla.com/spaces/lcfOS/wiki
+Author: Windel Bouwman
+ """ + 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_() + diff -r 1be00bcfaabb -r 4a37d6992bd3 python/libs/compiler/core/__init__.py --- a/python/libs/compiler/core/__init__.py Mon Dec 24 13:21:13 2012 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,6 +0,0 @@ -from .instruction import * -from .function import * -from .value import * -from .bitreader import BitReader -from .errors import CompilerException - diff -r 1be00bcfaabb -r 4a37d6992bd3 python/libs/compiler/core/basicblock.py --- a/python/libs/compiler/core/basicblock.py Mon Dec 24 13:21:13 2012 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,5 +0,0 @@ - -class BasicBlock(Value): - def __init__(self): - pass - diff -r 1be00bcfaabb -r 4a37d6992bd3 python/libs/compiler/core/bitreader.py --- a/python/libs/compiler/core/bitreader.py Mon Dec 24 13:21:13 2012 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,28 +0,0 @@ -from .errors import CompilerException -import struct - -def bits(f): - while True: - Byte = f.read(1) - for i in range(8): - yield Byte & 0x1 - Byte >>= 1 - -class BitReader: - def __init__(self, f): - self.stream = bits(f) - self.curword = None - def parseModule(self): - if self.read(8) != ord('B') or self.read(8) != ord('C'): - raise CompilerException('Invalid bitcode signature') - for bitsig in [0x0, 0xC, 0xE, 0xD]: - if self.read(4) != bitsig: - raise CompilerException('Invalid bitcode signature') - - def read(self, numbits): - if numbits == 8: - b = self.stream.read(1) - print(b) - return int(b[0]) - return 2 - diff -r 1be00bcfaabb -r 4a37d6992bd3 python/libs/compiler/core/context.py --- a/python/libs/compiler/core/context.py Mon Dec 24 13:21:13 2012 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,4 +0,0 @@ - -class Context: - pass - diff -r 1be00bcfaabb -r 4a37d6992bd3 python/libs/compiler/core/errors.py --- a/python/libs/compiler/core/errors.py Mon Dec 24 13:21:13 2012 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,47 +0,0 @@ -""" Error handling routines """ - -class CompilerException(Exception): - def __init__(self, msg, row=0, col=0, filename=None): - self.msg = msg - self.row = row - self.col = col - self.filename = filename - def __repr__(self): - return self.msg - def __str__(self): - return self.msg - -class ErrorNode: - def __init__(self, row, col, msg): - self.row, self.col = row,col - self.msg = msg - -def Error(msg, node=None): - if node is None: - raise CompilerException(msg) - else: - raise CompilerException(msg, node.row, node.col) - -def printError(source, e): - def printLine(row, txt): - print(str(row)+':'+txt) - if e.row == 0: - print('Error: {0}'.format(e.msg)) - else: - lines = source.split('\n') - prerow = e.row - 3 - if prerow < 1: - prerow = 1 - afterrow = e.row + 3 - if afterrow > len(lines): - afterrow = len(lines) - - # print preceding source lines: - for r in range(prerow, e.row): - printLine(r, lines[r-1]) - # print source line containing error: - printLine(e.row, lines[e.row-1]) - print(' '*(len(str(e.row)+':')+e.col-1) + '^ Error: {0}'.format(e.msg)) - # print trailing source line: - for r in range(e.row+1, afterrow+1): - printLine(r, lines[r-1]) diff -r 1be00bcfaabb -r 4a37d6992bd3 python/libs/compiler/core/function.py --- a/python/libs/compiler/core/function.py Mon Dec 24 13:21:13 2012 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,6 +0,0 @@ - -class Function: - def __init__(self): - pass - - diff -r 1be00bcfaabb -r 4a37d6992bd3 python/libs/compiler/core/instruction.py --- a/python/libs/compiler/core/instruction.py Mon Dec 24 13:21:13 2012 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,15 +0,0 @@ -from .value import Value - -class Instruction(Value): - pass - -class CallInstruction(Instruction): - pass - -class BinaryOperator(Instruction): - pass - -class LoadInstruction(Instruction): - def __init__(self, ptr, name, insertBefore): - self.setName(name) - diff -r 1be00bcfaabb -r 4a37d6992bd3 python/libs/compiler/core/llvmtype.py --- a/python/libs/compiler/core/llvmtype.py Mon Dec 24 13:21:13 2012 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,13 +0,0 @@ - -def Enum(**enums): - return type('Enum', (), enums) - -class llvmType: - typeID = Enum(Void=0, Double=3, Integer=10, \ - Function=11, Struct=12, \ - Array=13, Pointer=14\ - Vector=15) - def __init__(self, context, tid): - self.context = context - - diff -r 1be00bcfaabb -r 4a37d6992bd3 python/libs/compiler/core/module.py --- a/python/libs/compiler/core/module.py Mon Dec 24 13:21:13 2012 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,4 +0,0 @@ - -class Module: - pass - diff -r 1be00bcfaabb -r 4a37d6992bd3 python/libs/compiler/core/value.py --- a/python/libs/compiler/core/value.py Mon Dec 24 13:21:13 2012 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,18 +0,0 @@ - -class Value: - def __init__(self, vty): - self.valueType = ty - self.name = None - def getContext(self): - return self.valueType.context - - def setName(self, name): - if not self.name and not name: - return - self.name = name - - if self.st: - pass - else: - pass - diff -r 1be00bcfaabb -r 4a37d6992bd3 python/ppci/core/__init__.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/python/ppci/core/__init__.py Mon Dec 24 13:24:59 2012 +0100 @@ -0,0 +1,6 @@ +from .instruction import * +from .function import * +from .value import * +from .bitreader import BitReader +from .errors import CompilerException + diff -r 1be00bcfaabb -r 4a37d6992bd3 python/ppci/core/basicblock.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/python/ppci/core/basicblock.py Mon Dec 24 13:24:59 2012 +0100 @@ -0,0 +1,5 @@ + +class BasicBlock(Value): + def __init__(self): + pass + diff -r 1be00bcfaabb -r 4a37d6992bd3 python/ppci/core/bitreader.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/python/ppci/core/bitreader.py Mon Dec 24 13:24:59 2012 +0100 @@ -0,0 +1,28 @@ +from .errors import CompilerException +import struct + +def bits(f): + while True: + Byte = f.read(1) + for i in range(8): + yield Byte & 0x1 + Byte >>= 1 + +class BitReader: + def __init__(self, f): + self.stream = bits(f) + self.curword = None + def parseModule(self): + if self.read(8) != ord('B') or self.read(8) != ord('C'): + raise CompilerException('Invalid bitcode signature') + for bitsig in [0x0, 0xC, 0xE, 0xD]: + if self.read(4) != bitsig: + raise CompilerException('Invalid bitcode signature') + + def read(self, numbits): + if numbits == 8: + b = self.stream.read(1) + print(b) + return int(b[0]) + return 2 + diff -r 1be00bcfaabb -r 4a37d6992bd3 python/ppci/core/context.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/python/ppci/core/context.py Mon Dec 24 13:24:59 2012 +0100 @@ -0,0 +1,4 @@ + +class Context: + pass + diff -r 1be00bcfaabb -r 4a37d6992bd3 python/ppci/core/errors.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/python/ppci/core/errors.py Mon Dec 24 13:24:59 2012 +0100 @@ -0,0 +1,47 @@ +""" Error handling routines """ + +class CompilerException(Exception): + def __init__(self, msg, row=0, col=0, filename=None): + self.msg = msg + self.row = row + self.col = col + self.filename = filename + def __repr__(self): + return self.msg + def __str__(self): + return self.msg + +class ErrorNode: + def __init__(self, row, col, msg): + self.row, self.col = row,col + self.msg = msg + +def Error(msg, node=None): + if node is None: + raise CompilerException(msg) + else: + raise CompilerException(msg, node.row, node.col) + +def printError(source, e): + def printLine(row, txt): + print(str(row)+':'+txt) + if e.row == 0: + print('Error: {0}'.format(e.msg)) + else: + lines = source.split('\n') + prerow = e.row - 3 + if prerow < 1: + prerow = 1 + afterrow = e.row + 3 + if afterrow > len(lines): + afterrow = len(lines) + + # print preceding source lines: + for r in range(prerow, e.row): + printLine(r, lines[r-1]) + # print source line containing error: + printLine(e.row, lines[e.row-1]) + print(' '*(len(str(e.row)+':')+e.col-1) + '^ Error: {0}'.format(e.msg)) + # print trailing source line: + for r in range(e.row+1, afterrow+1): + printLine(r, lines[r-1]) diff -r 1be00bcfaabb -r 4a37d6992bd3 python/ppci/core/function.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/python/ppci/core/function.py Mon Dec 24 13:24:59 2012 +0100 @@ -0,0 +1,6 @@ + +class Function: + def __init__(self): + pass + + diff -r 1be00bcfaabb -r 4a37d6992bd3 python/ppci/core/instruction.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/python/ppci/core/instruction.py Mon Dec 24 13:24:59 2012 +0100 @@ -0,0 +1,15 @@ +from .value import Value + +class Instruction(Value): + pass + +class CallInstruction(Instruction): + pass + +class BinaryOperator(Instruction): + pass + +class LoadInstruction(Instruction): + def __init__(self, ptr, name, insertBefore): + self.setName(name) + diff -r 1be00bcfaabb -r 4a37d6992bd3 python/ppci/core/llvmtype.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/python/ppci/core/llvmtype.py Mon Dec 24 13:24:59 2012 +0100 @@ -0,0 +1,13 @@ + +def Enum(**enums): + return type('Enum', (), enums) + +class llvmType: + typeID = Enum(Void=0, Double=3, Integer=10, \ + Function=11, Struct=12, \ + Array=13, Pointer=14\ + Vector=15) + def __init__(self, context, tid): + self.context = context + + diff -r 1be00bcfaabb -r 4a37d6992bd3 python/ppci/core/module.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/python/ppci/core/module.py Mon Dec 24 13:24:59 2012 +0100 @@ -0,0 +1,4 @@ + +class Module: + pass + diff -r 1be00bcfaabb -r 4a37d6992bd3 python/ppci/core/value.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/python/ppci/core/value.py Mon Dec 24 13:24:59 2012 +0100 @@ -0,0 +1,18 @@ + +class Value: + def __init__(self, vty): + self.valueType = ty + self.name = None + def getContext(self): + return self.valueType.context + + def setName(self, name): + if not self.name and not name: + return + self.name = name + + if self.st: + pass + else: + pass +