Пример #1
0
    def __init__(self):
        ManipulatedFrame.__init__(self)
        # set of selected point
        self.selection = []
        # axis for axial displacement
        self.axis = [
            SelectionAxis(self,
                          SelectionAxis.X,
                          material=Material((250, 30, 30), 1)),
            SelectionAxis(self,
                          SelectionAxis.Y,
                          material=Material((30, 250, 30), 1)),
            SelectionAxis(self,
                          SelectionAxis.Z,
                          material=Material((250, 250, 250), 1))
        ]

        # previous position to compute displacement
        self.previousPosition = Vec()
        self.manipulated = pyqtSignal(
        )  # AUTO SIGNAL TRANSLATION in class SelectionManipulator
        self.manipulated.connect(
            self.propagatePointTranslation
        )  # QObject.connect(self,SIGNAL("manipulated()"),self.propagatePointTranslation)
        for axis in self.axis:
            axis.translated.connect(
                self.propagateAxisTranslation
            )  # QObject.connect(axis,SIGNAL("translated(PyQt_PyObject)"),self.propagateAxisTranslation)
Пример #2
0
class MaterialPanelWidget(QWidget):
    valueChanged = pyqtSignal()

    def __init__(self, parent):
        QWidget.__init__(self, parent)
        self.setObjectName("materialPanelContents")
        self.verticalLayout = QVBoxLayout(self)
        self.verticalLayout.setSpacing(0)
        self.verticalLayout.setContentsMargins(0, 0, 0, 0)
        self.verticalLayout.setObjectName("materialPanelLayout")

        self.materialpanel = QScrollArea(self)
        self.view = MaterialPanelView(self.materialpanel)
        self.materialpanel.setWidget(self.view)
        self.materialpanel.setWidgetResizable(True)
        self.materialpanel.setObjectName("materialPanelArea")
        self.verticalLayout.addWidget(self.materialpanel)

        self.view.valueChanged.connect(self.__updateStatus)

    def __updateStatus(self):
        self.valueChanged.emit()

    def setTurtle(self, turtle):
        self.view.setTurtle(turtle)

    def getTurtle(self):
        return self.view.getTurtle()

    def setAppearanceList(self, matlist):
        self.view.setAppearanceList(matlist)

    def getAppearanceList(self):
        return self.view.getAppearanceList()
Пример #3
0
class LpyTabBarNeighbor(QWidget):
    
    newDocumentRequest = pyqtSignal()

    def __init__(self,parent):
        QWidget.__init__(self,parent)
        
    def mouseDoubleClickEvent(self,event):
        self.newDocumentRequest.emit()
        QWidget.mouseDoubleClickEvent(self,event)
Пример #4
0
class ComputationTask(QThread):

    killed = pyqtSignal()

    def __init__(self,
                 process=None,
                 postprocess=None,
                 preprocess=None,
                 cleanupprocess=None):
        QThread.__init__(self)
        self.process = process
        self.postprocess = postprocess
        self.preprocess = preprocess
        self.cleanupprocess = cleanupprocess
        self.threaded = True
        self.exception = None

    def initialize(self):
        if self.preprocess:
            self.preprocess(self)

    def run(self):
        if self.process:
            if self.threaded:
                try:
                    self.process(self)
                except:
                    self.exception = ThreadTransferException(
                        sys.exc_info()[0],
                        sys.exc_info()[1],
                        sys.exc_info()[2])
            else:
                self.process(self)

    def finalize(self):
        if self.threaded and not self.exception is None:
            raise self.exception
        if self.postprocess:
            self.postprocess(self)

    def apply(self):
        self.threaded = False
        self.initialize()
        self.run()
        self.finalize()

    def kill(self):
        self.quit()
        if self.cleanupprocess:
            self.cleanupprocess()
        self.killed.emit()

    def __call__(self):
        self.start()
Пример #5
0
    class ItemSlider(QSpinBox):
        
        valueChanged = pyqtSignal('PyQt_PyObject') 

        def __init__(self,orientation, parent, item):
            QSpinBox.__init__(self, parent)
            self.item = item
            scalar = item.scalar
            self.setRange(scalar.minvalue,scalar.maxvalue)
            self.setValue(scalar.value)
            self.valueChanged.connect(self.updateItem) # QObject.connect(self,SIGNAL('valueChanged(int)'),self.updateItem)

        def updateItem(self,value):
            self.item.scalar.value = value
            self.item.setText(str(value))
            self.valueChanged.emit(self.item.scalar) # self.emit(SIGNAL('valueChanged(PyQt_PyObject)'),self.item.scalar) # AUTO SIGNAL TRANSLATION
Пример #6
0
class MyItemModel(QStandardItemModel):
    
    moveRequest = pyqtSignal(int,int)

    def __init__(self,a,b,scalarmap):
        QStandardItemModel.__init__(self,a,b)
        self.scalarmap = scalarmap
        
    def dropMimeData(self,data,action,row,column,parent):        
        encoded = data.data("application/x-qstandarditemmodeldatalist")
        stream = QDataStream(encoded, QIODevice.ReadOnly)
        r = stream.readInt()
        self.moveRequest.emit(r,parent.row()) # self.emit(SIGNAL("moveRequest(int,int)"),r,parent.row()) # AUTO SIGNAL TRANSLATION
        return True
        
    def supportedDropActions(self):
        return Qt.MoveAction
Пример #7
0
class MaterialPanelDock(QDockWidget):
    valueChanged = pyqtSignal()

    def __init__(self, parent, name=None):
        QDockWidget.__init__(self, parent)
        if name:
            self.setObjectName(name.replace(' ', '_'))
        self.setWindowTitle('Color Map')
        self.view = MaterialPanelWidget(self)
        self.setWidget(self.view)
        self.view.valueChanged.connect(self.__updateStatus)

    def setStatusBar(self, st):
        self.view.statusBar = st

    def showMessage(self, msg, timeout):
        if hasattr(self, 'statusBar'):
            self.statusBar.showMessage(msg, timeout)
        else:
            print(msg)

    def __updateStatus(self):
        self.valueChanged.emit()
Пример #8
0
class CtrlPoint(ManipulatedFrame):
    """the class CtrlPoint  represent the control points of a NurbsPatch by PlantGL Spheres. 
        The user can grab them and move them dynamically. 
        Change of value is automatically propagate to the initial structure using the position_setter function """

    #modified = pyqtSignal()
    #manipulated = pyqtSignal()
    translated = pyqtSignal("PyQt_PyObject", "PyQt_PyObject")
    valueChanged = pyqtSignal(int)

    def __init__(self, position, position_setter, color=(30, 30, 250), id=0):
        """Constructor 
           :param position: initial position of the control point
           :param position_setter: makes it possible to propagate position change to initial structure
        """
        ManipulatedFrame.__init__(self)
        self.id = id

        # current position and method to propagate displacement on original structure
        self.setPosition(Vec(position[0], position[1], position[2]))
        self.position_setter = position_setter

        # point selection or focus
        self.selected = False
        self.hasFocus = False
        self.color = color

        # previous position to compute displacement
        self.previousPosition = self.position()

        # Constraint the direction of translation
        self.constraint = WorldConstraint()
        self.constraint.setRotationConstraintType(
            AxisPlaneConstraint.FORBIDDEN)
        self.setConstraint(self.constraint)

        # SIGNAL to propagate displacement
        self.modified.connect(
            self.__propagate_position_change__
        )  # QObject.connect(self,SIGNAL('modified()'),self.__propagate_position_change__)
        self.manipulated.connect(
            self.__emit_translation__
        )  # QObject.connect(self,SIGNAL('manipulated()'),self.__emit_translation__)

        # manipulated call back
        self.callback = None

    def setCallBack(self, callback):
        self.callback = callback

    def __push_position__(self):
        """ push current position status as previous position """
        self.previousPosition = self.position()

    def displacement(self):
        """ get displacement applied to the point """
        delta = self.position() - self.previousPosition
        self.__push_position__()
        return delta

    def __propagate_position_change__(self):
        """ apply position change to initial structure """
        self.position_setter(self.position())

    def __emit_translation__(self):
        """ emit a SIGNAL when a translation has been applied to the point """
        pos = self.position()
        tr = pos - self.previousPosition
        self.translated.emit(
            self, tr
        )  # self.emit(SIGNAL("translated(PyQt_PyObject,PyQt_PyObject)"),self,tr) # AUTO SIGNAL TRANSLATION
        self.valueChanged.emit(
            self.id
        )  # self.emit(SIGNAL("valueChanged(int)"),self.id) # AUTO SIGNAL TRANSLATION
        if self.callback:
            self.callback(self.id)

    def apply_translation(self, tr):
        """ apply a displacement. USefull when point is part of selection and a displacement  occur on another point """
        self.setPosition(self.position() + tr)
        self.__push_position__()

    def representation(self, pointsize=0.05):
        """ Compute representation of the control points as PlantGL Spheres"""
        p = self.position()
        #we change the color of the point if it is selected or has focus
        if self.hasFocus: m = Material((250, 30, 30), 1)  # red
        elif self.selected: m = Material((250, 250, 30), 1)  #yellow
        else: m = Material(self.color, 1)
        #the point is represented by a PlantGl Sphere
        primitive = pointsize if isinstance(pointsize,
                                            Geometry) else Sphere(pointsize)
        return Shape(Translated(Vector3(p.x, p.y, p.z), primitive), m, self.id)

    def getx(self):
        """ Get the x coordinate """
        return self.position().x

    def gety(self):
        """ Get the y coordinate """
        return self.position().y

    def getz(self):
        """ Get the z coordinate """
        return self.position().z

    def setx(self, x):
        """ Set the x coordinate """
        p = self.position()
        p.x = x
        self.setPosition(p)

    x = property(getx, """ The x coordinate property """)
    y = property(gety, """ The y coordinate property """)
    z = property(getz, """ The z coordinate property """)
