Skip to content
This repository has been archived by the owner on Jul 22, 2024. It is now read-only.

Add destroyall #31

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open

Add destroyall #31

wants to merge 1 commit into from

Conversation

timholy
Copy link
Member

@timholy timholy commented Jan 3, 2014

This is useful in interactive settings where you pop open a large number of windows.

@vtjnash
Copy link
Contributor

vtjnash commented Jan 3, 2014

This seems useful. Do we want to just use the existing Gtk builtin though: gtk_window_list_toplevels

@timholy
Copy link
Member Author

timholy commented Jan 3, 2014

Good idea. So many functions to learn...

I should mention: one trick Matlab has is the ability to set the HandleVisibility on objects. This becomes important if one has an implementation of gcf(). Doing this on the Julia side would let us pull such tricks.

However, my sense is that such functionality, if desired, should be implemented at a higher level (e.g., plotting packages). So unless I hear otherwise, I'll rework this to use gtk_window_list_toplevels.

@vtjnash
Copy link
Contributor

vtjnash commented Jan 3, 2014

I think the need to have "hidden" windows is a function of their plotting interface developing from a model with a single gcf(). We should be better about having the user in control of the graphics handles so that, as you said, higher level packages can provide other abstractions.

Yeah, I'm pretty much learning the scope of Gtk's functionality along with you here -- I've just read through the list of functions more times trying to build the constructors.

@timholy
Copy link
Member Author

timholy commented Jan 3, 2014

This seems a little harder than I expected. Here's the naive version:

function destroyall()
    list = gslist(ccall((:gtk_window_list_toplevels,libgtk), Ptr{GSList{GObject}}, ()))
    # The equivalent (?) of calling g_object_ref for each item, see docs on gtk_window_list_toplevels
    wins = Set()
    for win in list
        push!(wins, win)
    end
    @show length(wins)
    for win in wins
        destroy(win)
    end
end

Testing:

using Gtk.ShortNames, Base.Graphics
win1 = Window("1")
win2 = Window("2")
win1 = Window("3")   # note this overwrites win1
gc()   # everything works if you don't do this
destroyall()

with result

ERROR: GObject didn't have a corresponding Julia object
 in convert at /home/tim/.julia/Gtk/src/gtktypes.jl:66
 in next at /home/tim/.julia/Gtk/src/gslist.jl:27
 in destroyall at /home/tim/.julia/Gtk/src/windows.jl:24

Once the Julia reference to the first win1 gets garbage-collected, it seems the default list-iteration fails. (Note this fails before it ever gets to the @show length(wins), so it breaks on any attempt to iterate over the list.)

I see several options:

  • Go back to maintaining a reference to each window that gets created (e.g., in allwindows = Set())---that way, for windows there will always be a corresponding Julia object. This is a special-purpose solution to the problem, and seems to be a more complicated variant of my initial pull request.
  • Bypass the standard iteration for GSList in this particular case, and manually iterate over the list. Also special-purpose.
  • Set up a parallel "weakref" list iteration facility for cases where Gtk objects may not have corresponding Julia objects. (essentially, generalizing the previous option)
  • Change convert (in gtktypes.jl:66) to construct a wrapper object when necessary, rather than throwing an error.

Of those four, the first two seem distasteful.

I may not be understanding the structure here well enough to see an even better solution right under my nose.

@vtjnash
Copy link
Contributor

vtjnash commented Jan 4, 2014

This is some nice clean example code: http://src.chromium.org/chrome/trunk/src/chrome/browser/lifetime/application_lifetime_gtk.cc

The equivalent (?) of calling g_object_ref for each item, see docs on gtk_window_list_toplevels

It might be easiest to just write this using ccalls.

Strangely, your code seems to work for me.

Bypass the standard iteration for GSList in this particular case, and manually iterate over the list. Also special-purpose.

You could declare the type of the GSList to be Void so that it returned Ptr{Void} objects

Change convert (in gtktypes.jl:66) to construct a wrapper object when necessary, rather than throwing an error.

This is preferable, and needs to be done eventually.

@timholy
Copy link
Member Author

timholy commented Jan 4, 2014

