Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Invalid object conversion [ObjectConversionDefect] gobject.Object to StringObject #128

Open
gavr123456789 opened this issue May 23, 2021 · 7 comments

Comments

@gavr123456789
Copy link

listitem.getItem() returns Object
StringObject is ref object of gobject.Object
So it looks like Nim is missing UpCast? Or am I misunderstanding something.
Googling nim upcast get nothing.

import gintro/[gtk4, gobject, gio]
import std/with

proc setup_cb(factory: gtk4.SignalListItemFactory, listitem: gtk4.ListItem) =
  listitem.setChild(newLabel(""))
  
proc bind_cb(factory: gtk4.SignalListItemFactory, listitem: gtk4.ListItem) =
  var 
    lb = listitem.getChild().Label
    strobj = listitem.getItem().StringObject
    txt = gtk4.getString(strobj)
  echo txt
  lb.text = txt

proc unbind_cb(factory: gtk4.SignalListItemFactory, listitem: gtk4.ListItem) =
  echo "unbind"

proc teardown_cb(factory: gtk4.SignalListItemFactory, listitem: gtk4.ListItem) =
  listitem.setChild (nil)

proc activate(app: gtk4.Application) =
  let
    window = newApplicationWindow(app)
    scr = newScrolledWindow()
  
  var 
    sl = gtk4.newStringList("one", "two", "three", "four")
    ls = cast[ListModel](sl)
    ns = gtk4.newNoSelection(ls)
    factory = gtk4.newSignalListItemFactory()
    lv = newListView(ns, factory)
  
  scr.setChild lv


  with factory:
    connect("setup", setup_cb)
    connect("bind", bind_cb)
    connect("unbind", unbind_cb)
    connect("teardown", teardown_cb)


  with window:
    title = "Nim ListView"
    setChild scr
    show
  


proc main =
  let app = newApplication("org.gtk.example")
  app.connect("activate", activate)
  discard run(app)

main()

Error:

/home/gavr/Projects/Nim/gintro/examples/gtk4/ListView.nim(55) ListView
/home/gavr/Projects/Nim/gintro/examples/gtk4/ListView.nim(53) main
/home/gavr/.nimble/pkgs/gintro-#head/gintro/gio.nim(31021) run
/usr/lib/nim/core/macros.nim(559) connect_for_signal_cdecl_activate5
/home/gavr/Projects/Nim/gintro/examples/gtk4/ListView.nim(45) activate
/home/gavr/.nimble/pkgs/gintro-#head/gintro/gtk4.nim(3824) setChild
/usr/lib/nim/core/macros.nim(565) connect_for_signal_cdecl_bind2
/home/gavr/Projects/Nim/gintro/examples/gtk4/ListView.nim(10) bind_cb
/usr/lib/nim/system/fatal.nim(49) sysFatal
Error: unhandled exception: invalid object conversion [ObjectConversionDefect]
@gavr123456789
Copy link
Author

gavr123456789 commented May 23, 2021

Wait, no, this is even more strange.
Label -> Widget
StringObject -> Object
Upcast of Widget to Label works with 'smart' upcast with .Label
Upcast of Object to StringObject throw exeption and works only with unsafe C 'up'cast.

This version can compiles:

import gintro/[gtk4, gobject, gio]
import std/with

proc setup_cb(factory: gtk4.SignalListItemFactory, listitem: gtk4.ListItem) =
  listitem.setChild(newLabel(""))
  
proc bind_cb(factory: gtk4.SignalListItemFactory, listitem: gtk4.ListItem) =
  var 
    lb = listitem.getChild().Label
    strobj = cast[StringObject](listitem.getItem())
    txt = gtk4.getString(strobj)
  echo txt
  lb.text = txt

proc unbind_cb(factory: gtk4.SignalListItemFactory, listitem: gtk4.ListItem) =
  echo "unbind"

proc teardown_cb(factory: gtk4.SignalListItemFactory, listitem: gtk4.ListItem) =
  listitem.setChild (nil)

proc activate(app: gtk4.Application) =
  let
    window = newApplicationWindow(app)
    scr = newScrolledWindow()
  
  var 
    sl = gtk4.newStringList("one", "two", "three", "four")
    ls = cast[ListModel](sl)
    ns = gtk4.newNoSelection(ls)
    factory = gtk4.newSignalListItemFactory()
    lv = newListView(ns, factory)
  
  scr.setChild lv


  with factory:
    connect("setup", setup_cb)
    connect("bind", bind_cb)
    connect("unbind", unbind_cb)
    connect("teardown", teardown_cb)


  with window:
    title = "Nim ListView"
    setChild scr
    show
  