Пример #9
0
class MaterialPanelView(QGLWidget):
    valueChanged = pyqtSignal()

    def __init__(self, parent):
        QGLWidget.__init__(self, parent)
        self.unitsize = 30
        self.sphere = None
        self.spherelist = None
        self.darkcheck = [0.5, 0.5, 0.5]
        self.lightcheck = [0.9, 0.9, 0.9]
        self.checklist = None
        self.selectedBorderList = None
        self.turtle = PglTurtle()
        self.mousepos = None
        self.initpos = None
        self.menuselection = None
        self.selectionbegin = None
        self.selectionend = None
        self.cursorselection = None
        self.cutaction = False
        self.preview = None
        self.previewselection = None
        self.previewtrigger = QTimer(self)
        self.previewtrigger.setInterval(1000)
        self.previewtrigger.setSingleShot(True)
        self.clipboard = None
        self.previewtrigger.timeout.connect(self.displayPreview)
        self.valueChanged.connect(self.computeSize)

        self.lastTextureDir = '.'
        self.preview_start_show = False
        self.defaultmat = Material('default')
        self.setAcceptDrops(True)

    def setTurtle(self, turtle):
        self.turtle = turtle
        self.cutaction = False
        self.computeSize()
        if self.isVisible(): self.updateGL()

    def getTurtle(self):
        return self.turtle

    def getMaterial(self, index):
        return self.turtle.getMaterial(index)

    def setMaterial(self, index, value):
        self.turtle.setMaterial(index, value)

    def insertMaterial(self, index, value):
        self.turtle.insertMaterial(index, value)

    def delMaterial(self, index):
        self.turtle.removeColor(index)

    def nbMaterial(self):
        return self.turtle.getColorListSize()

    def swapMaterial(self, index1, index2):
        mat1, mat2 = self.getMaterial(index1), self.getMaterial(index2)
        mat1.name = 'Color_' + str(index2)
        mat2.name = 'Color_' + str(index1)
        self.setMaterial(index1, mat2), self.setMaterial(index2, mat1)

    def setAppearanceList(self, appList):
        # for i,mat in enumerate(materialList):
        #     self.setMaterial(i,mat)
        # if self.nbMaterial() > len(materialList):
        #     for i in xrange(self.nbMaterial()-1,len(materialList)-1,-1):
        #         self.delMaterial(i)
        self.turtle.setColorList(appList)
        self.cutaction = False
        self.computeSize()
        if self.isVisible(): self.updateGL()

    def getAppearanceList(self):
        return self.turtle.getColorList()

    def resizeGL(self, w, h):
        if w == 0 or h == 0: return
        self.computeSize()

    def computeSize(self, w=None, h=None):
        if w is None: w = self.width()
        if h is None: h = self.height()
        if w == 0 or h == 0: return
        nbcitem, nbritem = self.getNbColRow(w, h)
        pcolor = self.positionColor(self.nbMaterial())
        maxwidth = pcolor[0] + (2 * self.unitsize)
        self.setMinimumSize(maxwidth, 2 * self.unitsize)

    def initializeGL(self):
        glClearColor(1.0, 1.0, 1.0, 1.0)

        glShadeModel(GL_SMOOTH)
        glLightfv(GL_LIGHT0, GL_POSITION, [1000, 1000, 2000, 0])
        glLightModelfv(GL_LIGHT_MODEL_AMBIENT, [1.0, 1.0, 1.0, 1.0])
        glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)

        glEnable(GL_LIGHTING)
        glEnable(GL_LIGHT0)
        glEnable(GL_DEPTH_TEST)
        glEnable(GL_BLEND)

        if self.sphere:
            gluDeleteQuadric(self.sphere)
        self.sphere = gluNewQuadric()
        gluQuadricDrawStyle(self.sphere, GLU_FILL)
        gluQuadricNormals(self.sphere, GLU_SMOOTH)

        self.spherelist = glGenLists(1)
        glNewList(self.spherelist, GL_COMPILE)
        gluSphere(self.sphere, self.unitsize * 0.8, 80, 80)
        glEndList()

        checkdepth = -self.unitsize
        self.checklist = glGenLists(1)
        glNewList(self.checklist, GL_COMPILE)
        glDisable(GL_LIGHTING)
        glBegin(GL_QUADS)
        glColor3fv(self.lightcheck)
        glVertex3f(-self.unitsize, self.unitsize, checkdepth)
        glVertex3f(0, self.unitsize, checkdepth)
        glVertex3f(0, 0, checkdepth)
        glVertex3f(-self.unitsize, 0, checkdepth)

        glVertex3f(0, 0, checkdepth)
        glVertex3f(self.unitsize, 0, checkdepth)
        glVertex3f(self.unitsize, -self.unitsize, checkdepth)
        glVertex3f(0, -self.unitsize, checkdepth)

        glColor3fv(self.darkcheck)
        glVertex3f(0, self.unitsize, checkdepth)
        glVertex3f(self.unitsize, self.unitsize, checkdepth)
        glVertex3f(self.unitsize, 0, checkdepth)
        glVertex3f(0, 0, checkdepth)

        glVertex3f(-self.unitsize, 0, checkdepth)
        glVertex3f(0, 0, checkdepth)
        glVertex3f(0, -self.unitsize, checkdepth)
        glVertex3f(-self.unitsize, -self.unitsize, checkdepth)
        glEnd()
        glEndList()

        self.texturedQuadList = glGenLists(1)
        glNewList(self.texturedQuadList, GL_COMPILE)
        glBegin(GL_QUADS)
        glTexCoord2d(0, 1)
        glVertex3f(-self.unitsize, -self.unitsize, checkdepth)
        glTexCoord2d(1, 1)
        glVertex3f(self.unitsize, -self.unitsize, checkdepth)
        glTexCoord2d(1, 0)
        glVertex3f(self.unitsize, self.unitsize, checkdepth)
        glTexCoord2d(0, 0)
        glVertex3f(-self.unitsize, self.unitsize, checkdepth)
        glEnd()
        glEndList()

        borderdepth = -self.unitsize + 1
        self.selectedBorderList = glGenLists(1)
        glNewList(self.selectedBorderList, GL_COMPILE)
        glLineWidth(3)
        glColor3f(0.3, 0.3, 0.3)
        glBegin(GL_LINE_STRIP)
        glVertex3f(-self.unitsize, -self.unitsize, borderdepth)
        glVertex3f(self.unitsize, -self.unitsize, borderdepth)
        glVertex3f(self.unitsize, self.unitsize, borderdepth)
        glVertex3f(-self.unitsize, self.unitsize, borderdepth)
        glVertex3f(-self.unitsize, -self.unitsize, borderdepth)
        glEnd()
        glLineWidth(1)
        glColor3f(0.0, 0.0, 0.0)
        borderdepth += 1
        glBegin(GL_LINE_STRIP)
        glVertex3f(-self.unitsize, -self.unitsize, borderdepth)
        glVertex3f(self.unitsize, -self.unitsize, borderdepth)
        glVertex3f(self.unitsize, self.unitsize, borderdepth)
        glVertex3f(-self.unitsize, self.unitsize, borderdepth)
        glVertex3f(-self.unitsize, -self.unitsize, borderdepth)
        glEnd()
        glEndList()

    def paintGL(self):
        cursorselection = -1
        if not self.isVisible(): return
        w, h = self.width(), self.height()
        if w == 0 or h == 0: return
        if self.mousepos != None and self.geometry().contains(self.mousepos):
            cursorselection = self.selectedColor(self.mousepos.x(),
                                                 self.mousepos.y())
        scaling = self.window().devicePixelRatio()
        glViewport(0, 0, int(w * scaling), int(h * scaling))
        glMatrixMode(GL_PROJECTION)
        glLoadIdentity()
        glOrtho(0, w, h, 0, -3000, 1000)
        glMatrixMode(GL_MODELVIEW)
        glLoadIdentity()
        try:
            glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)
        except:
            # The visible attribute is not correctly set on mac.
            # The widget is not really visible and the initialization failed.
            return
        glShadeModel(GL_SMOOTH)
        nbcitem, nbritem = self.getNbColRow(w, h)
        colindex = 0
        d = Discretizer()
        glr = GLRenderer(d)
        selectionbegin = self.selectionbegin
        selectionend = self.selectionend
        for i in range(0, int(nbcitem)):
            for j in range(0, int(nbritem)):
                if colindex < self.nbMaterial():
                    curmat = self.getMaterial(colindex)
                else:
                    curmat = self.defaultmat
                istexture = curmat.isTexture()
                inselection = self.isInSelection(colindex)
                glPushMatrix()
                glTranslate(self.unitsize + 2 * self.unitsize * i,
                            self.unitsize + 2 * self.unitsize * j, 0)
                if inselection and not self.initpos is None:
                    glCallList(self.checklist)
                    decal = self.mousepos - self.initpos
                    glTranslate(decal.x(), decal.y(), 2 * self.unitsize)
                if istexture:
                    glCallList(self.checklist)
                    glTranslate(0, 0, 1)
                    glEnable(GL_LIGHTING)
                    curmat.apply(glr)
                    glCallList(self.texturedQuadList)
                    glDisable(GL_TEXTURE_2D)
                    if inselection:
                        glCallList(self.selectedBorderList)
                else:
                    glCallList(self.checklist)
                    if inselection:
                        glCallList(self.selectedBorderList)
                    glEnable(GL_LIGHTING)
                    curmat.apply(glr)
                    if colindex == cursorselection:
                        glScalef(1.3, 1.3, 1.3)
                    elif inselection:
                        glScalef(1.2, 1.2, 1.2)
                    glCallList(self.spherelist)
                glPopMatrix()
                glColor3f(0, 0, 0)
                self.renderText(2 * self.unitsize * i,
                                2 * self.unitsize * (j + 1), 4 * self.unitsize,
                                str(colindex))
                colindex += 1
            glPushMatrix()
            glTranslate(self.unitsize + 2 * self.unitsize * i,
                        self.unitsize + 2 * self.unitsize * nbritem, 0)
            glCallList(self.checklist)
            glPopMatrix()

    def getNbColRow(self, w, h):
        nbcol = (w / (2 * self.unitsize)) + 1
        nbrow, rest = divmod(h, 2 * self.unitsize)
        if rest > self.unitsize:
            nbrow += 1
        if nbrow == 0: nbrow = 1
        return nbcol, nbrow

    def isInSelection(self, i):
        if self.selectionend is None: return False
        if self.selectionbegin is None: return False
        if self.selectionbegin <= i <= self.selectionend: return True
        if self.selectionend <= i <= self.selectionbegin: return True
        return False

    def lenSelection(self):
        if self.selectionend is None: return 0
        if self.selectionbegin is None: return 0
        return abs(self.selectionend - self.selectionbegin) + 1

    def setSelection(self, beg, end=None):
        self.selectionbegin = beg
        if end is None:
            self.selectionend = beg
        else:
            self.selectionend = end

    def getSelection(self):
        return range(self.selectionbegin, self.selectionend + 1)

    def selectedColor(self, x, y):
        w = self.width()
        h = self.height()
        nbcol, nbrow = self.getNbColRow(w, h)
        nbcolselection = x // (2 * self.unitsize)
        nbrowselection = y // (2 * self.unitsize)
        if nbcolselection >= nbcol or nbrowselection >= nbrow: return -1
        id = nbcolselection * nbrow + nbrowselection
        return id

    def positionColor(self, id):
        w = self.width()
        h = self.height()
        nbcol, nbrow = self.getNbColRow(w, h)
        idcol, idrow = divmod(id, nbrow)
        return (idcol * (2 * self.unitsize), idrow * (2 * self.unitsize))

    def mouseDoubleClickEvent(self, event):
        x, y = event.pos().x(), event.pos().y()
        self.edition(self.selectedColor(x, y))
        self.initpos = None
        event.accept()
        self.updateGL()

    def mousePressEvent(self, event):
        if self.preview and self.preview.isVisible():
            self.preview.hide()
        if event.button() == Qt.LeftButton:
            self.initpos = event.pos()
            self.mousepos = self.initpos
            x, y = event.pos().x(), event.pos().y()
            if event.modifiers() & Qt.ShiftModifier:
                self.selectionend = self.selectedColor(x, y)
            else:
                self.setSelection(self.selectedColor(x, y))
            self.updateGL()

    def mouseReleaseEvent(self, event):
        if event.button() == Qt.LeftButton:
            x, y = event.pos().x(), event.pos().y()

            if event.modifiers() & Qt.ShiftModifier:
                self.selectionend = self.selectedColor(x, y)
            else:
                initialselection = self.selectionbegin
                self.setSelection(self.selectedColor(x, y))

            if self.selectionbegin == self.selectionend:
                if initialselection != self.selectionbegin:
                    self.showMessage(
                        "Swap colors " + str(initialselection) + " and " +
                        str(self.selectionbegin), 2000)
                    self.swapMaterial(initialselection, self.selectionbegin)
                    self.valueChanged.emit()
                else:
                    self.showMessage(
                        "Click on color " + str(self.selectionbegin), 2000)

            else:
                self.selectionbegin, self.selectionend = min(
                    self.selectionbegin,
                    self.selectionend), max(self.selectionbegin,
                                            self.selectionend)
                self.showMessage(
                    "Selected colors from " + str(self.selectionbegin) +
                    ' to ' + str(self.selectionend), 2000)
            self.initpos = None
            self.updateGL()

    def mouseMoveEvent(self, event):
        self.mousepos = event.pos()
        lastcursorselection = self.cursorselection
        self.cursorselection = self.selectedColor(self.mousepos.x(),
                                                  self.mousepos.y())
        if self.cursorselection < 0 or not self.geometry().contains(
                self.mousepos):
            self.cursorselection = lastcursorselection
        if event.buttons() == Qt.LeftButton:
            if self.previewtrigger.isActive():
                self.previewtrigger.stop()
            if event.modifiers() & Qt.ShiftModifier:
                self.selectionend = self.cursorselection
            # else:
            #     if self.cursorselection != lastcursorselection and not lastcursorselection is None:
            #         p1 = QPoint(*self.positionColor(lastcursorselection))
            #         p2 = QPoint(*self.positionColor(self.cursorselection))
            #         self.initpos = self.initpos - p1 + p2
            #         self.setSelection(self.cursorselection)
        elif event.buttons(
        ) == Qt.NoButton and not self.cursorselection is None and self.cursorselection >= 0:
            cmat = self.getMaterial(self.cursorselection)
            if cmat.isTexture():
                if lastcursorselection != self.cursorselection or (
                        self.preview is None
                        and not self.previewtrigger.isActive()):
                    self.setToolTip('Texture ' + str(self.cursorselection) +
                                    ' : "' + cmat.image.filename + '"')
                    if not self.preview is None:
                        self.preview.hide()
                    self.previewselection = self.cursorselection
                    self.previewtrigger.start()
                elif self.preview and self.preview.isVisible():
                    self.preview.move(event.globalPos() + QPoint(2, 2))
            else:
                if lastcursorselection != self.cursorselection:
                    self.setToolTip('Color ' + str(self.cursorselection))
                if not self.preview is None and self.preview.isVisible():
                    self.preview.hide()
                if self.previewtrigger.isActive():
                    self.previewtrigger.stop()
        self.showMessage("Mouse on color " + str(self.cursorselection), 2000)
        self.updateGL()

    def displayPreview(self):
        self.preview = QSplashScreen(
            self,
            QPixmap(self.getMaterial(self.previewselection).image.filename))
        self.preview.move(QCursor.pos() + QPoint(2, 2))
        self.preview.show()

    def leaveEvent(self, event):
        if not self.geometry().contains(self.mapFromGlobal(QCursor.pos())):
            self.mousepos = None
            self.initpos = None
            self.setMouseTracking(False)
            self.updateGL()
            if self.preview and self.preview.isVisible():
                self.preview.hide()
            if self.previewtrigger.isActive():
                self.previewtrigger.stop()
        QGLWidget.leaveEvent(self, event)

    def enterEvent(self, event):
        self.mousepos = None
        self.setMouseTracking(True)
        QGLWidget.enterEvent(self, event)

    def showMessage(self, msg, timeout):
        if hasattr(self, 'statusBar'):
            self.statusBar.showMessage(msg, timeout)

    def edition(self, id):
        self.showMessage("Edit color = " + str(id), 2000)
        if id >= 0:
            color = self.getMaterial(id)
            if not color.isTexture():
                try:
                    res = editMaterialInDialog(
                        color)  # Warning Here! QLayout problem.
                    if res is None or res == QDialog.Accepted:
                        self.valueChanged.emit()
                except Exception as e:
                    print(e)
                    print(
                        'editMaterialInDialog not supported by your version of PlantGL'
                    )
            else:
                self.edittexture(color.image.filename)

    def contextMenuEvent(self, event):
        self.menuselection = self.selectedColor(event.x(), event.y())
        if self.lenSelection() > 0:
            if not self.isInSelection(self.menuselection):
                self.setSelection(self.menuselection)
                self.updateGL()
        else:
            self.setSelection(self.menuselection)
            self.updateGL()
        menu = QMenu("Color Edit", self)
        menu.addAction("Copy", self.copymaterial)
        menu.addAction("Cut", self.cutmaterial)
        action = menu.addAction("Paste", self.pastematerial)
        if self.clipboard is None:
            action.setEnabled(False)
        action = menu.addAction("Load texture", self.loadtexture)
        if self.lenSelection() > 1:
            action.setEnabled(False)
        if self.lenSelection() == 1 and self.getMaterial(
                self.getSelection()[0]).isTexture():
            menu.addAction("Open repository", self.opentexturerepository)
        menu.addAction("Remove", self.removematerial)
        action = menu.addAction("Interpolate", self.interpolatematerial)
        if self.lenSelection() < 3:
            action.setEnabled(False)
        menu.exec_(event.globalPos())

    def opentexturerepository(self):
        import os, sys
        cmat = self.getMaterial(self.getSelection()[0])

        fname = os.path.abspath(cmat.image.filename)
        mdir = os.path.dirname(fname)
        if sys.platform == 'win32':
            import subprocess
            subprocess.call('explorer /select,"' + fname + '"')
        elif sys.platform == 'linux2':
            os.system('xdg-open "' + mdir + '"')
        else:
            os.system('open "' + mdir + '"')

    def copymaterial(self):
        self.clipboard = [(i, self.getMaterial(i))
                          for i in self.getSelection()]
        self.cutaction = False
        self.menuselection = None

    def cutmaterial(self):
        self.clipboard = [(i, self.getMaterial(i))
                          for i in self.getSelection()]
        self.cutaction = True
        self.menuselection = None

    def pastematerial(self):
        if self.clipboard is None or not self.lenSelection() in [
                1, len(self.clipboard)
        ]:
            QMessageBox.warning(
                self, 'Copy error',
                'Cannot copy materials ! Source and target are inconsistents')
        else:
            if self.lenSelection() == 1:
                sel = self.selectionbegin
                if self.cutaction == True:
                    sourceindices = [i[0] for i in self.clipboard]
                    if sel in sourceindices:
                        sel = min(sourceindices)
                        sourceindices.pop(sourceindices.index(sel))
                    for i in reversed(sourceindices):
                        self.delMaterial(i)
                        if i < sel:
                            sel -= 1
                print('Copy ', self.clipboard[0][0], 'to', sel)
                self.setMaterial(sel, self.clipboard[0][1].deepcopy())
                for i, color in reversed(self.clipboard[1:]):
                    self.insertMaterial(sel + 1, color.deepcopy())
                self.setSelection(sel, sel + len(self.clipboard) - 1)
            else:
                for color, pos in zip(self.clipboard, self.getSelection()):
                    self.setMaterial(pos, color[1].deepcopy())
                if self.cutaction == True:
                    iminus = 0
                    for pos, color in reversed(self.clipboard):
                        if not self.isInSelection(pos):
                            self.delMaterial(pos)
                            if pos < self.selectionbegin:
                                iminus -= 1
                    self.setSelection(self.selectionbegin - iminus,
                                      self.selectionend - iminus)
            self.cutaction = None
            self.clipboard = None
            self.valueChanged.emit()
        self.menuselection = None
        self.updateGL()

    def loadtexture(self):
        self.edittexture(self.menuselection)

    def edittexture(self, i, initialfile=None):
        fname, ffilter = QFileDialog.getOpenFileName(
            self, "Texture Selection",
            self.lastTextureDir if initialfile is None else initialfile,
            "All files (*.*)")
        if len(fname) == 0: return
        self.lastTextureDir = os.path.dirname(str(fname))
        format = QImageReader.imageFormat(fname)
        if len(format) == 0:
            QMessageBox.warning(self, "Format error!",
                                "Format not supported !")
            self.edittexture(i, initialfile)
        else:
            self.setMaterial(i, ImageTexture(str(fname)))
            self.valueChanged.emit()

    def removematerial(self):
        if not self.menuselection is None and self.nbMaterial(
        ) > self.menuselection:
            if not self.selectionend is None:
                for i in self.getSelection():
                    self.delMaterial(self.selectionbegin)
                self.selectionbegin = None
                self.selectionend = None
            else:
                self.delMaterial(self.menuselection)
            self.valueChanged.emit()

    def interpolatematerial(self):
        if not self.selectionend is None:
            beg = self.selectionbegin
            end = self.selectionend
            self.showMessage(
                "Interpolate colors from " + str(beg) + " to " + str(end),
                2000)
            deltaselection = end - beg
            ratio = 1 / float(deltaselection)
            fmat = self.getMaterial(beg)
            lmat = self.getMaterial(end)
            iratio = 0
            for i in range(beg + 1, end):
                iratio += ratio
                self.setMaterial(i, fmat.interpolate(lmat, iratio))
            self.selectionbegin, self.selectionend = None, None
            self.valueChanged.emit()

    def dragEnterEvent(self, event):
        event.acceptProposedAction()

    def dropEvent(self, event):
        if event.mimeData().hasUrls():
            self.fileDropEvent(
                self.selectedColor(event.pos().x(),
                                   event.pos().y()),
                str(event.mimeData().urls()[0].toLocalFile()))

    def fileDropEvent(self, pos, fname):
        from openalea.lpy.cpfgcompat.data_import import import_colormap, import_materialmap
        base, ext = os.path.splitext(fname)
        if ext == '.map' or ext == '.mat':
            if ext == '.map':
                newcolors = import_colormap(fname)
            else:
                newcolors = import_materialmap(fname)
            if len(newcolors) > 0:
                self.setMaterials(newcolors)
            else:
                QMessageBox.warning(
                    self, 'Data Error',
                    'Read no data from file "' + os.path.basename(fname) + '"')
        else:
            format = QImageReader.imageFormat(fname)
            if len(format) != 0:
                self.setMaterial(pos, ImageTexture(str(fname)))
                self.valueChanged.emit()
Пример #10
0
    class ItemSlider(QWidget):

        valueChanged = pyqtSignal('PyQt_PyObject')

        def __init__(self, orientation, parent, item):
            QWidget.__init__(self, parent)
            self.setFocusPolicy(Qt.StrongFocus)
            self.setMinimumHeight(20)
            horizontalLayout = QHBoxLayout(self)
            horizontalLayout.setContentsMargins(0, 0, 0, 0)
            self.label = QLabel(self)
            horizontalLayout.addWidget(self.label)
            self.slider = QSlider(orientation, self)
            horizontalLayout.addWidget(self.slider)
            self.item = item
            scalar = item.scalar
            self.isfloat = scalar.isFloat()
            if item.scalar.isFloat():
                self.spinBox = QDoubleSpinBox(self)
                self.spinBox.setSingleStep(0.1**scalar.decimals)
            else:
                self.spinBox = QSpinBox(self)
            self.spinBox.setMinimumHeight(20)
            horizontalLayout.addWidget(self.spinBox)
            self.spinBox.hide()
            #self.slider.hide()
            self.chgButton = QPushButton('O', self)
            self.chgButton.setMaximumWidth(15)
            self.chgButton.setMinimumWidth(15)
            horizontalLayout.addWidget(self.chgButton)
            self.setRange(scalar.minvalue, scalar.maxvalue)
            self.label.setMinimumWidth(self.labelwidth)
            self.setValue(scalar.value)
            self.locked = False

            if self.isfloat:
                self.slider.valueChanged.connect(
                    self.updateInt2FloatItem
                )  # QObject.connect(self.slider,SIGNAL('valueChanged(int)'),self.updateInt2FloatItem)
                self.spinBox.valueChanged.connect(
                    self.updateItem
                )  # QObject.connect(self.spinBox,SIGNAL('valueChanged(double)'),self.updateItem)
            else:
                self.slider.valueChanged.connect(
                    self.updateItem
                )  # QObject.connect(self.slider,SIGNAL('valueChanged(int)'),self.updateItem)
                self.spinBox.valueChanged.connect(
                    self.updateItem
                )  # QObject.connect(self.spinBox,SIGNAL('valueChanged(int)'),self.updateItem)
            self.chgButton.pressed.connect(
                self.changeEditor
            )  # QObject.connect(self.chgButton,SIGNAL('pressed()'),self.changeEditor)

        def updateInt2FloatItem(self, value):
            a = 10.**self.item.scalar.decimals
            self.updateItem(value / a)

        def updateItem(self, value):
            if self.item.scalar.value != value and not self.locked:
                self.locked = True
                self.item.scalar.value = value
                self.setValue(value)
                self.item.setText(str(value))
                self.label.setMinimumWidth(self.labelwidth)
                self.valueChanged.emit(
                    self.item.scalar
                )  # self.emit(SIGNAL('valueChanged(PyQt_PyObject)'),self.item.scalar) # AUTO SIGNAL TRANSLATION
                self.locked = False

        def setRange(self, minv, maxv):
            if self.isfloat:
                a = 10**self.item.scalar.decimals
                self.labelwidth = self.fontMetrics().width(' ' +
                                                           str(int(a * maxv)) +
                                                           '. ')
                self.slider.setRange(int(minv * a), int(maxv * a))
            else:
                self.slider.setRange(minv, maxv)
                self.labelwidth = self.fontMetrics().width(' ' + str(maxv) +
                                                           ' ')
            self.spinBox.setRange(minv, maxv)
            self.label.setText(' ' * (2 + len(str(maxv))))

        def setValue(self, value):
            if self.isfloat:
                a = 10**self.item.scalar.decimals
                nv = int(value * a)
                if self.slider.value() != nv:
                    self.slider.setValue(value * a)
            else:
                if self.slider.value() != value:
                    self.slider.setValue(value)
            if self.spinBox.value() != value:
                self.spinBox.setValue(value)

        def changeEditor(self):
            if self.spinBox.isHidden():
                self.slider.hide()
                self.label.hide()
                self.spinBox.show()
                self.spinBox.move(0, 0)
            else:
                self.slider.show()
                self.label.show()
                self.spinBox.hide()
Пример #11
0
class ScalarEditor(QTreeView):
    valueChanged = pyqtSignal()
    itemValueChanged = pyqtSignal('PyQt_PyObject')

    def __init__(self, parent):
        QTreeView.__init__(self, parent)
        self.scalars = []
        self.scalarmap = {}
        self.initTable()
        self.scalarDelegate = ScalarEditorDelegate(self)
        self.setItemDelegateForColumn(1, self.scalarDelegate)
        self.createContextMenu()
        self.metaIntEdit = ScalarDialog(self)
        self.metaFloatEdit = FloatScalarDialog(self)
        self.setItemsExpandable(False)
        self.setIndentation(0)

    def initTable(self):
        self.scalarModel = MyItemModel(0, 1, self.scalarmap)
        self.scalarModel.itemChanged.connect(
            self.internalItemChanged
        )  # QObject.connect(self.scalarModel,SIGNAL('itemChanged(QStandardItem*)'),self.internalItemChanged)
        self.scalarModel.moveRequest.connect(
            self.moveItem
        )  # QObject.connect(self.scalarModel,SIGNAL('moveRequest(int,int)'),self.moveItem)
        self.scalarModel.setHorizontalHeaderLabels(["Parameter", "Value"])
        self.setModel(self.scalarModel)

    def contextMenuEvent(self, event):
        items = self.selection()
        self.deleteAction.setEnabled(len(items) > 0)
        self.editAction.setEnabled(
            len(items) == 1 and not (self.scalars[items[0]].isCategory()
                                     or self.scalars[items[0]].isBool()))
        self.menu.exec_(event.globalPos())

    def createContextMenu(self):
        self.menu = QMenu("Scalar Edit", self)
        self.menu.addAction("New Integer", self.newScalar)
        self.menu.addAction("New Float", self.newFloatScalar)
        self.menu.addAction("New Boolean", self.newBoolScalar)
        self.menu.addSeparator()
        self.menu.addAction("New Category", self.newCategoryScalar)
        self.menu.addSeparator()
        self.deleteAction = self.menu.addAction("Delete", self.deleteScalars)
        self.editAction = self.menu.addAction("Edit", self.editMetaScalar)

    def selection(self):
        items = list(set([i.row() for i in self.selectedIndexes()]))
        items.sort(key=lambda x: -x)
        return items

    def deleteScalars(self):
        for i in self.selection():
            self.scalarModel.removeRow(i)
            del self.scalars[i]
        self.valueChanged.emit()

    def editMetaScalar(self):
        item = self.selection()[0]
        v = self.scalars[item]
        sc = self.visualEditMetaScalar(v)
        if sc and v != sc:
            v.importValue(sc)
            v.si_name.setText(v.name)
            v.si_value.setText(str(v.value))
            self.itemValueChanged.emit(
                v
            )  # self.emit(SIGNAL('itemValueChanged(PyQt_PyObject)'),v) # AUTO SIGNAL TRANSLATION
            self.valueChanged.emit()

    def visualEditMetaScalar(self, scalar):
        metaEdit = self.metaIntEdit
        if scalar.isFloat():
            metaEdit = self.metaFloatEdit
        metaEdit.setScalar(scalar)
        res = metaEdit.exec_()
        if res: return metaEdit.getScalar()

    def getItems(self, scalar):
        si_name = QStandardItem(scalar.name)
        si_name.setEditable(True)
        #si_name.setData(scalar)
        si_name.scalar = scalar
        si_name.nameEditor = True
        if scalar.isCategory():
            b = QBrush(QColor(255, 255, 255))
            si_name.setForeground(b)
            b = QBrush(QColor(0, 0, 0))
            si_name.setBackground(b)
            return [si_name]
            si_value = QStandardItem()
            si_value.setEditable(False)
            si_value.setBackground(b)
        elif scalar.isBool():
            si_value = QStandardItem()
            si_value.setCheckable(True)
            si_value.setCheckState(
                Qt.Checked if scalar.value else Qt.Unchecked)
            si_value.stdEditor = True
        else:
            si_value = QStandardItem(str(scalar.value))
        si_value.scalar = scalar
        scalar.si_name = si_name
        scalar.si_value = si_value
        self.scalarmap[scalar.name] = (scalar, si_name, si_value)
        return [si_name, si_value]

    def newScalar(self):
        s = self.visualEditMetaScalar(IntegerScalar('default_scalar'))
        if s:
            self.scalars.append(s)
            self.scalarModel.appendRow(self.getItems(s))
            self.internalValueChanged(s)

    def newFloatScalar(self):
        s = self.visualEditMetaScalar(FloatScalar('default_scalar'))
        if s:
            self.scalars.append(s)
            self.scalarModel.appendRow(self.getItems(s))
            self.internalValueChanged(s)

    def newBoolScalar(self):
        s = BoolScalar('default_bool', True)
        self.scalars.append(s)
        self.scalarModel.appendRow(self.getItems(s))
        self.internalValueChanged(s)

    def newCategoryScalar(self):
        s = CategoryScalar('new category')
        self.scalars.append(s)
        ri = self.scalarModel.indexFromItem(
            self.scalarModel.invisibleRootItem())
        self.scalarModel.appendRow(self.getItems(s))
        self.setFirstColumnSpanned(len(self.scalars) - 1, ri, True)
        self.internalValueChanged(s)

    def setScalars(self, values):
        self.scalars = values
        self.replotScalars()

    def getScalars(self):
        return self.scalars

    def replotScalars(self):
        self.initTable()
        ri = self.scalarModel.indexFromItem(
            self.scalarModel.invisibleRootItem())
        for i, sc in enumerate(self.scalars):
            self.scalarModel.appendRow(self.getItems(sc))
            if sc.isCategory():
                self.setFirstColumnSpanned(i, ri, True)

    def internalValueChanged(self, scalar):
        self.itemValueChanged.emit(
            scalar
        )  # self.emit(SIGNAL('itemValueChanged(PyQt_PyObject)'),scalar) # AUTO SIGNAL TRANSLATION
        self.valueChanged.emit()

    def internalItemChanged(self, item):
        if hasattr(item, 'nameEditor'):
            item.scalar.name = str(item.text())
            self.valueChanged.emit()
        elif hasattr(item, 'stdEditor'):
            item.scalar.value = item.checkState() == Qt.Checked
            self.valueChanged.emit()

    def moveItem(self, r0, r1):
        item = self.scalars.pop(r0)
        if r1 == -1:
            self.scalars.append(item)
        else:
            self.scalars.insert(r1, item)
        self.replotScalars()
        self.valueChanged.emit()
