Help needed, Save Current Scene script.

Hi,
Well, you would think I could do this myself, but I cannot.
I can fix bugs in scripts, catalog & document them.
Basically a Librarian. But a fairly good one.:wink:

So here’s what I need.
A script that will save only the current scene & only everything in it.
Example, I have 10 scenes, when I save, Blender Saves all the scenes.
Not just the current or Active scene.

There may be some useful code in the Blender Library Script.
http://wiki.blender.org/index.php/Extensions:Py/Scripts/Manual/System/blend_library
or search this python section of the forum, I think there’s an update somewhere,
it would seem most of the needed functions are in this script.

Any help anyone needs, just ask, I don’t write scripts myself, but I do know some about functions & which ones could be needed & where to find them & examples of how they could be put to best use & workflow proposals to put it all together.

So if anyone is willing to help, it will be greatly appreciated by lot’s of people.
I need the script for Blender 2.48a Windows.
From there I will push for inclusion in 2.49 & integration (eventually) into 2.50.

Any opinions or help would be great.
I do think this is a very much needed feature.

Thanks.
Brendon.

This should work (try it first on some not important drawings, data loss is on your own risk):

#!BPY

""" Registration info for Blender menus:
Name: 'Save Current Scene (.blend)...'
Blender: 243
Group: 'Export'
Tooltip: 'Save only current scene'
"""

# --------------------------------------------------------------------------
# ***** BEGIN GPL LICENSE BLOCK *****
#
# Copyright (C) 2006 - 2007 Mariano Hidalgo AKA uselessdreamer
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program 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 General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software Foundation,
# Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
#
# ***** END GPL LICENCE BLOCK *****
# --------------------------------------------------------------------------

__author__  = "Stani (SPE Python IDE)"
__url__     = ["pythonide.stani.be", "blenderartists.org"]
__version__ = "0.1"

import Blender
import bpy

active_scene    = bpy.data.scenes.active
filename_current = Blender.Get('filename')

def save_scene(filename, filename_current=filename_current, 
        scene=active_scene):
    # save current file first
    filename_current    = Blender.Get('filename')
    if not filename_current:
        filename_current= Blender.sys.makename(ext='_temp.blend')
    Blender.Save(filename_current,1)
    # remove other scenes & save
    scenes              = bpy.data.scenes
    for scene in scenes:
        if scene != active_scene:
            scenes.unlink(scene)
    Blender.Save(filename,1)
    # restore current file
    Blender.Load(filename_current)

if __name__=='__main__':
    filename = Blender.sys.makename(ext='_%s.blend'%(active_scene.name))
    Blender.Window.FileSelector(
        save_scene, 
        'Save current scene as', 
        filename)

Of course place this in your ~/.blender/scripts folder

hi stani,
closest script yet.
& the best so far.
We have tried the modifying the Blender library script,
which gives the exact same file size as your script.
However the Library script gives errors if you try to open or save the file in Blender that does not include the Library script.
Your script fixed this problem.

Next problem.
Both scripts do not clean out the unused materials,
as I have an extensive Material library as a scene,
this is not cleaned out.
Also, but I guess not as important, the text editor is not cleaned either.

It may take a couple of hours, but I’m uploading 3 files, to show examples of how (& why)
the different methods work & the desired result.

Thanks, this is starting to work well.
Brendon.

Unused materials should be gone as well. It does here. Are sure they are not used anymore by any object? Otherwise provide links to a source and expected target file as you mentioned. The following code also removes any text:

#!BPY

""" Registration info for Blender menus:
Name: 'Save Current Scene (.blend)...'
Blender: 243
Group: 'Export'
Tooltip: 'Save only current scene'
"""

# --------------------------------------------------------------------------
# ***** BEGIN GPL LICENSE BLOCK *****
#
# Copyright (C) 2006 - 2007 Mariano Hidalgo AKA uselessdreamer
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program 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 General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software Foundation,
# Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
#
# ***** END GPL LICENCE BLOCK *****
# --------------------------------------------------------------------------

__author__  = "Stani (SPE Python IDE)"
__url__     = ["pythonide.stani.be", "blenderartists.org"]
__version__ = "0.1"

import Blender
import bpy

active_scene    = bpy.data.scenes.active
filename_current = Blender.Get('filename')

def save_scene(filename, filename_current=filename_current, 
        scene=active_scene):
    # save current file first
    filename_current    = Blender.Get('filename')
    if not filename_current:
        filename_current= Blender.sys.makename(ext='_temp.blend')
    Blender.Save(filename_current,1)
    # remove other scenes
    scenes              = bpy.data.scenes
    for scene in scenes:
        if scene != active_scene:
            scenes.unlink(scene)
    # remove texts
    texts               = bpy.data.texts
    for text in texts:
        texts.unlink(text)
    # save only current scene
    Blender.Save(filename,1)
    # restore current file
    Blender.Load(filename_current)

