Apr 09

In this article I explore the gtkdialog Window object, its attributes and actions. In passing. other gtkdialog objects will be used without much elaboration, that elaboration intended to follow in subsequent articles.

This is the next article in a series of articles on gtkdialog, “gtkdialog Exploration – articles and examples”, which can be found at https://blogs.czapski.id.au/2017/04/gtkdialog-exploration.

It appears that gtkdialog, based as it seems to be on the GTK object model, leverages the GTK object hierarchy. Consider the reference for the Window object at http://01micko.com/reference/window.html.

The very first link points to a GtkWindow – http://developer.gnome.org/gtk2/2.24/GtkWindow.html.

In the GTK object hierarchy, https://developer.gnome.org/gtk2/2.24/GtkWindow.html#GtkWindow.object-hierarchy, the Window object is several levels below the GObject, the topmost object.

As briefly discussed in the previous article the GtkWindow, and consequently the gtkdialog Window, object inherit, as I understand it, from the objects higher up in the hierarchy. This will come into play when we explore attributes that can be set for a Window, and actions that can be configured for a Window.

Pre-Requisites

This article assumes that the Virtual Box Machine Image created in accordance with the instructions in the blog article to be found at https://blogs.czapski.id.au/2016/10/configure-virtual-box-virtual-machine-and-install-centos-6-8-base-image is available but it is expected that pretty much any Linux environment will do just as well so long as it supports yad and gtkdialog. For convenience I posted the export of the VirtualBox image which would have been built if the reader followed all the articles in the series “Build a Linux-based Infrastructure Solution Demonstration Series” to date, that is to the 8th of March 2017. The link to the main article is https://blogs.czapski.id.au/2017/04/virtualbox-image-with-content-as-built-so-far.

It also assumes that yad and gtkdialog are installed, as discussed in the article “Install yad and gtkdialog” (https://blogs.czapski.id.au/2017/04/gtkdialog-for-rapid-prototyping-of-linux-applications-install-gtkialog-and-yad)

Window Object Attributes

The gtkdialog Window object is the outermost visible object in the hierarchy of gtkdialog objects.

We explored the window object to an extent in the previous article, while setting the context and discussing comments and similar matters. Let’s now explore the Window object attributes more.

Reference documentation for the gtkdialog Window object, http://01micko.com/reference/window.html, provides a table of attributes specific to this gtkdialog object and makes a reference to ancestor class properties.

Attributes that can be configured for the window widget are defined in the widget’s reference page see https://developer.gnome.org/gtk2/2.24/GtkWindow.html#GtkWindow.object-hierarchy, section Properties – https://developer.gnome.org/gtk2/2.24/GtkWindow.html#GtkWindow.properties

Some of them are useful and some of them are not, as far as I am concerned or can empirically determine. Changing values of some of them do not have visible consequences.

Not all attributes/properties are recognised and acted upon by  gtkdialog. Some of the properties are inherited from the parent objects, like GtkContainer and GtkWidget. For example, border-width, which defines the amount of space between the window frame and the window content, is inherited from the GtkContainer.

Similarly, sensitive property is inherited from the GtkWidget and when set to true effectively disables everything inside the window, except for the window decorations like minimise, maximise and close. An “interesting” side effect of this is that setting sensitive to false and decorated to false will take away the means of closing the window except by killing the process which created the window 🙂

Properties like window-position take one of the pre-defined enumeration values.

For window-position see https://developer.gnome.org/gtk2/2.24/gtk2-Standard-Enumerations.html#GtkWindowPosition or http://gtk.php.net/manual/en/html/gtk/gtk.enum.windowposition.html (which is not directly related to gtkdialog as I use it but seems a good enough source of enumerations which GTK defines and supports.

By the way, tooltip-markup causes warnings to be emitted to the console, which, apart from being ugly, appear benign.

See the reference page: file:///home/demo/gtkdialog-0.8.3/doc/reference/window.html

To work out what attributes are actually supported, in this case by the window widget, we need to do some exploration. Discussion below goes into this to an extent and I provided most of the useful attributes that can be set, both from GtkWindow itself and inherited from GtkContainer and GtkWidget.

Let me reproduce the table and expand it to include inherited attributes which empirical observation leads me to believe have a visual impact on the window object.

The table below summarised Window attributes/properties and indicates which work with the gtkdialog and which do not as far as I can tell. “Do not” may well be a function of me not seeing any visible difference with the property set to different values. Some ancestor attributes/properties which are not really useable from gtkdialog are omitted.

Name Description Value Default From Since OK?
image-name Image filename

I could not figure out what this does for a Window object.

gtkdialog 0.8.1
block-function-signals Block signal emissions from functions

I am not quite sure what this does for a Window object. See reference for where this comes from. Perhaps you will get it. http://www.murga-linux.com/puppy/viewtopic.php?p=544704#544704

true or false false gtkdialog 0.7.21
file-monitor Emit signal when input file(s) change

If a file is specified as the value of the window’s <input file>xxxx</input> directive, and this property is set to true, and the content of the file changes then the signal of type “file-changed” will be emitted. If an <action signal=”file-changed”>xxx</action> or its variant is defined then the action will be executed.

true or false false gtkdialog 0.8.1 Y
auto-refresh Auto refresh when input file(s) change.

If a file is specified as the value of the window’s <input file>xxxx</input> directive, and this property is set to true, and the content of the file changes then the window’s title bar text will automatically display the content of the file (or some reasonable leading part of it). If this property is false then the input file change will not be reflected in the title bar.

true or false false gtkdialog 0.8.1 Y
 
accept-focus Whether the window should receive the input focus. The title bar is greyed out on false but objects inside the window are still active, for example, buttons are clickable on and do their thing, but keystrokes are not delivered to the window. true or false true GtkWindow Y
allow-grow If TRUE, users can expand the window beyond its minimum size. true or false true GtkWindow Y
allow-shrink If TRUE, the window has no minimum size. Setting this to TRUE seems pretty useless and perhaps unfriendly. true or false true GtkWindow Y
decorated Whether the window should be decorated by the window manager, that is whether it should have the title bar and the various buttons typically present there. true or false true GtkWindow Y
default-height The default height of the window, used when initially showing the window. Must be >= -1.

The value of this attribute overrides the height-request value if one is specified.

integer -1 GtkWindow Y
default-width The default width of the window, used when initially showing the window. Must be >= -1.

The value of this attribute overrides the width-request value if one is specified.

integer -1 GtkWindow Y
deletable Whether the window frame should have a close button. true or false true GtkWindow Y
icon-name The icon-name property specifies the name of the themed icon to use as the window icon. File path GtkWindow Y
mnemonics-visible Whether mnemonics are currently visible in this window. If buttons are defined and have “use-underline” attribute set to true and a mnemonic indicated with the “_” before the character in the label, or are “standard” buttons like OK or cancel, the indicated or default letter in the label will be underlined when the window is drawn if this property is set to true. true or false false GtkWindow Y
resizable If TRUE, users can resize the window. true or false true GtkWindow Y
skip-pager-hint TRUE if the window should not be shown in the pager. true or false false GtkWindow ?
skip-taskbar-hint TRUE if the window should not be shown in the task bar true or false false GtkWindow Y
title The title of the window. Overwritten by the <label>…</label> directive if one is specified string GtkWindow Y
type-hint These are hints for the window manager that indicate what type of function the window has. The window manager can use this when determining decoration and behaviour of the window. See enumerations – link below. Does not seem useful in the gtkdialog context. integer 0 GtkWindow Y
window-position The initial position of the window

GTK_WIN_POS_NONE=0

GTK_WIN_POS_CENTER=1

GTK_WIN_POS_MOUSE=2

Integer [0..2] 0 GtkWindow Y
 
border-width The amount of whitespace between the window frame and the content of the window Integer 5 GtkContainer Y
 
can-default Whether the widget can be the default widget.

This does not seem to have any effect when applied to a Window object

true or false false GtkWidget ?
can-focus Whether the widget can accept the input focus.

This does not seem to have any effect when applied to a Window object

true or false false GtkWidget ?
has-default Whether the widget is the default widget.

This does not seem to have any effect when applied to a Window object

true or false false GtkWidget ?
has-focus Whether the widget has the input focus.

This does not seem to have any effect when applied to a Window object

true or false false GtkWidget ?
has-tooltip Whether this widget has a tooltip.
Disables tooltip display if tooltip is defined for the Window
true or false false GtkWidget Y
height-request Override for height request of the widget, or -1 if natural height should be used.

Has not effect of default-height is specified.

>=0 -1 GtkWidget Y
is-focus Whether the widget is the focus widget within the toplevel.

Does not seem to have any effect when applied to the Window object

true or false false GtkWidget ?
name The name of the widget.

Nothing visible happens when this attribute is provided with a value.

string NULL GtkWidget ?
receives-default If TRUE, the widget will receive the default action when it is focused.

I don’t know what that is supposed to accomplish. Nothing visible happens regardless of the property value.

true or false false GtkWidget ?
sensitive Whether the widget responds to input.

FALSE disables all components contained in the window.

true or false TRUE GtkWidget Y
style GtkStyle GtkWidget ???
tooltip-markup The contents of the tooltip for this widget.

Simple markup can be used to call out parts of tooltip text in.

See http://www.murga-linux.com/puppy/viewtopic.php?t=40418 for a markup explorer tool.

Ineffective if tooltip-disabled is TRUE.

string NULL GtkWidget Y
tooltip-text The contents of the tooltip for this widget.

Ineffective if tooltip-disabled is TRUE.

string NULL GtkWidget Y
visible Whether the widget is visible.

It is somewhat self-defeating to make a window invisible on creation unless it is a “subsidiary” window and can be made visible form some other piece of logic.

true or false true GtkWidget Y
width-request Override for width request of the widget, or -1 if natural width should be used.

Has not effect if default-width is specified.

>=0 -1 GtkWidget Y

The following example includes just about every attribute that can be defined for a Window object. Experiment with the various settings to see what you can see.

scriptName=ex09
touch /tmp/${scriptName}.sh
chmod ug+x /tmp/${scriptName}.sh

cat <<-'EOSCRIPT' > /tmp/${scriptName}.sh
#!/bin/bash

GTKDIALOG=gtkdialog

fnEchoDateTime() {
echo "$(date)"
}
export -f fnEchoDateTime

GTK_WIN_POS_NONE=0
GTK_WIN_POS_CENTER=1
GTK_WIN_POS_MOUSE=2


MAIN_DIALOG_FILE=${0}.gtkd
cat <<-EOUIDFEINITION > ${MAIN_DIALOG_FILE}
<window 
    decorated="true" this="shows the title bar and its nprmal decorations"
    allow-grow="true" this="can resize to be bigger"
    allow-shrink="true" this="can resize to be smaller - including making it practically invisible"
    xx-default-height="200" remove="xx- to enable the attribute and see how that affects height-request"
    xx-default-width="400" remove="xx- to enable the attribute and see how that affects width-request"
    resizable="true" xx-resizable="overrides default-height and default-width if set to false"  this="works"
    deletable="true" xx-deletable="this keep (default) or remove the close button" this="works"
    icon-name="gtk-apply" this="works - shows designated icon at the left of the title bar"
    modal="false" this="does not seem to be doing anything different on true and on false"
    skip-pager-hint="true" this="does not seem to be doing anything different on tru and on false"
    skip-taskbar-hint="false" this="seems to be preventing the window being listed in the window selector when true"
    title="My First Window" this="is selfexplanatory and works"
    window-position="${GTK_WIN_POS_CENTER}"
    border-width="5" xx-border-width="is an inherited GtkContainer property - see object hierarchy at https://developer.gnome.org/gtk2/2.24/GtkWindow.html#GtkWindow.object-hierarchy"
    sensitive="true" xx-sensitive="is an inherited GtkWidget property that disables everything inside the window - see obect hierarchy"
    tooltip-markup="<span size='small'>This is a <b>window tooltip</b></span>"
    icon="gimp" this="does not do anything visible"
    mnemonics-visible="false" this="if buttons have underline for menemonics or are standard buttons mnemonics are initially underlined"
    accept-focus="true" this="if false makes window not focusaable (title bar is gray) - buttons are still clickable"
    window-position="${GTK_WIN_POS_CENTER}"
    auto-refresh="false" goes="with <input file>somefile</input>"
    border-width="5"
    can-default="false"
    can-focus="false"
    can-default="false"
    has-focus="false"
    has-tooltip="true"
    height-request="150" does="not have any effect if default-height is specified"
    width-request="400" does="not have any effect if default-width is specified"
    is-focus="true"
    name="myWindow"
    receives-focus="false"
    sensitive="true"
    xx-tooltip-text="This is a <b>window tooltip</b>"
    visible="true"
>
    <vbox>
        <frame   Description  >
            <text label="This is an example window."></text>
        </frame>
        <hbox>
            <button use-underline="true" tooltip-text="Refresh window variable - update date in window title">
                <label> _Refresh </label>
                <input file icon="gtk-refresh"></input>
                <width>16</width>
                <action function="refresh">vWindow</action>
            </button>
            <button ok></button>
            <button cancel></button>
        </hbox>
    </vbox>

    <label>Window Label</label>
    <variable>vWindow</variable>
    <input>fnEchoDateTime</input>
$(:<<'COMMENT'
    <input file>/tmp/aa.txt</input>
COMMENT
)
    
    <action this-is-window="escape" signal="key-press-event" condition="command_is_true( [[ \$KEY_RAW = 0x9 ]] && echo true )">EXIT:exit</action> 

</window>
EOUIDFEINITION

case ${1} in
    -d | --dump) cat ${MAIN_DIALOG_FILE} ;;
    *) gtkdialog --center --file=${MAIN_DIALOG_FILE} ;;
esac

rm -f ${MAIN_DIALOG_FILE}
EOSCRIPT

/tmp/${scriptName}.sh

See https://github.com/GNOME/gtk/blob/master/gdk/gdktypes.h for enumerations.

Window Object Directives

Note that the window defines a bunch of window specific child objects, which the reference calls directives – label, variable, input, etc..  These must be specified after all the other window child objects else gtkdialog will throw a hissy fit.

The label directive provides window title if title attribute is not set. If it is set the label directive is ignored. Consider:

scriptName=ex10
touch /tmp/${scriptName}.sh
chmod ug+x /tmp/${scriptName}.sh

cat <<-'EOSCRIPT' > /tmp/${scriptName}.sh
#!/bin/bash

GTKDIALOG=gtkdialog

fnEchoDateTime() {
echo "$(date)"
}
export -f fnEchoDateTime

GTK_WIN_POS_NONE=0
GTK_WIN_POS_CENTER=1
GTK_WIN_POS_MOUSE=2

MAIN_DIALOG_FILE=${0}.gtkd
cat <<-EOUIDFEINITION > ${MAIN_DIALOG_FILE}
<window 
    title="My First Window"
    window-position="${GTK_WIN_POS_CENTER}"
    default-width="400"
>
    <vbox>
        <frame   Description  >
            <text label="This is an example window."></text>
        </frame>
        <hbox>
            <button use-underline="true" tooltip-text="Refresh window variable - update date in window title">
                <label> _Refresh </label>
                <input file icon="gtk-refresh"></input>
                <width>16</width>
                <action function="refresh">vWindow</action>
            </button>
            <button ok></button>
            <button cancel></button>
        </hbox>
    </vbox>

    <label>Window Label</label>
    <variable>vWindow</variable>
$(:<<'COMMENT'
    <input>fnEchoDateTime</input>
    <input file>/tmp/aa.txt</input>
COMMENT
)
    <action this-is-window="escape" signal="key-press-event" condition="command_is_true( [[ \$KEY_RAW = 0x9 ]] && echo true )">EXIT:exit</action> 

</window>
EOUIDFEINITION

case ${1} in
    -d | --dump) cat ${MAIN_DIALOG_FILE} ;;
    *) gtkdialog --center --file=${MAIN_DIALOG_FILE} ;;
esac

rm -f ${MAIN_DIALOG_FILE}
EOSCRIPT

/tmp/${scriptName}.sh

Consider a variant where the title attribute to the Window object is not provided.

scriptName=ex11
touch /tmp/${scriptName}.sh
chmod ug+x /tmp/${scriptName}.sh

cat <<-'EOSCRIPT' > /tmp/${scriptName}.sh
#!/bin/bash

GTKDIALOG=gtkdialog

fnEchoDateTime() {
echo "$(date)"
}
export -f fnEchoDateTime

GTK_WIN_POS_NONE=0
GTK_WIN_POS_CENTER=1
GTK_WIN_POS_MOUSE=2

MAIN_DIALOG_FILE=${0}.gtkd
cat <<-EOUIDFEINITION > ${MAIN_DIALOG_FILE}
<window 
    window-position="${GTK_WIN_POS_CENTER}"
    default-width="400"
>
    <vbox>
        <frame   Description  >
            <text label="This is an example window."></text>
        </frame>
        <hbox>
            <button use-underline="true" tooltip-text="Refresh window variable - update date in window title">
                <label> _Refresh </label>
                <input file icon="gtk-refresh"></input>
                <width>16</width>
                <action function="refresh">vWindow</action>
            </button>
            <button ok></button>
            <button cancel></button>
        </hbox>
    </vbox>

    <label>Window Label</label>
    <variable>vWindow</variable>
$(:<<'COMMENT'
    <input>fnEchoDateTime</input>
    <input file>/tmp/aa.txt</input>
COMMENT
)
    <action this-is-window="escape" signal="key-press-event" condition="command_is_true( [[ \$KEY_RAW = 0x9 ]] && echo true )">EXIT:exit</action> 

</window>
EOUIDFEINITION

case ${1} in
    -d | --dump) cat ${MAIN_DIALOG_FILE} ;;
    *) gtkdialog --center --file=${MAIN_DIALOG_FILE} ;;
esac

rm -f ${MAIN_DIALOG_FILE}
EOSCRIPT

/tmp/${scriptName}.sh

The sensitive directive does the same thing as the sensitive attribute to the window tag – enables or disables window content. Consider:

scriptName=ex12
touch /tmp/${scriptName}.sh
chmod ug+x /tmp/${scriptName}.sh

cat <<-'EOSCRIPT' > /tmp/${scriptName}.sh
#!/bin/bash

GTKDIALOG=gtkdialog

fnEchoDateTime() {
echo "$(date)"
}
export -f fnEchoDateTime

GTK_WIN_POS_NONE=0
GTK_WIN_POS_CENTER=1
GTK_WIN_POS_MOUSE=2

MAIN_DIALOG_FILE=${0}.gtkd
cat <<-EOUIDFEINITION > ${MAIN_DIALOG_FILE}
<window 
    window-position="${GTK_WIN_POS_CENTER}"
    default-width="400"
