0

Can a button in tcl could be linked to multiple command line arguments ? I have a code which runs when a button is clicked, a progressbar code with time in seconds is also linked with it , and should start at same time when this button is pressed. I put both procs as a command in button command argument using {} but it fails with Error.

Code Snippet

button .b -image $p -command {progressbar 300 run_structural_comparision}

proc progressbar {seconds} {
    ttk::progressbar .pg -orient horizontal -mode determinate -maximum $seconds
    pack .pg -side left
    update idletasks

    # Do some real work here for $seconds seconds
    for {set i 0} {$i < $seconds} {incr i} {
        after 1000;       # Just waiting in this example, might as well do something useful here
        .pg step;         # After some part of the work, advance the progressbar
        update idletasks; # Needed to update the progressbar
    }

    # Done, clean up the dialog and progressbar
}


proc run_structural_comparision {} {
    type_run
    global ENTRYfilename ENTRYfilename2 curDIR curDIR2 typep reflib compLib rundir hvt_verilog logfile
    set path [concat $reflib $compLib]
##    set path [concat $ENTRYfilename $ENTRYfilename2] 
    puts $path
    set str "compare_structure -overlap_when -type {timing constraint} -report compare_structure_"
    set trt ".txt"
    set structure [concat [string trim $str][string trim $typep][string trim $trt] $path]
    puts $structure
    puts $rundir
    cd $rundir
    set filename [concat "compare_structure_" $typep ".tcl"]
    if  {[ file exists $rundir/$filename] == 1 } {
        exec rm -rf $rundir/compare_structure_$typep.tcl
    }

2 Answers 2

2

A button's -command callback is a Tcl script. It will be evaluated at the global level of the stack. If you want to run two commands, you can just put a script in there to run the two commands:

button .b -command { command_1; command_2 }

This will run them sequentially. Tcl is naturally single-threaded as that is by far the easiest programming model for people to work with. It's possible to make code that works by doing callbacks to appear to be doing multiple things at once (that's how Tk works, just like virtually all other GUIs on the planet) but you're really only doing one thing at a time.

But your real question…

The core of what you need is a way to run the program that takes a long time in the background so that you can monitor it and continue to update the GUI. This is not a trivial topic, unfortunately, and the right answer will depend on exactly what is going on.

One of the simplest techniques is where the CPU-bound processing is done in a subprocess. In that case, you can run the subprocess via a pipeline and set fileevent to give you a notification callback when output is produced or the program terminates. Tcl is very good at this sort of thing; things that many languages have as very advanced techniques just feel natural when done with Tcl, as a great deal of thought has been put into how to make I/O callbacks work nicely.

If it's in-process and long-running without the opportunity for callbacks, things get more complex as you have to have the processing and the GUI updates in different threads. Which isn't too hard if you've got everything set up right, but which might require substantial re-architecting of your program (as it is usual for threads in Tcl to be extremely strongly partitioned from each other).

Sign up to request clarification or add additional context in comments.

Comments

1

The simplest thing to do is to create a procedure that calls the two functions. If you wantie:

proc on_button_press {seconds} {
    after idle [list progressbar $seconds]
    after idle [list run_structural_comparision]
}

You can put multiple calls in the immediate button handler command string but it quickly gets complicated. But in short, use a semicolon to separate the two commands.

Your use if update idletasks should be considered a "code smell". ie: avoid it. In this case, in the progressbar function, setup the bar then just have everything else called by after calls to update the state of the progress.

I suspect your rm -rf may not do what you want. It it likely to lockup the interface as you get nothing back until the command has completed. Better is to write a function to walk the directory tree and delete the files with file delete and you can then raise progress events as you go and keep the UI alive by breaking up the processing into chunks using after again.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.