Source code for ipymd.visualise.opengl.qtviewer

import time

import sys
from PyQt4.QtGui import QMainWindow
from PyQt4.QtCore import QTimer, Qt
from PyQt4 import QtCore, QtGui
from PyQt4.QtOpenGL import *

from .qchemlabwidget import QChemlabWidget

app_created = False
app = QtCore.QCoreApplication.instance()
if app is None:
    app = QtGui.QApplication(sys.argv)
    app_created = True
    app.references = set()

[docs]class FpsDraw(object): def __init__(self, parent): self.ctimer = QTimer() self.ctimer.start(0) self.parent = parent self.prev = time.time() self.ctimer.timeout.connect(self.parent.update)
[docs] def draw(self): self.cur = time.time() #elapsed = self.cur - self.prev self.prev = self.cur
class QtViewer(QMainWindow): """Bases: `PyQt4.QtGui.QMainWindow` View objects in space. This class can be used to build your own visualization routines by attaching :doc:`renderers <chemlab.graphics.renderers>` and :doc:`uis <chemlab.graphics.uis>` to it. .. seealso:: :doc:`/graphics` **Example** In this example we can draw 3 blue dots and some overlay text:: from chemlab.graphics import QtViewer from chemlab.graphics.renderers import PointRenderer from chemlab.graphics.uis import TextUI vertices = [[0.0, 0.0, 0.0], [0.0, 1.0, 0.0], [2.0, 0.0, 0.0]] blue = (0, 255, 255, 255) colors = [blue,] * 3 v = QtViewer() pr = v.add_renderer(PointRenderer, vertices, colors) tu = v.add_ui(TextUI, 100, 100, 'Hello, world!') v.run() """ def __init__(self): QMainWindow.__init__(self) # Pre-initializing an OpenGL context can let us use opengl # functions without having to show the window first... context = QGLContext(QGLFormat(), None) widget = QChemlabWidget(context, self) context.makeCurrent() self.setCentralWidget(widget) self.resize(1000, 800) self.widget = widget self.key_actions = {} def run(self): '''Display the QtViewer ''' self.show() app.exec_() def schedule(self, callback, timeout=100): '''Schedule a function to be called repeated time. This method can be used to perform animations. **Example** This is a typical way to perform an animation, just:: from chemlab.graphics import QtViewer from chemlab.graphics.renderers import SphereRenderer v = QtViewer() sr = v.add_renderer(SphereRenderer, centers, radii, colors) def update(): # calculate new_positions sr.update_positions(new_positions) v.widget.repaint() v.schedule(update) v.run() .. note:: remember to call QtViewer.widget.repaint() each once you want to update the display. **Parameters** callback: function() A function that takes no arguments that will be called at intervals. timeout: int Time in milliseconds between calls of the *callback* function. **Returns** a `QTimer`, to stop the animation you can use `Qtimer.stop` ''' timer = QTimer(self) timer.timeout.connect(callback) timer.start(timeout) return timer def add_renderer(self, klass, *args, **kwargs): '''Add a renderer to the current scene. **Parameter** klass: renderer class The renderer class to be added args, kwargs: Arguments used by the renderer constructor, except for the *widget* argument. .. seealso:: :py:class:`~chemlab.graphics.renderers.AbstractRenderer` .. seealso:: :doc:`/api/chemlab.graphics.renderers` **Return** The istantiated renderer. You should keep the return value to be able to update the renderer at run-time. ''' renderer = klass(self.widget, *args, **kwargs) self.widget.renderers.append(renderer) return renderer def remove_renderer(self, rend): '''Remove a renderer from the current view. **Example** :: rend = v.add_renderer(AtomRenderer) v.remove_renderer(rend) .. versionadded:: 0.3 ''' if rend in self.widget.renderers: self.widget.renderers.remove(rend) else: raise Exception("The renderer is not in this viewer") def has_renderer(self, rend): '''Return True if the renderer is present in the widget renderers. ''' return rend in self.widget.renderers def update(self): super(QtViewer, self).update() self.widget.update() def add_ui(self, klass, *args, **kwargs): '''Add an UI element for the current scene. The approach is the same as renderers. .. warning:: The UI api is not yet finalized ''' ui = klass(self.widget, *args, **kwargs) self.widget.uis.append(ui) return ui def add_post_processing(self, klass, *args, **kwargs): '''Add a post processing effect to the current scene. The usage is as following:: from chemlab.graphics import QtViewer from chemlab.graphics.postprocessing import SSAOEffect v = QtViewer() effect = v.add_post_processing(SSAOEffect) .. seealso:: :doc:`/api/chemlab.graphics.postprocessing` **Return** an instance of :py:class:`~chemlab.graphics.postprocessing.base.AbstractEffect` .. versionadded:: 0.3 ''' pp = klass(self.widget, *args, **kwargs) self.widget.post_processing.append(pp) return pp def remove_post_processing(self, pp): '''Remove a post processing effect. ..versionadded:: 0.3 ''' self.widget.post_processing.remove(pp) def clear(self): del self.widget.renderers[:] # Events def keyPressEvent(self, evt): angvel = 0.3 if evt.key() == Qt.Key_Up: self.widget.camera.orbit_x(angvel) if evt.key() == Qt.Key_Down: self.widget.camera.orbit_x(-angvel) if evt.key() == Qt.Key_Left: self.widget.camera.orbit_y(-angvel) if evt.key() == Qt.Key_Right: self.widget.camera.orbit_y(angvel) if evt.key() == Qt.Key_Plus: self.widget.camera.mouse_zoom(0.1) if evt.key() == Qt.Key_Minus: self.widget.camera.mouse_zoom(-0.1) else: action = self.key_actions.get(evt.key(), None) if action: action() self.widget.repaint() if __name__ == '__main__': QtViewer().run()