From d65fc84a6815673b9c4085bd4b3e4830167f59fb Mon Sep 17 00:00:00 2001 From: Willian Padovani Germano Date: Sat, 16 Apr 2005 05:25:42 +0000 Subject: Note: this commit includes new functionality to save and restore scripts configure options. This is ongoing work, scripts still have to be updated to use this feature and more tests are needed, though many have been performed. The new Scripts Config Editor script is the main part of this. If anyone wants to check it, only the AC3D importer and exporter have already been updated to use it: simply open them (you can then cancel with ESC) to have the data created, then try the config editor. Scripts: - Thanks Jean-Michel Soler (jms) for updated versions of dispaint, fixfromarmature and unweld (also renamed to remove version part). - Thanks Bart for the upgraded VRML exporter (great doc webpage!). It is available as VRML 97 and the original VRML 2 is for now still there, to help users testing the new version. For the next release the old one should be removed, of course. - New script: Scripts Config Editor (Scripts win -> Scripts -> System). Scripts with config options (simple data that is to be set according to user needs or preferences) can use this facility instead of providing a gui and writing config files to disk themselves. - Added new menu: System, available in the Scripts win. - Updated sys_info.py, help_browse.py and the AC3D importer and exporter. - Removed use of the Scrollbar and added arrow keys and mouse wheel support instead in Daniel Dunbar's old doc_browser.py. The scrollbar events handling doesn't exist, Ton suggested removing the scrollbar from the API months ago. For now its ref doc is gone and no bundled script uses it, until we get time to implement it properly. - Added module BPyRegistry.py with functions to handle reading / writing config files automatically to the scripts/bpydata/config dir. - Removing dir release/bpydata and its contents (moved earlier to release/scripts/bpydata/) - Bug #2379: made small changes to bevel_center's ui to fix a problem reported by Alexander Ewering (intrr): http://projects.blender.org/tracker/?func=detail&atid=125&aid=2379&group_id=9 BPython: - Thanks Campbell Barton for new functionality: Blender.Get() now can also return all the paths from the user prefs -> file paths win and there is a new function: Blender.sys.expandpath() to transform Blender paths (those starting with '//' and ending with '#') to absolute paths. - Added function Blender.ShowHelp(), to open the Scripts Help Browser with a given help page -- just a time saver for scripts. - Improved function Blender.Run() to also work with gui and file select scripts. - Found a (new?) crash related to NMesh.PutRaw when creating a new object while in edit mode. Leaving / entering edit mode fixes the problem, so a check for obj created, edit mode and leaving / re-entering it were added to the code for now (gdb didn't help much, no backtrace) - doc updates, including splitting intro page in two, with bpython related stuff (registering / documenting / configuring scripts and command line mode (thanks Chris Want for "use system variables to pass parameters to scripts" idea). - Registry: functions have been updated to support writing to / reading from disk, for the config editor -- only simple config data supported, for large amounts coders should write to a file themselves. This is done with a new parameter: Registry.GetKey(keyname, True) will also search for the key on the config dir, if not already loaded; equiv. for Registry.SetKey(keyname, dict, True). Data is only written to / read from disk when needed and only scripts already used (assuming they support this functionality) will have config data saved. --- source/blender/python/BPY_menus.c | 5 + source/blender/python/BPY_menus.h | 3 +- source/blender/python/api2_2x/Blender.c | 188 ++++++++++- source/blender/python/api2_2x/EXPP_interface.c | 31 +- source/blender/python/api2_2x/NMesh.c | 7 +- source/blender/python/api2_2x/Registry.c | 85 +++-- source/blender/python/api2_2x/Sys.c | 31 +- source/blender/python/api2_2x/doc/API_intro.py | 195 ++---------- source/blender/python/api2_2x/doc/API_related.py | 377 +++++++++++++++++++++++ source/blender/python/api2_2x/doc/Blender.py | 52 +++- source/blender/python/api2_2x/doc/Draw.py | 78 +++-- source/blender/python/api2_2x/doc/Object.py | 44 +-- source/blender/python/api2_2x/doc/Registry.py | 49 +-- source/blender/python/api2_2x/doc/Render.py | 4 +- source/blender/python/api2_2x/doc/Sys.py | 21 +- 15 files changed, 882 insertions(+), 288 deletions(-) create mode 100644 source/blender/python/api2_2x/doc/API_related.py (limited to 'source/blender/python') diff --git a/source/blender/python/BPY_menus.c b/source/blender/python/BPY_menus.c index 40dd8543762..e71d06b4141 100644 --- a/source/blender/python/BPY_menus.c +++ b/source/blender/python/BPY_menus.c @@ -94,6 +94,8 @@ static int bpymenu_group_atoi( char *str ) return PYMENU_HELPSYSTEM; else if( !strcmp( str, "Render" ) ) return PYMENU_RENDER; + else if( !strcmp( str, "System" ) ) + return PYMENU_SYSTEM; else if( !strcmp( str, "Object" ) ) return PYMENU_OBJECT; else if( !strcmp( str, "Mesh" ) ) @@ -141,6 +143,9 @@ char *BPyMenu_group_itoa( short menugroup ) case PYMENU_RENDER: return "Render"; break; + case PYMENU_SYSTEM: + return "System"; + break; case PYMENU_OBJECT: return "Object"; break; diff --git a/source/blender/python/BPY_menus.h b/source/blender/python/BPY_menus.h index 52ab5e85e4a..8ef94b1ce79 100644 --- a/source/blender/python/BPY_menus.h +++ b/source/blender/python/BPY_menus.h @@ -25,7 +25,7 @@ * * This is a new part of Blender. * - * Contributor(s): Willian P. Germano + * Contributor(s): Willian P. Germano, Matt Ebb * * ***** END GPL/BL DUAL LICENSE BLOCK ***** */ @@ -87,6 +87,7 @@ typedef enum { PYMENU_MISC, PYMENU_OBJECT, PYMENU_RENDER,/* exporters to external renderers */ + PYMENU_SYSTEM, PYMENU_THEMES, PYMENU_UV,/* UV editing tools, to go in UV/Image editor space, 'UV' menu */ PYMENU_WIZARDS,/* complex 'app' scripts */ diff --git a/source/blender/python/api2_2x/Blender.c b/source/blender/python/api2_2x/Blender.c index 988e9ac67bd..e2f68a9a6f9 100644 --- a/source/blender/python/api2_2x/Blender.c +++ b/source/blender/python/api2_2x/Blender.c @@ -24,7 +24,8 @@ * * This is a new part of Blender. * - * Contributor(s): Michel Selten, Willian P. Germano, Joseph Gilbert + * Contributor(s): Michel Selten, Willian P. Germano, Joseph Gilbert, + * Campbell Barton * * ***** END GPL/BL DUAL LICENSE BLOCK ***** */ @@ -66,6 +67,8 @@ #include "../BPY_extern.h" /* BPY_txt_do_python_Text */ #include "../BPY_menus.h" /* to update menus */ +extern PyObject *bpy_registryDict; /* defined in ../BPY_interface.c */ + /**********************************************************/ /* Python API function prototypes for the Blender module. */ /**********************************************************/ @@ -76,6 +79,7 @@ static PyObject *Blender_Quit( PyObject * self ); static PyObject *Blender_Load( PyObject * self, PyObject * args ); static PyObject *Blender_Save( PyObject * self, PyObject * args ); static PyObject *Blender_Run( PyObject * self, PyObject * args ); +static PyObject *Blender_ShowHelp( PyObject * self, PyObject * args ); static PyObject *Blender_UpdateMenus( PyObject * self); extern PyObject *Text3d_Init( void ); /* missing in some include */ @@ -137,6 +141,15 @@ static char Blender_Run_doc[] = "(script) - Run the given Python script.\n\ (script) - the path to a file or the name of an available Blender Text."; +static char Blender_ShowHelp_doc[] = +"(script) - Show help for the given Python script.\n\ + This will try to open the 'Scripts Help Browser' script, so to have\n\ +any help displayed the passed 'script' must be properly documented\n\ +with the expected strings (check API ref docs or any bundled script\n\ +for examples).\n\n\ +(script) - the filename of a script in the default or user defined\n\ + scripts dir (no need to supply the full path name)."; + static char Blender_UpdateMenus_doc[] = "() - Update the menus where scripts are registered. Only needed for\n\ scripts that save other new scripts in the default or user defined folders."; @@ -152,6 +165,7 @@ static struct PyMethodDef Blender_methods[] = { {"Load", Blender_Load, METH_VARARGS, Blender_Load_doc}, {"Save", Blender_Save, METH_VARARGS, Blender_Save_doc}, {"Run", Blender_Run, METH_VARARGS, Blender_Run_doc}, + {"ShowHelp", Blender_ShowHelp, METH_VARARGS, Blender_ShowHelp_doc}, {"UpdateMenus", ( PyCFunction ) Blender_UpdateMenus, METH_NOARGS, Blender_UpdateMenus_doc}, {NULL, NULL, 0, NULL} @@ -272,6 +286,110 @@ static PyObject *Blender_Get( PyObject * self, PyObject * args ) } if (!ret) ret = EXPP_incr_ret(Py_None); } + /* USER PREFS: */ + else if( StringEqual( str, "yfexportdir" ) ) { + if (U.yfexportdir[0] != '\0') { + char yfexportdir[FILE_MAXDIR]; + + BLI_strncpy(yfexportdir, U.yfexportdir, FILE_MAXDIR); + BLI_convertstringcode(yfexportdir, G.sce, 0); + + if( BLI_exists( yfexportdir ) ) + ret = PyString_FromString( yfexportdir ); + } + if (!ret) ret = EXPP_incr_ret(Py_None); + } + /* fontsdir */ + else if( StringEqual( str, "fontsdir" ) ) { + if (U.fontdir[0] != '\0') { + char fontdir[FILE_MAXDIR]; + + BLI_strncpy(fontdir, U.fontdir, FILE_MAXDIR); + BLI_convertstringcode(fontdir, G.sce, 0); + + if( BLI_exists( fontdir ) ) + ret = PyString_FromString( fontdir ); + } + if (!ret) ret = EXPP_incr_ret(Py_None); + } + /* texturesdir */ + else if( StringEqual( str, "texturesdir" ) ) { + if (U.textudir[0] != '\0') { + char textudir[FILE_MAXDIR]; + + BLI_strncpy(textudir, U.textudir, FILE_MAXDIR); + BLI_convertstringcode(textudir, G.sce, 0); + + if( BLI_exists( textudir ) ) + ret = PyString_FromString( textudir ); + } + if (!ret) ret = EXPP_incr_ret(Py_None); + } + /* texpluginsdir */ + else if( StringEqual( str, "texpluginsdir" ) ) { + if (U.plugtexdir[0] != '\0') { + char plugtexdir[FILE_MAXDIR]; + + BLI_strncpy(plugtexdir, U.plugtexdir, FILE_MAXDIR); + BLI_convertstringcode(plugtexdir, G.sce, 0); + + if( BLI_exists( plugtexdir ) ) + ret = PyString_FromString( plugtexdir ); + } + if (!ret) ret = EXPP_incr_ret(Py_None); + } + /* seqpluginsdir */ + else if( StringEqual( str, "seqpluginsdir" ) ) { + if (U.plugseqdir[0] != '\0') { + char plugseqdir[FILE_MAXDIR]; + + BLI_strncpy(plugseqdir, U.plugseqdir, FILE_MAXDIR); + BLI_convertstringcode(plugseqdir, G.sce, 0); + + if( BLI_exists( plugseqdir ) ) + ret = PyString_FromString( plugseqdir ); + } + if (!ret) ret = EXPP_incr_ret(Py_None); + } + /* renderdir */ + else if( StringEqual( str, "renderdir" ) ) { + if (U.renderdir[0] != '\0') { + char renderdir[FILE_MAXDIR]; + + BLI_strncpy(renderdir, U.renderdir, FILE_MAXDIR); + BLI_convertstringcode(renderdir, G.sce, 0); + + if( BLI_exists( renderdir ) ) + ret = PyString_FromString( renderdir ); + } + if (!ret) ret = EXPP_incr_ret(Py_None); + } + /* soundsdir */ + else if( StringEqual( str, "soundsdir" ) ) { + if (U.sounddir[0] != '\0') { + char sounddir[FILE_MAXDIR]; + + BLI_strncpy(sounddir, U.sounddir, FILE_MAXDIR); + BLI_convertstringcode(sounddir, G.sce, 0); + + if( BLI_exists( sounddir ) ) + ret = PyString_FromString( sounddir ); + } + if (!ret) ret = EXPP_incr_ret(Py_None); + } + /* tempdir */ + else if( StringEqual( str, "tempdir" ) ) { + if (U.tempdir[0] != '\0') { + char tempdir[FILE_MAXDIR]; + + BLI_strncpy(tempdir, U.tempdir, FILE_MAXDIR); + BLI_convertstringcode(tempdir, G.sce, 0); + + if( BLI_exists( tempdir ) ) + ret = PyString_FromString( tempdir ); + } + if (!ret) ret = EXPP_incr_ret(Py_None); + } /* According to the old file (opy_blender.c), the following if statement is a quick hack and needs some clean up. */ else if( StringEqual( str, "vrmloptions" ) ) { @@ -493,11 +611,56 @@ static PyObject *Blender_Save( PyObject * self, PyObject * args ) return Py_None; } +static PyObject *Blender_ShowHelp(PyObject *self, PyObject *args) +{ + PyObject *script = NULL; + char hspath[FILE_MAXDIR + FILE_MAXFILE]; /* path to help_browser.py */ + char *sdir = bpy_gethome(1); + PyObject *rkeyd = NULL, *arglist = NULL; + + if (!PyArg_ParseTuple(args, "O!", &PyString_Type, &script)) + return EXPP_ReturnPyObjError(PyExc_TypeError, + "expected a script filename as argument"); + + /* first we try to find the help_browser script */ + + if (sdir) BLI_make_file_string("/", hspath, sdir, "help_browser.py"); + + if (!sdir || (!BLI_exists(hspath) && (U.pythondir[0] != '\0'))) { + char upydir[FILE_MAXDIR]; + + BLI_strncpy(upydir, U.pythondir, FILE_MAXDIR); + BLI_convertstringcode(upydir, G.sce, 0); + BLI_make_file_string("/", hspath, upydir, "help_browser.py"); + + if (!BLI_exists(hspath)) + return EXPP_ReturnPyObjError(PyExc_RuntimeError, + "can't find script help_browser.py"); + } + + /* now we store the passed script in the registry dict and call the + * help_browser to show help info for it */ + rkeyd = PyDict_New(); + if (!rkeyd) + return EXPP_ReturnPyObjError(PyExc_MemoryError, + "can't create py dictionary!"); + + PyDict_SetItemString(rkeyd, "script", script); + PyDict_SetItemString(bpy_registryDict, "__help_browser", rkeyd); + + arglist = Py_BuildValue("(s)", hspath); + Blender_Run(self, arglist); + Py_DECREF(arglist); + + return EXPP_incr_ret(Py_None); +} + static PyObject *Blender_Run(PyObject *self, PyObject *args) { char *fname = NULL; Text *text = NULL; int is_blender_text = 0; + Script *script = NULL; if (!PyArg_ParseTuple(args, "s", &fname)) return EXPP_ReturnPyObjError(PyExc_TypeError, @@ -527,7 +690,28 @@ static PyObject *Blender_Run(PyObject *self, PyObject *args) } } - BPY_txt_do_python_Text(text); + /* (this is messy, check Draw.c's Method_Register and Window.c's file + * selector for more info) + * - caller script is the one that called this Blender_Run function; + * - called script is the argument to this function: fname; + * To mark scripts whose global dicts can't be freed right after + * the script execution (or better, 'first pass', since these scripts + * leave callbacks for gui or file/image selectors) we flag them. But to + * get a pointer to them we need to check which one is currently + * running (if none we're already at a spacescript). To make sure only + * the called script will have the SCRIPT_RUNNING flag on, we unset it + * for the caller script here: */ + script = G.main->script.first; + while (script) { + if (script->flags & SCRIPT_RUNNING) break; + script = script->id.next; + } + + if (script) script->flags &= ~SCRIPT_RUNNING; /* unset */ + + BPY_txt_do_python_Text(text); /* call new script */ + + if (script) script->flags |= SCRIPT_RUNNING; /* set */ if (!is_blender_text) free_libblock(&G.main->text, text); diff --git a/source/blender/python/api2_2x/EXPP_interface.c b/source/blender/python/api2_2x/EXPP_interface.c index 4be155db944..62d1f4e799f 100644 --- a/source/blender/python/api2_2x/EXPP_interface.c +++ b/source/blender/python/api2_2x/EXPP_interface.c @@ -65,6 +65,7 @@ char *bpy_gethome(int append_scriptsdir) { static char homedir[FILE_MAXDIR]; static char scriptsdir[FILE_MAXDIR]; + char tmpdir[FILE_MAXDIR]; char bprogdir[FILE_MAXDIR]; char *s; int i; @@ -91,20 +92,37 @@ char *bpy_gethome(int append_scriptsdir) } else return homedir; } + else homedir[0] = '\0'; - /* otherwise, use argv[0] (bprogname) to get .blender/ in + /* if either: + * no homedir was found or + * append_scriptsdir = 1 but there's no scripts/ inside homedir, + * use argv[0] (bprogname) to get .blender/ in * Blender's installation dir */ s = BLI_last_slash( bprogname ); i = s - bprogname + 1; - PyOS_snprintf( bprogdir, i, bprogname ); - BLI_make_file_string( "/", homedir, bprogdir, ".blender" ); + PyOS_snprintf( bprogdir, i, "%s", bprogname ); - if (BLI_exists(homedir)) { + /* using tmpdir to preserve homedir (if) found above: + * the ideal is to have a home dir with scripts dir inside + * it, but if that isn't available, it's possible to + * have a 'broken' home dir somewhere and a scripts dir in the + * cvs sources */ + BLI_make_file_string( "/", tmpdir, bprogdir, ".blender" ); + + if (BLI_exists(tmpdir)) { if (append_scriptsdir) { - BLI_make_file_string("/", scriptsdir, homedir, "scripts"); - if (BLI_exists(scriptsdir)) return scriptsdir; + BLI_make_file_string("/", scriptsdir, tmpdir, "scripts"); + if (BLI_exists(scriptsdir)) { + PyOS_snprintf(homedir, FILE_MAXDIR, "%s", tmpdir); + return scriptsdir; + } + else { + homedir[0] = '\0'; + scriptsdir[0] = '\0'; + } } else return homedir; } @@ -113,6 +131,7 @@ char *bpy_gethome(int append_scriptsdir) if (append_scriptsdir) { BLI_make_file_string("/", scriptsdir, bprogdir, "release/scripts"); if (BLI_exists(scriptsdir)) return scriptsdir; + else scriptsdir[0] = '\0'; } return NULL; diff --git a/source/blender/python/api2_2x/NMesh.c b/source/blender/python/api2_2x/NMesh.c index f2bf3aa31e4..2e6f2ca2601 100644 --- a/source/blender/python/api2_2x/NMesh.c +++ b/source/blender/python/api2_2x/NMesh.c @@ -2796,7 +2796,7 @@ static PyObject *M_NMesh_PutRaw( PyObject * self, PyObject * args ) ob = add_object( OB_MESH ); if( !ob ) { PyErr_SetString( PyExc_RuntimeError, - "Fatal: could not create mesh object" ); + "Fatal: could not create mesh object" ); return 0; } @@ -2824,6 +2824,11 @@ static PyObject *M_NMesh_PutRaw( PyObject * self, PyObject * args ) if( !during_script( ) ) EXPP_allqueue( REDRAWVIEW3D, 0 ); + if (ob && G.obedit) { /* prevents a crash when a new object is created */ + exit_editmode(1); + enter_editmode(); + } + // @OK...this requires some explanation: // Materials can be assigned two ways: // a) to the object data (in this case, the mesh) diff --git a/source/blender/python/api2_2x/Registry.c b/source/blender/python/api2_2x/Registry.c index e0ba81b1029..bf839630aa2 100644 --- a/source/blender/python/api2_2x/Registry.c +++ b/source/blender/python/api2_2x/Registry.c @@ -33,11 +33,11 @@ #include "Registry.h" #include +#include /* G.f & G_DEBUG */ #include "gen_utils.h" - /* the Registry dictionary */ PyObject *bpy_registryDict = NULL; @@ -64,17 +64,23 @@ char M_Registry_Keys_doc[] = Each key references another dict with saved data from a specific script.\n"; char M_Registry_GetKey_doc[] = - "(name) - Get a specific entry (dict) from the Registry dictionary\n\ - (name) - a string that references a specific script.\n"; + "(name, disk = False) - Get an entry (a dict) from the Registry dictionary\n\ + (name) - a string that references a specific script;\n\ + (disk = False) - search on the user (if available) or default scripts config\n\ +data dir.\n"; char M_Registry_SetKey_doc[] = - "(key, dict) - Store an entry in the Registry dictionary.\n\ + "(key, dict, disk = False) - Store an entry in the Registry dictionary.\n\ If an entry with the same 'key' already exists, it is substituted.\n\ (key) - the string to use as a key for the dict being saved.\n\ - (dict) - a dictionary with the data to be stored.\n"; + (dict) - a dictionary with the data to be stored.\n\ + (disk = False) - also write data as a config file inside the user (if\n\ +available) or default scripts config data dir.\n"; char M_Registry_RemoveKey_doc[] = - "(key) - Remove the dict with key 'key' from the Registry.\n"; + "(key, disk = False) - Remove the dict with key 'key' from the Registry.\n\ + (key) - the name of the key to delete;\n\ + (disk = False) - if True the respective config file is also deleted.\n"; /*****************************************************************************/ /* Python method structure definition for Blender.Registry module: */ @@ -118,25 +124,35 @@ static PyObject *M_Registry_GetKey( PyObject * self, PyObject * args ) { PyObject *pyentry = NULL; PyObject *pydict = NULL; + int disk = 0; if( !bpy_registryDict ) return EXPP_ReturnPyObjError( PyExc_RuntimeError, - "No Registry dictionary found!" ); + "No Registry dictionary found!" ); - if( !PyArg_ParseTuple( args, "O!", &PyString_Type, &pyentry ) ) + if( !PyArg_ParseTuple( args, "O!|i", &PyString_Type, &pyentry, &disk ) ) return EXPP_ReturnPyObjError( PyExc_AttributeError, - "expected a string" ); + "expected a string and optionally a bool" ); pydict = PyDict_GetItem( bpy_registryDict, pyentry ); /* borrowed ... */ - if( !pydict ) -/* return EXPP_ReturnPyObjError (PyExc_KeyError, - "no such key in the Registry"); */ - pydict = Py_None; /* better to return None than an error */ - - Py_INCREF( pydict ); /* ... so we incref it */ - /* should we copy the dict instead? */ - return pydict; + if (!pydict) { + if (disk > 0) { + /* try to get data from disk */ + char buf[256]; + PyOS_snprintf(buf, sizeof(buf), + "import Blender, BPyRegistry; BPyRegistry.LoadConfigData('%s')", + PyString_AsString(pyentry)); + if (!PyRun_SimpleString(buf)) + pydict = PyDict_GetItem(bpy_registryDict, pyentry); + else PyErr_Clear(); + } + + if (!pydict) /* no need to return a KeyError, since without doubt */ + pydict = Py_None; /* Py_None means no key (all valid keys are dicts) */ + } + + return EXPP_incr_ret (pydict); /* ... so we incref it */ } /*****************************************************************************/ @@ -147,14 +163,15 @@ static PyObject *M_Registry_SetKey( PyObject * self, PyObject * args ) { PyObject *pystr = NULL; PyObject *pydict = NULL; + int disk = 0; if( !bpy_registryDict ) return EXPP_ReturnPyObjError( PyExc_RuntimeError, "No Registry dictionary found!" ); - if( !PyArg_ParseTuple( args, "O!O!", + if( !PyArg_ParseTuple( args, "O!O!|i", &PyString_Type, &pystr, &PyDict_Type, - &pydict ) ) + &pydict, &disk ) ) return EXPP_ReturnPyObjError( PyExc_AttributeError, "expected a string and a dictionary" ); @@ -162,6 +179,19 @@ static PyObject *M_Registry_SetKey( PyObject * self, PyObject * args ) return EXPP_ReturnPyObjError( PyExc_RuntimeError, "Registry_SetKey: couldn't update the Registry dict" ); + if (disk) { + /* try to save data to disk */ + char buf[256]; + PyOS_snprintf(buf, sizeof(buf), + "import Blender, BPyRegistry; BPyRegistry.SaveConfigData('%s')", + PyString_AsString(pystr)); + if (PyRun_SimpleString(buf) != 0) { + PyErr_Clear(); + if (G.f & G_DEBUG) + fprintf(stderr, "\nCan't save script configuration data!\n"); + } + } + Py_INCREF( Py_None ); return Py_None; } @@ -173,18 +203,31 @@ static PyObject *M_Registry_SetKey( PyObject * self, PyObject * args ) static PyObject *M_Registry_RemoveKey( PyObject * self, PyObject * args ) { PyObject *pystr = NULL; + int disk = 0; if( !bpy_registryDict ) return EXPP_ReturnPyObjError( PyExc_RuntimeError, "No Registry dictionary found!" ); - if( !PyArg_ParseTuple( args, "O!", &PyString_Type, &pystr ) ) + if( !PyArg_ParseTuple( args, "O!|i", &PyString_Type, &pystr, &disk ) ) return EXPP_ReturnPyObjError( PyExc_AttributeError, - "expected a string" ); + "expected a string and optionally a bool" ); if( PyDict_DelItem( bpy_registryDict, pystr ) ) /* returns 0 on success */ return EXPP_ReturnPyObjError( PyExc_KeyError, "no such key in the Registry" ); + else if (disk) { + /* try to delete from disk too */ + char buf[256]; + PyOS_snprintf(buf, sizeof(buf), + "import Blender, BPyRegistry; BPyRegistry.RemoveConfigData('%s')", + PyString_AsString(pystr)); + if (PyRun_SimpleString(buf) != 0) { + PyErr_Clear(); + if (G.f & G_DEBUG) + fprintf(stderr, "\nCan't remove script configuration data file!\n"); + } + } Py_INCREF( Py_None ); return Py_None; diff --git a/source/blender/python/api2_2x/Sys.c b/source/blender/python/api2_2x/Sys.c index 5af539ac881..64bdc49bf79 100644 --- a/source/blender/python/api2_2x/Sys.c +++ b/source/blender/python/api2_2x/Sys.c @@ -25,13 +25,14 @@ * * This is a new part of Blender. * - * Contributor(s): Willian P. Germano + * Contributor(s): Willian P. Germano, Campbell Barton * * ***** END GPL/BL DUAL LICENSE BLOCK ***** */ #include #include +#include /* G.scene->r.cfra */ #include #include #include @@ -47,10 +48,11 @@ static PyObject *M_sys_dirname( PyObject * self, PyObject * args ); static PyObject *M_sys_join( PyObject * self, PyObject * args ); static PyObject *M_sys_splitext( PyObject * self, PyObject * args ); static PyObject *M_sys_makename( PyObject * self, PyObject * args, - PyObject * kw ); + PyObject * kw ); static PyObject *M_sys_exists( PyObject * self, PyObject * args ); static PyObject *M_sys_time( PyObject * self ); static PyObject *M_sys_sleep( PyObject * self, PyObject * args ); +static PyObject *M_sys_expandpath( PyObject *self, PyObject *args); /*****************************************************************************/ /* The following string definitions are used for documentation strings. */ @@ -108,6 +110,15 @@ The return value is as follows:\n\ \t 2: path is an existing dirname;\n\ \t-1: path exists but is neither a regular file nor a dir."; +static char M_sys_expandpath_doc[] = +"(path) - Expand this Blender internal path to a proper file system path.\n\ +(path) - the string path to convert.\n\n\ +Note: internally Blender paths can contain two special character sequences:\n\ +- '//' (at start) for base path directory (the current .blend's dir path);\n\ +- '#' (at ending) for current frame number.\n\n\ +This function expands these to their actual content, returning a valid path.\n\ +If the special chars are not found in the given path, it is simply returned."; + /*****************************************************************************/ /* Python method structure definition for Blender.sys module: */ /*****************************************************************************/ @@ -122,6 +133,7 @@ struct PyMethodDef M_sys_methods[] = { {"exists", M_sys_exists, METH_VARARGS, M_sys_exists_doc}, {"sleep", M_sys_sleep, METH_VARARGS, M_sys_sleep_doc}, {"time", ( PyCFunction ) M_sys_time, METH_NOARGS, M_sys_time_doc}, + {"expandpath", M_sys_expandpath, METH_VARARGS, M_sys_expandpath_doc}, {NULL, NULL, 0, NULL} }; @@ -395,3 +407,18 @@ static PyObject *M_sys_exists( PyObject * self, PyObject * args ) return Py_BuildValue( "i", i ); } + +static PyObject *M_sys_expandpath( PyObject * self, PyObject * args ) +{ + char *path = NULL; + char expanded[FILE_MAXDIR + FILE_MAXFILE]; + + if (!PyArg_ParseTuple( args, "s", &path)) + return EXPP_ReturnPyObjError( PyExc_TypeError, + "expected string argument" ); + + BLI_strncpy(expanded, path, FILE_MAXDIR + FILE_MAXFILE); + BLI_convertstringcode(expanded, G.sce, G.scene->r.cfra); + + return PyString_FromString(expanded); +} diff --git a/source/blender/python/api2_2x/doc/API_intro.py b/source/blender/python/api2_2x/doc/API_intro.py index 8f4031756f2..ce2b7d8fc93 100644 --- a/source/blender/python/api2_2x/doc/API_intro.py +++ b/source/blender/python/api2_2x/doc/API_intro.py @@ -40,9 +40,16 @@ The Blender Python API Reference - L{Texture} - L{Types} - L{Window} - - L{Theme} (new) + - L{Theme} - L{World} - - L{sys} + - L{sys} (*) + + Additional information: + ----------------------- + + - L{Misc facilities}: + - scripts: registering in menus, documenting, configuring (new); + - command line examples (new). (*) - marks updated. @@ -91,57 +98,6 @@ These are the basic ways to execute scripts in Blender: 6. A script can call another script (that will run in its own context, with its own global dictionary) with the L{Blender.Run} module function. -Registering scripts: --------------------- - To be registered a script needs two things: - - be either in the default scripts dir or in the user defined scripts path - (see Info window, paths tab); - - have a proper header. - - Try 'blender -d' to know where your default dir for scripts is, it will - inform either the dir or the file with that info already parsed, which is - in the same dir of the scripts folder. - - The header should be like this one (all double and single apostrophes below - are required):: - #!BPY - - # \"\"\" - # Name: 'Script Name' - # Blender: 233 - # Group: 'Export' - # Submenu: 'All' all - # Submenu: 'Selected' sel - # Submenu: 'Configure (gui)' gui - # Tooltip: 'Export to some format.' - # \"\"\" - - where: - - B{Name} is the string that will appear in the menu; - - B{Blender} is the minimum program version required to run the script; - - B{Group} defines where the script will be put, see all groups in the - Scripts Window's header, menu "Scripts"; - - B{Submenu} adds optional submenus for further control; - - B{Tooltip} is the (short) tooltip string for the menu entry. - - note: - - all double and single apostrophes above are required; - - B{*NEW*}: you can "comment out" the header above, by starting lines with - '#', like we did. This is not required (except for the first line, #!BPY, - of course), but this way the header won't conflict with Python tools that - you can use to generate documentation for your script code. Just - remember to keep this header above any other line with triple - double-quotes (\"\"\") in your script. - - Submenu lines are not required, use them if you want to provide extra - options. To see which submenu the user chose, check the "__script__" - dictionary in your code: __script__['arg'] has the defined keyword (the word - after the submenu string name: all, sel or gui in the example above) of the - chosen submenu. For example, if the user clicked on submenu 'Selected' above, - __script__['arg'] will be "sel". - - If your script requires extra data or configuration files, there is a special - folder where they can be saved: see 'datadir' in L{Blender.Get}. Interaction with users: ----------------------- @@ -171,53 +127,22 @@ Command line mode: run scripts from the program itself: you can't import the Blender module into an external Python interpreter. - But with "OnLoad" script links, the "-b" background mode and additions like - the "-P" command line switch, L{Blender.Save}, L{Blender.Load}, - L{Blender.Quit} and the L{Library} module, for many tasks it's possible to - control Blender via some automated process using scripts. Note that command - line scripts are run before Blender initializes its windows (and in '-b' mode - no window will be initialized), so many functions that get or set window - related attributes (like most in L{Window}) don't work here. If you need - those, use an ONLOAD script link (see L{Scene.Scene.addScriptLink}) instead -- - it's also possible to use a command line script to write or set an ONLOAD - script link. Check the L{Blender.mode} module var to know if Blender is being - executed in "background" or "interactive" mode. - - Background mode examples:: - - # Open Blender in background mode with file 'myfile.blend' - # and run the script 'script.py': - - blender -b myfile.blend -P script.py - - # Note: a .blend file is always required. 'script.py' can be a file - # in the file system or a Blender Text stored in 'myfile.blend'. + On the other hand, for many tasks it's possible to control Blender via + some automated process using scripts. Interested readers should learn about + features like "OnLoad" script links, the "-b " (background mode) + and "-P