Friday, May 14, 2021

Simple string padding

Just a quick way of centering a substring within a space passed string.  Nothings perfect, especially when odd numbers are concerned.

 proc pad { str max} {
    set len [string length $str]
    
    set tmp [string repeat " " $max]
    set dif [expr $max-$len]
    set fr [expr $dif/2]
    set to [expr $fr+$len]
    set str [string replace $tmp $fr $to $str]
    
    return $str
}


Wednesday, May 12, 2021

Text Widget List Viewer


 

 

The list widget is a great tool for displaying related information in sets. That is a sample, distributed across rows and columns. To do this the list widget creates cells for each sample and allocated memory to handle its formatting and data. In practice, this means two 'blocks' for each piece of data. The information to display, and how to display it.

This arrangement works fine for large humanly workable amounts of data, perhaps up to 1,500 or more rows. But, what about more, much, much more?

I have a translation data set with source and target language pairs well in excess of 57,000 pairs. The list widget will handle these, but the delay receiving and rendering is far too long. The solution then, is to tweak the text widget to handle similar rows with a row picker. Sounds complicated, but its not. Just a couple of tweaks needed. Put the text widget inside an eventBox and then trap any direct events being passed to it, whilst allowing the scrolledWindow container to get the input it needs.

Why do this? By default the text widget will change the mouse pointer to an edit bar. The eventBox retains use of the

Achieving the latter is the easiest, nothing required. The scrolledWindow takes priority over the eventBox by default. So, turn off the text cursor, and make it invisible and respond to the eventBox mouse button events to change the paragraph colour of the selected block -simples!

 


# !/bin/sh
# the next line restarts using tclsh \
exec tclsh "$0" "$@"

package require Gnocl


# ~~~~~~~~~~~~~~~~~~~~~~~~~~

set txt [gnocl::text -editable 0 -cursorVisible 0]
$txt tag create idx -paragraph orange

set ebox [gnocl::eventBox -child $txt -aboveChild 1]
gnocl::window -child $ebox -setSize 0.2


# add some text to the widget.
for {set i 0} {$i<250} {incr i} {
append buff "$i) Lorem\n"
append buff [gnocl::lorem]\n
}
$txt set $buff
puts [$txt getLineCount]


#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# SET EBOX EVENTS TO SIMULATE LINE SELECTION AND TEXT SCROLLING
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
$ebox configure \
-onButtonRelease {
    %c tag remove start end -tags idx
    %c setCursor [%c getPos]
    %c tag apply lineStart lineEnd -tags idx
} \
-onScroll {
    if { "%D" == "down" } {
        incr ::scrollPos 5
            if { $::scrollPos > [%c getLineCount] } { set ::scrollPos [%c getLineCount]}
        } else {
            incr ::scrollPos -5
            if { $::scrollPos < 0 } { set ::scrollPos 0}
        }
    %c scrollToPosition "$::scrollPos 0"   
}
 


 

Saturday, May 08, 2021

Zen and the Art of Computer Programming


When we learn to code we work on simple problems, maybe the sort of tasks that were originally used to test and develop our favourite languages, libraries and apis. In a nutshell, simple solutions to simple problems. 

But, when complexity takes over, rules often go out of the window - there may be causes, but these cannot be easily identified and defined. The may be at a 'lower level', perhaps in dependencies or even compilation errors. 

I've just had to resolve one such problem arising from the failure to display of child widgets in an eventBox. After modifying the gnoclOptChild code in the parseoptions.c module to offer the ability to replace child objects in a GtkBin, the new code worked for other GtkBin objects but not for an eventBox. 

The solution was to copy the contents of the gnoclOptChild function and divide the code between the configure and cget module functions. This also needed a modification to the EBoxOptions array. Happy again now!

So why the Zen and Computer programming? 

Always keep an open mind, things doesn't always go the way you expect them to.

Thursday, May 06, 2021

Notebook Tabs with Embedded Widgets.

Ok, its of limited use but it is possible to embed user defined widgets in a notebook page tab. In all likelihood it will still be a label and a button embedded in hbox and not a list or tree view! 

Including custom labels does have its drawbacks, its not really practicable to use accelerators in the same way and, if any commands or substitution strings are passed then these will return label widget-ids and not the label text string.



# !/bin/sh
# the next line restarts using tclsh \
exec tclsh "$0" "$@"

package require Gnocl

set nb [gnocl::notebook]

$nb addPage [gnocl::button -text HI] AAAAA

$nb tabConfigure 0 -data 1


# label and box
set hbox [gnocl::hBox -padding 0 -borderWidth 0]
$hbox add [gnocl::label -text %_H_IHIHI]
$hbox add [gnocl::image -image %#SaveAs -stockSize smallToolBar ]


$nb label 0 $hbox

puts [$nb getLabel 0]

gnocl::window -child $nb

Wednesday, May 05, 2021

Setting Variables from Lists

The format for defining a Tcl proc is simply:

proc name {var1 var2.. varn} { }

There is a special keyword when defining the variable list, args. When placed at the end of proceedures argument list, it results all other values being passed as a single list. In effect then, it allows a Tcl proc to recieve a variable number of arguments.

So, the above definition could be re-written as:

proc name {args} { }

The implication here is that keeping to a single string as an even numbered list tags and values, it becomes possible to define variables within the calling procedure. Once passed, these can be set using the standard Tcl foreach command.

proc name {args} {
    foreach {*}$args {break}
}

Now, this process can be taken one step further. Many system commands have values, commands and switches and it can be helpful to emulate these patterns within a Tcl script as it saves the mind power of having to switch between programming styles.

The next step then is:

proc name {val1 val2 args} {
    foreach {*}$args {break}
}

Where val1 and val2 can be subcommands, required values and arguments or optional values in the form of switches.  

If option tags with leading '-' are used be used these will have detrimental effect to creating legal variable names but they can be removed quite easily using one two ways using regsub or a string Tcl commands in combination with foreach. i.e.,

foreach {a b} $args { regsub -line {\-} $a {} a ; set $a $b }

Or, 

foreach {a b} $args { set [string range $a 1 end] $b }


Monday, May 03, 2021

To Close or to Hide, that is the Question.

Sometimes you want make a popup window with some editing or parameter setting functionality which is a bit like a dialog, although floating palette is what immediately comes to mind. 

The problem is though, that whilst it's easy to adjust the window decorations, to remove the delete icons, the window's pull-down menu will still have a close option which will, by default, destroy the window when clicked. 

To disable this, and simply hide the window so that it can be shown again, set the -hideOnDelete option to 1. 

Following this callback scripts can be used to respond to the visibility state of the window, acting as some form of Ok button if necessary.

 

# !/bin/sh
# the next line restarts using tclsh \
exec tclsh "$0" "$@"

package require Gnocl

set txt
[gnocl::text]

set win1 [gnocl::window -child $txt] \
    -title Win1 -x 500 -y 400 -setSize 0.125

# EXTRA OPTIONS
$win configure \
    -hideOnDelete 1 \

    -onHide { puts BYE! } \
    -onShow { puts Hello-Cheeky! }]


# toggle win1 hide/show states
set win2 [gnocl::window -child [gnocl::button -text "Show win1" -onClicked {
    $win1 show
    }] -title Win2 -x 800 -y 400 -setSize 0.125  ]

# revoke the hide on delete status for win1
set win3 [gnocl::window -child [gnocl::button -text "Turn off Hide" -onClicked {
    $win1 configure -hideOnDelete 0
    }] -title Win2 -x 1100 -y 400 -setSize 0.125]



Sunday, May 02, 2021

Had a Real Pig of a Morning Today!


 

I've spent the better part of half a day solving what really ought to a have been a trivial problem. Well, the solution was trivial but tracking down the cause of it all was very frustrating indeed!

Consider this:

set menu [gnoc::menu]

Hitherto, in order to be compliant with now ancient formatting practices, a menu automatically has a tearoff item place at position 0 in the children list. So, to hide it, because we don't want one use:

set menu [gnoc::menu -tearoff 0]

or

set menu [gnoc::menu]

$menu configure -tearoff 0]

So far, so good. But, if the gnocl::application command is used to produce a working UI framework, and extra widgets including addition menu bar items, and the show subcommand used for the application toplevel then BOOM! 

The recently made menus without tearoffs suddenly got tearoffs again. 😠

The issue was that the existing menu.c configure function simply hid or showed the default tearoff item. This needs to be created or destroyed when needed.

Well, job done now. Onwards and upwards!

 

 

 

 

 

json_shell_utils.tcl