I have created a simple thread to continuously display a message box till the user deos not want to some operation. Following is the code:
thread::create { while [tk_messageBox -message "Do you want to Exit?!!" -type yesno] {
doSomething
}}
But there is no message box displayed although the thread is created.
How can I really see these messageboxes?
You need to make Tk be present in the thread as well; only the Thread package is present by default in subordinate threads:
thread::create {
package require Tk
while [tk_messageBox -message "Do you want to Exit?!!" -type yesno] {
doSomething
}
}
Also, you need to fix a bunch of other problems in your code.
Always put the condition of a while in {braces}. Without that, the dynamic parts of the expression will only be evaluated once, which really isn't what you ever want with a while.
Make sure your thread does thread::wait, as that enables improved process and thread management. Your message box loop needs to be rewritten entirely.
This might lead to this code:
thread::create {
package require Tk
proc periodicallyMaybeDoSomething {} {
if {[tk_messageBox -message "Do you want to Exit?!!" -type yesno]} {
thread::exit
}
doSomething
# pick a better delay maybe?
after 1 periodicallyMaybeDoSomething
}
after 1 periodicallyMaybeDoSomething
thread::wait
}
If you're using 8.6, you may be able to use coroutines to make the code more elegant.
Related
The -errorline element of the return options dictionary for the following TCL script is "2":
puts [info patchlevel]
try {
error "this is an error"
} trap {} {result ropts} {
puts $result
puts $ropts
}
How do I get the stacktrace to display the line number in the source file where the error was actually raised (ie. line 4 instead of 2) ?
Example screenshot:
Tcl often has that information available, but doesn't use it.
It has the information available because you have a chance to retrieve it with info frame and getbytecode (which is in the tcl::unsupported namespace, mostly because we reserve the right to change how the bytecodes themselves work at any time). I'm not quite sure if that would work in your specific case, but if you put your test code in a procedure then it definitely would. (There are complexities here with fragility that I don't fully understand.)
It doesn't use it because, for backward-compatibility with existing tooling, it uses the line numbers it was using prior to the creation of the machinery to support info frame. Those line numbers are relative to the local script fragment (which is whatever reports the line number in the error info trace first); in this case, that is the body of the try.
I don't like that it works like that at all. However, changing things is a bit tricky because we'd need to also figure out what else to report and what to do in the cases where the information genuinely isn't available (such as for automatically-generated code where things are assembled from many strings from many lines).
I have an advanced function
function Start-Executable {
[CmdletBinding()]
Param (
[String]$Arg1,
[String[]]$Arg2,
[Parameter(ValueFromPipeline=$true)][String[]]$PipeValue
)
Begin {
# Setup code
}
Process {
# What goes on here?
foreach ($val in $PipeValue) {
# Process val here
}
}
End {
}
}
In my setup code, I need to do something different depending on whether the user supplied pipeline input. But I don't know how I can tell in my BEGIN block if there's pipeline input. Is there something I can check for that?
One other thing I tried was putting the setup code in the PROCESS block, before the loop over $PipeValue, but that doesn't work because it appears that the PROCESS block is called once for each pipeline value, with $PipeValue being an array of one item each time. Is that right? If PROCESS is being called repeatedly for each value, why do I need another loop inside the PROCESS block?
Update
To clarify, what I'm trying to do is create a process in BEGIN, feed it input in PROCESS and read the output in END. For this to work I need to set RedirectStandardInput to $true if there is pipeline input, but not otherwise.
As workarounds, I can either make the user specify with an extra argument (and if they get it wrong, things don't work) or I can set a $first_time flag in BEGIN, then create the process the first time PROCESS is called. If I get to END without having created the process, I create it there with RedirectStandardInput as $false. That's more code duplication than I like, but if it's the only option I may have to do it that way.
$MyInvocation.ExpectingInput returns true if the function is invoked with pipeline input, and false otherwise.
This works in begin, process, and end blocks. It does not work in dynamicparam in PowerShell 5.1 or lower.
The Begin block runs before the pipeline is started, so there's no way for that code to know what's in the pipeline.
As far as needing another loop inside the Process block, you have to have that if the function needs to accept whatever $PipeValue is as either pipeline input or passed as a parameter. If it's only going to accept that as pipeline data, then there's no point in having a parameter for it. Just use $_ inside the process block.
function Start-Executable {
[CmdletBinding()]
Param (
[String]$Arg1,
[String[]]$Arg2,
[Parameter(ValueFromPipeline=$true, parametersetname="nopipeline")][String[]]$PipeValue
)
Begin {
# Setup code
if($PSCmdlet.ParameterSetName -eq "nopipeline")
{
Write-Host "No pipeline input"
}
else
{
Write-Host "Pipeline input"
}
}
Process {
$PipeValue
}
End {
}
}
This seems to do the trick.
Basically, the parameter is assigned to a parameter set.
If the parameter is used, $pscmdlet.ParameterSetName contains "nopipeline".
If using the pipeline, then $pscmdlet.ParameterSetName contains "__AllParameterSets"
Sample output:
PS H:\> #("a","b","c") | Start-Executable
Pipeline input
a
b
c
PS H:\> Start-Executable -PipeValue #("a","b","c")
No pipeline input
a
b
c
Edit: Note that this however won't tell you that there is pipeline input. It will tell you whether the named parameter was used.
In other words, this happens:
PS H:\> Start-Executable
Pipeline input
I wanted to know how to implement GOTO in tcl.
I am writing a test case where I have say 5 steps.
If my step 1 fails I don't want to proceed further and I want to skip the existing things and goto a common clean up section.
Please help me with if there are any GOTO commands in tcl.
Thanks,
Ramya.
There is no goto in Tcl, and for relatively technical reasons it's impossible to implement one.
But you can do what you're after in other ways. Since you're dealing with test cases, I hope you're using the tcltest package for the job. With that, you can specify cleanup code quite easily:
tcltest::test test-1.1 "verify that the foo works" -setup {
allocate some resources
} -body {
whatever to do the test...
return [our results]
} -cleanup {
drop those resources
make sure that we are nice and clean
} -result "the expected test result"
You can skip out of the body of a test easily by just doing a return; the tcltest::test command will detect it and treat that as the result. It is usually better to try to keep each test independent of the others though: that makes it much easier to track down what's going wrong when a test fails.
If you're not using tcltest, you are still best refactoring into something where you can use return to skip out early. You can combine that with try…finally… (either natively in Tcl 8.6, or with this code on the Tcler's Wiki) to make things easy:
proc doThings {} {
try {
# do thing-1
if {$no_more} return
# do thing-2
if {$no_more} return
# do thing-3
if {$no_more} return
# do thing-4
if {$no_more} return
# do thing-5
} finally {
# do cleanup
}
}
how can i add some kind of help to the user defined functions in TCL
Supposing if i have a function called runtest {ip_address test_time},
How to describe what the test or procedure is about in the TCL_shell?
How can i specify the information to the user, if he types in function_name --help in the TCL shell the user should be able to know what the function does and what exactly are the parameters.
how can i do this?
While it is true that it is not part of the language, it is fairly easy to implement something that adds this functionality to pre-existing functions. The only caveat being that the function then cannot take the string --help as a valid argument since that argument will trigger the feature.
Here's one simple implementation:
# Lets call the feature "document". As in, add documentation:
proc document {procname text} {
rename $procname __$procname
proc $procname args [string map [list %TEXT% $text %PROC% $procname] {
if {$args == "--help"} {
puts {%TEXT%}
} else {
set script [linsert $args 0 __%PROC%]
return [uplevel 1 $script]
}
}]
}
What this does is to override the function (by renaming and then declaring another function of the same name) and see if the function is called with the argument --help. If it is it prints the documentation otherwise it executes the original function. Just be careful not to call this twice on the same function (it can be modified for it to work though).
So you can do things like:
proc foo {} {puts 2}
document foo {Prints the number 2.}
Now if you call:
foo --help
and it would output:
Prints the number 2.
You don't have to touch the existing procedures:
proc help {procname} {
puts $::helptext($procname)
}
proc addhelp {procname text} {
set ::helptext($procname) $text
}
addhelp foo "this is the help text for procedure foo"
help foo
Without redefining the proc command, you cannot. Ie, that functionality is not built into the language, but would be possible to add if you so wished.
I will note that adding the capability yourself, while possible, is probably beyond the difficulty where it's worth doing, especially for someone that isn't intimately familiar with the language. That being said, you can check the tclers wiki for a preexisting implementation.
I tend to prefer to put help in a separate file (e.g., as HTML) which lets me browse it in another window. There are so many ways you can do that while still keeping the docs with the code, such as through doxygen.
There are also a number of ways of doing interactive documentation, several of which are described in the Tcler's Wiki; it would seem to me that some of the techniques mentioned on that page (together with #slebetman's answer) would give you what you wanted. (I think it would be easier to have a separate help command as that would avoid having the help syntax interfere with the command, but that's your call.)
Is it a requirement of good TCL code? What would happen if we don't use the "unset" keyword in a script? Any ill-effects I should know about?
I'm inheriting some legacy code and the errors that come about due to "unset"-ing non-existent variables are driving me up the wall!
It's possible to determine whether a variable exists before using it, using the info exists command. Be sure that if you're not using unset, that you don't upset the logic of the program somewhere else.
There's no Tcl-specific reason to unset a variable, that is, it's not going to cause a memory leak or run out of variable handles or anything crazy like that. Using unset may be a defensive programming practice, because it prevents future use of a variable after it's no longer relevant. Without knowing more about the exact code you're working with, it's hard to give more detailed info.
In addition to the other responses, if your Tcl version is new enough, you can also use:
unset -nocomplain foo
That'll unset foo if it exists, but won't complain if it doesn't.
Depends on the system stats it may give "unable to allocate bytes" issue as and when your script is storing huge data into variables and arrays. it'll break once the cache or RAM is full saying "unable to allocate XXXXXXXX bytes".
Make sure you're not storing that much data into variables, otherwise do unset once the use is over for the respective datasets(variables)
For note as I don't seem able to comment on the "info exists" above;
I use this form often..
if { [info exists pie] && [$pie == "ThisIsWhatIWantInPie"]} {
puts "I found what I wanted in pie."
} else {
puts "Pie did not exist; but I still did not error,TCL's evaluation \
will see the conditional failed on the [info exists] and not \
continue onto the comparison."
}
In addition to the other responses, I want to add that, if you want to neglect the errors raising as a result of unsetting non-existent variable use 'catch'.
#!/bin/bash
catch {unset newVariable}