Синтаксис signal.connect

Я пытаюсь создать окно с двумя FileChooserButtons, Первый должен помочь пользователю выбрать каталог, поэтому я использую действие Select_folder; второе - позволить пользователю выбрать файл.

Проблема в том, что я хотел, чтобы второй изменил текущую папку в зависимости от выбора, сделанного пользователем в первом.

Моей первоначальной идеей было использовать Signal.connect, как в строке:

Signal.connect(chooser1, "selection_changed", folder_changed, null)

Тем не менее, это приводит меня к следующей ошибке компиляции:

exercise4_1.gs:62.55-62.68: error: Cannot create delegate without target for instance method or closure
        Signal.connect(chooser1, "selection_changed", folder_changed, null)
                                                      ^^^^^^^^^^^^^^
Compilation failed: 1 error(s), 0 warning(s)

Я также попытался добавить (callback)folder_changed согласно этому почтовому сообщению в списке рассылки vala, но безрезультатно.

Это весь код:

[indent=4]

uses
    Gtk
    GLib

class TestWindow : Window
    chooser1:Gtk.FileChooserButton
    chooser2:Gtk.FileChooserButton
    construct()

        // General characteristics of the window
        title = "File chooser"
        window_position = WindowPosition.CENTER
        destroy.connect(Gtk.main_quit)
        chooser1 = new FileChooserButton(
                                            "Choose a Folder",
                                            FileChooserAction.SELECT_FOLDER
                                            )
        chooser2 = new FileChooserButton(
                                             "Chooser a Folder",
                                             FileChooserAction.OPEN
                                             )
        chooser1.set_current_folder(Environment.get_home_dir())
        chooser2.set_current_folder(Environment.get_home_dir())

        Signal.connect(chooser1, "selection_changed", folder_changed, null)

        var box = new Gtk.Box(Gtk.Orientation.VERTICAL, 0)
        box.pack_start(chooser1, true, true,0)
        box.pack_start(chooser2, true, true,0)
        add(box)


    def folder_changed()
        var folder = chooser1.get_filename()
        chooser2.set_current_folder(folder)


init
    Gtk.init (ref args)
    var test = new TestWindow ()
    test.show_all ()
    Gtk.main ()
  1. Это, конечно, мое отсутствие понимания об этом конкретном синтаксисе, но, поскольку я застрял, я был бы признателен за указатель, чтобы вытащить меня из него.

  2. В качестве дополнительного, менее важного пункта, какова лучшая практика: разбивать и вставлять длинные строки или разрешать их в коде?

1 ответ

Решение

Обратный вызов для Gtk должен включать параметр для объекта, который сгенерировал сигнал. Также Genie и Vala имеют синтаксическую поддержку сигналов GLib, чтобы с ними было легче работать. Вот пример, основанный на вашем коде:

[indent=4]
uses
    Gtk

class TestWindow:Window
    _file_chooser:FileChooserButton

    construct()
        title = "File chooser"
        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() )
        folder_chooser.selection_changed.connect( folder_changed )

        _file_chooser = new FileChooserButton(
                                        "Chooser a File",
                                        FileChooserAction.OPEN
                                        )
        _file_chooser.set_current_folder( Environment.get_home_dir() )

        var box = new Box( Orientation.VERTICAL, 0 )
        box.pack_start( folder_chooser, true, true, 0 )
        box.pack_start( _file_chooser, 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 )

init
    Gtk.init( ref args )
    var test = new TestWindow()
    test.show_all()
    Gtk.main()

Несколько замечаний:

  • Название сигнала, "selection_changed" стал атрибутом folder_chooser который ты тогда connect к. Компилятор Vala выполняет преобразование в GLib.Signal во время компиляции
  • FileChooserButton, folder_chooser, был удален из области видимости класса. Теперь к нему обращаются как к аргументу обратного вызова. Таким образом, он определяется как параметр функции обратного вызова
  • Вы заметите, что параметр для обратного вызова ожидает FileChooser типа а не FileChooserButton тип. Это потому что selection_changed сигнал является частью FileChooser интерфейс, который FileChooserButton затем реализует. Это эффективно дает FileChooserButton более одного типа
  • Хотя _file_chooser объявлен таким образом, что он доступен во всей области видимости класса, он был сделан доступным только внутри класса с помощью подчеркивания

С помощью Signal.connect() гораздо ближе к C API для Gtk. Если вам нужно сделать это, то следующие действия работают на основе вашего исходного кода:

[indent=4]
uses
    Gtk

class TestWindow:Window
    chooser1:FileChooserButton
    chooser2:FileChooserButton
    construct()

        // General characteristics of the window
        title = "File chooser"
        window_position = WindowPosition.CENTER
        destroy.connect( Gtk.main_quit )
        chooser1 = new FileChooserButton(
                                         "Choose a Folder",
                                         FileChooserAction.SELECT_FOLDER
                                        )
        chooser2 = new FileChooserButton(
                                        "Chooser a Folder",
                                        FileChooserAction.OPEN
                                        )
        chooser1.set_current_folder( Environment.get_home_dir() )
        chooser2.set_current_folder( Environment.get_home_dir() )

        Signal.connect( 
                      chooser1, 
                      "selection_changed", 
                      (GLib.Callback)folder_changed,
                      self
                      )

        var box = new Box( Orientation.VERTICAL, 0 )
        box.pack_start( chooser1, true, true, 0 )
        box.pack_start( chooser2, true, true, 0 )
        add( box )

    [CCode( instance_pos = 2 )]
    // or [CCode( instance_pos = -1 )] to always be last
    def folder_changed( folder_chooser:Widget )
        folder:string = chooser1.get_uri()
        chooser2.set_current_folder_uri( folder )

init
    Gtk.init( ref args )
    var test = new TestWindow()
    test.show_all()
    Gtk.main()

Несколько замечаний:

  • Да, вам нужно привести к обратному вызову GLib.Callback как вы нашли в почтовом сообщении, на которое вы ссылались
  • Данные экземпляра вам нужны Window объект, который вы создали FileChooserButton ибо так меняется null в self работает здесь
  • Vala поместит данные экземпляра в качестве первого параметра, поэтому, чтобы переопределить значение по умолчанию, вы должны использовать CCode атрибут, то есть [CCode( instance_pos = 2 )] в этом случае
  • Ожидается, что объект, генерирующий сигнал, будет первым параметром функции обратного вызова, поэтому он присутствует в определении, даже если он не используется в этом примере. Это определяется как Widget типа, но вы можете изменить это на FileChooser использовать get_uri() вызов

Что касается вашего вопроса о форматировании кода, я предпочитаю разбивать длинные строки, как вы можете видеть. Я не уверен, что пока есть согласованная "лучшая практика" для Genie.

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