Пример #12
0
class NurbsObjectEditor(QtWidgets.QWidget):
    valueChanged = pyqtSignal()
    manipulated = pyqtSignal()

    def __init__(self, parent, dimension=2):
        QtWidgets.QWidget.__init__(self, parent)
        self.gridLayout = QtWidgets.QGridLayout(self)
        self.gridLayout.setContentsMargins(2, 0, 11, 2)

        self.view = DimensionViewClass[dimension](self)
        self.view.valueChanged.connect(self.propagate_valuechanged)
        self.view.manipulated.connect(self.propagate_manipulated)

        self.stridenames = [
            n for n in ['u', 'v', 'w']
            if hasattr(self.view, 'set' + n.upper() + 'Stride')
        ]
        dim = len(self.stridenames)
        maxrow = 1 + 2 * dim

        self.gridLayout.addWidget(self.view, 0, 0, 2, maxrow - 1)
        self.view.selectionChanged.connect(self.selectionEvent)

        self.weightSpinBox = QtWidgets.QDoubleSpinBox(self)
        self.weightSpinBox.setMinimum(-100)
        self.weightSpinBox.setMaximum(100)
        self.weightSpinBox.valueChanged.connect(self.setWeigthToSelection)
        self.weightSpinBox.setEnabled(False)
        self.gridLayout.addWidget(self.weightSpinBox, 0, maxrow, 1, 1)

        self.weigthSlider = QtWidgets.QSlider(self)
        self.weigthSlider.setMinimum(-200)
        self.weigthSlider.setMaximum(200)
        self.weigthSlider.setOrientation(QtCore.Qt.Vertical)
        self.weigthSlider.setValue(0)
        self.weigthSlider.sliderPressed.connect(self.weigthSliderPressed)
        self.weigthSlider.sliderReleased.connect(self.weigthSliderReleased)
        self.weigthSlider.sliderMoved.connect(self.setWeigthRatioToSelection)
        self.weigthSlider.setEnabled(False)
        self.gridLayout.addWidget(self.weigthSlider, 1, maxrow, 1, 1)

        label = QtWidgets.QLabel(self)
        label.setText("Discretization")
        self.gridLayout.addWidget(label, 2, 0, 1, 1)
        self.sliders = []
        for i, stridename in enumerate(self.stridenames):
            strideSlider = QtWidgets.QSlider(self)
            strideSlider.setMinimum(2)
            strideSlider.setMaximum(50)
            strideSlider.setOrientation(QtCore.Qt.Horizontal)
            strideSlider.setValue(
                getattr(self.view.nurbsObject, stridename + 'stride'))
            strideSlider.valueChanged.connect(
                getattr(self.view, 'set' + stridename.upper() + 'Stride'))
            self.sliders.append(strideSlider)
            self.gridLayout.addWidget(strideSlider, 2, 1 + 2 * i, 1, 1)
            spacerItem = QtWidgets.QSpacerItem(20, 20,
                                               QtWidgets.QSizePolicy.Minimum,
                                               QtWidgets.QSizePolicy.Minimum)
            self.gridLayout.addItem(spacerItem, 2, 1 + 2 * i + 1, 1, 1)

    def selectionEvent(self):
        active = (len(self.view.getSelection()) != 0)
        self.weightSpinBox.setEnabled(active)
        self.weigthSlider.setEnabled(active)
        if active:
            self.weightSpinBox.setValue(
                sum([
                    p.position_setter.weight
                    for p in self.view.selectionManipulator.selection
                ]) / len(self.view.selectionManipulator.selection))

    def weigthSliderPressed(self):
        if self.view.selectionManipulator:
            self.cweight = sum([
                p.position_setter.weight
                for p in self.view.selectionManipulator.selection
            ]) / len(self.view.selectionManipulator.selection)
        else:
            self.cweight = 1

    def weigthSliderReleased(self):
        self.weigthSlider.setValue(0)

    def setWeigthRatioToSelection(self, value):
        ratio = 1 + (value / 100.)
        self.weightSpinBox.setValue(self.cweight * ratio)
        self.view.setWeigthToSelection(self.cweight * ratio)

    def setWeigthToSelection(self, value):
        self.view.setWeigthToSelection(value)

    def setNurbsObject(self, nurbsObject):
        self.view.setNurbsObject(nurbsObject)
        for slider, stridename in zip(self.sliders, self.stridenames):
            slider.setValue(
                getattr(self.view.nurbsObject, stridename + 'stride'))

    def getNurbsObject(self):
        return self.view.getNurbsObject()

    def propagate_valuechanged(self):
        self.valueChanged.emit()

    def propagate_manipulated(self):
        self.manipulated.emit()

    def rescaleObject(self):
        """ return a default nurbs patch """
        Dialog = QtWidgets.QDialog(self)
        gridLayout = QtWidgets.QGridLayout(Dialog)
        Dialog.values = []
        for i, axis in enumerate(['X', 'Y', 'Z']):
            sectionLabel = QtWidgets.QLabel(Dialog)
            sectionLabel.setText(axis)
            gridLayout.addWidget(sectionLabel, i, 0)
            spacer = QtWidgets.QSpacerItem(1, 10)
            gridLayout.addItem(spacer, i, 1)
            valuebox = QtWidgets.QDoubleSpinBox(Dialog)
            valuebox.setDecimals(3)
            valuebox.setMinimum(0.001)
            valuebox.setMaximum(9999999999)
            valuebox.setValue(1)
            Dialog.values.append(valuebox)
            gridLayout.addWidget(valuebox, i, 2)
        buttonBox = QtWidgets.QDialogButtonBox(Dialog)
        buttonBox.setOrientation(Qt.Horizontal)
        buttonBox.setStandardButtons(QtWidgets.QDialogButtonBox.Cancel
                                     | QtWidgets.QDialogButtonBox.Ok)
        buttonBox.accepted.connect(Dialog.accept)
        buttonBox.rejected.connect(Dialog.reject)
        gridLayout.addWidget(buttonBox, 3, 0, 1, 2)
        if Dialog.exec_():
            scale = Vector3([v.value() for v in Dialog.values])
            self.view.applyScaling(scale)

    def createCurstomDefaultObject(self):
        """ return a default nurbs patch """
        import inspect
        Dialog = QtWidgets.QDialog(self)
        gridLayout = QtWidgets.QGridLayout(Dialog)
        Dialog.values = []
        pname = inspect.getfullargspec(self.view.newDefaultNurbsObject).args
        for i, axis in enumerate(pname):
            sectionLabel = QtWidgets.QLabel(Dialog)
            sectionLabel.setText(axis)
            gridLayout.addWidget(sectionLabel, i, 0)
            spacer = QtWidgets.QSpacerItem(1, 10)
            gridLayout.addItem(spacer, i, 1)
            valuebox = QtWidgets.QSpinBox(Dialog)
            valuebox.setMinimum(2)
            valuebox.setMaximum(1000)
            valuebox.setValue(4)
            Dialog.values.append(valuebox)
            gridLayout.addWidget(valuebox, i, 2)
        buttonBox = QtWidgets.QDialogButtonBox(Dialog)
        buttonBox.setOrientation(Qt.Horizontal)
        buttonBox.setStandardButtons(QtWidgets.QDialogButtonBox.Cancel
                                     | QtWidgets.QDialogButtonBox.Ok)
        buttonBox.accepted.connect(Dialog.accept)
        buttonBox.rejected.connect(Dialog.reject)
        gridLayout.addWidget(buttonBox, 3, 0, 1, 2)
        if Dialog.exec_():
            nbpoints = [v.value() for v in Dialog.values]
            self.setNurbsObject(self.view.newDefaultNurbsObject(*nbpoints))

    def createDefaultObject(self, *dims):
        self.setNurbsObject(self.view.newDefaultNurbsObject(*dims))
Пример #13
0
class AbstractNurbsObjectEditorView(QGLViewer):
    """ The class NurbsPatchEditor is the viewer of the scene, it contains all the informations about the NurbsPatch, that's why a getter and a setter have been created, the NubrsPatch is defined by a 2 dimensional Array of 3d Vectors"""

    Edit, Rotate = list(range(2))
    valueChanged = pyqtSignal()
    manipulated = pyqtSignal()
    selectionChanged = pyqtSignal()

    def __init__(self, parent):
        """ Constructor 
            :param parent: the parent widget
        """
        QGLViewer.__init__(self, parent)
        self.setStateFileName('.nurbsobjecteditor.xml')

        self.mode = AbstractNurbsObjectEditorView.Edit

        # the nurbs patch
        self.nurbsObject = None

        # matrix of ctrl point manipulator
        self.ctrlPointManipulators = {}

        # shape and material to display the patch
        self.nurbsShape = Shape()
        self.defaultMaterial = Material((30, 200, 30), 1, transparency=0.5)
        self.nurbsShape.appearance = self.defaultMaterial

        # plantgl basic object to display nurbs
        self.discretizer = Discretizer()
        self.glrenderer = GLRenderer(self.discretizer)
        self.glrenderer.renderingMode = GLRenderer.Dynamic
        self.ctrlrenderer = GLCtrlPointRenderer(self.discretizer)
        self.bboxcomputer = BBoxComputer(self.discretizer)

        self.ctrlPointDisplay = None

        #   Current selection
        self.selectionManipulator = None
        self.focus = None

        # rectangular selection
        self.selectionRect = False  # for rectangular selection (with control)
        self.__rectangle = QRect()
        self.__rectangleInit = None

        self._drawNurbsObject = True
        self._drawGrid = True

        #creation of a default NurbsPatch
        self.setNurbsObject(self.newDefaultNurbsObject())

    def getSelection(self):
        if self.selectionManipulator:
            return self.selectionManipulator.selection
        else:
            return []

    def clear(self):
        """ clear current edition """
        self.ctrlPointManipulators = {}
        self.clearSelectionManipulator()
        self.resetDisplay()

    def getCtrlPoints(self):
        return self.nurbsObject.ctrlPointMatrix

    def getCtrlPointsDim(self):
        return self.getCtrlPoints().sizes()

    def getCtrlPointManipulators(self):
        return self.ctrlPointManipulators

    def getBounds(self):
        """ Get the Bounding Box of the scene:
            return the minpos and maxpos that can be used by setSceneBoundingBox function"""
        self.nurbsObject.apply(self.bboxcomputer)
        res = self.bboxcomputer.result
        return Vec(*res.lowerLeftCorner), Vec(*res.upperRightCorner)

    def __propagate_valuechanged__(self, pid=None):
        """ emit a SIGNAL every time a value changed """
        self.setSceneBoundingBox(*self.getBounds())
        self.valueChanged.emit()
        self.resetDisplay()

    def resetDisplay(self, pid=None):
        self.ctrlPointDisplay = None

    def init(self):
        """ init function """
        self.setSceneBoundingBox(*self.getBounds())
        self.restoreStateFromFile()

        # init mouse interaction
        self.mode = AbstractNurbsObjectEditorView.Edit
        self.setInteractionMode(False)

        self.setMouseBindingDescription(Qt.ShiftModifier, Qt.LeftButton,
                                        "Rectangular selection")
        self.setMouseBindingDescription(Qt.NoModifier, Qt.LeftButton,
                                        "Camera/Control Points manipulation")
        self.setMouseBindingDescription(
            Qt.NoModifier, Qt.LeftButton,
            "When double clicking on a line, create a new line", True)

        self.setKeyDescription(Qt.Key_K, "Toggles control point wire display")
        self.setKeyDescription(Qt.Key_G, "Toggles patch display")

    def setFocus(self, point):
        """ Set focus to given control point """
        if self.focus:
            self.focus.hasFocus = False
        self.focus = point
        if self.focus:
            point.hasFocus = True

    def clearSelectionManipulator(self):
        """ clear the selection manipulator """
        if not self.selectionManipulator is None:
            self.selectionManipulator.clear()
            self.selectionManipulator.valueChanged.disconnect(
                self.__propagate_valuechanged__
            )  # QObject.disconnect(self.selectionManipulator,SIGNAL("valueChanged()"),self.__propagate_valuechanged__)
            self.selectionManipulator = None
            self.resetDisplay()

    def createSelectionManipulator(self):
        """ ensure that the selection manipulator is existing and valid """
        if not self.selectionManipulator:
            self.selectionManipulator = SelectionManipulator()
            self.selectionManipulator.valueChanged.connect(
                self.__propagate_valuechanged__
            )  # QObject.connect(self.selectionManipulator,SIGNAL("valueChanged()"),self.__propagate_valuechanged__)
            self.resetDisplay()

    def mousePressEvent(self, event):
        """ Check for eventual operations the user asks: 
            shift start rectangular selection
            else check for which point is selected
        """
        #Rectangular selection
        if event.modifiers() == Qt.ShiftModifier:
            if event.button() == Qt.LeftButton:
                self.__rectangle = QRect(event.pos(), event.pos())
                self.__rectangleInit = event.pos()
                self.selectionRect = True
            elif event.button() == Qt.RightButton:
                self.clearSelectionManipulator()
                self.selectionChanged.emit()
            self.updateGL()
        else:
            pointSelection = False
            if not self.selectionManipulator is None:
                pointSelection = self.selectionManipulator.checkIfAxisGrabsMouse(
                    event, self)
            if not pointSelection:
                for cCtrlPoint in self.getCtrlPointManipulators().values():
                    cCtrlPoint.checkIfGrabsMouse(event.x(), event.y(),
                                                 self.camera())
                    if cCtrlPoint.grabsMouse():
                        pointSelection = True
                        self.setFocus(cCtrlPoint)
                        if self.selectionManipulator and cCtrlPoint.selected:
                            self.setManipulatedFrame(self.selectionManipulator)
                        else:
                            self.setManipulatedFrame(cCtrlPoint)
                        break
            self.setInteractionMode(pointSelection)
            self.updateGL()

        QGLViewer.mousePressEvent(self, event)

    def setInteractionMode(self, frame=True):
        if frame:
            if self.mode != AbstractNurbsObjectEditorView.Edit:
                self.mode = AbstractNurbsObjectEditorView.Edit
                self.setMouseBinding(Qt.NoModifier, Qt.LeftButton,
                                     QGLViewer.FRAME, QGLViewer.TRANSLATE)
                self.setMouseBinding(Qt.NoModifier, Qt.RightButton,
                                     QGLViewer.FRAME,
                                     QGLViewer.NO_MOUSE_ACTION)

                self.setMouseBinding(Qt.ControlModifier, Qt.LeftButton,
                                     QGLViewer.CAMERA, QGLViewer.ROTATE)
                self.setMouseBinding(Qt.ControlModifier, Qt.RightButton,
                                     QGLViewer.CAMERA, QGLViewer.TRANSLATE)
                self.setMouseBinding(Qt.ControlModifier, Qt.MiddleButton,
                                     QGLViewer.CAMERA, QGLViewer.ZOOM)
        else:
            if self.mode != AbstractNurbsObjectEditorView.Rotate:
                self.mode = AbstractNurbsObjectEditorView.Rotate
                self.setMouseBinding(Qt.ControlModifier, Qt.LeftButton,
                                     QGLViewer.FRAME, QGLViewer.TRANSLATE)
                self.setMouseBinding(Qt.ControlModifier, Qt.RightButton,
                                     QGLViewer.FRAME,
                                     QGLViewer.NO_MOUSE_ACTION)
                self.setMouseBinding(Qt.NoModifier, Qt.LeftButton,
                                     QGLViewer.CAMERA, QGLViewer.ROTATE)
                self.setMouseBinding(Qt.NoModifier, Qt.RightButton,
                                     QGLViewer.CAMERA, QGLViewer.TRANSLATE)
                self.setMouseBinding(Qt.NoModifier, Qt.MiddleButton,
                                     QGLViewer.CAMERA, QGLViewer.ZOOM)

    def mouseDoubleClickEvent(self, event):
        """ mouseDoubleClickEvent: add a control point to the selection double clicking on it, empty the selection double clicking elsewhere """
        if event.modifiers() & Qt.ShiftModifier:
            selection = False
            #Doubleclick on a control point add/delete it from the selection array#
            for cCtrlPoint in self.getCtrlPointManipulators().values():
                cCtrlPoint.checkIfGrabsMouse(event.x(), event.y(),
                                             self.camera())
                if cCtrlPoint.grabsMouse():
                    cCtrlPoint.selected = not cCtrlPoint.selected
                    selection = True
                    self.createSelectionManipulator()
                    self.selectionManipulator.toogleSelection(cCtrlPoint)
            #Double click with shift anywhere but on a control point -> empty selection #
            if not selection:
                self.clearSelectionManipulator()
            self.selectionChanged.emit()
        else:
            ## Adding a column on the NurbsPatch#
            """ when double click on a line, the line is doubled """
            self.select(
                event.pos()
            )  # select call drwWithName that render line of control points with ids
            selection = self.selectedName()
            if selection >= 0:
                selectedPoints = self.addElement(selection)
                if selectedPoints:
                    self.createSelectionManipulator()
                    for index in selectedPoints:
                        self.selectionManipulator.add(
                            self.ctrlPointManipulators[index])
                else:
                    self.clearSelectionManipulator()
            else:
                self.clearSelectionManipulator()
            self.selectionChanged.emit()

    def mouseMoveEvent(self, event):
        """ mouseMoveEvent """

        #rectangular selection
        if self.selectionRect:
            self.__rectangle = QRect(self.__rectangleInit,
                                     event.pos()).normalized()
            self.updateGL()

        # by default camera or manipulated frame is manipulated
        QGLViewer.mouseMoveEvent(self, event)

    def mouseReleaseEvent(self, event):
        """On mouse release, we release every grabbed objects"""
        QGLViewer.mouseReleaseEvent(self, event)
        # clear manipulated object
        self.setManipulatedFrame(None)
        self.setFocus(None)
        if self.selectionManipulator:
            self.selectionManipulator.clearFocus()

        # end of rectangular selection :
        if self.selectionRect:
            self.selectionRect = False
            # Possibly swap left/right and top/bottom to make rectangle_ valid.
            self.__rectangle = QRect(self.__rectangleInit,
                                     event.pos()).normalized()
            # make rectangle with a minimal size
            if self.__rectangle.width() < 10 or self.__rectangle.height() < 10:
                self.__rectangle = QRect(event.pos().x() - 5,
                                         event.pos().y() - 5, 10, 10)
            self.selectionFromRect(self.__rectangle)
        self.setInteractionMode(False)
        self.updateGL()

    def selectionFromRect(self, rect):
        """ check if control point projections are in the given rectangle. If yes, put point in selection """
        selectionOccur = False
        for cCtrlPoint in self.getCtrlPointManipulators().values():
            point = self.camera().projectedCoordinatesOf(cCtrlPoint.position())
            if (self.__rectangle.contains(int(point.x), int(point.y))):
                selectionOccur = True
                if not self.selectionManipulator:
                    self.selectionManipulator = SelectionManipulator()
                    self.selectionManipulator.valueChanged.connect(
                        self.__propagate_valuechanged__
                    )  # QObject.connect(self.selectionManipulator,SIGNAL("valueChanged()"),self.__propagate_valuechanged__)
                self.selectionManipulator.toogleSelection(cCtrlPoint)
        if self.selectionManipulator and self.selectionManipulator.empty():
            self.clearSelectionManipulator()
        self.ctrlPointDisplay = None
        if selectionOccur:
            self.selectionChanged.emit()

    def keyPressEvent(self, e):
        modifiers = e.modifiers()
        # A simple switch on e->key() is not sufficient if we want to take state key into account.
        # With a switch, it would have been impossible to separate 'F' from 'CTRL+F'.
        # That's why we use imbricated if...else and a "handled" boolean.
        if ((e.key() == Qt.Key_K) and (modifiers == Qt.NoModifier)):
            self._drawGrid = not self._drawGrid
            self.updateGL()
        elif (e.key() == Qt.Key_G) and (modifiers == Qt.NoModifier):
            self._drawNurbsObject = not self._drawNurbsObject
            self.updateGL()
        else:
            QGLViewer.keyPressEvent(self, e)

    def addElement(self, selection):
        nbcolumn = self.getCtrlPoints().getColumnNb()
        minp, maxp = self.getBounds()
        diff = norm(maxp - minp) / 20
        if selection >= nbcolumn:
            row = selection - nbcolumn
            newpatch = addRowToPatch(self.nurbsObject, row, Vector4(diff, 0))
            selectedPoints = [(row, i) for i in range(nbcolumn)]
        else:
            newpatch = addColumnToPatch(self.nurbsObject, selection,
                                        Vector4(diff, 0))
            selectedPoints = [(i, selection)
                              for i in range(self.getCtrlPoints().getRowNb())]
        self.setNurbsObject(newpatch)
        return selectedPoints

    def draw(self):
        """ 
        Draw function :
        Control points of the nurbsPatch are represented by spheres.
        If some of them are selected, a medium axis will be drawn by lines and little spheres, 
        allowing us to grab one of them to perform a constrained move
        """

        ogl.glDisable(ogl.GL_LIGHTING)
        ogl.glPolygonMode(ogl.GL_FRONT_AND_BACK, ogl.GL_LINE)
        # draw the wire of the patch
        ogl.glLineWidth(1)

        if self._drawNurbsObject:
            self.drawNurbsObjectWire()
        if self._drawGrid:
            self.drawCtrlWire()

        # patch and control point shpere rendering
        ogl.glPolygonMode(ogl.GL_FRONT_AND_BACK, ogl.GL_FILL)
        ogl.glEnable(ogl.GL_LIGHTING)
        ogl.glLightModeli(ogl.GL_LIGHT_MODEL_TWO_SIDE, ogl.GL_TRUE)
        ogl.glEnable(ogl.GL_BLEND)
        ogl.glBlendFunc(ogl.GL_SRC_ALPHA, ogl.GL_ONE_MINUS_SRC_ALPHA)

        if self._drawNurbsObject:
            self.drawNurbsObject()
        self.drawCtrlPoints()

        ogl.glDisable(ogl.GL_BLEND)

        if self.selectionRect:
            # Draw rectangular selection
            self.startScreenCoordinatesSystem()  # use screen coordinates
            r = self.__rectangle
            rect = Shape(
                Polyline2D([(r.x(), r.y()), (r.x() + r.width(), r.y()),
                            (r.x() + r.width(), r.y() + r.height()),
                            (r.x(), r.y() + r.height()), (r.x(), r.y())]),
                Material((200, 200, 200), 1))
            ogl.glEnable(ogl.GL_LINE_STIPPLE)
            ogl.glLineStipple(1, 0x0FFF)
            ogl.glLineWidth(2)
            rect.apply(self.glrenderer)
            ogl.glDisable(ogl.GL_LINE_STIPPLE)
            self.stopScreenCoordinatesSystem()
        ogl.glLineWidth(1)

    def drawCtrlPoints(self):
        if self.ctrlPointDisplay is None:
            scradius = self.sceneRadius()
            pointsize = scradius / 30.

            shapes = []
            #define the nurbsShape and the control points geometry
            for ctrlPoint in self.getCtrlPointManipulators().values():
                shapes.append(ctrlPoint.representation(pointsize))

            # draw the frame of multiple selection
            if self.selectionManipulator:
                shapes += self.selectionManipulator.representation(pointsize *
                                                                   1.2)
            self.ctrlPointDisplay = Scene(shapes)

        # control point shpere rendering
        self.ctrlPointDisplay.apply(self.glrenderer)

    def drawCtrlWire(self):

        # ctrl line rendering
        Material((255, 255, 255), 1).apply(self.glrenderer)
        self.nurbsObject.apply(self.ctrlrenderer)

    def drawNurbsObject(self):
        sc = Scene([self.nurbsShape])

        # patch rendering
        sc.apply(self.glrenderer)

    def drawNurbsObjectWire(self):
        shwire = Shape(self.nurbsObject, Material((120, 255, 0), 0.8))
        shwire.apply(self.glrenderer)

    def drawWithNames(self):
        """ draw control lines with names """
        pointmatrix = self.getCtrlPoints()
        lines = []
        m = Material()
        maxcolumn = pointmatrix.getColumnNb()
        for colid in range(maxcolumn):
            lines.append(
                Shape(
                    Polyline(Point4Array(
                        pointmatrix.getColumn(colid)).project(),
                             width=3), m, colid))
        for rowid in range(pointmatrix.getRowNb()):
            lines.append(
                Shape(
                    Polyline(Point4Array(pointmatrix.getRow(rowid)).project(),
                             width=3), m, maxcolumn + rowid))
        for sh in lines:
            ogl.glPushName(sh.id)
            sh.apply(self.glrenderer)
            ogl.glPopName()

    def getNurbsObject(self):
        """ return the edited nurbs patch """
        return self.nurbsObject

    def setNurbsObject(self, nurbsObject):
        """ set the nurbs patch to edit """
        self.clear()
        self.nurbsObject = nurbsObject
        self.setNurbsObjectView()
        self.setControlPointManipulators()

    def setControlPointManipulators(self):
        import itertools
        dims = list(map(float, self.getCtrlPointsDim()))

        def color(idx):
            try:
                r = 30 + int(220 * idx[0] / dims[0])
                g = 30 + int(220 * idx[1] / dims[1]) if len(dims) > 1 else 250
                b = 30 + int(220 * idx[2] / dims[2]) if len(dims) > 2 else 250
            except:
                r = 250
                g = 30 + int(220 * idx / dims[0])
                b = 250
            return (r, g, b)

        ctrlpoints = self.getCtrlPoints()
        self.ctrlPointManipulators = {}
        dims = self.getCtrlPointsDim()
        if len(dims) == 1:
            idxiter = list(range(dims[0]))
        else:
            idxiter = itertools.product(*[list(range(d)) for d in dims])

        for pid, index in enumerate(idxiter):
            ctrlPoint = CtrlPoint(ctrlpoints[index],
                                  Pos4Setter(self.getCtrlPoints(), index,
                                             ctrlpoints[index].w),
                                  color=color(index),
                                  id=pid)
            ctrlPoint.setCallBack(self.__propagate_valuechanged__)
            self.ctrlPointManipulators[index] = ctrlPoint

        self.setSceneBoundingBox(*self.getBounds())

    def setNurbsObjectView(self):
        self.nurbsShape.geometry = self.nurbsObject

    def helpString(self):
        return helpstr

    def closeEvent(self, event):
        """close the windows properly"""
        helpwidget = self.helpWidget()
        if helpwidget and helpwidget.isVisible():
            helpwidget.hide()
        QGLViewer.closeEvent(self, event)

    def setUStride(self, value):
        self.nurbsObject.ustride = value
        self.valueChanged.emit()
        self.updateGL()

    def setWeigthToSelection(self, value):
        if self.selectionManipulator:
            try:
                if len(value) > 0:
                    for v, p in zip(value,
                                    self.selectionManipulator.selection):
                        p.position_setter.weight = v
                        p.__propagate_position_change__()
            except:
                if abs(value) < 1e-3:
                    return
                for p in self.selectionManipulator.selection:
                    p.position_setter.weight = value
                    p.__propagate_position_change__()
        self.updateGL()
