"""Helper functions and classes.
@author Michael Reimpell
"""
# Copyright (C) 2005 Michael Reimpell
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation; either
# version 2.1 of the License, or (at your option) any later version.
#
# This library is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public
# License along with this library; if not, write to the Free Software
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
# epydoc doc format
__docformat__ = "javadoc en"
import sys
import os
import string
import pickle
import Blender
def indent(indent):
"""Indentation.
@param indent Level of indentation.
@return String.
"""
return " "*indent
class Singleton:
"""Ensure that a class has at most one instance.
@cvar instances Existing instances.
"""
instances = {}
def __init__(self):
"""Constructor.
"""
assert self.__class__ not in Singleton.instances.keys(), \
"Class \"%s\" is a singleton." % self.__class__
Singleton.instances[self.__class__] = self
return
# @classmethod
def getSingleton(singletonClass):
"""Returns singleton instance.
"""
instance = None
if Singleton.instances.has_key(singletonClass):
instance = Singleton.instances[singletonClass]
else:
instance = singletonClass()
return instance
getSingleton = classmethod(getSingleton)
class Model:
"""Model interface.
"""
def __init__(self):
self.viewList = []
return
def addView(self, view):
self.viewList.append(view)
return
def removeView(self, view):
if view in self.viewList:
self.viewList.remove(view)
return
# protected
def _notify(self):
"""Notify views.
"""
for view in self.viewList:
view.update()
return
class View:
"""View interface.
"""
def __init__(self, model):
self.attachModel(model)
return
def __del__(self):
self.detachModel()
return
def attachModel(self, model):
self.model = model
self.model.addView(self)
return
def detachModel(self):
self.model.removeView(self)
return
def update(self):
return
class Log(Singleton, Model):
"""Logs messages and status.
Logs messages as a list of strings and keeps track of the status.
Possible status values are info, warning and error.
@cvar INFO info status
@cvar WARNING warning status
@cvar ERROR error status
"""
INFO, WARNING, ERROR = range(3)
def __init__(self):
"""Constructor.
"""
Singleton.__init__(self)
Model.__init__(self)
self.clear()
return
def clear(self):
"""Clears the log.
"""
self.messageList = []
self.status = Log.INFO
return
def logInfo(self, message):
"""Logs an info message.
@param message message string
"""
self.messageList.append((Log.INFO, message))
self._notify()
return
def logWarning(self, message):
"""Logs a warning message.
The status is set to Log.WARNING
if it is not already Log.ERROR
.
@param message message string
"""
self.messageList.append((Log.WARNING, "Warning: "+message))
if not self.status == Log.ERROR:
self.status = Log.WARNING
self._notify()
return
def logError(self, message):
"""Logs an error message.
The status is set to Log.ERROR
.
@param message message string
"""
self.messageList.append((Log.ERROR, "Error: "+message))
self.status = Log.ERROR
self._notify()
return
def getStatus(self):
"""Gets the current status.
The status can be
Log.INFO
Log.WARNING
Log.ERROR
None
is passed, the converter is searched in $PATH.
@param converter Location of OgreXMLConverter.
"""
self.converter = converter
# update registry
registryDict = Blender.Registry.GetKey('OgrePackage', True)
if converter is None:
# remove registry entry
if registryDict and registryDict.has_key('OgreXMLConverter'):
del registryDict['OgreXMLConverter']
Blender.Registry.SetKey('OgrePackage', registryDict, True)
else:
# set registry entry
if registryDict is None:
registryDict = {}
registryDict['OgreXMLConverter'] = self.converter
Blender.Registry.SetKey('OgrePackage', registryDict, True)
return
def findConverter(self):
"""Find converter in path.
@return converter location or None
if converter is not found.
"""
converter = None
path = os.environ.get('PATH')
if path:
pathList = string.split(path, ':')
found = False
i = 0
while not found and (i < len(pathList)):
if os.path.exists(os.path.join(pathList[i], 'OgreXMLConverter')):
converter = os.path.join(pathList[i], 'OgreXMLConverter')
found = True
i += 1
return converter
def convert(self, filename, arguments=''):
"""Converts given file with the OgreXMLConverter.
@param filename The xml filename to pass to the converter.
@param arguments Additional arguments to pass to the converter.
"""
converter = None
if self.converter:
converter = self.converter
elif self.findConverter():
converter = 'OgreXMLConverter'
if converter:
dir = os.path.dirname(filename)
# ensure proper encoding of filenames
encodedDir = os.path.normpath(dir)
encodedFilename = os.path.normpath(filename)
encodedConverter = os.path.normpath(converter)
# call the converter
commandLine = '"' + encodedConverter + '" -log "' \
+ os.path.join(encodedDir, 'OgreXMLConverter.log') \
+ '" ' + self.converterArgs + ' ' + arguments \
+ ' "' + encodedFilename + '"'
if os.name == "nt":
# workaround for popen windows bug
commandLine = '"' + commandLine + '"'
Log.getSingleton().logInfo("Running OgreXMLConverter: " + commandLine)
xmlConverter = os.popen(commandLine, 'r')
for line in xmlConverter:
Log.getSingleton().logInfo("OgreXMLConverter: " + line)
xmlConverter.close()
return
class ConvertibleXMLFile:
"""
"""
def __init__(self, filename):
self.filename = filename
self.converterOptionDict = {}
self.fileObject = open(self.filename, "w")
return
def write(self, str):
self.fileObject.write(str)
return
def close(self):
self.fileObject.close()
return
def addConverterOption(self, option, value=''):
self.converterOptionDict[option] = value
return
def convert(self):
arguments = ''
for (key, value) in self.converterOptionDict.iteritems():
arguments += ' ' + key + ' ' + value
OgreXMLConverter.getSingleton().convert(self.filename, arguments)
return
class PathName:
"""Splits a pathname independent of the underlying os.
Blender saves pathnames in the os specific manner. Using os.path may result in problems
when the export is done on a different os than the creation of the .blend file.
"""
def __init__(self, pathName):
self.pathName = pathName
return
def dirname(self):
return os.path.dirname(self.pathName)
def basename(self):
baseName = os.path.basename(self.pathName)
# split from non-os directories
# \\
baseName = baseName.split('\\').pop()
# /
baseName = baseName.split('/').pop()
if (baseName != baseName.replace(' ','_')):
# replace whitespace with underscore
Log.getSingleton().logWarning("Whitespaces in filename \"%s\" replaced with underscores." % baseName)
baseName = baseName.replace(' ','_')
return baseName
def path(self):
return self.pathName