# HG changeset patch # User windel # Date 1329579743 -3600 # Node ID cbf199e007c28d9c8fd99ed79362599c816afde7 # Parent e47bfef80baf4e637ce4b2e1575c5b8fb85c5f54 Added some demo applications diff -r e47bfef80baf -r cbf199e007c2 applications/lab/bouncing_cube.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/applications/lab/bouncing_cube.py Sat Feb 18 16:42:23 2012 +0100 @@ -0,0 +1,405 @@ +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 + +""" + 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 e47bfef80baf -r cbf199e007c2 applications/lab/diagrameditor.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/applications/lab/diagrameditor.py Sat Feb 18 16:42:23 2012 +0100 @@ -0,0 +1,335 @@ +#!/usr/bin/python + +from PyQt4 import QtGui, QtCore +from PyQt4.QtGui import * +from PyQt4.QtCore import * +import sys + +""" + This script implements a basic diagram editor. +""" + +class Connection: + """ + - fromPort + - list of line items in between + - toPort + """ + def __init__(self, fromPort, toPort): + self.fromPort = fromPort + self.pos1 = None + self.pos2 = None + self.p1dir = None + self.p2dir = None + self.setFromPort(fromPort) + self.toPort = toPort + # Create arrow item: + self.linePieces = [] + def setFromPort(self, fromPort): + self.fromPort = fromPort + if self.fromPort: + self.pos1 = fromPort.scenePos() + self.fromPort.posCallbacks.append(self.setBeginPos) + def setToPort(self, toPort): + self.toPort = toPort + if self.toPort: + self.pos2 = toPort.scenePos() + self.toPort.posCallbacks.append(self.setEndPos) + def setBeginPos(self, pos1): + self.pos1 = pos1 + if self.pos1 and self.pos2: + self.updateLineStukken() + def setEndPos(self, endpos): + self.pos2 = endpos + if self.pos1 and self.pos2: + self.updateLineStukken() + def updateLineStukken(self): + """ + This algorithm determines the optimal routing of all signals. + TODO: implement nice automatic line router + """ + # TODO: create pieces of lines. + + # Determine the current amount of linestukken: + x1, y1 = self.pos1.x(), self.pos1.y() + x2, y2 = self.pos2.x(), self.pos2.y() + + ds = editor.diagramScene + + if y1 == y2 or x1 == x2: + pass + else: + # We require two lijnstukken to make one corner! + while len(self.linePieces) < 2: + lp = LinePieceItem() + ds.addItem(lp) + self.linePieces.append(lp) + lp1 = self.linePieces[0] + lp2 = self.linePieces[1] + lp1.setLine(QLineF(x1, y1, x2, y1)) + lp2.setLine(QLineF(x2, y1, x2, y2)) + + def delete(self): + editor.diagramScene.removeItem(self.arrow) + # Remove position update callbacks: + +class ParameterDialog(QDialog): + def __init__(self, parent=None): + super(ParameterDialog, self).__init__(parent) + self.button = QPushButton('Ok', self) + l = QVBoxLayout(self) + l.addWidget(self.button) + self.button.clicked.connect(self.OK) + def OK(self): + self.close() + +class PortItem(QGraphicsEllipseItem): + """ Represents a port to a subsystem """ + def __init__(self, name, parent=None): + QGraphicsEllipseItem.__init__(self, QRectF(-6,-6,12.0,12.0), parent) + self.setCursor(QCursor(QtCore.Qt.CrossCursor)) + # Properties: + self.setBrush(QBrush(Qt.red)) + # Name: + self.name = name + self.posCallbacks = [] + self.setFlag(self.ItemSendsScenePositionChanges, True) + def itemChange(self, change, value): + if change == self.ItemScenePositionHasChanged: + value = value.toPointF() # Required! + for cb in self.posCallbacks: + cb(value) + return value + return super(PortItem, self).itemChange(change, value) + def mousePressEvent(self, event): + editor.startConnection(self) + +# Block part: +class HandleItem(QGraphicsEllipseItem): + """ A handle that can be moved by the mouse """ + def __init__(self, parent=None): + super(HandleItem, self).__init__(QRectF(-4.0,-4.0,8.0,8.0), parent) + self.posChangeCallbacks = [] + self.setBrush(QtGui.QBrush(Qt.white)) + self.setFlag(self.ItemIsMovable, True) + self.setFlag(self.ItemSendsScenePositionChanges, True) + self.setCursor(QtGui.QCursor(Qt.SizeFDiagCursor)) + + def itemChange(self, change, value): + if change == self.ItemPositionChange: + value = value.toPointF() + x, y = value.x(), value.y() + # TODO: make this a signal? + # This cannot be a signal because this is not a QObject + for cb in self.posChangeCallbacks: + res = cb(x, y) + if res: + x, y = res + value = QPointF(x, y) + return value + # Call superclass method: + return super(HandleItem, self).itemChange(change, value) + +class BlockItem(QGraphicsRectItem): + """ + Represents a block in the diagram + Has an x and y and width and height + width and height can only be adjusted with a tip in the lower right corner. + + - in and output ports + - parameters + - description + """ + def __init__(self, name='Untitled', parent=None): + super(BlockItem, self).__init__(parent) + w = 60.0 + h = 40.0 + # Properties of the rectangle: + self.setPen(QtGui.QPen(QtCore.Qt.blue, 2)) + self.setBrush(QtGui.QBrush(QtCore.Qt.lightGray)) + self.setFlags(self.ItemIsSelectable | self.ItemIsMovable) + self.setCursor(QCursor(QtCore.Qt.PointingHandCursor)) + # Label: + self.label = QGraphicsTextItem(name, self) + # Create corner for resize: + self.sizer = HandleItem(self) + self.sizer.setPos(w, h) + self.sizer.posChangeCallbacks.append(self.changeSize) # Connect the callback + #self.sizer.setVisible(False) + self.sizer.setFlag(self.sizer.ItemIsSelectable, True) + + # Inputs and outputs of the block: + self.inputs = [] + self.inputs.append( PortItem('a', self) ) + self.inputs.append( PortItem('b', self) ) + self.inputs.append( PortItem('c', self) ) + self.outputs = [] + self.outputs.append( PortItem('y', self) ) + # Update size: + self.changeSize(w, h) + def editParameters(self): + pd = ParameterDialog(self.window()) + pd.exec_() + + def contextMenuEvent(self, event): + menu = QMenu() + menu.addAction('Delete') + pa = menu.addAction('Parameters') + pa.triggered.connect(self.editParameters) + menu.exec_(event.screenPos()) + + def changeSize(self, w, h): + """ Resize block function """ + # Limit the block size: + if h < 20: + h = 20 + if w < 40: + w = 40 + self.setRect(0.0, 0.0, w, h) + # center label: + rect = self.label.boundingRect() + lw, lh = rect.width(), rect.height() + lx = (w - lw) / 2 + ly = (h - lh) / 2 + self.label.setPos(lx, ly) + # Update port positions: + if len(self.inputs) == 1: + self.inputs[0].setPos(-4, h / 2) + elif len(self.inputs) > 1: + y = 5 + dy = (h - 10) / (len(self.inputs) - 1) + for inp in self.inputs: + inp.setPos(-4, y) + y += dy + if len(self.outputs) == 1: + self.outputs[0].setPos(w+4, h / 2) + elif len(self.outputs) > 1: + y = 5 + dy = (h - 10) / (len(self.outputs) + 0) + for outp in self.outputs: + outp.setPos(w+4, y) + y += dy + return w, h + +class LinePieceItem(QGraphicsLineItem): + def __init__(self): + super(LinePieceItem, self).__init__(None) + self.setPen(QtGui.QPen(QtCore.Qt.red,2)) + self.setFlag(self.ItemIsSelectable, True) + def x(self): + pass + +class EditorGraphicsView(QGraphicsView): + def __init__(self, scene, parent=None): + QGraphicsView.__init__(self, scene, parent) + 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 = str(event.mimeData().data('component/name')) + b1 = BlockItem(name) + b1.setPos(self.mapToScene(event.pos())) + self.scene().addItem(b1) + +class LibraryModel(QStandardItemModel): + def __init__(self, parent=None): + QStandardItemModel.__init__(self, parent) + def mimeTypes(self): + return ['component/name'] + def mimeData(self, idxs): + mimedata = QMimeData() + for idx in idxs: + if idx.isValid(): + txt = self.data(idx, Qt.DisplayRole).toByteArray() + mimedata.setData('component/name', txt) + return mimedata + +class DiagramScene(QGraphicsScene): + def __init__(self, parent=None): + super(DiagramScene, self).__init__(parent) + def mouseMoveEvent(self, mouseEvent): + editor.sceneMouseMoveEvent(mouseEvent) + super(DiagramScene, self).mouseMoveEvent(mouseEvent) + def mouseReleaseEvent(self, mouseEvent): + editor.sceneMouseReleaseEvent(mouseEvent) + super(DiagramScene, self).mouseReleaseEvent(mouseEvent) + +class DiagramEditor(QWidget): + def __init__(self, parent=None): + QtGui.QWidget.__init__(self, parent) + self.setWindowTitle("Diagram editor") + + # Widget layout and child widgets: + self.horizontalLayout = QtGui.QHBoxLayout(self) + self.libraryBrowserView = QtGui.QListView(self) + 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.red) + painter.drawEllipse(36, 2, 20, 20) + painter.setBrush(Qt.yellow) + painter.drawEllipse(20, 20, 20, 20) + painter.end() + + self.libItems = [] + self.libItems.append( QtGui.QStandardItem(QIcon(pixmap), 'Block') ) + self.libItems.append( QtGui.QStandardItem(QIcon(pixmap), 'Uber Unit') ) + self.libItems.append( QtGui.QStandardItem(QIcon(pixmap), 'Device') ) + for i in self.libItems: + self.libraryModel.appendRow(i) + self.libraryBrowserView.setModel(self.libraryModel) + self.libraryBrowserView.setViewMode(self.libraryBrowserView.IconMode) + self.libraryBrowserView.setDragDropMode(self.libraryBrowserView.DragOnly) + + self.diagramScene = DiagramScene(self) + self.diagramView = EditorGraphicsView(self.diagramScene, self) + self.horizontalLayout.addWidget(self.libraryBrowserView) + self.horizontalLayout.addWidget(self.diagramView) + + # Populate the diagram scene: + b1 = BlockItem('SubSystem1') + b1.setPos(50,100) + self.diagramScene.addItem(b1) + b2 = BlockItem('Unit2') + b2.setPos(-250,0) + self.diagramScene.addItem(b2) + + self.startedConnection = None + fullScreenShortcut = QShortcut(QKeySequence("F11"), self) + fullScreenShortcut.activated.connect(self.toggleFullScreen) + def toggleFullScreen(self): + self.setWindowState(self.windowState() ^ Qt.WindowFullScreen) + def startConnection(self, port): + self.startedConnection = Connection(port, None) + def sceneMouseMoveEvent(self, event): + if self.startedConnection: + pos = event.scenePos() + self.startedConnection.setEndPos(pos) + def sceneMouseReleaseEvent(self, event): + # Clear the actual connection: + if self.startedConnection: + pos = event.scenePos() + items = self.diagramScene.items(pos) + for item in items: + if type(item) is PortItem: + self.startedConnection.setToPort(item) + if self.startedConnection.toPort == None: + self.startedConnection.delete() + self.startedConnection = None + +if __name__ == '__main__': + app = QtGui.QApplication(sys.argv) + global editor + editor = DiagramEditor() + editor.show() + editor.resize(700, 800) + app.exec_() +