>
    <vbox>
        <frame   Description  >
            <text label="This is an example window."></text>
        </frame>
        <hbox>
            <button use-underline="true" tooltip-text="Refresh window variable - update date in window title">
                <label> _Refresh </label>
                <input file icon="gtk-refresh"></input>
                <width>16</width>
                <action function="refresh">vWindow</action>
            </button>
            <button ok></button>
            <button cancel></button>
        </hbox>
    </vbox>

    <label>Window Label</label>
    <sensitive>false</sensitive>
    <variable>vWindow</variable>
$(:<<'COMMENT'
    <input>fnEchoDateTime</input>
    <input file>/tmp/aa.txt</input>
COMMENT
)
    <action this-is-window="escape" signal="key-press-event" condition="command_is_true( [[ \$KEY_RAW = 0x9 ]] && echo true )">EXIT:exit</action> 

</window>
EOUIDFEINITION

case ${1} in
    -d | --dump) cat ${MAIN_DIALOG_FILE} ;;
    *) gtkdialog --center --file=${MAIN_DIALOG_FILE} ;;
esac

rm -f ${MAIN_DIALOG_FILE}
EOSCRIPT

/tmp/${scriptName}.sh

The variable directive names the variable which can be used elsewhere to reference the window and act upon it, for example disable or enable, activate or deactivate, show or hide, refresh or clear, or set title. The example below provides a variable name vWindow to allow the button’s action directive to trigger the refresh action on the window. When the refresh action signal is processed the function fnEchoDateTime will be executed and its output will be provided to the Window object which will use it to set the window title. Consider the following example. Click the Refresh button a few times with at least 1 second between clicks and see what happens to the window title.

scriptName=ex13
touch /tmp/${scriptName}.sh
chmod ug+x /tmp/${scriptName}.sh

cat <<-'EOSCRIPT' > /tmp/${scriptName}.sh
#!/bin/bash

GTKDIALOG=gtkdialog

fnEchoDateTime() {
echo "$(date)"
}
export -f fnEchoDateTime

GTK_WIN_POS_NONE=0
GTK_WIN_POS_CENTER=1
GTK_WIN_POS_MOUSE=2

MAIN_DIALOG_FILE=${0}.gtkd
cat <<-EOUIDFEINITION > ${MAIN_DIALOG_FILE}
<window 
    title="My First Window"
    window-position="${GTK_WIN_POS_CENTER}"
    default-width="400"
>
    <vbox>
        <frame   Description  >
            <text label="This is an example window."></text>
        </frame>
        <hbox>
            <button use-underline="true" tooltip-text="Refresh window variable - update date in window title">
                <label> _Refresh </label>
                <input file icon="gtk-refresh"></input>
                <width>16</width>
             <action function="refresh">vWindow</action>
            </button>
            <button ok></button>
            <button cancel></button>
        </hbox>
    </vbox>

    <variable>vWindow</variable>
    <input>fnEchoDateTime</input>

    <action this-is-window="escape" signal="key-press-event" condition="command_is_true( [[ \$KEY_RAW = 0x9 ]] && echo true )">EXIT:exit</action> 

</window>
EOUIDFEINITION

case ${1} in
    -d | --dump) cat ${MAIN_DIALOG_FILE} ;;
    *) gtkdialog --center --file=${MAIN_DIALOG_FILE} ;;
esac

rm -f ${MAIN_DIALOG_FILE}
EOSCRIPT

/tmp/${scriptName}.sh

The input directive can invoke logic which can return a string to which the window title will be set when triggered by refresh of the variable named as the window variable. See example above. This function, as all such functions invoked by the gtkdialog, is expected to write to the stdout whatever text is supposed to be the input to the object – in this case the window title.

The output file directive will cause the “value” of this object to be written to the named file if the save action is triggered. In the case of the window object the title will be written to the file. Consider:

scriptName=ex14
touch /tmp/${scriptName}.sh
chmod ug+x /tmp/${scriptName}.sh

cat <<-'EOSCRIPT' > /tmp/${scriptName}.sh
#!/bin/bash

GTKDIALOG=gtkdialog

fnEchoDateTime() {
echo "$(date)"
}
export -f fnEchoDateTime

GTK_WIN_POS_NONE=0
GTK_WIN_POS_CENTER=1
GTK_WIN_POS_MOUSE=2

MAIN_DIALOG_FILE=${0}.gtkd
cat <<-EOUIDFEINITION > ${MAIN_DIALOG_FILE}
<window 
    title="My First Window"
    window-position="${GTK_WIN_POS_CENTER}"
    default-width="400"
>
    <vbox>
        <frame   Description  >
            <text label="This is an example window."></text>
        </frame>
        <hbox>
            <button use-underline="true" tooltip-text="Save window data">
                <label> _Save </label>
                <input file icon="gtk-save"></input>
                <width>16</width>
              <action function="save">vWindow</action>
            </button>
            <button use-underline="true" tooltip-text="Refresh window variable - update date in window title">
                <label> _Refresh </label>
                <input file icon="gtk-refresh"></input>
                <width>16</width>
                <action function="refresh">vWindow</action>
            </button>
            <button ok></button>
            <button cancel></button>
        </hbox>
    </vbox>

    <variable>vWindow</variable>
    <input>fnEchoDateTime</input>

    <output file>/tmp/gtkd.out</output>

    <action signal="key-press-event" condition="command_is_true( [[ \$KEY_RAW = 0x9 ]] && echo true )">EXIT:exit</action> 

</window>
EOUIDFEINITION

case ${1} in
    -d | --dump) cat ${MAIN_DIALOG_FILE} ;;
    *) gtkdialog --center --file=${MAIN_DIALOG_FILE} ;;
esac

rm -f ${MAIN_DIALOG_FILE}
EOSCRIPT

/tmp/${scriptName}.sh

The action directive provides a mechanism for invoking logic when certain kinds of events affecting the window object occur. The example above used the ‘<action function="save">…</action>‘ markup to add an action to a button object, to be invoked when the button is pressed. Another example would be invoking the close window logic when ESC key is pressed. In the example above, the key-press-event signal is intercepted in the action directive and if the raw key code was (0x9), the Escape key, the gtkdialog calls the exit logic and the window is closed.

Exploring the action directive

Let’s consider the following signals, defined in the Window object reference:

Name Description Content Since
action signal=”type” Execute command on signal Shell command
action signal=”type” Perform function on signal function:parameter
action signal=”type” condition=”type” Execute command on signal conditionally Shell command 0.8.3
action signal=”type” condition=”type” Perform function on signal conditionally function:parameter 0.8.3

In ex13, above, we used the “action function” and “action signal”, to save the window title to a file and to exit the application respectively. Note that the reference does not include the “action function” variant but it is supported and works, as the example illustrates.

Explore the reference for the Window object, http://01micko.com/reference/window.html, to see the signals and the functions defined in that document.

The “action condition” construct is perhaps worth exploring as it is explained what the “_is_true”/
_is_false” is supposed to return. To cut to the chase, the command or the function is supposed to return the literal text “true” or “false”. The example below extends the example above by making the save action conditional on the file not already existing. This is contrived but makes the point.

Consider:

scriptName=ex15
touch /tmp/${scriptName}.sh
chmod ug+x /tmp/${scriptName}.sh

cat <<-'EOSCRIPT' > /tmp/${scriptName}.sh
#!/bin/bash

GTKDIALOG=gtkdialog

fnEchoDateTime() {
    echo "$(date)"
}
export -f fnEchoDateTime

fnAlreadySaved() {
    vFileName="${1}"
    [[ -f ${vFileName} ]] && { echo "true" ; yad --title="File ${vFileName} exists" --text="File ${vFileName} exists - not saving" --center --image="gtk-dialog-error" --window-icon="gtk-dialog-error" --width=500 --height=100 --button="gtk-ok:0"; } || echo "false"
}
export -f fnAlreadySaved


GTK_WIN_POS_NONE=0
GTK_WIN_POS_CENTER=1
GTK_WIN_POS_MOUSE=2

MAIN_DIALOG_FILE=${0}.gtkd
cat <<-EOUIDFEINITION > ${MAIN_DIALOG_FILE}
<window 
    title="My First Window"
    window-position="${GTK_WIN_POS_CENTER}"
    default-width="400"
>
    <vbox>
        <frame   Description  >
            <text label="This is an example window."></text>
        </frame>
        <hbox>
            <button use-underline="true" tooltip-text="Save window data">
                <label> _Save </label>
                <input file icon="gtk-save"></input>
                <width>16</width>
                <action function="save" condition="command_is_false(fnAlreadySaved /tmp/gtkd.out)">vWindow</action>
            </button>
            <button use-underline="true" tooltip-text="Refresh window variable - update date in window title">
                <label> _Refresh </label>
                <input file icon="gtk-refresh"></input>
                <width>16</width>
                <action function="refresh">vWindow</action>
            </button>
            <button ok></button>
            <button cancel></button>
        </hbox>
    </vbox>

    <variable>vWindow</variable>
    <input>fnEchoDateTime</input>

    <output file>/tmp/gtkd.out</output>

    <action signal="key-press-event" condition="command_is_true( [[ \$KEY_RAW = 0x9 ]] && echo true )">EXIT:exit</action> 

</window>
EOUIDFEINITION

case ${1} in
    -d | --dump) cat ${MAIN_DIALOG_FILE} ;;
    *) gtkdialog --center --file=${MAIN_DIALOG_FILE} ;;
esac

rm -f ${MAIN_DIALOG_FILE}
EOSCRIPT

touch /tmp/gtkd.out; rm /tmp/gtkd.out; /tmp/${scriptName}.sh

Exploring “action signal” directive

Let’s explore the “action signal” directive to see what sort of signals the Window object receives.

From the reference:

There is no default signal for this widget.

The “file-changed” signal is emitted if file-monitor is true and the input file being monitored has changed.

The following signals are connected-up for all widgets:

button-press-event
button-release-event
configure-event
enter-notify-event
leave-notify-event
focus-in-event
focus-out-event
hide
show
realize
key-press-event
key-release-event
map-event
unmap-event

From examples, additional events:

delete-event
destroy-event

Consider the following example, which is based on the /home/demo/gtkdialog-0.8.3/examples/miscellaneous/signals example.

scriptName=ex16
touch /tmp/${scriptName}.sh
chmod ug+x /tmp/${scriptName}.sh

cat <<-'EOSCRIPT' > /tmp/${scriptName}.sh
#!/bin/bash

GTKDIALOG=gtkdialog

MAIN_DIALOG_FILE=${0}.gtkd
cat <<-EOUIDFEINITION > ${MAIN_DIALOG_FILE}
<window title="Signals" icon-name="gtk-dialog-warning">
    <vbox>
        <frame Widgets>
            <text>
                <label>Label</label>
                <action signal="button-press-event">echo Label: button-press-event</action>
                <action signal="button-release-event">echo Label: button-release-event</action>
                <action signal="configure-event">echo Label: configure-event</action>
                <action signal="enter-notify-event">echo Label: enter-notify-event</action>
                <action signal="leave-notify-event">echo Label: leave-notify-event</action>
                <action signal="focus-in-event">echo Label: focus-in-event</action>
                <action signal="focus-out-event">echo Label: focus-out-event</action>
                <action signal="key-press-event">echo Label: key-press-event</action>
                <action signal="key-release-event">echo Label: key-release-event</action>
                <action signal="hide">echo Label: hide</action>
                <action signal="show">echo Label: show</action>
                <action signal="realize">echo Label: realize</action>
                <action signal="map-event">echo Label: map-event</action>
                <action signal="unmap-event">echo Label: unmap-event</action>
            </text>
            <entry>
                <default>Entry</default>
                <action signal="button-press-event">echo Entry: button-press_event</action>
                <action signal="button-release-event">echo Entry: button-release-event</action>
                <action signal="configure-event">echo Entry: configure-event</action>
                <action signal="enter-notify-event">echo Entry: enter-notify-event</action>
                <action signal="leave-notify-event">echo Entry: leave-notify-event</action>
                <action signal="focus-in-event">echo Entry: focus-in-event</action>
                <action signal="focus-out-event">echo Entry: focus-out-event</action>
                <action signal="key-press-event">echo Entry: key-press-event</action>
                <action signal="key-release-event">echo Entry: key-release-event</action>
                <action signal="hide">echo Entry: hide</action>
                <action signal="show">echo Entry: show</action>
                <action signal="realize">echo Entry: realize</action>
                <action signal="map-event">echo Entry: map-event</action>
                <action signal="unmap-event">echo Entry: unmap-event</action>
            </entry>
        </frame>
        <hbox>
            <button ok>
                <action signal="button-press-event">echo Button: button-press_event</action>
                <action signal="button-release-event">echo Button: button-release-event</action>
                <action signal="configure-event">echo Button: configure-event</action>
                <action signal="enter-notify-event">echo Button: enter-notify-event</action>
                <action signal="leave-notify-event">echo Button: leave-notify-event</action>
                <action signal="focus-in-event">echo Button: focus-in-event</action>
                <action signal="focus-out-event">echo Button: focus-out-event</action>
                <action signal="key-press-event">echo Button: key-press-event</action>
                <action signal="key-release-event">echo Button: key-release-event</action>
                <action signal="hide">echo Button: hide</action>
                <action signal="show">echo Button: show</action>
                <action signal="realize">echo Button: realize</action>
                <action signal="map-event">echo Button: map-event</action>
                <action signal="unmap-event">echo Button: unmap-event</action>
            </button>
        </hbox>
    </vbox>
    <action signal="button-press-event">echo Window: button-press_event</action>
    <action signal="button-release-event">echo Window: button-release-event</action>
    <action signal="configure-event">echo Window: configure-event</action>
    <action signal="delete-event">echo Window: delete-event</action>
    <action signal="destroy-event">echo Window: destroy-event</action>
    <action signal="enter-notify-event">echo Window: enter-notify-event</action>
    <action signal="leave-notify-event">echo Window: leave-notify-event</action>
    <action signal="focus-in-event">echo Window: focus-in-event</action>
    <action signal="focus-out-event">echo Window: focus-out-event</action>
    <action signal="key-press-event">echo Window: key-press-event</action>
    <action signal="key-release-event">echo Window: key-release-event</action>
    <action signal="hide">echo Window: hide</action>
    <action signal="show">echo Window: show</action>
    <action signal="realize">echo Window: realize</action>
    <action signal="map-event">echo Window: map-event</action>
    <action signal="unmap-event">echo Window: unmap-event</action>
</window>
EOUIDFEINITION

case ${1} in
    -d | --dump) cat ${MAIN_DIALOG_FILE} ;;
    *) gtkdialog --center --file=${MAIN_DIALOG_FILE} ;;
esac

rm -f ${MAIN_DIALOG_FILE}
EOSCRIPT

/tmp/${scriptName}.sh

Each if the events gives rise to an opportunity to hook in logic to be executed when the event is triggered.

In examples ex15, the following stanza illustrates the handling of the key-press-event in such a way that the exit function is called conditionally if the key which was pressed was the Escape key (0x9).

<action signal="key-press-event" condition="command_is_true( [[ \$KEY_RAW = 0x9 ]] && echo true )">EXIT:exit</action>

Exploring “action function” directive

From the reference (http://01micko.com/reference/window.html):

The following functions can be performed upon this widget by any widget capable of emitting signals:

Type Description Parameter Since
enable Sensitise widget Variable name
disable Desensitise widget Variable name
show Show widget Variable name 0.8.1
hide Hide widget Variable name 0.8.1
refresh Reload input data[1] Variable name 0.8.1
save Save widget data Variable name 0.8.1
clear Remove all widget data Variable name 0.8.1

We used the “save” function in example ex15. Now we will consider disable, enable, hide and show functions.

Let’s consider the following example:

scriptName=ex17
touch /tmp/${scriptName}.sh
chmod ug+x /tmp/${scriptName}.sh

cat <<-'EOSCRIPT' > /tmp/${scriptName}.sh
#!/bin/bash

GTKDIALOG=gtkdialog

fnEchoDateTime() {
    echo "$(date)"
}
export -f fnEchoDateTime

GTK_WIN_POS_NONE=0
GTK_WIN_POS_CENTER=1
GTK_WIN_POS_MOUSE=2

MAIN_DIALOG_FILE=${0}.gtkd
cat <<-EOUIDFEINITION > ${MAIN_DIALOG_FILE}
<window 
    title="My First Window"
    window-position="${GTK_WIN_POS_CENTER}"
    default-width="400"
>
    <vbox>
        <frame   Description  >
            <text>
                <label>"This is an example window."</label>
                <variable>vText</variable>
            </text>
        </frame>
        <hbox>
            <button use-underline="true" tooltip-text="Hide Text">
                <label> _Hide </label>
                <variable>vBtnHide</variable>
                <action function="hide" condition="visible_is_true(vText)">vText</action>
                <action function="hide" condition="visible_is_true(vBtnHide)">vBtnHide</action>
                <action function="show" condition="visible_is_false(vBtnShow)">vBtnShow</action>
            </button>
            <button visible="false" use-underline="true" tooltip-text="Show Text">
                <label> _Show </label>
                <variable>vBtnShow</variable>
                <action function="show" condition="visible_is_false(vText)">vText</action>
                <action function="show" condition="visible_is_false(vBtnHide)">vBtnHide</action>
                <action function="hide" condition="visible_is_true(vBtnShow)">vBtnShow</action>
            </button>

            <button use-underline="true" tooltip-text="Disable Text">
                <label> _Disable </label>
                <variable>vBtnDisable</variable>
                <action function="disable" condition="sensitive_is_true(vText)">vText</action>
                <action function="hide" condition="visible_is_true(vBtnDisable)">vBtnDisable</action>
                <action function="show" condition="visible_is_false(vBtnEnable)">vBtnEnable</action>
            </button>
            <button visible="false" use-underline="true" tooltip-text="Enable Text">
                <label> _Enable </label>
                <variable>vBtnEnable</variable>
                <action function="enable" condition="sensitive_is_false(vText)">vText</action>
                <action function="show" condition="visible_is_false(vBtnDisable)">vBtnDisable</action>
                <action function="hide" condition="visible_is_true(vBtnEnable)">vBtnEnable</action>
            </button>

            <button use-underline="true" tooltip-text="update date in window title">
                <label> _Refresh </label>
                <input file icon="gtk-refresh"></input>
                <width>16</width>
                <action function="refresh">vWindow</action>
            </button>
            <button ok></button>
            <button cancel></button>
        </hbox>
    </vbox>

    <variable>vWindow</variable>
    <input>fnEchoDateTime</input>

    <output file>/tmp/gtkd.out</output>

    <action signal="key-press-event" condition="command_is_true( [[ \$KEY_RAW = 0x9 ]] && echo true )">EXIT:exit</action> 

</window>
EOUIDFEINITION

case ${1} in
    -d | --dump) cat ${MAIN_DIALOG_FILE} ;;
    *) gtkdialog --center --file=${MAIN_DIALOG_FILE} ;;
