#!/usr/bin/env python
import math
import os
import sys
from iv import *
from qt import *
class Info(Exception):
def __init__(self, info):
Exception.__init__(self, info)
# __init__()
# class Info
class Viewer(SoQtExaminerViewer):
"""Demonstrates how to:
(1) reimplement processSoEvent() to customize event handling.
(2) use the offscreen renderer to save a scene to a bitmap or .(e)ps file.
(3) use the PostScript vectorize action to save a scene to an .(e)ps file.
"""
def __init__(self, *args):
SoQtExaminerViewer.__init__(self, *args)
# Disable the viewer decorations.
self.setDecoration(False)
# Set an orthographic camera.
self.setCameraType(SoOrthographicCamera.getClassTypeId())
# Enable the axis cross in the lower right corner.
self.setFeedbackVisibility(True)
# Set a blue background.
self.setBackgroundColor(SbColor(0.0, 0.0, 1.0))
# The next statement starts the scene manager of the widget
self.setSceneGraph(None)
# Change the table to change the mapping from buttons to actions.
self.actions = {
# left button
SoMouseButtonEvent.BUTTON1: self.spin,
# right button
SoMouseButtonEvent.BUTTON2: self.zoom,
# middle button
SoMouseButtonEvent.BUTTON3: self.pan,
}
# The actions use the current and last mouse position.
self.new2f = None
self.old2f = None
# The action to take on a SoLocation2Event.
self.action = None
# The spin action uses a spin projector.
self.spinProjector = SbSphereSheetProjector(
SbSphere(SbVec3f(0, 0, 0), 0.8), True)
viewVolume = SbViewVolume()
viewVolume.ortho(-1, 1, -1, 1, -1, 1)
self.spinProjector.setViewVolume(viewVolume)
# An alias for self.getCamera()
self.camera = None
# Coin documentation advises to reutilize SoOffscreenRender instances.
self.offscreenRenderer = None
# __init__()
def saveAsVector(self, filename):
"""Save the scene in a vector format.
"""
# FIXME: this is not working well. Who is to blame, SIM or me?
# Use saveAsBitmap() for good results.
try:
# Get the scene including camera and light from the scene manager;
# see the documentation for SoOffscreenRenderer.render().
scene = self.getSceneManager().getSceneGraph()
action = SoVectorizePSAction()
action.setBackgroundColor(True, SbColor(0.0, 0.0, 1.0))
output = action.getOutput()
if not output.openFile(filename):
raise Info('Unable to open %s for writing' % filename)
viewportSize = self.getViewportRegion().getViewportSizePixels()
viewportRatio = float(viewportSize[0]) / float(viewportSize[1])
if viewportRatio > 1.0:
action.setOrientation(SoVectorizeAction.LANDSCAPE)
viewportRatio = 1.0 / viewportRatio
else:
action.setOrientation(SoVectorizeAction.PORTRAIT)
pageSize = action.getPageSize()
pageRatio = float(pageSize[0]) / float(pageSize[1])
if pageRatio < viewportRatio:
xPageSize = float(pageSize[0])
yPageSize = xPageSize / viewportRatio
else:
yPageSize = float(pageSize[1])
xPageSize = yPageSize * viewportRatio
action.beginPage(SbVec2f(), SbVec2f((xPageSize, yPageSize)))
action.calibrate(self.getViewportRegion())
action.apply(scene)
action.endPage()
output.closeFile()
except Info, info:
QMessageBox.information(
self.getParentWidget(),
'Saving to a vector format:',
info[0],
QMessageBox.Ok,
)
# saveAsVector()
def getBitmapExtensions(self):
"""Returns a sorted list of all offscreen renderer file types.
"""
if not self.offscreenRenderer:
self.offscreenRenderer = SoOffscreenRenderer(SbViewportRegion())
extensions = []
for i in range(self.offscreenRenderer.getNumWriteFiletypes()):
for extension in self.offscreenRenderer.getWriteFiletypeInfo(i)[0]:
if extension not in extensions:
extensions.append(extension)
extensions.sort()
return extensions
# getBitmapExtensions()
def saveAsBitmap(self, root, ext):
"""Save the scene in a bitmap format.
"""
try:
# Get the scene including camera and light from the scene manager;
# see the documentation for SoOffscreenRenderer.render().
scene = self.getSceneManager().getSceneGraph()
viewportRegion = self.getViewportRegion()
if not self.offscreenRenderer:
self.offscreenRenderer = SoOffscreenRenderer(viewportRegion)
else:
self.offscreenRenderer.setViewportRegion(viewportRegion)
self.offscreenRenderer.setBackgroundColor(
self.getBackgroundColor())
if not self.offscreenRenderer.render(scene):
raise Info('Failed to render the scenegraph')
if not self.offscreenRenderer.writeToFile(
'%s.%s' % (root, ext), ext
):
raise Info('Failed to write "%s.%s"' % (root, ext))
except Info, info:
QMessageBox.information(
self.getParentWidget(),
'Saving to a bitmap format:',
info[0],
QMessageBox.Ok,
)
# saveAsBitmap()
def pan(self, camera):
"""Pan the camera.
"""
viewVolume = camera.getViewVolume(self.getGLAspectRatio())
focalDistance = camera.focalDistance.getValue()
panPlane = viewVolume.getPlane(focalDistance)
line = SbLine()
viewVolume.projectPointToLine(self.new2f, line)
new3f = SbVec3f()
panPlane.intersect(line, new3f)
viewVolume.projectPointToLine(self.old2f, line)
old3f = SbVec3f()
panPlane.intersect(line, old3f)
camera.position.setValue(camera.position.getValue() + old3f - new3f)
# pan()
def spin(self, camera):
"""Spin the camera.
"""
self.spinProjector.project(self.old2f)
rotation = SbRotation()
self.spinProjector.projectAndGetRotation(self.new2f, rotation)
rotation.invert()
# Find global coordinates of focal point.
direction = SbVec3f()
camera.orientation.getValue().multVec(SbVec3f(0, 0, -1), direction)
focalpoint = camera.position.getValue() \
+ direction * camera.focalDistance.getValue()
# Set new orientation value by accumulating the new rotation.
camera.orientation.setValue(rotation * camera.orientation.getValue())
# Reposition camera to point to the same old focal point.
camera.orientation.getValue().multVec(SbVec3f(0, 0, -1), direction)
camera.position.setValue(
focalpoint - direction * camera.focalDistance.getValue())
# spin()
def zoom(self, camera):
"""Zoom the camera.
"""
assert(isinstance(camera, SoOrthographicCamera))
factor = math.exp(20.0*(self.new2f[1] - self.old2f[1]))
camera.height.setValue(factor*camera.height.getValue())
oldFocalDistance = camera.focalDistance.getValue()
newFocalDistance = factor * oldFocalDistance
direction = SbVec3f()
camera.orientation.getValue().multVec(SbVec3f(0, 0, -1), direction)
oldPosition = camera.position.getValue()
newPosition = oldPosition - direction * (
newFocalDistance - oldFocalDistance)
if (newPosition.length() < 1e19):
camera.position.setValue(newPosition)
camera.focalDistance.setValue(newFocalDistance)
# zoom()
def processSoEvent(self, event):
"""Intercept events to pan, spin, and zoom for an orthographic camera.
"""
camera = self.getCamera()
if not isinstance(camera, SoOrthographicCamera):
return SoQtExaminerViewer.processSoEvent(self, event)
if SoMouseButtonEvent.isButtonPressEvent(
event, SoMouseButtonEvent.ANY
):
self.new2f = event.getNormalizedPosition(self.getViewportRegion())
self.action = self.actions.get(event.getButton(), None)
return True
if SoMouseButtonEvent.isButtonReleaseEvent(
event, SoMouseButtonEvent.ANY
):
self.new2f = None
self.old2f = None
self.action = None
return True
if self.action and isinstance(event, SoLocation2Event):
self.old2f = self.new2f
self.new2f = event.getNormalizedPosition(self.getViewportRegion())
self.action(camera)
return True
return SoQtExaminerViewer.processSoEvent(self, event)
# processSoEvent()
# class Viewer
class MainWindow(QMainWindow):
def __init__(self):
QMainWindow.__init__(
self, None, 'IVuPy Main Window Example', Qt.WDestructiveClose)
# Since SoQt widgets are not derived from QWidget,
# it is useful to give them a shell QWidget as parent:
shell = QWidget(self)
self.viewer = Viewer(shell)
# and now
self.setCentralWidget(shell)
# instead of self.setCentralWidget(self.viewer) which raises an error.
fileMenu = QPopupMenu(self)
self.menuBar().insertItem('&File', fileMenu)
fileMenu.insertItem(
'&Open...', self.openFile, Qt.CTRL + Qt.Key_O)
fileMenu.insertItem(
'Save as &vector...', self.saveAsVector)
fileMenu.insertItem(
'Save as &bitmap...', self.saveAsBitmap)
fileMenu.insertItem(
'&Quit', qApp, SLOT('closeAllWindows()'), Qt.CTRL + Qt.Key_Q)
self.statusBar().message('Ready', 5000)
self.resize(600, 400)
# __init__()
def setSceneGraph(self, scene):
self.viewer.setSceneGraph(scene)
# setSceneGraph()
def openFile(self):
"""Open an Inventor file to display in the viewer.
"""
name = QFileDialog.getOpenFileName(
os.path.join(os.pardir, 'data'), 'Inventor files (*.iv)', self)
if not name:
self.statusBar().message('Reading aborted', 5000);
return
source = SoInput()
# str(name), since a QString is not accepted by SoInput.openFile()
if not source.openFile(str(name)):
self.statusBar().message('Failed to open %s' % name, 5000)
scene = SoDB.readAll(source)
self.viewer.setSceneGraph(scene)
# openFile()
def saveAsVector(self):
"""Saves the scene in the viewer as a ps file.
"""
pattern = 'Files (*.eps *.ps)'
name = QFileDialog.getSaveFileName('.', pattern, self)
self.viewer.saveAsVector(str(name))
# saveAsVector()
def saveAsBitmap(self):
"""Saves the scene in the viewer as a bitmap or (e)ps file.
"""
extensions = self.viewer.getBitmapExtensions()
pattern = 'Files (%s)' % ' '.join(['*.%s' % x for x in extensions])
name = QFileDialog.getSaveFileName('.', pattern, self)
root, ext = os.path.splitext(str(name))
if ext.startswith('.'):
ext = ext[1:]
if ext not in extensions:
self.statusBar().message(
'"%s" bitmap format is not supported' % ext)
return
self.viewer.saveAsBitmap(root, ext)
# saveAsBitmap()
# class MainWindow
def main():
# Initialize Qt and SoQt
app = QApplication(sys.argv)
SoQt.init(None)
demo = MainWindow()
demo.show()
app.connect(app, SIGNAL('lastWindowClosed()'), app, SLOT('quit()'))
SoQt.mainLoop()
# main()
if __name__ == '__main__':
main()
# Local Variables: ***
# mode: python ***
# End: ***