proc main =
  let app = newApplication("org.gtk.example")
  app.connect("activate", activate)
  discard run(app)

main()

@StefanSalewski
Copy link
Owner

Thanks for reporting, I will try to investigate that issue soon.

@StefanSalewski
Copy link
Owner

Have you a working C code example? Starting from working C code is always the best way to debug. All the listview/treeview stuff is by far the hardest in GTK. In C code a lot of casts are performed, in Nim we should never use direct casts but provide converters or something similar. I really should read the Krause book again and all the tutorials. Well I will see if I can discover the problem.

@gavr123456789
Copy link
Author

@StefanSalewski
Copy link
Owner

Thanks, that will make debugging much easier.

@StefanSalewski
Copy link
Owner

OK, I have compiled and run his C example. But there is only a block of text displayed, really not that interesting.

I started with his C code and converted it to Nim, see below. Works, but we need two ugly casts indeed. getItem() returns a plain GObject, but we need a StringObject. And the other is StringList to StringModel. With getChild() and Label a conversion works because both are Widgets.

For fixing all this as clean as possible we have to learn the API well and then we can see if we can somehow introduce automatic conversions or give the user type safe converters. I think a few are already available. But all that is a task for maybe 6 weeks fulltime: Learning, writing documentation (the GTK4 book, I guess 50 pages), testing and providing converters when necessary. As long as we have zero users (well one with you) my motivation for that is not that large. Maybe just tell the figma guy what you did with ValaGtk and ask him how you can do that with figma. Maybe we should indeed switch to fidget? In another thread they recently promoted flutter.

Yesterday I got a request from Japan from someone who wants to translate the Nim Beginner book to Japanese, so I think I will try to add a few more pages to the book now. And I want to do some GTK programming myself again, have done nothing in the last years.

import gintro/[gtk4, gobject, gio]

proc setup_cb(factory: SignalListItemFactory; listitem: ListItem) =
  let lb = newLabel()
  listitem.setChild(lb)

proc bind_cb(self: SignalListItemFactory; listitem: ListItem) =
  let lb: Label = Label(listItem.getChild)
  let strobj: StringObject = cast[StringObject](listItem.getItem)
  let text = strobj.getString
  lb.setText(text)

proc unbind_cb(self: SignalListItemFactory; listitem: ListItem) =
  # There's nothing to do here.
  # If you does something like setting a signal in bind_cb,
  # then disconnecting the signal is necessary in unbind_cb.
  discard

proc teardown_cb(factory: SignalListItemFactory; listitem: ListItem) =
  listitem.setChild (nil)
  # When the child of listitem is set to NULL, the reference to GtkLabel will be released and lb will be destroyed.
  # Therefore, g_object_unref () for the GtkLabel object doesn't need in the user code.

# ----- activate, open, startup handlers -----
proc app_activate(app: Application) =
  echo "activate"
  let win = newApplicationWindow(app)
  win.setDefaultSize(600, 400)
  let scr = newScrolledWindow()
  win.setChild (scr)
  let arrax = ["one", "two", "three", "four"]
  let sl: gio.ListModel = cast[ListModel](newStringList(arrax)) # GtkStringList — A list model for strings
  let ns: NoSElection = newNoSelection(sl) # A selection model that does not allow selecting anything
  let factory: SignalListItemFactory = newSignalListItemFactory()
  factory.connect("setup", setup_cb)
  factory.connect("bind", bind_cb)
  factory.connect("unbind", unbind_cb)
  factory.connect("teardown", teardown_cb)
  let lv = newListView(ns, factory)
  scr.setChild (lv)
  win.show

proc app_startup(app: Application) =
  echo "startup"

proc main =
  let app = newApplication("org.gtk.example") # "com.github.ToshioCP.list1"
  app.connect("startup", app_startup)
  app.connect("activate", app_activate)
  let status = app.run
  quit(status)

main()

@gavr123456789
Copy link
Author

gavr123456789 commented May 24, 2021

Heh, looks like you got the same program with same 2 C cast.
I think this problem can be solved with new concepts #130 when they will come out

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

No branches or pull requests

2 participants