On Mon, 3 Oct 2005, Terry Jones wrote:

> Is it possible to embed a pymol window into a bigger independent
> application?

"Embed"? No. At least, not if you want the pymol window to act as a
widget that can be embedded in a frame of some sort.

The last time I used pymol was version 0.87, so the information
below may very well be useless. Reader beware.

> I am writing some code using PyQt and various other things (like PyOpenGL,
> Open Inventor, pivy), and it would be nice to be able to be able to pop a
> window with pymol in it (if that's the right way to put it). So the tk GUI
> would not be there, just the PyMol graphics window.

You *can* do that, if I'm understanding you correctly. The pymol
window can be popped up from your application, you just can't embed it
into another window. (And, at least in 0.87, you couldn't dismiss
the pymol window, because pymol insisted on terminating the
whole application).

> When I do what seems most obvious, putting 
> 
>   from pymol import cmd
> 
> in a file and running python on it, it tells me there's no such module as
> pymol. Maybe this is simply my python path not including the right thing.

It's a little tricker, see below.

> If it matters, I'm on Mac OS X (10.3.9) but I'm running python 2.4, not the
> stock distributed 2.3.  Would that stop me being able to import pymol as
> above (I mean is it incompatible, not is the path to pymol unknown, which I
> assume is probably the case)?

I've always used pymol with my own Python interpreter, you shouldn't
have to use what came with pymol.

And Warren wrote:

> We are very interested in this, but it is outside the scope of PyMOL
> 1.x.  It will definitely be possible in PyMOL 2.x, but we'll need to
> break the API a bit for that in order to achieve an object-oriented
> usage paradigm.

2 years ago I was involved with a project where we only wanted to use
pymol as a rendering widget, but it was impossible. Our prototype used
pymol's separate window, but I recommended that the project look for
another solution because my impression was that the pymol project was
more interested in being an application, and introducing new features.
Glad to hear a "library" or "toolkit" usage will be a deliverable for
2.X.

> To accomplish this with current versions, it is necessary to launch and
> message PyMOL as an independent process or at least as a singleton
> component within a Python interpreter that includes a PyQT user
> interface in a separate window.
> 
> You can get a graphics-only PyMOL window by launching with command line
> options -qxiF.  You can control window placement with -X  -Y # -H # -W #

And so here is what we did on our project. We had a python/tk
application that had a UI to do something other than molecule
viewing. There was a button to launch a "structure viewer". Ideally it
would launch a top level window that had a navbar on top, a
residue/atom tree on the left, and the pymol rendering window as the
main part of the top level window. Instead we launched the toplevel
which did layout the navbar and tree widget, but then launched pymol
separately. The navbar and tree controlled pymol via cmd.* calls, plus
it interacted with the "twpick" Wizard to detect when a user clicked
on something.

So in a file, say sw.py:

import os, threading, sys

import __main__

import Tkinter
from   Tkinter import *
from   tkFileDialog  import *
import Pmw

. . . app specific stuff. . .

#
# First, add the pymol modules path to the python module path
# so we can we can import pymol
#
modules_path = os.environ['PYMOL_PATH']+'/modules'
if modules_path not in sys.path:
    sys.path.append(modules_path)

#
# Now we can import pymol
#
# This stuff is for pymol v0.86 and above
__main__.pymol_launch = 0
__main__.pymol_argv = [ "pymol", "-q -i -x" ] # no UI

import pymol
from pymol import cmd, util
from pymol.cgo import *

# Don't really need a separate class for this, but much more
# was envisioned. . .
class PymolWindow(threading.Thread):
 
     def __init__(self):
         # print "PymolWindow: New Instance"
         threading.Thread.__init__(self)
 
     def run(self):
         pymol_argv = [ "pymol", "-q -i -x" ] # no UI
         #pymol_argv = [ "pymol", "-q -x " ] # with internal UI

         # Now start the pymol threads
         pymol.invocation.parse_args(pymol_argv)
         pymol.start_pymol()

class StructureWidget(Pmw.MegaWidget):

. . . app specific stuff. . .

    def __init__(self, mainGUI, parent, **kwDict):
        . . . app specific stuff. . .
        self.defineoptions(kwDict, optiondefs)
        Pmw.MegaWidget.__init__(self, parent, **kwDict)

        self._createComponents()

        # Pymol should be ready for commands now

        # print "Loading picking event wizard..."
        cmd.wizard("twpick")
        # Quiet down pymol a bit
        cmd.feedback('disable', 'executive', 'everything')
        
    def _createComponents(self):

        # Create the Pymol Window
        self.pymol = PymolWindow()
        self.pymol.start()

        # While pymol starts, construct our GUI
        self._createMenuBar(self.parent) 
        . . . app specific stuff. . .

        # Wait for pymol before making any pymol calls
        e = threading.Event()
        while not pymol._cmd.ready():
            e.wait(0.1)

Can you follow that? Pymol is running in a separate thread and you can
now control it via cmd.* calls. For example, there was a pulldown to
select a rendering style, one of which was cartoon mode, and it
ultimately called:

    #
    # Set the cartoon mode.
    #
    def setCartoonDisplayStyle(self, objName, type):
        # print "SetCartoonStyle to %s on %s" % (type, objName)
        if (type == 'off'):
            cmd.hide('cartoon', objName);
        else:
            cmd.cartoon(type, objName)
            cmd.show('cartoon', objName)


Or, there was a pulldown to rebind the mouse buttons in a few preset
configurations, which ultimately called this method:

    def rebindMouse(self, which): 

        if which == 'mouseModeSelect':
            # print "Rebinding the mouse for selection"
            pymol.controlling.ring_dict['OurApp'] = [ 'OurApp_three_button' ]
            pymol.controlling.mode_dict['OurApp_three_button'] = [ 
                                           ('l','none','rota'),
                                           ('m','none','movz'),
                                           ('r','none','pkat'),
                                           ('l','shft','movz'),
                                           ('m','shft','move'),
                                           ('r','shft','pkat'),   # +lb
                                           ('l','ctrl','movz'),
                                           ('m','ctrl','move'),
                                           ('r','ctrl','pkat'),   # +lb
                                           ('l','ctsh','orig'),
                                           ('m','ctsh','orig'),
                                           ('r','ctsh','orig')
                                                              ]
        . . . rest elided. . .

Or, the app had a PDB class, and to load a PDB into pymol:

    # Load a PDB from a string. 
    def loadPDBString(self, pdbString, seqID):

        # Turn the sequence ID into a pymol-capable name
        pmName = self.seqIDToPymolName(seqID)

        # print "Loading from String:"
        cmd.read_pdbstr(pdbString, pmName, 1, 1)

        # Check the PDB for HELIX or SHEET records, which define the 
        # secondary structure in the PDB. If they're not there, then
        # we'll call pymol and attempt to create a secondary structure.
        if (None == re.search('\nHELIX', pdbString) and
            None == re.search('\nSHEET', pdbString)):
            # print "####" 
            # print "####  WARNING: NO Secondary Structure defined in PDB."
            # print "####           Calling pymol's util.ss() to generate one." 
            # print "####"
            util.ss(pmName)

        self._finishLoadingPDB(pmName, seqID);

    #
    # This version of load takes a PDB object
    #
    def loadPDB(self, pdb, seqID):
        # Add it to our list of PDBs
        self.loadedPDBs[seqID] = pdb

        # And load it...
        self.loadPDBString(pdb.pdbStr, seqID)
        . . . rest elided. . .

    # Called after a PDB has been loaded. It creates the objects
    # we use internally to maninipulate the structure
    def _finishLoadingPDB(self, pmName, seqID):
        visProps = self.getVisualProps(pmName)
        #cmd.rebuild()
        self.sm.addStructure(seqID)

        #self.addNameToWSMenu(objName) # Don't need this anymore, since we have 
Tree

        # Now make a named selection that corresponds to the working set object.
        # We need this because certain props (eg. cartoon_style) only seem to
        # work on selections. Go figure.
        cmd.select(pmName + "Sel", "byObj all")
        cmd.deselect()
        . . . rest elided. . .

Does this help at all? Again, this was pymol 0.87 so I am not sure if
you can start pymol this way in the current release. And although we
used Tk to build our GUI, you should be able to use anything you like.

Our app worked on Windows and Linux, we never did any work on the Mac.

Good luck,

-Bob



Reply via email to