Execl ошибка отладки
цель
Моя цель - создать простой интерфейс для Pandoc. Я узнал, что execl - это хороший способ вызова исполняемых файлов в системе.
Обратите внимание в следующем коде на функцию btn_pressed, которая вызывает pandoc с помощью упомянутого метода.
[indent=4]
uses
Posix
Gtk
class TestWindow:Window
_file_chooser:FileChooserButton
_entry:Gtk.Entry
_button:Gtk.Button
_file:File
construct()
title = "Pandoc GUI"
window_position = WindowPosition.CENTER
destroy.connect( Gtk.main_quit )
var folder_chooser = new FileChooserButton("Choose a Folder",FileChooserAction.SELECT_FOLDER)
folder_chooser.set_current_folder( Environment.get_home_dir() )
//I used selection_changed directly as per the question in stack_exchange
//http://stackru.com/questions/34689763/the-signal-connect-syntax
folder_chooser.selection_changed.connect( folder_changed )
_file_chooser = new FileChooserButton("Choose a File",FileChooserAction.OPEN)
_file_chooser.set_current_folder( Environment.get_home_dir() )
_file_chooser.file_set.connect( file_changed )
_entry = new Gtk.Entry()
_entry.set_text("Here the file name")
_button = new Button.with_label("Convert to pdf")
_button.set_sensitive(false)
_button.clicked.connect(btn_pressed)
var box = new Box( Orientation.VERTICAL, 0 )
box.pack_start( folder_chooser, true, true, 0 )
box.pack_start( _file_chooser, true, true, 0 )
box.pack_start( _entry, true, true, 0 )
box.pack_start( _button, true, true, 0 )
add( box )
def folder_changed( folder_chooser_widget:FileChooser )
folder:string = folder_chooser_widget.get_uri()
_file_chooser.set_current_folder_uri( folder )
def file_changed ( file_chooser_widget: FileChooser )
_file = File.new_for_uri(file_chooser_widget.get_uri())
try
info:FileInfo = _file.query_info (FileAttribute.ACCESS_CAN_WRITE, FileQueryInfoFlags.NONE, null)
writable: bool = info.get_attribute_boolean (FileAttribute.ACCESS_CAN_WRITE)
if !writable
_entry.set_sensitive (false)
else
_button.set_sensitive (true)
except e: Error
print e.message
_entry.set_text(_file.get_basename())
def btn_pressed ()
var md_name=_entry.get_text()+".md -s -o "+_entry.get_text()+".pdf"
execl("/usr/bin/pandoc", md_name)
_button.set_sensitive (false)
init
Gtk.init( ref args )
var test = new TestWindow()
test.show_all()
Gtk.main()
ошибка
Во время выполнения я не получаю никакого ответа от своего кода, даже без рендеринга PDF.
Вопрос
- Как отладить выполнение двоичного файла с execl?
1 ответ
Я хотел бы использовать GLib.Subprocess
вызывать внешние команды, потому что это обеспечивает лучший контроль над вводом и выводом из внешней команды. Изменение приведенного ниже примера на execl
должно быть достаточно легко, хотя
Первым делом нужно отсоединить вашу внешнюю команду от вашего оконного объекта. Это делает его более тестируемым. Для этого используется отдельный объект - обертка вокруг Subprocess
вызов. Сохранить этот код как ToPDF.gs
:
namespace FileConverters
class ToPDF
const _command:string = "pandoc"
def async convert( source:string, output:string )
try
var flags = SubprocessFlags.STDOUT_PIPE \
| SubprocessFlags.STDERR_PIPE
var subprocess = new Subprocess( flags,
_command,
source,
output
)
output_buffer:Bytes
yield subprocess.communicate_async( null,
null,
out output_buffer,
null
)
if ( subprocess.get_exit_status() == 0 )
debug( "command successful: \n %s",
(string)output_buffer.get_data()
)
else
debug( "command failed" )
except err:Error
debug( err.message )
ToPDF
класс теперь отделен от остальной части вашего приложения. Это означает, что его можно использовать повторно. Чтобы проиллюстрировать это, ниже показан интеграционный тест, в котором используется класс.
ToPDF
также использует асинхронный код. Поэтому я объясню это в первую очередь. Создание метода асинхронным означает, что он будет работать одновременно с основным потоком приложения. При одновременном вызове внешней программы это означает, что основной поток не блокируется, пока ожидает завершения внешней программы. С помощью async
означает, что функция разделена на две части. Первая часть называется с convert.begin( source, output )
и побежит к yield
команда. В этот момент выполнение программы разделяется на две части. Основной поток вернется к вызывающему convert.begin
, но то, что началось в фоновом режиме, это Subprocess
, Когда Subprocess
закончил возвращается convert
и завершает вызов метода.
Сохранить интеграционный тест как ToPDFTest.gs
:
uses FileConverters
init
var a = new ToPDF()
a.convert.begin( "source_file.md", "output_file.pdf" )
var loop = new MainLoop()
var loop_quitter = new LoopQuitter( loop )
Timeout.add_seconds( 2, loop_quitter.quit )
loop.run()
class LoopQuitter
_loop:MainLoop
construct( loop:MainLoop )
_loop = loop
def quit():bool
_loop.quit()
return false
Компилировать с valac --pkg gio-2.0 ToPDF.gs ToPDFTest.gs
Затем запустите тест с G_MESSAGES_DEBUG=all ./ToPDFTest
Тест использует MainLoop
, который является базовым классом Gtk.Main
, Для имитации длительной программы устанавливается двухсекундный таймаут MainLoop.quit()
вызывается для завершения теста. к несчастью MainLoop.quit()
не имеет правильной подписи функции для Timeout
обратный вызов, так что класс-обертка, LoopQuitter
используется.
Подобные интеграционные тесты часто сохраняются и запускаются перед выпуском программного обеспечения, чтобы убедиться, что приложение работает с другими программными модулями.
Интегрировать ToPDF
с вашим окном вам нужно изменить
execl("/usr/bin/pandoc", md_name)
что-то вроде
var to_pdf = new Fileconverts.ToPDF()
to_pdf.convert.begin( md_name, pdf_name )
Вы можете также захотеть обернуть его в шаблон команды, подобный Избеганию глобальных переменных в Genie. Вы также можете изменить его, чтобы обеспечить лучшую обратную связь с пользователем.