Can I use code in my add on to parse the console to improve its functionality

there’s another add on: ADD-ON #1 that the code from MY ADD-ON improves

every time you add a material with ADD-ON #1 it outputs this text to the console “Material Creation Time 0.031016826629638672”

Can I make a function in MY ADD-ON that will look at the console and every time that “Material Creation Time” occurs, output a 1 to a variable

if somebody could give me an example code snippet or an example function I would appreciate It thinks in advance… would this be too heavy as far as processing power I’d like to keep my add on really light as far as the amount of resources it uses

I think this is some complicated thinking… why not implement a counter in the other addon ?.. and something to ask for this value…
And… you are a bit vague with your description…

I was a bit vague but all I really need is that value as a 1 or 0… Then I know if a material was added or not…this way… Is there any way I could explain it better or if you don’t know how to do this do you know anybody that might?..thanks for replying to my question🙂

You said “and something to ask for this value” how would I ask for this value without Accessing the console

Because os this:

i guessed that this one is adding some functionallity to your addon… expanding it… …??

…but because of the vagueness… i could not tell…

Because both addons are python code it would be easier to establish any kind of intercommunication than checking some output on the console… (which as AFAIK can’t be done)…

I was thinking of actual programing and not any patchwork pseudo solution…

Again: precise description of the actual situation (addon names for example) and the use case (counting produces material… for what ??) could bring up by far easier ways of solutions…

I’ve kinda teamed up a bit with the creator of Extreme PBR https://blendermarket.com/products/extreme-pbr-addon-for-blender-279-2 to an extent and that my add on Super-Solid-Mode https://blendermarket.com/products/super-solid-mode improves the functionality of his add on and i’m even updating it currently to do that even more to a pretty high level…

as one of the ways I improve his add on every time you add a material with his add on my add on notices that and it activates an auto mode in my add on which automatically activates the diffuse image texture on all materials in the scene, making solid mode look nice…

I noticed in the console that every time you add a material with extreme PBR it outputs Material Creation Time 0.12754535675048828(number varies dependent on the material added)

so I figured because I already have pretty complex code to Figure out when one of his materials is added that if I could just parse the console output or if there is some way to retrieve that that information from blender, like precisely when that Material Creation Time event is set off… then my add on would be really light in this regard as far as resource usage because that would just be like a toggle

he prints this in one of the lines of his files This is where it happens:

master_material_creator(None, context, self.options, self.node, None)
print(“Material Creation Time”, time.time()-start)
return {‘FINISHED’}

here is the master_material_creator function(anymore advice on this is appreciated):