Hmm, anyone know a way to get the GType from an instance? Presumably that's what's needed to create the correct Julia object, given a naked Ptr{GObject}. There's the G_TYPE_FROM_INSTANCE C-macro...

Is this part of what's provided by #27? I confess I haven't had the time to learn all the background needed to follow that work.

@bfredl
Copy link
Contributor

bfredl commented Jan 4, 2014

Already in master there is
G_OBJECT_CLASS_TYPE(w) = G_TYPE_FROM_CLASS(G_OBJECT_GET_CLASS(w))
In my wrapper methods I do

#used when returning gobject 
function GSubType{T<:GObjectI}(::Type{T}, hnd::Ptr{GObjectI}) 
    if hnd == C_NULL
        error("can't handle NULL returns yet")
    end
    g_type = GLib.G_OBJECT_CLASS_TYPE(hnd)
    regtype = GLib.register_gtype(g_type)
    #TODO: lookup existing julia wrapper first
    return regtype.wrapper(hnd)::T
end

this is inside GI.jl (gobject-introspection) now, but doesn't actually depend on the gobject-introspection library, only on libgobject.

@timholy
Copy link
Member Author

timholy commented Jan 4, 2014

Thanks, that's very helpful. So presumably one could go ptr to instance -> ptr to type and then look up the corresponding Julia type in a dict of Gtk ptr to types.

I guess the next question is how to build the dictionary. The only workable approach I see is to compile the dict as instances are constructed, i.e., change the GType macro so that all the constructors include

gtype_dict[unsafe_load(convert(Ptr{Ptr{Void}},handle))] = $(esc(gname))

@bfredl
Copy link
Contributor

bfredl commented Jan 4, 2014

Well if one does
ccall((:gtk_test_register_all_types,libgtk), Void, ())
before applying the @gtype macros, the dict can be populated directly by the macro, by using g_type_from_name. That's what I do in my branch. I might put together a new PR soon that implements only this without adding the libgintrospection dependency.

@timholy
Copy link
Member Author

timholy commented Jan 4, 2014

Gotcha. I can verify

using Gtk.ShortNames
ccall((:gtk_test_register_all_types, Gtk.libgtk), Void, ())
win = Window("Testing");
p = Gtk.G_OBJECT_CLASS_TYPE(win)
bytestring(ccall((:g_type_name,Gtk.libgobject), Ptr{Uint8}, (Ptr{Void},), p))
ccall((:g_type_from_name,Gtk.libgobject), Ptr{Void}, (Ptr{Uint8},), "GtkWindow")

does indeed do what one hopes.

@timholy
Copy link
Member Author

timholy commented Jan 4, 2014

(gtk_test_register_all_types is one hard-to-find function!)

@vtjnash
Copy link
Contributor

vtjnash commented Jan 4, 2014

I'm not sure why you are calling gtk_test_register_all_types, your code works just as well without it.

@timholy
Copy link
Member Author

timholy commented Jan 4, 2014

Simply because it was recommended. I suppose that when one builds an instance, it registers that type?

So does my solution to add

gtype_dict[unsafe_load(convert(Ptr{Ptr{Void}},handle))] = $(esc(gname))

to macro GType seem reasonable?

Then a "safe" conversion method might be

G_OBJECT_GET_CLASS(p::Ptr{GObjectI}) = unsafe_load(convert(Ptr{Ptr{Void}},p))
G_OBJECT_CLASS_TYPE(w::Union(GObject, Ptr{GObjectI})) = G_TYPE_FROM_CLASS(G_OBJECT_GET_CLASS(w))

function j_object(p::Ptr{GObjectI})
    gtype = G_OBJECT_CLASS_TYPE(p)
    if !haskey(gtype_dict, gtype)
        T = eval(symbol(bytestring(ccall((:g_type_name,libgobject), Ptr{Uint8}, (Ptr{Void},), gtype))))
        gtype_dict[p] = T
    end
    gtype_dict[p](p)
end

The eval fallback is there in case one gets handed an object that has never been explicitly constructed.

@vtjnash
Copy link
Contributor

vtjnash commented Jan 4, 2014

no. but I think bfredl is preparing a pull request now (related to #27) which will include the elements needed to support this

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants