def cb_elinfo_beam(attr, old, new, curr_doc: CurrentDoc):
    """
    Callback for the beam CheckboxGroup.
    Set EI and/ or EA to be infinite.
    :param attr: not used
    :param old: not used
    :param new: changed attribute 'active' of the beam CheckboxGroup (list)
    :return: none
    """

    # block user input while element info box gets changed (because of cb_get_textinput)
    curr_doc.elinfo_input_blocked = True

    elinfo = curr_doc.input_element_info
    element_index = curr_doc.elinfo_current_element[2]

    # get current state of EA and EI
    ea_infinite = False
    ei_infinite = False
    if curr_doc.data_sources.ds_nodedep_elements.data['ea'][
            element_index] == float("inf"):
        ea_infinite = True
    if curr_doc.data_sources.ds_nodedep_elements.data['ei'][
            element_index] == float("inf"):
        ei_infinite = True

    # EA was changed to infinite
    if 0 in new and not ea_infinite:
        curr_doc.data_sources.ds_nodedep_elements.data['ea'][
            element_index] = float("inf")
        elinfo["ea"].disabled = True
    # EA was changed to finite
    elif 0 not in new and ea_infinite:
        curr_doc.data_sources.ds_nodedep_elements.data['ea'][
            element_index] = 1.0
        elinfo["ei"].value = "%3.1f" % 1.0
        elinfo["ea"].disabled = False

    # EI was changed to infinite
    if 1 in new and not ei_infinite:
        curr_doc.data_sources.ds_nodedep_elements.data['ei'][
            element_index] = float("inf")
        elinfo["ei"].disabled = True
    # EI was changed to finite
    elif 1 not in new and ei_infinite:
        curr_doc.data_sources.ds_nodedep_elements.data['ei'][
            element_index] = 1.0
        elinfo["ei"].value = "%3.1f" % 1.0
        elinfo["ei"].disabled = False

    # release for user input
    curr_doc.elinfo_input_blocked = False
