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. Вы также можете изменить его, чтобы обеспечить лучшую обратную связь с пользователем.

Другие вопросы по тегам