esac

rm -f ${MAIN_DIALOG_FILE}
EOSCRIPT

/tmp/${scriptName}.sh

In the script above we are not hiding/showing and enabling/disabling the window, as we might, because as soon as we hide the window it will disappear and as soon as we disable it we will not be able to do anything to it except close it using the close “button” in the title bar. We are, instead, jumping ahead a bit, hiding and showing the text object and enabling and disabling the text object. This example also demonstrates how a “toggle” button can be implemented, in this case by alternately hiding and showing two different buttons (hide and show, disable/enable). The end-user impression will be that a single button changes the label and the button function on click (hide/how, disable/enable).

To see how to hide/show and disable/enable the window object consider the following, not particularly useful in practice, example:

scriptName=ex18
touch /tmp/${scriptName}.sh
chmod ug+x /tmp/${scriptName}.sh

cat <<-'EOSCRIPT' > /tmp/${scriptName}.sh
#!/bin/bash

GTKDIALOG=gtkdialog

fnEchoDateTime() {
    echo "$(date)"
}
export -f fnEchoDateTime

GTK_WIN_POS_NONE=0
GTK_WIN_POS_CENTER=1
GTK_WIN_POS_MOUSE=2

MAIN_DIALOG_FILE=${0}.gtkd
cat <<-EOUIDFEINITION > ${MAIN_DIALOG_FILE}
<window 
    title="My First Window"
    window-position="${GTK_WIN_POS_CENTER}"
    default-width="400"
>
    <vbox>
        <frame   Description  >
            <text>
                <label>"Click Hide or Disable and wait up to 5 seconds for the timer to trigger the reversal of the action."</label>
                <variable>vText</variable>
            </text>
        </frame>
        <hbox>
            <timer sensitive="false" milliseconds="true" interval="5000" visible="false" disabled="true">
                <variable>tmr0</variable>
                <action>show:vWindow</action>
            </timer>
            <timer sensitive="false" milliseconds="true" interval="5000" visible="false" disabled="true">
                <variable>tmr1</variable>
                <action>enable:vWindow</action>
            </timer>
            <button use-underline="true" tooltip-text="Hide Window">
                <label> _Hide </label>
                <action function="hide" condition="visible_is_true(vWindow)">vWindow</action>
                <action function="enable" condition="sensitive_is_false(tmr0)">tmr0</action>
            </button>
            <button use-underline="true" tooltip-text="Disable Window">
                <label> _Disable </label>
                <action function="disable" condition="sensitive_is_true(vWindow)">vWindow</action>
                <action function="enable" condition="sensitive_is_false(tmr1)">tmr1</action>
            </button>
            <button use-underline="true" tooltip-text="update date in window title">
                <label> _Refresh </label>
                <input file icon="gtk-refresh"></input>
                <width>16</width>
                <action function="refresh">vWindow</action>
            </button>
            <button ok></button>
            <button cancel></button>
        </hbox>
    </vbox>

    <variable>vWindow</variable>
    <input>fnEchoDateTime</input>

    <output file>/tmp/gtkd.out</output>

    <action signal="key-press-event" condition="command_is_true( [[ \$KEY_RAW = 0x9 ]] && echo true )">EXIT:exit</action> 

</window>
EOUIDFEINITION

case ${1} in
    -d | --dump) cat ${MAIN_DIALOG_FILE} ;;
    *) gtkdialog --center --file=${MAIN_DIALOG_FILE} ;;
esac

rm -f ${MAIN_DIALOG_FILE}
EOSCRIPT

/tmp/${scriptName}.sh

In the script above we are jumping ahead a bit and using timers to trigger events that will show the hidden window and enable the disabled content after an interval.

Perhaps provision of a bit of context is in order at this point for no other reason than to make the basic action processing explicit.

Consider the two actions defined for the Hide button.

<action function="hide" condition="visible_is_true(vWindow)">vWindow</action>

<action function="enable" condition="sensitive_is_false(tmr0)">tmr0</action>

The first action will hide the window. The condition “visible_is_true(vWindow)” is superfluous in this case because the button will only be visible if the window is visible but it is there as an example of a conditional action.

The second action will enable a timer which will trigger its own action to reverse the effect of the button press on the Window object after an interval of up to 5 seconds.

Unless told otherwise, of which in a later article, the button press will cause the two actions to be executed one after the other. There are some circumstances in which execution of an action in the action list will prevent actions defined after it being executed but these are rare circumstances, which will be discussed in a subsequent article. One can have several actions which a button press will trigger. Other active objects can have actions as well, as can be seen with the timer objects and will be also seen in subsequent articles in which other objects are discussed and used.

For the time being one can assume that all actions defined for an object, like a window or a button, will get evaluated sequentially and will be executed if appropriate (like for example if the condition is met).

The style attribute does nothing visible. It is quite likely that I don’t know how to define a style. There is an alternative way to “style” objects which works in some instances and can be used as appropriate.

Consider the following example:

scriptName=ex19
touch /tmp/${scriptName}.sh
chmod ug+x /tmp/${scriptName}.sh

cat <<-'EOSCRIPT' > /tmp/${scriptName}.sh
#!/bin/bash

GTKDIALOG=gtkdialog

fnEchoDateTime() {
echo "$(date)"
}
export -f fnEchoDateTime

echo '
style "bgWhite" { bg[NORMAL] = "#FFFFFF" }
style "fgWhite" { fg[NORMAL] = "#FFFFFF" }
style "bgBlack" { bg[NORMAL] = "#000000" }
style "bgRed" { bg[NORMAL] = "#FF0000" }
style "fgRed" { fg[NORMAL] = "#FF0000" }
style "bgGreen" { bg[NORMAL] = "#FFFF00" }
style "fgGreen" { fg[NORMAL] = "#FFFF00" }
style "bgBlue" { bg[NORMAL] = "#0000FF" }
style "fgBlue" { fg[NORMAL] = "#0000FF" }

widget "MyWindowBg" style "bgBlack"
widget "MyWindowBg.GtkVBox.GtkHBox.MyButtonBg" style "bgGreen"

widget "MyWindowBg.GtkVBox.myEVB" style "bgBlue"

widget_class "*<GtkFrame>.GtkLabel" style "fgWhite"
widget_class "*<GtkFrame>.*.GtkLabel" style "fgGreen"
widget_class "*<GtkButton>.*.GtkLabel" style "fgRed"

' > /tmp/gtkrc_mono 
export GTK2_RC_FILES=/tmp/gtkrc_mono

GTK_WIN_POS_NONE=0
GTK_WIN_POS_CENTER=1
GTK_WIN_POS_MOUSE=2

MAIN_DIALOG_FILE=${0}.gtkd
cat <<-EOUIDFEINITION > ${MAIN_DIALOG_FILE}
<window 
    title="My First Window"
    window-position="${GTK_WIN_POS_CENTER}"
    default-width="400"
    name="MyWindowBg"
>
    <vbox>
        <eventbox name="myEVB" above-child="false" visible-window="true">
        <frame   Description  >
            <text><label>This is an example window.</label></text>
        </frame>
        </eventbox>
        <hbox>
            <button name="MyButtonBg" use-underline="true" tooltip-text="Refresh window variable - update date in window title">
                <label> _Refresh </label>
                <input file icon="gtk-refresh"></input>
                <width>16</width>
                <action function="refresh">vWindow</action>
            </button>
            <button ok></button>
            <button cancel></button>
        </hbox>
    </vbox>

    <variable>vWindow</variable>
    <input>fnEchoDateTime</input>
    <action this-is-window="escape" signal="key-press-event" condition="command_is_true( [[ \$KEY_RAW = 0x9 ]] && echo true )">EXIT:exit</action> 
</window>
EOUIDFEINITION

case ${1} in
    -d | --dump) cat ${MAIN_DIALOG_FILE} ;;
    *) gtkdialog --center --file=${MAIN_DIALOG_FILE} ;;
esac

rm -f ${MAIN_DIALOG_FILE}
EOSCRIPT

/tmp/${scriptName}.sh

Here we define and use GTK styles to change foreground and/or background colours of the various objects.

Here is how the application window looks.

This is just a brief teaser. How to apply styles to objects, what works and what does not, etc., may be covered in a future article.

Exploring Window examples

In /home/demo/gtkdialog-0.8.3/examples/window there are 3 examples. Review the window GUI definitions and run the examples

/bin/sh /home/demo/gtkdialog-0.8.3/examples/window/window --dump

/bin/sh /home/demo/gtkdialog-0.8.3/examples/window/window

/bin/sh /home/demo/gtkdialog-0.8.3/examples/window/window_attributes --dump

/bin/sh /home/demo/gtkdialog-0.8.3/examples/window/window_attributes

/bin/sh /home/demo/gtkdialog-0.8.3/examples/window/window_signals --dump

/bin/sh /home/demo/gtkdialog-0.8.3/examples/window/window_signals
Apr 09

The biggest hurdle for a gtkdialog newbe, or at least so it was for me, was getting handle on how to structure the code, where to find documentation and how to use it, where to find examples and how to add comments to the code or comment out blocks of code without braking scripts.

In this article I go over these topics in some detail. It seems necessary to consider the basics before launching into more ambitious topics.

This is the next article in a series of articles on gtkdialog, “gtkdialog Exploration – articles and examples”, which can be found at https://blogs.czapski.id.au/2017/04/gtkdialog-exploration.

Creadits:

Gtkdialog – A small utility for fast and easy GUI building.
2003-2007  László Pere <pipas@linux.pte.hu>
2011-2012  Thunor thunorsif@hotmail.com
Project site: https://code.google.com/archive/p/gtkdialog/
Original documentation: http://linux.pte.hu/~pipas/gtkdialog/
Most recent documentation (not very recent) http://xpt.sourceforge.net/techdocs/language/gtkdialog/gtkde03-GtkdialogUserManual/.

The project home page contains links to other resources which are worth looking at.

Pre-Requisites

This article assumes that the Virtual Box Machine Image created in accordance with the instructions in the blog article to be found at https://blogs.czapski.id.au/2016/10/configure-virtual-box-virtual-machine-and-install-centos-6-8-base-image is available but it is expected that pretty much any Linux environment will do just as well so long as it supports yad and gtkdialog.

For convenience I posted the export of the VirtualBox image which would have been built if the reader followed all the articles in the series “Build a Linux-based Infrastructure Solution Demonstration Series” to date, that is to the 8th of March 2017. The link to the main article is https://blogs.czapski.id.au/2017/04/virtualbox-image-with-content-as-built-so-far.

gtkdialog application structure and basic interaction with the environment

A gtkdialog application UI is, in effect, a window with content. Perhaps the simplest reasonable gtkdialog application with gtkdialog GUI definition is shown below:

touch /tmp/ex01.sh
chmod ug+x /tmp/ex01.sh

cat <<-'EOSCRIPT' > /tmp/ex01.sh
#!/bin/bash
MAIN_DIALOG_FILE=/tmp/ex01.gtkd

cat <<-'EOUIDFEINITION' > ${MAIN_DIALOG_FILE}
<window>
  <vbox>
    <text>
      <label>I am a window.</label>
    </text>
    <button ok></button>
  </vbox>
</window>
EOUIDFEINITION

case ${1} in
    -d | --dump) cat ${MAIN_DIALOG_FILE} ;;
    *) gtkdialog --center --file=${MAIN_DIALOG_FILE} ;;
esac

rm -f ${MAIN_DIALOG_FILE}
EOSCRIPT

/tmp/ex01.sh

This application’s UI looks like this:

Note that the standard window controls, close, maximise, minimise and window menu are provided, as is the title bar with some title even though we specified neither in the UI definition.

The application has a text box with literal text we provided and a button with the label we provided. Clicking the button will cause the UI to exit with the following text emitted to stdout.

EXIT="OK"

The gtkdialog application consists of a GUI definition, expressed as gtkdialog-specific XML structure, provided to a gtkdialog executable as a file or as a shell variable.

The GUI definition specifies all the visual components, event handlers, actions, etc.. More on the event handlers, actions and similar in a later article. For now we will keep things simple.

Common GUI definition convention

In a typical gtkdialog example online, and in the examples found under ~/gtkdialog-0.8.3/examples/ you will see the following idiom for defining and using gtkdialog UIs:

touch /tmp/ex02.sh
chmod ug+x /tmp/ex02.sh

cat <<-'EOSCRIPT' > /tmp/ex02.sh
#!/bin/bash

MAIN_DIALOG='
<window>
  <vbox>
    <text>
      <label>I am a window.</label>
    </text>
    <button ok></button>
  </vbox>
</window>
'
export MAIN_DIALOG

case ${1} in
    -d | --dump) echo ${MAIN_DIALOG} ;;
    *) gtkdialog --center ${MAIN_DIALOG} ;;
esac
EOSCRIPT

/tmp/ex02.sh

Here the GUI definition is explicitly assigned to a shell variable with “hard” quotes surrounding it. I don’t like this idiom and I don’t use this idiom. The reason is that shell quoting becomes quite tricky and error-prone as soon as the GUI definition becomes more complex and GUI components invoke external commands or shell functions with parameters, etc..

Instead, I use the ‘”here document” piped to a file’ idiom, like in the ex01.sh example above. This is the idiom I will continue to use so you will need to “translate” examples I use to examples provided with gtkdialog and seen online, or translate them to my way of doing things, whichever you prefer.

How can gtkdialog application interact with the environment (basic)

It was briefly mentioned in the text following ex01.sh that when the OK button is clicked the GUI is closed and gtkdialog emits to the stdout

EXIT="OK"

If there were other buttons and other variables defined and populated in the GUI their names and values would similarly be emitted to the stdout.

Let’s try the following example to demonstrate this:

touch /tmp/ex03.sh
chmod ug+x /tmp/ex03.sh

cat <<-'EOSCRIPT' > /tmp/ex03.sh
#!/bin/bash
MAIN_DIALOG_FILE=${0}.gtkd
cat <<-'EOUIDFEINITION' > ${MAIN_DIALOG_FILE}
<window>
  <vbox>
    <text>
      <label>I am a window.</label>
      <variable>vLabelVariable</variable>
    </text>
    <button ok></button>
    <button cancel></button>
  </vbox>
</window>
EOUIDFEINITION

case ${1} in
    -d | --dump) cat ${MAIN_DIALOG_FILE} ;;
    *) gtkdialog --center --file=${MAIN_DIALOG_FILE} ;;
esac
rm -f ${MAIN_DIALOG_FILE}
EOSCRIPT

/tmp/ex03.sh

When the Cancel button is pressed the following is emitted to the stdout

vLabelVariable="I am a window."
EXIT="Cancel"

When the OK button is pressed the following is emitted to the stdout

vLabelVariable="I am a window."
EXIT="OK"

A script can intercept the value of the variables, for example the EXIT variable, and alter subsequent processing as required.

Let’s modify the script so that it recognises the value of the EXIT variable.

cat <<-'EOSCRIPT' > /tmp/ex03.sh
#!/bin/bash

MAIN_DIALOG_FILE=${0}.gtkd
cat <<-'EOUIDFEINITION' > ${MAIN_DIALOG_FILE}
<window>
  <vbox>
    <text>
      <label>I am a window.</label>
      <variable>vLabelVariable</variable>
    </text>
    <button ok></button>
    <button cancel></button>
  </vbox>
</window>
EOUIDFEINITION

case ${1} in
    -d | --dump) cat ${MAIN_DIALOG_FILE} ;;
    *) RESULTS=$(gtkdialog --center --file=${MAIN_DIALOG_FILE}) ;;
esac

I=$IFS; IFS=""
for STATEMENTS in "${RESULTS}"; do
  echo 'STATEMENTS:-->>'"${STATEMENTS}"'<<--'
  eval $STATEMENTS
done
IFS=$I

if [[ "$EXIT" = "OK" ]]; then
  echo "You clicked OK with vLabelVariable containing '${vLabelVariable}'"
else
  echo "You pressed the Cancel button."
fi

rm -f ${MAIN_DIALOG_FILE}
EOSCRIPT

/tmp/ex03.sh

Executing this script produces:

STATEMENTS:-->>vLabelVariable="I am a window."
EXIT="OK"<<--
You clicked OK with vLabelVariable containing 'I am a window.'

A shell scripter will undoubtedly come up with alternate ways of getting at the variable names and values.

Note that the content of the vLabelVariable is also emitted. This may or may not be useful. It is possible to prevent the variable being emitted by providing the export=”false” attribute when defining the variable in the GUI. The script variant below illustrates this.

cat <<-'EOSCRIPT' > /tmp/ex03.sh
#!/bin/bash

MAIN_DIALOG_FILE=${0}.gtkd

cat <<-'EOUIDFEINITION' > ${MAIN_DIALOG_FILE}
<window>
  <vbox>
    <text>
      <label>I am a window.</label>
      <variable export="false">vLabelVariable</variable>
    </text>
    <button ok></button>
    <button cancel></button>
  </vbox>
</window>
EOUIDFEINITION

case ${1} in
    -d | --dump) cat ${MAIN_DIALOG_FILE} ;;
    *) RESULTS=$(gtkdialog --center --file=${MAIN_DIALOG_FILE}) ;;
esac

I=$IFS; IFS=""
for STATEMENTS in "${RESULTS}"; do
  echo 'STATEMENTS:-->>'"${STATEMENTS}"'<<--'
  eval $STATEMENTS
done
IFS=$I

if [[ "$EXIT" = "OK" ]]; then
  echo "You clicked OK with vLabelVariable containing '${vLabelVariable}'"
else
  echo "You pressed the Cancel button."
fi

rm -f ${MAIN_DIALOG_FILE}
EOSCRIPT

/tmp/ex03.sh

Executing the modified script produces the following:

STATEMENTS:-->>EXIT="OK"<<--
You clicked OK with vLabelVariable containing ''

The variable vLabelVariable was not exported and its value is not available.

I am jumping ahead a bit here by making a reference to a forward topic. The topic of bash functions used as callbacks and action logic will be dealt with later. For now one needs to know that whether to use export=”false” is a reasonably important decision because any variable defined with export=”false” will not be visible in the shell environment of the callback/action functions so will either be inaccessible or will have to be passed as an argument to a callback/action function. I don’t see harm in not specifying export=”false”. Perhaps there is a performance benefit is so doing.

More on the topic of callbacks and actions in a later article.

Explore examples

I confess that without the examples provided with the gtkdialog source distribution and also available online at https://github.com/01micko/gtkdialog/tree/master/examples I would have given up on gtkdialog. The reference documentation is just that – reference – a place to go to look up a specific piece of information knowing that such information can be looked up in the reference.