def cb_get_textinput(attr, old, new, curr_doc: CurrentDoc, key):
    """
    Callback for the TextInputs of the element info box.
    :param attr: not used
    :param old: previous attribute 'value' of the TextInput (string)
    :param new: changed attribute 'value' of the TextInput (string)
    :param key: key of the specific TextInput (string)
    :return: none
    """

    # check whether user input is blocked
    if curr_doc.elinfo_input_blocked:
        return

    element_name = curr_doc.elinfo_current_element[0]
    element_indep = curr_doc.elinfo_current_element[1]
    element_index = curr_doc.elinfo_current_element[2]

    try:
        value = float(new)
    except ValueError:
        curr_doc.div_input.text = "Error: Please enter a number!"
        return

    # TODO: idea - don't accept zeros as TextInput for some keys?
    # if not(key == "angle" or key == "xn_start" or key == "xn_end" or key == "yq_start" or key == "yq_end") \
    #         and value == 0:
    #     vis_init.div_input.text = "Error: Please enter a non-zero number!"
    #     return

    # change data sources and glyphs in plot according to text input
    if key == "angle":
        # convert angle to radians and call method to adapt the angle in the input plot
        angle = math.radians(value)
        if element_indep:
            curr_doc.data_sources.ds_indep_elements.data['angle'][
                element_index] = angle
            vis_editEl.change_angle_indep(curr_doc, element_name, angle,
                                          element_index)
        else:
            curr_doc.data_sources.ds_nodedep_elements.data['angle'][
                element_index] = angle
            vis_editEl.change_angle_nodedep(curr_doc, element_name, angle,
                                            element_index)
    elif key == "k":
        k = curr_doc.data_sources.ds_nodedep_elements.data['k'][element_index]
        curr_doc.data_sources.ds_nodedep_elements.data['k'][
            element_index] = value
        if value < 0 <= k:
            vis_editEl.draw_moment_negative(curr_doc,
                                            element_name,
                                            element_index,
                                            negative=True)
        elif value > 0 >= k:
            vis_editEl.draw_moment_negative(curr_doc,
                                            element_name,
                                            element_index,
                                            negative=False)
    elif key == "force":
        curr_doc.data_sources.ds_nodedep_elements.data['f'][
            element_index] = value
    elif key == "moment":
        moment = curr_doc.data_sources.ds_nodedep_elements.data['moment'][
            element_index]
        curr_doc.data_sources.ds_nodedep_elements.data['moment'][
            element_index] = value
        if value < 0 <= moment:
            vis_editEl.draw_moment_negative(curr_doc,
                                            element_name,
                                            element_index,
                                            negative=True)
        elif value > 0 >= moment:
            vis_editEl.draw_moment_negative(curr_doc,
                                            element_name,
                                            element_index,
                                            negative=False)
    elif key == "h":
        curr_doc.data_sources.ds_nodedep_elements.data['h'][
            element_index] = value
    elif key == "ea":
        curr_doc.data_sources.ds_nodedep_elements.data['ea'][
            element_index] = value
    elif key == "ei":
        curr_doc.data_sources.ds_nodedep_elements.data['ei'][
            element_index] = value
    elif key == "xn_start" or key == "xn_end" or key == "yq_start" or key == "yq_end":
        nodedep_llxn = curr_doc.data_sources.ds_nodedep_elements.data['ll_x_n']
        nodedep_llyq = curr_doc.data_sources.ds_nodedep_elements.data['ll_y_q']
        elinfo = curr_doc.input_element_info
        curr_doc.elinfo_input_blocked = True
        if key == "xn_start":
            xn_start = value
            xn_end = nodedep_llxn[element_index][1]
            yq_start = nodedep_llyq[element_index][0]
            yq_end = nodedep_llyq[element_index][1]
            if (xn_start == 0 and xn_end == 0 and yq_start == 0
                    and yq_end == 0):
                yq_start, yq_end = -1, -1
                elinfo["yq_start"].value = "%3.1f" % yq_start
                elinfo["yq_end"].value = "%3.1f" % yq_end
            elif (xn_start > 0 and xn_end <= 0) or (xn_start < 0
                                                    and xn_end >= 0):
                xn_end = 0
                elinfo["xn_end"].value = "%3.1f" % xn_end
        elif key == "xn_end":
            xn_start = nodedep_llxn[element_index][0]
            xn_end = value
            yq_start = nodedep_llyq[element_index][0]
            yq_end = nodedep_llyq[element_index][1]
            if (xn_end == 0 and xn_start == 0 and yq_start == 0
                    and yq_end == 0):
                yq_start, yq_end = -1, -1
                elinfo["yq_start"].value = "%3.1f" % yq_start
                elinfo["yq_end"].value = "%3.1f" % yq_end
            elif (xn_end > 0 and xn_start <= 0) or (xn_end < 0
                                                    and xn_start >= 0):
                xn_start = 0
                elinfo["xn_start"].value = "%3.1f" % xn_start
        elif key == "yq_start":
            xn_start = nodedep_llxn[element_index][0]
            xn_end = nodedep_llxn[element_index][1]
            yq_start = value
            yq_end = nodedep_llyq[element_index][1]
            if (yq_start == 0 and yq_end == 0 and xn_end == 0
                    and xn_start == 0):
                xn_start, xn_end = 1, 1
                elinfo["xn_start"].value = "%3.1f" % xn_start
                elinfo["xn_end"].value = "%3.1f" % xn_end
            elif (yq_start > 0 and yq_end <= 0) or (yq_start < 0
                                                    and yq_end >= 0):
                yq_end = 0
                elinfo["yq_end"].value = "%3.1f" % yq_end
        else:
            xn_start = nodedep_llxn[element_index][0]
            xn_end = nodedep_llxn[element_index][1]
            yq_start = nodedep_llyq[element_index][0]
            yq_end = value
            if (yq_end == 0 and yq_start == 0 and xn_end == 0
                    and xn_start == 0):
                xn_start, xn_end = 1, 1
                elinfo["xn_start"].value = "%3.1f" % xn_start
                elinfo["xn_end"].value = "%3.1f" % xn_end
            elif (yq_end > 0 and yq_start <= 0) or (yq_end < 0
                                                    and yq_start >= 0):
                yq_start = 0
                elinfo["yq_start"].value = "%3.1f" % yq_start
        curr_doc.elinfo_input_blocked = False
        # check lineload before change
        if not (xn_start == 0 and xn_end == 0 and yq_start == 0
                and yq_end == 0):
            load_x_n = (xn_start, xn_end)
            load_y_q = (yq_start, yq_end)
            # change data source
            nodedep_llxn[element_index] = load_x_n
            nodedep_llyq[element_index] = load_y_q
            # draw adapted line load
            local = curr_doc.data_sources.ds_nodedep_elements.data['ll_local'][
                element_index]
            vis_editEl.draw_lineload(curr_doc, element_name, load_x_n,
                                     load_y_q, local, element_index)
        else:
            curr_doc.div_input.text = "Error: Invalid line load values, one value has to be non-zero!"
            return
    elif key == "dT":
        tt = curr_doc.data_sources.ds_nodedep_elements.data['dT_T'][
            element_index][1]
        at = curr_doc.data_sources.ds_nodedep_elements.data['dT_T'][
            element_index][2]
        curr_doc.data_sources.ds_nodedep_elements.data['dT_T'][
            element_index] = (value, tt, at)
    elif key == "T":
        dt = curr_doc.data_sources.ds_nodedep_elements.data['dT_T'][
            element_index][0]
        at = curr_doc.data_sources.ds_nodedep_elements.data['dT_T'][
            element_index][2]
        curr_doc.data_sources.ds_nodedep_elements.data['dT_T'][
            element_index] = (dt, value, at)
    elif key == "aT":
        dt = curr_doc.data_sources.ds_nodedep_elements.data['dT_T'][
            element_index][0]
        tt = curr_doc.data_sources.ds_nodedep_elements.data['dT_T'][
            element_index][1]
        curr_doc.data_sources.ds_nodedep_elements.data['dT_T'][
            element_index] = (dt, tt, value)

    # tell user about changed value
    curr_doc.div_input.text = "New value accepted!"
