2

I am playing around with YAD dialogs in BASH and am having trouble with the button construction. I can't get a YAD button to call a function in the same script. Is there a way to do this?

My understanding is the if I use the following construction, pressing the button will call the command that follows the colon This example (which works) will open an instance of Firefox if a user clicks the Open Browser button:

yad --button="Open browser":firefox

I have a script with several BASH functions. I'd like a button press to call one of the functions. It doesn't. The following is a simple script that, when run, demonstrates the disappointing behavior:

#!/bin/bash,

click_one()
{
   yad --center --text="Clicked the one"
}

click_two()
{
   yad --center --text="Clicked the two"
}

cmd="yad --center --text=\"Click a button to see what happens\" \
      --button=\"One\":click_one \
      --button=\"Two\":2 \
      --button=\"Date\":date \
      --button=\"Exit\":99"

proceed=true

while $proceed; do
    eval "$cmd"
    exval=$?

    case $exval in
        2) click_two;;
        99) proceed=false;;
    esac
done

In the code above, button Date works as expected, calling the date command. Buttons Two and Exit work because I'm checking the exit value of the command and branching on its value. Sadly (for me), button One does nothing. I had hoped that clicking on button One would call the local function click_one. I would like to know if there's a way to format the YAD command so that the click_one function is called.

While the above code suggests a workaround using the exit value, my real goal is to apply a successful answer to a form button that, as far as I can figure out so far, doesn't return an exit value. In other words, the following also fails silently, but I'd like it to invoke function click_one:

yad --form --field="One":fbtn click_one
chuckj
  • 135
  • 1
  • 12

2 Answers2

4

a possible way:

#!/bin/bash


click_one(){
   yad --center --text="Clicked the one"
}

click_two(){
   yad --center --text="Clicked the two"
}

export -f click_one click_two

yad \
    --title "My Title" \
    --center --text="Click a button to see what happens" \
    --button="One":"bash -c click_one" \
    --button="Two":"bash -c click_two" \
    --button="Date":"date" \
    --button="Exit":0


echo $?
kyodev
  • 573
  • 2
  • 14
1

Apparently not, it needs to be an actual command.

You could: put your functions in a separate file and as the command, launch bash, source that file, and call the function.

Here, I'm also restructuring your code to store the yad command in an array. This will make your script more robust:

# using an array makes the quoting a whole lot easier
cmd=(
    yad --center --text="Click a button to see what happens" 
      --button="One":"bash -c 'source /path/to/functions.sh; click_one'"
      --button="Two":2 
      --button="Date":date 
      --button="Exit":99
)

while true; do
    "${cmd[@]}"      # this is how to invoke the command from the array
    exval=$?
    case $exval in
        2) click_two;;
        99) break;;
    esac
done
glenn jackman
  • 238,783
  • 38
  • 220
  • 352
  • 2
    Both this answer and the one from @kyodev included part of the solution, using "bash -c click_one". What is missing is that I should export the functions in order to access them from the yad control. In my final version, I added `export -f click_one` and `export -f click_two` before the while loop, then `unset click_one` and `unset click_two` after (to prevent the functions being available after the script ends). +1 for suggesting the command array, a construct with which I was not previously familiar. – chuckj Apr 10 '18 at 03:01
  • 1
    @chuckj I don't know if there was an update to `yad` after this discussion but there is now a `--use-interp` option which routes commands to the shell (`bash` if not specified). I tend to have it in the environment variable: `export YAD_OPTIONS='--use-interp'` – Daxx Apr 20 '23 at 19:40
  • @Daxx Though haven't played with `yad` for a while, I have been trying figure out writing Bash builtins recently, and what you suggest makes sense. The `make_bare_simple_command()` allows you to call functions defined in the script, which I think I was hoping for when I originally posted this. – chuckj Apr 22 '23 at 12:44
  • @chuckj With the addition of `export -f`, as you acknowledged, and `--use-interp`, the functions in your OP work as you wanted (without `bash -c` added). `make_bare_simple_command()` looks a bit too "bash internal", to me. If you want something more widely available, you could drop it in `~/.bashrc` (see, e.g. https://askubuntu.com/questions/344557/where-can-i-put-a-user-defined-shell-function) – Daxx Apr 23 '23 at 01:09