def master_material_creator(load_path, context, self_options, self_node, name_by_images, is_for_shader_overlay=False):
    # ##################################################################################################### #
    # ##                                            BEGIN                                               ### #
    # ##################################################################################################### #

    addon_prefs = get_addon_preferences()
    scn = context.scene
    scnProp = get_scnprop(scn)
    # wima().xpbr_main_prev = wima().xpbr_main_prev
    preview_mat_name = get_winman_main_preview()

    if self_options != "IMPORT_EMPTY_MODULE":
        if preview_mat_name in {"Empty...", ""}:
            text = "Empty Material, please select a valid material from the preview."
            draw_info(text, "Info", 'INFO')
            return

    if is_for_shader_overlay:
        # ob = bpy.data.objects.get("ExtremePbrTempObj")
        # if ob:
        #     for mat in ob.data.materials:
        #         if mat is not None:
        #             bpy.data.materials.remove(mat)
        #
        # else:
        #     mesh = bpy.data.meshes.new("ExtremePbrTempObj")
        #     ob = bpy.data.objects.new(name="ExtremePbrTempObj", object_data=mesh)
        mesh = bpy.data.meshes.new("ExtremePbrTempObj")
        ob = bpy.data.objects.new(name="ExtremePbrTempObj", object_data=mesh)
        if ob.name[-4] == "." and ob.name[-3:].isnumeric():
            ob.name = ob.name[:-4]

    else:
        ob = context.object

    eevee_default_settings(context)

    if not ob_type_multiple_type(ob):
        return

    if ob.mode in ['TEXTURE_PAINT', 'VERTEX_PAINT']:
        bpy.ops.extremepbr.painter(options='STOP_PAINT')

    active_uv = adduvmap(context, ob)
    # Eccezione per gli oggetti non di tipo mesh:
    if active_uv:
        if 'X_PBR_Paint_Uv_Layer' in ob.data.uv_layers.active.name:
            ob.data.uv_layers.active = active_uv

    # -----------------------------------------------------------------------------------------------------
    #                   Dichiarazione di percorsi, dipende dalle situazioni
    # -----------------------------------------------------------------------------------------------------

    switch = scnProp.libraries_switch

    # Qui abbiamo inserito un'automazione che riconosce i materiali .blend da quelli formato immagine dalla libreria default

    # if not load_type:
    load_path, load_type, storage_method, json_data_mat_info = return_load_data_paths(scnProp, load_path)

    if self_options != 'IMPORT_EMPTY_MODULE':
        if storage_method == 'TO_DOWNLOAD':
            text = "Attention, this material version has yet to be downloaded from extreme-addons, to download this material version, use the button below the size selector. or install everything via the material installation buttons in the addon options"
            draw_info(text, "Info", 'INFO')
            return

        if not load_path and not load_type and not storage_method and not json_data_mat_info:
            text = "Warning, this material does not contain any useful files, or, the library has been manipulated. Make sure you haven't manually tapped anything inside the library folders"
            draw_info(text, "Info", 'INFO')
            return

        if storage_method in ['SAVE_FX']:
            text = "This material has been saved as FX, so it cannot be used as a Material or Module Material"
            draw_info(text, "Info", 'INFO')
            return

        # Condizione per cui un materiale salvato completamente non può essere un modulo
        # Poichè ci potrebbero essere fino a 4 moduli in questo tipo di salvataggio
        if load_type == 'BLEND' and storage_method in ['SAVE_MATERIAL', None, ''] and self_options in ['ADD_MODULE',
                                                                                                       'REPLACE_MODULE']:
            text = "This material ( " + preview_mat_name + " ) has been saved as complete Node Tree Material, so it cannot be used as Module Material"
            draw_info(text, "Info", 'INFO')
            return

    # -----------------------------------------------------------------------------------------------------
    # -----------------------------------------------------------------------------------------------------

    # Add Module e replace module non necessitano del processo del "Replace" "IMPORT_EMPTY_MODULE" quindi return
    # Questo è solo per creare un module EMPTY per costruire un materiale
    if self_options == 'IMPORT_EMPTY_MODULE':
        mat = create_empty_mat_module_or_fx(context)
        force_driver_nodes(mat)
        return

    ####################################################################################

    remove_material_geonode([ob])

    if self_options == 'ADD_MODULE':
        # Controllo se si può fare add module, poichè i materiali diversi da Module, non possono essere miscelati
        mixer = [n for n in ob.active_material.node_tree.nodes if n.type == 'GROUP' if n.node_tree if
                 'X_PBR_Mixer' in get_ngprop(n.node_tree).group_id_name]
        if not mixer:
            text = "Attention, you cannot sum to this material: ( " + ob.active_material.name + " ) as it is not a material created with Extreme PBR (4,0,10) or later"
            draw_info(text, 'Info', 'INFO')
            return

        get_modules = [n for n in ob.active_material.node_tree.nodes if n.type == 'GROUP' if n.node_tree if
                       get_ngprop(n.node_tree).group_id_name == 'MODULE']
        moduleIndex = len(get_modules)

        if moduleIndex > 3:
            # questo è il limite di 4 dei moduli, massimo 4 simultaneamente
            return

        # Rimuovo il mixer per il solito bug dei module di tipo Shader:
        # Bug pesante di Blender che cambia i module delle connessioni
        remove_mixer_nodes(ob.active_material)
        if load_type == 'SHADER_MAKER':
            mat, matProp, module_data = shadermaker_pbr(scn, scnProp, ob, moduleIndex + 1, preview_mat_name,
                                                        self_options)
            if not mat and not matProp and not module_data:
                return

        elif load_type == 'SHADER_MAKER_AUTO':
            mat, matProp, module_data = create_default_material(scn, load_path, scnProp, ob, moduleIndex + 1, None,
                                                                name_by_images, self_options)
            if not mat and not matProp and not module_data:
                return
        elif load_type == 'DEFAULT':
            mat, matProp, module_data = create_default_material(scn, load_path, scnProp, ob, moduleIndex + 1,
                                                                json_data_mat_info, preview_mat_name, self_options)
        elif load_type == 'BLEND':
            mat, matProp, module_data = append_mat(scn, load_type, load_path, scnProp, ob, storage_method,
                                                   moduleIndex + 1, json_data_mat_info, self_options, preview_mat_name)
            if not mat or not matProp:
                return

        mixer_node, mask_node = connect_mask_to_mixer(scnProp, mat, matProp)
        align_module(mat)
        result = paint_node_manager(ob, mat, matProp, mask_node.node_tree)
        if not result:
            return
        force_driver_nodes(mat)
        len_images_and_simplify(mat)
        return

    if self_options == 'REPLACE_MODULE':
        # Ci siamo accorti che il replace module non spegne i diplacement, quindi ti mettiamo qui la soluzione:
        mat = ob.active_material
        matProp = get_matprop(mat)

        # qui si acquisisce quale module, splittando l'unput che è diviso da virgola
        moduleName = self_node
        moduleIndex = int(moduleName[-1])
        # DIsattiva il displace se il modulo 1 viene sostituito:
        if moduleIndex == 1:
            if matProp.displace_on_off:
                matProp.displace_on_off = False

        if load_type == 'SHADER_MAKER':
            mat, matProp, module_data = shadermaker_pbr(scn, scnProp, ob, moduleIndex, preview_mat_name, self_options)

        elif load_type == 'SHADER_MAKER_AUTO':
            mat, matProp, module_data = create_default_material(scn, load_path, scnProp, ob, moduleIndex, None,
                                                                name_by_images, self_options)
            if not mat and not matProp and not module_data:
                return
        elif load_type == 'DEFAULT':
            mat, matProp, module_data = create_default_material(scn, load_path, scnProp, ob, moduleIndex,
                                                                json_data_mat_info, preview_mat_name, self_options)
            ndProp = get_ndprop(mat.node_tree.nodes[moduleName])
            ndProp.restore_to_default_value = True


        elif load_type == 'BLEND':
            mat, matProp, module_data = append_mat(scn, load_type, load_path, scnProp, ob, storage_method, moduleIndex,
                                                   json_data_mat_info, self_options, preview_mat_name)
            align_module(mat)
            return

        align_module(mat)
        force_driver_nodes(mat)
        len_images_and_simplify(mat)

        return

    ###Replace Start
    if self_options == 'REPLACE_MATERIAL':

        mat = ob.active_material
        # Crash_01
        if mat:
            matProp = get_matprop(mat)
            if matProp.displace_on_off:
                matProp.displace_on_off = False

        if not ob.active_material:
            return
        replace_pre(ob, scnProp)

    if load_type == 'SHADER_MAKER':
        mat, matProp, module_data = shadermaker_pbr(scn, scnProp, ob, 1, preview_mat_name, self_options)

    elif load_type == 'SHADER_MAKER_AUTO':
        mat, matProp, module_data = create_default_material(scn, load_path, scnProp, ob, 1, None, name_by_images,
                                                            self_options)
        if not mat and not matProp and not module_data:
            return

    elif load_type == 'DEFAULT':
        mat, matProp, module_data = create_default_material(scn, load_path, scnProp, ob, 1, json_data_mat_info,
                                                            preview_mat_name, self_options, is_for_shader_overlay=is_for_shader_overlay)

    elif load_type == 'BLEND':
        # Qui carica qualsiasi materiale della libreria in formato .blend:
        mat, matProp, module_data = append_mat(scn, load_type, load_path, scnProp, ob, storage_method, 1,
                                               json_data_mat_info, self_options, preview_mat_name)

        if not mat:
            # text = "This material appears to be having problems, appears to have been modified in the .blend file It is recommended to eliminate this material"
            # draw_info(text,'Info','INFO')
            return

    # Replace End
    if self_options == 'REPLACE_MATERIAL':
        replace_post(ob, mat, matProp, scnProp)

    if ob.mode == 'EDIT':
        bpy.ops.object.material_slot_assign()

    matProp.uv_box = addon_prefs.default_uv_box
    force_driver_nodes(mat)

    if matProp.material_type != 'SIMPLE_PBR':
        align_module(mat)
        len_images_and_simplify(mat)

    return

I’m so sorry I didn’t paste that code in my latest reply to you formatted… I re-pasted it so you can actually read

It Is there a way to take a look at my reply again… and give me some advice

Well… you found the areas where a simple print is used… now you want some process intercommunication… ?? Like so:

Tysm…is there an online tut how to use this?

There are some examples in the this docu… also on the left side… → Beginners Guide
and maybe also look at:

Documentation… Beginners Guide… Learning Python…

I just noticed the first link was regular python not blender python Did you post this because blender dot exe and the console are two separate processes and that’s how I would communicate between them

I suggested something like this:

because the installed addons can be iterated with something like this (maybe some API changes…)

for addon in bpy.context.preferences.addons:
    print(addon.module)

…so you have acess to them and their methods/functions…

So if the output is not only printed but also counted… (added functionality by you)… then the other addon can ask for it… (also added by you…)
…or…
the addons intercommunicate with sockets, named pipes or whatever…

But AFAIK… monitoring the console is to expensive and/or (even) not possible… (bypassing stdin/stfout is posssible but this wouuld be fro the comple blender process… )…

I mentioned the question for the usecase so that maybe other users who are more into this could make some proposals…

In terms of software architecture, one way to connect two irrelevant codebases is through event-driven programming. In Blender there is the easy way that is “message bus”.

import bpy

# an object that acts as the owner of the subscription
owner = object()

# where you want to subscribe to
# (must be a property instance of bpy.types.Property or a type of bpy.types.Struct)
subscribe_to = bpy.context.object.location

# callback function
def msgbus_callback(*args):
    print("Something changed!", args)

# important to note
# * that events that generated from user won't trigger events
# * in this example you can't trigger event by moving the object on viewport
# * you can only trigger event by changing the property from UI panel

bpy.msgbus.subscribe_rna(
    key=subscribe_to, # type that will cause the event to fire
    owner=owner, # object type that will trigger the event
    args=(1, 2, 3), # something optional if needed
    notify=msgbus_callback, # function that will be called once the event is fired
)

Without message bus propably you would consider finding a workaround like this:
https://www.devdungeon.com/content/python-use-stringio-capture-stdout-and-stderr

But with message bus, say that you can subscribe to some specific property the other addon has, and when it changes you would have the callback effect running your code. (Probably the other programmer would have to create a mockup property that executes just before {'FINISHED'} return statement, and would cause an even trigger like this if you subscribe to).

1 Like