if __name__=='__main__':
    filename = Blender.sys.makename(ext='_%s.blend'%(active_scene.name))
    Blender.Window.FileSelector(
        save_scene, 
        'Save current scene as', 
        filename)

hi stani,
thanks for your help on this.
there is little difference between the results of the two scripts.
this is my fault.
the new script with the text clean returns an error that a script is running.
So the need there I presume would be to turn off scriptlinks, but that’s a guess.
It could be a pynode script causing the error.

However it does remove all scenes except the current scene.
Also even with the error, all text is removed from the .blend.
The Materials database is still present.

I am still uploading the files, probably about an hour away…

Thanks.
Brendon

Please give the full error traceback and links to the files. You can alsoemail them to me if you prefer. Send me a private message in that case.

ok, finally got some files up.

This is the file saved from my pimped blender build. 42 meg.
http://www.cdupload.com/files/50500_nxfxb/Kais_Default.7z

This is the Same file after running your first script. 21meg.
http://www.cdupload.com/files/50501_kdwen/stani_Default_Scene.7z
(note the material database is still there & all the text, you second script will remove the text.)

This is the Clean Scene I use to append to, to achieve the desired result.
http://www.cdupload.com/files/50506_ei69e/clean%20default.7z

This is the desired result.
http://www.cdupload.com/files/50505_jzpl1/clean%20Appended%20default.7z

I am beginning to think that the only way to do this is to append the Scene to the Clean Default.
Unfortunately this must be done manually.

However stani, you are getting very close to the desired result, so it may well be script able to
Save Current Scene.

Thanks.
Brendon.

meta the game engine had the same problem at the time the crystal space game was being attempted. someone, i believe the mod of the game forum, fixed it in the game engine. if you can get them to take a look for you he can probably get it to not carry over unused materials and save that memory for you. he did the fix in c for the engine, but he has found where the cause is and fix for the problem.

Hi Brandon,

Thanks for providing these files. Now I can reproduce your use case. Let’s see what I can do. Maybe some issues can not be ressolved. For example find out which of the materials in the scenes uses which pynodes in order to delete the other texts

I know much more about Python than about Blender, but for you it is maybe the opposite :rolleyes:

The script in the end will not be ideal, but hopefully useful. Do you know which python modules are bundled with Blender and don’t require a system wide install (e.g. os? subprocess?) I can’t find documentation about that easily. Maybe you can help me with that.

This should come pretty close. You need to save this file as save_current_scene.py in your Blender scripts folder otherwise it won’t work. Also read the bpydoc This script could be a base for:

  • exporting any scene, not just the active one
  • splitting a blend drawing into separate scene drawings
  • exporting just some (selected) objects, similar to wblock in AutoCAD. Is there any interest in that as well?
#!BPY

""" Registration info for Blender menus:
Name: 'Save Current Scene (.blend)...'
Blender: 248
Group: 'Export'
Tooltip: 'Save only current scene'
"""

# --------------------------------------------------------------------------
# ***** BEGIN GPL LICENSE BLOCK *****
#
# Copyright (C) 2009 Stani (SPE Python IDE)
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program 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 General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software Foundation,
# Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
#
# ***** END GPL LICENCE BLOCK *****
# --------------------------------------------------------------------------

__author__  = "Stani (SPE Python IDE)"
__url__     = ["pythonide.stani.be", "blenderartists.org"]
__version__ = "0.2"
__bpydoc__ = """\
The filename of this script should be the value of SCRIPT_NAME in the 
folder ~/.blender/scripts 

The script tries to be as prudent as possible.
Probably some cleaning up can be skipped to gain more speed.

Known issues:
- Blender might display a Error Totblock because of action.removeChannel
  (see clear_channels function) This is probably a memory leak, but
  I don't know why.
- Blender will show several "Unable to open" warnings. It is safe to
  ignore these messages.
"""

import os
import sys

import bpy
import Blender
from   Blender import Armature, Curve, Draw, Ipo, Types, Window

SCRIPT_NAME         = 'save_current_scene.py'
ACTIVE_SCENE        = bpy.data.scenes.active
FILENAME_CURRENT    = Blender.Get('filename')
SCRIPT              = os.path.expanduser(os.path.join('~','.blender',
                        'scripts',SCRIPT_NAME))