def cb_show_element_info(attr,
                         old,
                         new,
                         curr_doc: CurrentDoc,
                         indep=False,
                         nodedep=False):
    """
    Callback to show information about an element in the input plot when ds_element_info was changed. ds_element_info
    gets changed by cb_plot_tap when a tap in the input plot was caught without an element button being active.
    Also called when an element was just added to the plot and it was not an element of a test case.
    :param attr: not used
    :param old: not used
    :param new: changed ds_element_info.data when no button element is active and a tap in the plot was caught (dict)
    :param indep: True if just added element is a node independent one (bool)
    :param nodedep: True if just added element is a node independent one (bool)
    :return: none
    """

    # block user input while element info box gets changed (because of cb_get_textinput)
    curr_doc.elinfo_input_blocked = True

    reset_element_info(curr_doc)
    elinfo = curr_doc.input_element_info

    indep_x = curr_doc.data_sources.ds_indep_elements.data['x']
    indep_y = curr_doc.data_sources.ds_indep_elements.data['y']
    indep_t = curr_doc.data_sources.ds_indep_elements.data['type']
    indep_n = curr_doc.data_sources.ds_indep_elements.data['name']
    indep_a = curr_doc.data_sources.ds_indep_elements.data['angle']

    nodedep_n1 = curr_doc.data_sources.ds_nodedep_elements.data['name_node1']
    nodedep_n2 = curr_doc.data_sources.ds_nodedep_elements.data['name_node2']
    nodedep_t = curr_doc.data_sources.ds_nodedep_elements.data['type']
    nodedep_n = curr_doc.data_sources.ds_nodedep_elements.data['name']
    nodedep_x = curr_doc.data_sources.ds_nodedep_elements.data['x']
    nodedep_y = curr_doc.data_sources.ds_nodedep_elements.data['y']
    nodedep_l = curr_doc.data_sources.ds_nodedep_elements.data['length']
    nodedep_dt = curr_doc.data_sources.ds_nodedep_elements.data['dT_T']
    nodedep_k = curr_doc.data_sources.ds_nodedep_elements.data['k']
    nodedep_h = curr_doc.data_sources.ds_nodedep_elements.data['h']
    nodedep_ei = curr_doc.data_sources.ds_nodedep_elements.data['ei']
    nodedep_ea = curr_doc.data_sources.ds_nodedep_elements.data['ea']
    nodedep_m = curr_doc.data_sources.ds_nodedep_elements.data['moment']
    nodedep_f = curr_doc.data_sources.ds_nodedep_elements.data['f']
    nodedep_lll = curr_doc.data_sources.ds_nodedep_elements.data['ll_local']
    nodedep_llxn = curr_doc.data_sources.ds_nodedep_elements.data['ll_x_n']
    nodedep_llyq = curr_doc.data_sources.ds_nodedep_elements.data['ll_y_q']
    nodedep_a = curr_doc.data_sources.ds_nodedep_elements.data['angle']

    # check if element was just added
    if indep:
        index = len(indep_x) - 1
        name = indep_n[index]
    elif nodedep:
        index = len(curr_doc.data_sources.ds_nodedep_elements.data['x']) - 1
        name = nodedep_n[index]
    # search for element in data sources if it was not just added
    else:
        # initialize for element search
        index = -1
        name = False
        tap_x = new['tap_x'][0]
        tap_y = new['tap_y'][0]
        # check if tap was on an node independent element in the input plot
        for i in range(0, len(indep_x)):
            if abs(indep_x[i] - tap_x) < curr_doc.catch_radius and abs(
                    indep_y[i] - tap_y) < curr_doc.catch_radius:
                index = i
                name = indep_n[index]
                indep = True
                break
        # check if tap was on a node dependent element
        else:
            # search in glyph data source of type spring, load_point, load_moment or temp
            ds_glyph = curr_doc.data_sources.ds_glyph_springsPointMomentTemp
            ds_glyph_x = ds_glyph.data['glyph_x']
            ds_glyph_y = ds_glyph.data['glyph_y']
            for j in range(len(ds_glyph_x)):
                if abs(ds_glyph_x[j] - tap_x) < curr_doc.catch_radius and abs(
                        ds_glyph_y[j] - tap_y) < curr_doc.catch_radius:
                    name = ds_glyph.data['name_user'][j]
                    break
            # search in glyph data source of beams
            else:
                ds_glyph = curr_doc.data_sources.ds_glyph_beam
                ds_glyph_x = ds_glyph.data['x']
                ds_glyph_y = ds_glyph.data['y']
                for j in range(len(ds_glyph_x)):
                    if abs(ds_glyph_x[j] -
                           tap_x) < curr_doc.catch_radius and abs(
                               ds_glyph_y[j] - tap_y) < curr_doc.catch_radius:
                        name = ds_glyph.data['name_user'][j]
                        break
                # search in glyph data source of line loads
                else:
                    ds_glyph = curr_doc.data_sources.ds_glyph_lineload
                    ds_glyph_x = ds_glyph.data['glyph_x']
                    ds_glyph_y = ds_glyph.data['glyph_y']
                    for j in range(len(ds_glyph_x)):
                        if abs(ds_glyph_x[j] -
                               tap_x) < curr_doc.catch_radius and abs(
                                   ds_glyph_y[j] -
                                   tap_y) < curr_doc.catch_radius:
                            name = ds_glyph.data['name_user'][j]
                            break
            # get index of element in data source of node dependent elements
            if name:
                for i in range(len(nodedep_n)):
                    if name == nodedep_n[i]:
                        index = i
                        break

    # reaction if no element was found for that tap
    if index == -1:
        curr_doc.div_input.text = "No button element active and no element of graph clicked."
        return

    # make information about current element global for the callbacks and get enum_type
    if indep:
        curr_doc.elinfo_current_element = (name, True, index)
        enum_type = indep_t[index]
    else:
        curr_doc.elinfo_current_element = (name, False, index)
        enum_type = nodedep_t[index]

    # adapt element info box
    elinfo["name"].value = name
    if indep:
        # set values
        elinfo["x"].value = "%3.1f" % indep_x[index]
        curr_doc.div_element_info["x"].text = "x:"
        elinfo["y"].value = "%3.1f" % indep_y[index]
        curr_doc.div_element_info["y"].text = "y:"
        # enable input
        if not enum_type == eLnum.ElSupEnum.NODE.value and not enum_type == eLnum.ElSupEnum.JOINT.value:
            elinfo["angle"].disabled = False
            elinfo["angle"].value = "%3.1f" % math.degrees(indep_a[index])
    elif enum_type == eLnum.ElSupEnum.SPRING_SUPPORT.value or enum_type == eLnum.ElSupEnum.SPRING_MOMENT_SUPPORT.value:
        # set values
        elinfo["x"].value = "%3.1f" % nodedep_x[index]
        curr_doc.div_element_info["x"].text = "x:"
        elinfo["y"].value = "%3.1f" % nodedep_y[index]
        curr_doc.div_element_info["y"].text = "y:"
        elinfo["angle"].value = "%3.1f" % math.degrees(nodedep_a[index])
        elinfo["k"].value = "%3.1f" % nodedep_k[index]
        # enable input
        elinfo["angle"].disabled = False
        elinfo["k"].disabled = False
    elif enum_type == eLnum.ElSupEnum.SPRING.value:
        # set values
        elinfo["x"].value = nodedep_n1[index]
        curr_doc.div_element_info["x"].text = "node 1:"
        elinfo["y"].value = nodedep_n2[index]
        curr_doc.div_element_info["y"].text = "node 2:"
        elinfo["angle"].value = "%3.1f" % math.degrees(nodedep_a[index])
        elinfo["k"].value = "%3.1f" % nodedep_k[index]
        # enable input
        elinfo["k"].disabled = False
    elif enum_type == eLnum.ElSupEnum.LOAD_POINT.value:
        # set values
        elinfo["x"].value = "%3.1f" % nodedep_x[index]
        curr_doc.div_element_info["x"].text = "x:"
        elinfo["y"].value = "%3.1f" % nodedep_y[index]
        curr_doc.div_element_info["y"].text = "y:"
        elinfo["angle"].value = "%3.1f" % math.degrees(nodedep_a[index])
        elinfo["force"].value = "%3.1f" % nodedep_f[index]
        # enable input
        elinfo["angle"].disabled = False
        elinfo["force"].disabled = False
    elif enum_type == eLnum.ElSupEnum.LOAD_MOMENT.value:
        # set values
        elinfo["x"].value = "%3.1f" % nodedep_x[index]
        curr_doc.div_element_info["x"].text = "x:"
        elinfo["y"].value = "%3.1f" % nodedep_y[index]
        curr_doc.div_element_info["y"].text = "y:"
        elinfo["moment"].value = "%3.1f" % nodedep_m[index]
        # enable input
        elinfo["moment"].disabled = False
    elif enum_type == eLnum.ElSupEnum.LOAD_TEMP.value:
        # set values
        elinfo["x"].value = nodedep_n1[index]
        curr_doc.div_element_info["x"].text = "node 1:"
        elinfo["y"].value = nodedep_n2[index]
        curr_doc.div_element_info["y"].text = "node 2:"
        elinfo["dT"].value = "%3.1f" % nodedep_dt[index][0]
        elinfo["T"].value = "%3.1f" % nodedep_dt[index][1]
        elinfo["aT"].value = "%3.1f" % nodedep_dt[index][2]
        # enable input
        elinfo["dT"].disabled = False
        elinfo["T"].disabled = False
        elinfo["aT"].disabled = False
    elif enum_type == eLnum.ElSupEnum.BEAM.value:
        # set values
        elinfo["x"].value = nodedep_n1[index]
        curr_doc.div_element_info["x"].text = "node 1:"
        elinfo["y"].value = nodedep_n2[index]
        curr_doc.div_element_info["y"].text = "node 2:"
        elinfo["angle"].value = "%3.1f" % math.degrees(nodedep_a[index])
        elinfo["length"].value = "%3.1f" % nodedep_l[index]
        elinfo["h"].value = "%3.1f" % nodedep_h[index]
        ea = nodedep_ea[index]
        ei = nodedep_ei[index]
        active = []
        if ea == float("inf"):
            active.append(0)
        else:
            elinfo["ea"].value = "%3.1f" % ea
            elinfo["ea"].disabled = False

        if ei == float("inf"):
            active.append(1)
        else:
            elinfo["ei"].value = "%3.1f" % ei
            elinfo["ei"].disabled = False
        curr_doc.group_element_info["beam"].active = active
        # enable input
        curr_doc.group_element_info["beam"].disabled = False
        elinfo["h"].disabled = False
    elif enum_type == eLnum.ElSupEnum.LOAD_LINE.value:
        # set values
        elinfo["x"].value = nodedep_n1[index]
        curr_doc.div_element_info["x"].text = "node 1:"
        elinfo["y"].value = nodedep_n2[index]
        curr_doc.div_element_info["y"].text = "node 2:"
        elinfo["length"].value = "%3.1f" % nodedep_l[index]
        elinfo["xn_start"].value = "%3.1f" % nodedep_llxn[index][0]
        elinfo["xn_end"].value = "%3.1f" % nodedep_llxn[index][1]
        elinfo["yq_start"].value = "%3.1f" % nodedep_llyq[index][0]
        elinfo["yq_end"].value = "%3.1f" % nodedep_llyq[index][1]
        local = nodedep_lll[index]
        if local:
            curr_doc.group_element_info["ll"].active = 0
            curr_doc.div_element_info["xn"].text = "* n"
            curr_doc.div_element_info["yq"].text = "* q"
        else:
            curr_doc.group_element_info["ll"].active = 1
            curr_doc.div_element_info["xn"].text = "* p<sub>x</sub>"
            curr_doc.div_element_info["yq"].text = "* p<sub>y</sub>"
        # enable input
        curr_doc.group_element_info["ll"].disabled = False
        elinfo["xn_start"].disabled = False
        elinfo["xn_end"].disabled = False
        elinfo["yq_start"].disabled = False
        elinfo["yq_end"].disabled = False

    # resize element info box to children truly containing information (WARNING: too slow!)
    # vis_init.layout_element_info.children[1].children.insert(0, vis_init.children_element_info["del"])
    # vis_init.layout_element_info.children[1].children.insert(0, vis_init.children_element_info["name"])
    # for key in elinfo:
    #     if not key == "name" and not key == "del":
    #         if not elinfo[key].value == "":
    #             child = vis_init.children_element_info[key]
    #             child_index = len(vis_init.layout_element_info.children[1].children) - 1
    #             vis_init.layout_element_info.children[1].children.insert(child_index, child)
    # show element info box
    # vis_init.layout_element_info.visible = True

    # release for user input
    curr_doc.elinfo_input_blocked = False