Page 1 of 1

Change value in tree from tcl procedure

Posted: Thu Sep 08, 2022 3:51 pm
by tiago_valente
Is it possible to change tha value of a field in the tree, from a tcl procedure and update its value in the tree?
If it is, can you provide an simple example.

Re: Change value in tree from tcl procedure

Posted: Thu Sep 08, 2022 6:00 pm
by escolano
Yes,
the data tree is stored in a XML DOM structure, and the window treectrl is filled from this data.
using the tdom package (https://docs.activestate.com/activetcl/8.6/tcl/tdom) you can access to this data
https://gidsimulation.atlassian.net/wik ... nformation

e.g.
load the problemtype Examples/cmas2d_customlib
you will see a tree like this
cmas2d_customlib_tree.png
cmas2d_customlib_tree.png (33.9 KiB) Viewed 2981 times
you can see the XML of his DOM with a command like this:
-np- W [[$::gid_groups_conds::doc documentElement] asXML]

Code: Select all

rials" pn="Edit materials" icon="darkorange-block1.png" proc="Cmas2d::EditDatabaseListDirect %W %DICT %BC"/>
            </value>
        </condition>
        <container n="materials" pn="Materials" icon="darkorange-block1" help="Materials database">
            <blockdata n="material" name="Air" sequence="1" editable_name="unique" icon="darkorange-wind-sign" help="Material definition" morebutton="0">
                <value n="Density" pn="Density" v="1.01" help="Superficial density assuming a thickness of 1 meter" unit_magnitude="M/L^2" units="kg/m^2"/>
            </blockdata>
            <blockdata n="material" name="Steel" sequence="1" editable_name="unique" icon="darkorange-bracket" help="Material definition" morebutton="0">
                <value n="Density" pn="Density" v="7850" help="Superficial density assuming a thickness of 1 meter" unit_magnitude="M/L^2" units="kg/m^2"/>
            </blockdata>
            <blockdata n="material" name="Aluminium" sequence="1" editable_name="unique" icon="darkorange-alu" help="Material definition" morebutton="0">
                <value n="Density" pn="Density" v="2650" help="Superficial density assuming a thickness of 1 meter" unit_magnitude="M/L^2" units="kg/m^2"/>
            </blockdata>
        </container>
    </container>
    <display_options frame_width="321" is_frame_open="1" view_conditions_search="1" conditions_search_values="" frame_side="left"/>
    <global_preferences>
        <units>
            <unit_magnitude_user_sel n="M" values="kg"/>
            <unit_magnitude_user_sel n="L" values="m"/>
            <unit_mesh n="m"/>
        </units>
        <tree_preferences/>
    </global_preferences>
</cmas2d_customlib_data>
If do you want for example ask for the 'Density' of the material named 'Steel' , can see in the XML that in this problemtype the materials are in a node named 'container' with an attribute 'n' with value 'materials' and then several child nodes named 'blockdata' with an attribute 'n' with value 'material' and we want the one that match the attribute 'name' that we want to ask.
then we use an xpath like this
set x_path "//container\[@n='materials'\]/blockdata\[@n='material' and @name='$material'\]"
and inside there are sub-childs with name= 'value for each question, and we want the one with attribute 'n' equal to our question to ask
set x_path_3 ".//value\[@n='$question'\]"

can define these procedures

Code: Select all

proc my_get_dom_nodes { xpath } {
    set doc $gid_groups_conds::doc
    set root [$doc documentElement]
    set dom_nodes [$root selectNodes $xpath]
    return $dom_nodes
}

proc my_get_material { material question } {
    set value ""
    set x_path "//container\[@n='materials'\]/blockdata\[@n='material' and @name='$material'\]"
    set dom_material [my_get_dom_nodes $x_path]
    if { [llength $dom_material] != 1 } {
        error "Error: material '$material' not found"
    } else {
        set x_path_3 ".//value\[@n='$question'\]"
        set dom_node [lindex [$dom_material selectNodes $x_path_3] 0]
        if { $dom_node == "" } {
            error "GiD_AccessValue: property $question not found in material $material"
        }
        set value [$dom_node getAttribute v ""]
    }
    return $value
}
and use it like this:
-np- W [my_get_material Steel Density]
and will get the value
7850

if do you want to set its value, can define another procedure like this:

Code: Select all

proc my_set_material { material question value } {
    set x_path "//container\[@n='materials'\]/blockdata\[@n='material' and @name='$material'\]"
    set dom_material [my_get_dom_nodes $x_path]
    if { [llength $dom_material] != 1 } {
        error "Error: material '$material' not found"
    } else {
        set x_path_3 ".//value\[@n='$question'\]"
        set dom_node [lindex [$dom_material selectNodes $x_path_3] 0]
        if { $dom_node == "" } {
            error "GiD_AccessValue: property $question not found in material $material"
        }
        $dom_node setAttribute v $value
    }
    return $value
}
and use it like this to set to other value:
-np- W [my_set_material Steel Density 8200.5]

and to refresh the tree widget is possible to use something like this (simple but expensive because will destroy and rebuild the whole widget)
-np-GidUtils::UpdateWindow CUSTOMLIB

Re: Change value in tree from tcl procedure

Posted: Fri Sep 09, 2022 11:32 am
by tiago_valente
Thanks for the reply.

In regard to the update of the tree, the command gid_groups_conds::actualize_conditions_window is less "expensive" than the proposed GidUtils::UpdateWindow ?

Best regards

Re: Change value in tree from tcl procedure

Posted: Fri Sep 09, 2022 11:57 am
by escolano
They will have similar cost,
in fact GidUtils::UpdateWindow CUSTOMLIB will call gid_groups_conds::actualize_conditions_window

I recommend you use GidUtils::UpdateWindow instead directly gid_groups_conds::actualize_conditions_window
because it use a common centralized syntax to update several windows (CUSTOMLIB, GROUPS, LAYER, ...)
and the same for
GidUtils::OpenWindow, GidUtils::CloseWindow, GidUtils::ExistsWindow, GidUtils::ToggleWindow
(see comments on <GiD>\scripts\dev_kit.tcl to know the names of windows for these hight level functions)

If you do several changes of the tree and do a final update the cost doesn't matter, but don't update it after each change, in this case must use some lower level function to not rebuild the whole tktreectrl widget)