Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
354 views
in Technique[技术] by (71.8m points)

gtk - The signal.connect syntax

I am trying to create a window with two FileChooserButtons. The first one should help the user pick a directory, thus I am using the action Select_folder; the second is to allow the user pick a file.

The problem is that I wanted the second one to change the current folder depending on the choice the user made in the first one.

My initial idea was to use Signal.connect, as in the line:

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

However, this is getting me the following compilation error:

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)

I've also tried adding (callback)folder_changed as per this mail communication at vala mailing list, to no avail.

This is the whole code:

[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. It is certainly my lack of understanding about this particular syntax, but since I am stuck, I would appreciate a pointer to get me out of it.

  2. As an extra, less important point, what is the best practice: to split and indent long lines or to allow them in the code?

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

A callback for Gtk needs to include a parameter for the object that generated the signal. Also Genie and Vala have syntax support for GLib signals to make signals easier to work with. Here is an example based on your code:

[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()

A few points to note:

  • The signal name, "selection_changed" has become an attribute of folder_chooser which you then connect to. The Vala compiler does the conversion to GLib.Signal at compile time
  • The FileChooserButton, folder_chooser, has been removed from the scope of the class. It is now accessed by being passed as an argument to the callback. So it is defined as a parameter of the callback function
  • You will notice the parameter for the callback expects a FileChooser type and not a FileChooserButton type. This is because the selection_changed signal is part of the FileChooser interface that the FileChooserButton then implements. This effectively gives a FileChooserButton more than one type
  • Although _file_chooser is declared so it is available within the whole scope of the class, it has been made only accessible within the class by using the underscore

Using Signal.connect() is much closer to the C API for Gtk. If you need to do this then the following works based on your original code:

[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()

A few points to note:

  • Yes you do need to cast the callback to GLib.Callback as you found in the mail message you linked to
  • The instance data you need is the Window object you have created the FileChooserButton for, so changing null to self works here
  • Vala will put instance data as the first parameter, so to override the default you have to use a CCode attribute, that is [CCode( instance_pos = 2 )] in this case
  • The object generating the signal is still expected to be the first parameter of the callback function, so it is there in the definition even though it is unused in this example. This is defined as Widget type, but you can change this to FileChooser to use the get_uri() call

For your code formatting question I prefer to split long lines, as you can see. I'm not sure there is an agreed "best practise" for Genie as yet.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...