Yes, there are some conveniences using qf to build the form as well as the convenience of having form input contained in an array.
Attributes can be repeated automatically (except id, value, and name) invoking the proc qf_remember_attributes.
The procs qf_choice and qf_choices can be used to switch between input modes. qf_choices can switch between checkboxes and multiple select options by just changing the type. qf_choice can switch between radio buttons and select option by changing the type.
The label tag is automatically wrapped around an input tag that is supplied an id and label.
Multiple forms can be built at the same time. Each form is given a form_id when qf_form is invoked. The most recent form_id is used by default, so no need to use this level of complexity with one form.
values are automatically quoted to prevent breaking html interpretation. Unquote with ad_unquote
qf errors silently on the page (reporting error to log) for example if qf_form is not invoked first.
qf_close automatically adds form and fieldset close tags to all open forms.
These procs can help to generate clean html in other procs by separating the collection of data from the tag build. For example, when using qf_choices to help build an html widget, basically one sets up a query to build a list of lists, and then passes it to qf_choices. This has been an issue for me with ecommerce, where manually building the html in the middle of qf_form has resulted in some unexpected tag rendering due to code logic errors. Complete form widgets can be built as well. This may be a handy tool in dotLRN for helping build dynamic testing/survey apps without having to depend on someone with ad_form proficiency.
Regarding customizable templates