#! /usr/bin/env python
#@+leo-ver=4-thin
#@+node:ekr.20070227091955.1:@thin leoBridge.py
#@@first
'''A module to allow full access to Leo commanders from outside Leo.'''
#@@language python
#@@tabwidth -4
#@<< about the leoBridge module >>
#@+node:ekr.20070227091955.2:<< about the leoBridge module >>
#@@nocolor
#@+at
#
# A **host** program is a Python program separate from Leo. Host
# programs may be
# created by Leo, but at the time they are run host programs are not
# part of Leo in
# any way. The leoBridge module gives host programs access to all
# aspects of Leo,
# including all of Leo's source code, the contents of any .leo file,
# all
# configuration settings in .leo files, etc.
#
# Host programs will use the leoBridge module like this::
# import leo.core.leoBridge as leoBridge
# bridge = leoBridge.controller(gui='nullGui',verbose=False)
# if bridge.isOpen():
# g = bridge.globals()
# c = bridge.openLeoFile(path)
# Notes:
#
# - The leoBridge module imports no modules at the top level.
#
# - leoBridge.controller creates a singleton *bridge controller*
# that grants
# access to Leo's objects, including fully initialized g and c
# objects. In
# particular, the g.app and g.app.gui vars are fully initialized.
#
# - By default, leoBridge.controller creates a null gui so that no
# Leo windows
# appear on the screen.
#
# - As shown above, the host program should gain access to Leo's
# leoGlobals module
# using bridge.globals(). The host program should not import
# leo.core.leoGlobals as leoGlobals directly.
#
# - bridge.openLeoFile(path) returns a completely standard Leo
# commander. Host
# programs can use these commanders as described in Leo's scripting
# chapter.
#@-at
#@nonl
#@-node:ekr.20070227091955.2:<< about the leoBridge module >>
#@nl
gBridgeController = None # The singleton bridge controller.
# This module must import *no* modules at the outer level!
#@+others
#@+node:ekr.20070227092442:controller
def controller(gui='nullGui',loadPlugins=True,readSettings=True,verbose=False):
'''Create an singleton instance of a bridge controller.'''
global gBridgeController
if not gBridgeController:
gBridgeController = bridgeController(gui,loadPlugins,readSettings,verbose)
return gBridgeController
#@nonl
#@-node:ekr.20070227092442:controller
#@+node:ekr.20070227092442.2:class bridgeController
class bridgeController:
'''Creates a way for host programs to access Leo.'''
#@ @+others
#@+node:ekr.20070227092442.3:ctor (bridgeController)
def __init__ (self,guiName,loadPlugins,readSettings,verbose):
self.g = None
self.gui = None
self.guiName = guiName
self.loadPlugins = loadPlugins
self.readSettings = readSettings
self.verbose = verbose
self.mainLoop = False # True only if a non-null-gui mainloop is active.
self.initLeo()
#@-node:ekr.20070227092442.3:ctor (bridgeController)
#@+node:ekr.20070227092442.4:globals
def globals (self):
'''Return a fully initialized leoGlobals module.'''
return self.isOpen() and self.g
#@nonl
#@-node:ekr.20070227092442.4:globals
#@+node:ekr.20070227093530:initLeo & helpers
def initLeo (self):
'''Init the Leo app to which this class gives access.
This code is based on leo.run().'''
if not self.isValidPython(): return
#@ << import leoGlobals and leoApp >>
#@+node:ekr.20070227093629.1:<< import leoGlobals and leoApp >>
# Import leoGlobals, but do NOT set g.
try:
import leo.core.leoGlobals as leoGlobals
except ImportError:
print("Error importing leoGlobals.py")
# Create the application object.
try:
import leo.core.leoApp as leoApp
leoGlobals.app = leoApp.LeoApp()
except ImportError:
print("Error importing leoApp.py")
# NOW we can set g.
self.g = g = leoGlobals
assert(g.app)
g.app.leoID = None
# Set leoGlobals.g here, rather than in leoGlobals.
leoGlobals.g = leoGlobals
#@-node:ekr.20070227093629.1:<< import leoGlobals and leoApp >>
#@nl
g.computeStandardDirectories()
if not self.getLeoID(): return
#@ << import leoNodes and leoConfig >>
#@+node:ekr.20070227093629.2:<< import leoNodes and leoConfig >>
try:
import leo.core.leoNodes as leoNodes
except ImportError:
print("Error importing leoNodes.py")
import traceback ; traceback.print_exc()
try:
import leo.core.leoConfig as leoConfig
except ImportError:
print("Error importing leoConfig.py")
import traceback ; traceback.print_exc()
#@-node:ekr.20070227093629.2:<< import leoNodes and leoConfig >>
#@nl
g.app.inBridge = True # Added 2007/10/21: support for g.getScript.
g.app.nodeIndices = leoNodes.nodeIndices(g.app.leoID)
g.app.config = leoConfig.configClass()
if self.readSettings:
g.app.config.readSettingsFiles(None,verbose=self.verbose)
self.createGui() # Create the gui *before* loading plugins.
if self.verbose: self.reportDirectories()
self.adjustSysPath()
if self.loadPlugins:
g.doHook("start1") # Load plugins.
g.app.computeSignon()
g.init_sherlock(args=[])
g.app.initing = False
g.doHook("start2",c=None,p=None,v=None,fileName=None)
#@+node:ekr.20070302061713:adjustSysPath
def adjustSysPath (self):
'''Adjust sys.path to enable imports as usual with Leo.'''
import sys
g = self.g
#g.trace('loadDir',g.app.loadDir)
leoDirs = ('config','doc','extensions','modes','plugins','core','test') # 2008/7/30
for theDir in leoDirs:
path = g.os_path_finalize_join(g.app.loadDir,'..',theDir)
if path not in sys.path:
sys.path.append(path)
#@-node:ekr.20070302061713:adjustSysPath
#@+node:ekr.20070227095743:createGui
def createGui (self):
g = self.g
if self.guiName == 'nullGui':
import leo.core.leoGui as leoGui
import leo.core.leoFrame as leoFrame
g.app.gui = leoGui.nullGui("nullGui")
g.app.log = g.app.gui.log = log = leoFrame.nullLog()
log.isNull = False
log.enabled = True # Allow prints from nullLog.
elif self.guiName == 'qt':
import leo.plugins.qtGui as qtGui
g.app.gui = qtGui.leoQtGui()
print('qtGui created')
else:
assert False,'leoBridge.py: unsupported gui: %s' % self.guiName
#@-node:ekr.20070227095743:createGui
#@+node:ekr.20070227093629.4:isValidPython
def isValidPython(self):
import sys
if sys.platform == 'cli':
return True
message = """\
Leo requires Python 2.2.1 or higher.
You may download Python from http://python.org/download/
"""
try:
# This will fail if True/False are not defined.
import leo.core.leoGlobals as g
# print('leoBridge:isValidPython:g',g)
# Set leoGlobals.g here, rather than in leoGlobals.py.
g.g = g
except ImportError:
print("isValidPython: can not import leo.core.leoGlobals as leoGlobals")
return 0
except:
print("isValidPytyhon: unexpected exception: import leo.core.leoGlobals as leoGlobals.py as g")
import traceback ; traceback.print_exc()
return 0
try:
version = '.'.join([str(sys.version_info[i]) for i in (0,1,2)])
ok = g.CheckVersion(version,'2.2.1')
if not ok:
print(message)
g.app.gui.runAskOkDialog(None,"Python version error",message=message,text="Exit")
return ok
except:
print("isValidPython: unexpected exception: g.CheckVersion")
import traceback ; traceback.print_exc()
return 0
#@nonl
#@-node:ekr.20070227093629.4:isValidPython
#@+node:ekr.20070227094232:getLeoID
def getLeoID (self):
import os
import sys
g = self.g ; tag = ".leoID.txt"
homeDir = g.app.homeLeoDir # Was homeDir.
globalConfigDir = g.app.globalConfigDir
loadDir = g.app.loadDir
verbose = False and not g.app.unitTesting
#@ << try to get leoID from sys.leoID >>
#@+node:ekr.20070227094232.1:<< try to get leoID from sys.leoID>>
# This would be set by in Python's sitecustomize.py file.
# Use hasattr & getattr to suppress pylint warning.
# We also have to use a "non-constant" attribute to suppress another warning!
nonConstantAttr = "leoID"
if hasattr(sys,nonConstantAttr):
g.app.leoID = getattr(sys,nonConstantAttr)
if verbose and not g.app.silentMode:
g.es("leoID=",g.app.leoID,spaces=False,color='red')
#@nonl
#@-node:ekr.20070227094232.1:<< try to get leoID from sys.leoID>>
#@nl
if not g.app.leoID:
#@ << try to get leoID from "leoID.txt" >>
#@+node:ekr.20070227094232.2:<< try to get leoID from "leoID.txt" >>
for theDir in (homeDir,globalConfigDir,loadDir):
# N.B. We would use the _working_ directory if theDir is None!
if theDir:
try:
fn = g.os_path_join(theDir,tag)
f = open(fn,'r')
s = f.readline()
f.close()
if s and len(s) > 0:
g.app.leoID = s.strip()
if verbose and not g.app.silentMode:
g.es('leoID=',g.app.leoID,' (in ',theDir,')',spaces=False,color="red")
break
elif verbose:
g.es('empty ',tag,' (in ',theDir,')',spaces=False,color = "red")
except IOError:
g.app.leoID = None
except Exception:
g.app.leoID = None
g.es('unexpected exception in app.setLeoID',color='red')
g.es_exception()
#@-node:ekr.20070227094232.2:<< try to get leoID from "leoID.txt" >>
#@nl
if not g.app.leoID:
#@ << try to get leoID from os.getenv('USER') >>
#@+node:ekr.20070227094232.3:<< try to get leoID from os.getenv('USER') >>
try:
theId = os.getenv('USER')
if theId:
if verbose: g.es_print("using os.getenv('USER'):",repr(theId),color='red')
g.app.leoID = theId
except Exception:
pass
#@-node:ekr.20070227094232.3:<< try to get leoID from os.getenv('USER') >>
#@nl
return g.app.leoID
#@-node:ekr.20070227094232:getLeoID
#@+node:ekr.20070227093629.9:reportDirectories
def reportDirectories (self):
g = self.g
for kind,theDir in (
("global config",g.app.globalConfigDir),
("home",g.app.homeDir),
):
g.es('',kind,'directory','',':',theDir,color='blue')
#@-node:ekr.20070227093629.9:reportDirectories
#@-node:ekr.20070227093530:initLeo & helpers
#@+node:ekr.20070227093918:isOpen
def isOpen (self):
g = self.g
return g and g.app and g.app.gui
#@nonl
#@-node:ekr.20070227093918:isOpen
#@+node:ekr.20070227092442.5:openLeoFile & helpers
def openLeoFile (self,fileName):
'''Open a .leo file, or create a new Leo frame if no fileName is given.'''
g = self.g
useLog = True
if self.isOpen():
fileName = self.completeFileName(fileName)
c = self.createFrame(fileName)
if useLog:
g.app.gui.log = log = c.frame.log
log.isNull = False
log.enabled = True
# g.pr('createGui:','g.app:',id(g.app),g.app)
# g.pr('createGui:','g.app.gui',g.app.gui)
return c
else:
return None
#@+node:ekr.20070227093629.5:completeFileName (leoBridge)
def completeFileName (self,fileName):
g = self.g
if not (fileName and fileName.strip()): return ''
import os
fileName = g.os_path_finalize_join(os.getcwd(),fileName)
head,ext = g.os_path_splitext(fileName)
if not ext: fileName = fileName + ".leo"
return fileName
#@-node:ekr.20070227093629.5:completeFileName (leoBridge)
#@+node:ekr.20070227093629.6:createFrame (leoBridge)
def createFrame (self,fileName):
'''Create a commander and frame for the given file.
Create a new frame if the fileName is empty or non-exisent.'''
g = self.g
trace = False
if fileName.strip():
if g.os_path_exists(fileName):
if trace:
import time ; t1 = time.time()
# This takes a long time due to imports in c.__init__
ok, frame = g.openWithFileName(fileName,None)
if trace:
t2 = time.time()
g.trace('g.openWithFileName: %0.2fsec' % (t2-t1))
if ok: return frame.c
else:
g.es_print('file not found', fileName,'creating new window')
# Create a new frame. Unlike leo.run, this is not a startup window.
c,frame = g.app.newLeoCommanderAndFrame(fileName=fileName)
frame.setInitialWindowGeometry()
frame.resizePanesToRatio(frame.ratio,frame.secondary_ratio)
# Call the 'new' hook for compatibility with plugins.
g.doHook("new",old_c=None,c=c,new_c=c)
return c
#@-node:ekr.20070227093629.6:createFrame (leoBridge)
#@-node:ekr.20070227092442.5:openLeoFile & helpers
#@-others
#@-node:ekr.20070227092442.2:class bridgeController
#@-others
#@-node:ekr.20070227091955.1:@thin leoBridge.py
#@-leo
|