def write(x):
    """Alternative for the print function. (python 3 compatible)"""
    sys.stdout.write(x)
    sys.stdout.flush()
    
def get_data(ob):
    """Returns data of an object and in case of a mesh, 
    returns Mesh instead of NMesh."""
    data    = ob.data
    if type(data) is Types.NMeshType:
        data    = bpy.data.meshes[ob.data.name]
    return data


def clear_action(x):
    """Clears an object from action, action strips and constraints."""
    for strip in list(x.actionStrips):
        x.actionStrips.remove(strip)
    for constraint in list(x.constraints):
        x.constraints.remove(constraint)
    action = x.action
    if action:
        clear_channels(action)
        x.action = None

        
def clear_channels(action):
    """Clears an action from its channels."""
    for channel, ipo in action.getAllChannelIpos().items():
        if ipo:
            clear_curves(ipo)
    # FIXME: this causes an Error Totblock
    for channel in action.getChannelNames():
       action.removeChannel(channel)

    
def clear_ipo(x):
    """Clears an object from its ipo."""
    ipo                 = x.getIpo()
    if ipo:
        clear_curves(ipo)
        x.ipo           = None

        
def clear_curves(ipo):
    """Clear the curves from an Ipo."""
    for curve in ipo.curveConsts.keys():
        try:
            ipo[getattr(Ipo,curve)]  = None
        except (KeyError,ValueError):
            pass

def remove_texts():
    """Remove all unnecessary texts."""
    texts               = bpy.data.texts
    for text in texts:
        # keep pynodes, which have more than two users
        if text.users < 2:
            texts.unlink(text)


