On Dec 21, 2010, at 6:38 PM, Nat Echols wrote:

On Tue, Dec 21, 2010 at 2:42 PM, Jason Ferrara <jason@discordia.org> wrote:
I've been using the pymol2 interface to run pymol inside Qt widgets. There doesn't seem to be any documentation for it, but once you figure it out it seems to work well.

Do you think you could put a quick summary of how to do this on the wiki?  I've made a few futile attempts to embed it in wxPython over the last year and a half, and it isn't even obvious how to get the modules installed correctly.  I've been able to get as far as creating an OpenGL context and starting PyMOL through pymol2, but after that I'm stuck with either a blank window (if I do nothing else), or a segmentation fault (if I call the "draw" method).



I'll see if I can find some time to write up a wiki entry. In the meantime, below is my code for a pymol qt widget which may help.

A couple notes:

_pymolPool.getInstance() returns an instance of pymol2.PyMOL.  I keep a cache of pymol instances so that I can create and destroy pymol widgets quickly without the delay caused by pymol initialization. 

Keyboard events aren't supported. If you want keyboard events you have to add event handlers similar to the mouse event handlers.


class PymolQtWidget(QGLWidget):
    _buttonMap = {Qt.LeftButton:0,
                Qt.MidButton:1,
                Qt.RightButton:2}

    def __init__(self, parent, enableUi):
        f = QGLFormat()
        f.setStencil(True)
        f.setRgba(True)
        f.setDepth(True)
        f.setDoubleBuffer(True)
        QGLWidget.__init__(self, f, parent=parent)
        self._enableUi=enableUi
        self.pymol = _pymolPool.getInstance()
        self.cmd = self.pymol.cmd
        self.toPymolName = self.pymol.toPymolName
        self._pymolProcess()
        if not self._enableUi:
            self.pymol.cmd.set("internal_gui",0)
            self.pymol.cmd.set("internal_feedback",0)
            self.pymol.cmd.button("double_left","None","None")
            self.pymol.cmd.button("single_right","None","None")
        self.pymol.reshape(self.width(),self.height())
        self._timer = QtCore.QTimer()
        self._timer.setSingleShot(True)
        self._timer.timeout.connect(self._pymolProcess)
        self.resizeGL(self.width(),self.height())
        globalSettings.settingsChanged.connect(self._updateGlobalSettings)
        self._updateGlobalSettings()

        

    def __del__(self):
        _pymolPool.returnInstance(self.pymol)

        

    def _updateGlobalSettings(self):
        for k,v in globalSettings.settings.iteritems():
            self.pymol.cmd.set(k, v)
        self.update()

            

    def redoSizing(self):
        self.resizeGL(self.width(), self.height())

        

    def paintGL(self):
        glViewport(0,0,self.width(), self.height())
        bottom = self.mapToGlobal(QtCore.QPoint(0,self.height())).y()
        self.pymol.cmd.set("stereo_stencil_parity", bottom & 0x1)
        self._doIdle()
        self.pymol.draw()

    def mouseMoveEvent(self, ev):
        self.pymol.drag(ev.x(), self.height()-ev.y(),0)
        self._pymolProcess()

    def mousePressEvent(self, ev):
        if not self._enableUi:
            self.pymol.cmd.button("double_left","None","None")
            self.pymol.cmd.button("single_right","None","None")
        self.pymol.button(self._buttonMap[ev.button()], 0, ev.x(), self.height()-ev.y(),0)
        self._pymolProcess()

    def mouseReleaseEvent(self, ev):
        self.pymol.button(self._buttonMap[ev.button()], 1, ev.x(), self.height()-ev.y(),0)
        self._pymolProcess()
        self._timer.start(0)

        

    def resizeGL(self, w, h):
        self.pymol.reshape(w,h, True)
        self._pymolProcess()

        

    def initializeGL(self):
        pass

    def _pymolProcess(self):
        self._doIdle()
        self.update()

        

    def _doIdle(self):
        if self.pymol.idle():
            self._timer.start(0)