1
$\begingroup$

I'm trying to organize a big scene file where some objects has this name (small sample) "US40.004, US40.005, US41.001, US51.006" or even possible for the names to be like this "US-009.009, US-009.010, US-015, US-016" as you can see the names are incremental or in sequence, and then I have some other objects with the name "USA, USDOT"

On my script I ask to select all the objects with "US" in their names but it select also the "USA, USDOT" which I don't want then to be selected.

Is there a way to tell the script to focus on selecting the objects that has US plus the sequence?

I hope this makes sense.

Here is my code so far:

import bpy


params = ["US", "US-"]

for obj in bpy.context.scene.objects:
    if any(x in obj.name for x in params):
        obj.select_set(True)
        bpy.context.view_layer.objects.active = obj
    else: 
        obj.select_set(False)
$\endgroup$

2 Answers 2

3
$\begingroup$

well, unfortunately you did not exactly specify, which ones should be included and which ones not.

it basically uses regular expressions and searches for starting characters "US" and then an optional "-" and a number. Then a match is found. Since you know python i think you are able to adapt that to your code. Have fun!

So i made it like this:

import re
    
txt = {"US40.004", "US40.005", "US41.001", "US51.006", "US-009.009", "US-009.010", "US-015", "US-016", "USA", "us30.0055", "uS30.99"}
    
for eachText in txt:
    x = re.findall("US[-]?\d", eachText.upper())
        
    if (x):
        print("Yes, there is at least one match!", eachText)
    else:
        print("No match", eachText)

result:

Yes, there is at least one match! US-009.010
Yes, there is at least one match! us30.0055
Yes, there is at least one match! US51.006
Yes, there is at least one match! US-016
Yes, there is at least one match! uS30.99
Yes, there is at least one match! US-009.009
Yes, there is at least one match! US41.001
Yes, there is at least one match! US40.004
Yes, there is at least one match! US-015
Yes, there is at least one match! US40.005
No match USA

my change was just this: eachText.upper() -> so i convert the text to uppercase before i check whether it fits or not.

$\endgroup$
7
  • $\begingroup$ That's great thanks, the only thing I can't make my head around is what if I don't want to feed the txt list manually, for example I only add in the txt = {"US", "US-"} and the code search among all the objects the ones that has US(number sequence) and ignore the "USA" object. $\endgroup$ Commented Apr 8, 2021 at 11:19
  • $\begingroup$ i don't understand your question. the txt -> is your bpy.context.scene.objects. You just have to exchange your "if any(x in obj.name for x in params):" to "x = re.findall("US[-]?\d", obj.name) if (x):" $\endgroup$
    – Chris
    Commented Apr 8, 2021 at 11:25
  • $\begingroup$ i updated my answer $\endgroup$
    – Chris
    Commented Apr 8, 2021 at 11:28
  • $\begingroup$ Thank you so much for your time and support. I think in my head the question sounded ok but was difficult to express it with words. $\endgroup$ Commented Apr 8, 2021 at 12:36
  • 1
    $\begingroup$ i updated my answer $\endgroup$
    – Chris
    Commented Apr 8, 2021 at 14:24
4
$\begingroup$

bpy.ops.object.select_pattern

As mentioned in your previous question re the operator script Script is selecting all the objects (unwanted) there is already a pattern matching operator, bpy.ops.object.select_pattern which uses Unix file token matching. See no need to "reinvent the wheel" and write another, which so far has no extra functionality. Here is quick run down on how it works. (The code of the operator is available in bl_operators/object.py.)

>>> bpy.ops.object.select_pattern(
select_pattern()
bpy.ops.object.select_pattern(pattern="*", case_sensitive=False, extend=True)
Select objects matching a naming pattern

options to extend the selection, and a case insensitive match.

>>> import fnmatch
>>> fnmatch.fnmatch(
fnmatch(name, pat)
Test whether FILENAME matches PATTERN.
Patterns are Unix shell style:
*       matches everything
?       matches any single character
[seq]   matches any character in seq
[!seq]  matches any char not in seq
An initial period in FILENAME is not special.
Both FILENAME and PATTERN are first case-normalized
if the operating system requires it.
If you don't want this, use fnmatchcase(FILENAME, PATTERN).

So for example sake will pinch

>>> txt = {"US40.004", "US40.005", "US41.001", "US51.006", "US-009.009", "US-009.010", "US-015", "US-016", "USA", "USADOT"}

make a pattern, Case sensitive, any name starting with "US" but not having an uppercase character as third character. then end with anything

>>> pattern = "US[!A-Z]*"
>>> for t in txt:
...     t, fnmatch.fnmatchcase(t, pattern)
...     
('US-009.009', True)
('US40.005', True)
('US41.001', True)
('US51.006', True)
('US-016', True)
('US-009.010', True)
('USA', False)
('USADOT', False)
('US-015', True)
('US40.004', True)

Looks about right, so a call with instead object names matching the sample list

bpy.ops.object.select_pattern(pattern="US[!A-Z]*", case_sensitive=True)

in object mode will select all objects denoted True above

To clarify, re the commentary below, it is not necessary to use the operator could (thought this was implied TBH) instead

for ob in scene.objects:
    ob.select_set(fnmatch.fnmatchcase(ob.name, pattern)) # match case

Re setting the active object. If the context object is no longer selected, or None and you wish it to be one of the selected

if context.selected_objects and context.object not in context.selected_objects:
    context.view_layer.objects.active = context.selected_objects[0]

ie If the active object is set and selected probably don't want to randomly change it to another of the selected objects. (BTW most selection operators do not set the active object)

$\endgroup$
7
  • $\begingroup$ Thank you for the detailed explanation, although your answer worked properly I like to avoid the use of operators (not sure if it's a urban legend that they are slower), but it is really great having both solutions in one place. $\endgroup$ Commented Apr 8, 2021 at 13:21
  • $\begingroup$ Do you mind measuring the execution time of both scripts? I guess the operator is faster in this case. If so, there is no reason to "avoid the use of operators" IMHO @JuanCarlos $\endgroup$
    – brockmann
    Commented Apr 8, 2021 at 13:26
  • $\begingroup$ I will try to test both later, if I have understood right the whole operator thingy is that the scene gets updated everytime that an operator is called, for small scenes nothing to worry about, but when the scenes are huge and complex scripts then the speed will be noticeable. Here is an interesting discussion about it: link $\endgroup$ Commented Apr 8, 2021 at 14:14
  • $\begingroup$ Depends on the case... and in this case it's just 1 call which does 2 things at once @JuanCarlos so what we can do is measuring the execution time to make sure... $\endgroup$
    – brockmann
    Commented Apr 8, 2021 at 14:40
  • $\begingroup$ yep, isn't slower and doesn't make much sense to reinvent the wheel in this case. I think it's funny that beginners in particular come up with that post... had the same discussion 2 years ago, believers. $\endgroup$
    – brockmann
    Commented Apr 8, 2021 at 22:01

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .