[Bf-python] Pyrex - an easier way to write Python/C modules?

Yann Vernier yann at algonet.se
Sat Nov 6 14:09:30 CET 2004


I messed around a while, and eventually got a drop in replacement for
Blender.Text working. Pyrex is written in Python, and could be better
integrated with scons than this - it already has a module to extend
distutils. 

http://nz.cosc.canterbury.ac.nz/~greg/python/Pyrex/

-- 
PGP fingerprint = 9242 DC15 2502 FEAB E15F  84C6 D538 EC09 5380 5746
-------------- next part --------------
Index: SConscript
===================================================================
RCS file: /cvsroot/bf-blender/blender/source/blender/python/SConscript,v
retrieving revision 1.20
diff -u -r1.20 SConscript
--- SConscript	21 Sep 2004 05:28:17 -0000	1.20
+++ SConscript	6 Nov 2004 12:47:38 -0000
@@ -42,7 +42,7 @@
                 'api2_2x/Window.c',
                 'api2_2x/World.c',
                 'api2_2x/Image.c',
-                'api2_2x/Text.c',
+                #'api2_2x/Text.c',
                 'api2_2x/Texture.c',
                 'api2_2x/Noise.c',
                 'api2_2x/charRGBA.c',
@@ -73,4 +73,13 @@
 python_env.Append (CPPPATH = user_options_dict['PYTHON_INCLUDE'])
 python_env.Append (CPPPATH = user_options_dict['OPENGL_INCLUDE'])
 python_env.Append (CPPPATH = extra_includes)
-python_env.Library (target='#'+user_options_dict['BUILD_DIR']+'/lib/blender_python', source=source_files)
+
+pyrexbuild = Builder(action = 'pyrexc -o $TARGET $SOURCE',
+			suffix = python_env['CFILESUFFIX'], src_suffix = '.pyx')
+python_env.Append (BUILDERS = {'Pyrex': pyrexbuild})
+
+textc = python_env.Pyrex('api2_2x/Text')
+texth = python_env.SideEffect('api2_2x/Text.h', textc)
+
+python_env.Library (target='#'+user_options_dict['BUILD_DIR']+'/lib/blender_python', source=source_files+[textc])
+
-------------- next part --------------
cdef extern from "DNA_ID.h":
  struct ID:
    void *next
    #void *prev
    #ID *newid
    #Library *lib
    char name[24]
    #short us
    #short flag
    #int pad

cdef extern from "DNA_listBase.h":
  struct Link:
    Link *next
    Link *prev
  struct ListBase:
    void *first
    void *last

cdef extern from "DNA_text_types.h":
  struct TextLine:
    TextLine *next
    TextLine *prev
    char *line
    int len
    int blen
  struct Text:
    ID id
    char *name
    int flags
    int nlines
    ListBase lines
    #TextLine *curl
    #TextLine *sell
    #int curc
    #int selc
    #char *undo_buf
    #int undo_pos
    #int undo_len
    #void *compiled
  enum:
    TXT_FOLLOW

cdef extern from "BKE_library.h":
  void rename_id(ID *id, char *name)
  void free_libblock(ListBase *lb, void *idv)
  void free_libblock_us(ListBase *lb, void *idv)

cdef extern from "BKE_main.h":
  struct Main:
    ListBase text

cdef extern from "BKE_global.h":
  struct Global:
    Main *main
  Global G

-------------- next part --------------
from blenkernel cimport Text as c_Text

cdef public class Text [object BPy_Text, type Text_Type]:
  cdef c_Text *text

-------------- next part --------------
"The Blender Text module"

from blenkernel cimport G, \
  Text as c_Text, TextLine, TXT_FOLLOW, \
  free_libblock, rename_id
cdef extern from "BKE_text.h":
  c_Text *add_empty_text()
  c_Text *add_text(char *file)
  int txt_get_undostate()
  void txt_set_undostate(int)
  void txt_sel_all(c_Text*)
  void txt_cut_sel(c_Text*)
  void txt_insert_buf(c_Text *text, char *in_buffer)
  void txt_move_eof(c_Text *text, short sel)

cdef int isLinked(Text text):
  cdef c_Text *iter
  if text.text is NULL:
    return 0
  iter=<c_Text*>G.main.text.first
  while iter:
    if iter == text.text:
      return 1
    iter=<c_Text*>iter.id.next
  # Here's a problem, this Text is invalid.
  text.text=NULL
  return 0
cdef int checkLinked(Text text) except 0:
  if text.text and isLinked(text):
    return 1
  else:
    raise RuntimeError("This object isn't linked to a Blender Text Object")

