13
$\begingroup$

I am only just starting to learn/use python in blender. I have the following script

import bpy

for obj in bpy.data.objects:

    if obj.name[0:4] == "Text" and obj.type == "MESH":

        bpy.context.scene.objects.active = obj
        bpy.ops.object.mode_set(mode = 'EDIT')
        print(bpy.context.mode)


        #bpy.ops.mesh.select_all(action = 'SELECT')
        #print(bpy.ops.mesh.remove_doubles(0.0001))
        bpy.ops.object.mode_set(mode = 'OBJECT')

I am trying to work out why I keep getting a context error for bpy.ops.mesh.select_all so I added the print(bpy.context.mode) line and commented out the line that was failing (plus the next line which takes action)

what I get in the console (this script is being run from the console rather than run in place) is a mixture of 'OBJECT' and 'EDIT_MESH' outputs.. suggesting that mode_set is failing a lot of the time.

What am I doing wrong?

I am using blender 2.68a on Windows 7.

Edit I discovered the actual cause of my issue (hinted at by Adhi's edit) and wanted to add it to my post so that others can take something away from this - the objects that were not being switched into edit mode were on another layer. Once I made all layers active my script was able to switch all objects into edit mode. So basically an object in an unselected layer cannot be set into edit mode.

I am also grateful for the other help and suggestions, they have been useful to help me learn python scripting within blender which is something I would love to use more extensively.

$\endgroup$
  • $\begingroup$ can you give more info about your environmnt (os, Blender v. #)? How many objects you have? $\endgroup$ – dimus Oct 21 '13 at 19:59
  • $\begingroup$ Just with a quick glance at your code, it appears like you're trying to set multiple objects in Edit mode at the same time. This is not currently possible. $\endgroup$ – Fweeb Oct 21 '13 at 20:02
  • $\begingroup$ OS- Windows 7. Blender 2.68a. I'm not sure exactly how many objects. There are approximately 400 $\endgroup$ – MrVimes Oct 21 '13 at 20:21
  • 1
    $\begingroup$ for what its worth -- I ran your script on 1536 cubes with duplicated vertices (24576 verts total) and it went through fine (reducing verts to 12288). The only change was print(bpy.ops.mesh.remove_doubles(threshold=0.00001)). Blender 1.68a, mac os x mountain lion $\endgroup$ – dimus Oct 21 '13 at 22:05
  • 1
    $\begingroup$ As a side note: You can use obj.name.starswith("Text"), and note that bpy.context.mode and bpy.context.object.mode differ, see bug tracker. $\endgroup$ – CodeManX Oct 22 '13 at 10:03
15
$\begingroup$

As mentioned by dimus, your problem is solved by adding keyword to the mesh.remove_doubles operator's first argument, meant as threshold property value. With that little bug fixed, your script works properly.

What causes the error is that, keyword arguments are used to set operator properties, and positional arguments to set how the operator is called (further explained in the API docs). By not using a keyword for the first argument, you're turning it into a positional argument, thus interpreted as operator execution context which is an error.

EDIT: Another possible error is iterating over bpy.data.objects, which comprises of all objects in every scene in a file. The script is bound to fail if there's more than one scene in a file containing a MESH object, because it'll try to make object from a different scene the active object of current scene which is contextually incorrect. You should iterate over bpy.context.scene.objects instead.

Here's the amended code, can't see any other bug:

import bpy

scene = bpy.context.scene
scene.layers = [True] * 20 # Show all layers

for obj in scene.objects:
    if obj.type == 'MESH':
        scene.objects.active = obj

        bpy.ops.object.mode_set(mode='EDIT')
        bpy.ops.mesh.remove_doubles(threshold=0.0001)
        bpy.ops.object.mode_set(mode='OBJECT')
$\endgroup$
  • $\begingroup$ Actually I hadn't got as far as correcting the remove_doubles function because the function that was failing was select_all. it was failing whenever the object had not managed to enter edit mode. $\endgroup$ – MrVimes Oct 22 '13 at 18:24
  • $\begingroup$ @MrVimes: I missed another bug :) It's still not related to mode_set, though. Can't see cases where that one could fail. Showing the Outliner space of your test file would help. $\endgroup$ – Adhi Oct 22 '13 at 23:52
  • $\begingroup$ I have marked yours as accepted because your edit pointed me in the right direction for finding out what was actually going on - the objects that were not going into edit mode were on another layer. I shift-clicked all the layers with objects in them and ran the script again - it was then able to switch all objects into edit mode. $\endgroup$ – MrVimes Oct 23 '13 at 18:11
  • $\begingroup$ So there's another one. Blind troubleshooting sure is fun :D Edited again for completeness. $\endgroup$ – Adhi Oct 23 '13 at 22:42
4
$\begingroup$

I, too, have had issues in the past using the mesh select_all operator from within Edit mode. As an alternative, try looping through all of the vertices and setting their select property to True. Then, switch into Edit mode before doing the Remove Doubles operator. Your code should look something like this:

for obj in bpy.data.objects:

    if obj.name[0:4] == "Text" and obj.type == 'MESH':

        bpy.context.scene.objects.active = obj
        for vert in obj.vertices:
            vert.select = True
        bpy.ops.object.mode_set(mode='EDIT')
        bpy.ops.mesh.remove_doubles(threshold=0.0001)
        bpy.ops.object.mode_set(mode='OBJECT')
$\endgroup$
2
$\begingroup$

I came across this subject and want to post my solution, removing doubles of object with given objectname :

def myremovedoubles(objectname, mergedist):
    obj = None
    try:
        obj = bpy.data.objects[objectname]
    except ( RuntimeError ):
        pass  
    if (obj != None): 
        try:
            bpy.ops.object.mode_set(mode='OBJECT')
            bpy.ops.object.select_all(action='DESELECT')
            bpy.data.objects[objectname].select = True            
            scene.objects.active = obj
            bpy.ops.object.mode_set(mode='EDIT')
            bpy.ops.mesh.remove_doubles(mergedist=mergedist)
            bpy.ops.object.mode_set(mode='OBJECT')
            return True
        except ( RuntimeError ):
            pass  
    return False   

this function will return True if executed without RuntimeError, meaning the remove_doubles function was executed

it doesn't crash when the object doesn't exist it sets the object as the only active (and selected) it switches between modes, assuming mode='OBJECT' is the default

$\endgroup$
  • $\begingroup$ Not a fan of the try catch, especially when IMO not required. For instance obj = bpy.data.objects.get(objectname) removes the need for your first try. $\endgroup$ – batFINGER Aug 8 '17 at 10:07
  • $\begingroup$ and scene is not available when this function is called from another filen and mergedist doesn't always work : more often the name of that argument is threshold. and when using something like def do(func, *args) I get an unexpected list of unavailable context properties : bpy.context. seems unavailable. If I pass bpy.context.scene as argument other errors come up. The try/catch thing is just a point of view, I assume the .get function loops the available, so probably takes more action $\endgroup$ – user42646 Aug 9 '17 at 3:16
  • $\begingroup$ Rubbish. Because code fails silently, it hasn't worked. All the cases you mention will just cause your code to fail silently. Also, trying to set an object from data (bpy.data.objects) as context object, if it is not linked to the context scene will cause an error, which is an easily avoided basic logic error IMO. Get the object from context.scene.objects Finally, here is the Blender API Best Practice "point of view" on try / except $\endgroup$ – batFINGER Aug 10 '17 at 6:42
  • $\begingroup$ On a lighter note, using bmesh, removes the need for edit mode and bpy.ops... altogether. Example here $\endgroup$ – batFINGER Aug 10 '17 at 6:54
  • $\begingroup$ @user42646 it's great that you've tried to post an answer. This is not a good way to solve the problem, and as batFINGER points out bmesh is the far more modern way to solve this. That's a danger and posting new answers to old questions and that's why new answers to old question pop up fresh in the review queue. Anyway I gave you an up vote just for "effort". Hint: In the beginning it's easier to get reputation (points) asking questions unless you have a pretty good answer. Welcome to stackexchange! If you haven't already, why don't you take the tour $\endgroup$ – uhoh Aug 29 '17 at 9:53

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.