I am comfortable with tcl but newbie with tk.
I want to display some texts on a window and after several search I found here an example that seem good for me.
My issue is that the display is not put in real time but only when the program end.
Here is the main lines of my program.
Main_program.tcl
#!/bin/sh
# -*- tcl -*-
# The next line is executed by /bin/sh, but not tcl \
exec /usr/local/Cellar/tcl-tk/bin/tclsh "$0" "$#"
set DEBUG 1
source ./GUI_mgt.tcl
source ./utils.tcl
for {set i 0} {$i<500} {incr i} {
after 10
debug_puts $i
}
utils.tcl
proc debug_puts {message} {
if {$::DEBUG} { writeToLog $message }
}
GUI_mgt.tcl
package require Tk
grid [text .log -state disabled -width 80 -height 24 -wrap none]
proc writeToLog {msg} {
set numlines [lindex [split [.log index "end - 1 line"] "."] 0]
.log configure -state normal
if {$numlines==24} {.log delete 1.0 2.0}
if {[.log index "end-1c"]!="1.0"} {.log insert end "\n"}
.log insert end "$msg"
.log configure -state disabled
}
Question: what is wrong or missed in this code ?
Do you know some package or example I can use to display sentences on a separate window ?
Note: I use tcl tk 8.6.6 on macOS Sierra 10.12.5
Your test program is not written in an event driven fashion, so the problems with updating the screen are exacerbated.
The after 10 statement will hang the program and not allow the event loop to be reentered. For purposes of testing only, try:
set ::w10 0
after 10 [list set ::w10 1]
vwait ::w10
instead of the after 10 command. Use of the vwait command is not normally recommended, as nested vwait's will not work as expected.
When you have a very busy loop, the Tk program may never have a chance to re-enter its event loop, and the display is never updated.
The simplest solution is to put an
update
statement at the end of the writeToLog procedure. This is not always the best way to handle this type of issue, but it works.
It will also slow down your loop, as the window must be redrawn each time a log message is written.
Another method would be to put the calculation process into a separate thread or process and send the status updates to the main GUI process.
Ok, so for those of you who use the "update" command or "update idletasks" command, you may have noticed that your GUI or Text Widget will freeze, if you try and move the GUI around on the screen with your mouse, or even try to maximize or resize it. This is a side effect of using this command. It causes an "external event". Which causes the GUI to freeze, or to display "Not responding".
Enter the following two lines of code in place of "update idletasks" and you will not have this freezing issue:
after 500 {set go_on yes}
vwait go_on
I had the same issue with a few different TCL scripts. All we were looking for was a bare bone basic Text window, to display all of our "puts" messages in real-time in the log window, or in a TK text widget.
As Brad mentioned, I found the simplest solution was to use the "update" command. I used the command "update idletasks" and it works perfectly for both "puts" or "insert" commands for your text widget.
Here is my example using a basic TK text widget.
I used two separate procs. One to create the Text logging window, and the second for printing a desired message throughout the script.
The result is real-time logging, WHILE your TCL script is running...and not all displayed at once when your script completes. THANK GOD!!! It was so frustrating trying to track down how to do this. Finally I learned about the "update" command. :P
proc REALTIME_TEXT_LOGGING {} {
# Creates a text widget
text .t -yscrollcommand ".scroll set" -setgrid true -width 40 -height 10 -wrap word
scrollbar .scroll -command ".t yview"
pack .scroll -side right -fill y
pack .t -expand yes -fill both
# Set up the tags
.t tag configure big -font {-family helvetica -size 24 -weight bold}
.t tag configure color1 -foreground red
.t tag configure sunken -relief sunken -borderwidth 1
#Button to activate the display of messages
button .b -text "PRINT MESSAGES TO THE LOG" \
-command {
Insert_Text "HELLO EVERYONE!! This is MSG #1"
after 5000
Insert_Text "HELLO EVERYONE!! This is MSG #2"
after 5000
Insert_Text "HELLO EVERYONE!! This is MSG #3"
after 5000
Insert_Text "HELLO EVERYONE!! This is MSG #4"
after 5000
Insert_Text "HELLO EVERYONE!! This is MSG #5"
}
.t window create end -window .b
}
proc Insert_Text {message} {
# Insert text that has the property of the tags from your TK text widget.
# I also used the "puts" command here, to show printing to the default TCL logging window.
.t insert end "$message\n"
puts "$message\n"
update idletasks
}
Related
Here is the case;
There is this app called "termux" on android which allows me to use a terminal on android, and one of the addons are androids API's like sensors, tts engines, etc.
I wanted to make a script in ruby using this app, specifically this api, but there is a catch:
The script:
require('json')
JSON.parse(%x'termux-sensor -s "BMI160 Gyro" -n 1')
-s = Name or partially the name of the sensor
-n = Count of times the command will run
returns me:
{
"BMI160 Gyroscope" => {
"values" => [
-0.03...,
0.00...,
1.54...
]
}
}
I didn't copied and pasted the values, but that's not the point, the point is that this command takes almost a full second the load, but there is a way to "make it faster"
If I use the argument "-d" and not use "-n", I can specify the time in milliseconds to delay between data being sent in STDOUT, it also takes a full second to load, but when it loads, the delay works like charm
And since I didn't specify a 'n' number of times, it never stops, and there is the problem
How can I retrieve the data continuously in ruby??
I thought about using another thread so it won't stop my program, but how can I tell ruby to return the last X lines of the STDOUT from a command that hasn't and will not ever stop since "%x'command'" in ruby waits for a return?
If I understood you need to connect to stdout from a long running process.
see if this works for your scenario using IO.popen:
# by running this program
# and open another terminal
# and start writing some data into data.txt
# you will see it appearing in this program output
# $ date >> data.txt
io_obj = IO.popen('tail -f ./data.txt')
while !io_obj.eof?
puts io_obj.readline
end
I found out a built in module that saved me called PTY and the spawn#method plus thread management helped me to keep a variable updated with the command values each time the command outputted new bytes
I'm new to ruby and this issue is bugging me for a while . Whenever i use gets to take user input , my gets statement is executed right after i run the file .I'm using git Bash to run my file.rb file ,
puts "some unnecessary text"
puts "Hello world"
puts "now you should input something"
x = gets.chomp
puts 36
puts "your input is " + x + " right?"
the program should print the first 3 lines before waiting for an input but it waits for the input right after i run it
$ruby file.rb
|
it waits for eternity unless I press enter . If i write something,
$ ruby file.rb
myInput
some unnecessary text
Hello world
now you should input something
36
your input is myInput right?
it runs okay . So I'm forced to write my input at the beginning .
It's not much of a problem right now but it'll cause a lot if headaches when i write bigger and more complex code . Any solutions ?
ps: It seems the problem only occurs with git Bash (windows) . Powershell works just fine .
It seems that the standard output is buffered.
Try to put at the beginning of the file (first two lines) old_sync = $stdout.sync $stdout.sync = true and a the end of the file (last line) $stdout.sync = old_sync.
The call to the IO#sync= method set the sync mode to true. This cause that all output is immediately flushed, at the end of the script we restore its value to its original old value, see Ruby documentation for details.
In summary:
old_sync = $stdout.sync # cache old value
$stdout.sync = true # set mode to true
# your scripting staff
$stdout.sync = old_sync # restore old value
If this trick works at least you know the reason for the weird behaviour. You can find some explanation also in this SO post.
I'm trying to get gtk.ProgressBar.set_text('Text') to work when I click on a button, prior to launch my subprocess.
Here is my code (full source here):
def on_button_clicked(self, button, progress_bar, filename):
self.execute(progress_bar, filename)
def execute(self, progress_bar, filename):
progress_bar.set_text('Encoding')
progress_bar.pulse()
cmd = ['ffmpeg', '-y',
'-i', filename,
'-r', '30',
'/tmp/test-encode.mkv']
process = sp.Popen(cmd, stdout=sp.PIPE, stderr=sp.PIPE)
process.wait()
progress_bar.set_text('Done')
I tried moving progress_bar.set_text('Encoding') in on_button_clicked() but it doesn't change anything: I click the button, the job is done (the file is duly produced and OK) and only then the progress bar says "Done".
I did my homework and read all related questions, but they either don't use subprocess, or parse "regular" command outputs.
Just add
progress_bar.set_text('Encoding')
while gtk.events_pending(): # this forces GTK to refresh the screen
gtk.main_iteration()
in on_button_clicked() to force GTK to refresh the screen before continuing.
You could even add
progress_bar.set_text('Finished')
while gtk.events_pending(): # this forces GTK to refresh the screen
gtk.main_iteration()
in execute once the file is written.
I'm making a command line tool using Ruby. It will print a lot of text on screen. Currently, I'm using shell pipeline (may_app | more) to do so. But I think it's better to has a default pager.
It's just like what you see when execute git log . One can disable pager by using git --nopager log.
I've done quite much google work and find one gem: hirb , but it seems a little overkill.
After many tries, I'm current using shell wrapper to do so:
#!/bin/bash
# xray.rb is the core script
# doing the main logic and will
# output many rows of text on
# screen
XRAY=$HOME/fdev-xray/xray.rb
if [ "--nopager" == "$1" ]; then
shift
$XRAY $*
else
$XRAY $* | more
fi
It works. But is there a better way?
You are doing it right. But instead using more you'd better get a pager from $PAGER environment variable, if any.
Some people prefer less to more for example, and others have their favorite parser options set in this var.
You can use the pipe in Ruby via a call to system and provide the options (along with a nice help interface) like so:
require 'optparse'
pager = ENV['PAGER'] || 'more'
option_parser = OptionParser.new do |opts|
opts.on("--[no-]pager",
"[don't] page output using #{pager} (default on)") do |use_pager|
pager = nil unless use_pager
end
end
option_parser.parse!
command = "cat #{ARGV[0]}"
command += " | #{pager}" unless pager.nil?
unless system(command)
STDERR.puts "Problem running #{command}"
exit 1
end
Now, you support --pager and --no-pager on the command line, which is nice to do.
The Problem
I want to press a key when I have a line highlighted and convert from a single line:
JGLogEntry *logEntry = [JGLogEntry applicationNoWindowsFrom:date1 to:date2 intoMOC:mockRawMOC];
to a multiline statement:
JGLogEntry *logEntry = [JGLogEntry applicationNoWindowsFrom:date1
to:date2
intoMOC:mockRawMOC];
What I've Tried
I've got a simple ruby script that almost gets me there.
#!/usr/bin/env ruby
s = STDIN.read
s.gsub!(/(:.+?\w) (\w.+?)/,'\1' + "\n\t" +'\2')
print s
When I set the output to "Replace Selection", I get this:
JGLogEntry *logEntry = [JGLogEntry applicationNoWindowsFrom:date1
to:date2
intoMOC:mockRawMOC];
When I set the output to "Place on Clipboard", then paste it in, I get the desired result:
JGLogEntry *logEntry = [JGLogEntry applicationNoWindowsFrom:date1
to:date2
intoMOC:mockRawMOC];
However, this is two keypresses which is clumsy.
Any ideas how I can get the replaced text to obey Xcode's auto indent rules?
Check the pre-installed script for "Convert tabs to spaces", and how it executes an in-line applescript. Use that to tell XCode to perform the menu item
Edit > Format > Re-Indent
I'm not sure how you do that with ruby, nor the details about the applescript content, but I would wager it's fairly straight-forward..