Skip to content
theodox edited this page Apr 21, 2016 · 17 revisions

A minq Stream represents an arbitrary stream of values. Usually -- but not always -- this is a stream of Maya node names.

In reality a Stream is a 'stored query', like a SQL statement: it's not a list of saved values, but rather a description of a chain of operations. Simply declaring the stream does not cause Maya to do anything: the stream only issues commands and returns results when executed. This happens when you iterate over a stream or call its execute() method:

q = Scene(['top', 'front'])
print q.execute()    # forces query 'q' to execute
#[u'|top' , u'|front']

for item in q:   # iterating also forces execution
    print item
# u'|top'
# u'|front'

calling tuple(), list() or dict() on a Stream will also make it execute:

tuple(Cameras())  
# Result: (u'frontShape', u'perspShape', u'sideShape', u'topShape') # 

list(Scene().only('polyCreator'))
# Result: [u'polyCube1', u'polyCube2', u'polyCube3', u'polyUnite1'] # 

You can also check for contents using built in python checks like in, any, or all. These will also make the query execute :

u'|top' in Cameras()
# True

u'|pCube1' in Cameras()
# False

any(Meshes().like('cube'))
# True

Creating a stream.

Technically you can create a stream with any iterable value: so

Stream(['top', 'persp'])

will create a valid stream that could be used for any minq operations. For that matter

Stream([1,2,3])

will work as well - although of most minq operations will filter out those non-maya-object values.

'using' for starting from a known object

minq offers as convenience function for creating a stream without the extra punctuation: in preference to Stream() you'll usually start a stream like this:

using("top")....

using() takes Python variable arguments, so you can pass more than one object:

using("top", "persp")

