def make_hideable(node, show=True): """ Creates a SoSwitch with node as children, and puts it in property node.root """ switch = SoSwitch() switch.addChild(node) node.root = switch def getVisible(self): return self.root.whichChild.getValue() != SO_SWITCH_NONE def setVisible(self, val): self.root.whichChild = SO_SWITCH_ALL if val else SO_SWITCH_NONE node.__class__.visible = property(getVisible, setVisible) node.visible = show return node
class BaseObject(object): """Common functionality for all graphic objects. It's just a wrapper around a SoSeparator It has this OI structure: Switch { Separator { DrawStyle {} Translation { translation 0 0 0 } } } Has methods for hide/show itself, and for translation """ def __init__(self): self.animation = None self.root = SoSwitch() self.separator = SoSeparator() self.drawStyle = SoDrawStyle() self.translation = SoTranslation() self.root.addChild(self.separator) self.separator.addChild(self.drawStyle) self.separator.addChild(self.translation) self.translation.translation = (0, 0, 0) self.show() @fluid def setVisible(self, visible): self.root.whichChild = SO_SWITCH_ALL if visible else SO_SWITCH_NONE def getVisible(self): return self.root.whichChild.getValue() == SO_SWITCH_ALL visible = property(fget=getVisible, fset=setVisible) @fluid def show(self): self.setVisible(True) @fluid def hide(self): self.setVisible(False) @fluid def setName(self,name): self.root.setName(name) @fluid def setBoundingBox(self, xrange=None, yrange=None, zrange=None): """ @param xrange: 2-tuple @param yrange: 2-tuple @param zrange: 2-tuple """ def createPlane(normalref, point): sfplane = SoSFPlane() sfplane.setValue(SbPlane(normalref, point)) return sfplane if yrange is not None: self.clipPlaneXZ1 = SoClipPlane() self.clipPlaneXZ2 = SoClipPlane() self.clipPlaneXZ1.plane = createPlane(SbVec3f(0, 1, 0), SbVec3f(0, yrange[0], 0)) self.clipPlaneXZ2.plane = createPlane(SbVec3f(0, -1, 0), SbVec3f(0, yrange[1], 0)) self.separator.insertChild(self.clipPlaneXZ1, 0) self.separator.insertChild(self.clipPlaneXZ2, 1) if xrange is not None: self.clipPlaneYZ1 = SoClipPlane() self.clipPlaneYZ2 = SoClipPlane() self.clipPlaneYZ1.plane = createPlane(SbVec3f(1, 0, 0), SbVec3f(xrange[0], 0, 0)) self.clipPlaneYZ2.plane = createPlane(SbVec3f(-1, 0, 0), SbVec3f(xrange[1], 0, 0)) self.separator.insertChild(self.clipPlaneYZ1, 2) self.separator.insertChild(self.clipPlaneYZ2, 3) if zrange is not None: self.clipPlaneXY1 = SoClipPlane() self.clipPlaneXY2 = SoClipPlane() self.clipPlaneXY1.plane = createPlane(SbVec3f(0, 0, 1), SbVec3f(0, 0, zrange[0])) self.clipPlaneXY2.plane = createPlane(SbVec3f(0, 0, -1), SbVec3f(0, 0, zrange[1])) self.separator.insertChild(self.clipPlaneXY1, 4) self.separator.insertChild(self.clipPlaneXY2, 5) @fluid def setDrawStyle(self, style): self.drawStyle.style = style @fluid def setOrigin(self, pos): self.translation.translation = pos def getOrigin(self): return self.translation.translation.getValue() origin = property(fget=getOrigin, fset=setOrigin) def getAnimation(self): return self.animation def resetObjectForAnimation(self): pass def toText(self): """obtains the openinventor format representation""" wa = SoWriteAction() return wa.apply(self.root) @classmethod def Create(cls, oi_object): bo = cls() bo.separator.addChild(oi_object) return bo
class Chapter(QtCore.QObject): """A Chapter""" pageChanged = QtCore.pyqtSignal(Page, int) def __init__(self, name=""): super(Chapter, self).__init__() self.name = name self.book = None self.root = SoSeparator() self.root.setName("Chapter:root") self.pagesSwitch = SoSwitch() self.pagesSwitch.setName("Chapter:pages") self.root.addChild(self.pagesSwitch) self.__pages = nodeDict() ## ============================ self.setupGui() def setupGui(self): ## self.widget has next, prev buttons, plus a QStackedWidget for holding per page controls self.widget = ChangePageUI() ## the initial state self.widget.previous.hide() self.widget.next.hide() connect(self.widget.next, "clicked(bool)", self.nextPage) connect(self.widget.previous, "clicked(bool)", self.prevPage) ## ============================ self.notesStack = QtGui.QStackedWidget() ## ============================ def setBook(self, book): self.book = book @property def pages(self): """The list of pages""" return self.__pages def createPage(self): """ Creates a new page and appends it to this chapter """ page = Page() self.addPage(page) return page def addPage(self, page): """ Adds 'page' to this chapter. page can be a Page or a SoNode. Searches page for a 'getGui' function, which should return a widget. @param page: Page | SoNode """ ## ============================ ## page can be a Page or SoNode root = getattr(page, "root", page) self.pages[root] = page self.pagesSwitch.addChild(root) ## ============================ guiLayout = QtGui.QVBoxLayout() guiLayout.setMargin(0) guiLayout.setSpacing(0) widget = QtGui.QWidget() widget.setObjectName("PageGui") widget.setLayout(guiLayout) self.widget.pageStack.addWidget(widget) ## ============================ notesLayout = QtGui.QVBoxLayout() notesLayout.setMargin(0) notesLayout.setSpacing(0) widget = QtGui.QWidget() widget.setObjectName("PageNotas") widget.setLayout(notesLayout) self.notesStack.addWidget(widget) ## ============================ ## this sets self.pagesSwitch, self.widget.pageStack, self.notasStack ## only change the page if theres a book already if self.book is not None: self.whichPage = len(self.pagesSwitch) - 1 ## ============================ guiLayout.addWidget(page.getGui()) notesLayout.addWidget(page.getNotes()) ## ============================ if len(self.pagesSwitch) == 2: self.widget.previous.show() self.widget.next.show() def getGui(self): return self.widget def getNotes(self): return self.notesStack def chapterSpecificIn(self): """code to be executed whenever the chapter is displayed this is intended for global changes to the scenegraph that are needed by this chapter """ pass @property def page(self): """the current page""" if self.whichPage < 0: return None return self.pages[self.pagesSwitch[self.whichPage]] def getWhichPage(self): """ Returns the index of the current page """ return self.pagesSwitch.whichChild.getValue() def setWhichPage(self, n): """ Activates the n-th page @param n: """ if len(self.pagesSwitch) > 0: self.pagesSwitch.getChild(n) self.pagesSwitch.whichChild = n self.widget.pageStack.setCurrentIndex(n) self.notesStack.setCurrentIndex(n) self.pageChanged.emit(self.page, n) whichPage = property(getWhichPage, setWhichPage) def changePage(self, direction): self.whichPage = (self.whichPage + direction) % len(self.pagesSwitch) def nextPage(self): self.changePage(1) def prevPage(self): self.changePage(-1)
class BaseObject(object): """Common functionality for all graphic objects. It's just a wrapper around a SoSeparator It has this OI structure: Switch { Separator { DrawStyle {} Translation { translation 0 0 0 } } } Has methods for hide/show itself, and for translation """ def __init__(self): self.animation = None self.root = SoSwitch() self.separator = SoSeparator() self.drawStyle = SoDrawStyle() self.translation = SoTranslation() self.root.addChild(self.separator) self.separator.addChild(self.drawStyle) self.separator.addChild(self.translation) self.translation.translation = (0, 0, 0) self.show() @fluid def setVisible(self, visible): self.root.whichChild = SO_SWITCH_ALL if visible else SO_SWITCH_NONE def getVisible(self): return self.root.whichChild.getValue() == SO_SWITCH_ALL visible = property(fget=getVisible, fset=setVisible) @fluid def show(self): self.setVisible(True) @fluid def hide(self): self.setVisible(False) @fluid def setName(self, name): self.root.setName(name) @fluid def setBoundingBox(self, xrange=None, yrange=None, zrange=None): """ @param xrange: 2-tuple @param yrange: 2-tuple @param zrange: 2-tuple """ def createPlane(normalref, point): sfplane = SoSFPlane() sfplane.setValue(SbPlane(normalref, point)) return sfplane if yrange is not None: self.clipPlaneXZ1 = SoClipPlane() self.clipPlaneXZ2 = SoClipPlane() self.clipPlaneXZ1.plane = createPlane(SbVec3f(0, 1, 0), SbVec3f(0, yrange[0], 0)) self.clipPlaneXZ2.plane = createPlane(SbVec3f(0, -1, 0), SbVec3f(0, yrange[1], 0)) self.separator.insertChild(self.clipPlaneXZ1, 0) self.separator.insertChild(self.clipPlaneXZ2, 1) if xrange is not None: self.clipPlaneYZ1 = SoClipPlane() self.clipPlaneYZ2 = SoClipPlane() self.clipPlaneYZ1.plane = createPlane(SbVec3f(1, 0, 0), SbVec3f(xrange[0], 0, 0)) self.clipPlaneYZ2.plane = createPlane(SbVec3f(-1, 0, 0), SbVec3f(xrange[1], 0, 0)) self.separator.insertChild(self.clipPlaneYZ1, 2) self.separator.insertChild(self.clipPlaneYZ2, 3) if zrange is not None: self.clipPlaneXY1 = SoClipPlane() self.clipPlaneXY2 = SoClipPlane() self.clipPlaneXY1.plane = createPlane(SbVec3f(0, 0, 1), SbVec3f(0, 0, zrange[0])) self.clipPlaneXY2.plane = createPlane(SbVec3f(0, 0, -1), SbVec3f(0, 0, zrange[1])) self.separator.insertChild(self.clipPlaneXY1, 4) self.separator.insertChild(self.clipPlaneXY2, 5) @fluid def setDrawStyle(self, style): self.drawStyle.style = style @fluid def setOrigin(self, pos): self.translation.translation = pos def getOrigin(self): return self.translation.translation.getValue() origin = property(fget=getOrigin, fset=setOrigin) def getAnimation(self): return self.animation def resetObjectForAnimation(self): pass def toText(self): """obtains the openinventor format representation""" wa = SoWriteAction() return wa.apply(self.root) @classmethod def Create(cls, oi_object): bo = cls() bo.separator.addChild(oi_object) return bo
def wrap(node, show=True): """wrap node with a SoSwitch, returns the switch""" switch = SoSwitch() switch.addChild(getattr(node, 'root', node)) switch.whichChild = SO_SWITCH_ALL if show else SO_SWITCH_NONE return switch
class Book(QtCore.QObject): """A Book-like object""" chapterChanged = QtCore.pyqtSignal(int) pageChanged = QtCore.pyqtSignal(Page, int) def __init__(self): super(Book, self).__init__() self.root = SoSeparator() self.root.setName("Book:root") self.chapters = SoSwitch() self.chapters.setName("Book:chapters") self.root.addChild(self.chapters) ## this dictionary contains the chapters python objects ## not only the SoSeparator self.chaptersObjects = nodeDict() self.setupGui() def __len__(self): """The number of chapters""" return len(self.chapters) def setupGui(self): ## chapterStack has one widget (of controls) per chapter self.chaptersStack = QtGui.QStackedWidget() self.notesStack = QtGui.QStackedWidget() def createChapter(self): """Creates a new empty Chapter""" chapter = Chapter() self.addChapter(chapter) return chapter def addChapter(self, chapter): """ Appends chapter to this book @param chapter: Chapter """ ## we probably should check that chapter is derived ## from base.Chapter self.chaptersObjects[chapter.pagesSwitch] = chapter self.chapters.addChild(chapter.root) chapter.setBook(self) ## ============================ ## setup the UI self.chaptersStack.addWidget(chapter.getGui()) self.notesStack.addWidget(chapter.getNotes()) self.whichChapter = len(self.chapters) - 1 #======================================================================= chapter.pageChanged.connect(self.relayPageChangedSignal) def relayPageChangedSignal(self, page, n): logger.debug('relayPageChangedSignal: %s', n) self.pageChanged.emit(page, n) @property def chapterSwitch(self): """the switch of the current chapter""" if self.whichChapter < 0: return None return self.chapters[self.whichChapter][0] @property def chapter(self): """returns the current chapter""" if self.whichChapter < 0: return None return self.chaptersObjects[self.chapterSwitch] @property def page(self): """returns the current page in the chapter""" return self.chapter.page if self.whichChapter >= 0 else None def getWhichChapter(self): """returns the selected chapter""" return self.chapters.whichChild.getValue() def setWhichChapter(self, n): """ Sets the current chapter @param n: int """ self.chapters.whichChild = n self.chaptersStack.setCurrentIndex(n) self.notesStack.setCurrentIndex(n) self.chapter.chapterSpecificIn() self.chapterChanged.emit(n) # noinspection PyStatementEffect self.chapter.page and self.pageChanged.emit(self.chapter.page, self.chapter.whichPage) whichChapter = property(getWhichChapter, setWhichChapter)