Пример #14
0
class LpyTabBar(QTabBar):
    
    switchDocument = pyqtSignal(int,int)
    newDocumentRequest = pyqtSignal()

    def __init__(self,parent):
        QTabBar.__init__(self,parent)
        #self.setDrawBase(False)
        self.selection = None
        self.lpystudio = None
        self.initialtab = None
        self.inserted = set()
        
    def connectTo(self,lpystudio):
        self.lpystudio = lpystudio
        self.switchDocument.connect(lpystudio.switchDocuments)
        self.currentChanged.connect(lpystudio.changeDocument)
        self.newDocumentRequest.connect(lpystudio.newfile)
        
    def mousePressEvent(self,event):
        if event.button() == Qt.LeftButton:
            self.initialtab = self.tabAt(event.pos())
        QTabBar.mousePressEvent(self,event)
    
    def mouseReleaseEvent(self,event):
        if event.button() == Qt.LeftButton:
            tabselect = self.tabAt(event.pos())
            if tabselect != -1 and not self.initialtab is None:
                if tabselect != self.initialtab:
                    self.switchDocument.emit(tabselect,self.initialtab)
            self.initialtab = None
        QTabBar.mousePressEvent(self,event)

    # def mouseMoveEvent(self,event):
    #     tabselect = self.tabAt(event.pos())
    #     if tabselect != -1 and self.initialtab != None:
    #         if tabselect != originaltab:
    #             pass
    #             #self.emit(SIGNAL("switchDocument"),tabselect,originaltab)
    #     QTabBar.mouseMoveEvent(self,event)
    # def mouseDoubleClickEvent(self,event):
    #     tabselect = self.tabAt(event.pos())
    #     if tabselect != -1 :
    #         self.emit(SIGNAL("newDocumentRequest"))
    #     QTabBar.mouseDoubleClickEvent(self,event)

    def contextMenuEvent(self,event):
        self.selection = self.tabAt(event.pos())
        if self.selection != -1:
            menu = QMenu(self)
            action = menu.addAction('Close')
            action.triggered.connect(self.close)
            action = menu.addAction('Close all except this ')
            action.triggered.connect(self.closeAllExcept)
            menu.addSeparator()
            if self.lpystudio.simulations[self.selection].readonly:
                action = menu.addAction('Remove Readonly ')
                action.triggered.connect(self.removeReadOnly)
            else:
                action = menu.addAction('Set Readonly ')
                action.triggered.connect(self.setReadOnly)
            menu.addSeparator()
            action = menu.addAction('Copy filename ')
            action.triggered.connect(self.copyFilename)
            action = menu.addAction('Open folder')
            action.triggered.connect(self.openFolder)
            action = menu.addAction('Open terminal')
            action.triggered.connect(self.openTerminalAtFolder)
            fname = self.lpystudio.simulations[self.selection].fname
            if fname and svnmanip.hasSvnSupport() :
                if svnmanip.isSvnFile(fname):
                    menu.addSeparator()
                    status = svnmanip.svnFileTextStatus(fname)
                    if status != svnmanip.added:
                        action = menu.addAction('SVN Update')
                        action.triggered.connect(self.svnUpdate)
                    if status in  [svnmanip.added,svnmanip.modified]:
                        action = menu.addAction('SVN Commit')
                        action.triggered.connect(self.svnCommit)
                    if status != svnmanip.normal:
                        action = menu.addAction('SVN Revert')
                        action.triggered.connect(self.svnRevert)
                    if status != svnmanip.added:
                        menu.addSeparator()
                        action = menu.addAction('Is Up-to-date ?')
                        action.triggered.connect(self.svnIsUpToDate)
                elif svnmanip.isSvnFile(os.path.dirname(fname)):
                    menu.addSeparator()
                    action = menu.addAction('SVN Add')
                    action.triggered.connect(self.svnAdd)
            menu.exec_(event.globalPos())
    def openFolder(self):
        import os, sys
        fname = os.path.abspath(self.lpystudio.simulations[self.selection].fname)
        mdir = os.path.dirname(fname)
        if sys.platform == 'win32':
                import subprocess
                subprocess.call('explorer /select,"'+fname+'"')
        elif sys.platform == 'linux2':
                os.system('xdg-open "'+mdir+'"')
        else:
                os.system('open "'+mdir+'"')
    def openTerminalAtFolder(self):
        import os, sys
        fname = os.path.abspath(self.lpystudio.simulations[self.selection].fname)
        mdir = os.path.dirname(fname)
        if sys.platform == 'win32':
                import subprocess
                subprocess.call('CMD /K CD "'+mdir+'"')
        elif sys.platform == 'linux2':
                os.system('gnome-terminal --working-directory "'+mdir+'"')
        elif sys.platform == 'darwin':
                os.system('open -a"Terminal" "'+mdir+'"')

    def close(self):
        self.lpystudio.closeDocument(self.selection)

    def closeAllExcept(self):
        self.lpystudio.closeAllExcept(self.selection)

    def copyFilename(self):
        QApplication.clipboard().setText(self.lpystudio.simulations[self.selection].fname)

    def removeReadOnly(self):
        self.lpystudio.simulations[self.selection].removeReadOnly()

    def setReadOnly(self):
        self.lpystudio.simulations[self.selection].setReadOnly()
        
    def svnUpdate(self):
        self.lpystudio.simulations[self.selection].svnUpdate()
        
    def svnIsUpToDate(self):
        self.lpystudio.simulations[self.selection].svnIsUpToDate()
        
    def svnAdd(self):
        self.lpystudio.simulations[self.selection].svnAdd()
        
    def svnRevert(self):
        self.lpystudio.simulations[self.selection].svnRevert()
        
    def svnCommit(self):
        self.lpystudio.simulations[self.selection].svnCommit()
    
    def insertTab(self, index, val1, val2 = None):
        self.inserted.add(index)
        if val2 : QTabBar.insertTab(self, index, val1, val2)
        else : QTabBar.insertTab(self, index, val1)
Пример #15
0
class ComputationTaskManager(QObject):
    endTask = pyqtSignal('PyQt_PyObject')
    killedTask = pyqtSignal('PyQt_PyObject')

    def __init__(self):
        self.computationThread = None
        self.computationMutex = QMutex()
        self.with_thread = False

    def toggleUseThread(self):
        self.with_thread = not self.with_thread
        if not self.with_thread:
            self.computationMutex.unlock()

    def finalizeTask(self):
        ct = self.computationThread
        if not self.computationThread is None:
            try:
                self.computationThread.finalize()
            except ThreadTransferException as e:
                self.graberror((e.exc_type, e.exc_value, e.exc_traceback))
            except:
                self.graberror()
            self.releaseCR()
            self.computationThread = None
        else:
            self.releaseCR()
        self.endTask.emit(ct)

    def abortTask(self):
        ct = self.computationThread
        self.computationThread = None
        self.releaseCR()
        self.endTask.emit(ct)
        self.clear()

    def killTask(self):
        ct = self.computationThread
        ct.kill()
        self.computationThread = None
        self.releaseCR()
        self.killedTask.emit(ct)
        self.clear()

    def registerTask(self, task):
        if self.computationThread is None:
            if self.with_thread:
                task.finished.connect(self.finalizeTask)
                if QT_VERSION <= 4:
                    task.terminated.connect(self.abortTask)
                self.computationThread = task
                task.initialize()
                task.start()
            else:
                try:
                    self.computationThread = task
                    task.apply()
                except:
                    self.graberror()
                self.computationThread = None
                self.releaseCR()
                self.endTask.emit(task)

    def acquireCR(self):
        """ acquire computation ressources """
        if not self.computationMutex.tryLock():
            self.taskRunningEvent()
        else:
            self.acquireEvent()
        self.isRunning()

    def taskRunningEvent(self):
        raise Exception('A task is already running')
        pass

    def isRunning(self):
        if self.computationMutex.tryLock():
            self.computationMutex.unlock()
            return False
        else:
            return True

    def releaseCR(self):
        """ release computation ressources """
        self.computationMutex.tryLock()
        self.computationMutex.unlock()
        self.releaseEvent()

    def acquireEvent(self):
        pass

    def releaseEvent(self):
        pass

    def graberror(self, exc_info=None, displayDialog=True):
        """ grab error """
        if exc_info is None:
            exc_info = sys.exc_info()
        tb.print_exception(*exc_info)
        self.lastexception = exc_info[1]
        errmsg = self.getErrorMessage(exc_info)
        self.errorEvent(exc_info, errmsg, displayDialog)
        self.endErrorEvent()

        #if displayDialog:
        #    self.endErrorEvent(self.errorMessage(errmsg))
        #else:
        #    self.endErrorEvent(None)
    def getErrorMessage(self, exc_info):
        exception = exc_info[1]
        msg = str(exc_info[1])
        if exc_info[0] == SyntaxError and len(msg) == 0:
            msg = exc_info[1].msg
        return str(exc_info[0].__name__) + ':' + str(msg)

    def errorMessage(self, msg):
        return QMessageBox.warning(self, "Exception", msg, QMessageBox.Ok)

    def errorEvent(self, exc_info, errmsg, displayDialog):
        pass

    def endErrorEvent(self):
        pass
