1.. currentmodule:: dialog
2
3:class:`Dialog` class overview
4==============================
5
6Initializing a :class:`Dialog` instance
7---------------------------------------
8
9Since all widgets in pythondialog are implemented as methods of the
10:class:`Dialog` class, a pythondialog-based application usually starts by
11creating a :class:`!Dialog` instance.
12
13.. autoclass:: Dialog
14   :members: __init__
15
16.. _autowidgetsize:
17
18.. rubric:: About the *autowidgetsize* option
19
20The *autowidgetsize* option should be convenient in situations where figuring
21out suitable widget size parameters is a burden, for instance when developing
22little scripts that don't need too much visual polishing, when a widget is
23used to display data, the size of which is not easily predictable, or simply
24when one doesn't want to hardcode the widget size.
25
26This option is implemented in the following way: for a given size parameter
27(for instance, *width*) of a given widget, the default value in the
28widget-producing method is now ``None`` if it previously had a non-zero
29default. At runtime, if the value seen by the widget-producing method is not
30``None``, it is used as is; on the contrary, if that value is ``None``, it is
31automatically replaced with:
32
33  - ``0`` if the :class:`Dialog` instance has been initialized with
34    *autowidgetsize* set to ``True``;
35  - the old default otherwise, in order to preserve backward-comptability.
36
37.. note::
38
39  - the *autowidgetsize* option is currently marked as experimental, please
40    give some feedback;
41  - you may encounter questionable results if you only set one of the *width*
42    and *height* parameters to ``0`` for a given widget (seen in
43    :program:`dialog` 1.2-20140219).
44
45.. warning::
46
47  You should not explicitly pass ``None`` for a size parameter such as *width*
48  or *height*. If you want a fixed size, specify it directly (as an int);
49  otherwise, either use the *autowidgetsize* option or set the parameter to
50  ``0`` (e.g., ``width=0``).
51
52
53.. _passing-dialog-common-options:
54
55Passing :program:`dialog` "common options"
56------------------------------------------
57
58Every widget method has a \*\*kwargs argument allowing you to pass
59:term:`common options <dialog common options>` (see the :manpage:`dialog(1)`
60manual page) to :program:`dialog` for this widget call. For instance, if *d*
61is a :class:`Dialog` instance, you can write::
62
63  d.checklist(args, ..., title="A Great Title", no_shadow=True)
64
65The *no_shadow* option is worth looking at:
66
67  #. It is an option that takes no argument as far as :program:`dialog` is
68     concerned (unlike the :option:`--title` option, for instance). When you
69     list it as a keyword argument, the option is really passed to
70     :program:`dialog` only if the value you gave it evaluates to ``True`` in
71     a boolean context. For instance, ``no_shadow=True`` will cause
72     :option:`--no-shadow` to be passed to :program:`dialog` whereas
73     ``no_shadow=False`` will cause this option not to be passed to
74     :program:`dialog` at all.
75
76  #. It is an option that has a hyphen (``-``) in its name, which you must
77     change into an underscore (``_``) to pass it as a Python keyword
78     argument. Therefore, :option:`--no-shadow` is passed by giving a
79     ``no_shadow=True`` keyword argument to :class:`Dialog` methods (the
80     leading two dashes are also consistently removed).
81
82.. note::
83
84   When :meth:`Dialog.__init__` is called with
85   :samp:`{pass_args_via_file}=True` (or without any explicit setting for this
86   option, and the pythondialog as well as :program:`dialog` versions are
87   recent enough so that the option is enabled by default), then the options
88   are not directly passed to :program:`dialog`. Instead, all options are
89   written to a temporary file which :program:`dialog` is pointed to via
90   :option:`--file`. This ensures better confidentiality with respect to other
91   users of the same computer.
92
93
94.. versionadded:: 2.14
95   Support for the *default_button* and *no_tags* common options.
96
97.. versionadded:: 3.0
98   Proper support for the *extra_button*, *item_help* and *help_status* common
99   options.
100
101
102Return value of widget-producing methods
103----------------------------------------
104
105Most :class:`Dialog` methods that create a widget (actually: all methods that
106supervise the exit of a widget) return a value which fits into one of these
107categories:
108
109  #. The return value is a :term:`Dialog exit code` (see below).
110
111  #. The return value is a sequence whose first element is a Dialog exit code
112     (the rest of the sequence being related to what the user entered in the
113     widget).
114
115For instance, :meth:`Dialog.yesno` returns a single Dialog exit code that will
116typically be :attr:`Dialog.OK` or :attr:`Dialog.CANCEL`, depending on the
117button chosen by the user. However, :meth:`Dialog.checklist` returns a tuple
118of the form :samp:`({code}, [{tag}, ...])` whose first element is a Dialog
119exit code and second element lists all tags for the entries selected by the
120user.
121
122.. _Dialog-exit-code:
123
124"Dialog exit code" (high-level)
125^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
126
127A :dfn:`Dialog exit code`, or :dfn:`high-level exit code`, is a string
128indicating how/why a widget-producing method ended. Most widgets return one of
129the :term:`standard Dialog exit codes <standard Dialog exit code>`: ``"ok"``,
130``"cancel"``, ``"esc"``, ``"help"``, ``"extra"`` and ``"timeout"``,
131respectively available as :attr:`Dialog.OK`, :attr:`Dialog.CANCEL`,
132:attr:`Dialog.ESC`, :attr:`Dialog.HELP`, :attr:`Dialog.EXTRA` and
133:attr:`Dialog.TIMEOUT`, *i.e.,* attributes of the :class:`Dialog` class.
134However, some widgets may return additional, non-standard exit codes; for
135instance, the :meth:`~Dialog.inputmenu` widget may return ``"accepted"`` or
136``"renamed"`` in addition to the standard Dialog exit codes.
137
138When getting a Dialog exit code from a widget-producing method, user code
139should compare it with :attr:`Dialog.OK` and friends (or equivalently, with
140``"ok"`` and friends) using the ``==`` operator. This allows to easily replace
141:attr:`Dialog.OK` and friends with objects that compare the same with ``"ok"``
142and ``u"ok"`` in Python 2, for instance.
143
144The following attributes of the :class:`Dialog` class hold the :term:`standard
145Dialog exit codes <standard Dialog exit code>`:
146
147.. autoattribute:: Dialog.OK
148
149.. autoattribute:: Dialog.CANCEL
150
151.. autoattribute:: Dialog.ESC
152
153.. autoattribute:: Dialog.EXTRA
154
155.. autoattribute:: Dialog.HELP
156
157.. autoattribute:: Dialog.TIMEOUT
158
159The following attributes are obsolete and should not be used in pythondialog
1603.0 and later:
161
162.. autoattribute:: Dialog.DIALOG_OK
163
164.. autoattribute:: Dialog.DIALOG_CANCEL
165
166.. autoattribute:: Dialog.DIALOG_ESC
167
168.. autoattribute:: Dialog.DIALOG_EXTRA
169
170.. autoattribute:: Dialog.DIALOG_HELP
171
172.. autoattribute:: Dialog.DIALOG_ITEM_HELP
173
174.. _dialog-exit-status:
175
176"dialog exit status" (low-level)
177^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
178
179When returning from a widget call, the :term:`Dialog exit code` is normally
180derived by pythondialog from an integer called :dfn:`dialog exit status`, or
181:dfn:`low-level exit code`. This integer is returned by the :program:`dialog`
182backend upon exit. The different possible values for the dialog exit status
183are referred to as ``DIALOG_OK``, ``DIALOG_CANCEL``, ``DIALOG_ESC``,
184``DIALOG_ERROR``, ``DIALOG_EXTRA``, ``DIALOG_HELP``, ``DIALOG_ITEM_HELP``
185and  ``DIALOG_TIMEOUT`` in the :manpage:`dialog(1)` manual page.
186
187.. note::
188
189  - ``DIALOG_HELP`` and ``DIALOG_ITEM_HELP`` both map to :attr:`Dialog.HELP`
190    in pythondialog, because they both correspond to the same user action and
191    the difference brings no information that the caller does not already
192    have;
193
194  - ``DIALOG_ERROR`` has no counterpart as a :class:`Dialog` attribute,
195    because it is automatically translated into a :exc:`DialogError` exception
196    when received.
197
198In pythondialog 2.x, the low-level exit codes were available as the
199``DIALOG_OK``, ``DIALOG_CANCEL``, etc. attributes of :class:`Dialog`
200instances. For compatibility, the :class:`Dialog` class has attributes of the
201same names that are mapped to :attr:`Dialog.OK`, :attr:`Dialog.CANCEL`, etc.,
202but their use is deprecated as of pythondialog 3.0.
203
204
205Adding an Extra button
206----------------------
207
208With most widgets, it is possible to add a supplementary button called
209:dfn:`Extra button`. To do that, you simply have to use ``extra_button=True``
210(keyword argument) in the widget call. By default, the button text is "Extra",
211but you can specify another string with the *extra_label* keyword argument.
212
213When the widget exits, you know if the :guilabel:`Extra` button was pressed if
214the :term:`Dialog exit code` is :attr:`Dialog.EXTRA` (``"extra"``). Normally,
215the rest of the return value is the same as if the widget had been closed with
216:guilabel:`OK`. Therefore, if the widget normally returns a list of three
217integers, for instance, you can expect to get the same information if
218:guilabel:`Extra` is pressed instead of :guilabel:`OK`.
219
220.. note::
221
222  This feature can be particularly useful in combination with the *yes_label*,
223  *no_label*, *help_button* and *help_label* :term:`common options <dialog
224  common options>` to provide a completely different set of buttons than the
225  default for a given widget.
226
227
228Providing on-line help facilities
229---------------------------------
230
231With most :program:`dialog` widgets, it is possible to provide online help to
232the final user. At the time of this writing (October 2014), there are three
233main options governing these help facilities in the :program:`dialog` backend:
234:option:`--help-button`, :option:`--item-help` and :option:`--help-status`.
235Since :program:`dialog` 1.2-20130902, there is also :option:`--help-tags` that
236modifies the way :option:`--item-help` works. As explained previously
237(:ref:`passing-dialog-common-options`), in order to use these options in
238pythondialog, you can pass the *help_button*, *item_help*, *help_status* and
239*help_tags* keyword arguments to :class:`Dialog` widget-producing methods.
240
241Adding a :guilabel:`Help` button
242^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
243In order to provide a :guilabel:`Help` button in addition to the normal
244buttons of a widget, you can pass ``help_button=True`` (keyword argument) to
245the corresponding :class:`Dialog` method. For instance, if *d* is a
246:class:`Dialog` instance, you can write::
247
248  code = d.yesno("<text>", height=10, width=40, help_button=True)
249
250or::
251
252  code, answer = d.inputbox("<text>", init="<init>",
253                            help_button=True)
254
255When the method returns, the :term:`Dialog exit code` is :attr:`Dialog.HELP`
256(i.e., the string ``"help"``) if the user pressed the :guilabel:`Help` button.
257Apart from that, it works exactly as if ``help_button=True`` had not been
258used. In the last example, if the user presses the :guilabel:`Help` button,
259*answer* will contain the user input, just as if :guilabel:`OK` had been
260pressed. Similarly, if you write::
261
262  code, t = d.checklist(
263                "<text>", height=0, width=0, list_height=0,
264                choices=[ ("Tag 1", "Item 1", False),
265                          ("Tag 2", "Item 2", True),
266                          ("Tag 3", "Item 3", True) ],
267                help_button=True)
268
269and find that ``code == Dialog.HELP``, then *t* contains the tag string for
270the highlighted item when the :guilabel:`Help` button was pressed.
271
272Finally, note that it is possible to choose the text written on the
273:guilabel:`Help` button by supplying a string as the *help_label* keyword
274argument.
275
276.. _providing-inline-per-item-help:
277
278Providing inline per-item help
279^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
280In addition to, or instead of the :guilabel:`Help` button, you can provide
281:dfn:`item-specific help` that is normally displayed at the bottom of the
282widget. This can be done by passing the ``item_help=True`` keyword argument to
283the widget-producing method and by including the item-specific help strings in
284the appropriate argument.
285
286For widgets where item-specific help makes sense (i.e., there are several
287elements that can be highlighted), there is usually a parameter, often called
288*elements*, *choices*, *nodes*..., that must be provided as an iterable
289describing the various lines/items/nodes/... that can be highlighted in the
290widget. When ``item_help=True`` is passed, every element of this iterable must
291be completed with a string which is the :dfn:`item-help string` of the element
292(using :manpage:`dialog(1)` terminology). For instance, the following call
293with no inline per-item help support::
294
295  code, t = d.checklist(
296                "<text>", height=0, width=0, list_height=0,
297                choices=[ ("Tag 1", "Item 1", False),
298                          ("Tag 2", "Item 2", True),
299                          ("Tag 3", "Item 3", True) ],
300                help_button=True)
301
302can be altered this way to provide inline item-specific help::
303
304  code, t = d.checklist(
305                "<text>", height=0, width=0, list_height=0,
306                choices=[ ("Tag 1", "Item 1", False, "Help 1"),
307                          ("Tag 2", "Item 2", True,  "Help 2"),
308                          ("Tag 3", "Item 3", True,  "Help 3") ],
309                help_button=True, item_help=True, help_tags=True)
310
311With this modification, the item-help string for the highlighted item is
312displayed in the bottom line of the screen and updated as the user highlights
313other items.
314
315If you don't want a :guilabel:`Help` button, just use ``item_help=True``
316without ``help_button=True`` (*help_tags* doesn't matter in this case). Then,
317you have the inline help at the bottom of the screen, and the following
318discussion about the return value can be ignored.
319
320If the user chooses the :guilabel:`Help` button, *code* will be equal to
321:attr:`Dialog.HELP` (``"help"``) and *t* will contain the tag string
322corresponding to the highlighted item when the :guilabel:`Help` button was
323pressed (``"Tag 1/2/3"`` in the example). This is because of the *help_tags*
324option; without it (or with ``help_tags=False``), *t* would have contained the
325:term:`item-help string` of the highlighted choice (``"Help 1/2/3"`` in the
326example).
327
328If you remember what was said earlier, if ``item_help=True`` had not been used
329in the previous example, *t* would still contain the tag of the highlighted
330choice if the user closed the widget with the :guilabel:`Help` button. This is
331the same as when using ``item_help=True`` in combination with
332``help_tags=True``; however, you would get the :term:`item-help string`
333instead if *help_tags* were ``False`` (which is the default, as in the
334:program:`dialog` backend, and in order to preserve compatibility with the
335:meth:`Dialog.menu` implementation that is several years old).
336
337Therefore, I recommend for consistency to use ``help_tags=True`` whenever
338possible when specifying ``item_help=True``. This makes ``"--help-tags"`` a
339good candidate for use with :meth:`Dialog.add_persistent_args` to avoid
340repeating it over and over. However, there are two cases where
341``help_tags=True`` cannot be used:
342
343  - when the version of the :program:`dialog` backend is lower than
344    1.2-20130902 (the :option:`--help-tags` option was added in this version);
345  - when using empty or otherwise identical tags for presentation purposes
346    (unless you don't need to tell which element was highlighted when the
347    :guilabel:`Help` button was pressed, in which case it doesn't matter to be
348    unable to discriminate between the tags).
349
350Getting the widget status before the :guilabel:`Help` button was pressed
351^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
352Typically, when the user chooses :guilabel:`Help` in a widget, the application
353will display a dialog box such as :meth:`~Dialog.textbox`,
354:meth:`~Dialog.msgbox` or :meth:`~Dialog.scrollbox` and redisplay the original
355widget afterwards. For simple widgets such as :meth:`~Dialog.inputbox`, when
356the :term:`Dialog exit code` is equal to :attr:`Dialog.HELP`, the return value
357contains enough information to redisplay the widget in the same state it had
358when :guilabel:`Help` was chosen. However, for more complex widgets such as
359:meth:`~Dialog.radiolist` (resp. :meth:`~Dialog.checklist`, or
360:meth:`~Dialog.form` and its derivatives), knowing the highlighted item is not
361enough to restore the widget state after processing the help request: one
362needs to know the checked item (resp. list of checked items, or form contents).
363
364This is where the *help_status* keyword argument becomes useful. Example::
365
366  code, t = d.checklist(
367                "<text>", height=0, width=0, list_height=0,
368                choices=[ ("Tag 1", "Item 1", False),
369                          ("Tag 2", "Item 2", True),
370                          ("Tag 3", "Item 3", True) ],
371                help_button=True, help_status=True)
372
373When :guilabel:`Help` is chosen, ``code == Dialog.HELP`` and *t* is a tuple of
374the form :samp:`({tag}, {selected_tags}, {choices})` where:
375
376  - *tag* gives the tag string of the highlighted item (which would be the
377    value of *t* if *help_status* were set to ``False``);
378  - *selected_tags* is the... list of selected tags (note that highlighting
379    and selecting an item are different things!);
380  - *choices* is a list built from the original *choices* argument of the
381    :meth:`~Dialog.checklist` call and from the list of selected tags, that
382    can be used as is to create a widget with the same items and selection
383    state as the original widget had when :guilabel:`Help` was chosen.
384
385Normally, pythondialog should always provide something similar to the last
386item in the previous example in order to make it as easy as possible to
387redisplay the widget in the appropriate state. To know precisely what is
388returned with ``help_status=True``, the best way is probably to experiment
389and/or read the code (by the way, there are many examples of widgets with
390various combinations of the *help_button*, *item_help* and *help_status*
391keyword arguments in the demo).
392
393.. note::
394
395  The various options related to help support are not mutually exclusive; they
396  may be used together to provide good help support.
397
398It is also worth noting that the documentation of the various widget-producing
399methods is written, in most cases, under the assumption that the widget was
400closed "normally" (typically, with the :guilabel:`OK` or :guilabel:`Extra`
401button). For instance, a widget documentation may state that the method
402returns a tuple of the form :samp:`({code}, {tag})` where *tag* is ..., but
403actually, if using ``item_help=True`` with ``help_tags=False``, the *tag* may
404very well be an :term:`item-help string`, and if using ``help_status=True``,
405it is likely to be a structured object such as a tuple or list. Of course,
406handling all these possible variations for all widgets would be a tedious task
407and would probably significantly degrade the readability of said
408documentation.
409
410.. versionadded:: 3.0
411   Proper support for the *item_help* and *help_status* common options.
412
413
414Screen-related methods
415----------------------
416
417Getting the terminal size:
418
419.. automethod:: Dialog.maxsize
420
421.. automethod:: Dialog.set_background_title
422
423Obsolete methods
424^^^^^^^^^^^^^^^^
425
426.. automethod:: Dialog.setBackgroundTitle
427
428.. automethod:: Dialog.clear
429
430
431Checking the versions of pythondialog and its backend
432-----------------------------------------------------
433
434Version of pythondialog
435^^^^^^^^^^^^^^^^^^^^^^^
436
437.. autoclass:: VersionInfo
438   :members:
439   :special-members:
440
441.. autodata:: version_info
442   :annotation:
443
444.. autodata:: __version__
445   :annotation:
446
447
448Version of the backend
449^^^^^^^^^^^^^^^^^^^^^^
450
451The :class:`Dialog` constructor retrieves the version string of the
452:program:`dialog` backend and stores it as an instance of a
453:class:`BackendVersion` subclass into the
454:attr:`Dialog.cached_backend_version` attribute. This allows doing things such
455as (*d* being a :class:`Dialog` instance)::
456
457  if d.compat == "dialog" and \
458    d.cached_backend_version >= DialogBackendVersion("1.2-20130902"):
459      ...
460
461in a reliable way, allowing to fix the parsing and comparison algorithms right
462in the appropriate :class:`BackendVersion` subclass, should the
463:program:`dialog`-like backend versioning scheme change in unforeseen ways.
464
465As :program:`Xdialog` seems to be dead and not to support
466:option:`--print-version`, the :attr:`Dialog.cached_backend_version` attribute
467is set to ``None`` in :program:`Xdialog`-compatibility mode (2013-09-12).
468Should this ever change, one should define an :class:`XDialogBackendVersion`
469class to handle the particularities of the :program:`Xdialog` versioning
470scheme.
471
472.. attribute:: Dialog.cached_backend_version
473
474  Instance of a :class:`BackendVersion` subclass; it is initialized by the
475  :class:`Dialog` constructor and used to store the backend version, avoiding
476  the need to repeatedly call ``dialog --print-version`` or a similar
477  command, depending on the backend.
478
479  When using the :program:`dialog` backend,
480  :attr:`Dialog.cached_backend_version` is a :class:`DialogBackendVersion`
481  instance.
482
483.. automethod:: Dialog.backend_version
484
485
486Enabling debug facilities
487-------------------------
488
489.. automethod:: Dialog.setup_debug
490
491
492Miscellaneous methods
493---------------------
494
495.. automethod:: Dialog.add_persistent_args
496
497.. note::
498
499   When :meth:`Dialog.__init__` is called with
500   :samp:`{pass_args_via_file}=True` (or without any explicit setting for this
501   option, and the pythondialog as well as :program:`dialog` versions are
502   recent enough so that the option is enabled by default), then the arguments
503   are not directly passed to :program:`dialog`. Instead, all arguments are
504   written to a temporary file which :program:`dialog` is pointed to via
505   :option:`--file`. This ensures better confidentiality with respect to other
506   users of the same computer.
507
508.. automethod:: Dialog.dash_escape
509
510.. automethod:: Dialog.dash_escape_nf
511
512.. _examples-of-dash-escaping:
513
514A contrived example using these methods could be the following, which sets a
515weird background title starting with two dashes (``--My little program``) for
516the life duration of a :class:`Dialog` instance *d*::
517
518  d.add_persistent_args(d.dash_escape_nf(
519      ["--backtitle", "--My little program"]))
520
521or, equivalently::
522
523  d.add_persistent_args(["--backtitle"]
524      + d.dash_escape(["--My little program"]))
525