Shinmera / qtools

Qtools is a collection of utilities to aid development with CommonQt
https://shinmera.github.io/qtools
zlib License
210 stars 17 forks source link

DEFINE-SLOT for more complex types #12

Closed resttime closed 8 years ago

resttime commented 8 years ago

Looked through code/documentation and it doesn't seem like DEFINE-SLOT and DEFINE-SIGNAL can support more complex types. An example would be QListWidget that signals itemDoubleClicked(QListWidgetItem* item) but QListWidgetItem* can't be used with DEFINE-SLOT in any way with what Qtools allows (I think). Unless I'm massively overlooking something is it just not supported? Not really a problem since I just fell back on using some CommonQt to connect slots/signals. If it's a bug I haven't looked too much into it to figure out a solution but I think it fails is because the type gets downcased somewhere along the line before being passed to the function QT::FIND-SIGNAL-QTYPE which signals an error that it can't find the type. It would work with the original case otherwise.

;;; Various other naming conventions tried as well
(define-slot (main-window slotty) ((music-item QListWidgetItem*))
  (declare (connected main-window (item-double-clicked QListWidgetItem*)))
  (print (q+:text music-item)))
Shinmera commented 8 years ago

For define-slot you can pass a third piece to each argument, which can be a string of the Qt type to use. See widget-convenience.lisp line 38. In the case of define-signal you can just use a string rather than a symbol. However, in this case, I do not think CommonQt can support it at all due to the fact that a QListWidgetItem is neither a QObject, nor a primitive type. It might support passing it around (I forget), but you definitely can't extend it or do much of anything useful with it.

This is part of the reason why I wrote Qtools-UI, which has its own implementation of a list widget. You might want to have a look at that.

resttime commented 8 years ago

I tried passing the string of the Qt type as a third argument but then it breaks on macro expansion because a string isn't a valid type for specializing arguments of DEFMETHOD. Though with your suggestions on strings, I tried more combinations of the type as a string and surprised myself by getting it working. I believe it's one I tried before but I guess I forgot to restart my lisp that one time or something. The code up above is one of those failed combinations and very wrong since it doesn't even connect to the right widget hahaha.

Also signals/slots for complex types are supported by CommonQt though I guess technically only the "pointers" of them. When I couldn't get it working in Qtools, I figured out it could work in CommonQt and made a work around.

;;; My workaround with CommonQt's CONNECT and SIGNAL! from Qtools
(connect music-selection "itemDoubleClicked(QListWidgetItem*)" main-window
           (lambda (receiver item) 
             (signal! receiver (music-selected string) (q+:text item))))

Shouldn't be needed, hurray! Quick example that works on my system for signals and slots:

(ql:quickload '("qtcore" "qtgui" "qtools"))

(in-package #:cl-user)
(defpackage test
  (:use #:cl+qt)
  (:export #:main))
(in-package #:test)

(in-readtable :qtools)

;; Widgets
(define-widget thing (QWidget) ())
(define-subwidget (thing text-label) (q+:make-qlabel))
(define-subwidget (thing pass-label-button) (q+:make-qpushbutton)
  (q+:set-text pass-label-button "Click me to pass TEXT-LABEL through a signal"))
(define-subwidget (thing list-of-stuff) (q+:make-qlistwidget)
  (loop for a upto 10 do (q+:add-item list-of-stuff (write-to-string a))))

;; Test custom signal passing Qt objects
(define-signal (thing pass-that-text-label) ("QLabel*"))

(define-slot (thing pass-signaler) ()
  (declare (connected pass-label-button (pressed)))
  (signal! thing (pass-that-text-label "QLabel*") text-label))

;; Test slots receiving Qt objects
(define-slot (thing what-did-we-double-click?) ((item "QListWidgetItem*"))
  (declare (connected list-of-stuff (item-double-clicked "QListWidgetItem*")))
  (q+:set-text text-label (q+:text item)))

(define-slot (thing we-got-text-label) ((label "QLabel*"))
  (declare (connected thing (pass-that-text-label "QLabel*")))
  (q+:set-text label "Text label was got!"))

;; Make layout!
(define-subwidget (thing layout) (q+:make-qvboxlayout thing)
  (q+:add-widget layout pass-label-button)
  (q+:add-widget layout text-label)
  (q+:set-text text-label "Double Click List Item to activate slot!")
  (q+:add-widget layout list-of-stuff))

(defun main ()
  (with-main-window (window (make-instance 'thing)
                     #+windows :main-thread #+windows nil)))

I guess this technically wasn't really an issue so it can be closed. Thanks though, since this basically helped me figure everything out. Mentioning in the docs about this might be good. Anyways, now that I know I can pass Qt objects around like this I'm going to rightfully abuse this concept. Hehehe.

Also, I was going to mention that :MAIN-THREAD kinda breaks things on windows but that seems handled in a recent commit. I don't know about OSX but it would make stuff related to DEFVAR variables not work. I think it was due to how lexical behavior is treated with respect to threads, so a heads up that it might be an issue in OSX too.

Shinmera commented 8 years ago

Oh, whoops, yeah, I switched the argument order around in my head. Second is the Qt type and third is the lisp class for the specializer of the method it creates underneath. Things are a bit confusing there because of the way translation works. Generally though anywhere where a C++ type is expected underneath you can pass a string to specify it explicitly.

Shinmera commented 8 years ago

As an addendum, in the future if you have immediate questions like this, you can hit me up on Freenode/#shirakumo. I'm in the channel 24/7; though I might not always be around immediately, I'll definitely respond as soon as I can.

Shinmera commented 8 years ago

Also, another quick note: as of 20af5bb1d34050218a3f8940b872712b9ef66592 there is no need to load all of the different smoke modules in a row. In your example, you'd only have to load qtgui; since it depends on qtcore and qtools those get automatically pulled in.