Пример #16
0
class Curve2DEditor(QGLViewer):
    BLACK_THEME = {
        'Curve': (255, 255, 255),
        'BackGround': (51, 51, 51),
        'Text': (255, 255, 255),
        'CtrlCurve': (122, 122, 0),
        'GridStrong': (102, 102, 102),
        'GridFade': (51, 51, 51),
        'Points': (250, 30, 30),
        'FirstPoint': (250, 30, 250),
        'SelectedPoint': (30, 250, 30),
        'DisabledBackGround': (150, 150, 150)
    }
    WHITE_THEME = {
        'Curve': (255, 0, 0),
        'BackGround': (255, 255, 255),
        'Text': (0, 0, 0),
        'CtrlCurve': (25, 0, 25),
        'GridStrong': (102, 102, 102),
        'GridFade': (153, 153, 153),
        'Points': (30, 250, 30),
        'FirstPoint': (250, 30, 250),
        'SelectedPoint': (30, 250, 30),
        'DisabledBackGround': (150, 150, 150)
    }
    valueChanged = pyqtSignal()

    def __init__(self, parent, constraints=Curve2DConstraint()):
        QGLViewer.__init__(self, parent)
        self.selection = -1
        self.setStateFileName('.curveeditor.xml')

        self.sphere = Sphere(radius=0.02)
        self.curveshape = Shape()
        self.setTheme()  #Color theme

        self.pointsConstraints = constraints
        self.accessorType = {
            NurbsCurve2D: Nurbs2DAccessor,
            BezierCurve2D: Bezier2DAccessor,
            Polyline2D: Polyline2DAccessor,
        }
        self.setCurve(self.newDefaultCurve())
        self.discretizer = Discretizer()
        self.renderer = GLRenderer(self.discretizer)
        self.renderer.renderingMode = GLRenderer.Dynamic
        self.ctrlrenderer = GLCtrlPointRenderer(self.discretizer)
        self.bgimage = False
        self.font = QFont()
        if not hasattr(self, 'updateGL'):
            self.updateGL = self.update

    def setTheme(self, theme=BLACK_THEME):
        self.curveMaterial = Material(theme['Curve'], 1)
        self.defaultColor = QColor(*theme['BackGround'])
        self.disabledBGColor = QColor(*theme['DisabledBackGround'])
        self.textColor = [v / 255. for v in theme['Text']] + [1.0]
        self.ctrlCurveColor = [v / 255. for v in theme['CtrlCurve']] + [0.0]
        self.gridColor = [v / 255. for v in theme['GridStrong']] + [0.0]
        self.gridColor2 = [v / 255. for v in theme['GridFade']] + [0.0]
        self.pointColor = Material(theme['Points'], 1)
        self.firstPointColor = Material(theme['FirstPoint'], 1)
        self.selectedPointColor = Material(theme['SelectedPoint'], 1)
        self.curveshape.appearance = self.curveMaterial

    def applyTheme(self, theme=BLACK_THEME):
        self.setTheme(theme)
        if self.isVisible():
            self.createControlPointsRep()
            self.updateGL()

    def newDefaultCurve(self):
        return self.pointsConstraints.defaultCurve()

    def init(self):
        self.updateSceneDimension()
        #self.setHandlerKeyboardModifiers(QGLViewer.CAMERA, Qt.AltModifier)
        #self.setHandlerKeyboardModifiers(QGLViewer.FRAME,  Qt.NoModifier)
        #self.setHandlerKeyboardModifiers(QGLViewer.CAMERA, Qt.ControlModifier)
        self.setMouseBinding(Qt.ControlModifier, Qt.LeftButton,
                             QGLViewer.FRAME, QGLViewer.TRANSLATE)
        self.setMouseBinding(Qt.ControlModifier, Qt.RightButton,
                             QGLViewer.FRAME, QGLViewer.NO_MOUSE_ACTION)
        self.setMouseBinding(Qt.NoModifier, Qt.LeftButton, QGLViewer.CAMERA,
                             QGLViewer.TRANSLATE)
        self.setMouseBinding(Qt.NoModifier, Qt.RightButton, QGLViewer.CAMERA,
                             QGLViewer.NO_MOUSE_ACTION)
        self.camera().setUpVector(Vec(0, 1, 0))
        self.camera().setType(Camera.ORTHOGRAPHIC)
        self.camConstraint = WorldConstraint()
        self.camConstraint.setRotationConstraintType(
            AxisPlaneConstraint.FORBIDDEN)
        self.camera().frame().setConstraint(self.camConstraint)
        self.showEntireScene()
        self.setSelectRegionWidth(5)
        self.setSelectRegionHeight(5)
        self.manipulator = ManipulatedFrame()
        self.setManipulatedFrame(self.manipulator)
        self.manipulator.manipulated.connect(
            self.updatePoints
        )  # QObject.connect(self.manipulator,SIGNAL('manipulated()'),self.updatePoints)
        #self.setBackgroundImage("test.png") #self.openImage() # QQQ

    def getCurve(self):
        """ Get the edited curve """
        return self.curveshape.geometry

    def updateSceneDimension(self):
        minp, maxp = self.curveAccessor.bounds()
        minp, maxp = Vector3(minp, 0), Vector3(maxp, 0)
        self.setSceneBoundingBox(Vec(*minp), Vec(*maxp))

    def setCurve(self, curve):
        """ Set the edited curve """
        self.curveshape.geometry = curve
        curve.width = 2
        self.curveAccessor = self.accessorType[type(curve)](curve)
        self.pointsConstraints.checkInitialCurve(self.curveAccessor)
        self.createControlPointsRep()
        self.updateSceneDimension()
        if self.isVisible(): self.showEntireScene()

    def openImage(self):
        name, selection = QFileDialog.getOpenFileName(
            self, "Select an image", ".", "Images (*.png *.xpm *.jpg)")

        # In case of Cancel
        if not name: return

        self.setBackgroundImage(name)

        return

    def closeImage(self):
        self.bgimage = False

    def setBackgroundImage(self, imagefilename):
        # code taken from the backgroundImage.cpp file that is part of the QGLViewer library

        self.u_max = 1.0
        self.v_max = 1.0

        # load image
        img = QImage(imagefilename)

        if img.isNull():
            qWarning("Unable to load file, unsupported file format")
            return

        # qWarning("Loading " + imagefilename + " " + str(img.width()) + "x" + str(img.height()) +" pixels")

        self.imageWidth = float(img.width())
        self.imageHeight = float(img.height())
        # QQQ qWarning(str(self.imageWidth) + " " + str(self.imageHeight))

        # 1E-3 needed. Just try with width=128 and see !
        newWidth = 1 << (int)(1 + log(img.width() - 1 + 1E-3) / log(2.0))
        newHeight = 1 << (int)(1 + log(img.height() - 1 + 1E-3) / log(2.0))

        self.u_max = img.width() / float(newWidth)
        self.v_max = img.height() / float(newHeight)

        if ((img.width() != newWidth) or (img.height() != newHeight)):
            #qWarning("Image size set to " + str(newWidth) + "x" + str(newHeight) + " pixels")
            img = img.copy(0, 0, newWidth, newHeight)

        glImg = QImage(QGLWidget.convertToGLFormat(img))  # flipped 32bit RGBA

        # Bind the img texture...
        glTexImage2D(GL_TEXTURE_2D, 0, 4, glImg.width(), glImg.height(), 0,
                     GL_RGBA, GL_UNSIGNED_BYTE,
                     glImg.bits().asstring(glImg.sizeInBytes()))
        # Another way to bind img texture:
        # if self.textureId:
        # self.deleteTexture(self.textureId)
        # self.textureId = None
        # self.textureId = self.bindTexture(img,GL_TEXTURE_2D,GL_RGBA)
        # if self.textureId :
        # glBindTexture(GL_TEXTURE_2D, self.textureId)

        self.bgimage = True

    def drawBackground(self):
        # code taken from the backgroundImage.cpp file that is part of the QGLViewer library

        # set texturing for background image
        glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR)
        glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR)
        # Nice texture coordinate interpolation
        glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST)

        glDisable(GL_LIGHTING)
        glEnable(GL_TEXTURE_2D)
        glColor3f(1, 1, 1)

        # to draw texture in entire window:
        # self.startScreenCoordinatesSystem(True)
        # # Draws the background quad
        # glNormal3f(0.0, 0.0, 1.0)
        # glBegin(GL_QUADS)
        # glTexCoord2f(0.0, 1.0-self.v_max)
        # glVertex2i(0,0)
        # glTexCoord2f(0.0, 1.0)
        # glVertex2i(0,self.height())
        # glTexCoord2f(self.u_max, 1.0)
        # glVertex2i(self.width(),self.height())
        # glTexCoord2f(self.u_max, 1.0-self.v_max)
        # glVertex2i(self.width(),0)
        # glEnd()
        # self.stopScreenCoordinatesSystem()

        # Draws the quad in (0,0) to (1.0,imageHeight/imageWidth)
        self.startScreenCoordinatesSystem(True)
        centre_x = self.width() * 0.5 - self.imageWidth * 0.5
        centre_y = self.height() * 0.5 - self.imageHeight * 0.5

        glNormal3f(0.0, 0.0, 1.0)
        glBegin(GL_QUADS)
        glTexCoord2f(0.0, 1.0 - self.v_max)
        glVertex2f(centre_x, centre_y)
        glTexCoord2f(0.0, 1.0)
        glVertex2f(centre_x, centre_y + self.imageHeight)
        glTexCoord2f(self.u_max, 1.0)
        glVertex2f(centre_x + self.imageWidth, centre_y + self.imageHeight)
        glTexCoord2f(self.u_max, 1.0 - self.v_max)
        glVertex2f(centre_x + self.imageWidth, centre_y)
        glEnd()
        self.stopScreenCoordinatesSystem()

        # Depth clear is not absolutely needed. An other option would have been to draw the
        # QUAD with a 0.999 z value (z ranges in [0, 1[ with startScreenCoordinatesSystem()).
        glClear(GL_DEPTH_BUFFER_BIT)
        glDisable(GL_TEXTURE_2D)
        glEnable(GL_LIGHTING)

    def draw(self):
        if self.isEnabled():
            if self.bgimage == False:
                self.setBackgroundColor(self.defaultColor)
            else:
                self.drawBackground()
        else:
            self.setBackgroundColor(self.disabledBGColor)
        self.start = self.pointOnEditionPlane(QPoint(0, self.height() - 1))
        self.end = self.pointOnEditionPlane(QPoint(self.width() - 1, 0))
        self.sphere.radius = (self.end[0] - self.start[0]) / 80
        self.discretizer.clear()
        self.curveshape.apply(self.renderer)
        try:
            glColor4fv(self.ctrlCurveColor)
        except:
            pass
        self.curveshape.apply(self.ctrlrenderer)
        self.ctrlpts.apply(self.renderer)
        self.drawGrid()

    def mRenderText(self, x, y, text):
        #error = glGetError()
        if self.font is None:
            self.font = QFont()
        self.renderText(x, y, 0, text)
        #self.drawText(x,y, text)
        #error = glGetError()
        #if error :  print gluErrorString(error)

    def drawVLine(self, x, y1, y2):
        glVertex3f(x, y1, 0)
        glVertex3f(x, y2, 0)

    def drawHLine(self, x1, x2, y):
        glVertex3f(x1, y, 0)
        glVertex3f(x2, y, 0)

    def drawGrid(self):
        xr = self.end[0] - self.start[0]
        xy = self.end[1] - self.start[1]
        if xr <= 0 or xy <= 0:
            return
        nbdigit = max(int(round(log(xr, 10))), int(round(log(xy, 10))))
        xdelta = pow(10, nbdigit) / 10.

        fxval = round(self.start[0] / xdelta)
        lxval = round(self.end[0] / xdelta)
        cxval = fxval * xdelta
        nbiter = int((lxval - fxval))
        glColor4fv(self.gridColor2)
        glLineWidth(1)
        glBegin(GL_LINES)
        for i in range(nbiter + 1):
            self.drawVLine(cxval, self.start[1], self.end[1])
            cxval += xdelta
        glEnd()
        cxval = fxval * xdelta
        glColor4fv(self.textColor)
        for i in range(nbiter + 1):
            self.mRenderText(cxval, self.start[1], '%.1f' % cxval)
            cxval += xdelta
        glLineWidth(2)
        glColor4fv(self.gridColor)
        cxval = round(self.start[0] / (10 * xdelta)) * (10 * xdelta)
        glBegin(GL_LINES)
        for i in range(int(nbiter / 10) + 1):
            self.drawVLine(cxval, self.start[1], self.end[1])
            cxval += (10 * xdelta)
        glEnd()

        fyval = round(self.start[1] / xdelta)
        lyval = round(self.end[1] / xdelta)
        firstcyval = fyval * xdelta
        cyval = firstcyval
        nbiter = int((lyval - fyval))
        glColor4fv(self.gridColor2)
        glLineWidth(1)
        glBegin(GL_LINES)
        for i in range(nbiter + 1):
            self.drawHLine(self.start[0], self.end[0], cyval)
            cyval += xdelta
        glEnd()
        cyval = firstcyval
        glColor4fv(self.textColor)
        for i in range(nbiter + 1):
            glVertex3f(self.end[0], cyval, 0)
            self.mRenderText(self.start[0], cyval, '%.1f' % cyval)
            cyval += xdelta
        glLineWidth(2)
        glColor4fv(self.gridColor)
        cyval = round(self.start[1] / (10 * xdelta)) * (10 * xdelta)
        glBegin(GL_LINES)
        for i in range(int(nbiter / 10) + 1):
            self.drawHLine(self.start[0], self.end[0], cyval)
            cyval += (10 * xdelta)
        glEnd()

    def drawWithNames(self):
        self.renderer.clear()
        for sh in self.ctrlpts:
            glPushName(sh.id)
            sh.apply(self.renderer)
            glPopName()
        # self.renderer.renderingMode = GLRenderer.Selection
        # self.renderer.selectionMode = GLRenderer.ShapeId
        # self.renderer.beginProcess()
        # self.ctrlpts.apply(self.renderer)
        # self.renderer.endProcess()
        # self.renderer.renderingMode = GLRenderer.Dynamic

    def pointOnEditionPlane(self, pos):
        orig, direction = self.camera().convertClickToLine(pos)
        npoint = orig + direction * (orig[2] / (-direction[2]))
        return (npoint[0], npoint[1])

    def setInteractionMode(self, frame=True):
        if frame:
            self.setMouseBinding(Qt.NoModifier, Qt.LeftButton, QGLViewer.FRAME,
                                 QGLViewer.TRANSLATE)
            self.setMouseBinding(Qt.NoModifier, Qt.RightButton,
                                 QGLViewer.FRAME, QGLViewer.NO_MOUSE_ACTION)
            self.setMouseBinding(Qt.ControlModifier, Qt.LeftButton,
                                 QGLViewer.CAMERA, QGLViewer.TRANSLATE)
            self.setMouseBinding(Qt.ControlModifier, Qt.RightButton,
                                 QGLViewer.CAMERA, QGLViewer.NO_MOUSE_ACTION)
        else:
            self.setMouseBinding(Qt.ControlModifier, Qt.LeftButton,
                                 QGLViewer.FRAME, QGLViewer.TRANSLATE)
            self.setMouseBinding(Qt.ControlModifier, Qt.RightButton,
                                 QGLViewer.FRAME, QGLViewer.NO_MOUSE_ACTION)
            self.setMouseBinding(Qt.NoModifier, Qt.LeftButton,
                                 QGLViewer.CAMERA, QGLViewer.TRANSLATE)
            self.setMouseBinding(Qt.NoModifier, Qt.RightButton,
                                 QGLViewer.CAMERA, QGLViewer.NO_MOUSE_ACTION)

    def mousePressEvent(self, event):
        if event.modifiers() != Qt.ControlModifier:
            self.select(event.pos())
            self.selection = self.selectedName()
            if self.selection != -1:
                p = self.curveAccessor.getPoint(self.selection)
                self.manipulator.setPosition(Vec(p[0], p[1], 0))
                self.setManipulatedFrame(self.manipulator)
                self.createControlPointsRep()
                self.setInteractionMode(True)
                self.updateGL()
                QGLViewer.mousePressEvent(self, event)
            else:
                QGLViewer.mousePressEvent(self, event)
        else:
            return QGLViewer.mousePressEvent(self, event)

    def mouseDoubleClickEvent(self, event):
        if event.modifiers() == Qt.NoModifier:
            if event.button() == Qt.LeftButton:
                self.select(event.pos())
                selection = self.selectedName()
                if selection == -1:
                    npoint = self.pointOnEditionPlane(event.pos())
                    res = self.pointsConstraints.addPointEvent(
                        npoint, self.curveAccessor)
                    if res:
                        index, npoint = res
                        self.curveAccessor.insertPoint(index, npoint)
                        self.valueChanged.emit()
            elif event.button() == Qt.RightButton:
                self.select(event.pos())
                selection = self.selectedName()
                if selection != -1:
                    self.curveAccessor.delPoint(selection)
                    self.valueChanged.emit()
            self.createControlPointsRep()
            self.updateGL()
        elif event.modifiers() == Qt.ShiftModifier:
            self.select(event.pos())
            selection = self.selectedName()
            if selection != -1:
                self.curveAccessor.delPoint(selection)
                self.valueChanged.emit()
                self.createControlPointsRep()
                self.updateGL()
        else:
            QGLViewer.mouseDoubleClickEvent(self, event)

    def mouseReleaseEvent(self, event):
        if self.selection != -1:
            self.selection = -1
            self.setInteractionMode(False)
        self.createControlPointsRep()
        self.updateSceneDimension()
        self.updateGL()
        QGLViewer.mouseReleaseEvent(self, event)

    def changeEvent(self, event):
        if event.type() == QEvent.EnabledChange:
            if self.isEnabled():
                self.pointColor = Material((250, 30, 30), 1)
            else:
                self.pointColor = Material((30, 30, 30), 1)
            self.createControlPointsRep()
        QGLViewer.changeEvent(self, event)

    def updatePoints(self):
        sel = self.selection
        if sel != -1:
            p = self.manipulator.getPosition()
            p = (p[0], p[1])
            p = self.pointsConstraints.movePointEvent(p, self.selection,
                                                      self.curveAccessor)
            self.curveAccessor.setPoint(self.selection, p)
            self.createControlPointsRep()
            self.updateGL()
            self.valueChanged.emit()

    def createControlPointsRep(self):
        self.ctrlpts = Scene([
            Shape(Translated(Vector3(p[0], p[1], 0), self.sphere),
                  self.pointColor,
                  id=i) for i, p in enumerate(self.curveAccessor.points())
        ])
        if self.selection != -1:
            self.ctrlpts[self.selection].appearance = self.selectedPointColor
        if self.selection != 0:
            self.ctrlpts[0].appearance = self.firstPointColor
Пример #17
0
class SelectionManipulator(ManipulatedFrame):

    valueChanged = pyqtSignal()

    def __init__(self):
        ManipulatedFrame.__init__(self)
        # set of selected point
        self.selection = []
        # axis for axial displacement
        self.axis = [
            SelectionAxis(self,
                          SelectionAxis.X,
                          material=Material((250, 30, 30), 1)),
            SelectionAxis(self,
                          SelectionAxis.Y,
                          material=Material((30, 250, 30), 1)),
            SelectionAxis(self,
                          SelectionAxis.Z,
                          material=Material((250, 250, 250), 1))
        ]

        # previous position to compute displacement
        self.previousPosition = Vec()
        self.manipulated = pyqtSignal(
        )  # AUTO SIGNAL TRANSLATION in class SelectionManipulator
        self.manipulated.connect(
            self.propagatePointTranslation
        )  # QObject.connect(self,SIGNAL("manipulated()"),self.propagatePointTranslation)
        for axis in self.axis:
            axis.translated.connect(
                self.propagateAxisTranslation
            )  # QObject.connect(axis,SIGNAL("translated(PyQt_PyObject)"),self.propagateAxisTranslation)

    def __push_position__(self):
        """ push current position status as previous position """
        self.previousPosition = self.position()

    def displacement(self):
        """ Gives current displacement of the frame """
        delta = self.position() - self.previousPosition
        self.__push_position__()
        return delta

    def propagateAxisTranslation(self, displacement):
        """ Apply the displacement of the axis on the set of selected control points """
        self.setPosition(self.position() + displacement)
        self.__push_position__()
        for point in self.selection:
            point.apply_translation(displacement)
        self.valueChanged.emit(
        )  # self.emit(SIGNAL("valueChanged()")) # AUTO SIGNAL TRANSLATION

    def propagatePointTranslation(self):
        """ Apply the displacement of self on the set of selected control points """
        displacement = self.displacement()
        for point in self.selection:
            point.apply_translation(displacement)
        self.valueChanged.emit(
        )  # self.emit(SIGNAL("valueChanged()")) # AUTO SIGNAL TRANSLATION

    def clear(self):
        """ remove all control points """
        self.manipulated.disconnect(self.propagatePointTranslation)
        for ctrlpoint in self.selection:
            ctrlpoint.selected = False
            ctrlpoint.translated.disconnect(self.propagatePointTranslation)
            #QObject.disconnect(ctrlpoint,SIGNAL("translated(PyQt_PyObject,PyQt_PyObject)"),self.propagatePointTranslation)
        self.selection = None

    def add(self, ctrlpoint):
        """ add a ctrl point to the selection """
        self.selection.append(ctrlpoint)
        ctrlpoint.selected = True
        #ctrlpoint.translated.connect(self.propagatePointTranslation) # QObject.connect(ctrlpoint,SIGNAL("translated(PyQt_PyObject,PyQt_PyObject)"),self.propagatePointTranslation)
        self.__update__()

    def remove(self, ctrlpoint):
        """ remove a ctrl point to the selection """
        ctrlpoint.selected = False
        #ctrlpoint.translated.connect(self.propagatePointTranslation) # QObject.connect(ctrlpoint,SIGNAL("translated(PyQt_PyObject,PyQt_PyObject)"),self.propagatePointTranslation)
        del self.selection[self.selection.index(ctrlpoint)]
        self.__update__()

    def toogleSelection(self, ctrlpoint):
        """ add a ctrl point to the selection or remove it if already in it"""
        if ctrlpoint in self.selection:
            self.remove(ctrlpoint)
        else:
            self.add(ctrlpoint)

    def __update__(self):
        """ update the central point position when selection has changed """
        self.centralPoint = sum([i.position() for i in self.selection],
                                Vec()) / len(self.selection)
        self.setPosition(self.centralPoint)
        self.__push_position__()

    def representation(self, pointsize=0.05):
        """ define the representation of this manipulator """
        shapes = []
        shapes += self.axis[0].representation(pointsize)
        shapes += self.axis[1].representation(pointsize)
        shapes += self.axis[2].representation(pointsize)
        return shapes

    def empty(self):
        """ Tell whether the selection set is empty """
        return len(self.selection) == 0

    def checkIfAxisGrabsMouse(self, event, viewer):
        """ check whether an axis has been selected """
        for axis in self.axis:
            axis.checkIfGrabsMouse(event.x(), event.y(), viewer.camera())
            if axis.grabsMouse():
                viewer.setManipulatedFrame(axis)
                axis.hasFocus = True
                return True
        return False

    def clearFocus(self):
        """ clear focus """
        for axis in self.axis:
            axis.hasFocus = False
Пример #18
0
class SelectionAxis(ManipulatedFrame):
    """ The selection Axis class. 
        We consider here that x, y and z axis are represented. 
        These axis are represented by plantGL polylines and spheres 
        and must be grabbable for constrained displacements
    """
    O, X, Y, Z = (Vec(0, 0, 0), Vec(1, 0, 0), Vec(0, 1, 0), Vec(0, 0, 1)
                  )  # predefined axis direction
    AxisLength = 0.2  # length of axis representation

    manipulated = pyqtSignal()
    translated = pyqtSignal('PyQt_PyObject')

    def __init__(self,
                 parent,
                 direction=Vec(),
                 material=Material(),
                 selectionMaterial=Material((0, 0, 0), 1)):
        """Constructor 
        
        :param parent : the reference frame on which it is defined. In practice, it is a SelectionManipulator.
        :param direction: the direction of the axis.
        :param material: color of the representation of this axis
        :param selectionMaterial: color of the representation of this axis when it is selected
        """
        ManipulatedFrame.__init__(self)
        self.setReferenceFrame(parent)

        # selection
        self.hasFocus = False

        # direction and position
        self.direction = direction
        self.initpos = direction * SelectionAxis.AxisLength
        self.setTranslation(self.initpos)

        # display option
        self.material = material
        self.selectionMaterial = selectionMaterial

        #displacement computation
        self.previousPosition = Vec()

        # Constraint the direction of translation
        self.constraint = WorldConstraint()
        self.constraint.setRotationConstraintType(
            AxisPlaneConstraint.FORBIDDEN)
        self.constraint.setTranslationConstraint(AxisPlaneConstraint.AXIS,
                                                 self.direction)
        self.setConstraint(self.constraint)

        self.manipulated.connect(
            self.__emit_translation__
        )  # QObject.connect(self,SIGNAL("manipulated()"),self.__emit_translation__)

    def representation(self, pointsize=0.05):
        """ Compute representation of the SelectionAxis as line and sphere. """
        p = self.position()
        p2 = p - self.direction * SelectionAxis.AxisLength
        if self.hasFocus: material = self.selectionMaterial
        else: material = self.material
        sh = [Shape(Polyline([Vector3(*p2), Vector3(*p)]), material)]
        sh += [Shape(Translated(Vector3(*p), Sphere(pointsize)), material)]
        return sh

    def __emit_translation__(self):
        """ emit a SIGNAL when a translation occur on this axis """
        tr = self.translation()
        self.setTranslation(self.initpos)
        self.translated.emit(
            tr - self.initpos
        )  # self.emit(SIGNAL("translated(PyQt_PyObject)"),tr-self.initpos) # AUTO SIGNAL TRANSLATION
