Closed hamzashahid-blit closed 1 year ago
The (gobject:type-from-name "GtkLabel")
in your code returns 0
, which causes (gtk:make-list-store ...)
to return a null pointer.
To make (gobject:type-from-name "GtkLabel")
return a non-zero value, you need to get this type registered by the GObject system first. For example, you can move the (item1 (make-label :str "BRUHSEIDh"))
or add a seemingly meaningless (gtk:make-label :str "")
before it.
BTW, Gtk.ListStore
has been deprecated in version 4.10, and you should use gio:make-list-store
to create it instead.
I just added an example of a ListView
for your reference:
Thank you so much! I wasn't expecting an answer. That's awesome, I will try it out right now.
In your example, gobj:coerce is not found. I will try and download from latest commit.
In your example, gobj:coerce is not found. I will try and download from latest commit.
It's in cl-glib. You may need to clone it manually or update your Ultralisp distribution.
Yup, I cloned cl-gtk4 and cl-glib and the example works now.
Okay, so ultimately I wanted to create a GtkColumnView. I got to this code:
(gtk:define-application (:name closting
:id "com.closting")
(gtk:define-main-window (window (gtk:make-application-window :application gtk:*application*))
(setf (gtk:window-title window) "Closting")
(let* ((main-model (gtk:make-string-list :strings '("HP Zbook 15" "250$" "300$"
"Infinix S90" "40$" "60$"
"Casio Calculator" "50$" "80$")))
(column-view (gtk:make-column-view :model (gtk:make-single-selection :model main-model)))
(col1-factory (gtk:make-signal-list-item-factory))
(col1 (gtk:make-column-view-column :title "First Col!" :factory col1-factory))
(col2-factory (gtk:make-signal-list-item-factory))
(col2 (gtk:make-column-view-column :title "Second Col!" :factory col2-factory))
(col3-factory (gtk:make-signal-list-item-factory))
(col3 (gtk:make-column-view-column :title "Third Col!" :factory col3-factory)))
(gtk:column-view-append-column column-view col1)
(gtk:column-view-append-column column-view col2)
(gtk:column-view-append-column column-view col3)
(setf (gtk:widget-vexpand-p column-view) t
(gtk:widget-hexpand-p column-view) t)
(flet ((setup (factory item)
(declare (ignore factory))
(setf (gtk:list-item-child item) (gtk:make-label :str "")))
(bind (factory item)
(declare (ignore factory))
(format t "POSSSS: ~a~%" (gtk:list-item-position item))
(when (zerop (mod (1+ (gtk:list-item-position item)) 3))
(setf (gtk:label-text (gobj:coerce (gtk:list-item-child item) 'gtk:label))
(gobj:value-string (gobj:coerce (gtk:list-item-item item) 'gtk:string-object)))))
(unbind (factory item) (declare (ignore factory item)))
(teardown (factory item) (declare (ignore factory item))))
(gtk:connect col1-factory "setup" #'setup)
(gtk:connect col1-factory "bind" #'bind)
(gtk:connect col1-factory "unbind" #'unbind)
(gtk:connect col1-factory "teardown" #'teardown))
(setf (gtk:window-child window) column-view))
(unless (gtk:widget-visible-p window)
(gtk:window-present window))))
But instead of getting every third value in each cell. I get a lot of blank cells. What I need is to somehow skip over elements in the model. Or how should I go about just creating a simple column view at all? Maybe I am doing it wrong. Also kindly inform me if this comment is out of the scope of this issue. I figured it isn't as ColumnView uses ListView. Thanks
Regards, Hamza
You can use StringList
as the elements of your ListModel
:
(gtk:define-application (:name closting
:id "com.closting")
(gtk:define-main-window (window (gtk:make-application-window :application gtk:*application*))
(setf (gtk:window-title window) "Closting")
(let* ((rows (loop :for row :in '(("HP Zbook 15" "250$" "300$")
("Infinix S90" "40$" "60$")
("Casio Calculator" "50$" "80$"))
:collect (gtk:make-string-list :strings row)))
(main-model (gio:make-list-store :item-type (gobj:type-from-name "GtkStringList")))
(column-view (gtk:make-column-view :model (gtk:make-single-selection :model main-model)))
(col1-factory (gtk:make-signal-list-item-factory))
(col1 (gtk:make-column-view-column :title "First Col!" :factory col1-factory))
(col2-factory (gtk:make-signal-list-item-factory))
(col2 (gtk:make-column-view-column :title "Second Col!" :factory col2-factory))
(col3-factory (gtk:make-signal-list-item-factory))
(col3 (gtk:make-column-view-column :title "Third Col!" :factory col3-factory)))
(gtk:column-view-append-column column-view col1)
(gtk:column-view-append-column column-view col2)
(gtk:column-view-append-column column-view col3)
(loop :for row :in rows :do (gio:list-store-append main-model row))
(setf (gtk:widget-vexpand-p column-view) t
(gtk:widget-hexpand-p column-view) t)
(flet ((setup (factory item)
(declare (ignore factory))
(setf (gtk:list-item-child item) (gtk:make-label :str "")))
(unbind (factory item) (declare (ignore factory item)))
(teardown (factory item) (declare (ignore factory item))))
(loop :for factory :in (list col1-factory col2-factory col3-factory)
:for index :from 0
:do (gtk:connect factory "setup" #'setup)
(gtk:connect factory "unbind" #'unbind)
(gtk:connect factory "teardown" #'teardown)
(gtk:connect factory "bind"
(let ((i index))
(lambda (factory item)
(declare (ignore factory))
(setf (gtk:label-text (gobj:coerce (gtk:list-item-child item) 'gtk:label))
(gtk:string-list-get-string (gobj:coerce (gtk:list-item-item item) 'gtk:string-list) i)))))))
(setf (gtk:window-child window) column-view))
(unless (gtk:widget-visible-p window)
(gtk:window-present window))))
Thanks a lot! Just one final question, do you have any idea of how to store custom types in a list store? For example: Instead of every row being a StringList, how can I make it something like:
(defclass product-info (gobject:object)
((name :initarg :name
:accessor name)
(cost :initarg :cost
:accessor cost)
(sale :initarg :sale
:accessor sale)))
This does not work as it says something about gobject:object being a forward referenced class, whatever that means...
If you don't require eql
to guarantee object equality, using print1-to-string
and read-from-string
seems to be the simplest way. Otherwise, you may need some mechanism to identify objects. For example:
Also, why did we have to put a (let ((i index))) around tthe bind lambda function?
If you don't require
eql
to guarantee object equality, usingprint1-to-string
andread-from-string
seems to be the simplest way. Otherwise, you may need some mechanism to identify objects. For example:
- You can put objects in an array and use the array index to retrieve the object.
- Use a UUID library or similar to generate a unique integer or string identifier for an object, and use it as a key in a hash table to retrieve the object.
Wait, so what does equality have to do with having custom types?
Also, why did we have to put a (let ((i index))) around tthe bind lambda function?
Since the loop variables in loop
are mutable in most implementations, we need to make the closure immutably capture the index
so that different columns can access data with different column indexes.
Wait, so what does equality have to do with having custom types?
(eql (read-from-string (prin1-to-string '(1 2 3))) '(1 2 3)) ; => NIL
(equal (read-from-string (prin1-to-string '(1 2 3))) '(1 2 3)) ; => T
Okay, I get that, but I still don't understand how I would go about implementing this to make a new custom type.
This is a simple way you can use when you don't need eql
to guarantee object equality:
(defstruct product-info
name cost sale)
(gtk:define-application (:name closting
:id "com.closting")
(gtk:define-main-window (window (gtk:make-application-window :application gtk:*application*))
(setf (gtk:window-title window) "Closting")
(let* ((rows (loop :for (name cost sale) :in '(("HP Zbook 15" "250$" "300$")
("Infinix S90" "40$" "60$")
("Casio Calculator" "50$" "80$"))
:collect (prin1-to-string (make-product-info :name name :cost cost :sale sale))))
(main-model (gtk:make-string-list :strings rows))
(column-view (gtk:make-column-view :model (gtk:make-single-selection :model main-model)))
(col1-factory (gtk:make-signal-list-item-factory))
(col1 (gtk:make-column-view-column :title "First Col!" :factory col1-factory))
(col2-factory (gtk:make-signal-list-item-factory))
(col2 (gtk:make-column-view-column :title "Second Col!" :factory col2-factory))
(col3-factory (gtk:make-signal-list-item-factory))
(col3 (gtk:make-column-view-column :title "Third Col!" :factory col3-factory)))
(gtk:column-view-append-column column-view col1)
(gtk:column-view-append-column column-view col2)
(gtk:column-view-append-column column-view col3)
(setf (gtk:widget-vexpand-p column-view) t
(gtk:widget-hexpand-p column-view) t)
(flet ((setup (factory item)
(declare (ignore factory))
(setf (gtk:list-item-child item) (gtk:make-label :str "")))
(unbind (factory item) (declare (ignore factory item)))
(teardown (factory item) (declare (ignore factory item))))
(loop :for factory :in (list col1-factory col2-factory col3-factory)
:for accessor :in (list #'product-info-name #'product-info-cost #'product-info-sale)
:do (gtk:connect factory "setup" #'setup)
(gtk:connect factory "unbind" #'unbind)
(gtk:connect factory "teardown" #'teardown)
(gtk:connect factory "bind"
(let ((accessor accessor))
(lambda (factory item)
(declare (ignore factory))
(let ((row (read-from-string (gtk:string-object-string (gobj:coerce (gtk:list-item-item item) 'gtk:string-object)))))
(setf (gtk:label-text (gobj:coerce (gtk:list-item-child item) 'gtk:label)) (funcall accessor row))))))))
(setf (gtk:window-child window) column-view))
(unless (gtk:widget-visible-p window)
(gtk:window-present window))))
Ohhhhhhh, Thanks once again! I couldn't have thought of that.
If you want to get the same object under EQL comparison, or other unreadable objects in the bind
signal handler, I prefer using UUIDs to identify objects:
(defstruct product-info
name cost sale)
(gtk:define-application (:name closting
:id "com.closting")
(gtk:define-main-window (window (gtk:make-application-window :application gtk:*application*))
(setf (gtk:window-title window) "Closting")
(let ((product-table (make-hash-table :test #'equal)))
(let* ((rows (loop :for (name cost sale) :in '(("HP Zbook 15" "250$" "300$")
("Infinix S90" "40$" "60$")
("Casio Calculator" "50$" "80$"))
:for product-info := (make-product-info :name name :cost cost :sale sale)
:for uuid := (prin1-to-string (uuid:make-v1-uuid))
:do (setf (gethash uuid product-table) product-info)
:collect uuid))
(main-model (gtk:make-string-list :strings rows))
(column-view (gtk:make-column-view :model (gtk:make-single-selection :model main-model)))
(col1-factory (gtk:make-signal-list-item-factory))
(col1 (gtk:make-column-view-column :title "First Col!" :factory col1-factory))
(col2-factory (gtk:make-signal-list-item-factory))
(col2 (gtk:make-column-view-column :title "Second Col!" :factory col2-factory))
(col3-factory (gtk:make-signal-list-item-factory))
(col3 (gtk:make-column-view-column :title "Third Col!" :factory col3-factory)))
(gtk:column-view-append-column column-view col1)
(gtk:column-view-append-column column-view col2)
(gtk:column-view-append-column column-view col3)
(setf (gtk:widget-vexpand-p column-view) t
(gtk:widget-hexpand-p column-view) t)
(flet ((setup (factory item)
(declare (ignore factory))
(setf (gtk:list-item-child item) (gtk:make-label :str "")))
(unbind (factory item) (declare (ignore factory item)))
(teardown (factory item) (declare (ignore factory item))))
(loop :for factory :in (list col1-factory col2-factory col3-factory)
:for accessor :in (list #'product-info-name #'product-info-cost #'product-info-sale)
:do (gtk:connect factory "setup" #'setup)
(gtk:connect factory "unbind" #'unbind)
(gtk:connect factory "teardown" #'teardown)
(gtk:connect factory "bind"
(let ((accessor accessor))
(lambda (factory item)
(declare (ignore factory))
(let* ((uuid (gtk:string-object-string (gobj:coerce (gtk:list-item-item item) 'gtk:string-object)))
(row (gethash uuid product-table)))
(setf (gtk:label-text (gobj:coerce (gtk:list-item-child item) 'gtk:label)) (funcall accessor row))))))))
(setf (gtk:window-child window) column-view)))
(unless (gtk:widget-visible-p window)
(gtk:window-present window))))
Oh, that's a really cool solution. Will definitely use that!
Okay, even though you closed this, I get unhandled memory errors from this:
(defun create-financial-module-column-view (columns data)
(let* ((string-list-data (loop :for row :in data
:collect (gtk:make-string-list :strings row)))
(main-model (gio:make-list-store :item-type (gobj:type-from-name "GtkStringList")))
(column-view (gtk:make-column-view :model (gtk:make-single-selection :model main-model))))
(setf (gtk:widget-vexpand-p column-view) t
(gtk:widget-hexpand-p column-view) t)
;; TEMPORARY
(setf (gtk:column-view-reorderable-p column-view) t)
(loop :for row :in string-list-data
:do (gio:list-store-append main-model row))
(loop :for column-name :in columns
:for index :from 0
:do (let* ((col-factory (gtk:make-signal-list-item-factory))
(column (gtk:make-column-view-column :title column-name :factory col-factory)))
(setf (gtk:column-view-column-resizable-p column) t)
(flet ((setup (factory item)
(declare (ignore factory))
(setf (gtk:list-item-child item) (gtk:make-label :str "DEFAULT")))
(unbind (factory item) (declare (ignore factory item)))
(teardown (factory item) (declare (ignore factory item))))
(gtk:connect col-factory "setup" #'setup)
(gtk:connect col-factory "unbind" #'unbind)
(gtk:connect col-factory "teardown" #'teardown)
(gtk:connect col-factory "bind" ;(lambda (factory item) (declare (ignore factory item)))
(let ((i index))
(lambda (factory item)
(declare (ignore factory))
(format t "PLEAAAAAASEWORKKKK: ~a~%" (gobj:coerce (gtk:list-item-child item) 'gtk:label))
;; (setf (gtk:label-text (gobj:coerce (gtk:list-item-child item) 'gtk:label))
;; (format nil "BRUHRHRUHRUHRUHR, ~a~%" i)
;; ;; (gtk:string-list-get-string (gobj:coerce (gtk:list-item-item item)
;; ;; 'gtk:string-list)
;; ;; i)
;; )
))))
(gtk:column-view-append-column column-view column)))
column-view))
I narrowed the error down to using gobj:coerce to convert from (gtk:list-item-item item) to 'gtk:label. Why does this happen? As a hint, when run another time, it runs normally. Weirdly this causes annoying sbcl errors and I have to restart the REPL.
This may be a memory issue caused by the garbage collector. Please refer to https://github.com/bohonghuang/cl-gtk4/issues/20#issuecomment-1586115138. Could you update cl-gobject-introspection and try again?
Okay.
I wasn't even using that version of cl-gobject-introspection. I spend hours debuggin that, It works now, thanks a bunch!
Hello, love the work you have done! I wanted to create a GtkListView, but didn't know how to. After hours of looking through the GTK docs I came up with this code:
Turns out for a reason I cannot understand, list-box-append gives out an error saying it takes only 1 argument... Please give an example for ListViews or explain this problem to me! I really don't want to drop this application and write it in another language. Thanks a bunch.
Regards, Hamza