cdef public class Text [object BPy_Text, type Text_Type]:
  "Blender Text"
  def getName(self):
    "() - Return Text Object name"
    checkLinked(self)
    # A bit clumsy, but slicing causes the string conversion to be done first.
    return self.text.id.name+2
  def setName(self, name):
    "(str) - Change Text Object name"
    checkLinked(self)
    # Note: length check is absent here.
    # rename_id should check, hardcoding all over is bad.
    rename_id(&self.text.id, name)
  property name:
    "The Text name."
    checkLinked(self)
    def __get__(self):
      return self.getName()
    def __set__(self, value):
      self.setName(value)
  def getFilename(self):
    "() - Return Text Object filename"
    checkLinked(self)
    if self.text.name is not NULL:
      return self.text.name
  property filename:
    def __get__(self):
      return self.getFilename()
  property mode:
    def __get__(self):
      checkLinked(self)
      return self.text.flags
  def getNLines(self):
    "() - Return number of lines in text buffer"
    # Apparently this isn't updated in Blender itself, by comments in Text.c
    cdef TextLine *line
    cdef int nlines
    checkLinked(self)
    nlines=0
    line=<TextLine*>self.text.lines.first
    while line:
      nlines=nlines+1
      line=line.next
    self.text.nlines=nlines
    return nlines
  property nlines:
    def __get__(self):
      return self.getNLines()
  def clear(self):
    "() - Clear Text buffer"
    cdef int oldstate
    checkLinked(self)
    oldstate=txt_get_undostate()
    txt_set_undostate(1)
    txt_sel_all(self.text)
    txt_cut_sel(self.text)
    txt_set_undostate(oldstate)
  def write(self, char *line):
    "(line) - Append string 'str' to Text buffer"
    cdef int oldstate
    checkLinked(self)
    oldstate=txt_get_undostate()
    txt_insert_buf(self.text, line)
    txt_move_eof(self.text, 0)
    txt_set_undostate(oldstate)
  def set(self, char *name, int val):
    "(name, val) - Set attribute 'name' to value 'val'"
    checkLinked(self)
    if name == "follow_cursor":
      if val:
        self.text.flags = self.text.flags|TXT_FOLLOW
      else:
        self.text.flags = self.text.flags&~TXT_FOLLOW
  def asLines(self):
    "() - Return text buffer as a list of lines"
    cdef TextLine *line
    checkLinked(self)
    line=<TextLine*>self.text.lines.first
    res=[]
    while line:
      res.append(line.line)
      line=line.next
    return res
  def __cmp__(self, Text other not None):
    if self.text is other.text:
      return 0
    else:
      return -1
  def __repr__(self):
    if self.text and isLinked(self):
      return '[Text "%s"]'%(self.text.id.name+2)
    else:
      return '[Text <deleted>]'

#cdef extern from "string.h":
#  int strcmp(char*,char*)
def Get(name=None):
  """(name) - return the Text with name 'name', \
returns None if not found.\n If 'name' is not specified, \
it returns a list of all Texts in the\ncurrent scene."""
  cdef c_Text *iter
  cdef Text txt
  cdef char *wantedname
  iter=<c_Text*>G.main.text.first
  if name is not None:
    wantedname=name
    while iter:
      if strcmp(iter.id.name+2, wantedname) == 0:
        txt=Text()
        txt.text=iter
        return txt
      iter=<c_Text*>iter.id.next
    raise NameError('Text "%s" not found'%name)
  else:
    res=[]
    while iter:
      txt=Text()
      txt.text=iter
      res.append(txt)
      iter=<c_Text*>iter.id.next
    return res

# This warning didn't get a proper traceback.
#cdef extern from *:
#  # Pointer types wrong. Why doesn't extern object work? How do you deal with borrowed refs?
#  int PyErr_Warn(void* category, char *message) except -1
#  void* PyExc_DeprecationWarning
def get(*args, **kwargs):
  "Obsolete name for Blender.Text.Get()"
  #PyErr_Warn(PyExc_DeprecationWarning, "Blender.Text.get() is deprecated - use Blender.Text.Get()")
  return Get(*args, **kwargs)

def New(name=None, int follow_cursor=0):
  "(name=None, follow_cursor=0) - return a new Text object"
  cdef c_Text *bl_text
  cdef Text text
  bl_text=add_empty_text()
  if not bl_text:
    raise RuntimeError("couldn't create Text Object in Blender")
  text=Text()
  text.text=bl_text
  if follow_cursor:
    bl_text.flags = bl_text.flags|TXT_FOLLOW
  if name is not None:
    rename_id(&bl_text.id, name)
  return text

def Load(char *filename):
  "(filename) - return text from file filename as a Text Object, \
returns None if not found.\n"
  cdef Text txt
  txt=Text()
  txt.text=add_text(filename)
  if txt.text is NULL:
    raise IOError("couldn't load text")
  return txt

cdef extern from "../BPY_extern.h":
  void BPY_clear_bad_scriptlinks(c_Text *byebye)
cdef extern from "BKE_sca.h":
  void free_text_controllers(c_Text *txt)
cdef extern from "BIF_drawtext.h":
  void unlink_text(c_Text *text)
def unlink(Text text not None):
  "(text) - remove Text object 'text' from Blender"
  if text.text is NULL:
    raise RuntimeError("this text was already unlinked!")
  BPY_clear_bad_scriptlinks(text.text)
  free_text_controllers(text.text)
  unlink_text(text.text)
  free_libblock(&G.main.text, text.text)
  text.text=NULL


# These two are auto-generated by Pyrex
cdef extern from *:	# These are declared "elsewhere"; actually, in Pyrex.
  void *module "__pyx_m"
  void PyErr_Print()
  void* PyErr_Occurred()
# But this one still needs a prototype.. *sigh*
# Also don't know quite how to do error checking, so we don't.
cdef public void initText() #except *
# Blender uses these
cdef public object Text_Init():
  initText()
  if PyErr_Occurred():
    PyErr_Print()
  # Note: Unlike the old Blender.Text module, we keep another reference ourselves.
  return <object>module
cdef public object Text_CreatePyObject(c_Text *text):
  cdef Text txt
  txt=Text()
  txt.text=text
  return txt

-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 189 bytes
Desc: Digital signature
URL: <http://lists.blender.org/pipermail/bf-python/attachments/20041106/32e2a192/attachment.sig>


More information about the Bf-python mailing list