Пример #19
0
class ObjectDialog(QDialog):
    """the class that will create dialog between the panel and the editor window"""
    valueChanged = pyqtSignal()
    hidden = pyqtSignal()
    AutomaticUpdate = pyqtSignal(bool)

    def __init__(self, *args):
        """during the init of the dialog we have to know the editor we want to open, the typ variable will allow us to know that"""
        QDialog.__init__(self,*args)
        self.hasChanged = False
        self.automaticUpdate = False
        #self.setModal(True)
    
    def setupUi(self,editor):
        self.setObjectName("ObjectDialog")
        self.resize(389, 282)
        self.verticalLayout = QVBoxLayout(self)
        self.verticalLayout.setSpacing(2)
        self.verticalLayout.setContentsMargins(2, 2, 2, 2)
        self.verticalLayout.setObjectName("verticalLayout")
        self._menu = QMenuBar(self)
        try:
            self._menu.setNativeMenuBar(False)
        except: pass
        self.verticalLayout.addWidget(self._menu)
        self.objectView = editor
        sizePolicy = QSizePolicy(QSizePolicy.Preferred, QSizePolicy.Expanding)
        sizePolicy.setHorizontalStretch(0)
        sizePolicy.setVerticalStretch(5)
        #sizePolicy.setHeightForWidth(self.objectView.sizePolicy().hasHeightForWidth())
        self.objectView.setSizePolicy(sizePolicy)
        self.objectView.setObjectName("objectView")
        self.verticalLayout.addWidget(self.objectView)
        self.horizontalLayout = QHBoxLayout()
        self.horizontalLayout.setSpacing(0)
        self.horizontalLayout.setSizeConstraint(QLayout.SetFixedSize)
        self.horizontalLayout.setObjectName("horizontalLayout")
        self.autoUpdateCheckBox = QCheckBox(self)
        self.autoUpdateCheckBox.setObjectName("autoUpdateCheckBox")
        self.horizontalLayout.addWidget(self.autoUpdateCheckBox)
        spacerItem = QSpacerItem(48, 20, QSizePolicy.Expanding, QSizePolicy.Minimum)
        self.horizontalLayout.addItem(spacerItem)
        self.okButton = QPushButton(self)
        self.okButton.setObjectName("okButton")
        self.horizontalLayout.addWidget(self.okButton)
        self.applyButton = QPushButton(self)
        self.applyButton.setObjectName("applyButton")
        self.horizontalLayout.addWidget(self.applyButton)
        self.cancelButton = QPushButton(self)
        self.cancelButton.setObjectName("cancelButton")
        self.horizontalLayout.addWidget(self.cancelButton)
        self.verticalLayout.addLayout(self.horizontalLayout)
        
        self.setWindowTitle("ObjectDialog")
        self.autoUpdateCheckBox.setText("Auto update")
        self.okButton.setText("Ok")
        self.applyButton.setText("Apply")
        self.cancelButton.setText("Cancel")
      
        self.cancelButton.pressed.connect(self.reject)
        self.okButton.pressed.connect(self.__ok)
        self.applyButton.pressed.connect(self.__apply)
        self.autoUpdateCheckBox.toggled.connect(self.setAutomaticUpdate)
        self.objectView.valueChanged.connect(self.__valueChanged)
        
    def menu(self):
        return self._menu
    
    def __valueChanged(self):
        if self.automaticUpdate:
            self.valueChanged.emit()
        else:
            self.hasChanged = True

    def __apply(self):
        self.valueChanged.emit()
        self.hasChanged = False
        
    def __ok(self):
        self.valueChanged.emit()
        self.hasChanged = False
        self.accept()
        self.close()

    def hideEvent(self,event):
        self.hidden.emit()

    def setAutomaticUpdate(self,value):
        """setAutomaticUpdate: checking the autoupdate box will make the QDialog send a 'valueChanged()' signal each time it recieve the same Signal from the objectView"""
        if self.automaticUpdate != value:
            self.automaticUpdate = value
            self.applyButton.setEnabled(not self.automaticUpdate)
            self.AutomaticUpdate.emit(value)
            if self.automaticUpdate and self.hasChanged :
                self.__apply()
                

    def closeEvent(self,event):
        QDialog.closeEvent(self,event)