A newbie needs to understand the broad capabilities, concepts and functionality, and needs some elaboration. This sort of documentation is missing. True, there are a few tutorials but they are hard to find and are pretty limited in the coverage and scope, so examples are the best means of finding out what can be done and how, unless a comprehensive tutorial becomes available.

Until it does, spend time looking at examples to see what the various tags and attributes exist and what they accomplish. For this set of articles I installed examples locally to ~/gtkdialog-0.8.3/examples. There is no index so explore each on its own, or use the indexed and accessible source at the github URL quoted above.

The simplest way is to invoke each example as a shells script, which is what most of them are, and see what is shown, and then look at the script source to see how it does it.

For example:

~/gtkdialog-0.8.3/examples/window/window

Then see what the GUI definition looks like

~/gtkdialog-0.8.3/examples/window/window --dump
<window>
    <vbox>
        <frame Description>
            <text>
                <label>This is an example window.</label>
            </text>
        </frame>
        <hbox>
            <button ok></button>
            <button cancel></button>
        </hbox>
    </vbox>
</window>

And finally, look at the source:

geany ~/gtkdialog-0.8.3/examples/window/window &
#!/bin/sh

GTKDIALOG=gtkdialog

MAIN_DIALOG='
<window>
    <vbox>
        <frame Description>
            <text>
                <label>This is an example window.</label>
            </text>
        </frame>
        <hbox>
            <button ok></button>
            <button cancel></button>
        </hbox>
    </vbox>
</window>
'
export MAIN_DIALOG

case $1 in
    -d | --dump) echo "$MAIN_DIALOG" ;;
    *) $GTKDIALOG --program=MAIN_DIALOG ;;
esac

Reference Documentation

Reference documentation is available in a number of places, and is more or less useful, depending on a bunch of factors.

In the demo image it is available at ~/gtkdialog-0.8.3/doc/reference/. Again, there is no index so pick the first HTML document that comes to hand and view it in a web browser. Internal links will allow you to navigate from object to object so this is not much of a drama. An index is available online at http://01micko.com/reference/. So, locally:

firefox ~/gtkdialog-0.8.3/doc/reference/button.html &

For a newbie the first glance at this is somewhat off-putting. Still, note the following:

  1. At the bottom of the page, and all other pages, is a list of hyperlinks to the various gtkdialog objects so you can navigate form button to text to window and so on
  2. The definition lists all the valid tags and discusses the various attributes, signals, functions, etc. – more on these topics in subsequent articles
  3. The tag attributes section lists all attributes specific to this tag, BUT it does not list these attributes which can be applied to the tag but which are inherited from the parent objects in the object hierarchy
  4. In this case the innocuous sentence “See the GtkButton widget and ancestor class properties.” hints that there may be other properties that can be applied to the button and links to the page which contains more on the topic. Note that this is an external link so an Internet connection will be needed to access it.
  5. The external source for GtkButton contains, amongst other things, the object hierarchy of which the button object is a child as well as a list of properties defined for that object. The hierarchy is navigable and inherited parent properties can be discovered by following the links.
  6. Not all defined properties, signals and functions are mapped by, and visible to, gtkdialog or useable from it. For example, the border-width property of the GtkContainer, which is a grandparent of the GtkButton, is useable from the GtkButton but the other two properties are not visible or useable
  7. One has to exercise one’s mind a bit to translate data types to what can/should be specified as the attribute value for a particular attribute/property, for example one would specify ‘<window border-width=”6″ …’.

As I practice it, gtkdialog is pretty easy to work with to try various things. Exploration of what the various attributes do to a GUI is pretty painless, particularly once one knows how to effectively comment out blocks of gtkdialog GUI definition XML for various experiments.

The following are links to material that I found useful when dealing with specific topics in my work with gtkdialog so far. They are collected here for ease of reference.

gtkdialog Refrences and Examples

Themes and Styles

Object Hierarchy – Window

Commenting out blocks of XML

One of the reasons I use ‘”here documents” piped to a file’ for persisting gtkdialog GUI definitions is because I can easily comment out blocks of XML without breaking gtkdialog at runtime.

Let’s consider what a person familiar with XML would try first:

touch /tmp/ex04.sh
chmod ug+x /tmp/ex04.sh

cat <<-'EOSCRIPT' > /tmp/ex04.sh
#!/bin/bash

MAIN_DIALOG_FILE=${0}.gtkd

cat <<-'EOUIDFEINITION' > ${MAIN_DIALOG_FILE}
<window>
<vbox>
<!--
    <text>
      <label>I am a window.</label>
    </text>
-->
<button ok></button>
</vbox>
</window>
EOUIDFEINITION

case ${1} in
-d | --dump) cat ${MAIN_DIALOG_FILE} ;;
*) gtkdialog --center --file=${MAIN_DIALOG_FILE} ;;
esac

rm -f ${MAIN_DIALOG_FILE}
EOSCRIPT

/tmp/ex04.sh

Executing this script produces the following:

** ERROR **: gtkdialog: Error in line 4, near token 'string': syntax error

aborting...
/tmp/ex04.sh: line 18:  5569 Aborted                 (core dumped) gtkdialog --center --file=${MAIN_DIALOG_FILE}

Opps – no banana. Not only that, but the message is pretty cryptic, the line number reference is to a line number in XML GUI definition and the error may not actually be in the line being referenced. With large GUI definitions debugging could be a headache.

One way to try to zoom in on the line number in the XML is to do the following, bearing in mind that the line number in the error message may not be the line number where the error is caused, and that some breakages prevent XML being generated so this may not work at all:

/tmp/ex04.sh --dump | cat -n | more

1    <window>
2      <vbox>
3    <!--
4        <text>
5          <label>I am a window.</label>
6        </text>
7    -->
8        <button ok></button>
9      </vbox>
10    </window>

Note, too, that the script’s error line number is useless – it is the number of the line where gtkdialog command is invoked. This will be particularly useless when we start working with bash functions and errors will be reported with respect to the line numbers inside the functions. But I am getting ahead of myself.

Clearly, standard XML <!– commented out –> syntax does not work.

Let’s try a combination of shell techniques to do the job. Consider this script:

scriptName=ex05
touch /tmp/${scriptName}.sh
chmod ug+x /tmp/${scriptName}.sh

cat <<-'EOSCRIPT' > /tmp/${scriptName}.sh
#!/bin/bash

MAIN_DIALOG_FILE=${0}.gtkd
cat <<-EOUIDFEINITION > ${MAIN_DIALOG_FILE}
<window>
<vbox>
$(:<<-'COMMENT--------------------'
    <text>
      <label>I am a window.</label>
    </text>
COMMENT--------------------
)
<button ok></button>
</vbox>
</window>
EOUIDFEINITION

case ${1} in
-d | --dump) cat ${MAIN_DIALOG_FILE} ;;
*) gtkdialog --center --file=${MAIN_DIALOG_FILE} ;;
esac

rm -f ${MAIN_DIALOG_FILE}
EOSCRIPT

/tmp/${scriptName}.sh --dump

This produces the following:

<window>
<vbox>

<button ok></button>
</vbox>
</window>

Notice that the entire section of XML between and including <text>…</text> tags has gone missing.

Let’s analyse what is happening.

  1. Inner “Here document” syntax “cat <<-EOUIDFEINITION > ${MAIN_DIALOG_FILE}” is different from the outer “Here document” syntax “cat <<-'EOSCRIPT' > /tmp/${scriptName}.sh“. Note the hard quotes around the here document delimiter ‘EOSCRIPT’. Quoting the document delimiter prevents bash from expanding commands and shell variables at the time the document is created. Let’s see what the document looks like after it is created:
     cat /tmp/${scriptName}.sh
#!/bin/bash
MAIN_DIALOG_FILE=${0}.gtkd
cat <<-EOUIDFEINITION > ${MAIN_DIALOG_FILE}
<window>
<vbox>
$(:<<-'COMMENT--------------------'
<text>
<label>I am a window.</label>
</text>
COMMENT--------------------
)
<button ok></button>
</vbox>
</window>
EOUIDFEINITION
case ${1} in
-d | --dump) cat ${MAIN_DIALOG_FILE} ;;
*) gtkdialog --center --file=${MAIN_DIALOG_FILE} ;;
esac
rm -f ${MAIN_DIALOG_FILE}

Note that the variables which would be expanded if the here document delimiter was not hard quoted are not expanded.

  1. At runtime, when this script is executed, the inner “here document” will be created. This time the here document delimiter is not quoted so commands and variables will be expanded by the shell, resulting in the $() construct being executed. This construct is a “execute in a subshell” construct, causing bash to create a subshell and execute whatever is inside $() as a stream of commands right there and then, when the here document is being created, so that whatever output is produced by the subshell, if any, will get incorporated into the here document in its place.
    See, for example, http://unix.stackexchange.com/questions/147420/what-is-in-a-command for a discussion
  2. The command ‘:’ is a no-operation, or a null command for bash so there is no command to execute. Everything following ‘:’ will be ignored except for variable substitution and interpretation of shell-special characters. See http://stackoverflow.com/questions/3224878/what-is-the-purpose-of-the-colon-gnu-bash-builtin for a discussion.
  3. <<-'COMMENT--------------------'” and everything following it until “COMMENT--------------------” is a “here document” which is ignored since ‘:’ causes it to be ignored. Hard-quoting the delimiter “COMMENT--------------------” prevents any $xxxx present in the text form being expanded by the shell and any shell-special characters, like <>|{}, etc. from being interpreted, causing errors

In essence the entire $(….) disappears when the script executes and does not get embedded in the xml document which gtkdialog executes. Great way to comment out large chunks of XML when exploring with alternatives.

Short comments inside gtkdialog XML

Two other techniques can be used to provide comments inside the gtkdialog GUI XML.

I am again running ahead a bit but this is in a good cause 🙂 Each object in gtkdialog except “frame” can be decorated with a bunch of object-specific attributes. Gtkdialog parser is very forgiving of attribute names which it does not recognise so a sneaky way takes advantage of that fact is making it possible to invent a tag name and have its value contain some comment text. Consider the following script:

scriptName=ex06
touch /tmp/${scriptName}.sh
chmod ug+x /tmp/${scriptName}.sh

cat <<-'EOSCRIPT' > /tmp/${scriptName}.sh
#!/bin/bash

MAIN_DIALOG_FILE=${0}.gtkd

cat <<-EOUIDFEINITION > ${MAIN_DIALOG_FILE}
<window xx-window-comment="this is a comment on the window tag">
<vbox>
<text xx-text-comment="I am using an unrecognised attribute to provide a comment">
<label>I am a window.</label>
</text>
<button ok></button>
</vbox>
</window>
EOUIDFEINITION

case ${1} in
-d | --dump) cat ${MAIN_DIALOG_FILE} ;;
*) gtkdialog --center --file=${MAIN_DIALOG_FILE} ;;
esac

rm -f ${MAIN_DIALOG_FILE}
EOSCRIPT

/tmp/${scriptName}.sh

The script happily executed because unknown attributes were ignored yet the comment text is in the XML as can be readily seen:

/tmp/${scriptName}.sh --dump
<window xx-window-comment="this is a comment on the window tag">
<vbox>
<text xx-text-comment="I am using an unrecognised attribute to provide a comment">
<label>I am a window.</label>
</text>
<button ok></button>
</vbox>
</window>

Note that if a future version of gtkdialog tightens up parsing and rejects unknown attribute names then scripts which use this technique will break.

The second method is a variation on the $(:…) technique, discussed above.

Consider the following script

scriptName=ex07
touch /tmp/${scriptName}.sh
chmod ug+x /tmp/${scriptName}.sh

cat <<-'EOSCRIPT' > /tmp/${scriptName}.sh
#!/bin/bash

MAIN_DIALOG_FILE=${0}.gtkd

cat <<-EOUIDFEINITION > ${MAIN_DIALOG_FILE}
<window>
<vbox>
$(: this is a single line comment - to continue it over multiple lines \
one must provide line continuation to shell, \, and carry on. \
The $() construct must be closed with the )
<text>
<label>I am a window.</label>
</text>
<button ok></button>
</vbox>
</window>
EOUIDFEINITION

case ${1} in
-d | --dump) cat ${MAIN_DIALOG_FILE} ;;
*) gtkdialog --center --file=${MAIN_DIALOG_FILE} ;;
esac

rm -f ${MAIN_DIALOG_FILE}
EOSCRIPT

/tmp/${scriptName}.sh

Again, everything inside  $() disappeared.

/tmp/${scriptName}.sh --dump
<window>
<vbox>

<text>
<label>I am a window.</label>
</text>
<button ok></button>
</vbox>
</window>

Note that everything inside $() is expanded by the shell and special characters are acted upon. Including < >, for example, will cause breakage – try it. Consider the following script

scriptName=ex08
touch /tmp/${scriptName}.sh
chmod ug+x /tmp/${scriptName}.sh

cat <<-'EOSCRIPT' > /tmp/${scriptName}.sh
#!/bin/bash

MAIN_DIALOG_FILE=${0}.gtkd

cat <<-EOUIDFEINITION > ${MAIN_DIALOG_FILE}
<window>
<vbox>
$(: this is a single line comment - to continue it over multiple lines \
one must provide line continuation to shell, \, and carry on. \
The $() construct < > must be closed with the )
<text>
<label>I am a window.</label>
</text>
<button ok></button>
</vbox>
</window>
EOUIDFEINITION

case ${1} in
-d | --dump) cat ${MAIN_DIALOG_FILE} ;;
*) gtkdialog --center --file=${MAIN_DIALOG_FILE} ;;
esac

rm -f ${MAIN_DIALOG_FILE}
EOSCRIPT

/tmp/${scriptName}.sh
/tmp/ex08.sh: command substitution: line 6: syntax error near unexpected token `>'
/tmp/ex08.sh: command substitution: line 6: `: this is a single line comment - to continue it over multiple lines         one must provide line continuation to shell, \, and carry on.         The $() construct < > must be closed with the )'

** ERROR **: Error opening file '/tmp/ex08.sh.gtkd': No such file or directory
aborting...
/tmp/ex08.sh: line 19:  6225 Aborted                 (core dumped) gtkdialog --center --file=${MAIN_DIALOG_FILE}

If you use these kinds of comments watch out for shell-special characters. Clearly this kind of comments is not suitable for commenting out chunks of XML. While one can use these kinds comments it seems to me safer to use undefined attributes for short comments and $(:<<-‘….’…) comments for large comments and for commenting out blocks of XML in the gtkdialog GUI definitions.

Apr 09

In this article I discuss my rationale for using gtkdialog and, more importantly, I provide links to a growing list of articles on topics related to gtkdialog – applying styles to gtkdialog applications, examples, exploration of the various gtkdialog objects and suchlike.

Here is the list of my articles on the topic of gtkdialog:

My demos are typically designed to showcase back-end infrastructure, with one or more integrated products delivering specific functionality to client applications. The implication of, and the very point of, the infrastructure is that it does deliver functionality to any front-end client application. It shelters the front-end application from the complexities of integration of multiple components, orchestrating functionality across disparate products and platforms, and the need for the front-end developers to possess the requisite skills to effectively work in such complex, multi-technology environments.

