Lisp HUG Maillist Archive

newbie CAPI question

Hi all,

I'm new to both Lisp and CAPI, and I'm working my way up the Lispworks
learning curve, but I've got a feeling that I'm "doing it wrong".  I'm
cobbling together a little database application, so I started with a
login window as shown below.

What seems un-Lispy to me is the way I'm extracting values from the
window in do-login: pulling values by accessors works for a simple
window, but as I scale up to add grids of database records, this seems
really inelegant.  I'm thinking that there must be some generally
accepted way of iterating across a pane, finding the values entered by
the user, and mapping them into CLOS slots (where the CLOS objects are
being persisted via CLSQL or something similar) without having to
specify each accessor directly.  Or perhaps this isn't even the best
way to go about it?

I'd appreciate any advice (or pointers to other material that I should
be reading) on 1) better approaches for the code below, and 2)
building CAPI applications with GUI/CLOS mappings.

Thanks much,
Kev


(capi:define-interface login-window ()
  ()
  (:panes
   (title-pane-1
    capi:title-pane
    :text "Username:")
   (text-input-pane-1
    capi:text-input-pane
    :accessor username)
   (title-pane-2
    capi:title-pane
    :text "Password:")
   (password-pane-1
    capi:password-pane
    :accessor password)
   (title-pane-3
    capi:title-pane
    :text "Database:")
   (text-input-pane-2
    capi:text-input-pane
    :text "default"
    :accessor database)
   (push-button-1
    capi:push-button
    :text "Login"
    :default-p t
    :callback 'do-login))
  (:layouts
   (login-layout
    capi:grid-layout
    '(title-pane-1 text-input-pane-1
                   title-pane-2 password-pane-1
                   title-pane-3 text-input-pane-2
                   "" push-button-1)
    :x-adjust '(:right )
    :y-adjust '(:center)
    :x-gap 6
    :y-gap 6
    :internal-border 6))
  (:default-initargs
   :best-height 55
   :best-width 250
   :layout 'login-layout
   :title "Login"))


(defun do-login (data interface)
(declare (ignore data))
(capi:display-message (concatenate 'string
                                   "Login: "
                                   (capi:text-input-pane-text
(username interface))
                                   "/"
                                   (capi:text-input-pane-text
(password interface))
                                   "@"
                                   (capi:text-input-pane-text
(database interface)))))

(defun test-login ()
  (capi:contain (make-instance 'login-window)))


Re: newbie CAPI question

Unable to parse email body. Email id is 9908

Re: newbie CAPI question

Perhaps I'm misunderstanding, but I thought I was safe there, since
do-login is the callback from the button.  Or is it only the
push-button itself that's safe inside the callback?  Should the
callback use capi:in-pane-process to access data outside the scope of
the push-button?

Kev

On Sat, Feb 6, 2010 at 1:55 PM, Paul Tarvydas
<tarvydas@visualframeworksinc.com> wrote:
> ...
>> Code running a callback does NOT need this complication,
>
> I meant "code running IN a callback ..."
>


Re: newbie CAPI question

Unable to parse email body. Email id is 9912

Re: newbie CAPI question


Paul Tarvydas writes:
 > 
 > ...
 > > What seems un-Lispy to me is the way I'm extracting values from the
 > > window in do-login: pulling values by accessors works for a simple
 > > window, but as I scale up to add grids of database records, this seems
 > > really inelegant.  I'm thinking that there must be some generally
 > > accepted way of iterating across a pane, finding the values entered by
 > > the user, and mapping them into CLOS slots (where the CLOS objects are
 > > being persisted via CLSQL or something similar) without having to
 > > specify each accessor directly.  Or perhaps this isn't even the best
 > > way to go about it?
 > > 
 > > I'd appreciate any advice (or pointers to other material that I should
 > > be reading) on 1) better approaches for the code below, and 2)
 > > building CAPI applications with GUI/CLOS mappings.
 > ...
 > 
 > 
 > d) Remember that you can sub-class capi doo-dads.  You can add your
 > own getter/setter/update-view methods if this makes sense in your
 > design.  Remember that CLOS oo works differently from most other
 > kinds of oo - you can add methods specialized on combinations of
 > method parameter types, so sub-classing isn't always necessary, or,
 > sub-classing can help tune the control-flow through a maze of
 > widgets.  Capi callbacks need not be functions, they can be methods,
 > too.
 > 

I think this is an important point (amongst the others).  I came from a
Visual Basic/MSVC perspective and it took me a while to "get" the
subclassing aspects of CAPI (the more practice you get at Common Lisp
the more it becomes clear).  If you view the interface as well as the
controls within it as candidates for subclassing, you can add a lot of
"local" semantics- data slots, generic functions, etc to tailor behavior
to your requirements.

That means you need not be bound to specifying only capi controls in the
interface definition- if you create a class that inherits from a CAPI
object, then you can use it in the interface just as a CAPI object of
the type you inherited from.  You can take that too far by piling all
kinds of extra junk in the class which tends to bind the interface to
your app data, which in turn causes accumulation of complexity in the
interface control code- which turns into a real drag.

