4

I need to setup a configuration file in Bash and believe Zenity is the best method but am having problems finding a link using google with a suitable example.

So far my code looks like this:

#!/bin/bash

zenity --forms --title="Laptop Adaptive Brightness" --text="Set Configuration" \
   --add-entry="/sys/class/backlight/??????/brightness" \
   --add-entry="Night time (min) value" \
   --add-entry="Day time (max) value" \
   --add-entry="Minutes after sunrise to max" \
   --add-entry="Minutes before sunset to min"

exit

and the resulting screen looks like this:

Adaptive Brightness Config

If values can't be preassigned to Zenity variables then I could put the current value into the field label ie:

   --add-entry="Night time (min) value (Current: $min)", \

And the user can leave the field blank to remain the same or enter a new value.

I'm still missing the best way of extracting Zenity fields to bash variables and writing them to the configuration file. Also I'm missing the code to read from configuration file to bash variables.

I'd appreciate a link to a Zenity example or a full answer would be even better. Thank you.

PS When I run the above script an error is reported when it completes:

$ adaptive-brightness-configGtk-Message: GtkDialog mapped without a transient parent. This is discouraged.
||||

I'm a little confused by this and if you can point out what I'm doing wrong that would be appreciated too.

  • 4
    Probably you're overestimating Zenity's capabilities... I would make a simple GUI with Python (using Tk, GTK, Qt or whatever you want) instead of restricting myself to Bash+Zenity. – Byte Commander Feb 27 '17 at 00:35

2 Answers2

3

Zenity can only display previous value when there is only one entry field. As such the code below puts the previous values into the label fields and instructs user to type new value into entry fields or leave it blank to keep existing value.

The Bash Code

#!/bin/bash

# Read configuration file with entries separated by " " into array
IFS=' ' read -ra CfgArr < ~/bin/adaptive-brightness-configuration-file

# Zenity form with current values in entry label
# because initializing multiple entry data fields not supported
output=$(zenity --forms --title="Laptop Adaptive Brightness Configuration" \
        --text="Enter new settings or leave entries blank to keep (existing) settings" \
   --add-entry="/sys/class/backlight/??????/brightness driver : (${CfgArr[0]})" \
   --add-entry="Day time maximum display brightness : (${CfgArr[1]})" \
   --add-entry="Transition minutes after sunrise to maximum : (${CfgArr[2]})" \
   --add-entry="Night time minimum display brightness : (${CfgArr[3]})" \
   --add-entry="Transition minutes before sunset to minimum : (${CfgArr[4]})")

IFS='|' read -a ZenArr <<<$output # Split zenity entries separated by "|" into array elements

# Update non-blank zenity array entries into configuration array
for i in ${!ZenArr[@]}; do
    if [[ ${ZenArr[i]} != "" ]]; then CfgArr[i]=${ZenArr[i]} ; fi
done

# write configuration file using array (fields automatically separated by " ")
echo "${CfgArr[@]}" > ~/bin/adaptive-brightness-configuration-file

I was surprised after hours of googling, examples of this code couldn't be found. Hopefully others googling the same problem can find this code.

The Screen

adaptive brightness 3

In this answer the zenity form has a different order and expanded labels for fields. Although 4882 is maximum for this intel_backlight driver it's like staring into the sun and 1000 is practical maximum indoors.

Many thanks to muru for guidance converting original code from old-style COBOL format using field names, to modern Bash format utilizing arrays.


Using yad instead of zenity

In 2018 I revamped the project and renamed it to Eyesome. Now it uses yad which is a super-charged forked version of zenity. yad uses the same coding style in bash and adds more functionality.

Notebook support for multiple tabs

Using yad you can display current field values plus create forms in tabbed notebook format:

eyesome-edit-configuration-general.png

Whilst writing this answer I noticed the screen was out of date and says 5 to 20 seconds. I've changed it to say 1 to 20 seconds for the next publication.

Monitor 3 from the tab list

Here is what Monitor 3 looks like from the tab list:

eyesome-edit-configuration-monitor-3.png

Sample code for generating this screen is listed in the next section.

Sample code

The three monitors share a common function to build the bulk of the code. For Monitor 3 we use:

# Monitor 3 notebook page
BuildMonitorPage "$CFG_MON3_NDX"
yad --plug=$KEY --tabnum=4 --form \
    "${aMonPage[@]}" > "$res4" &

The BuildMonitorPage function does the heavy lifting though. Here is what it looks like:

