diff options
-rw-r--r-- | source/blender/python/BPY_menus.c | 341 | ||||
-rw-r--r-- | source/blender/python/api2_2x/Blender.c | 15 | ||||
-rw-r--r-- | source/blender/python/api2_2x/NMesh.c | 4 | ||||
-rw-r--r-- | source/blender/python/api2_2x/doc/Blender.py | 6 | ||||
-rw-r--r-- | source/blender/python/api2_2x/doc/Window.py | 24 |
5 files changed, 224 insertions, 166 deletions
diff --git a/source/blender/python/BPY_menus.c b/source/blender/python/BPY_menus.c index 5a2db4d1be2..6bae8fdf3e0 100644 --- a/source/blender/python/BPY_menus.c +++ b/source/blender/python/BPY_menus.c @@ -25,7 +25,7 @@ * * This is a new part of Blender. * - * Contributor(s): Willian P. Germano + * Contributor(s): Willian P. Germano, Michael Reimpell * * ***** END GPL/BL DUAL LICENSE BLOCK ***** */ @@ -583,171 +583,198 @@ void BPyMenu_PrintAllEntries( void ) } } -/* bpymenu_GetDataFromDir: - * this function scans the scripts dir looking for .py files with the +/** Creates BPyMenu entries for scripts from directory. + * + * This function scans the scripts directory looking for .py files with the * right header and menu info, using that to fill the bpymenu structs. * whichdir defines if the script is in the default scripts dir or the * user defined one (U.pythondir: whichdir == 1). * Speed is important. -*/ + * <code>whichdir</code> defines if the script is in the default scripts dir + * or the user defined one (U.pythondir: <code>whichdir == 1</code>). + * <p> + * The first line of the script must be '<code>#!BPY</code>'. + * The header registration lines must appear between the first pair of + * '<code># \"\"\"</code>' and follow this order (the single-quotes are part of + * the format): + * <p> + * <code> + * # \"\"\"<br> + * # Name: 'script name for the menu'<br> + * # Blender: <code>short int</code> (minimal Blender version)<br> + * # Group: 'group name' (defines menu)<br> + * # Submenu: 'submenu name' related_1word_arg<br> + * # Tooltip: 'tooltip for the menu'<br> + * # \"\"\"<br> + * </code> + * <p> + * Notes: + * <ul> + * <li> There may be more than one submenu line, or none: + * Submenus and the tooltip are optional; + * <li> The Blender version is the same number reported by + * <code>Blender.Get('version')</code> in BPython or <code>G.version</code> + * in C; + * <li> Line length must be less than 99. + * <li> Script headers as python documentation strings without the leading + * hash character (#) should no longer be used. + * </ul> + * + * @param dirname Directory name to scan. + * @param whichdir Specifies the directory. 1 if user defined script directory, + * else default script directory. + * @return 0 on success. + */ + static int bpymenu_CreateFromDir( char *dirname, int whichdir ) { - DIR *dir; - FILE *fp; - struct stat st; - struct dirent *dir_entry; - BPyMenu *pymenu; - char *s, *fname, pathstr[FILE_MAXFILE + FILE_MAXDIR]; - char line[100], w[100]; - char name[100], submenu[100], subarg[100], tooltip[100]; - int res = 0, version = 0; - - dir = opendir( dirname ); - - if( !dir ) { - if ( DEBUG ) - printf("BPyMenus warning: could not open dir %s.\n", dirname); - return -1; - } - -/* we scan the dir for filenames ending with .py and starting with the - * right 'magic number': '#!BPY'. All others are ignored. */ - - while( ( dir_entry = readdir( dir ) ) != NULL ) { - fname = dir_entry->d_name; - /* ignore anything starting with a dot */ - if( fname[0] == '.' ) - continue; /* like . and .. */ - - /* also skip filenames whose extension isn't '.py' */ - s = strstr( fname, ".py" ); - if( !s || *( s + 3 ) != '\0' ) - continue; - - BLI_make_file_string( "/", pathstr, dirname, fname ); - - /* paranoia: check if this is really a file and not a disguised dir */ - if( ( stat( pathstr, &st ) == -1 ) || !S_ISREG( st.st_mode ) ) - continue; - - fp = fopen( pathstr, "rb" ); - - if( !fp ) { - if( DEBUG ) - printf( "BPyMenus error: couldn't open %s.\n", - pathstr ); - continue; - } - - /* finally, look for the start string '#!BPY', with - * or w/o white space(s) between #! and BPY */ - fgets( line, 100, fp ); - if( line[0] != '#' || line[1] != '!' ) - goto discard; - - if( !strstr( line, "BPY" ) ) - goto discard; - - /* file passed the tests, look for the three double-quotes */ - while( fgets( line, 100, fp ) ) { - if( strstr( line, "\"\"\"" )) { - res = 1; /* found */ - break; + DIR *dir; /* directory stream object */ + FILE *currentFile; /* file stream object */ + struct dirent *dirEntry; /* directory entry */ + struct stat fileStatus; + char *fileExtension; + char fileName[FILE_MAXFILE + FILE_MAXDIR]; /* filename including path */ + /* parser variables */ + char line[100]; + char head[100]; + char middle[100]; + char tail[100]; + int nMatches; + int parserState; + /* script header variables */ + char scriptName[100]; + int scriptBlender; + int scriptGroup; + BPyMenu *scriptMenu = NULL; + /* other */ + int scanDir = 1; + int returnValue = 0; + + /* open directory stream */ + dir = opendir(dirname); + if (dir != NULL) { + /* directory stream opened */ + while (((dirEntry = readdir(dir)) != NULL) && (scanDir == 1)) { + /* Check if filename does not start with a dot, + * ends with '.py' and is a regular file. */ + BLI_make_file_string("/", fileName, dirname, dirEntry->d_name); + fileExtension = strstr(dirEntry->d_name, ".py"); + + if ((strncmp(dirEntry->d_name, ".", 1) != 0) + && (fileExtension != NULL) + && (*(fileExtension + 3) == '\0') + && (stat(fileName, &fileStatus) == 0) + && (S_ISREG(fileStatus.st_mode))) { + /* check file header */ + currentFile = fopen(fileName, "rb"); + if (currentFile != NULL) { + parserState = 1; /* state of parser, 0 to terminate */ + while ((parserState != 0) && (fgets(line, 100, currentFile) != NULL)) { + switch (parserState) { + case 1: /* #!BPY */ + if (strncmp(line, "#!BPY", 5) == 0) { + parserState++; + } else { + parserState = 0; + } + break; + case 2: /* # \"\"\" */ + if ((strstr(line, "\"\"\""))) { + parserState++; + } + break; + case 3: /* # Name: 'script name for the menu' */ + nMatches = sscanf(line, "%[^']'%[^']'%c", head, scriptName, tail); + if ((nMatches == 3) && (strstr(head, "Name:") != NULL)) { + parserState++; + } else { + if (DEBUG) { + fprintf(stderr, "BPyMenus error: Wrong 'Name' line: %s\n", fileName); + } + parserState = 0; + } + break; + case 4: /* # Blender: <short int> */ + nMatches = sscanf(line, "%[^1234567890]%i%c", head, &scriptBlender, tail); + if (nMatches == 3) { + parserState++; + } else { + if (DEBUG) { + fprintf(stderr, "BPyMenus error: Wrong 'Blender' line: %s\n", fileName); + } + parserState = 0; + } + break; + case 5: /* # Group: 'group name' */ + nMatches = sscanf(line, "%[^']'%[^']'%c", head, middle, tail); + if ((nMatches == 3) && (strstr(head, "Group:") != NULL)) { + scriptGroup = bpymenu_group_atoi(middle); + if (scriptGroup < 0) { + if (DEBUG) { + fprintf(stderr, "BPyMenus error: Unknown group \"%s\": %s\n", middle, fileName); + } + parserState = 0; + } else { + /* register script */ + scriptMenu = bpymenu_AddEntry(scriptGroup, (short int) scriptBlender, scriptName, + dirEntry->d_name, whichdir, NULL); + if (scriptMenu == NULL) { + if (DEBUG) { + fprintf(stderr, "BPyMenus error: Couldn't create entry for: %s\n", fileName); + } + /* abort */ + parserState = 0; + scanDir = 0; + returnValue = -2; + } else { + parserState++; + } + } + } else { + if (DEBUG) { + fprintf(stderr, "BPyMenus error: Wrong 'Group' line: %s\n", fileName); + } + parserState = 0; + } + break; + case 6: /* optional elements */ + /* # Submenu: 'submenu name' related_1word_arg */ + nMatches = sscanf(line, "%[^']'%[^']'%s\n", head, middle, tail); + if ((nMatches == 3) && (strstr(head, "Submenu:") != NULL)) { + bpymenu_AddSubEntry(scriptMenu, middle, tail); + } else { + /* # Tooltip: 'tooltip for the menu */ + nMatches = sscanf(line, "%[^']'%[^']'%c", head, middle, tail); + if ((nMatches == 3) + && ((strstr(head, "Tooltip:") != NULL) || (strstr(head, "Tip:") != NULL))) { + bpymenu_set_tooltip(scriptMenu, middle); + } + parserState = 0; + } + break; + default: + parserState = 0; + break; + } + } + /* close file stream */ + fclose(currentFile); + } else { + /* open file failed */ + if(DEBUG) { + fprintf(stderr, "BPyMenus error: Couldn't open %s.\n", dirEntry->d_name); + } + } } } - - if( !res ) - goto discard; - - /* Now we're ready to get the registration info. A little more structure - * was imposed to the format, for speed. The registration lines must - * appear between the first pair of triple double-quotes and - * follow this order (the single-quotes are part of the format, - * but as noted below, now the whole registration part can be commented - * out so external Python tools can ignore them): - * - * Name: 'script name for the menu' - * Blender: <short int> (minimal Blender version) - * Group: 'group name' (defines menu) - * Submenu: 'submenu name' related_1word_arg - * Tooltip: 'tooltip for the menu' - * - * notes: - * - there may be more than one submenu line, or none: - * submenus and the tooltip are optional; - * - the Blender version is the same number reported by - * Blender.Get('version') in BPython or G.version in C; - * - NEW in 2.35: Michael Reimpell suggested and even provided - * a patch (read but not used to keep changes to a minimum for - * now, shame on me) to make registration code also accept - * commented out registration lines, so that BPython menu - * registration doesn't mess with Python documentation tools. */ - - /* first the name: */ - res = fscanf( fp, "%[^']'%[^'\r\n]'\n", w, name ); - if( ( res != 2 ) || !strstr(w, "Name") ) { - if( DEBUG ) - printf( "BPyMenus error: wrong 'Name' line in %s.\n", pathstr ); - goto discard; - } - - /* minimal Blender version: */ - res = fscanf( fp, "%s %d\n", w, &version ); - if( ( res != 2 ) || !strstr(w, "Blender") ) { - if( DEBUG ) - printf( "BPyMenus error: wrong 'Blender' line in %s.\n", pathstr ); - goto discard; - } - - line[0] = '\0'; /* used as group for this part */ - - /* the group: */ - res = fscanf( fp, "%[^']'%[^'\r\n]'\n", w, line ); - if( ( res != 2 ) || !strstr(w, "Group" ) ) { - if( DEBUG ) - printf( "BPyMenus error: wrong 'Group' line in %s.\n", pathstr ); - goto discard; - } - - res = bpymenu_group_atoi( line ); - if( res < 0 ) { - if( DEBUG ) - printf( "BPyMenus error: unknown 'Group' %s in %s.\n", line, pathstr ); - goto discard; - } - - pymenu = bpymenu_AddEntry( res, ( short ) version, name, fname, - whichdir, NULL ); - if( !pymenu ) { - if( DEBUG ) - printf( "BPyMenus error: couldn't create entry for %s.\n", pathstr ); - fclose( fp ); - closedir( dir ); - return -2; - } - - /* the (optional) submenu(s): */ - while( fgets( line, 100, fp ) ) { - res = sscanf( line, "%[^']'%[^'\r\n]'%s\n", w, submenu, - subarg ); - if( ( res != 3 ) || !strstr( w, "Submenu" ) ) - break; - bpymenu_AddSubEntry( pymenu, submenu, subarg ); - } - - /* the (optional) tooltip: */ - res = sscanf( line, "%[^']'%[^'\r\n]'\n", w, tooltip ); - if( ( res == 2 ) && (!strstr( w, "Tooltip") || !strstr( w, "Tip" ))) { - bpymenu_set_tooltip( pymenu, tooltip ); - } - - discard: - fclose( fp ); - continue; + /* close directory stream */ + closedir(dir); + } else { + /* open directory stream failed */ + fprintf(stderr, "opendir %s failed: %s\n", dirname, strerror(errno)); + returnValue = -1; } - - closedir( dir ); - return 0; + return returnValue; } static int bpymenu_GetStatMTime( char *name, int is_file, time_t * mtime ) diff --git a/source/blender/python/api2_2x/Blender.c b/source/blender/python/api2_2x/Blender.c index d9b4151388d..761fffabb50 100644 --- a/source/blender/python/api2_2x/Blender.c +++ b/source/blender/python/api2_2x/Blender.c @@ -97,8 +97,10 @@ static char Blender_Get_doc[] = "(request) - Retrieve settings from Blender\n\ 'staframe' - Returns the start frame of the animation\n\ 'endframe' - Returns the end frame of the animation\n\ 'filename' - Returns the name of the last file read or written\n\ + 'homedir' - Returns Blender's home dir\n\ 'datadir' - Returns the dir where scripts can save their data, if available\n\ 'scriptsdir' - Returns the main dir where scripts are kept, if available\n\ + 'uscriptsdir' - Returns the user defined dir for scripts, if available\n\ 'version' - Returns the Blender version number"; static char Blender_Redraw_doc[] = "() - Redraw all 3D windows"; @@ -217,6 +219,13 @@ static PyObject *Blender_Get( PyObject * self, PyObject * args ) if( StringEqual( str, "filename" ) ) { return ( PyString_FromString( G.sce ) ); } + if( StringEqual( str, "homedir" ) ) { + if( BLI_exists( bpy_gethome() )) + return PyString_FromString( bpy_gethome() ); + else + return EXPP_incr_ret( Py_None ); + } + if( StringEqual( str, "datadir" ) ) { char datadir[FILE_MAXDIR]; BLI_make_file_string( "/", datadir, bpy_gethome( ), @@ -235,6 +244,12 @@ static PyObject *Blender_Get( PyObject * self, PyObject * args ) else return EXPP_incr_ret( Py_None ); } + if( StringEqual( str, "uscriptsdir" ) ) { + if( BLI_exists( U.pythondir ) ) + return PyString_FromString( U.pythondir ); + else + return 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. */ if( StringEqual( str, "vrmloptions" ) ) { diff --git a/source/blender/python/api2_2x/NMesh.c b/source/blender/python/api2_2x/NMesh.c index 53bceb23cd8..38d189e007f 100644 --- a/source/blender/python/api2_2x/NMesh.c +++ b/source/blender/python/api2_2x/NMesh.c @@ -1059,10 +1059,10 @@ static PyObject *NMesh_hasFaceUV( PyObject * self, PyObject * args ) switch ( flag ) { case 0: - me->flags |= NMESH_HASFACEUV; + me->flags &= ~NMESH_HASFACEUV; break; case 1: - me->flags &= ~NMESH_HASFACEUV; + me->flags |= NMESH_HASFACEUV; break; default: break; diff --git a/source/blender/python/api2_2x/doc/Blender.py b/source/blender/python/api2_2x/doc/Blender.py index c6bb41a3d6d..a864566c4e5 100644 --- a/source/blender/python/api2_2x/doc/Blender.py +++ b/source/blender/python/api2_2x/doc/Blender.py @@ -10,7 +10,7 @@ """ The main Blender module. -B{New}: 'scriptsdir' parameter in L{Get}. +B{New}: 'homedir', 'scriptsdir' and 'uscriptsdir' parameters in L{Get}. Blender ======= @@ -36,11 +36,15 @@ def Get (request): - 'staframe': the start frame of the animation - 'endframe': the end frame of the animation - 'filename': the name of the last file read or written + - 'homedir': Blender's home dir - 'datadir' : the path to the dir where scripts should store and retrieve their data files, including saved configuration (can be None, if not found). - 'scriptsdir': the path to the main dir where scripts are stored (can be None, if not found). + - 'uscriptsdir': the path to the user defined dir for scripts, see + the paths tab in the User Preferences window in Blender + (can be None, if not found). - 'version' : the Blender version number @return: The requested data. """ diff --git a/source/blender/python/api2_2x/doc/Window.py b/source/blender/python/api2_2x/doc/Window.py index 60f1396dbce..bde72cc815c 100644 --- a/source/blender/python/api2_2x/doc/Window.py +++ b/source/blender/python/api2_2x/doc/Window.py @@ -87,6 +87,13 @@ DrawProgressBar:: - L: left mouse button - M: middle mouse button - R: right mouse button + +@warn: The event system in Blender needs a rewrite, though we don't know when that will happen. Until then, event related functions here (L{QAdd}, L{QRead}, +L{QHandle}, etc.) can be used, but they are actually experimental and can be +substituted for a better method when the rewrite happens. In other words, use +them at your own risk, because though they should work well and allow many +interesting and powerful possibilities, they can be deprecated in some future +version of Blender / Blender Python. """ def Redraw (spacetype = '<Types.VIEW3D>'): @@ -305,18 +312,23 @@ def QRead (): # let's catch all events and move the 3D Cursor when user presses # the left mouse button. from Blender import Draw, Window + + v3d = Window.ScreenInfo(Window.Types.VIEW3D) + id = v3d[0]['id'] # get the (first) VIEW3D's id + done = 0 + while not done: # enter a 'get event' loop evt, val = Window.QRead() # catch next event - if evt in [Draw.ESCKEY, Draw.QKEY]: done = 1 # end loop + if evt in [Draw.MOUSEX, Draw.MOUSEY]: + continue # speeds things up, ignores mouse movement + elif evt in [Draw.ESCKEY, Draw.QKEY]: done = 1 # end loop elif evt == Draw.SPACEKEY: Draw.PupMenu("Hey!|What did you expect?") elif evt == Draw.Redraw: # catch redraw events to handle them Window.RedrawAll() # redraw all areas - elif evt == Draw.LEFTMOUSE and val: # left button pressed - v3d = Window.ScreenInfo(Window.Types.VIEW3D) - id = v3d[0]['id'] # get the (first) VIEW3D's id - Window.QAdd(id, evt, 1) # add the caught mouse event to it + elif evt == Draw.LEFTMOUSE: # left button pressed + Window.QAdd(id, evt, 1) # add the caught mouse event to our v3d # actually we should check if the event happened inside that area, # using Window.GetMouseCoords() and v3d[0]['vertices'] values. Window.QHandle(id) # process the event @@ -328,7 +340,7 @@ def QRead (): @return: [event, val], where: - event: int - the key or mouse event (see L{Draw}); - val: int - 1 for a key press, 0 for a release, new x or y coordinates - for mouse events. + for mouse movement events. """ def QAdd (win, event, val, after = 0): |