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.
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
What I'm trying to achieve:
From a Ruby process, spawning a subprocess
The subprocess should print as normal back to the terminal. By "normal", I mean the process shouldn't miss out color output, or ignore user input (STDIN).
For that subprocess, capturing STDOUT/STDERR (jointly) e.g. into a String variable that can be accessed after the subprocess is dead. Escape characters and all.
Capturing STDOUT/STDERR is possible by passing a different IO pipe, however the subprocess can then detect that it's not in a tty. For example git log will not print characters that influence text color, nor use it's pager.
Using a pty to launch the process essentially "tricks" the subprocess into thinking it's being launched by a user. As far as I can tell, this is exactly what I want, and the result of this essentially ticks all the boxes.
My general tests to test if a solution fits my needs is:
Does it run ls -al normally?
Does it run vim normally?
Does it run irb normally?
The following Ruby code is able to check all the above:
to_execute = "vim"
output = ""
require 'pty'
require 'io/console'
master, slave = PTY.open
slave.raw!
pid = ::Process.spawn(to_execute, :in => STDIN, [:out, :err] => slave)
slave.close
master.winsize = $stdout.winsize
Signal.trap(:WINCH) { master.winsize = $stdout.winsize }
Signal.trap(:SIGINT) { ::Process.kill("INT", pid) }
master.each_char do |char|
STDOUT.print char
output.concat(char)
end
::Process.wait(pid)
master.close
This works for the most part but it turns out it's not perfect. For some reason, certain applications seem to fail to switch into a raw state. Even though vim works perfectly fine, it turned out neovim did not. At first I thought it was a bug in neovim but I have since been able to reproduce the problem using the Termion crate for the Rust language.
By setting to raw manually (IO.console.raw!) before executing, applications like neovim behave as expected, but then applications like irb do not.
Oddly spawning another pty in Python, within this pty, allows the application to work as expected (using python -c 'import pty; pty.spawn("/usr/local/bin/nvim")'). This obviously isn't a real solution, but interesting nonetheless.
For my actual question I guess I'm looking towards any help to resolving the weird raw issue or, say if I've completely misunderstood tty/pty, any different direction to where/how I should look at the problem.
[edited: see the bottom for the amended update]
Figured it out :)
To really understand the problem I read up a lot on how a PTY works. I don't think I really understood it properly until I drew it out. Basically PTY could be used for a Terminal emulator, and that was the simplest way to think of the data flow for it:
keyboard -> OS -> terminal -> master pty -> termios -> slave pty -> shell
|
v
monitor <- OS <- terminal <- master pty <- termios
(note: this might not be 100% correct, I'm definitely no expert on the subject, just posting it incase it helps anybody else understand it)
So the important bit in the diagram that I hadn't really realised was that when you type, the only reason you see your input on screen is because it's passed back (left-wards) to the master.
So first thing's first - this ruby script should first set the tty to raw (IO.console.raw!), it can restore it after execution is finished (IO.console.cooked!). This'll make sure the keyboard inputs aren't printed by this parent Ruby script.
Second thing is the slave itself should not be raw, so the slave.raw! call is removed. To explain this, I originally added this because it removes extra return carriages from the output: running echo hello results in "hello\r\n". What I missed was that this return carriage is a key instruction to the terminal emulator (whoops).
Third thing, the process should only be talking to the slave. Passing STDIN felt convenient, but it upsets the flow shown in the diagram.
This brings up a new problem on how to pass user input through, so I tried this. So we basically pass STDIN to the master:
input_thread = Thread.new do
STDIN.each_char do |char|
master.putc(char) rescue nil
end
end
that kind of worked, but it has its own issues in terms of some interactive processes weren't receiving a key some of the time. Time will tell, but using IO.copy_stream instead appears to solve that issue (and reads much nicer of course).
input_thread = Thread.new { IO.copy_stream(STDIN, master) }
update 21st Aug:
So the above example mostly worked, but for some reason keys like CTRL+c still wouldn't behave correctly. I even looked up other people's approach to see what I could be doing wrong, and effectively it seemed the same approach - as IO.copy_stream(STDIN, master) was successfully sending 3 to the master. None of the following seemed to help at all:
master.putc 3
master.putc "\x03"
master.putc "\003"
Before I went and delved into trying to achieve this in a lower level language I tried out 1 more thing - the block syntax. Apparently the block syntax magically fixes this problem.
To prevent this answer getting a bit too verbose, the following appears to work:
require 'pty'
require 'io/console'
def run
output = ""
IO.console.raw!
input_thread = nil
PTY.spawn('bash') do |read, write, pid|
Signal.trap(:WINCH) { write.winsize = STDOUT.winsize }
input_thread = Thread.new { IO.copy_stream(STDIN, write) }
read.each_char do |char|
STDOUT.print char
output.concat(char)
end
Process.wait(pid)
end
input_thread.kill if input_thread
IO.console.cooked!
end
Bundler.send(:with_env, Bundler.clean_env) do
run
end
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
}
Have a small example pasted down which launches a system tray icon using gtk2 gem.
#!/usr/bin/env ruby
# encoding: UTF-8
require 'gtk2'
si=Gtk::StatusIcon.new
si.stock=Gtk::Stock::DIALOG_INFO
si.tooltip='StatusIcon'
si.signal_connect('activate'){|icon| icon.blinking=!(icon.blinking?)}
info=Gtk::ImageMenuItem.new(Gtk::Stock::INFO)
info.signal_connect('activate'){p "Embedded: #{si.embedded?}"; p "Visible: #{si.visible?}"; p "Blinking: #{si.blinking?}"}
quit=Gtk::ImageMenuItem.new(Gtk::Stock::QUIT)
quit.signal_connect('activate'){Gtk.main_quit}
menu=Gtk::Menu.new
menu.append(info)
menu.append(Gtk::SeparatorMenuItem.new)
menu.append(quit)
menu.show_all
si.signal_connect('popup-menu'){|tray, button, time| menu.popup(nil, nil, button, time)}
si.signal_connect('scroll-event'){|icon, event|
modifier=event.state#A GdkModifierType indicating the state of modifier keys and mouse buttons
##Handle only control and shift key
ctrl_shift=(Gdk::Window::CONTROL_MASK|Gdk::Window::SHIFT_MASK)
mod=modifier&ctrl_shift
case mod
when 0
print "(None)"
when Gdk::Window::CONTROL_MASK
print "Control+"
when Gdk::Window::SHIFT_MASK
print "Shift+"
when (Gdk::Window::CONTROL_MASK|Gdk::Window::SHIFT_MASK)
print "Control+Shift+"
end
##Check for direction
case event.direction
when Gdk::EventScroll::UP
print "up\n"
when Gdk::EventScroll::DOWN
print "down\n"
end
}
exit if defined?(Ocra)
Gtk.main
This works in a second if I run it as a ruby script i.e., ruby my_program.rb
But I would like to distribute it so I chose OCRA which builds me a exe file so that the user can run it without ruby or its packages installed.
So I run ocra my_program.rb --windows and it gives me a nice exe file called my_program.exe.
Problem : When I launch my_program.exe it takes 25 to 30 seconds to load the program.
The system tray icon takes 1 second and takes 30 times more to launch when packaged as a exe file.
Is there any possible way to decrease this load time. Thank you very much in advance.
EDIT: After a heads up I changed the ocra exe creation to ocra my_program.rb --windows --no-lzma. This solution saved me 10 seconds and now it is taking around 18-20 seconds for the program to load.
Can I optimize it more for a shorter load time? Please help.
Please see discussion on related topic here:
http://programming.nullanswer.com/question/28323543
It might be helpful.
This may be an old bug; I found this report. I'm using Sublime 3 but I think this code also works on 2.
When I call self.view.run_command('save') within a plugin, the save does happen -- I can type the file in a console window and see the results. The dirty flag seems to get cleared. But the tab for the file contains a dot rather than an x, indicating the file hasn't been saved. And sure enough, if you try to close it, it asks if you want to save the file.
Is there any way to refresh the file window so it recognizes that the file has been saved?
Here's my plugin code: (This is my first plugin so please excuse obvious style issues)
# Sublime Text plugin to insert output in the OUTPUT_SHOULD_BE comment
# Bind to key with:
# { "keys": ["f12"], "command": "insert_output" },
import sublime, sublime_plugin, pprint, os, re
class InsertOutputCommand(sublime_plugin.TextCommand):
def run(self, edit):
outfile = self.view.file_name().rsplit('.')[0] + ".out"
if not os.path.exists(outfile):
sublime.error_message("Not Found: " + outfile)
return
out_data = open(outfile).read().strip()
region = self.view.find(r"/\* OUTPUT_SHOULD_BE\n", 0)
if region:
self.view.insert(edit, region.end(), out_data)
self.view.run_command('save')
self.view.window().focus_view(self.view)
else:
sublime.error_message("Not Found: OUTPUT_SHOULD_BE")
I'm sure this is probably a terrible hack, but it works:
self.view.run_command("save")
# Refresh the buffer and clear the dirty flag:
sublime.set_timeout(lambda: self.view.run_command("revert"), 10)
The revert command, which must be delayed in order to work, simply brings back whatever is stored in the file. Since the file was successfully saved on disk, this is just the same file that we already see on the screen. In the process, the dirty flag is cleared and the dot on the file tab becomes an x.
Feels very hacky to me and I'd love a more proper solution. But at least it works, ugly or not.