BuildMonitorPage () {
    # Move configuration array monitor 1-3 to Working Screen fields
    # $1 = CfgArr Starting Index Number
aMonPage=()
i=&quot;$1&quot;
aMonPage+=(&quot;--field=Monitor Number::RO&quot;)
aMonPage+=(&quot;${CfgArr[$((i++))]}&quot;)

aMonPage+=(&quot;--field=Monitor Status::CB&quot;)
Status=(&quot;${CfgArr[$((i++))]}&quot;)
cbStatus=&quot;Enabled!Disabled&quot;
cbStatus=&quot;${cbStatus/$Status/\^$Status}&quot;
aMonPage+=(&quot;$cbStatus&quot;)

aMonPage+=(&quot;--field=Monitor Type::CB&quot;)
Type=(&quot;${CfgArr[$((i++))]}&quot;)
cbType=&quot;Hardware!Software&quot;
cbType=&quot;${cbType/$Type/\^$Type}&quot;
aMonPage+=(&quot;$cbType&quot;)

aMonPage+=(&quot;--field=Monitor Name:&quot;)
aMonPage+=(&quot;${CfgArr[$((i++))]}&quot;)
aMonPage+=(&quot;--field=Internal Name:&quot;)
aMonPage+=(&quot;${CfgArr[$((i++))]}&quot;)
aMonPage+=(&quot;--field=Xrandr Name:&quot;)
aMonPage+=(&quot;${CfgArr[$((i++))]}&quot;)
aMonPage+=(&quot;--field=Daytime Brightness::NUM&quot;)
aMonPage+=(&quot;${CfgArr[$((i++))]}&quot;!0.1..9999!.01!2)
aMonPage+=(&quot;--field=Daytime Red::NUM&quot;)
aMonPage+=(&quot;${CfgArr[$((i++))]}&quot;!0.1..2.0!.01!2)
aMonPage+=(&quot;--field=Daytime Green::NUM&quot;)
aMonPage+=(&quot;${CfgArr[$((i++))]}&quot;!0.1..2.0!.01!2)
aMonPage+=(&quot;--field=Daytime Blue::NUM&quot;)
aMonPage+=(&quot;${CfgArr[$((i++))]}&quot;!0.1..2.0!.01!2)
aMonPage+=(&quot;--field=Nighttime Brightness::NUM&quot;)
aMonPage+=(&quot;${CfgArr[$((i++))]}&quot;!0.1..9999!.01!2)
aMonPage+=(&quot;--field=Nighttime Red::NUM&quot;)
aMonPage+=(&quot;${CfgArr[$((i++))]}&quot;!0.1..2.0!.01!2)
aMonPage+=(&quot;--field=Nighttime Green::NUM&quot;)
aMonPage+=(&quot;${CfgArr[$((i++))]}&quot;!0.1..2.0!.01!2)
aMonPage+=(&quot;--field=Nighttime Blue::NUM&quot;)
aMonPage+=(&quot;${CfgArr[$((i++))]}&quot;!0.1..2.0!.01!2)
aMonPage+=(&quot;--field=Current Brightness::RO&quot;)
aMonPage+=(&quot;${CfgArr[$((i++))]}&quot;)
aMonPage+=(&quot;--field=Current Gamma::RO&quot;)
aMonPage+=(&quot;${CfgArr[$((i++))]}&quot;)

} # BuildMonitorPage

Yad will store numbers internally to 6 decimal places by default. During presentation to user you can override the number of decimal places used. In the code above you see:

    aMonPage+=("--field=Nighttime Brightness::NUM")
    aMonPage+=("${CfgArr[$((i++))]}"!0.1..9999!.01!2)

The last line contains current value from configuration array (CfgArr) followed by:

  • 0.1 minimum allowed value
  • 9999 maximum allowed value
  • .01 step value if user presses up arrow or down arrow to change
  • 2 number of decimal places displayed on screen

To see all the screens and read an overview see this Ask Ubuntu Answer:

Visit the eyesome github page and download all the bash code here:

  • 2
    Suggestions: use mapfile to read the contents of a file directly into an array. Use IFS='|' read -a fields <<<$output to get the contents of $output into an array, instead of repeatedly invoking awk. – muru Feb 27 '17 at 05:30
  • @muru your great suggestions have been implemented. Thanks for nudging me down the path to learning bash arrays :) The code is more concise now. – WinEunuuchs2Unix Feb 27 '17 at 07:25
  • 2
    Next, DRY: those ifs could be written: for i in ${!ZenArr[@]}; do if [[ ${ZenArr[i]} != "" ]]; then CfgArr[i]=${ZenArr[i]}; fi; done – muru Feb 27 '17 at 08:23
  • (There is a concise way to write that if condition: CfgArr[i]=${ZenArr[i]:-${CfgArr[i]}}, but it's rather ugly, IMO). – muru Feb 27 '17 at 08:26
  • @muru Thank you for latest if suggestion. I've acknowledged your due credit in body of answer. – WinEunuuchs2Unix Feb 27 '17 at 11:43
  • 1
    Thanks, but you don't need to do that! :) One last thing: use "${CfgArr[@]}" , which expands to each array element as a separate argument, so echo "${CfgArr[@]}" will get you the same effect. – muru Feb 27 '17 at 11:46
  • I should have figured ${CfgArr[@]} on my own because I was using that in echo to screen during debugging shrugs. Yes I had to give you credit and you're most welcome :) – WinEunuuchs2Unix Feb 27 '17 at 11:53
1

yad does the same a lot easier

$ yad --form                                                  \
   --field "/sys/class/backlight/??????/brightness driver" 10 \
   --field "Day time maximum display brightness" 20           \
   --field "Transition minutes after sunrise to maximum" 30   # And so go on

enter image description here

Sergiy Kolodyazhnyy
  • 105,154
  • 20
  • 279
  • 497
Julio N
  • 41