To prove the points I typically have technical artefacts stand in for the client applications, for example using curl (https://curl.haxx.se/) or httpie (https://httpie.org/). This approach strips all the non-essential, tool-induced fluff, and exposes the bare requests and bare responses that the application needs to exchange with the back-end to get the job done. In the case of SOAP Web Services it would be the SOAP Requests and SOAP Responses. In the case of REST web services it would be HTTP Requests, potentially with JSON POST data, and REST Responses.

Alas, there are occasions when audience is not knowledgeable enough to appreciate what the technical artefacts are doing and what that means for the developer of the front-end application.

In the late 1990s early 2000s I was developing very complex and sophisticated web applications for healthcare. I started modestly enough with a web browser-based laboratory results reporting application using HTML 2 and a CERN httpd web server with complex custom backend – see https://blogs.czapski.id.au/wp-content/uploads/2010/03/for_blog_mczapski1.html if you are curious what that looked like then. The paper as it looked when it was hosted by Charles Sturt University in 1997 – https://web.archive.org/web/19970607211224/http://www.csu.edu.au/special/conference/apwww95/papers95/mczapski/mczapski.html – was archived in the Internet Archive by the WayBack Machine – https://archive.org/web/). I progressed to HTML 4, JavaScript, Apache httpd and custom Apache module for all the back-end database access and dynamic HTML generation (that was before the days of AJAX and other server-side technologies, so I invented my own serverside scripting methodology for the occasion).

All this is to say that when I started considering what technology I would use to create demonstration applications to show the client-side of integration to the back-end infrastructure for which I was building the demos I naturally turned to the web technologies. That, it turned out, was a blind alley for me. The time and effort investment necessary to choose from the plethora of competing frameworks, the inevitable need to combine multiple frameworks and technologies (like for example AngularJS, Backbone.io, Node.js, jQuery, etc., to acquire sufficient skills to be reasonably proficient and to get the actual job done seemed excessive. From my point of view the return on investment simply was not there.

What I was after was a tool that would put a reasonably-looking UI on top of the raw request/response exchanges with no need to write 3gl compiled code or Java or such, install execution environments (like node.js, web servers or servlet containers). It would ideally be invoked from the Linux shell with bunch of command line arguments which would make it do what I needed it to do.

I turned to Linux UI tools, starting with zenity (https://help.gnome.org/users/zenity/stable/), progressing through yad (http://www.webupd8.org/2010/12/yad-zenity-on-steroids-display.html) and finally stumbling upon gtkdialog (http://www.tecmint.com/gtkdialog-create-graphical-interfaces-and-dialog-boxes/).

It turned out that yad is ideal for quick dialog boxes of the sort that shows information and asks for OK or Cancel. It can do plenty more than that but I am using it for just such simple things. There is enough information on line to get one up to speed with yad pretty quickly for simple tasks. I built some UIs using yad but I was not satisfied with the rigidity of the component layout mechanism and the consequent lack of control over the layout.

I stumbled on the references to gtkdialog and started exploration. Alas, documentation for gtkdialog is almost non-existent, and what information can be had is scattered over group posts, blog articles and rather opaque reference pages. This alone makes the entry cost pretty high. One has to be fairly motivated to persevere with gtkdialog with no prior experience. I was reasonably motivated but there was a point, which I documented in my internal notes, when I concluded that it was “probably waste of time”. Still I persevered and I built a few pretty sophisticated client applications using gtkdialog and bash – each between 600 and 2000 lines of bash script. If you relate to these numbers then you will appreciate that for me gtkdialog is very attractive. I can develop, test and deploy a pretty sophisticated UI in a couple of days using bash as a scripting language with ready access to all the tools and technologies I need to do the back-end integration quickly and effectively.

Apr 08

In this article I walk through the steps that get the yad, gtkdialog and geany installed and tested.

This is the next article in a series of articles on gtkdialog, “gtkdialog Exploration – articles and examples”, which can be found at https://blogs.czapski.id.au/2017/04/gtkdialog-exploration.

Pre-Requisites

This article assumes that the Virtual Box Machine Image created in accordance with the instructions in the blog article to be found at https://blogs.czapski.id.au/2016/10/configure-virtual-box-virtual-machine-and-install-centos-6-8-base-image is available but it is expected that pretty much any VirtualBox Linux disk will do just as well so long as it supports yad and gtkdialog.

For convenience I posted the export of the VirtualBox image which would have been built if the reader followed all the articles in the series “Build a Linux-based Infrastructure Solution Demonstration Series” to date, that is to the 8th of March 2017. The link to the main article is  https://blogs.czapski.id.au/2016/10/build-a-linux-based-infrastructure-solution-demonstration-series.

In this article I am discussing how yad and gtkdialog can be obtained and installed on CentOS 6.8. yad because I use it for simple OK/Cancel dialog boxes and gtkdialog because I use it for the applications with reasonably sophisticated functionality for which yad would be too cumbersome to use.

Install yad

Download yad from https://sourceforge.net/projects/yad-dialog/, build and install.

cd ~/Downloads
wget https://downloads.sourceforge.net/project/yad-dialog/yad-0.38.2.tar.xz
tar xvf yad-0.38.2.tar.xz
cd yad-0.38.2
autoreconf -ivf
export CFLAGS="$CFLAGS -DPACKAGE_URL='\"http://yad-dialog.sf.net/\"'"
./configure
make
sudo make install

Is there help?

man yad

A simple information dialogue can be constructed as follows:

cat <<-'EODECK' > /tmp/yad01.sh
templateFile=/tmp/xml/GIndProv_template_request.xml
if [ ! -f ${templateFile} ]; then
    yad --title="Required XML Template does not exist" --text="Required XML Template\n${templateFile}\ndoes not exist - aborting" \
    --center --image="gtk-dialog-error" --window-icon="gtk-dialog-error" --width=500 --height=100 --button="gtk-ok:0"
    exit;
fi
EODECK

/bin/sh /tmp/yad01.sh

A more sophisticated dialog might look like this:

cat <<-'EODECK' > /tmp/yad_find_patient.sh
#!/bin/bash

fnUpdateFieldsSBR() {
    echo "3:${title:-MR}"
    echo "4:${lastName:-Smith}"
    echo "5:${gender:-Male}"
    echo "6:${ssn:-123456789}"

    echo "11:${firstName:-John}"
    echo "12:${suffix:-III}"
    echo "13:${dob:-10/10/1978}"
}
export -f fnUpdateFieldsSBR

yad \
--center \
--title="Find Patient by EUID" \
--text="<span size=\"xx-large\">Find Patient Details by EUID</span>\n" \
--form \
--width=550 \
--borders=5 \
--columns=2 \
--date-format="%m/%d/%Y" \
--align=right \
--field="Enterprise Unique ID" "${USAPatXEUIDX:-0001234567}" \
--field="Demographics:LBL" "" \
--field="Title:RO" "" \
--field="Last Name:RO" "" \
--field="Sex:RO" "" \
--field="Social Security Number:RO" "" \
--field="Options:LBL" "" \
--field="Show XML Request?:CHK" "FALSE" \
 \
--field "  Search!gtk-find:FBTN" "@bash -c \"fnUpdateFieldsSBR \"%16\" \"%8\" \"%1\"  \" " \
--field=":LBL" "" \
--field="First Name:RO" "" \
--field="Suffix:RO" "" \
--field="Date of Birth:RO" "" \
--field=".:LBL" "." \
--field=":LBL" "" \
--field="Show XML Response?:CHK" "FALSE" \
 \
--dialog-sep \
--button="Quit!gtk-quit:0"

EODECK
/bin/sh /tmp/yad_find_patient.sh

Install gtkdialog

Download gtkdialog from https://centos.pkgs.org/6/epel-x86_64/gtkdialog-0.8.3-8.el6.x86_64.rpm.html

# get and install the package
cd ~/Downloads
wget http://dl.fedoraproject.org/pub/epel/6/x86_64//gtkdialog-0.8.3-8.el6.x86_64.rpm
sudo yum localinstall -y gtkdialog-0.8.3-8.el6.x86_64.rpm

# get and install soruces for examples and documentation
wget https://fossies.org/linux/privat/old/gtkdialog-0.8.3.tar.gz
cd ~/
tar xvf ./Downloads/gtkdialog-0.8.3.tar.gz

Try a few examples provided in the source distribution to get some notion of what can be accomplished with gtkdialog:

~/gtkdialog-0.8.3/examples/menu/menu
~/gtkdialog-0.8.3/examples/comboboxentry/comboboxentry_advanced
~/gtkdialog-0.8.3/examples/button/button_attributes
~/gtkdialog-0.8.3/examples/notebook/notebook_advanced

Take a look at reference and other documentation:

firefox file:///home/demo/gtkdialog-0.8.3/doc/reference/window.html &
firefox http://gtk.php.net/manual/en/html/gdk/gdk.enum.windowtypehint.html &

Install geany

Download and install geany editor – it beats the gedit and other free alternatives in my opinion

cd ~/Downloads
wget http://download.geany.org/geany-1.24.1.tar.gz

tar xvf geany-1.24.1.tar.gz
cd geany-1.24.1
./autogen.sh
make
sudo make install

Test:

cp -v /home/demo/gtkdialog-0.8.3/examples/window/window /tmp/gtk_window.sh
geany /tmp/gtk_window.sh &

Inspect the code in geany and press F5 to execute it.

gtkdialog hello world

Create a modified script and experiment with the various settings of the window attributes

cat <<-'EODECK' > /tmp/gtk_window.sh
#!/bin/bash
GTKDIALOG=gtkdialog

MAIN_DIALOG='
<window \
decorated="true" \
allow-grow="true" \
allow-shrink="true" \
default-height="200" \
default-width="400" \
resizable="true" xx-resizable="overrides default-height and default-width if set to false" \
deletable="true" xx-deletable="this keep (default) or remove the close button from the window title bar" \
icon-name="gtk-refresh" \
modal="false" \
skip-pager-hint="true" \
skip-taskbar-hint="false" \
title="My First Window" \
window-position="1" \
border-width="5" xx-border-width="is an inherited GtkContainer property - see object hierarchy at https://developer.gnome.org/gtk2/2.24/GtkWindow.html#GtkWindow.object-hierarchy" \
sensitive="true" xx-sensitive="is an inherited GtkWidget property that disables everything inside the window - see obect hierarchy" \
tooltip-markup="This is a <b>window tooltip</b>" xx-tooltip-markup="is inherited from GtkWidget" \
>
    <vbox>
        <frame   Description  >
            <text>
                <label>This is an example window.</label>
            </text>
        </frame>
        <hbox>
            <button ok></button>
            <button cancel></button>
        </hbox>
    </vbox>
</window>
'
export MAIN_DIALOG
case $1 in
    -d | --dump) echo "$MAIN_DIALOG" ;;
    *) $GTKDIALOG --program=MAIN_DIALOG ;;
esac
EODECK

/tmp/gtk_window.sh

Experiment with the setting of the Window object attributes and see what effect the changes have on the appearance and behaviour of the window.

There is plenty more that can be done with gtkdialog. I intend to write more articles on this topic as the time goes by. This article provides the tooling for these later articles.

Apr 08

Introduction

Please see the article “Build a Linux-based Infrastructure Solution Demonstration Series” (https://blogs.czapski.id.au/2016/10/build-a-linux-based-infrastructure-solution-demonstration-series) for how this image came about and “gtkdialog Exploration” (https://blogs.czapski.id.au/2017/04/gtkdialog-exploration) for the rationale, introduction and links to articles in this series.

In this post I provide the link to the 7zip archive parts which put together will provide the VirtualBox “.ova” export file. The “.ova” can be imported into the VirtualBox 5.1.x as a new VirtualBox Machine Image.

This VirtualBox Machine Image has been built following steps in the series of blog articles under the collective heading of “Build a Linux-based Infrastructure Solution Demonstration Series”, to be found at https://blogs.czapski.id.au/2016/10/build-a-linux-based-infrastructure-solution-demonstration-series. It can be used as the basis for exploring other topics discussed in subsequent blog articles whether in this series or in related series.

Pre-Requisites

This article assumes that the Virtual Box 5.1.x software (https://www.virtualbox.org/) for the appropriate host OS has been installed.

Download Image Parts

The 7zip archive consists of 4 parts of around 650MB each, for a total of reconstructed .ova file of 2.5GB. The imported machine will take about 6GB of disk space.

Download the following to a suitable directory:

https://blogs.czapski.id.au/wp-content/uploads/2017/04/demo_v1.1.3_20170408.7z.001

https://blogs.czapski.id.au/wp-content/uploads/2017/04/demo_v1.1.3_20170408.7z.002

https://blogs.czapski.id.au/wp-content/uploads/2017/04/demo_v1.1.3_20170408.7z.003

https://blogs.czapski.id.au/wp-content/uploads/2017/04/demo_v1.1.3_20170408.7z.004

Combine Parts into .ova

Copy the parts to a suitable directory in a file system with at least 3 GB of free space.

Open the first part with an archiver capable of dealing with 7zip archives and extract the file demo_v1.1.3_20170408.ova.

On Windows in a command box the command might be like the following (substitute drive letters and paths as appropriate, noting that -oO:\ points to the output drive and directory and that there is a space between it and the name of the output file which follows):

"C:\Program Files\7-Zip\7z.exe" e D:\demo_v1.1.3_20170408.7z.001 -oO:\ demo_v1.1.3_20170408.ova -y

 

7-Zip [64] 16.04 : Copyright (c) 1999-2016 Igor Pavlov : 2016-10-04

Scanning the drive for archives:
1 file, 681574400 bytes (650 MiB)

Extracting archive: D:\demo_v1.1.3_20170408.7z.001
--
Path = D:\demo_v1.1.3_20170408.7z.001
Type = Split
Physical Size = 681574400
Volumes = 4
Total Physical Size = 2546348186
----
Path = demo_v1.1.3_20170408.7z
Size = 2546348186
--
Path = demo_v1.1.3_20170408.7z
Type = 7z
Physical Size = 2546348186
Headers Size = 154
Method = Copy
Solid = -
Blocks = 1

Everything is Ok

Size:       2546348032
Compressed: 2546348186

Import Image

Use the VirtualBox Manager UI to import the .ova file.

Before you do, check where in your file system the machine directory will be created and change as appropriate, bearing in mind that the target directory must be on a device with at least 7GB of free space or that the directory has a quote of at least 7GB of space if your environment uses disk space quotas:

File->Preferences->General->Default Machine Folder: ???

Once you are happy that the machine will go into the directory appropriate for your environment import the machine image:

File->Import Appliance…

Locate the .ova file, change whatever machine settings seem in need of change, like increase the amount of memory, the number of CPUs, etc., and Import.

Agree to the copyright statement (in which I claim no copyright or any other rights).

On my machine the process takes about 2 minutes and produces a machine image directory which uses approximately 6.6GB of space.

Make and use Snapshots

So that I have a known place to revert to if I mess something up, the first this I always do after importing an image is to take a “as imported” snapshot. I also take snapshots at various points in my work so that I don’t have too much to re-do if I make am mistake and have to roll back and re-do some work. I recommend that you consider this as a piece of good advice.

073_import_vm.png

Credentials

As discussed in the article “Configure Virtual Box Virtual Machine and Install CentOS 6.8 Base Image”, https://blogs.czapski.id.au/2016/10/configure-virtual-box-virtual-machine-and-install-centos-6-8-base-image, all passwords configured to this point are “welcome1”. That includes the root password. Change as necessary.

The image is configured in such a way that on boot the user “demo” will be automatically logged in. That user is in the sudoers file and has passwordless sudo rights.

Dec 27

Introduction

Please see the article “Build a Linux-based Infrastructure Solution Demonstration Series” (https://blogs.czapski.id.au/2016/10/build-a-linux-based-infrastructure-solution-demonstration-series) for rationale, introduction and links to articles in this series.

Ordinarily the demos I create use desktop backgrounds to guide the demonstration flow by “behaving” in a manner similar to a slideshow. When I double-click and “next” arrow/icon the desktop background is replaced by the “next” desktop background, or by “previous” desktop background if I double-click the “previous” arrow/icon. This will be discussed and shown in another article in this series.

For this to work effectively the standard desktop icons provided by Gnome must not be visible. Rather than trying to find a way of deleting these icons I hide them, which is possible in gnome 2.

Similarly, the top and bottom tool bars, which clearly show the Linux Gnome origin of the desktop, could usefully be hidden to eliminate distraction if top panel menus are not used in the demonstration and if bottom panel objects are not used in the demonstration. This, too, is possible.

In this post I discuss how to script hiding the standard desktop icons and how to script configuration of top and bottom panels so that they are able to be hidden.

Pre-Requisites

This article assumes that the Virtual Box Machine Image created in accordance with the instructions in the blog article to be found at https://blogs.czapski.id.au/2016/10/configure-virtual-box-virtual-machine-and-install-centos-6-8-base-image is available but it is expected that pretty much any VirtualBox Linux disk will do just as well so long as it runs Gnome 2 desktop.

Hide desktop icons

Our gnome desktop has a bunch of icons which will get in the way if we use a specific set of backgrounds to guide the demonstration.

Let’s create a script that will make standard desktop icons disappear

cat <<-'EODECK' > /media/sf_distros/scripts/014_hide_desktop_icons.sh

gconftool-2 -s -t bool /apps/nautilus/desktop/home_icon_visible "false"
gconftool-2 -s -t bool /apps/nautilus/desktop/volumes_visible "false"
gconftool-2 -s -t bool /apps/nautilus/desktop/trash_icon_visible "false"
gconftool-2 -s -t bool /apps/nautilus/desktop/computer_icon_visible "false"
EODECK
chmod ug+x /media/sf_distros/scripts/014_hide_desktop_icons.sh

Let’s execute the script to hide desktop icons

/bin/bash -v /media/sf_distros/scripts/014_hide_desktop_icons.sh

The icons are now invisible.

Executing commands in the script with “true” rather than “false” will make the icons re-appear.

Make top and bottom panels hideable

Our gnome desktop has a visible top panel and a visible bottom panel, which tell anyone somewhat familiar with IT that what they see is a Linux Gnome desktop.

This might be distracting if we don’t use top panel menus or bottom panel objects in our demonstration.

Ideally, since we might want to interact with the menus and objects when we are building the demo or preparing for a demo, the top and bottom panels should be accessible on demand.

The script shown below will add hide/show “button” at the extreme right of each panel. This button will be visible regardless of whether the panel is hidden or shown.

Let’s create and execute a script that will make top bar and bottom bar hideable.

cat <<-'EODECK' > /media/sf_distros/scripts/015_make_gnome_panels_hideable.sh
gconftool-2 -s -t bool /apps/panel/toplevels/top_panel/enable_buttons true
gconftool-2 -s -t bool /apps/panel/toplevels/top_panel/auto_hide false
gconftool-2 -s -t bool /apps/panel/toplevels/bottom_panel/enable_buttons true
EODECK
chmod gu+x /media/sf_distros/scripts/015_make_gnome_panels_hideable.sh

Let’s execute the script

/bin/bash /media/sf_distros/scripts/015_make_gnome_panels_hideable.sh

Note the middle line in the script – auto_hide false. If you change false to true then the top panel will auto-hide and will appear when the cursor is moved up to the top of the screen.

Give it a try and see how it works.

Add to initial bulk configuration script

It is expected that the image being configured a bit at a time in this series of articles will be created more than once for different purposes. With this assumptions the individual scripts are appended to a single script so that the second and subsequent images can be configured by a single script rather than having lots of scripts to execute manually.

Don’t actually execute this script while you are building the first image.

cat <<-'EODECK' >> /media/sf_distros/scripts/000_initial_bulk_configuration.sh
# update hide desktop icons
/bin/bash -v /media/sf_distros/scripts/014_hide_desktop_icons.sh

# make desktop panels hideable
/bin/bash -v /media/sf_distros/scripts/015_make_gnome_panels_hideable.sh

EODECK
chmod ug+x /media/sf_distros/scripts/000_initial_bulk_configuration.sh

 

Dec 24

Introduction

Please see the article “Build a Linux-based Infrastructure Solution Demonstration Series” (https://blogs.czapski.id.au/2016/10/build-a-linux-based-infrastructure-solution-demonstration-series) for rationale, introduction and links to articles in this series.

Ordinarily I create a dynamic virtual disk with the potential to grow to perhaps 120Gb In size. Since the disk I ordinarily create is dynamic the host disk space it needs gets allocated as needed, thus the disk may never actually grow to the defined size.

There may be circumstances, for example if one needs to create a raw disk for import into 3rd party virtualisation platform, when the size of the disk must be reduced to conform to externally imposed constraints.

In this post I discuss how a virtual disk with a working installation can be reduced in size while remaining useable. This will be accomplished using the virtual disk which was built up in this series of articles.

Pre-Requisites

This article assumes that the Virtual Box Machine Image created in accordance with the instructions in the blog article to be found at https://blogs.czapski.id.au/2016/10/configure-virtual-box-virtual-machine-and-install-centos-6-8-base-image is available but it is expected that pretty much any VirtualBox Linux disk will do just as well.

The instructions should work in other RedHat 6-like OS’ and OS versions.

Discussion

Let’s assume that we need to shrink a virtual disk of a configured Virtual Box Virtual Machine. This disk will be partitioned, if you are using the disk image created in the series of articles of which this one is a part, into two partitions. The partitions are the swap partition, 8GB in size, at the beginning of the disk, and the data partition, occupying the rest of the disk.

Boot the demo image. log in, and execute the following command in a terminal window to see how the disk is partitioned:

lsblk

I see:

NAME   MAJ:MIN RM   SIZE RO TYPE MOUNTPOINT
sda      8:0    0 117.2G  0 disk
??sda1   8:1    0     8G  0 part [SWAP]
??sda2   8:2    0 109.2G  0 part /
sr0     11:0    1  1024M  0 rom

There is one disk device, sda, which has 117.2GB of useable space, which has two partitions – sda1 is a swap partition of 8GB and sda2 is a file system partition of 102.2GB.

Let’s find out how much of the file system partition is actually used:

df -h

I see:

Filesystem      Size  Used Avail Use% Mounted on
/dev/sda2       108G  5.8G   97G   6% /
tmpfs           1.9G   80K  1.9G   1% /dev/shm

Let’s say that we don’t expect to use more than another 20GB so we can see that if we shrink the partition to around 30GB we will have plenty of room for what we expect to be doing.

The process involves shrinking the source disk’s partition and cloning the disk with a smaller partition, then discarding the original disk and using the smaller disk in its place.

To accomplish the task we will go through the following steps:

  1. Export the VM to a .ova archive – this will cause any differencing disks / snapshots to be applied and will give us a complete source virtual disk in the archive – this will also serve as a backup if we have issues shrinking the disk
  2. Import the exported VM image to create a new VM – we need a VM to manipulate the source virtual disk so we will use the one created by the import
  3. Create a blank, 40GB Virtual Disk to use as target
  4. Attach the new blank disk to the new VM – this will give us access to both the source and the target disks – source to shrink and target to copy the shrunk partition to it
  5. Boot from GParted Live CD – this will give us access to the GParted tools and to the virtual disk which to shrink
  6. Shrink the source disk file system partition to 30GB
  7. Partition the target disk
  8. Copy source disk’s swap partition to the target disk
  9. Copy source disk’s file system partition to the target disk
  10. Reboot with the CentOS Install disk in the DVD drive to have access to system rescue facilities
  11. Fix the Grub Bootloader
  12. Reboot with just the target disk attached

Resources

The resources we need are the Centos install ISO, which we should have from our earlier work, http://centos.mirror.digitalpacific.com.au/6.8/isos/x86_64/CentOS-6.8-x86_64-bin-DVD1.iso and GParted ISO, from http://downloads.sourceforge.net/project/gparted/gparted-live-stable/0.27.0-1/gparted-live-0.27.0-1-i686.iso?r=http%3A%2F%2Fgparted.sourceforge.net%2F&ts=1482278033&use_mirror=internode

Assuming that you don’t have the ISOs, in a terminal widow, execute the following commands:

wget http://centos.mirror.digitalpacific.com.au/6.8/isos/x86_64/CentOS-6.8-x86_64-bin-DVD1.iso -O /media/sf_distros/CentOS-6.8-x86_64-bin-DVD1.iso

wget http://downloads.sourceforge.net/project/gparted/gparted-live-stable/0.27.0-1/gparted-live-0.27.0-1-i686.iso -O /media/sf_distros/gparted-live-0.27.0-1-i686.iso

Export Virtual Disk

Let’s find out how our “demo” Virtual Machine is configured (assuming our host is a Windows OS – adjust as required if your host is a Unix-like OS).

@rem see what the VM looks like
set VBM="C:\Program Files\Oracle\VirtualBox\VBoxManage.exe"
%VBM% showvminfo "demo"

I see

Name:            demo
Groups:          /
Guest OS:        Red Hat (64-bit)
UUID:            a9a4e4a6-77d3-4c7c-abb1-024291c8bbf4
Config file:     d:\VirtualBoxDisks\demo\demo.vbox
Snapshot folder: d:\VirtualBoxDisks\demo\Snapshots
Log folder:      d:\VirtualBoxDisks\demo\Logs
Hardware UUID:   a9a4e4a6-77d3-4c7c-abb1-024291c8bbf4
Memory size:     4096MB
Page Fusion:     off
VRAM size:       128MB
CPU exec cap:    100%
HPET:            off
Chipset:         piix3
Firmware:        BIOS
Number of CPUs:  3
PAE:             on
Long Mode:       on
Triple Fault Reset: off
APIC:            on
X2APIC:          on
CPUID Portability Level: 0
CPUID overrides: None
Boot menu mode:  message and menu
Boot Device (1): DVD
Boot Device (2): HardDisk
Boot Device (3): Not Assigned
Boot Device (4): Not Assigned
ACPI:            on
IOAPIC:          on
BIOS APIC mode:  APIC
Time offset:     0ms
RTC:             UTC
Hardw. virt.ext: on
Nested Paging:   on
Large Pages:     on
VT-x VPID:       on
VT-x unr. exec.: on
Paravirt. Provider: Default
Effective Paravirt. Provider: KVM
State:           running (since 2016-12-23T23:48:11.544000000)
Monitor count:   1
3D Acceleration: on
2D Video Acceleration: off
Teleporter Enabled: off
Teleporter Port: 0
Teleporter Address:
Teleporter Password:
Tracing Enabled: off
Allow Tracing to Access VM: off
Tracing Configuration:
Autostart Enabled: off
Autostart Delay: 0
Default Frontend:
Storage Controller Name (0):            IDE
Storage Controller Type (0):            PIIX4
Storage Controller Instance Number (0): 0
Storage Controller Max Port Count (0):  2
Storage Controller Port Count (0):      2
Storage Controller Bootable (0):        on
IDE (0, 0): d:\VirtualBoxDisks\demo\Snapshots\{c8e1a17f-6271-483f-870f-3a691a93a635}.vdi (UUID: c8e1a17f-6271-483f-870f-3a691a93a635)
IDE (1, 0): Empty
NIC 1:           MAC: 08002753AFFF, Attachment: NAT, Cable connected: on, Trace: off (file: none), Type: 82540EM, Reported speed: 10000 Mbps
, Boot priority: 0, Promisc Policy: deny, Bandwidth group: none
NIC 1 Settings:  MTU: 0, Socket (send: 64, receive: 64), TCP Window (send:64, receive: 64)
NIC 2:           disabled
NIC 3:           disabled
NIC 4:           disabled
NIC 5:           disabled
NIC 6:           disabled
NIC 7:           disabled
NIC 8:           disabled
Pointing Device: PS/2 Mouse
Keyboard Device: PS/2 Keyboard
UART 1:          disabled
UART 2:          disabled
UART 3:          disabled
UART 4:          disabled
LPT 1:           disabled
LPT 2:           disabled
Audio:           enabled (Driver: DSOUND, Controller: AC97, Codec: STAC9700)
Clipboard Mode:  Bidirectional
Drag and drop Mode: disabled
Session name:    GUI/Qt
Video mode:      1600x900x32 at 0,0 enabled
VRDE:            disabled
USB:             enabled
EHCI:            enabled
XHCI:            disabled

USB Device Filters:
<none>

Available remote USB devices:
<none>

Currently Attached USB Devices:
<none>

Bandwidth groups:  <none>

Shared folders:
Name: 'distros', Host path: 'O:\DemoBuilding\Distros' (machine mapping), writable
VRDE Connection:    not active
Clients so far:     0

Video capturing:    not active
Capture screens:    0
Capture file:       d:\VirtualBoxDisks\demo\demo.webm
Capture dimensions: 1024x768
Capture rate:       512 kbps
Capture FPS:        25

Guest:

Configured memory balloon size:      0 MB
OS type:                             Linux26_64
Additions run level:                 2
Additions version:                   5.1.8 r111374

Guest Facilities:

Facility "VirtualBox Base Driver": active/running (last update: 2016/12/23 23:48:22 UTC)
Facility "VirtualBox System Service": active/running (last update: 2016/12/23 23:48:31 UTC)
Facility "Seamless Mode": active/running (last update: 2016/12/23 23:48:22 UTC)
Facility "Graphics Mode": active/running (last update: 2016/12/23 23:48:22 UTC)

Snapshots:
   Name: v1.0.0 Baseline - CentOS 6.8 Installed (UUID: dedfb09a-965b-42e5-898e-e2d207dca628)
   Description:
v1.0.0 Baseline - CentOS 6.8 Installed
      Name: v1.0.8 Update DHCP address in /etc/hosts at boot (UUID: 6473898e-c9e4-4d60-8c5e-5e16187e5125)
         Name: v1.0.9 Script adding a new gnome-terminal profile (UUID: 228ef39e-81ac-49ff-a5ee-7be9977899b7)
            Name: v1.1.0 Installed CentOS 6.8 and configured root environment (UUID: 4ddc0470-b586-44ad-908e-2a7af518de43)
               Name: Snapshot 1 (UUID: 5e5463f0-7fbb-472d-8473-bca3924e19f7)
               Name: v1.1.1 Script creating a new demo user (UUID: 1089b3c9-8e93-4d1c-a480-0eb745275915) *

Note that the disk has a bunch of snapshots so we need to export the machine to “flatten” them out into a complete virtual disk in a single file.

Make sure the source virtual machine is shut down.

Assuming that we use the directory on Windows which we use for software distributions, let’s create a directory for the export and export the virtual machine:

@rem create output directory and export disk image
mkdir O:\DemoBuilding\Distros\demo_export
set VBM="C:\Program Files\Oracle\VirtualBox\VBoxManage.exe"
%VBM% export "demo" --output O:\DemoBuilding\Distros\demo_export\demo_export.ova --ovf20 --manifest --options manifest,nomacs

I see

0%...10%...20%...30%...40%...50%...60%...70%...80%...90%...100%
Successfully exported 1 machine(s).

Import the exported VM

Let’s find out how the Virtual Machine export looks like (assuming our host is a Windows OS – adjust as required if your host is a Unix-like OS).

@rem import ova as --dry-run to see what a real import would look like
set VBM="C:\Program Files\Oracle\VirtualBox\VBoxManage.exe"
%VBM% import O:\DemoBuilding\Distros\demo_export\demo_export.ova  --dry-run

I see

0%...10%...20%...30%...40%...50%...60%...70%...80%...90%...100%
Interpreting O:\DemoBuilding\Distros\demo_export\demo_export.ova...
OK.
Disks:
  vmdisk1       0       -1      http://www.vmware.com/interfaces/specifications/vmdk.html#streamOptimized demo_export-disk1.vmdk  -1
-1

Virtual system 0:
 0: Suggested OS type: "RedHat_64"
    (change with "--vsys 0 --ostype <type>"; use "list ostypes" to list all possible values)
 1: Suggested VM name "demo_1"
    (change with "--vsys 0 --vmname <name>")
 2: Number of CPUs: 3
    (change with "--vsys 0 --cpus <n>")
 3: Guest memory: 4096 MB
    (change with "--vsys 0 --memory <MB>")
 4: Sound card (appliance expects "", can change on import)
    (disable with "--vsys 0 --unit 4 --ignore")
 5: USB controller
    (disable with "--vsys 0 --unit 5 --ignore")
 6: Network adapter: orig NAT, config 3, extra slot=0;type=NAT
 7: CD-ROM
    (disable with "--vsys 0 --unit 7 --ignore")
 8: IDE controller, type PIIX4
    (disable with "--vsys 0 --unit 8 --ignore")
 9: IDE controller, type PIIX4
    (disable with "--vsys 0 --unit 9 --ignore")
10: Hard disk image: source image=demo_export-disk1.vmdk, target path=o:\VirtualBoxDisks\demo_1\demo_export-disk1.vmdk, controller=8;channel
=0
    (change target path with "--vsys 0 --unit 10 --disk path";
    disable with "--vsys 0 --unit 10 --ignore")

Note that the name the imported VM would be give would be demo_1. This is not what we want so we will tell import to change it to demo2. There are a bunch of other settings that can be changed during import. The Virtual Box User Manual provides information on these options.

Let’s import the VM with the new name.

set VBM="C:\Program Files\Oracle\VirtualBox\VBoxManage.exe"
%VBM% import O:\DemoBuilding\Distros\demo_export\demo_export.ova --vsys 0 --vmname "demo2" --description "Shrunk VM"

I see:

0%...10%...20%...30%...40%...50%...60%...70%...80%...90%...100%
Interpreting O:\DemoBuilding\Distros\demo_export\demo_export.ova...
OK.
Disks:
  vmdisk1       0       -1      http://www.vmware.com/interfaces/specifications/vmdk.html#streamOptimized       demo_export-disk1.vmdk  -1
-1

Virtual system 0:
 0: Suggested OS type: "RedHat_64"
    (change with "--vsys 0 --ostype <type>"; use "list ostypes" to list all possible values)
 1: VM name specified with --vmname: "demo2"
 2: Number of CPUs: 3
    (change with "--vsys 0 --cpus <n>")
 3: Guest memory: 4096 MB
    (change with "--vsys 0 --memory <MB>")
 4: Sound card (appliance expects "", can change on import)
    (disable with "--vsys 0 --unit 4 --ignore")
 5: USB controller
    (disable with "--vsys 0 --unit 5 --ignore")
 6: Network adapter: orig NAT, config 3, extra slot=0;type=NAT
 7: CD-ROM
    (disable with "--vsys 0 --unit 7 --ignore")
 8: IDE controller, type PIIX4
    (disable with "--vsys 0 --unit 8 --ignore")
 9: IDE controller, type PIIX4
    (disable with "--vsys 0 --unit 9 --ignore")
10: Hard disk image: source image=demo_export-disk1.vmdk, target path=o:\VirtualBoxDisks\demo_1\demo_export-disk1.vmdk, controller=8;channel
=0
    (change target path with "--vsys 0 --unit 10 --disk path";
    disable with "--vsys 0 --unit 10 --ignore")
0%...10%...20%...30%...40%...50%...60%...70%...80%...90%...100%
Successfully imported the appliance.

Let’s see what the new VM looks like

set VBM="C:\Program Files\Oracle\VirtualBox\VBoxManage.exe"
%VBM% showvminfo "demo2"

I see:

Name:            demo2
Groups:          /
Guest OS:        Red Hat (64-bit)
UUID:            76bd82ae-c445-41a8-88d9-1c75ef422b89
Config file:     o:\VirtualBoxDisks\demo2\demo2.vbox
Snapshot folder: o:\VirtualBoxDisks\demo2\Snapshots
Log folder:      o:\VirtualBoxDisks\demo2\Logs
Hardware UUID:   76bd82ae-c445-41a8-88d9-1c75ef422b89
Memory size:     4096MB
Page Fusion:     off
VRAM size:       128MB
CPU exec cap:    100%
HPET:            off
Chipset:         piix3
Firmware:        BIOS
Number of CPUs:  3
PAE:             on
Long Mode:       on
Triple Fault Reset: off
APIC:            on
X2APIC:          on
CPUID Portability Level: 0
CPUID overrides: None
Boot menu mode:  message and menu
Boot Device (1): DVD
Boot Device (2): HardDisk
Boot Device (3): Not Assigned
Boot Device (4): Not Assigned
ACPI:            on
IOAPIC:          on
BIOS APIC mode:  APIC
Time offset:     0ms
RTC:             UTC
Hardw. virt.ext: on
Nested Paging:   on
Large Pages:     on
VT-x VPID:       on
VT-x unr. exec.: on
Paravirt. Provider: Default
Effective Paravirt. Provider: KVM
State:           powered off (since 2016-12-24T00:27:28.000000000)
Monitor count:   1
3D Acceleration: on
2D Video Acceleration: off
Teleporter Enabled: off
Teleporter Port: 0
Teleporter Address:
Teleporter Password:
Tracing Enabled: off
Allow Tracing to Access VM: off
Tracing Configuration:
Autostart Enabled: off
Autostart Delay: 0
Default Frontend:
Storage Controller Name (0):            IDE
Storage Controller Type (0):            PIIX4
Storage Controller Instance Number (0): 0
Storage Controller Max Port Count (0):  2
Storage Controller Port Count (0):      2
Storage Controller Bootable (0):        on
IDE (0, 0): o:\VirtualBoxDisks\demo_1\demo_export-disk1.vmdk (UUID: ee418695-52a7-45ee-a8fe-c1ba5d9568f7)
IDE (1, 0): Empty
NIC 1:           MAC: 080027CB8540, Attachment: NAT, Cable connected: on, Trace: off (file: none), Type: 82540EM, Reported speed: 10000 Mbps
, Boot priority: 0, Promisc Policy: deny, Bandwidth group: none
NIC 1 Settings:  MTU: 0, Socket (send: 64, receive: 64), TCP Window (send:64, receive: 64)
NIC 2:           disabled
NIC 3:           disabled
NIC 4:           disabled
NIC 5:           disabled
NIC 6:           disabled
NIC 7:           disabled
NIC 8:           disabled
Pointing Device: PS/2 Mouse
Keyboard Device: PS/2 Keyboard
UART 1:          disabled
UART 2:          disabled
UART 3:          disabled
UART 4:          disabled
LPT 1:           disabled
LPT 2:           disabled
Audio:           enabled (Driver: DSOUND, Controller: AC97, Codec: STAC9700)
Clipboard Mode:  Bidirectional
Drag and drop Mode: disabled
VRDE:            disabled
USB:             enabled
EHCI:            enabled
XHCI:            disabled

USB Device Filters:
<none>

Bandwidth groups:  <none>

Shared folders:
Name: 'distros', Host path: 'O:\DemoBuilding\Distros' (machine mapping), writable

Video capturing:    not active
Capture screens:    0
Capture file:       o:\VirtualBoxDisks\demo2\demo2.webm
Capture dimensions: 1024x768
Capture rate:       512 kbps
Capture FPS:        25

Guest:

Configured memory balloon size:      0 MB

Let’s boot the machine, make sure that it works, and shut it down again:

@rem start the vm, inspect and shut down
set VBM="C:\Program Files\Oracle\VirtualBox\VBoxManage.exe"
%VBM% startvm "demo2" --type gui

Shrink and clone the source disk

Create a blank, 40GB Virtual Disk to use as target

We need a target disk to which to “clone” the resized source disk partition. Let’s create one.

@rem Create a virtual disk of the right size - 40GB - as target for data move
set VBM="C:\Program Files\Oracle\VirtualBox\VBoxManage.exe"
set diskFile=o:\VirtualBoxDisks\demo2\demo2-disk1.vmdk
%VBM% createmedium disk --filename %diskFile% --size 40000 --format VMDK --variant Standard

I see:

0%...10%...20%...30%...40%...50%...60%...70%...80%...90%...100%
Medium created. UUID: 6bf53b79-630d-41c7-8c98-cf54313d5d7f

Attach the new blank disk to the new VM

Let’s attach the new blank disk to the new VM. This will give us access to both the source and the target disks – source to shrink and target to copy the shrunk partition to.

set VBM="C:\Program Files\Oracle\VirtualBox\VBoxManage.exe"
set diskFile=o:\VirtualBoxDisks\demo2\demo2-disk1.vmdk
%VBM% storageattach "demo2" --storagectl "IDE" --port 0 --device 1 --type hdd --medium hdd --medium %diskFile%

Let’s see what this did:

set VBM="C:\Program Files\Oracle\VirtualBox\VBoxManage.exe"
%VBM% showvminfo "demo2" | find "IDE"
Storage Controller Name (0):            IDE
IDE (0, 0): o:\VirtualBoxDisks\demo_1\demo_export-disk1.vmdk (UUID: ee418695-52a7-45ee-a8fe-c1ba5d9568f7)
IDE (0, 1): o:\VirtualBoxDisks\demo2\demo2-disk1.vmdk (UUID: 6bf53b79-630d-41c7-8c98-cf54313d5d7f)
IDE (1, 0): Empty

Boot from GParted Live CD

We need to boot this VM from GParted Live CD. This will give us access to the GParted tools, to the virtual disk which to shrink and to the target disk to which to copy the source partition.

@rem attach GParted ISO
set VBM="C:\Program Files\Oracle\VirtualBox\VBoxManage.exe"
%VBM% storageattach "demo2" --storagectl "IDE" --port 1 --device 0 --type dvddrive --medium O:\DemoBuilding\Distros\gparted-live-0.27.0-1-i686.iso

set VBM="C:\Program Files\Oracle\VirtualBox\VBoxManage.exe"
%VBM% showvminfo "demo2" | find "IDE"
Storage Controller Name (0):            IDE
IDE (0, 0): o:\VirtualBoxDisks\demo_1\demo_export-disk1.vmdk (UUID: ee418695-52a7-45ee-a8fe-c1ba5d9568f7)
IDE (0, 1): o:\VirtualBoxDisks\demo2\demo2-disk1.vmdk (UUID: 6bf53b79-630d-41c7-8c98-cf54313d5d7f)
IDE (1, 0): O:\DemoBuilding\Distros\gparted-live-0.27.0-1-i686.iso (UUID: 7aeea2b7-f91e-43a3-8104-12b23dc4a0c0)

Boot from the GParted ISO

set VBM="C:\Program Files\Oracle\VirtualBox\VBoxManage.exe"
%VBM% startvm "demo2"

Press enter to choose GPated Live

Choose OK to “Don’t touch keymap”.

Choose 33 (US English) for Language and choose 0 to continue to start X to use GParted automatically

GParted UI will appear. Proceed to the next section.

Shrink the source disk file system partition to 30GB

Make sure that the device being operated on is /dev/sda (top right corner of the GParted UI.

Click on the line describing device /dev/sda2 to select it, then click on the Resize/Move button.

Enter 30000 as New Size in MiB, press Tab to advance to the next field and click Resize/Move button.

Click Apply and then Apply again to confirm and to start the process.

Review the feedback and click Close (assuming this worked, which it should)

The source file system partition has be resized, leaving a considerable amount of disk unallocated. Follow next sections to clone the partitions to the new disk.

Partition the target disk

In this section we will partition the target disk, which has not yet been partitioned, so that it has the same number, type and layout of partitions as the source disk.

Pull down the devices drop down, top right corner, and choose the /dev/sdb device – the target disk.

Choose Device–>Create Partition Table …

Choose MSDOS and click Apply

Copy source disk’s swap partition to the target disk

In this section we will copy the swap partition from the source disk, /dev/sda, to the target disk, /dev/sdb, which has a large amount of unallocated space.

Pull down the devices drop down, top right corner, and choose the /dev/sda device – the source disk.

Click the line with /dev/sda1 to select the swap partition and click Copy.

 

Pull down the devices drop down, top right corner, and choose the /dev/sdb device – the target disk, then click Paste.

Leave the size and other aspects of the partition as they are and click the Paste button in the dialogue box.

Click the Apply button in the top button bar to execute the copy, and click Apply again.

Once the copy is completed and you inspected feedback information click the Close button to close the dialogue box

Copy source disk’s file system partition to the target disk

In this section we will copy file system partition from the source disk, /dev/sda, to the target disk, /dev/sdb, which has a large amount of unallocated space.

Pull down the devices drop down, top right corner, and choose the /dev/sda device – the source disk.

Click the line with /dev/sda2 to select the file system partition and click Copy.

Pull down the devices drop down, top right corner, and choose the /dev/sdb device – the target disk, then click Paste.

Adjust the size of the target partition using the slider until the Free space following is 0, then click the Paste button in the dialogue box

Click the Apply button in the top button bar to execute the action. This will take some time, which will vary with the size of the disk.

Once the copy is completed and you inspected feedback information click the Close button to close the dialogue box

Quit GParted.

Reboot with the CentOS Install disk in the DVD drive to have access to system rescue facilities

Double click the Close button, top left, on the desktop to eject the GParted ISO and shut down the image.

Detach the source drive.

set VBM="C:\Program Files\Oracle\VirtualBox\VBoxManage.exe"
set diskFile=o:\VirtualBoxDisks\demo2\demo2-disk1.vmdk
%VBM% storageattach "demo2" --storagectl "IDE" --port 0 --device 0 --type hdd --medium none

Attach CentOS Install ISO

@rem attach CentOS install ISO
set VBM="C:\Program Files\Oracle\VirtualBox\VBoxManage.exe"
%VBM% storageattach "demo2" --storagectl "IDE" --port 1 --device 0 --type dvddrive --medium O:\DemoBuilding\Distros\CentOS-6.8-x86_64-bin-DVD1.iso
%VBM% showvminfo "demo2" | find "IDE"
Storage Controller Name (0):            IDE
IDE (0, 1): o:\VirtualBoxDisks\demo2\demo2-disk1.vmdk (UUID: 6bf53b79-630d-41c7-8c98-cf54313d5d7f)
IDE (1, 0): O:\DemoBuilding\Distros\CentOS-6.8-x86_64-bin-DVD1.iso (UUID: c5ace9b9-edb3-4796-82c6-374c78991750)

Boot the image

@rem boot
%VBM% startvm "demo2"

Follow next section to fix the grub boot loader

Fix the Grub Bootloader

Choose the Rescue Installed System option and press enter

Choose Language English and OK

Choose Keyboard Type us and OK

Choose Setup Networking NO

Choose Rescue Continue

Choose OK

Choose OK

Choose Start Shell

Execute the following commands

chroot /mnt/sysimage
/sbin/grub-install /dev/sda
exit
exit

Choose Reboot to get back to the install menu

In Virtual Box menu bar click the close button and choose Power off the machine to stop the VM

Reboot with just the target disk attached

Detach the installation ISO and boot the image

@rem attach CentOS install ISO
set VBM="C:\Program Files\Oracle\VirtualBox\VBoxManage.exe"
%VBM% storageattach "demo2" --storagectl "IDE" --port 1 --device 0 --type dvddrive --medium none

Boot the image

@rem boot
%VBM% startvm "demo2"

Verify partitioning and partition sizes by executing in a terminal window

lsblk

I see:

NAME   MAJ:MIN RM  SIZE RO TYPE MOUNTPOINT
sda      8:0    0 39.1G  0 disk
??sda1   8:1    0    8G  0 part [SWAP]
??sda2   8:2    0 31.1G  0 part /

And

df -h

I see:

Filesystem      Size  Used Avail Use% Mounted on
/dev/sda2        31G  5.8G   24G  20% /
tmpfs           1.9G   76K  1.9G   1% /dev/shm

Shut down the VM – it is ready to use

Nov 27

Introduction

Please see the article “Build a Linux-based Infrastructure Solution Demonstration Series” (https://blogs.czapski.id.au/2016/10/build-a-linux-based-infrastructure-solution-demonstration-series) for rationale, introduction and links to articles in this series.

From time to time I may need another user account, configured with the same tools and settings as the demo user which was used in the earlier articles and will continue to be used in the subsequent articles.

In this post I show how a new user can be created and configured using the scripts which were already developed and which are available for use.

Pre-Requisites

This article assumes that

  1. The work is done in the Virtual Box Machine Image created in accordance with the instructions in the blog article to be found at https://blogs.czapski.id.au/2016/10/configure-virtual-box-virtual-machine-and-install-centos-6-8-base-image.
  2. The user “demo” has sudo access without a password. If this is not the case use the command “su -” and provide the password instead of saying “sudo -i” in the set of commands below

The instructions should work in other RedHat 6-like OS’ and OS versions.

Discussion

Let’s assume that we need another user’s environment configured the same way as the demo user’s environment. We were creating configuration scripts in the following articles:

  1. CentOS 6.8 – Script Adding Top Panel Applets – GEyes, ShowDesktop and Gnome Monitor
  2. CentOS 6.8 – Script adding a new gnome-terminal profile
  3. CentOS 6.8 – Create desktop branding scripts and brand desktop

Since the scripts are already available we will, in this article, create a different user, log in as that user, and execute the scripts to set up the environment.

Add user

Assuming that we are logged in as the demo user, which we will be if the article series was followed in the order in which it was published, we have sudo access.

NewUsername=ademo2
sudo useradd -c "${NewUsername} User" -m ${NewUsername}

sudo passwd ${NewUsername} <<'EOF'
welcome1
welcome1
EOF

sudo usermod -G vboxsf ${NewUsername}

Verify that the account was created

sudo -i -u ademo2 ls -al

su ademo2
Password: welcome1

cd
ls -al

exit

Use gdmflexiserver to switch to user ademo2 by running the following command

gdmflexiserver

The Greeter is displayed

041_greeter

Click “Other…”, then enter ademo2 as username and welcome1 as password.

Notice that the login desktop does not have the top panel applets to which we got used to by now when using the demo login into which we are automatically logged in at boot.

Disable Screen Saver and inactivity timer

After a while of inactivity a screen saver will be run. Let’s disable this feature. Right-click on the desktop and choose “Open in Terminal”, then execute the command scripts:

/bin/bash -v /media/sf_distros/scripts/002_disable_screen_saver_and_delays.sh

Configure top panel applets and nautilus file browser

Assuming that we are logged in as user ademo2, let’s add the top panel applets and configure nautilus file browser. Right-click on the desktop and choose “Open in Terminal…”, then execute the command scripts:

# add applets to top panel - you will get "Operation not permitted" at the end but the script will complete what it needs to do
/bin/bash -v /media/sf_distros/scripts/005_add_applets_to_top_panel.sh

# Add standard launchers to top panel - first "fix" the script which has the demo user embedded
sed -i 's|/demo/|/${USER}/|' /media/sf_distros/scripts/007_add_standard_launchers_to_top_panel.sh
/bin/bash -v /media/sf_distros/scripts/007_add_standard_launchers_to_top_panel.sh

/bin/bash -v /media/sf_distros/scripts/008_configure_nautilus_file_browser.sh

Configure gnome-terminal profiles

Assuming that this user account will be used to do the same kinds of things that the dmeo use will, let’s add the gnome-terminal profiles that we added for the demo user

/bin/bash -v /media/sf_distros/scripts/010_make_profile_very_visible.sh
/bin/bash -v /media/sf_distros/scripts/011_make_profile_console_scrolling.sh

Brand the desktop

Let’s brand the desktop so that we know what is there. This, as you will undoubtedly realise, is done on per-user basis, and perhaps needs to be reconsidered. If additional software is installed by a different user, for example the root user, neither the demo user nor the ademo2 user will have this reflected in their branded desktop. Still, this is probably better than nothing and as long as the administrator updates the branding files and runs the branding script at appropriate times things should go well enough.

# copy desktop branding script to the local directory for execution
# only needs to be done the first time
cp -v /media/sf_distros/scripts/013_source_desktop_branding_script.sh ${HOME}/brand_desktop.sh

# create a rarely changing content
cat <<-'EODECK' > ${HOME}/copyright_and_credits.txt
Copyright © 2016, Michael Czapski
EODECK

# create "current state" content - only needs to be done the first time branding is done
oldVer="v0.0.0"
newVer="v1.1.0"
touch ${HOME}/branding_installed_software_${oldVer}.txt
cp ${HOME}/branding_installed_software_${oldVer}.txt ${HOME}/branding_installed_software_${newVer}.txt

idLabel="Installed and Configured CentOS 6.8"

# append to "current state" content
cat <<-EODECK >> ${HOME}/branding_installed_software_${newVer}.txt
${newVer}, $(date +%Y-%m-%d\ %H:%M)
${idLabel}
EODECK

# brand the desktop
annotationFile=${HOME}/branding_installed_software_${newVer}.txt
creditsFile=${HOME}/copyright_and_credits.txt
buildFile=${HOME}/build_date.txt

${HOME}/brand_desktop.sh "${idLabel}" "${annotationFile}" "${creditsFile}" "${newVer}"

Explore, then log out from ademo2 user and return to demo user

Nov 26

Introduction

Please see the article “Build a Linux-based Infrastructure Solution Demonstration Series” (https://blogs.czapski.id.au/2016/10/build-a-linux-based-infrastructure-solution-demonstration-series) for rationale, introduction and links to articles in this series.

As I work with the various demonstration images I find myself taking longish breaks between bouts of activity so I like to have a clear indication, right on the desktop, of what state the image is in. What software was installed, what tis the name of the checkpoint I reached so that I can look up the specific section in the installation document I build at the same time, and so that I can resume work where I left off.

In this post I walk through the method of “branding” the desktop with text which tells me that what is installed, what the checkpoint has been reached, what the host name is and what user is logged in. I will subsequently use this script to brand the desktop each time something significant is done to the image.

The image blow shows the desktop branded using the method described in this article.

040_branded_desktop

Pre-Requisites

This article assumes that

  1. The work is done in the Virtual Box Machine Image created in accordance with the instructions in the blog article to be found at https://blogs.czapski.id.au/2016/10/configure-virtual-box-virtual-machine-and-install-centos-6-8-base-image.

The instructions should work in other RedHat 6-like OS’ and OS versions and on platforms that use gnome-2 desktop.

Discussion

The branding method relies on the ability to change desktop background using gconftool-2 by providing a location of the image which to use, and on the fact that ImageMagc can be used to programmatically manipulate images by adding text and other graphical artefacts to existing images.

This capability is used to mark up an existing desktop image with text that reflects the username, host name, label designating the state the image is in and a list of software which was installed up to the point at which desktop was branded.

Update ImageMagic Security Profile

As shipped in CentOS 6.8 and Oracle Linux 6U8 at least, ImageMagic, which I use to manipulate desktop image I will use, has security restrictions which prevent it being use the way it needs to be used to accomplish what needs to be accomplished in this post. To allow the manipulation to be done one must replace the ImageMagic security policy file. We will develop a script, to be added to the bilk initial setup script set, in the next section.

Let’s create a script which will update the ImageMagic security profile so that we can watermark the desktop image with text we want to see on the desktop.

cat <<-'EODECK' > /media/sf_distros/scripts/012_update_imagemagic_security_profile.sh

# Save ImageMagic policy file
if [ ! -f /etc/ImageMagick/policy.xml_orig ]; then
    sudo cp -v /etc/ImageMagick/policy.xml /etc/ImageMagick/policy.xml_orig
fi

# Create replacement policy file
cat <<-'EOF' | sudo tee /etc/ImageMagick/policy.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE policymap [
<!ELEMENT policymap (policy)+>
<!ELEMENT policy (#PCDATA)>
<!ATTLIST policy domain (delegate|coder|filter|path|resource) #IMPLIED>
<!ATTLIST policy name CDATA #IMPLIED>
<!ATTLIST policy rights CDATA #IMPLIED>
<!ATTLIST policy pattern CDATA #IMPLIED>
<!ATTLIST policy value CDATA #IMPLIED>
]>
<!--
  Configure ImageMagick policies.

 Domains include system, delegate, coder, filter, path, or resource.
 Rights include none, read, write, and execute.  Use | to combine them,
  for example: "read | write" to permit read from, or write to, a path.
  Use a glob expression as a pattern.
  Suppose we do not want users to process MPEG video images:
    <policy domain="delegate" rights="none" pattern="mpeg:decode" />
  Here we do not want users reading images from HTTP:
    <policy domain="coder" rights="read | write" pattern="HTTP" />
  Lets prevent users from executing any image filters:
    <policy domain="filter" rights="none" pattern="*" />
  The /repository file system is restricted to read only.  We use a glob
  expression to match all paths that start with /repository:
    <policy domain="path" rights="read" pattern="/repository/*" />
  Let's prevent possible exploits by removing the right to use indirect reads.
    <policy domain="path" rights="none" pattern="@*" />
  Any large image is cached to disk rather than memory:
    <policy domain="resource" name="area" value="1gb"/>
  Note, resource policies are maximums for each instance of ImageMagick (e.g.
  policy memory limit 1GB, -limit 2GB exceeds policy maximum so memory limit
  is 1GB).
-->
<policymap>
  <!-- <policy domain="system" name="precision" value="6"/> -->
  <!-- <policy domain="resource" name="temporary-path" value="/tmp"/> -->
  <!-- <policy domain="resource" name="memory" value="2GiB"/> -->
  <!-- <policy domain="resource" name="map" value="4GiB"/> -->
  <!-- <policy domain="resource" name="area" value="1gb"/> -->
  <!-- <policy domain="resource" name="disk" value="16eb"/> -->
  <!-- <policy domain="resource" name="file" value="768"/> -->
  <!-- <policy domain="resource" name="thread" value="4"/> -->
  <!-- <policy domain="resource" name="throttle" value="0"/> -->
  <!-- <policy domain="resource" name="time" value="3600"/> -->
  <policy domain="coder" rights="read | write" pattern="EPHEMERAL" />
  <policy domain="coder" rights="read | write" pattern="HTTPS" />
  <policy domain="coder" rights="read | write" pattern="HTTP" />
  <policy domain="coder" rights="read | write" pattern="URL" />
  <policy domain="coder" rights="read | write" pattern="FTP" />
  <policy domain="coder" rights="read | write" pattern="MVG" />
  <policy domain="coder" rights="read | write" pattern="MSL" />
  <policy domain="coder" rights="read | write" pattern="TEXT" />
  <policy domain="coder" rights="read | write" pattern="LABEL" />
  <policy domain="coder" rights="read | write" pattern="TXT" />
<!--  <policy domain="path" rights="none" pattern="@*" /> -->
</policymap>
EOF
EODECK
chmod ug+x /media/sf_distros/scripts/012_update_imagemagic_security_profile.sh

Execute the script to update the policy file

/bin/bash -v /media/sf_distros/scripts/012_update_imagemagic_security_profile.sh

See the differences

diff -y --suppress-common-lines /etc/ImageMagick/policy.xml /etc/ImageMagick/policy.xml_orig

Create Desktop Branding script

Create the branding script with 4 arguments

cat <<-'EODECK' > /media/sf_distros/scripts/013_source_desktop_branding_script.sh
#!/bin/bash
# check that each individual argument exists
: ${1?"Usage: $0 " '${idLabel} ${annotationFile} ${creditsFile} ${newVer} - provide content for the idLabel - exiting ...'}
: ${2?"Usage: $0 " '${idLabel} ${annotationFile} ${creditsFile} ${newVer} - provide path to annotationFile - exiting ...'}
: ${3?"Usage: $0 " '${idLabel} ${annotationFile} ${creditsFile} ${newVer} - provide path to creditsFile - exiting ...'}
: ${4?"Usage: $0 " '${idLabel} ${annotationFile} ${creditsFile} ${newVer} - provide version number like 'v1.2.3' - exiting ...'}

idLabel=${1}
annotationFile=${2}
creditsFile=${3}
newVer=${4}

# "build date" is a date/tim stamp reflecting when the script was run
cat <<-EOF > ${HOME}/build_date.txt
Build: ${newVer}, $(date +%d\ %b\ %Y\ %H:%M)
EOF
buildFile=${HOME}/build_date.txt

# ImageMagic command uses the default.png background - this can be readily changes
# Username and Host name are written near the top right (northeast) in 42pt
# and near the bottom left (southwest) in 32pt
# content of the annotation file, credits file and build file are written in the appropriate
# places in the image - see pictire for where this is
#
mkdir -p ~/imgman
cd ~/imgman
bgfile=$(hostname -s)_${USER}.jpg
myhost=$(hostname)
convert /usr/share/backgrounds/default.png \
-gravity northeast -fill white -pointsize 42 \
-annotate +20+60 "${idLabel}\n${USER}\n${myhost}" \
-gravity southwest -fill white -pointsize 32 \
-annotate +20+40 "${myhost}\n${USER}\n${idLabel}" \
-gravity northwest -fill white -pointsize 18 \
-annotate +300+120 @${annotationFile} \
-gravity southwest -fill white -pointsize 14 \
-annotate +700+120 "$( cat ${creditsFile})" \
-gravity southwest -fill white -pointsize 14 \
-annotate +700+100 "$( cat ${buildFile})" \
${bgfile}

# replace the current desktop background with the modified desktop background
gconftool-2 -s -t string /desktop/gnome/background/picture_filename "${HOME}/imgman/${bgfile}"
EODECK
chmod ug+x /media/sf_distros/scripts/013_source_desktop_branding_script.sh

Copy desktop branding script to the local directory for use

cp -v /media/sf_distros/scripts/013_source_desktop_branding_script.sh ${HOME}/brand_desktop.sh

Brand the desktop

Create branding file with the content that rarely changes – it will appear at the centre near the bottom of the desktop

cat <<-'EODECK' > ${HOME}/copyright_and_credits.txt
Copyright © 2016, Michael Czapski
EODECK

It is expected that the software list file, which in my case contains the list of software I installed and configured, will grow between the times the desktop gets branded. To keep the list up to date one will copy the most recent version of the branding file with a new version and append text to it to provide information on what changed since.

Append to software list file – the first time around the software list file will not exist

oldVer="v0.0.0"
newVer="v1.1.0"
touch ${HOME}/branding_installed_software_${oldVer}.txt
cp ${HOME}/branding_installed_software_${oldVer}.txt ${HOME}/branding_installed_software_${newVer}.txt

Label is the text that identifies the current iteration of the image and in my case correlates with the checkpoint names in the installation document used to build the image and possibly with the snapshot names in VirtualBox .

idLabel="Installed and Configured CentOS 6.8"

The branding file incorporates the version, date/time stamp and the label

cat <<-EODECK >> ${HOME}/branding_installed_software_${newVer}.txt
${newVer}, $(date +%Y-%m-%d\ %H:%M)
${idLabel}
EODECK

Execute the script to brand the desktop

annotationFile=${HOME}/branding_installed_software_${newVer}.txt
creditsFile=${HOME}/copyright_and_credits.txt
buildFile=${HOME}/build_date.txt

${HOME}/brand_desktop.sh "${idLabel}" "${annotationFile}" "${creditsFile}" "${newVer}"

Add to initial bulk configuration script

It is expected that the image being configured a bit at a time in this series of articles will be created more than once for different purposes. With this assumptions the individual scripts are appended to a single script so that the second and subsequent images can be configured by a single script rather than having lots of scripts to execute manually.

Copy the global desktop branding script to the local directory for execution

Don’t actually execute this script while you are building the first image.

cat <<-'EODECK' >> /media/sf_distros/scripts/000_initial_bulk_configuration.sh
# update ImageMagic security policy file
/bin/bash -v /media/sf_distros/scripts/012_update_imagemagic_security_profile.sh

# copy desktop branding script to the local directory for execution
cp -v /media/sf_distros/scripts/013_source_desktop_branding_script.sh ${HOME}/brand_desktop.sh

EODECK
chmod ug+x /media/sf_distros/scripts/000_initial_bulk_configuration.sh
Nov 20

Introduction

Please see the article “Build a Linux-based Infrastructure Solution Demonstration Series” (https://blogs.czapski.id.au/2016/10/build-a-linux-based-infrastructure-solution-demonstration-series) for rationale, introduction and links to articles in this series.

As I work with the various demonstration images I find myself using gnome-terminal profiles configured to display server logs, scripting interactions and other kinds of stuff using different font sizes, terminal backgrounds and application exit behaviours. I used to configure these manually in each image, then dumping configured profiles into a xml file and loading them into a new image. The gnome-terminal dumps are quite large so this is somewhat cumbersome.

In this post I walk through the method of script-driven addition of gnome terminal profiles and gconftool-2-based configuration so that all of this can be scripted and can avoid the need to do it manually.

Pre-Requisites

This article assumes that

  1. The work is done in the Virtual Box Machine Image created in accordance with the instructions in the blog article to be found at https://blogs.czapski.id.au/2016/10/configure-virtual-box-virtual-machine-and-install-centos-6-8-base-image.

The instructions should work in other RedHat 6-like OS’ and OS versions and on platforms that use gnome-2 desktop.

Discussion

A gnome-terminal profile can be created manually. Just start a gnome-terminal, pull down the Edit menu, choose Profiles and Add new profile. Creating a new profile programmatically is harder.

The following commands will find out what is the “last” profile and will work out what the internal name of the new profile needs to be, then will clone the Default profile and use it to create a new profile, with the new internal and visible names, ready for gconftool-2 configuration.

If you execute these commands you will create a profile.

In the next section you will create scripts which combine all of these commands so that a profile can be created using a single script.

By exporting and manipulating the gnome-terminal global profile list we will determine the name of the “last” profile. This name will either be “Default”, if there is only one, the default profile, or a name of the form “ProfileX”, where X will be 0, 1, 2, …, depending on the number of profiles, in addition to the “Default” profiles, which are configured.

The profile list will look like [Default], or [Default,Profile0], i.e. there will be square brackets surrounding the comma-separated list of names. The sed command will strip the square brackets so that we get just the comma-separated list of names.

profiles_list=$(gconftool-2 --get "/apps/gnome-terminal/global/profile_list" | sed "s|\[||;s|\]||;")
echo "1 Profiles List: " ${profiles_list}

Let’s take the list of profile names, “Default” or “Default,Profile0,Provile1”, etc.. and remove all elements from the list up to and including the last comma. This will leave either the last profile name of the form “ProfileX” or the literal “Default”

last_profile=$(echo "${profiles_list}" | sed "s/^.*,//" | sed 's/Profile//')
echo "Last Profile Name/Number: " ${last_profile}

If the last_profile is the literal “Default” then the “next” internal profile name will be “Profile0” otherwise it will be Profile(X + 1) -> is 2 then Profile3, etc.

Let’s set the “ProfileX” X number to 0 if only default is there or whatever the last is plus 1

if [ ${last_profile} == "Default" ]; then
    next_profile_number=0;
    echo "1 New Profile Number: " ${next_profile_number}
else
    next_profile_number=$(( ${last_profile} + 1 ));
    echo "2 New Profile Number: " ${next_profile_number}
fi
echo "New Profile Number: " ${next_profile_number}

We need to tell gnome that the list of profile names is different. If it was “Default,Profile0” then with the new profile it needs to be “Default,Profile0,Profile1”

Let’s construct profiles list with extra profile “number”

profiles_list=$(echo "[${profiles_list},Profile${next_profile_number}]")
echo "1 Profiles List: " ${profiles_list}

By now we established the new internal profile name – Profile0 or Profile1 or …

Let’s set the new visible profile name as an environment variable for ease of use later

profileName=MyNewProfile

Let’s get a dump of the default profile. We will use it as a template for the new profile

gconftool-2 --dump "/apps/gnome-terminal/profiles/Default" > /tmp/${USER}_gnome-terminal_profiles_${profileName}.xml

Let’s change global name to the new internal profile name in the Default profile dump

sed -i “s|Default|Profile${next_profile_number}|g” /tmp/${USER}_gnome-terminal_profiles_${profileName}.xml

Let’s load the new profile – it will not be useable until the next step is executed

gconftool-2 --load /tmp/${USER}_gnome-terminal_profiles_${profileName}.xml

Let’s tell gnome-terminal that is has another profile by updating the profile_list property

gconftool-2 --set --type list --list-type string "/apps/gnome-terminal/global/profile_list" "${profiles_list}"

Let’s change the visible_name property to the “friendly” name we want to see in the Profiles dropdown

gconftool-2 --set --type string /apps/gnome-terminal/profiles/Profile${next_profile_number}/visible_name "${profileName}"

Let’s set other properties to suit

gconftool-2 --set --type boolean /apps/gnome-terminal/profiles/Profile${next_profile_number}/use_system_font "false"
gconftool-2 --set --type boolean /apps/gnome-terminal/profiles/Profile${next_profile_number}/use_theme_colors "false"
gconftool-2 --set --type boolean /apps/gnome-terminal/profiles/Profile${next_profile_number}/login_shell "true"
gconftool-2 --set --type boolean /apps/gnome-terminal/profiles/Profile${next_profile_number}/default_show_menubar "false"
gconftool-2 --set --type boolean /apps/gnome-terminal/profiles/Profile${next_profile_number}/scrollback_unlimited "true"
gconftool-2 --set --type string /apps/gnome-terminal/profiles/Profile${next_profile_number}/exit_action "hold"
gconftool-2 --set --type string /apps/gnome-terminal/profiles/Profile${next_profile_number}/font "Monospace 14"
gconftool-2 --set --type string /apps/gnome-terminal/profiles/Profile${next_profile_number}/background_color "#FFFFFFFFDDDD"
gconftool-2 --set --type string /apps/gnome-terminal/profiles/Profile${next_profile_number}/foreground_color "#0000FFFF0000"
gconftool-2 --set --type string /apps/gnome-terminal/profiles/Profile${next_profile_number}/scrollbar_position "hidden"

Let’s create a terminal using the profile we just created

gnome-terminal --geometry=80x24+0+0 --profile=${profileName} title "${profileName}" --zoom 0.8 -e "/bin/sh"

The steps above helped us create and configure a new gnome-terminal profile and create a terminal which is using that profile.

Design the “create new gnome-terminal profile” script

cat <<-'EODECK' > /media/sf_distros/scripts/create_new_gnome-terminal_profile.sh
#!/bin/bash
# The following script will find out what is the "last" profile and will work out
# what the internal name of the new profile needs to be, then will clone the Default profile
# and use it to create a new profile, with the new internal and visible names,
# ready for gconftool-2 configuration.

# Let's set the new visible profile name as an environment variable
# for ease of use later

profileName=${1?"Usage: $0 profileName # please provide value for the visible_name profile property - make sure that it is a valid string identifier, i.e consisting of letters and digits only"}

# By exporting and manipulating the gnome-terminal global profile list we will determine
# the name of the "last" profile. This name will either be "Default", if there is only one,
# the default profile, or a name of the form "ProfileX", where X will be 0, 1, 2, …,
# depending on the number of profiles, in addition to the "Default" profiles,
# which are configured.

profiles_list=$(gconftool-2 --get "/apps/gnome-terminal/global/profile_list" | sed "s|\[||;s|\]||;")
echo "1 Profiles List: " ${profiles_list}

# Let's take the list of profile names, "Default" or "Default,Profile0,Provile1", etc..
# and remove all elements from the list up to and including the last comma.
# This will leave either the last profile name of the form "ProfileX" or the literal "Default"

last_profile=$(echo "${profiles_list}" | sed "s/^.*,//" | sed 's/Profile//')
echo "Last Profile Name/Number: " ${last_profile}

# If the last_profile is the literal "Default" then the "next" internal profile name
# will be "Profile0" otherwise it will be Profile(X + 1) -> is 2 then Profile3, etc.
# Let's set the "ProfileX" X number to 0 if only default is there or whatever the last is
# plus 1

if [ ${last_profile} == "Default" ]; then
    next_profile_number=0;
    echo "1 New Profile Number: " ${next_profile_number}
else
    next_profile_number=$(( ${last_profile} + 1 ));
    echo "2 New Profile Number: " ${next_profile_number}
fi
echo "New Profile Number: " ${next_profile_number}

# We need to tell gnome that the list of profile names is different.
# If it was "Default,Profile0" then with the new profile
# it needs to be "Default,Profile0,Profile1"
# Let's construct profiles list with extra profile "number"

profiles_list=$(echo "[${profiles_list},Profile${next_profile_number}]")
echo "1 Profiles List: " ${profiles_list}

# By now we established the new internal profile name - Profile0 or Profile1 or ...

# Let's get a dump of the default profile. We will use it as a template for the new profile

gconftool-2 --dump "/apps/gnome-terminal/profiles/Default" > /tmp/${USER}_gnome-terminal_profiles_${profileName}.xml

# Let's change global name to the new internal profile name in the Default profile dump

sed -i "s|Default|Profile${next_profile_number}|g" /tmp/${USER}_gnome-terminal_profiles_${profileName}.xml

# Let's load the new profile - it will not be useable until the next step is executed

gconftool-2 --load /tmp/${USER}_gnome-terminal_profiles_${profileName}.xml

# Let's tell gnome-terminal that is has another profile by updating the profile_list property

gconftool-2 --set --type list --list-type string "/apps/gnome-terminal/global/profile_list" "${profiles_list}"

# Let's change the visible_name property to the "friendly" name we want to see
# in the Profiles dropdown

gconftool-2 --set --type string /apps/gnome-terminal/profiles/Profile${next_profile_number}/visible_name "${profileName}"

EODECK
chmod ug+x /media/sf_distros/scripts/create_new_gnome-terminal_profile.sh

The following script, given the name of the profile, the data type fo the property to change, the name of the property to change and the value to which to change the property will make the change

cat <<-'EODECK' > /media/sf_distros/scripts/set_profile_property.sh
#!/bin/bash

profileName=${1?"Usage: $0 profileName keyName keyType keyValue # please provide value of the visible_name of the profile whose property you want to change"}
keyName=${2?"Usage: $0 profileName keyName keyType keyValue # please provide name of the profile key which you want to change"}
keyType=${3?"Usage: $0 profileName keyName keyType keyValue # please provide data type of the profile key which you want to change - boolean, string are supported - lists are not"}
keyValue=${4?"Usage: $0 profileName keyName keyType keyValue # please provide value of the profile key which you want to change"}

profileNum=$(gconftool-2 --search-key visible_name | grep ${profileName} | tail -n 1 | sed 's|^.*/Profile||;s|/.*$||')
echo "ProfileNum: " ${profileNum}

gconftool-2 --set --type ${keyType} /apps/gnome-terminal/profiles/Profile${profileNum}/${keyName} "${keyValue}"
EODECK
chmod ug+x /media/sf_distros/scripts/set_profile_property.sh

Create scripts for new profiles very_visble and console_scrolling

The very_visible profile will have larger font (Monospace, 14) and a green background, to distinguish the very_visible terminal from a “regular” gnome-terminal.

cat <<-'EODECK' > /media/sf_distros/scripts/010_make_profile_very_visible.sh
#!/bin/bash
/media/sf_distros/scripts/create_new_gnome-terminal_profile.sh very_visible

/media/sf_distros/scripts/set_profile_property.sh very_visible use_system_font boolean false
/media/sf_distros/scripts/set_profile_property.sh very_visible use_theme_colors boolean false
/media/sf_distros/scripts/set_profile_property.sh very_visible login_shell boolean true
/media/sf_distros/scripts/set_profile_property.sh very_visible default_show_menubar boolean false
/media/sf_distros/scripts/set_profile_property.sh very_visible scrollback_unlimited boolean true
/media/sf_distros/scripts/set_profile_property.sh very_visible exit_action string hold
/media/sf_distros/scripts/set_profile_property.sh very_visible font string "Monospace 14"
/media/sf_distros/scripts/set_profile_property.sh very_visible background_color string "#FFFFFFFFDDDD"
/media/sf_distros/scripts/set_profile_property.sh very_visible foreground_color string "#000000000000"
/media/sf_distros/scripts/set_profile_property.sh very_visible scrollbar_position string hidden
EODECK
chmod ug+x /media/sf_distros/scripts/010_make_profile_very_visible.sh

 

The console_scrolling profile will have larger font (Monospace, 9) and a black background, to distinguish the console_scrolling terminal from a “regular” gnome-terminal.

cat <<-'EODECK' > /media/sf_distros/scripts/011_make_profile_console_scrolling.sh
#!/bin/bash
/media/sf_distros/scripts/create_new_gnome-terminal_profile.sh console_scrolling

/media/sf_distros/scripts/set_profile_property.sh console_scrolling use_system_font boolean false
/media/sf_distros/scripts/set_profile_property.sh console_scrolling use_theme_colors boolean false
/media/sf_distros/scripts/set_profile_property.sh console_scrolling login_shell boolean true
/media/sf_distros/scripts/set_profile_property.sh console_scrolling default_show_menubar boolean false
/media/sf_distros/scripts/set_profile_property.sh console_scrolling scrollback_unlimited boolean true
/media/sf_distros/scripts/set_profile_property.sh console_scrolling scroll_on_output boolean true
/media/sf_distros/scripts/set_profile_property.sh console_scrolling exit_action string hold
/media/sf_distros/scripts/set_profile_property.sh console_scrolling font string "Monospace 9"
/media/sf_distros/scripts/set_profile_property.sh console_scrolling background_color string "#000000000000"
/media/sf_distros/scripts/set_profile_property.sh console_scrolling foreground_color string "#FFFFFFFFFFFF"
/media/sf_distros/scripts/set_profile_property.sh console_scrolling scrollbar_position string hidden
EODECK
chmod ug+x /media/sf_distros/scripts/011_make_profile_console_scrolling.sh

Execute commands to create and configure the two profiles

/bin/bash -v /media/sf_distros/scripts/010_make_profile_very_visible.sh
/bin/bash -v /media/sf_distros/scripts/011_make_profile_console_scrolling.sh

Add to initial bulk configuration script

It is expected that the image being configured a bit at a time in this series of articles will be created more than once for different purposes. With this assumptions the individual scripts are appended to a single script so that the second and subsequent images can be configured by a single script rather than having lots of scripts to execute manually.

Append “create gnome-terminal profiles” script execution commands to the initial bulk configuration script. This script is intended to collect all automated configuration commands and scripts so that they can be all executed in one go on a brand new image if one gets to do this the second and subsequent times.

Don’t actually execute this script while you are building the first image.

cat <<-'EODECK' >> /media/sf_distros/scripts/000_initial_bulk_configuration.sh
# create very_visible gnome-terminal profile
/bin/bash -v /media/sf_distros/scripts/010_make_profile_very_visible.sh

# create console_scrolling gnome-terminal profile
/bin/bash -v /media/sf_distros/scripts/011_make_profile_console_scrolling.sh

EODECK
chmod ug+x /media/sf_distros/scripts/000_initial_bulk_configuration.sh
preload preload preload