Пример #20
0
class LPyWindow(QMainWindow, lsmw.Ui_MainWindow, ComputationTaskManager):

    endTask = pyqtSignal('PyQt_PyObject')
    killedTask = pyqtSignal('PyQt_PyObject')

    instances = []

    def __init__(self, parent=None, withinterpreter=True):
        """
        :param parent : parent window
        """
        QMainWindow.__init__(self, parent)
        ComputationTaskManager.__init__(self)
        lsmw.Ui_MainWindow.__init__(self)

        self.setObjectName('LPYMainWindow')
        self.setWindowIcon(QIcon(":/images/icons/mango.png"))

        import weakref
        LPyWindow.instances.append(weakref.ref(self))

        self.withinterpreter = withinterpreter
        self.setupUi(self)
        self.editToolBar.hide()
        lpydock.initDocks(self)

        QTimer.singleShot(1000, lambda: lpydock.initShell(self))

        self.preferences = lpypreferences.LpyPreferences(self)
        icon = QIcon()
        icon.addPixmap(QPixmap(":/images/icons/history.png"), QIcon.Normal,
                       QIcon.Off)
        self.menuRecents.setIcon(icon)
        self.simulations = []
        self.currentSimulationId = None
        self.history = []
        self.historymaxsize = 50
        self.fileBackupEnabled = True
        self.codeBackupEnabled = True
        self.fitAnimationView = True
        self.fitRunView = True
        self.with_thread = False
        self.showPyCode = False
        self.displayMetaInfo = False
        self.reloadAtStartup = True
        self.fileMonitoring = True
        self.exitWithoutPrompt = False
        self.cCompilerPath = ''
        self.profilingMode = ProfilingWithFinalPlot
        self.desc_items = {
            '__authors__': self.authorsEdit,
            '__institutes__': self.intitutesEdit,
            '__copyright__': self.copyrightEdit,
            '__description__': self.descriptionEdit,
            '__references__': self.referenceEdit
        }
        self.com_mutex = QMutex()
        self.com_waitcondition = QWaitCondition()
        self.killsimudialog = KillSimulationDialog(self)
        self.plotter = LpyPlotter(self)
        self.use_own_view3D = False
        self.viewer = Viewer
        registerPlotter(self.plotter)

        class ViewerFuncAborter:
            def __init__(self):
                self.__shouldAbort = False
                self.__registered = False

            def shouldAbort(self):
                self.__shouldAbort = True

            def reset(self):
                self.__shouldAbort = False
                if not self.__registered:
                    self.__registered = True
                    Viewer.setDialogAbortFunc(self)

            def __call__(self):
                if self.__shouldAbort:
                    self.__shouldAbort = False
                    return True
                else:
                    return False

        self.viewAbortFunc = ViewerFuncAborter()
        self.frameFind.hide()
        self.frameReplace.hide()
        self.frameGoto.hide()
        self.codeeditor.initWithEditor(self)
        self.debugMode = False
        self.debugger = LpyVisualDebugger(self)
        st = self.statusBar()
        self.materialed.statusBar = st
        self.panelmanager = ObjectPanelManager(self)
        #self.documentNames.setShape(QTabBar.TriangularNorth)
        #self.documentNames.setTabsClosable(True)
        self.newfile()
        self.textEditionWatch = False
        self.documentNames.connectTo(self)

        self.endTask.connect(self.endTaskCheck)
        # self.documentNamesMore.newDocumentRequest = pyqtSignal() # AUTO SIGNAL TRANSLATION in class LPyWindow
        self.documentNamesMore.newDocumentRequest.connect(self.newfile)
        # self.documentNamesMore2.newDocumentRequest = pyqtSignal() # AUTO SIGNAL TRANSLATION in class LPyWindow
        self.documentNamesMore2.newDocumentRequest.connect(self.newfile)
        self.actionNew.triggered.connect(self.newfile)
        self.actionOpen.triggered.connect(lambda: self.openfile())
        self.actionSave.triggered.connect(lambda: self.savefile())
        self.actionSaveAll.triggered.connect(lambda: self.saveallfiles())
        self.actionSaveAs.triggered.connect(self.saveas)
        self.actionClose.triggered.connect(self.closeDoc)
        self.actionImportCpfgProject.triggered.connect(
            lambda: self.importcpfgproject())
        self.actionImportCpfgFile.triggered.connect(
            lambda: self.importcpfgfile())
        self.actionClear.triggered.connect(self.clearHistory)
        self.actionSaveSession.triggered.connect(self.saveSession)
        self.actionRun.triggered.connect(self.run)
        self.actionAnimate.triggered.connect(self.animate)
        self.actionStep.triggered.connect(self.step)
        self.actionRewind.triggered.connect(self.rewind)
        self.actionStepInterpretation.triggered.connect(
            self.stepInterpretation)
        self.actionIterateTo.triggered.connect(self.iterateTo)
        self.actionNextIterate.triggered.connect(self.nextIterate)
        self.actionAutoRun.triggered.connect(self.projectAutoRun)
        self.actionDebug.triggered.connect(self.debug)
        self.actionProfile.triggered.connect(self.profile)
        self.actionRecord.triggered.connect(self.record)
        self.actionStop.triggered.connect(self.cancelTask)
        self.actionStop.triggered.connect(self.abortViewer)
        self.actionExecute.triggered.connect(self.executeCode)
        self.actionComment.triggered.connect(self.codeeditor.comment)
        self.actionUncomment.triggered.connect(self.codeeditor.uncomment)
        self.actionInsertTab.triggered.connect(self.codeeditor.tab)
        self.actionRemoveTab.triggered.connect(self.codeeditor.untab)
        self.actionSyntax.triggered.connect(self.setSyntaxHighLightActivation)
        self.actionTabHightlight.triggered.connect(
            self.setTabHighLightActivation)
        self.actionPreferences.triggered.connect(self.preferences.show)
        self.animtimestep.valueChanged.connect(self.setTimeStep)
        self.animtimeSpinBox.valueChanged.connect(self.setTimeStep)
        self.codeeditor.textChanged.connect(self.textEdited)
        self.descriptionEdit.textChanged.connect(self.projectEdited)
        self.referenceEdit.textChanged.connect(self.projectEdited)
        self.authorsEdit.textChanged.connect(self.projectEdited)
        self.intitutesEdit.textChanged.connect(self.projectEdited)
        self.copyrightEdit.textChanged.connect(self.projectEdited)
        self.materialed.valueChanged.connect(self.projectEdited)
        self.scalarEditor.valueChanged.connect(self.projectEdited)
        self.scalarEditor.valueChanged.connect(self.projectParameterEdited)
        self.actionPrint.triggered.connect(self.printCode)
        self.actionView3D.setEnabled(self.use_own_view3D)
        self.actionView3D.triggered.connect(self.switchCentralView)
        self.aboutLpy = lambda x: doc.aboutLpy(self)
        self.actionAbout.triggered.connect(self.aboutLpy)
        self.actionAboutQt.triggered.connect(QApplication.aboutQt)
        self.aboutVPlants = lambda x: doc.aboutVPlants(self)
        self.helpDisplay.setText(doc.getSpecification())
        self.actionOnlineHelp.triggered.connect(self.onlinehelp)
        self.actionSubmitBug.triggered.connect(self.submitBug)
        self.actionCheckUpdate.triggered.connect(self.check_lpy_update)
        self.actionUseThread.triggered.connect(self.toggleUseThread)
        self.actionFitAnimationView.triggered.connect(
            self.toggleFitAnimationView)
        self.menuRecents.triggered.connect(self.recentMenuAction)
        self.initSVNMenu()
        self.printTitle()
        self.centralViewIsGL = False
        self.svnLastRevisionChecked = 0
        self.svnLastDateChecked = 0.0
        self.stackedWidget.setCurrentIndex(0)
        self.setAnimated(False)
        settings.restoreState(self)
        self.createRecentMenu()
        #if not py2exe_release:
        try:
            self.createTutorialMenu()
        except:
            pass
        self.textEditionWatch = True
        self._initialized = False
        try:
            self.lpy_update_enabled = self.check_lpy_update_available()
        except:
            pass

    def init(self):
        self.textEditionWatch = False
        self.recoverPreviousFiles()
        self.textEditionWatch = True
        if True:  #self.lpy_update_enabled:
            self.check_lpy_update(True)
        self.documentNames.show()
        self.currentSimulation().updateTabName()

    def check_lpy_update_available(self):
        available = True
        if not available:
            self.actionCheckUpdate.setEnabled(False)
        return available

    def retrieve_official_lpy_version(self, channel='openalea'):
        import urllib.request, urllib.error, urllib.parse
        versionurl = 'https://raw.githubusercontent.com/' + channel + '/lpy/master/src/openalea/lpy/__version__.py'
        try:
            response = urllib.request.urlopen(versionurl)
        except urllib.error.URLError as ue:
            import openalea.lpy.__version__ as lv
            return get_version_majorminor(
                lv.__version_number__), lv.LPY_VERSION_STR
        else:
            pyversioncode = response.read()
            lvofficial = {}
            exec(pyversioncode, lvofficial)
            return get_version_majorminor(lvofficial['__version_number__']
                                          ), lvofficial['LPY_VERSION_STR']

    def check_lpy_update(self, silent=False):
        import openalea.lpy.__version__ as lv
        import os, time
        if not silent or (
            (self.svnLastDateChecked + 7 * 24 * 60 * 60) < time.time()):
            self.svnLastDateChecked = time.time()
            officialversion, offverstring = self.retrieve_official_lpy_version(
            )
            officialdevversion, offverdevstring = self.retrieve_official_lpy_version(
                'fredboudon')
            if get_version_majorminor(lv.__version_number__) < officialversion:
                QMessageBox.information(
                    self, "Lpy Update",
                    "Your version is " + lv.LPY_VERSION_STR +
                    ".\nA new release version of lpy seems available on github :"
                    + offverstring + "\n.")
            elif get_version_majorminor(
                    lv.__version_number__) < officialdevversion:
                QMessageBox.information(
                    self, "Lpy Update",
                    "Your version is " + lv.LPY_VERSION_STR +
                    ".\nA new develop version of lpy seems available on github :"
                    + offverdevstring + "\n.")
            elif not silent:
                QMessageBox.information(
                    self, "Lpy Update", "Your version is " +
                    lv.LPY_VERSION_STR + ".\nYou are up-to-date!")
            else:
                self.statusBar().showMessage("L-Py " + lv.LPY_VERSION_STR +
                                             " is up-to-date.")

    def switchCentralView(self):
        if not self.centralViewIsGL:
            self.stackedWidget.setCurrentIndex(1)
        else:
            self.stackedWidget.setCurrentIndex(0)
        self.centralViewIsGL = not self.centralViewIsGL
        self.actionView3D.setChecked(self.centralViewIsGL)

    def setView3DCentral(self, enabled=True):
        if self.centralViewIsGL != enabled:
            self.switchCentralView()

    def setIntegratedView3D(self, enabled):
        if self.use_own_view3D != enabled:
            if PyQGLViewer:
                self.use_own_view3D = enabled
                if not enabled and self.centralViewIsGL:
                    self.switchCentralView()
                if not enabled:
                    self.viewer = Viewer
                    Viewer.display = self.previousplotfunction
                else:
                    if not hasattr(self, 'previousplotfunction'):
                        self.previousplotfunction = Viewer.display
                    self.viewer = self.view3D

                    def myplot(sc):
                        self.plotScene(sc)

                    Viewer.display = staticmethod(myplot)
                self.actionView3D.setEnabled(enabled)

    def getObjectPanels(self):
        return self.panelmanager.getObjectPanels()

    def getMaxObjectPanelNb(self):
        return self.panelmanager.getMaxObjectPanelNb()

    def setObjectPanelNb(self, nb, new_visible=True):
        self.panelmanager.setObjectPanelNb(nb, new_visible)

    #def createNewPanel(self,above):
    #    self.panelmanager.createNewPanel(above)
    def abortViewer(self):
        self.viewAbortFunc.shouldAbort()

    def currentSimulation(self):
        return self.simulations[self.currentSimulationId]

    def changeDocument(self, id):
        if self.currentSimulationId != id and id >= 0:
            if not self.currentSimulationId is None:
                self.currentSimulation().saveState()
            self.currentSimulationId = id
            self.currentSimulation().monitorfile()
            self.currentSimulation().restoreState()
        if self.documentNames.currentIndex() != id:
            self.documentNames.setCurrentIndex(id)

    def findDocumentId(self, fname):
        for i, s in enumerate(self.simulations):
            if s.fname == fname:
                return s.index

    def showDocumentAt(self, fname, line):
        if self.currentSimulation().fname == fname:
            self.codeeditor.gotoLine(line)
        else:
            id = None
            for i, s in enumerate(self.simulations):
                if s.fname == fname:
                    id = s.index
                    break
            if not id is None:
                self.changeDocument(id)
                self.codeeditor.gotoLine(line)

    def switchDocuments(self, id1, id2):
        self.simulations[id1], self.simulations[id2] = self.simulations[
            id2], self.simulations[id1]
        self.simulations[id1].index = id1
        self.simulations[id2].index = id2
        self.simulations[id1].updateTabName(True)
        self.simulations[id2].updateTabName(True)
        self.documentNames.currentChanged.disconnect(self.changeDocument)
        self.documentNames.setCurrentIndex(id1)
        self.documentNames.currentChanged.connect(self.changeDocument)

    def focusInEvent(self, event):
        self.currentSimulation().monitorfile()
        return QMainWindow.focusInEvent(self, event)

    def closeDoc(self):
        self.closeDocument()

    def closeDocument(self, id=None):
        if id is None:
            id = self.currentSimulationId
        if self.simulations[id].close():
            self.documentNames.removeTab(id)
            for i in range(id + 1, len(self.simulations)):
                self.simulations[i].index = i - 1

            self.textEditionWatch = False
            defaultdoc = self.codeeditor.defaultdoc
            self.codeeditor.setLpyDocument(defaultdoc)
            self.simulations.pop(id)
            self.textEditionWatch = True

            if len(self.simulations) == 0:
                self.currentSimulationId = None
                self.newfile()
            else:
                self.currentSimulation().restoreState()

    def end(self, force=False):
        if force:
            self.exitWithoutPrompt = force
        self.close()

    def closeEvent(self, e):
        self.debugger.endDebug()
        self.viewer.stop()
        settings.saveState(self)
        prompt = False
        if not self.exitWithoutPrompt:
            for simu in self.simulations:
                prompt = (prompt or simu.isEdited())
                if prompt: break
            if not prompt:
                answer = QMessageBox.warning(self, "Quitting",
                                             "Are you sure ?", QMessageBox.Ok,
                                             QMessageBox.Cancel)
                if answer == QMessageBox.Cancel: e.ignore()
                else: e.accept()
            else:
                for simu in reversed(self.simulations):
                    if not simu.close():
                        e.ignore()
                        return
                e.accept()
        else:
            e.accept()
        if e.isAccepted():
            self.interpreter.locals.clear()

    def saveSession(self):
        settings.saveState(self)

    def showEvent(self, event):
        if not self._initialized:
            self.init()
            self._initialized = True

    def taskRunningEvent(self):
        self.statusBar().showMessage('A task is already running !', 5000)
        raise Exception('A task is already running')

    def acquireEvent(self):
        self.enableButtons(False)

    def releaseEvent(self):
        self.enableButtons(True)

    def enableButtons(self, enabled):
        self.actionRun.setEnabled(enabled)
        self.actionAnimate.setEnabled(enabled)
        self.actionStep.setEnabled(enabled)
        self.actionRewind.setEnabled(enabled)
        self.actionClear.setEnabled(enabled)
        self.actionClose.setEnabled(enabled)
        self.actionProfile.setEnabled(enabled)
        if self.debugMode == True:
            self.actionDebug.setEnabled(True)
        else:
            self.actionDebug.setEnabled(enabled)
        self.actionStop.setEnabled(not enabled)
        self.documentNames.setEnabled(enabled)

    def projectAutoRun(self, value=True):
        self.currentSimulation().autorun = value

    def viewer_plot(self, scene):
        if self.use_own_view3D:
            self.setView3DCentral()
        self.viewer.display(scene)

    def plotScene(self, scene):
        if self.thread() != QThread.currentThread():
            #Viewer.display(scene)
            self.com_mutex.lock()
            e = QEvent(QEvent.Type(QEvent.User + 1))
            e.scene = scene
            QApplication.postEvent(self, e)
            self.com_waitcondition.wait(self.com_mutex)
            self.com_mutex.unlock()
        else:
            self.viewer_plot(scene)
            if not self.currentSimulation().autorun:
                QCoreApplication.instance().processEvents()

    def cancelTask(self):
        if self.debugMode:
            self.debugger.stop()
            if not self.debugger.running:
                self.debugMode = False
                self.releaseCR()
        else:
            if not self.computationThread is None:
                self.currentSimulation().cancel()
                self.killsimudialog.run(self.isRunning, self.killTask)
            else:
                if self.isRunning():
                    print("Force release")
                self.releaseCR()

    def customEvent(self, event):
        self.viewer_plot(event.scene)
        self.com_mutex.lock()
        self.com_mutex.unlock()
        self.com_waitcondition.wakeAll()

    def errorEvent(self, exc_info, errmsg, displayDialog):
        if self.withinterpreter:
            self.interpreterDock.show()

        t, v, trb = exc_info
        stacksummary = list(reversed(tb.extract_tb(trb)))
        print(len(stacksummary))
        for fid, frame in enumerate(stacksummary):
            print(frame.filename)
            if 'openalea/lpy' in frame.filename:
                stacksummary = stacksummary[:fid]
                break
        print(len(stacksummary))
        self.lastexception = v
        if t == SyntaxError:
            errorfile = v.filename
            lineno = v.lineno
        else:
            if len(stacksummary) > 0:
                st = stacksummary[0]
                errorfile = st.filename
                lineno = st.lineno
            else:
                errorfile = None
                lineno = None
        fnames = ['<string>', self.currentSimulation().getBaseName()]

        if errorfile in fnames:
            self.codeeditor.hightlightError(lineno)

        def showErrorOnFile():
            docid = self.findDocumentId(errorfile)
            if docid:
                self.showDocumentAt(errorfile, lineno)
            else:
                self.showfilecontent(errorfile)
            self.codeeditor.hightlightError(lineno)

        if displayDialog:
            dialog = QMessageBox(
                QMessageBox.Warning, "Exception",
                (os.path.basename(errorfile) + ':' if errorfile else '') +
                (str(lineno) + ':' if lineno else '') + errmsg, QMessageBox.Ok,
                self)
            if errorfile and (not errorfile in fnames):
                showbutton = dialog.addButton("Show file",
                                              QMessageBox.ApplyRole)
                showbutton.clicked.connect(showErrorOnFile)
            if len(stacksummary) > 0:
                dialog.setDetailedText(errmsg + '\n\n' +
                                       '\n'.join(tb.format_list(stacksummary)))
            dialog.exec_()
            #showError = QMessageBox.warning(self,"Exception", msg, QMessageBox.Ok)

    def endErrorEvent(self):
        if self.debugger.running:
            self.debugger.stopDebugger()
            self.debugMode = False

    def setToolBarApp(self, value):
        if type(value) == int:  # hack since pyqtSlot does not seems to work
            value = ['Icons', 'Texts', 'Icons and texts',
                     'Texts below icons'][value]
        for bar in [self.FileBar, self.LsytemBar, self.editToolBar]:
            bar.setToolButtonStyle({
                'Icons':
                Qt.ToolButtonIconOnly,
                'Texts':
                Qt.ToolButtonTextOnly,
                'Icons and texts':
                Qt.ToolButtonTextBesideIcon,
                'Texts below icons':
                Qt.ToolButtonTextUnderIcon
            }[str(value)])

    def getToolBarApp(self):
        return {
            Qt.ToolButtonIconOnly: (0, 'Icons'),
            Qt.ToolButtonTextOnly: (1, 'Texts'),
            Qt.ToolButtonTextBesideIcon: (2, 'Icons and texts'),
            Qt.ToolButtonTextUnderIcon: (3, 'Texts below icons')
        }[self.FileBar.toolButtonStyle()]

    def toggleUseThread(self):
        ComputationTaskManager.toggleUseThread(self)

    def toggleFitAnimationView(self):
        self.fitAnimationView = not self.fitAnimationView

    def textEdited(self):
        if self.textEditionWatch:
            self.currentSimulation().textEdited()

    def projectEdited(self):
        if self.textEditionWatch:
            self.currentSimulation().setEdited(True)

    def projectParameterEdited(self, primitiveChanged=False):
        if self.currentSimulation().autorun:
            if not self.isRunning():
                self.run(True, primitiveChanged)
            else:
                self.shouldrerun = True
                if hasattr(self, 'primitiveChanged'):
                    self.primitiveChanged |= primitiveChanged
                else:
                    self.primitiveChanged = primitiveChanged

    def endTaskCheck(self, task):
        if hasattr(task, 'checkRerun'):
            if hasattr(self, 'shouldrerun'):
                del self.shouldrerun
                primitiveChanged = self.primitiveChanged
                del self.primitiveChanged
                self.run(True, primitiveChanged)

    def printTitle(self):
        fname = self.currentSimulation().getFileName()
        self.setWindowFilePath(fname)
        t = 'L-Py - '
        t += self.currentSimulation().getTabName()
        self.setWindowTitle(t)
        self.setWindowIcon(self.currentSimulation().generateIcon())

    def printCode(self):
        printer = QPrinter()
        dialog = QPrintDialog(printer, self)
        dialog.setWindowTitle("Print Document")
        if dialog.exec_() != QDialog.Accepted: return
        self.codeeditor.print_(printer)

    def createNewLsystem(self, fname=None):
        i = len(self.simulations)
        self.simulations.append(LpySimulation(self, i, fname))
        self.currentSimulationId = i
        self.currentSimulation().registerTab()

    def setTimeStep(self, val):
        if isinstance(val, int):
            self.currentSimulation().timestep = val
        else:
            self.currentSimulation().timestep = val * 1000
        t = self.currentSimulation().timestep
        if t != self.animtimestep:
            self.animtimestep.setValue(t)
        if t * 0.001 != self.animtimeSpinBox:
            self.animtimeSpinBox.setValue(t * 0.001)
        if self.currentSimulation().lsystem:
            self.currentSimulation().lsystem.context(
            ).animation_timestep = t * 0.001
            self.projectEdited()

    def newfile(self):
        self.acquireCR()
        if not self.currentSimulationId is None and self.currentSimulationId > 0:
            self.currentSimulation().saveState()
        self.createNewLsystem()
        self.releaseCR()
        self.currentSimulation().restoreState()

    def recoverPreviousFiles(self):
        from . import lpytmpfile as tf
        import os
        torecover = tf.getPreviousTmpLpyFiles()
        nbrecoverfile = len(torecover)
        if nbrecoverfile > 0:
            answer = QMessageBox.warning(
                self, "Recovery mode",
                "Backup files exist (%s). Do you want to recover ?" %
                nbrecoverfile, QMessageBox.Ok, QMessageBox.Discard)
            recover = (answer == QMessageBox.Ok)
            for f in torecover:
                if recover:
                    self.acquireCR()
                    try:
                        self.currentSimulation().saveState()
                        self.createNewLsystem()
                        self.currentSimulation().importtmpfile(f)
                        self.currentSimulation().restoreState()
                        self.statusBar().showMessage("Load file '" + f + "'",
                                                     2000)
                        if len(self.simulations
                               ) == 2 and self.simulations[0].isDefault():
                            self.closeDocument(0)
                    except:
                        self.graberror()
                    self.releaseCR()
                else:
                    os.remove(f)

    def getSimuIndex(self, fname):
        for sim in self.simulations:
            if sim.fname == fname:
                return sim.index
        return None

    def openfile(self, fname=None):
        if fname is None:
            initialname = os.path.dirname(self.currentSimulation(
            ).fname) if self.currentSimulation().fname else '.'
            fname = QFileDialog.getOpenFileName(
                self, "Open  L-Py file", initialname,
                "L-Py Files (*.lpy);;All Files (*.*)")
            if not fname: return
            fname = fname[0]
            self.appendInHistory(fname)
        else:
            if not os.path.exists(fname):
                self.removeInHistory(fname)
                QMessageBox.warning(
                    self, "Inexisting file",
                    "File '" + fname + "' does not exist anymore.",
                    QMessageBox.Ok)
                fname = None
            else:
                self.appendInHistory(fname)
        if fname:
            ind = self.getSimuIndex(fname)
            if not ind is None:
                self.simulations[ind].makeCurrent()
            else:
                self.acquireCR()
                self.showfilecontent(fname)
                self.releaseCR()

    def showfilecontent(self, fname):
        try:
            self.currentSimulation().saveState()
            self.createNewLsystem(fname)
            self.currentSimulation().restoreState()
            self.statusBar().showMessage("Load file '" + fname + "'", 2000)
            if len(self.simulations) == 2 and self.simulations[0].isDefault():
                self.closeDocument(0)
        except:
            self.graberror()

    def savefile(self):
        self.currentSimulation().save()

    def saveas(self):
        self.currentSimulation().saveas()

    def saveallfiles(self):
        nbsaving = 0
        for sim in self.simulations:
            if sim.isEdited():
                sim.save()
                nbsaving += 1
        self.saveSession()
        self.statusBar().showMessage("No file to save." if nbsaving ==
                                     0 else "%i file(s) saved." % nbsaving)

    def importcpfgfile(self, fname=None):
        if fname is None:
            initialname = os.path.dirname(self.currentSimulation(
            ).fname) if self.currentSimulation().fname else '.'
            fname = QFileDialog.getOpenFileName(
                self, "Open  Cpfg File", initialname,
                "Cpfg Files (*.l);;All Files (*.*)")
            if not fname: return
            fname = str(fname[0])
        elif not os.path.exists(fname):
            QMessageBox.warning(self, "Inexisting file",
                                "File '" + fname + "' does not exist anymore.",
                                QMessageBox.Ok)
            fname = None
        if fname:
            self.importcpfgproject(fname)

    def importcpfgproject(self, fname=None):
        if fname is None:
            initialname = os.path.dirname(self.currentSimulation(
            ).fname) if self.currentSimulation().fname else '.'
            fname = QFileDialog.getExistingDirectory(self,
                                                     "Open  Cpfg Project",
                                                     initialname)
            if not fname: return
            fname = str(fname)
        elif not os.path.exists(fname):
            QMessageBox.warning(self, "Inexisting file",
                                "File '" + fname + "' does not exist anymore.",
                                QMessageBox.Ok)
            fname = None
        if fname:
            ind = self.getSimuIndex(fname)
            if not ind is None:
                self.simulations[ind].makeCurrent()
            else:
                self.acquireCR()
                try:
                    self.currentSimulation().saveState()
                    self.createNewLsystem()
                    self.currentSimulation().importcpfgproject(fname)
                    self.currentSimulation().restoreState()
                    self.statusBar().showMessage("Load file '" + fname + "'",
                                                 2000)
                    if len(self.simulations
                           ) == 2 and self.simulations[0].isDefault():
                        self.closeDocument(0)
                except:
                    self.graberror()
                self.releaseCR()

    def run(self, rerun=False, primitiveChanged=False):
        self.acquireCR()
        try:
            if not self.use_own_view3D:
                self.viewAbortFunc.reset()
            simu = self.currentSimulation()
            self.viewer.start()
            if not rerun:
                self.viewer.setAnimation(eStatic)
            else:
                if primitiveChanged or simu.getOptimisationLevel() < 2:
                    self.viewer.setAnimation(eAnimatedPrimitives)
                else:
                    self.viewer.setAnimation(eAnimatedScene)
            simu.updateLsystemCode()
            simu.isTextEdited()
            task = ComputationTask(simu.run,
                                   simu.post_run,
                                   simu.pre_run,
                                   cleanupprocess=simu.cleanup)
            task.checkRerun = True
            task.fitRunView = self.fitRunView
            self.registerTask(task)
        except:
            self.graberror()
            self.releaseCR()

    def step(self):
        self.acquireCR()
        simu = self.currentSimulation()
        if not self.use_own_view3D:
            self.viewAbortFunc.reset()
        try:
            task = ComputationTask(simu.step,
                                   simu.post_step,
                                   simu.pre_step,
                                   cleanupprocess=simu.cleanup)
            task.fitRunView = self.fitRunView
            self.registerTask(task)
        except:
            self.graberror()
            self.releaseCR()

    def stepInterpretation(self):
        self.acquireCR()
        simu = self.currentSimulation()
        if not self.use_own_view3D:
            self.viewAbortFunc.reset()
        try:
            task = ComputationTask(simu.stepInterpretation,
                                   simu.post_stepInterpretation,
                                   simu.pre_stepInterpretation,
                                   cleanupprocess=simu.cleanup)
            task.fitRunView = self.fitRunView
            self.registerTask(task)
        except:
            self.graberror()
            self.releaseCR()

    def iterateTo(self):
        simu = self.currentSimulation()
        initval = simu.iterateStep
        if initval is None: initval = 1
        val, ok = QInputDialog.getInteger(self, "Number of Iterations",
                                          "Choose number of iterations",
                                          initval, 1)
        if ok:
            simu.iterateStep = val
            self.nextIterate()

    def nextIterate(self):
        simu = self.currentSimulation()
        if simu.iterateStep is None:
            self.iterateTo()
        else:
            self.acquireCR()
            if not self.use_own_view3D:
                self.viewAbortFunc.reset()
            simu = self.currentSimulation()
            try:
                task = ComputationTask(simu.iterate,
                                       simu.post_step,
                                       simu.pre_step,
                                       cleanupprocess=simu.cleanup)
                task.fitRunView = self.fitRunView
                self.registerTask(task)
            except:
                self.graberror()
                self.releaseCR()

    def debug(self):
        if self.debugMode == True:
            next(self.debugger)
        else:
            self.debugMode = True
            self.acquireCR()
            if not self.use_own_view3D:
                self.viewAbortFunc.reset()
            simu = self.currentSimulation()
            try:
                simu.debug()
            except:
                self.graberror(displayDialog=False)
            self.releaseCR()
            self.debugMode = False

    def profile(self):
        self.profilerDock.show()
        self.acquireCR()
        simu = self.currentSimulation()
        if not self.use_own_view3D:
            self.viewAbortFunc.reset()
        try:
            task = ComputationTask(simu.profile,
                                   simu.post_profile,
                                   simu.pre_profile,
                                   cleanupprocess=simu.cleanup)
            task.mode = self.profilingMode
            task.fitRunView = self.fitRunView
            task.profileView = self.profileView
            self.registerTask(task)
        except:
            self.graberror()
            self.releaseCR()

    def rewind(self):
        self.acquireCR()
        try:
            self.currentSimulation().rewind()
        except:
            self.graberror()
        self.releaseCR()

    def animate(self):
        self.acquireCR()
        simu = self.currentSimulation()
        if not self.use_own_view3D:
            self.viewAbortFunc.reset()
        try:
            task = ComputationTask(simu.animate,
                                   simu.post_animate,
                                   simu.pre_animate,
                                   cleanupprocess=simu.cleanup)
            task.fitAnimationView = self.fitAnimationView
            self.registerTask(task)
        except:
            self.graberror()
            self.releaseCR()

    def record(self):
        fname = '.'
        if len(self.currentSimulation().fname) > 0:
            fname = os.path.splitext(
                self.currentSimulation().fname)[0] + '.png'
        fname = QFileDialog.getSaveFileName(
            self, 'Choose template image file name', fname,
            'Images (*.png,*.bmp,*.jpg);;All Files (*)')
        if fname:
            fname = str(fname[0])
            self.acquireCR()
            simu = self.currentSimulation()
            if not self.use_own_view3D:
                self.viewAbortFunc.reset()
            try:
                task = ComputationTask(simu.animate,
                                       simu.post_animate,
                                       simu.pre_animate,
                                       cleanupprocess=simu.cleanup)
                task.fitAnimationView = self.fitAnimationView
                task.recording = os.path.splitext(fname)[0] + '-'
                task.recording_suffix = os.path.splitext(fname)[1][1:]
                self.registerTask(task)
            except:
                self.graberror()
                self.releaseCR()

    def clear(self):
        self.acquireCR()
        try:
            self.currentSimulation().clear()
        except:
            self.graberror()
        self.releaseCR()

    def appendInHistory(self, fname):
        if fname is None:
            print('Wrong added file in history')
        fname = str(fname)
        if not fname in self.history:
            self.history.insert(0, fname)
        elif fname == self.history[0]:
            self.history.remove(fname)
            self.history.insert(0, fname)
        if len(self.history) > self.historymaxsize:
            del self.history[self.historymaxsize:]
        self.createRecentMenu()

    def removeInHistory(self, fname):
        fname = str(fname)
        if fname in self.history:
            self.history.remove(fname)
            self.createRecentMenu()

    def createRecentMenu(self):
        self.menuRecents.clear()
        icon = QIcon()
        icon.addPixmap(QPixmap(":/images/icons/codefile.png"), QIcon.Normal,
                       QIcon.Off)
        if len(self.history) > 0:
            for f in self.history:
                action = QAction(os.path.basename(f), self.menuRecents)
                action.setData(to_qvariant(f))
                action.setIcon(icon)
                self.menuRecents.addAction(action)
            self.menuRecents.addSeparator()
        self.menuRecents.addAction(self.actionClear)

    def createTutorialMenu(self):
        self.menuTutorials.clear()
        iconfile = QIcon()
        iconfile.addPixmap(QPixmap(":/images/icons/codefile.png"),
                           QIcon.Normal, QIcon.Off)
        iconfolder = QIcon()
        iconfolder.addPixmap(QPixmap(":/images/icons/fileopen.png"),
                             QIcon.Normal, QIcon.Off)
        from openalea.lpy.gui.shared_data import shared_data
        import openalea.lpy
        import os
        if 'CONDA_PREFIX' in os.environ:
            if sys.platform == 'win32':
                shared_data_path = os.path.join(os.environ['CONDA_PREFIX'],
                                                'Library', 'share', 'lpy',
                                                'tutorial')
            else:
                shared_data_path = os.path.join(os.environ['CONDA_PREFIX'],
                                                'share', 'lpy', 'tutorial')
        else:
            shared_data_path = shared_data(openalea.lpy,
                                           share_path='share/tutorial')
        if not shared_data_path is None and os.path.exists(shared_data_path):
            import os
            cpath = os.path.abspath(shared_data_path)
            cmenu = self.menuTutorials
            toprocess = [(cpath, cmenu)]
            while len(toprocess) > 0:
                cpath, cmenu = toprocess.pop(0)
                csubpath = os.listdir(cpath)
                csubpath.sort()
                for fname in csubpath:
                    absfname = os.path.join(cpath, fname)
                    if os.path.isdir(absfname):
                        childmenu = cmenu.addMenu(iconfolder,
                                                  os.path.basename(str(fname)))
                        toprocess.append((absfname, childmenu))
                        childmenu.triggered.connect(self.recentMenuAction)
                    elif fname[-4:] == '.lpy':
                        action = QAction(os.path.basename(str(fname)), cmenu)
                        action.setData(to_qvariant(absfname))
                        action.setIcon(iconfile)
                        cmenu.addAction(action)
        else:
            self.menuFile.removeAction(self.menuTutorials.menuAction())

    def recentMenuAction(self, action):
        self.openfile(str(action.data()))

    def clearHistory(self):
        self.history = []
        self.createRecentMenu()

    def setSyntaxHighLightActivation(self, value):
        self.textEditionWatch = False
        self.codeeditor.setSyntaxHighLightActivation(value)
        self.textEditionWatch = True

    def setTabHighLightActivation(self, value):
        self.textEditionWatch = False
        self.codeeditor.setTabHighLightActivation(value)
        self.textEditionWatch = True

    def setCCompilerPath(self, p):
        self.cCompilerPath = str(p)
        if len(self.cCompilerPath
               ) != 0 and not self.cCompilerPath in os.environ['PATH']:
            os.environ['PATH'] += ';' + self.cCompilerPath

    def executeCode(self):
        cmd = self.codeeditor.codeToExecute()
        #print '... '+'\n... '.join(cmd.splitlines())
        #self.interpreter.runcode(cmd)
        self.shellwidget.execute(cmd)
        cursor = self.codeeditor.textCursor()
        cursor.movePosition(QTextCursor.Down)
        self.codeeditor.setTextCursor(cursor)

    def submitBug(self):
        import webbrowser
        webbrowser.open("https://github.com/openalea/lpy/issues")

    def onlinehelp(self):
        import webbrowser
        webbrowser.open("https://lpy-fb.readthedocs.io/")

    def initSVNMenu(self):
        from . import svnmanip
        if not svnmanip.hasSvnSupport():
            self.menuSVN.setEnabled(False)
        else:
            self.menuSVN.aboutToShow.connect(self.updateSVNMenu)

            self.actionSVNUpdate.triggered.connect(self.svnUpdate)
            self.actionSVNCommit.triggered.connect(self.svnCommit)
            self.actionSVNRevert.triggered.connect(self.svnRevert)
            self.actionSVNAdd.triggered.connect(self.svnAdd)
            self.actionSVNIsUpToDate.triggered.connect(self.svnIsUpToDate)

    def updateSVNMenu(self):
        from . import svnmanip
        import os
        if svnmanip.hasSvnSupport():
            fname = self.currentSimulation().fname

            if not fname or not svnmanip.isSvnFile(fname):
                for action in [
                        self.actionSVNUpdate, self.actionSVNCommit,
                        self.actionSVNRevert, self.actionSVNIsUpToDate
                ]:
                    action.setEnabled(False)

                self.actionSVNAdd.setEnabled(
                    not fname is None
                    and svnmanip.isSvnFile(os.path.dirname(fname)))
            else:
                status = svnmanip.svnFileTextStatus(fname)
                isadded = (status == svnmanip.added)
                ismodified = (status == svnmanip.modified)
                isnormal = (status == svnmanip.normal)

                self.actionSVNUpdate.setEnabled(not isadded)
                self.actionSVNIsUpToDate.setEnabled(not isadded)
                self.actionSVNCommit.setEnabled(ismodified or isadded)
                self.actionSVNRevert.setEnabled(not isnormal)
                self.actionSVNAdd.setEnabled(False)

    def svnUpdate(self):
        self.currentSimulation().svnUpdate()

    def svnIsUpToDate(self):
        self.currentSimulation().svnIsUpToDate()

    def svnAdd(self):
        self.currentSimulation().svnAdd()

    def svnRevert(self):
        self.currentSimulation().svnRevert()

    def svnCommit(self):
        self.currentSimulation().svnCommit()