is equivalent to `Stream(['top', 'persp']).

creating streams from a type

Another common way to create a stream is with a NodeType class: for example, Meshes() will yield all of the poly meshes in the scene as a Stream, while Transforms() will yield all the transforms. You can also 'initialize' a stream with a list or tuple if you already know the names of nodes or objects you're looking for: for example, you could start with only some cameras like this:

Cameras(['topShape', 'sideShape])

or

Cameras(my_list_of_cameras)

In a case like this anything in the initial argument which does not match the node type will be filtered out.

To find objects in the scene without knowing types, you'll typically start with either Scene() or Everything(), which return all of the dagNodes or Maya entities in the scene respectively.

Chaining

Since stream methods usually return streams, it's usually convenient to chain a block of operations into one statement. For example

Scene().like('character').get(AllChildren).only(IKHandles).get(Parents)

will find the parents of ikHandles in the scene belonging to the root node 'character'. Along the way it create 5 different streams -- but written this way it doesn't require temporary variables to store intermediate results.

Streams as sets

Streams can be combined using set-like operators:

stream1 + stream2

returns a new stream containing the combined contents of both streams (duplicate values are removed)

stream1 - stream2

returns items that are only present in stream1 and not stream2

stream1 & stream2

returns only items that are common to both streams. In all cases the result of the operation is new stream.


Methods

Every stream offers the same set of methods, which can be used to filter, transform or format its contents.

###Filtering Methods

These methods are used to narrow the scope of a stream by removing objects based on name, attribute values or other criteria.

####only(*object_types, namespace=None) Returns a new stream containing only items of the supplied types. The types can be string typenames (such as 'transforms' or 'shapes') or they can be minq.core.NodeType classes. So

this_stream.only('transform')

and

this_stream.only(Transform)

are equivalent.

A handful of special NodeTypes (such as ObjectsOnly or Assemblies) can't be combined with others in a single call. If you try to use one of those as part of a single only() a minq will raise a QueryException.

if the optional namespace keyword is passed, this limits the stream to a given namespace. You can use with with or without a node type filter:

my_stream.only(Meshes, namespace='character1')
character2_root = Assemblies().only(namespace = 'character2').first()

####where(filter_function) Given a callable filter function, returns a new stream containing only items for which the filter function return a truth-tested value.

    def too_large_to_export (obj):
        return cmds.polyEvaluate(obj, v=True) > 16535

    non_exportable = Meshes().where(too_large_to_export)

where() supports a special factory class to generate some kinds of test functions for you in a more succinct way:

     above_origin = Transforms.where(item.tx >= 0)

For details see Attribute Queries

####where_not( filter_function) a convenience wrapper for 'where' that inverts the result -- useful for not having to make a lambda just to invert a common function

####having( attrib) Returns a stream of objects with the supplied attribute. For example

	Transforms().having('attrib_name')

returns only transform nodes with the a custom attribute named attrib_name. This is equivalent to cmds.ls("*.attrib_name", o=True)

####like(regex, exact=False) Given a string regular expression, returns a new stream containing only items where the regex finds a match. By default this allows subset metches using re.search, but if the optional 'exact' flag is set to True, the matches must be complete using re.match

If the incoming stream contains items that can't be converted to strings, will raise an exception


###Expansion and Transformation Methods

These functions change the content of a stream: for example, given a stream of shapes they might be used to return a stream of transforms which are the parents of those shapes. Streams can also return values which aren't Maya objects; you can use a function like get(Values) to get the values of a given attribute on every object in a stream with one call. ####get(*args, **kwargs) Returns a new stream which replaces the incoming stream with a new stream (rather than subsetting the original stream). For example:

   transforms.get(Children)

returns a streams containing the children of the stream .

Valid operations for get() are defined in the [minq.Projections] submodule. The most common ones are:

Name Returns
Children The immediate children of the current stream
Parents The immediate parents of the current stream
AllChildren Everything in the Maya DAG below the contents of the current stream
AllParents Everything in the Maya DAG above the contents of the current stream
History The upstream history for everything in the current stream
Future The downstream history for everything in the current Stream
Connections All of the DAG connections for the everything in the current stream

####append(*args, **kwargs) Takes the same arguments as get(), but rather than replacing the current stream it appends the results to the existing stream. For example to get a list of meshes and their parent transforms:

 Meshes().append(Parents)

####foreach(func) Returns a stream containing the results of func applied to everything in the stream. Func may return any kind of value, so there's not guarantee that the resulting stream will contain only maya objects; depending on the usage that may be what you want.

For PyMel users, stream.foreach(PyNode) will return the results of a query as PyNodes, rather than Maya strings.


###Stream Management Methods

These functions are used to manage streams.

####cache() returns a Stream wrapped around a the cached result of this query. This is used primarily to control when a query gets executed - if you're planning a complex branching operation using one query specialized into several sub-queries, you can use cache() to make sure you don't issue the query too many times.

####distinct() Returns a new stream which contains no duplicate elements. If the incoming stream contains items that can't be hashed, will raise an exception

####execute() Evaluate all of the queries in this Stream and return the result as a list. It's more idiomatic to use list(your_stream) but it is sometimes helpful to be certain exactly when a query will be fired off.

####first() Returns the first item in this stream as a single value. Note that the result is not a stream.

####flatten() Turns a stream containing multiple iterables (for example, the result of a get()) into a single stream.

####split(number_of_streams) Returns multiple 'copies' of this stream which can be iterated independently without actually re-executing the query. This is especially handy in conjunction with join().

####join(**streams) given a collection of named streams, return a table-like stream in which each 'row' has named values corresponding to the original stream. This is used for coordinating multiple streams: for example:

    bones = Joints()
    bone_parents = bones.get(Parents)
    with_parents = bones.join(parent = bone_parents)

will produce a list like

   # stream: [dataRow(object=u'joint3', parent=u'joint2'), dataRow(object=u'joint2', parent=u'joint1')....]

the join process doesn't make any effort to match up the contents, however -- if the different streams have different lengths enmpy 'columns' in the row will be filled with None and if the streams don't match up the results are not predictable.

The primary use for join() is for doing bulk queries using Attributes:

   bones = Joints().like('l_')
   tx = bones.get(Attribute, 'tx').get(Values)
   ty = bones.get(Attribute, 'ty').get(Values)
   bone_translations = bones.join(x = tx, y = ty)
   print bone_translations

  # stream: [dataRow(object=u'L_Ankle', x=-5.0, y=-1.39), dataRow(object=u'L_Heel', x=-0.028, y =6.18)....]

Note in this specific case it would be faster to use .get(Attribute, 't') rather than querying .tx and .ty separately -- but this pattern works for any arbitrary combination of attributes as long as all the objects in the stream have the attribute. In this form the query is issued to maya only once per stream, which is a big advantage over individually calling getAttr many times over.

####group_by(selector) Uses to group the incoming stream into key-value pairs where the key is the result of the selector and the values are the items in the stream which match that selector.

selector can be any arbitrary function which takes a stream value and produces a key value. For convenience it can be an integer or string index into a data row as well

Thus, for simple table lookups:

    cams = Cameras()
    ortho = cams.get(Attribute, 'orthographic').get(Values)
    table = cams.join(ortho = ortho)
    for key, value in  table.group_by('ortho'):
        print key, ":", value

    # False : [dataRow(index=u'|persp|perspShape', ortho=False)]
    # True : [dataRow(index=u'|front|frontShape', ortho=True), dataRow () ....]

or for more complex analysis:

    def poly_class (obj):
        count = cmds.polyEvaluate(obj, v=True)
        if count > 512: return 'high'
        if count > 256: return 'medium'
        return 'low'

    for k, v in Meshes().group_by(poly_class):
        print k, ":" , v

    # medium : [u'|pSphere1|pSphereShape1']
    # low : [u'|pCube1|pCubeShape1']

Since group_by results are always key-value pairs they can be turned directly into dictionaries:

    meshes_by_class = dict(Meshes().group_by(poly_class))

###Formatting Methods

These functions format the output of the stream. By default minq always returns long names, but depending on the operations you try

####long(self) Returns a new stream containing the long names of items in this stream. Any items which are not maya nodes in the stream will be filtered out. Minq

####short(self) Returns a new stream containing the short names of items in this stream. Any items which are not maya nodes in the stream will be filtered out.

####uuid(self) Returns a new stream containing the uuids of items in this stream. Any items which are not maya nodes in the stream will be filtered out.

####sort(key) returns a Stream which sorts the incoming stream using the default Python sort. If the optional key function is provided, the result will be sorted with that key.

This operation has to exhaust the incoming stream, so it is more memory and performance intensive than other minq operations.