def save_scene(filename, scene=ACTIVE_SCENE):
    """Saves a scene only."""
    write('
Exporting scene "%s" as "%s", please wait...

' %\
        (scene.name,filename))
    # save current file first
    Blender.Save(filename,1)
    Blender.Save(FILENAME_CURRENT,1)
    # start a new Blender session to extract the scene to not
    # interfere with the current one
    command = '%s -b %s -P %s extract %s' %\
        (Blender.sys.progname,filename,SCRIPT,scene.name)
    os.system(command)


def extract_scene(filename, scene=ACTIVE_SCENE, texts=True):
    """Remove all content which does not belong to the scene."""
    # objects
    scene_objects       = frozenset(scene.objects)
    scenes              = bpy.data.scenes
    
    write('Clean up actions...
')
    scene_actions = [ob.action for ob in scene_objects if ob.action]
    for action in Armature.NLA.GetActions().values():
        if not(action in scene_actions):
            clear_channels(action)
            
    write('Retrieve scene materials...
')
    scene_materials  = frozenset()
    for object in scene_objects:
        scene_materials = scene_materials.union(
                            frozenset(object.getMaterials()))
        data            = object.getData()
        if hasattr(data,'materials'):
            scene_materials  = scene_materials.union(
                                frozenset(data.materials))
                                
    write('Clean up non scene materials...
')
    for material in bpy.data.materials:
        if not (material in scene_materials):
            material.freeNodes()
            clear_ipo(material)
            material.clearScriptLinks()
            for index, texture in enumerate(material.getTextures()):
                if texture:
                    material.clearTexture(index)
                    
    write('Clean up datablocks...
')
    scene_data   = [get_data(ob) for ob in scene.objects] +\
                    [scene.world]
    scene_text3d = [t for t in scene_data 
                    if type(t) is Types.Text3dType]
    for data_type in ['meshes','metaballs','lamps','worlds']:
        for data in getattr(bpy.data,data_type):
            if not(data in scene_data):
                if hasattr(data, 'materials'):
                    data.materials = []
                if hasattr(data, 'clearIpo'):
                    clear_ipo(data)
                if hasattr(data,'clearScriptLinks'):
                    data.clearScriptLinks()
                    
    write('Clean up curves (incl. text3d)...
')
    # Text3d needs to be treated as curve, otherwise its materials
    # can't be cleared.
    scene_curves = [c for c in scene_data 
                    if type(c) is Types.CurveType] + \
                  [Curve.Get(t.name) for t in scene_text3d]
    for curve in Curve.Get():
        if not(curve in scene_curves):
            curve.materials = []
            
    write('Remove objects...
')
    for s in scenes:
        for o in s.objects:
            if not(o in scene_objects):
                clear_ipo(o)
                o.clearScriptLinks()
                o.clearTrack()
                o.removeAllProperties()
                o.clrParent()
                clear_action(o)
                s.objects.unlink(o)
                
    write('Remove other scenes...
')
    for s in scenes:
        if s != scene:
            s.update(1)
            scenes.unlink(s)
            
    write('Save only current scene...
')
    save(filename,n=4)


def save(filename, n):
    """Iterate save/load sessions to get rid of unreferenced data."""
    write('Remove texts...
')
    if n == 1:
        remove_texts()
    Blender.Save(filename,1)
    n   -= 1
    if n <= 0:
        return
    command = '%s -b %s -P %s save %d' %\
        (Blender.sys.progname, filename, SCRIPT, n)
    os.system(command)

    
if __name__=='__main__':
    if not FILENAME_CURRENT:
        menu = Draw.PupMenu('Please save the file first%t|OK')
    elif 'extract' in sys.argv:
        filename    = sys.argv[2]
        scene       = bpy.data.scenes[sys.argv[6]]
        extract_scene(filename, scene)
    elif 'save' in sys.argv:
        filename    = sys.argv[2]
        n           = int(sys.argv[6])
        save(filename, n)
    else:
        filename = Blender.sys.makename(ext='_%s.blend' %\
                    (ACTIVE_SCENE.name))
        Window.FileSelector(
            save_scene, 
            'Save current scene as', 
            filename)


yes, i am interested.
I think the blender_library script offers similar functionality. Updated version works for 2.48.

Hi Migius,

Can you clarify ‘similar’ (what is the same, what is different)? I wouldn’t want to work on duplicate functionality. Brendon gave me the impression that the blender library script could not export scenes which can be used independently of the blender library script. So what interests you specifically?

I’ve spent a lot of effort to make my script as efficient as possible and I focus purely on exporting, not on library functions. For example with the latest version of my script the exported scene is only 3mb instead of 21meg like in the first version of my script. I wrote some special code for that, which the blender library doesn’t.

As a test case I used the file of Brendon and exported the default scene with the Blender library script. This gives an error (like the first version of my script):

RuntimeError: there are other scripts running at the Scripts win, close them first!

You end up with a broken drawing and loosing the history of your current drawing. When I went to the library root path I could find the scene, but the result was not so good:

  • it was 41mb. (Compare that to the 3mb of my script.)
  • When I opened it, it was full of unused materials, ipos, … which you can see when you choose in the Outliner window ‘View oops schematic’. (Enjoy the giant spaghetti.)
  • The used material nodes are broken. (See the ‘Node Editor’ window.)

Maybe it is better if I split out an export library for my script, which than can be used by the blender library script as well. However I wonder if it is worth the effort with Blender 2.5 on its way.

In AutoCad I use wblock a lot, so it would be nice to have that feature in Blender as well.

Best regards,
Stani

Hi,
I am cautious of the library script as I never get it working.
Parts work, parts do not work.
Whilst the Library script is very good & I appreciate migius effort to keep this working,
It is not what I am looking for.

I need a fully portable script that can easily be distributed.
So if I copy & past an entire Blender version between computers or drives,
the script will just work.
no reinstalling, setting up or other hazards.
With the library script this simply will not work.

I don’t see this as a duplicated effort.
I do see this as a valuable feature.

stani, thanks for your efforts.
for me the second script works best, giving the 50% size reduction on my test files.
the latest script saves a copy of the .b.blend, which is over 100% of the original file save.
that is, in my test case.

For the pynode problem,
is there a way to write the used/active node back to the text editor on save?
or could the pynode be kept if active?
just to brainstorm this,
if the pynodes in the text editor have a prefix, say PyNode: name of node,
could this work to parse the active node via materials, get.active, if PyNode: = active = write?

Thanks for all your help.

The Quest for Save Current Scene continues…

Hi Meta,
Ha you’re still alive. I’ve been continuing working on the export script as part of my ar library:
http://ar.stani.be/
http://sd-2469.dedibox.fr/ar/tutorials/scene_selection.html

It now supports both exporting the current scene and the current selection (like wblock in autocad). It reduces your drawing to 41mb to 3mb. It has been tested and developed on Ubuntu Linux. I know other people where it worked 100%. It even deals perfectly with pynodes, no need to brainstorm.

What platform are you on? Windows. Probably I can fix it for that too. The .b.blend file is created because of differences between my platform and yours, but it should be easy to make that cross-platform. The .b.blend should normally not be created.

It is a pitty you reacted so late, as now I’m going on holiday. So probably I have no time to fix it in the coming weeks.

hi stani, thanks for the reply.
Enjoy your holiday!
Let me know when you have time to work on this, I should have got back to you sooner.
Thanks, your new description of the script is exactly what I need.
I must say, interesting download method for the ar library. =)
I’ll test the scripts, Windows XP & Vista & test your other tools.
Thanks Again.