Пример #21
0
class Margin(QWidget):
    
    lineClicked = pyqtSignal(int)

    def __init__(self,parent,editor):
        QWidget.__init__(self,parent)
        self.editor = editor
        self.showLines = True
        self.markers = {}
        self.markerStack = {}
        self.markerType = {}

    def paintEvent( self, paintEvent ):
        if self.showLines:
            maxheight = self.editor.viewport().height()
            maxline = self.editor.document().blockCount()
            painter = QPainter(self)
            painter.setPen(QPen(QColor(100,100,100)))
            h = 0
            line = -1
            while h < maxheight and line < maxline:
                cursor = self.editor.cursorForPosition(QPoint(1,h))
                nline = cursor.blockNumber()+1
                rect = self.editor.cursorRect(cursor)
                if nline > line:
                    line = nline
                    painter.drawText(0,rect.top()+2,40,rect.height()+2, Qt.AlignHCenter|Qt.AlignTop,str(line))
                    m = self.markers.get(line,None)
                    if not m is None:
                        lm = self.markerStack.get(line,None)
                        if not lm is None:
                            for slm in lm:
                                painter.drawPixmap(32,rect.top()+2,self.markerType[slm])
                        painter.drawPixmap(32,rect.top()+2,self.markerType[m])
                h = rect.top()+rect.height()+1
            painter.end()
    
    def mousePressEvent( self, event ):
        line = self.editor.cursorForPosition(event.pos()).blockNumber() 
        self.lineClicked.emit(line+1)
        #self.emit(SIGNAL("lineClicked(int)"),)
    def clear( self ):
        self.removeAllMarkers()
        self.markerType = {}
    def hasMarker(self):
        return len(self.markers) != 0
    def setMarkerAt(self,line,id):
        self.markers[line] = id
        if line in self.markerStack:
            del self.markerStack[line]
        self.update()
    def hasMarkerAt(self,line):
        return line in self.markers
    def hasMarkerTypeAt(self,line,id):
        if line in self.markers :
            if self.markers[line] == id: return True
            if line in self.markerStack:
                if id in self.markerStack[line]:
                    return True
        return False
    def getCurrentMarkerAt(self,line):
        return self.markers[line]
    def removeCurrentMarkerAt(self,line):
        del self.markers[line]
        if line in self.markerStack:
            self.markers[line] = self.markerStack[line].pop()
            if len(self.markerStack[line]) == 0:
                del self.markerStack[line]
        self.update()
    def removeMarkerTypeAt(self,line,id):
        if self.markers[line] == id:
            self.removeCurrentMarkerAt(line)
        else:
            self.markerStack[line].remove(id)
            if len(self.markerStack[line]) == 0:
                del self.markerStack[line]
        self.update()
    def removeAllMarkersAt(self,line):
        if line in self.marker:
            del self.markers[line]
        if line in self.markerStack:
            del self.markerStack[line]        
        self.update()
    def removeAllMarkers(self):
        self.markers = {}
        self.markerStack = {}        
        self.update()
    def addMarkerAt(self,line,id):
        val = self.markers.get(line,None)
        if not val is None:
            if line not in self.markerStack:
                self.markerStack[line] = []
            self.markerStack[line].append(val)
        self.markers[line] = id
        self.update()    
    def appendMarkerAt(self,line,id):
        val = self.markers.get(line,None)
        if not val is None:
            if line not in self.markerStack:
                self.markerStack[line] = []
            self.markerStack[line].append(id)
        else:
            self.markers[line] = id
        self.update()    
    def defineMarker(self,id,pixmap):
        self.markerType[id] = pixmap
    def getAllMarkers(self,id):
        return set([l for l,lid in self.markers.items() if id == lid]).union(set([l for l,lids in self.markerStack.items() if id in lids]))
    def decalMarkers(self,line,decal = 1):
        markers = {}
        markerStack = {}
        if decal < 0:
          for l,v in self.markers.items():
            if l <= line+decal:
                markers[l] = v
            elif l > line:
                markers[l+decal] = v
          for l,v in self.markerStack.items():
            if l <= line+decal:
                markerStack[l] = v
            elif l > line:
                markerStack[l+decal] = v        
        if decal > 0:
          for l,v in self.markers.items():
            if l < line:
                markers[l] = v
            else:
                markers[l+decal] = v
          for l,v in self.markerStack.items():
            if l < line:
                markerStack[l] = v
            else:
                markerStack[l+decal] = v
        if decal != 0:
            self.markers = markers
            self.markerStack = markerStack
            self.update()
    def saveState(self,obj):
        obj.markersState = (self.markers,self.markerStack)
    def restoreState(self,obj):
        if hasattr(obj,'markersState'):
            self.markers,self.markerStack = obj.markersState
        else:
            self.removeAllMarkers()
Пример #22
0
class NurbsPatchEditor(QGLViewer):
    """ The class NurbsPatchEditor is the viewer of the scene, it contains all the informations about the NurbsPatch, that's why a getter and a setter have been created, the NubrsPatch is defined by a 2 dimensional Array of 3d Vectors"""

    Edit, Rotate = list(range(2))
    valueChanged = pyqtSignal()

    def __init__(self, parent):
        """ Constructor 
            :param parent: the parent widget
        """
        QGLViewer.__init__(self, parent)
        self.setStateFileName('.nurbspatcheditor.xml')

        self.mode = NurbsPatchEditor.Edit

        # the nurbs patch
        self.nurbsPatch = None

        # matrix of ctrl point manipulator
        self.ctrlPointMatrix = []

        # shape and material to display the patch
        self.nurbsShape = Shape()
        self.defaultMaterial = Material((30, 200, 30), 1, transparency=0.5)
        self.nurbsShape.appearance = self.defaultMaterial

        # plantgl basic object to display nurbs
        self.discretizer = Discretizer()
        self.glrenderer = GLRenderer(self.discretizer)
        self.glrenderer.renderingMode = GLRenderer.Dynamic
        self.ctrlrenderer = GLCtrlPointRenderer(self.discretizer)
        self.bboxcomputer = BBoxComputer(self.discretizer)

        #   Current selection
        self.selectionManipulator = None
        self.focus = None

        # rectangular selection
        self.selectionRect = False  # for rectangular selection (with control)
        self.__rectangle = QRect()
        self.__rectangleInit = None

        #creation of a default NurbsPatch
        if len(self.ctrlPointMatrix) == 0:
            self.setNurbsPatch(self.newDefaultNurbsPatch())

    def __propagate_valuechanged__(self, pid=None):
        """ emit a SIGNAL every time a value changed """
        self.setSceneBoundingBox(*self.getBounds())
        self.valueChanged.emit(
        )  # self.emit(SIGNAL("valueChanged()")) # AUTO SIGNAL TRANSLATION

    def init(self):
        """ init function """
        self.setSceneBoundingBox(*self.getBounds())
        self.restoreStateFromFile()

        # init mouse interaction
        self.mode = NurbsPatchEditor.Edit
        self.setInteractionMode(False)

        self.setMouseBindingDescription(Qt.ShiftModifier + Qt.LeftButton,
                                        "Rectangular selection")
        self.setMouseBindingDescription(Qt.LeftButton,
                                        "Camera/Control Points manipulation")
        self.setMouseBindingDescription(
            Qt.LeftButton, "When double clicking on a line, create a new line",
            True)

    def getBounds(self):
        """ Get the Bounding Box of the scene:
            return the minpos and maxpos that can be used by setSceneBoundingBox function"""
        self.nurbsPatch.apply(self.bboxcomputer)
        res = self.bboxcomputer.result
        return Vec(*res.lowerLeftCorner), Vec(*res.upperRightCorner)

    def setFocus(self, point):
        """ Set focus to given control point """
        if self.focus:
            self.focus.hasFocus = False
        self.focus = point
        if self.focus:
            point.hasFocus = True

    def clearSelectionManipulator(self):
        """ clear the selection manipulator """
        if self.selectionManipulator:
            self.selectionManipulator.clear()
            self.selectionManipulator.valueChanged.disconnect(
                self.__propagate_valuechanged__
            )  # QObject.disconnect(self.selectionManipulator,SIGNAL("valueChanged()"),self.__propagate_valuechanged__)
            self.selectionManipulator = None

    def createSelectionManipulator(self):
        """ ensure that the selection manipulator is existing and valid """
        if not self.selectionManipulator:
            self.selectionManipulator = SelectionManipulator()
            self.selectionManipulator.valueChanged.connect(
                self.__propagate_valuechanged__
            )  # QObject.connect(self.selectionManipulator,SIGNAL("valueChanged()"),self.__propagate_valuechanged__)

    def mousePressEvent(self, event):
        """ Check for eventual operations the user asks: 
            shift start rectangular selection
            else check for which point is selected
        """
        #Rectangular selection
        if event.modifiers() == Qt.ShiftModifier:
            if event.button() == Qt.LeftButton:
                self.__rectangle = QRect(event.pos(), event.pos())
                self.__rectangleInit = event.pos()
                self.selectionRect = True
            elif event.button() == Qt.RightButton:
                self.clearSelectionManipulator()
            self.updateGL()
        else:
            pointSelection = False
            if self.selectionManipulator:
                pointSelection = self.selectionManipulator.checkIfAxisGrabsMouse(
                    event, self)
            if not pointSelection:
                for ctrlPointRow in self.ctrlPointMatrix:
                    for cCtrlPoint in ctrlPointRow:
                        cCtrlPoint.checkIfGrabsMouse(event.x(), event.y(),
                                                     self.camera())
                        if cCtrlPoint.grabsMouse():
                            pointSelection = True
                            self.setFocus(cCtrlPoint)
                            if self.selectionManipulator and cCtrlPoint.selected:
                                self.setManipulatedFrame(
                                    self.selectionManipulator)
                            else:
                                self.setManipulatedFrame(cCtrlPoint)
                            break
            self.setInteractionMode(pointSelection)
            self.updateGL()

        QGLViewer.mousePressEvent(self, event)

    def setInteractionMode(self, frame=True):
        if frame:
            if self.mode != NurbsPatchEditor.Edit:
                self.mode = NurbsPatchEditor.Edit
                self.setMouseBinding(Qt.LeftButton, QGLViewer.FRAME,
                                     QGLViewer.TRANSLATE)
                self.setMouseBinding(Qt.RightButton, QGLViewer.FRAME,
                                     QGLViewer.NO_MOUSE_ACTION)

                self.setMouseBinding(Qt.ControlModifier + Qt.LeftButton,
                                     QGLViewer.CAMERA, QGLViewer.ROTATE)
                self.setMouseBinding(Qt.ControlModifier + Qt.RightButton,
                                     QGLViewer.CAMERA, QGLViewer.TRANSLATE)
                self.setMouseBinding(Qt.ControlModifier + Qt.MiddleButton,
                                     QGLViewer.CAMERA, QGLViewer.ZOOM)
        else:
            if self.mode != NurbsPatchEditor.Rotate:
                self.mode = NurbsPatchEditor.Rotate
                self.setMouseBinding(Qt.ControlModifier + Qt.LeftButton,
                                     QGLViewer.FRAME, QGLViewer.TRANSLATE)
                self.setMouseBinding(Qt.ControlModifier + Qt.RightButton,
                                     QGLViewer.FRAME,
                                     QGLViewer.NO_MOUSE_ACTION)
                self.setMouseBinding(Qt.LeftButton, QGLViewer.CAMERA,
                                     QGLViewer.ROTATE)
                self.setMouseBinding(Qt.RightButton, QGLViewer.CAMERA,
                                     QGLViewer.TRANSLATE)
                self.setMouseBinding(Qt.MiddleButton, QGLViewer.CAMERA,
                                     QGLViewer.ZOOM)

    def mouseDoubleClickEvent(self, event):
        """ mouseDoubleClickEvent: add a control point to the selection double clicking on it, empty the selection double clicking elsewhere """
        if event.modifiers() & Qt.ShiftModifier:
            selection = False
            #Doubleclick on a control point add/delete it from the selection array#
            for ctrlPointRow in self.ctrlPointMatrix:
                for cCtrlPoint in ctrlPointRow:
                    cCtrlPoint.checkIfGrabsMouse(event.x(), event.y(),
                                                 self.camera())
                    if cCtrlPoint.grabsMouse():
                        cCtrlPoint.selected = not cCtrlPoint.selected
                        selection = True
                        self.createSelectionManipulator()
                        self.selectionManipulator.toogleSelection(cCtrlPoint)
            #Double click with shift anywhere but on a control point -> empty selection #
            if not selection:
                self.clearSelectionManipulator()
        else:
            ## Adding a column on the NurbsPatch#
            """ when double click on a line, the line is doubled """
            self.select(
                event.pos()
            )  # select call drwWithName that render line of control points with ids
            selection = self.selectedName()
            if selection >= 0:
                nbcolumn = self.nurbsPatch.ctrlPointMatrix.getColumnNb()
                minp, maxp = self.getBounds()
                diff = norm(maxp - minp) / 20
                if selection >= nbcolumn:
                    row = selection - nbcolumn
                    newpatch = addRowToPatch(self.nurbsPatch, row,
                                             Vector4(diff, 0))
                    selectedPoints = [(row, i) for i in range(nbcolumn)]
                else:
                    newpatch = addColumnToPatch(self.nurbsPatch, selection,
                                                Vector4(diff, 0))
                    selectedPoints = [(i, selection) for i in range(
                        self.nurbsPatch.ctrlPointMatrix.getRowNb())]
                self.setNurbsPatch(newpatch)
                self.createSelectionManipulator()
                for index in selectedPoints:
                    self.selectionManipulator.add(
                        self.ctrlPointMatrix[index[0]][index[1]])
            else:
                self.clearSelectionManipulator()

    def mouseMoveEvent(self, event):
        """ mouseMoveEvent """

        #rectangular selection
        if self.selectionRect:
            self.__rectangle = QRect(self.__rectangleInit,
                                     event.pos()).normalized()
            self.updateGL()

        # by default camera or manipulated frame is manipulated
        QGLViewer.mouseMoveEvent(self, event)

    def mouseReleaseEvent(self, event):
        """On mouse release, we release every grabbed objects"""
        QGLViewer.mouseReleaseEvent(self, event)
        # clear manipulated object
        self.setManipulatedFrame(None)
        self.setFocus(None)
        if self.selectionManipulator:
            self.selectionManipulator.clearFocus()

        # end of rectangular selection :
        if self.selectionRect:
            self.selectionRect = False
            # Possibly swap left/right and top/bottom to make rectangle_ valid.
            self.__rectangle = QRect(self.__rectangleInit,
                                     event.pos()).normalized()
            # make rectangle with a minimal size
            if self.__rectangle.width() < 10 or self.__rectangle.height() < 10:
                self.__rectangle = QRect(event.pos().x() - 5,
                                         event.pos().y() - 5, 10, 10)
            self.selectionFromRect(self.__rectangle)
        self.setInteractionMode(False)
        self.updateGL()

    def selectionFromRect(self, rect):
        """ check if control point projections are in the given rectangle. If yes, put point in selection """
        for ctrlPointRow in self.ctrlPointMatrix:
            for cCtrlPoint in ctrlPointRow:
                point = self.camera().projectedCoordinatesOf(
                    cCtrlPoint.position())
                if (self.__rectangle.contains(point.x, point.y)):
                    if not self.selectionManipulator:
                        self.selectionManipulator = SelectionManipulator()
                        self.selectionManipulator.valueChanged.connect(
                            self.__propagate_valuechanged__
                        )  # QObject.connect(self.selectionManipulator,SIGNAL("valueChanged()"),self.__propagate_valuechanged__)
                    self.selectionManipulator.toogleSelection(cCtrlPoint)
        if self.selectionManipulator and self.selectionManipulator.empty():
            self.clearSelectionManipulator()

    def draw(self):
        """ 
        Draw function :
        Control points of the nurbsPatch are represented by spheres.
        If some of them are selected, a medium axis will be drawn by lines and little spheres, 
        allowing us to grab one of them to perform a constrained move
        """
        scradius = self.sceneRadius()
        pointsize = scradius / 30.

        shapes = []
        #define the nurbsShape and the control points geometry
        #self.p=[]
        for ctrlPointLine in self.ctrlPointMatrix:
            for ctrlPoint in ctrlPointLine:
                shapes.append(ctrlPoint.representation(pointsize))

        # draw the frame of multiple selection
        if self.selectionManipulator:
            shapes += self.selectionManipulator.representation(pointsize * 1.2)

        shapes.append(self.nurbsShape)
        sc = Scene(shapes)

        ogl.glDisable(ogl.GL_LIGHTING)
        ogl.glPolygonMode(ogl.GL_FRONT_AND_BACK, ogl.GL_LINE)
        # draw the wire of the patch
        ogl.glLineWidth(1)
        shwire = Shape(self.nurbsPatch, Material((120, 255, 0), 0.8))
        shwire.apply(self.glrenderer)
        # ctrl line rendering
        ogl.glLineWidth(1)
        Material((255, 255, 255), 1).apply(self.glrenderer)
        self.nurbsPatch.apply(self.ctrlrenderer)
        # patch and control point shpere rendering
        ogl.glPolygonMode(ogl.GL_FRONT_AND_BACK, ogl.GL_FILL)
        ogl.glEnable(ogl.GL_LIGHTING)
        ogl.glLightModeli(ogl.GL_LIGHT_MODEL_TWO_SIDE, ogl.GL_TRUE)
        ogl.glEnable(ogl.GL_BLEND)
        ogl.glBlendFunc(ogl.GL_SRC_ALPHA, ogl.GL_ONE_MINUS_SRC_ALPHA)
        sc.apply(self.glrenderer)
        ogl.glDisable(ogl.GL_BLEND)

        if self.selectionRect:
            # Draw rectangular selection
            self.startScreenCoordinatesSystem()  # use screen coordinates
            r = self.__rectangle
            rect = Shape(
                Polyline2D([(r.x(), r.y()), (r.x() + r.width(), r.y()),
                            (r.x() + r.width(), r.y() + r.height()),
                            (r.x(), r.y() + r.height()), (r.x(), r.y())]),
                Material((200, 200, 200), 1))
            ogl.glEnable(ogl.GL_LINE_STIPPLE)
            ogl.glLineStipple(1, 0x0FFF)
            ogl.glLineWidth(2)
            rect.apply(self.glrenderer)
            ogl.glDisable(ogl.GL_LINE_STIPPLE)
            self.stopScreenCoordinatesSystem()
        ogl.glLineWidth(1)

    def drawWithNames(self):
        """ draw control lines with names """
        pointmatrix = self.nurbsPatch.ctrlPointMatrix
        lines = []
        m = Material()
        maxcolumn = pointmatrix.getColumnNb()
        for colid in range(maxcolumn):
            lines.append(
                Shape(
                    Polyline(Point4Array(
                        pointmatrix.getColumn(colid)).project(),
                             width=3), m, colid))
        for rowid in range(pointmatrix.getRowNb()):
            lines.append(
                Shape(
                    Polyline(Point4Array(pointmatrix.getRow(rowid)).project(),
                             width=3), m, maxcolumn + rowid))
        for sh in lines:
            ogl.glPushName(sh.id)
            sh.apply(self.glrenderer)
            ogl.glPopName()

    @staticmethod
    def newDefaultNurbsPatch():
        """ return a default nurbs patch """
        return NurbsPatch([[Vector4(j - 1.5, i - 1.5, 0, 1) for j in range(4)]
                           for i in range(4)])

    def getNurbsPatch(self):
        """ return the edited nurbs patch """
        return self.nurbsPatch

    def setNurbsPatch(self, nurbsPatch):
        """ set the nurbs patch to edit """
        self.clear()
        self.nurbsPatch = nurbsPatch
        self.nurbsShape.geometry = self.nurbsPatch
        nbLines = float(len(self.nurbsPatch.ctrlPointMatrix))
        pid = 1
        for j, linePoint in enumerate(self.nurbsPatch.ctrlPointMatrix):
            lineCtrlPoint = []
            nbCols = len(linePoint)
            for i in range(nbCols):
                ctrlPoint = CtrlPoint(linePoint[i].project(),
                                      Pos4Setter(
                                          self.nurbsPatch.ctrlPointMatrix,
                                          (j, i)),
                                      color=(30 + int(220 * j / nbLines),
                                             30 + int(220 * i / nbCols), 250),
                                      id=pid)
                pid += 1
                ctrlPoint.setCallBack(self.__propagate_valuechanged__)
                lineCtrlPoint.append(ctrlPoint)
            self.ctrlPointMatrix.append(lineCtrlPoint)
        self.setSceneBoundingBox(*self.getBounds())

    def clear(self):
        """ clear current edition """
        self.ctrlPointMatrix = []
        self.clearSelectionManipulator()

    def helpString(self):
        return helpstr

    def closeEvent(self, event):
        """close the windows properly"""
        helpwidget = self.helpWidget()
        if helpwidget and helpwidget.isVisible():
            helpwidget.hide()
        QGLViewer.closeEvent(self, event)