GtkDialog lets your bash script run in gui.
Here's some tips for learning more about gtkdialog.
This info is based on input from many people. Please share your knowledge.
###################################################################################
INDEX
1.) Where to start - links to documentation and examples.
2.) Syntax - How to use variables in the code.
3.) Let the script return data - Choose a proper coding structure.
4.) Let external code act on your gtkdialog gui
5.) The benefits of a config file
--> Set default status of Radiobuttons, Comboboxes...
--> How to store window size/placement
6.) Speed issues - Let your turtle run
7.) Extended use of widgets - Undocumented features
8.) Advanced Syntax - make your code readable
--> Comments in your code.
--> Include code if.......
9.) Tips and tricks
--> How to change the gui without restarting the app
--> Control the icon/label of menuitems.
--> Understand the alignment.
--> Window header icon.
--> Refresh image
--> Drag'n drop.
--> Hotkeys.
--> Set a fixed size of a progressbar.
--> Define unique gtk-theme.
--> Override window manager - skip taskbar, sticky, layer, border
--> Let entry accept (some) numbers only.
--> MIME.
--> Checkbox with image
--> Use a splash screen.
--> Press 'Enter' instead of clicking the 'Ok' button.
Tips and tricks - Links
--> Text managing
--> Calendar
--> Transparent background (gtkdialog desklets)
--> Right-click menu
--> Knob
--> Dynamic tabs
--> Chooser - navigate by pressing keys
###################################################################################
1.) Where to start
###################################################################################
>> Thunor has written a complete reference guide for the widgets.
>> Basic tutorials here and here
>> Examples are often easier to understand.
>> General documentation of gtkdialog is poor, but it's a start.
>> The command 'gtkdialog --help' shows available parameters.
Source code: https://github.com/01micko/gtkdialog
Compile info: http://murga-linux.com/puppy/viewtopic. ... 294#538294
Pets: http://murga-linux.com/puppy/viewtopic. ... 526#707526
NOTE!
woodenshoe-wi has fixed the sourcecode to work with GTK3
###################################################################################
2.) Syntax - How to use variables in the code.
###################################################################################
The basics of building a simple gui is told by the links in chapter 1.) You should have played a little bit before you continue.
About QUOTES
To show content of a variable in the gtkdialog code, we need to quote the variable in the gtkdialog code.
Code: Select all
#!/bin/bash
TEXT='Hello world'
export script='
<text>
<label>Content of variable is: '"$TEXT"'</label>
</text>'
gtkdialog --program=script
First realize that you put all the gtkdialog code into a variable - ie MAIN_WINDOW or Psync:
export Psync="<window title=\"Psync\"...........</window>"
if you now try to 'echo Psync' you'll get
<window title="Psync"...........</window>
which is correct code. But if you used ' instead of ", like this:
export Psync='<window title=\"Psync\"...........</window>'
you'll get
<window title=\"Psync\"...........</window>
This will give a syntax error.
export Psync='<window title="Psync"...........</window>'
will give correct syntax
Now using a variable like this:
TITLE=Psync
export Psync="<window title=\"$TITLE\"...........</window>"
returns
<window title="Psync"...........</window>
while
export Psync='<window title="$TITLE"...........</window>'
returns
<window title="$TITLE"...........</window>
and not the content of variable TITLE
Now return to the first example about quotes. This example combines the use of ' and ".
###################################################################################
3.) Let the script return data - Choose a proper coding structure
###################################################################################
The first step is to show a dialog window on your screen, then the next step is the interaction of the user with the gui. I think the easiest way is to divide guis into different groups. - Simple, Complex and Projects.
>> Simple
A simple dialog allows the user to give a response to a question/query on the screen:
- Do you want to continue?
- Please choose item in list
- ...
When the user click on the yes/no/cancel/ok-button the dialog exits and the script continues to evaluate the user input. Here is an example:
Code: Select all
#!/bin/bash
export script='
<vbox>
<text><label>Are you sure?</label></text>
<hbox>
<button no></button>
<button yes><action>EXIT:sure</action></button>
</hbox>
</vbox>'
I=$IFS; IFS=""
for STATEMENTS in $(gtkdialog --program=script); do
eval $STATEMENTS
done
IFS=$I
[ $EXIT = sure ] && gxmessage 'You are sure'
For more complex dialogs, we want to keep the gui alive even if the user clicks on something, so using an EXIT value is not the right choice. Calling pre-defined functions from inside the gtkdialog gui is a handy way to do this. We must remember to export all functions before executing our gui.
Code: Select all
#!/bin/sh
now () {
date > /tmp/date
}
export -f now
export script='
<vbox>
<entry>
<variable>ENTRY_DATE</variable>
<input>cat /tmp/date</input>
</entry>
<button>
<label>Refresh</label>
<action>now</action>
<action>refresh:ENTRY_DATE</action>
</button>
</vbox>'
gtkdialog -p script
When your project grows and its complexity makes it hard to work with, it's time to consider another approach. Splitting code into several files might be much more usable when working with your code. Group familiar functions into one file... In this example, the function-file must be saved in /root/test to work. Instead of sending <action> to EXIT, we send it to the file with the function 'now'.
NOTE, that I don't use an ordinary function (like the previous example), but a case command instead. The benefits are faster loading of your app since it does not read your functions into memory, but the greatest advantage is that you can edit the function while the gui is running. The downside are slower execution of the function.
Function file
Code: Select all
#!/bin/sh
case $1 in
-now)
date > /tmp/date
exit;;
esac
Code: Select all
export script='
<vbox>
<entry>
<variable>ENTRY_DATE</variable>
<input>cat /tmp/date</input>
</entry>
<button>
<label>Refresh</label>
<action>/root/test -now</action>
<action>refresh:ENTRY_DATE</action>
</button>
</vbox>'
I=$IFS; IFS=""
for STATEMENTS in $(gtkdialog -p script); do
eval $STATEMENTS
done
IFS=$I
###################################################################################
4.) Let external code act on your gtkdialog gui
###################################################################################
>> If you want a second gtkdialog-gui to act on your main gui. You simply "launch" the second gui from the main gui. The negative aspect about this solution is that before showing the main gui, it has to load all the launchable scripts. This means slower startup.
Code: Select all
#!/bin/sh
export launch_me='
<button>
<input file stock="gtk-refresh"></input>
<action>Refresh:DATE</action>
</button>'
export box='
<vbox>
<entry><input>date</input><variable>DATE</variable></entry>
<button><label>launch</label><action type="launch">launch_me</action></button>
</vbox>'
gtkdialog -p box
Gtkdialog updates a progressbar with info from standard output. Most common are the 'cat' and 'echo' commands. First 'echo "text in bar"' (optional), then 'echo 50' or whatever % integer to define how long the process is come. When a progressbar achieves 100% done ('echo 100'), it activates its <actions>. So, as long as % is below 100, the actions are not run, but as soon as you 'echo 100' it will execute your defined actions. What is very good, is that you now can 'echo 0', and start all over again. Last stage is to define if you want the progressbar shown in your gui. add visible="false" to make it invisible.
Here is a simple example of a clock
Code: Select all
#!/bin/sh
export box='
<vbox>
<progressbar visible="false">
<input>while [ A != B ]; do sleep 0.5; P=`cat /tmp/p`; echo $P; echo 100 > /tmp/p; done</input>
<action>refresh:ENTRY</action>
<action>echo 99 > /tmp/p</action>
</progressbar>
<entry>
<variable>ENTRY</variable>
<input>date +%H:%M:%S</input>
</entry>
</vbox>'
gtkdialog -p box
<timer milliseconds="true" interval="500" visible="false">
Gtkdialog misses a tailbox widget, but using this solution it is easy to build our own tailbox.
>> Both <timer> and <progressbar> widgets uses cpu-power, and in a complex gui this could be a real pitfall. Here follows an explanation of how it is solved in pMusic. It is a way to update a complex gui on non-user actions. User-actions is mouse-clicks and key-entries, and is the common way to interact between user and gui. An example of updating the gui on a non-user-action in pMusic is the loading of id3-tags for files shown in the file-browser....
When clicking on a directory, the browser field refreshes and shows the content of the new directory. Now, pMusic continues with a background process to read the id3 information of the files. When finished, the browser field should be updated by the new info. - Without the user to click anywhere.
The solution is to either use a <progressbar> which runs its <action> when reaches 100% or a <timer> which acts at a given interval. Older Pmusic releases used the <progressbar> solution while recent code uses a <timer>. Both solutions has the downside that they use an amount of cpu power. In a complex gui with several non-user actions this could end up with several cpu-hungry <timers> updating their unique widget(s) of the gui.
pMusic has used 2 <timers> running constantly to update their defined gui-part. You might think that 1 <timer> would be enough, - it could refresh all wanted widgets - it wouldn't harm if the refreshing didn't change anything. BUT, refreshing has an effect on the focus handling. It simply resets the focus, which means that:
- scrolling in <table> is moved to top.
- what you are about to write in an <entry> is cleared.
- Your selected item in the <tree> is not selected anymore
- ...
The idea of pMusic 2.4.0 was to update all kinds of trackinfo (meta-tags, lyrics, albumart, discography, ...) when a new track starts playing. This would not be possible with the underlaying event-handling in pMusic because it would either suck your cpu-power (with several <timers>), or a global refreshing would make it impossible to be a user (because of focus issues). Also, there is a fact that too many refreshing <action> on a <timer> will increase cpu-usage as well.
So the solution has been to add ONE <timer>, which refreshes a small set of <checkboxes> that (if true) refreshes a single or group of widgets.
<checkboxes> can be set true or false by their <input file>, so we can simply 'echo true' to its <input file>. The <timer> runs its <actions> each second, and the <checkbox> will be refreshed by its <input file>. The code could look like this:
Code: Select all
<checkbox visible="false">
<label>a</label>
<variable>UPDATE_GUI</variable>
<input>cat /path/UPDATE_GUI</input>
<action>if true refresh:PLAYLIST</action>
<action>if true refresh:ARTWORK</action>
<action>if true refresh:BUTTON_PLAY</action>
<action>if true echo false > /path/UPDATE_GUI</action>
</checkbox>
Example script here
###################################################################################
5.) The benefits of a config file
###################################################################################
A config file is a nice way to store the settings in your program. At next startup, it will show the settings like you left them last time.
Set default status of Radiobuttons, Comboboxes...
>> By default, gtkdialog always activates the first radiobutton and the first list-item for combobox.
An easy way to use a config file is to run it like an ordinary bash-script, and include variable definitions in it. It is most common to keep config files in home directory as a hidden file ($HOME/.testrc). The file might look like this:
Code: Select all
COMBOBOX="item 3"
ENTRY="default text"
RADIOBUTTON1="false"
RADIOBUTTON2="true"
Now let's go to the main script. For the radiobuttons and the entry, we set the <default> tag to the corresponding variable in the config file. Since the combobox doesn't support the <default> tag, we need a workaround. We simply build the combobox item-list so show our saved variable first.
Code: Select all
#!/bin/bash
#in case no testc file (first run), build the file
[ ! -s $HOME/.testrc ] && echo -e -n 'COMBOBOX="item 3"\nENTRY="default text"\nRADIOBUTTON1="false"\nRADIOBUTTON2="true"\n' > $HOME/.testrc
. $HOME/.testrc
#define combobox list items
COMBOBOX_ITEMS="<item>$COMBOBOX</item>" #stored value should be first in list
for I in 1 2 3 4; do COMBOBOX_ITEMS=`echo "$COMBOBOX_ITEMS<item>item $I</item>"`; done
export main="
<window title=\"The benefits of a config file\">
<vbox>
<frame The first item of list is the default choice in a Combobox>
<combobox>
<variable>COMBOBOX</variable>
$COMBOBOX_ITEMS
</combobox>
</frame>
<frame If nothing else is set, the first radiobutton is the active one>
<radiobutton>
<variable>RADIOBUTTON1</variable>
<label>Yes I am</label>
<default>$RADIOBUTTON1</default>
</radiobutton>
<radiobutton>
<variable>RADIOBUTTON2</variable>
<label>No I'm not</label>
<default>$RADIOBUTTON2</default>
</radiobutton>
</frame>
<frame Fetch entry-value from config file>
<entry>
<variable>ENTRY</variable>
<default>$ENTRY</default>
</entry>
</frame>
<hbox>
<button ok></button>
</hbox>
</vbox>
</window>"
gtkdialog -p main > $HOME/.testrc
Note that line 2 starts with a dot. It makes a huge difference if you skip it.
. $HOME/.config --> run script as a childprocess as the main process. The variable values in the config file are reachable for the main script.
$HOME/.config --> run script as a new process as the main process. The variable values in the config file will NOT be reachable for the main script.
How to store window size/placement
>> This example shows how we can save settings for window size and placement for next startup. Be also aware that this solution makes it possible to let user rescale gui smaller than the default size. Normally you define the size of, for example, a <tree>, and the user can only resize the gui larger, but when using <window default_height="$HEIGHT" default_width="$WIDTH">, you don't need to define <height> and <width> for the tree widget.
Code: Select all
#! /bin/bash
save_geometry (){
XWININFO=`xwininfo -stats -name SizeMe`
HEIGHT=`echo "$XWININFO" | grep 'Height:' | awk '{print $2}'`
WIDTH=`echo "$XWININFO" | grep 'Width:' | awk '{print $2}'`
X1=`echo "$XWININFO" | grep 'Absolute upper-left X' | awk '{print $4}'`
Y1=`echo "$XWININFO" | grep 'Absolute upper-left Y' | awk '{print $4}'`
X2=`echo "$XWININFO" | grep 'Relative upper-left X' | awk '{print $4}'`
Y2=`echo "$XWININFO" | grep 'Relative upper-left Y' | awk '{print $4}'`
X=$(($X1-$X2))
Y=$(($Y1-$Y2))
echo "export HEIGHT=$HEIGHT" > /tmp/geometry
echo "export WIDTH=$WIDTH" >> /tmp/geometry
echo "export X=$X" >> /tmp/geometry
echo "export Y=$Y" >> /tmp/geometry
chmod 700 /tmp/geometry
}
export -f save_geometry
[ -f /tmp/geometry ] && . /tmp/geometry
export DIALOG="
<window title=\"SizeMe\" default_height=\"$HEIGHT\" default_width=\"$WIDTH\">
<vbox>
<frame>
<text>
<label>If you resize or move this window, it will be remembered for next time.</label>
</text>
</frame>
<hbox>
<button ok>
</button>
</hbox>
</vbox>
<action signal=\"hide\">save_geometry</action>
</window>"
gtkdialog --program=DIALOG --geometry +"$X"+"$Y"
>> Another approach for size/placement is to make a call of your app from the cli.
Code: Select all
RIGHT=14 DOWN=36 WIDTH=80 HEIGHT=80 # define variables with defaults
GUI='
<vbox>
<button cancel></button>
</vbox>'
gtkdialog -p GUI -G ${1-${WIDTH}x${HEIGHT}+${RIGHT}+${DOWN}}
/tmp/gui 75x75 or
/tmp/gui +0+0 or
/tmp/gui for the default geometry
###################################################################################
6.) Speed issues - Let your turtle run
###################################################################################
Gtkdialog is not a fast gui-lib. If you are building a gui where speed matters, please check the following notes.
>> For an unknown reason, it is much faster to start your gtkdialog-xml-code from a file than from a variable.
gtkdialog --file=filename
gtkdialog --program=variable
The file alternative is highly recommended if your xml-code is getting complex.
>> Progressbars can suck your cpu-power. Adding a reasonable sleep value in the loop helps a lot.
>> Run large calculations as a background process. Pmusic builtin filebrowser (version 0.9) first shows songs in the directory, then it starts a background process to find meta information of the songs. When finished, it renders the song-list once again, now with complete information. How to use a background process with gtkdialog is explained in the chapter 'Let external code act on your gtkdialog gui'.
>> Even if gtkdialog is slow, your bash-code might make it MUCH slower. Be careful with the use of subshells, and slow commands such as sed, ps, expr, cat ... This is of course most important when building a loop.
The comparison of code shows how to do the same operation in 2 different ways. The speed differs a lot. First an example why you should avoid 'cat', then why never use 'expr' if speed matters. My system needed 4 seconds to calculate 1+1 thousand times with 'expr'. Using the builtin bash command it took 'no time'.
Code: Select all
# time for I in $(seq 1 1000); do B="`cat /tmp/tmp`"; done
real 0m3.095s
# time `IFS=$'\n'; for I in $(seq 1 1000); do B=($(<"/tmp/tmp")); done`
real 0m0.927s
# time for I in $(seq 1 1000); do expr 1 + 1 > /dev/null; done
real 0m4.286s
# time for I in $(seq 1 1000); do A=$((1+1)); done
real 0m0.032s
. . . . . . .
###################################################################################
7.) Extended use of widgets - Undocumented features
###################################################################################
Generals
>> Add tooltips.
<hbox tooltip-text=" This is the text ">...</hbox>
>> Add tooltips with advanced text layout.
<button tooltip-markup="<span background='yellow'><b><i> press alt m </i><b><span>">......</button>
>> Widgets might be invisible.
<progressbar visible="false">...</progressbar>
>> Define how much space the widget should take.
<text height-request="50" width-request="200">...</text>
>> Avoid tab-key to give focus
<edit can-focus="no">...</edit>
>> Align widget. xalign/yalign may hold 0 (left), 1 (right) and 2 (center). xalign/yalign works for most widgets. For button/progressbar they describe placement of label rather than widget itself.
<text xalign="0">...</text>
<window>
>> prevent user from resizing the gui.
<window allow-grow="false">
>> Shrink window smaller than the widgets requires to show up.
<window allow-shrink="true">
<text>
>> Text knowledge has been moved to its own post.
Please look at Text managing
>> Justify aligment to right (1), center (2), left (3).
<text justify="2">
>> Angle the text
<text angle="45">
>> Force one-line text
<text wrap="false">
<text single-line-mode="true">
>> Set underline pattern
<text label="Underline me with an useless pattern" pattern="__ __ __ __ __ __ _"></text>
<button>
>> You can skip the button decoration.
<button relief="2">...</button>
>> The image doesn't need to be left of label. It can be left/right/above/below. Values 0-3. Be aware that this seems to work only for icons in the gtk-stock.
<button use-stock="true" image-position="3">...</button>
>> Use gtk-stock values
<button use-stock="true" label="gtk-quit"></button>
>> Hotkey is set in 2 ways
<button use-stock="true" label="gtk-cancel">...</button>
<button use-underline="true"><label>_Cancel</label>...</button>
>> Scale icon
<button height-request="50" width-request="80"><input file>file.jpg</input><height>40</height><width>70</width></button>
<entry>
>> Give entry focus
<entry text="Focus is here" is-focus="true"></entry>
>> Not editable
<entry editable="false"></entry>
>> Password - show input text as ******, and a note if capslock is turned on
<entry visibility="false" caps-lock-warning="true"></entry>
>> Alternative way to define default text
<entry text="default string">
>> Show progress in entry - or simply change the color of the entry background
<entry progress-fraction="0.5">
>> Icons in <entry>
<entry primary-icon-activatable="true" primary-icon-stock="gtk-refresh" secondary-icon-activatable="true" secondary-icon-stock="gtk-clear"
>> Restrict input to a single line. Usually achieved by pasting, hardly possible by keyboard input. This also gets rid of special chars like a line-feed character
<entry truncate-multiline="true"
<notebook>
>> You can define which tab should be active by default. "0" represent tab1, "1" represent tab2 ...
<notebook page="1">...</notebook>
>> Tabs can be shown at Top/Bottom/Left/Right. Values from 0-3
<notebook tab-pos="3">...</notebook>
>> Tabs can chosen in right-click menu
<notebook enable-popup="true">...</notebook>
<edit>
>> insert margins
<edit left-margin="20">...</edit>
>> avoid user input
<edit editable="no">...</edit>
<pixmap>
>> Scale image
<pixmap><input file>file.jpg</input><height>40</height><width>70</width></pixmap>
<progressbar>
>> Avoid window to autoscale because of too much text in progressbar. You can truncate text at left (1), at right (3) or in the middle (2) by the ellipsize tag.
<progressbar text="Truncated the text in the middle" ellipsize="2"></progressbar>
>> Define the text alignment by text-xalign and text-yalign tag. try out values from 0-3 for left, right and center.
<progressbar text-xalign="1" text-yalign="1">
>> Do not show text
<progressbar show-text="false">
>> Alternative bar outfit. - view blocks
<progressbar bar-style="1">
>> Do not truncate result to block-size.
<progressbar discrete-blocks="25">
>> Set orientation
0 - Horizontal - left to right.
1 - Horizontal - right to left.
2 - Vertical - bottom to top.
3 - Vertical - top to bottom.
<progressbar orientation="3">
<chooser>
>> Type of chooser-widget
0 - Open file
1 - Save file
2 - Select folder
3 - Create folder
<chooser action="1">
>> Hide/Show 'create folders' button
<chooser create-folders="false">
<tree>
>> manual sort the list
<tree reorderable="true">...</tree> - See the Drag'n drop workaround to succeed
###################################################################################
8.) Advanced Syntax - make your code readable
###################################################################################
Comments in your code
Gtkdialog doesn't support comments in its xml code. But if we convert all comments before execution, it works. The reason why I use double hash (##) for my comments, is because colors in a <span> tag might be defined as 'red' or '#FF0000'. The latter would be removed by using a single hash.
Code: Select all
#!/bin/sh
XML='
<vbox> ##this is how to comment your xml code.
<text height-request="50"> ##force the text to take more space.
<label>Hello</label>
</text>
<hbox> ##hbox to align button to the right.
<button ok></button> ##button ok is fetched from the gtk stock.
</hbox>
</vbox>'
export GUI="`echo "$XML" | sed -e 's/##.*//'`"
gtkdialog -p GUI
Include code if.......
It is possible to build the gtkdialog out of many small pieces. Every piece is a variable and are put together when exporting the gtkdialog code. It is easy to think that a button should have different label depending on ........ Or like in the following example, - include a button if file/app/.. exists. Instead of defining all these pieces before the actual gtkdialog code, it is much more human readable if you define all 'inside' the gtkdialog code.
Code: Select all
script='
<vbox>
<hbox>
<text>
<label>Yes button added if file /root/testfile exists</label>
</text>'
[ -f /root/testfile ];then script=${script}'<button yes></button>'
script=${script}'
<button no>
</button>
</hbox>
</vbox>'
export SCRIPT="$script"
gtkdialog --program=SCRIPT
###################################################################################
9.) Tips and tricks
###################################################################################
How to change the gui without restarting the app
>> Recent Gtkdialog allows to hide/show widgets, To hide many widgets at once, you can hide a <hbox> or <vbox>. This will hide all widgets inside the box.
>>Another approach is to use the notebook attributes <notebook show-tabs="false" show-border="false">. Using several tabs in a notebook with hidden header, we can simulate a gui re-render. The only thing that is happening is that we switch tab, but since the user can't see those, he will be fooled...
More info here.
Control the icon/label of menuitems
>> If you have met problems to show a certain icon and at the same time give it an unique label, this will illustrate how it is recommended to be implemented for the benefit of anyone and everyone.
Code: Select all
<menuitem stock="gtk-undo" label="'$LOC118'" accel-key="0x07a" accel-mods="4">
<action>. '$APPDIR'/func -undo</action>
<action>refresh:PLAYLIST</action>
</menuitem>'
Understand the alignment
>> Gtkdialog tends to align objects to the right side of the window. Exceptions are radiobuttons and checkboxes which are aligned to the left. It is easy to center whatever object in your gui. Here's some alignment explanation:
- <hbox><button>...</button></hbox> will place button to the right.
- <button>...</button> and button will fill whole line.
- <hbox homogeneous=true><button>...</button></hbox> will place button in horizontal center.
- <hbox homogeneous=true><vbox homogeneous=true><button>...</button></vbox></hbox> will place button in center both vertical and horizontal.
- <hbox><text xalign="0">...</text></hbox> will place text at left. xalign/yalign may hold 0 (left), 1 (right) and 2 (center). xalign/yalign works for other widgets as well. For buttons they describe placement of label rather than button itself.
>> Using vertical rows can be troublesome, because often, the label defines the width of the objects. This leads to different width and it all looks crappy. You can override this by the attribute <vbox width-request="x">....</vbox>, where x represent the number of pixels for the width of the row. There is also a 'height-request' attribute.
>> Insert empty gap
Static:
Code: Select all
<text width-chars="40" height-request="120"><label>""</label></text>
Code: Select all
<hbox space-expand="false" space-fill="false">
<button yes></button>
<hbox space-expand="true" space-fill="true"><text><label>""</label></text></hbox>
<button no></button>
</hbox>
Window header icon
>> You can add your own icon (symlinks also work) if you place it in a gtk-theme directory structure as /usr/share/icons/hicolor/. ie. /usr/share/icons/hicolor/48x48/apps/games48.png
Now, run this command in the terminal to refresh the icon cache;
Code: Select all
gtk-update-icon-cache -f -i /usr/share/icons/hicolor
Code: Select all
<window title="A GTKDialog" icon-name="games48" resizable="false" decorated="true">
Refresh image
>> Gtkdialog can refresh an image in a <pixmap> widget without restarting the gui. Since the <input file> is static, we must change its content (by copy another file), and then refresh.
Code: Select all
#!/bin/bash
export MAIN='
<window>
<pixmap>
<variable>IMAGE</variable>
<input file>/root/image1.png</input>
</pixmap>
<button>
<label>Change image</label>
<action>cp -f /root/image2.png /root/image1.png</action>
<action type="refresh">IMAGE</action>
</button>
</window>'
gtkdialog --center --program=MAIN
Drag'n drop
Gtkdialog has limited support for drag'n drop. This is what we've learnt by now:
>> Drag from file browser (rox) to <entry> widget.
Code: Select all
export test="
<entry>
<default>Drag a directory from Rox</default>
</entry>"
gtkdialog -p test
>> Move items in list by drag'n drop. (More info in this thread)
Using the reorderable option in the <tree> has some strange behavior. For example will items get swallowed when moving them onto another item. The workaround restores the list if the user moves item onto, instead of in between 2 other items... This code by MochiMoppel is a result of 2 cracked nuts:
- When moving an item in the list the $PTR_Y variable returns 0. A return value >0 gives us the info that the user is clicking rather than moving.
- Normally all <action>s defined for a widget are processed before built-in actions. The trick is to process a user defined <action> after a built-in action. An invisible button allows the code to run a user defined save action after a built-in reorder action."
Code: Select all
#!/bin/bash
#Code below line 15 (break:) is activated when user move item in list
ls -1 /usr/share/backgrounds > /tmp/test
do_something () { Xdialog -info "Do something with\n$TREE" x 2000 ;} ; export -f do_something
echo '
<vbox>
<tree headers-clickable="false" reorderable="true">
<label>Backgrounds</label>
<input>cat /tmp/test</input>
<output file>/tmp/test</output>
<variable>TREE</variable>
<height>300</height><width>200</width>
<action>do_something &</action>
<action signal="button-release-event" condition="command_is_true( echo $PTR_Y )">break:</action>
<action signal="button-release-event">save:TREE</action>
<action signal="button-release-event">activate:BTN_SAVE</action>
</tree>
<button visible="false">
<variable>BTN_SAVE</variable>
<action>cp /tmp/test /tmp/testbackup</action>
<action>save:TREE</action>
<action condition="command_is_true([[ $(wc </tmp/test) != $(wc </tmp/testbackup) ]] && sed \"s/^|*//\" /tmp/testbackup > /tmp/test && echo true )">refresh:TREE</action>
</button>
</vbox>' | gtkdialog -s
Code: Select all
#!/bin/sh
move (){
PRESS_EVENT=$(cat /tmp/PRESS_EVENT)
[[ $PRESS_EVENT && $TREE ]] || exit #exit if at least one of the 2 values is empty
[[ $PRESS_EVENT == $TREE ]] && exit #exit if both are equal (=single or double click)
sed -i "/$PRESS_EVENT/d; /$TREE/ i\\$PRESS_EVENT" /tmp/list #remove PRESS_EVENT, then insert item PRESS_EVENT before item $TREE
}
export -f move
ls -1 /usr/share/backgrounds > /tmp/list
export test="
<tree rules_hint=\"true\" hover-selection=\"true\" tooltip-text=\"Drag'n drop items to move them in list\">
<label>Backgrounds</label>
<input>cat /tmp/list</input>
<variable>TREE</variable>
<height>300</height><width>200</width>
<action signal=\"button-press-event\">echo \$TREE > /tmp/PRESS_EVENT</action>
<action signal=\"button-release-event\">move</action>
<action signal=\"button-release-event\">refresh:TREE</action>
</tree>"
gtkdialog -p test
Hotkeys
>> An <action> signal inside the <window> widget gives the most direct hotkey
<action signal="key-press-event" condition="command_is_true( [ $(( ${KEY_MOD} & 5 )) = 5 ] && [ $KEY_RAW = 0x1b ] && echo true )">Refresh:varDATE</action>
SHIFT's state is held by by 0th bit and CTRL state by 2nd bit of $KEY_MOD variable (?????C?S) , so, in order to read their state correctly, we need to discard the rest of bits by logical AND: $(( $KEY_MOD & 5 )). If we would want SHIFT alone, it would be $(( $KEY_MOD & 1)) = 1, only CTRL - $(( $KEY_MOD & 4 )) = 4 and so on.
see this forum thread for more depth
>> Menuitems can set both accelerator key and modifier (SHIFT is 1, CTRL is 4 and ALT is 8 ). For a full list of accelerator key values check this
<menuitem stock-id="gtk-quit" accel-key="0x51" accel-mods="4">
>> You can activate buttons by pressing keyboard combinations. simply add the attribute 'use-underline'. If set, an underline in the text indicates the next character should be used for the mnemonic accelerator key.
<button use-underline="true"><label>_Refresh</label></button>
The defined char in the <label> + 'alt' key will activate given <action>.
Set a fixed size of a progressbar
>> Progressbars are by default rather big. But it's not that easy to define the width of a progressbar. To do this you have to combine some attributes.
Code: Select all
<hbox>
<vbox>
<progressbar width-request="100" height-request="20" Expand="no" Fill="no">
<input>echo 70; echo small bar</input>
</progressbar>
</vbox>
</hbox>
Define unique gtk-theme
>> Sometimes it is nice to change the gtktheme just for my app. Like this I get more options for my gtkdialog script.
Gtkdialog does not support monospace in the <edit> widget, but I can make it work by adding a unique gtk-theme only for my app. The best way to this is to add my new gtk-theme on top of the already existing. Now my gtk-theme can hold ONLY information about monospace, else it will use gtk-settings specified by user.
By giving the text-widget a name (see example), it is possible to have a gtk-theme for only this unique text string. This also works for text in widgets like <entry>.
Code: Select all
#!/bin/bash
echo 'style "specialmono"
{
font_name="Mono 12"
}
widget "*mono" style "specialmono"
class "GtkText*" style "specialmono"' > /tmp/gtkrc_mono
export GTK2_RC_FILES=/tmp/gtkrc_mono:/root/.gtkrc-2.0
export test_app="
<vbox>
<text name=\"mono\"><label>This text-widget uses monospace font....</label></text>
<text><label>...while this text-widget don't.</label></text>
<edit><default>All edit-widgets gets monospace.</default></edit>
</vbox>"
gtkdialog --program=test_app
Override window manager - skip taskbar, sticky, layer, border
>> Window on top
Code: Select all
echo -n '<window type-hint="6" window-position="1">
<vbox>
<edit justification="2" cursor-visible="false">
<default>"
This window stays on top
Has no window decoration
Can not be resized
Can not be moved
"</default>
</edit>
<button></button>
</vbox>
</window>'|gtkdialog -s
This is how to override default settings in your JWM configuration.
Your gtkdialog gui must be called with the --class parameter
Code: Select all
gtkdialog --class=APPLET -f myfile
Code: Select all
<Group>
<Class>APPLET</Class>
<Option>nolist</Option>
<Option>sticky</Option>
<Option>noborder</Option>
<Option>layer:above</Option>
- 'sticky' shows your app on all desktops.
- 'noborder' is equal to the gtkdialog attribute <window decorated="false">.
- 'layer:n' defines if other windows should cover your app, or if it should be on top. below, normal or above.
Let entry accept (some) numbers only
>> The following example shows how to limit the user input to a <entry>. Useful for 'all' kinds of settings.
Code: Select all
#!/bin/bash
export MAIN_DIALOG='
<vbox>
<text>
<label>"Enter a number (text will default to 0)"</label>
</text>
<entry editable="true" allow-empty="false">
<input>case $ENTRY in ""|*[^0-9]*) echo 0;; *) echo $ENTRY ;; esac</input>
<variable>ENTRY</variable>
<action signal="focus-out-event">refresh:ENTRY</action>
</entry>
<text>
<label>"Enter a number within [5,15], otherwise:
input will be capped at 15,
floored at 5,
or set to 10 in case of text input"</label>
</text>
<entry editable="true" allow-empty="false">
<input>A=$ENTRY2; case $A in [0-9]*) [[ $A -gt 15 ]] && echo 15 || [[ $A -lt 5 ]] && echo 5 || echo $A ;; *) echo 10;; esac</input>
<variable>ENTRY2</variable>
<action signal="focus-out-event">refresh:ENTRY2</action>
</entry>
<button ok></button>
</vbox>'
gtkdialog -p MAIN_DIALOG
unset MAIN_DIALOG
MIME
>> What should happen when the user double-clicks on an image-file in your gui. In some cases it is logical to show the image in an image-viewer.... but it is no need of linking all kinds of extensions to different programs. - It is already done by the filebrowser. Puppy uses 'rox' and if you execute 'rox /path/image.png' the image will show up in the defined viewer. Just as simple as that.
Checkbox with image
>> This example shows how to add a image beside the label of a checkbox. Be aware that this tecnique only works for gtk stock-items.
Code: Select all
<checkbox use-stock="true" label="gtk-fullscreen" image-position="1"
tooltip-text="checkbox derives from button ...">
</checkbox>
<button use-stock="true" label="gtk-quit">
</button>
Use a splash screen
>> A splash screen could be useful if the startup time is increasing. The delay may occur for 2 reasons. There are several startup processes, or the gtkdialog code is heavy. The latter case makes it necessary to close the splash-window first when the main window is drawn on screen. We use the <window> signal 'show' to determine when the splash should be closed.
Code: Select all
#!/bin/sh
#Splash
echo 0 > /tmp/splash
export SPLASH='
<window title="PCC" icon-name="gtk-preferences" resizable="false" decorated="false">'"
<vbox>
<pixmap><input file>/usr/share/midi-icons/configuration48.png</input></pixmap>
<text height-request=\"50\" use-markup=\"true\"><label>\"<b><span size='"'x-large'"' color='"'orange'"'>Puppy Control Center</span></b>\"</label></text>"'
<progressbar visible="false">
<label>nn</label>
<input>while [ "$M" != "100" ]; do M=`cat /tmp/splash`; echo $M; usleep 500000; done</input>
<action type="exit">Ready</action>
</progressbar>
</vbox></window>'
gtkdialog -p SPLASH --center &
sleep 2 #well, this is to simulate some startup processes....
export GUI='
<window title="PCC" icon-name="gtk-preferences" width-request="800">
<vbox>
<text height-request="200">
<label>Hello, welcome to the Puppy Control Center</label>
</text>
<hbox>
<button ok></button>
</hbox>
</vbox>
<action signal="show">echo 100 > /tmp/splash</action>
</window>'
gtkdialog -p GUI
Press 'Enter' instead of clicking the 'Ok' button
>> When writing input to the app it is much easier to just press the 'Enter' key to activate <action>.
Code: Select all
#!/bin/sh
export ok_box='
<window>
<vbox>
<entry activates_default="true"><default>press _enter_ to activate OK-