In practical terms, it means you can incorporate arbitrary amounts of
metadata in the controls rather than in parallel data-structures, and
use the controls themselves as parameters in generic functions.  Though
the tradeoff is binding app data to the interface structure, it reduces
the variable space you have to maintain.  Which is to say the ability to
do so is a tool which needs to be used appropriately.

To strengthen the division between data and interface, it might be
possible to consider the inherited capi class as the "interface form" of
a data class, then construct the interface by obtaining the right
objects from the data portion of the app rather than contructing things
at display time.  I've not tried a capi app that way, I don't know
relevant, important, or correct the concept is.

Greg


Re: newbie CAPI question

A solution is to make an abstract class to store your data and mix it with a
CAPI class to create your interface. For instance :

(defclass my-data ()  (data-1 data-2 .... etc.))

(defclass my-interface (interface my-data) ())

Now your slots are added to the interface class and you can simply iterate
thru the slots of your abstract class to get the names of the slots you want
to save on disk. To do this you get the slot list of your abstract class :

(clos:class-slots (find-class my-data))

You iterate thru this list and

to get the names of the slot :

(slot-value the-slot 'clos::name) ;I think there is a method to do the same

To get the value associated with this slot-name in your instance :

(slot-value my-instance the-name)

Another way to do the same is to subclass the CAPI class and use
clos:class-direct-slots to map only the slots of your subclass.

Best

Denis


Le 6/02/10 20:37, « [NOM] » <[ADRESSE]> a écrit :

> 
> Hi all,
> 
> I'm new to both Lisp and CAPI, and I'm working my way up the Lispworks
> learning curve, but I've got a feeling that I'm "doing it wrong".  I'm
> cobbling together a little database application, so I started with a
> login window as shown below.
> 
> What seems un-Lispy to me is the way I'm extracting values from the
> window in do-login: pulling values by accessors works for a simple
> window, but as I scale up to add grids of database records, this seems
> really inelegant.  I'm thinking that there must be some generally
> accepted way of iterating across a pane, finding the values entered by
> the user, and mapping them into CLOS slots (where the CLOS objects are
> being persisted via CLSQL or something similar) without having to
> specify each accessor directly.  Or perhaps this isn't even the best
> way to go about it?
> 
> I'd appreciate any advice (or pointers to other material that I should
> be reading) on 1) better approaches for the code below, and 2)
> building CAPI applications with GUI/CLOS mappings.
> 
> Thanks much,
> Kev
> 
> 
> (capi:define-interface login-window ()
>   ()
>   (:panes
>    (title-pane-1
>     capi:title-pane
>     :text "Username:")
>    (text-input-pane-1
>     capi:text-input-pane
>     :accessor username)
>    (title-pane-2
>     capi:title-pane
>     :text "Password:")
>    (password-pane-1
>     capi:password-pane
>     :accessor password)
>    (title-pane-3
>     capi:title-pane
>     :text "Database:")
>    (text-input-pane-2
>     capi:text-input-pane
>     :text "default"
>     :accessor database)
>    (push-button-1
>     capi:push-button
>     :text "Login"
>     :default-p t
>     :callback 'do-login))
>   (:layouts
>    (login-layout
>     capi:grid-layout
>     '(title-pane-1 text-input-pane-1
>                    title-pane-2 password-pane-1
>                    title-pane-3 text-input-pane-2
>                    "" push-button-1)
>     :x-adjust '(:right )
>     :y-adjust '(:center)
>     :x-gap 6
>     :y-gap 6
>     :internal-border 6))
>   (:default-initargs
>    :best-height 55
>    :best-width 250
>    :layout 'login-layout
>    :title "Login"))
> 
> 
> (defun do-login (data interface)
> (declare (ignore data))
> (capi:display-message (concatenate 'string
>                                    "Login: "
>                                    (capi:text-input-pane-text
> (username interface))
>                                    "/"
>                                    (capi:text-input-pane-text
> (password interface))
>                                    "@"
>                                    (capi:text-input-pane-text
> (database interface)))))
> 
> (defun test-login ()
>   (capi:contain (make-instance 'login-window)))
> 

-------------------------------------------------------
Denis Pousseur
70 rue de Wansijn
1180 Bruxelles, Belgique

Tel : 32 (0)2 219 31 09
Mail :  denis.pousseur@gmail.com
-------------------------------------------------------



Re: newbie CAPI question

Kevin Clifton wrote on Sat, 6 Feb 2010 12:37:54 -0700 22:37:

| What seems un-Lispy to me is the way I'm extracting values from the
| window in do-login: pulling values by accessors works for a simple
| window, but as I scale up to add grids of database records, this seems
| really inelegant.  I'm thinking that there must be some generally
| accepted way of iterating across a pane, finding the values entered by
| the user, and mapping them into CLOS slots (where the CLOS objects are
| being persisted via CLSQL or something similar) without having to
| specify each accessor directly.  Or perhaps this isn't even the best
| way to go about it?

YstokGrid was conceived for dealing with database records, see
http://lisp.ystok.ru/ygrid/
--
Sincerely,
Dmitriy Ivanov
lisp.ystok.ru


Updated at: 2020-12-10 08:39 UTC