├── .travis.yml ├── AUTHORS ├── COPYING ├── DEBUGGING ├── INSTALL ├── MANIFEST.in ├── PKG-INFO ├── README ├── TODO ├── debian ├── .gitignore ├── compat ├── control ├── copyright ├── python-dialog.docs ├── python-dialog.examples ├── rules └── source │ └── format ├── demo.py ├── dialog.py ├── setup.cfg └── setup.py /.travis.yml: -------------------------------------------------------------------------------- 1 | sudo: required 2 | language: generic 3 | 4 | services: 5 | - docker 6 | 7 | script: 8 | - wget -O- http://travis.debian.net/script.sh | sh - 9 | 10 | branches: 11 | except: 12 | - /^debian\/\d/ 13 | -------------------------------------------------------------------------------- /AUTHORS: -------------------------------------------------------------------------------- 1 | Peter Astrand (current maintainer) 2 | Robb Shecter 3 | Sultanbek Tezadov (http://sultan.da.ru/) 4 | Florent Rougon 5 | -------------------------------------------------------------------------------- /COPYING: -------------------------------------------------------------------------------- 1 | 2 | GNU LESSER GENERAL PUBLIC LICENSE 3 | Version 2.1, February 1999 4 | 5 | Copyright (C) 1991, 1999 Free Software Foundation, Inc. 6 | 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 7 | Everyone is permitted to copy and distribute verbatim copies 8 | of this license document, but changing it is not allowed. 9 | 10 | [This is the first released version of the Lesser GPL. It also counts 11 | as the successor of the GNU Library Public License, version 2, hence 12 | the version number 2.1.] 13 | 14 | Preamble 15 | 16 | The licenses for most software are designed to take away your 17 | freedom to share and change it. By contrast, the GNU General Public 18 | Licenses are intended to guarantee your freedom to share and change 19 | free software--to make sure the software is free for all its users. 20 | 21 | This license, the Lesser General Public License, applies to some 22 | specially designated software packages--typically libraries--of the 23 | Free Software Foundation and other authors who decide to use it. You 24 | can use it too, but we suggest you first think carefully about whether 25 | this license or the ordinary General Public License is the better 26 | strategy to use in any particular case, based on the explanations 27 | below. 28 | 29 | When we speak of free software, we are referring to freedom of use, 30 | not price. Our General Public Licenses are designed to make sure that 31 | you have the freedom to distribute copies of free software (and charge 32 | for this service if you wish); that you receive source code or can get 33 | it if you want it; that you can change the software and use pieces of 34 | it in new free programs; and that you are informed that you can do 35 | these things. 36 | 37 | To protect your rights, we need to make restrictions that forbid 38 | distributors to deny you these rights or to ask you to surrender these 39 | rights. These restrictions translate to certain responsibilities for 40 | you if you distribute copies of the library or if you modify it. 41 | 42 | For example, if you distribute copies of the library, whether gratis 43 | or for a fee, you must give the recipients all the rights that we gave 44 | you. You must make sure that they, too, receive or can get the source 45 | code. If you link other code with the library, you must provide 46 | complete object files to the recipients, so that they can relink them 47 | with the library after making changes to the library and recompiling 48 | it. And you must show them these terms so they know their rights. 49 | 50 | We protect your rights with a two-step method: (1) we copyright the 51 | library, and (2) we offer you this license, which gives you legal 52 | permission to copy, distribute and/or modify the library. 53 | 54 | To protect each distributor, we want to make it very clear that 55 | there is no warranty for the free library. Also, if the library is 56 | modified by someone else and passed on, the recipients should know 57 | that what they have is not the original version, so that the original 58 | author's reputation will not be affected by problems that might be 59 | introduced by others. 60 | ^L 61 | Finally, software patents pose a constant threat to the existence of 62 | any free program. We wish to make sure that a company cannot 63 | effectively restrict the users of a free program by obtaining a 64 | restrictive license from a patent holder. Therefore, we insist that 65 | any patent license obtained for a version of the library must be 66 | consistent with the full freedom of use specified in this license. 67 | 68 | Most GNU software, including some libraries, is covered by the 69 | ordinary GNU General Public License. This license, the GNU Lesser 70 | General Public License, applies to certain designated libraries, and 71 | is quite different from the ordinary General Public License. We use 72 | this license for certain libraries in order to permit linking those 73 | libraries into non-free programs. 74 | 75 | When a program is linked with a library, whether statically or using 76 | a shared library, the combination of the two is legally speaking a 77 | combined work, a derivative of the original library. The ordinary 78 | General Public License therefore permits such linking only if the 79 | entire combination fits its criteria of freedom. The Lesser General 80 | Public License permits more lax criteria for linking other code with 81 | the library. 82 | 83 | We call this license the "Lesser" General Public License because it 84 | does Less to protect the user's freedom than the ordinary General 85 | Public License. It also provides other free software developers Less 86 | of an advantage over competing non-free programs. These disadvantages 87 | are the reason we use the ordinary General Public License for many 88 | libraries. However, the Lesser license provides advantages in certain 89 | special circumstances. 90 | 91 | For example, on rare occasions, there may be a special need to 92 | encourage the widest possible use of a certain library, so that it 93 | becomes a de-facto standard. To achieve this, non-free programs must 94 | be allowed to use the library. A more frequent case is that a free 95 | library does the same job as widely used non-free libraries. In this 96 | case, there is little to gain by limiting the free library to free 97 | software only, so we use the Lesser General Public License. 98 | 99 | In other cases, permission to use a particular library in non-free 100 | programs enables a greater number of people to use a large body of 101 | free software. For example, permission to use the GNU C Library in 102 | non-free programs enables many more people to use the whole GNU 103 | operating system, as well as its variant, the GNU/Linux operating 104 | system. 105 | 106 | Although the Lesser General Public License is Less protective of the 107 | users' freedom, it does ensure that the user of a program that is 108 | linked with the Library has the freedom and the wherewithal to run 109 | that program using a modified version of the Library. 110 | 111 | The precise terms and conditions for copying, distribution and 112 | modification follow. Pay close attention to the difference between a 113 | "work based on the library" and a "work that uses the library". The 114 | former contains code derived from the library, whereas the latter must 115 | be combined with the library in order to run. 116 | ^L 117 | GNU LESSER GENERAL PUBLIC LICENSE 118 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 119 | 120 | 0. This License Agreement applies to any software library or other 121 | program which contains a notice placed by the copyright holder or 122 | other authorized party saying it may be distributed under the terms of 123 | this Lesser General Public License (also called "this License"). 124 | Each licensee is addressed as "you". 125 | 126 | A "library" means a collection of software functions and/or data 127 | prepared so as to be conveniently linked with application programs 128 | (which use some of those functions and data) to form executables. 129 | 130 | The "Library", below, refers to any such software library or work 131 | which has been distributed under these terms. A "work based on the 132 | Library" means either the Library or any derivative work under 133 | copyright law: that is to say, a work containing the Library or a 134 | portion of it, either verbatim or with modifications and/or translated 135 | straightforwardly into another language. (Hereinafter, translation is 136 | included without limitation in the term "modification".) 137 | 138 | "Source code" for a work means the preferred form of the work for 139 | making modifications to it. For a library, complete source code means 140 | all the source code for all modules it contains, plus any associated 141 | interface definition files, plus the scripts used to control 142 | compilation and installation of the library. 143 | 144 | Activities other than copying, distribution and modification are not 145 | covered by this License; they are outside its scope. The act of 146 | running a program using the Library is not restricted, and output from 147 | such a program is covered only if its contents constitute a work based 148 | on the Library (independent of the use of the Library in a tool for 149 | writing it). Whether that is true depends on what the Library does 150 | and what the program that uses the Library does. 151 | 152 | 1. You may copy and distribute verbatim copies of the Library's 153 | complete source code as you receive it, in any medium, provided that 154 | you conspicuously and appropriately publish on each copy an 155 | appropriate copyright notice and disclaimer of warranty; keep intact 156 | all the notices that refer to this License and to the absence of any 157 | warranty; and distribute a copy of this License along with the 158 | Library. 159 | 160 | You may charge a fee for the physical act of transferring a copy, 161 | and you may at your option offer warranty protection in exchange for a 162 | fee. 163 | 164 | 2. You may modify your copy or copies of the Library or any portion 165 | of it, thus forming a work based on the Library, and copy and 166 | distribute such modifications or work under the terms of Section 1 167 | above, provided that you also meet all of these conditions: 168 | 169 | a) The modified work must itself be a software library. 170 | 171 | b) You must cause the files modified to carry prominent notices 172 | stating that you changed the files and the date of any change. 173 | 174 | c) You must cause the whole of the work to be licensed at no 175 | charge to all third parties under the terms of this License. 176 | 177 | d) If a facility in the modified Library refers to a function or a 178 | table of data to be supplied by an application program that uses 179 | the facility, other than as an argument passed when the facility 180 | is invoked, then you must make a good faith effort to ensure that, 181 | in the event an application does not supply such function or 182 | table, the facility still operates, and performs whatever part of 183 | its purpose remains meaningful. 184 | 185 | (For example, a function in a library to compute square roots has 186 | a purpose that is entirely well-defined independent of the 187 | application. Therefore, Subsection 2d requires that any 188 | application-supplied function or table used by this function must 189 | be optional: if the application does not supply it, the square 190 | root function must still compute square roots.) 191 | 192 | These requirements apply to the modified work as a whole. If 193 | identifiable sections of that work are not derived from the Library, 194 | and can be reasonably considered independent and separate works in 195 | themselves, then this License, and its terms, do not apply to those 196 | sections when you distribute them as separate works. But when you 197 | distribute the same sections as part of a whole which is a work based 198 | on the Library, the distribution of the whole must be on the terms of 199 | this License, whose permissions for other licensees extend to the 200 | entire whole, and thus to each and every part regardless of who wrote 201 | it. 202 | 203 | Thus, it is not the intent of this section to claim rights or contest 204 | your rights to work written entirely by you; rather, the intent is to 205 | exercise the right to control the distribution of derivative or 206 | collective works based on the Library. 207 | 208 | In addition, mere aggregation of another work not based on the Library 209 | with the Library (or with a work based on the Library) on a volume of 210 | a storage or distribution medium does not bring the other work under 211 | the scope of this License. 212 | 213 | 3. You may opt to apply the terms of the ordinary GNU General Public 214 | License instead of this License to a given copy of the Library. To do 215 | this, you must alter all the notices that refer to this License, so 216 | that they refer to the ordinary GNU General Public License, version 2, 217 | instead of to this License. (If a newer version than version 2 of the 218 | ordinary GNU General Public License has appeared, then you can specify 219 | that version instead if you wish.) Do not make any other change in 220 | these notices. 221 | ^L 222 | Once this change is made in a given copy, it is irreversible for 223 | that copy, so the ordinary GNU General Public License applies to all 224 | subsequent copies and derivative works made from that copy. 225 | 226 | This option is useful when you wish to copy part of the code of 227 | the Library into a program that is not a library. 228 | 229 | 4. You may copy and distribute the Library (or a portion or 230 | derivative of it, under Section 2) in object code or executable form 231 | under the terms of Sections 1 and 2 above provided that you accompany 232 | it with the complete corresponding machine-readable source code, which 233 | must be distributed under the terms of Sections 1 and 2 above on a 234 | medium customarily used for software interchange. 235 | 236 | If distribution of object code is made by offering access to copy 237 | from a designated place, then offering equivalent access to copy the 238 | source code from the same place satisfies the requirement to 239 | distribute the source code, even though third parties are not 240 | compelled to copy the source along with the object code. 241 | 242 | 5. A program that contains no derivative of any portion of the 243 | Library, but is designed to work with the Library by being compiled or 244 | linked with it, is called a "work that uses the Library". Such a 245 | work, in isolation, is not a derivative work of the Library, and 246 | therefore falls outside the scope of this License. 247 | 248 | However, linking a "work that uses the Library" with the Library 249 | creates an executable that is a derivative of the Library (because it 250 | contains portions of the Library), rather than a "work that uses the 251 | library". The executable is therefore covered by this License. 252 | Section 6 states terms for distribution of such executables. 253 | 254 | When a "work that uses the Library" uses material from a header file 255 | that is part of the Library, the object code for the work may be a 256 | derivative work of the Library even though the source code is not. 257 | Whether this is true is especially significant if the work can be 258 | linked without the Library, or if the work is itself a library. The 259 | threshold for this to be true is not precisely defined by law. 260 | 261 | If such an object file uses only numerical parameters, data 262 | structure layouts and accessors, and small macros and small inline 263 | functions (ten lines or less in length), then the use of the object 264 | file is unrestricted, regardless of whether it is legally a derivative 265 | work. (Executables containing this object code plus portions of the 266 | Library will still fall under Section 6.) 267 | 268 | Otherwise, if the work is a derivative of the Library, you may 269 | distribute the object code for the work under the terms of Section 6. 270 | Any executables containing that work also fall under Section 6, 271 | whether or not they are linked directly with the Library itself. 272 | ^L 273 | 6. As an exception to the Sections above, you may also combine or 274 | link a "work that uses the Library" with the Library to produce a 275 | work containing portions of the Library, and distribute that work 276 | under terms of your choice, provided that the terms permit 277 | modification of the work for the customer's own use and reverse 278 | engineering for debugging such modifications. 279 | 280 | You must give prominent notice with each copy of the work that the 281 | Library is used in it and that the Library and its use are covered by 282 | this License. You must supply a copy of this License. If the work 283 | during execution displays copyright notices, you must include the 284 | copyright notice for the Library among them, as well as a reference 285 | directing the user to the copy of this License. Also, you must do one 286 | of these things: 287 | 288 | a) Accompany the work with the complete corresponding 289 | machine-readable source code for the Library including whatever 290 | changes were used in the work (which must be distributed under 291 | Sections 1 and 2 above); and, if the work is an executable linked 292 | with the Library, with the complete machine-readable "work that 293 | uses the Library", as object code and/or source code, so that the 294 | user can modify the Library and then relink to produce a modified 295 | executable containing the modified Library. (It is understood 296 | that the user who changes the contents of definitions files in the 297 | Library will not necessarily be able to recompile the application 298 | to use the modified definitions.) 299 | 300 | b) Use a suitable shared library mechanism for linking with the 301 | Library. A suitable mechanism is one that (1) uses at run time a 302 | copy of the library already present on the user's computer system, 303 | rather than copying library functions into the executable, and (2) 304 | will operate properly with a modified version of the library, if 305 | the user installs one, as long as the modified version is 306 | interface-compatible with the version that the work was made with. 307 | 308 | c) Accompany the work with a written offer, valid for at least 309 | three years, to give the same user the materials specified in 310 | Subsection 6a, above, for a charge no more than the cost of 311 | performing this distribution. 312 | 313 | d) If distribution of the work is made by offering access to copy 314 | from a designated place, offer equivalent access to copy the above 315 | specified materials from the same place. 316 | 317 | e) Verify that the user has already received a copy of these 318 | materials or that you have already sent this user a copy. 319 | 320 | For an executable, the required form of the "work that uses the 321 | Library" must include any data and utility programs needed for 322 | reproducing the executable from it. However, as a special exception, 323 | the materials to be distributed need not include anything that is 324 | normally distributed (in either source or binary form) with the major 325 | components (compiler, kernel, and so on) of the operating system on 326 | which the executable runs, unless that component itself accompanies 327 | the executable. 328 | 329 | It may happen that this requirement contradicts the license 330 | restrictions of other proprietary libraries that do not normally 331 | accompany the operating system. Such a contradiction means you cannot 332 | use both them and the Library together in an executable that you 333 | distribute. 334 | ^L 335 | 7. You may place library facilities that are a work based on the 336 | Library side-by-side in a single library together with other library 337 | facilities not covered by this License, and distribute such a combined 338 | library, provided that the separate distribution of the work based on 339 | the Library and of the other library facilities is otherwise 340 | permitted, and provided that you do these two things: 341 | 342 | a) Accompany the combined library with a copy of the same work 343 | based on the Library, uncombined with any other library 344 | facilities. This must be distributed under the terms of the 345 | Sections above. 346 | 347 | b) Give prominent notice with the combined library of the fact 348 | that part of it is a work based on the Library, and explaining 349 | where to find the accompanying uncombined form of the same work. 350 | 351 | 8. You may not copy, modify, sublicense, link with, or distribute 352 | the Library except as expressly provided under this License. Any 353 | attempt otherwise to copy, modify, sublicense, link with, or 354 | distribute the Library is void, and will automatically terminate your 355 | rights under this License. However, parties who have received copies, 356 | or rights, from you under this License will not have their licenses 357 | terminated so long as such parties remain in full compliance. 358 | 359 | 9. You are not required to accept this License, since you have not 360 | signed it. However, nothing else grants you permission to modify or 361 | distribute the Library or its derivative works. These actions are 362 | prohibited by law if you do not accept this License. Therefore, by 363 | modifying or distributing the Library (or any work based on the 364 | Library), you indicate your acceptance of this License to do so, and 365 | all its terms and conditions for copying, distributing or modifying 366 | the Library or works based on it. 367 | 368 | 10. Each time you redistribute the Library (or any work based on the 369 | Library), the recipient automatically receives a license from the 370 | original licensor to copy, distribute, link with or modify the Library 371 | subject to these terms and conditions. You may not impose any further 372 | restrictions on the recipients' exercise of the rights granted herein. 373 | You are not responsible for enforcing compliance by third parties with 374 | this License. 375 | ^L 376 | 11. If, as a consequence of a court judgment or allegation of patent 377 | infringement or for any other reason (not limited to patent issues), 378 | conditions are imposed on you (whether by court order, agreement or 379 | otherwise) that contradict the conditions of this License, they do not 380 | excuse you from the conditions of this License. If you cannot 381 | distribute so as to satisfy simultaneously your obligations under this 382 | License and any other pertinent obligations, then as a consequence you 383 | may not distribute the Library at all. For example, if a patent 384 | license would not permit royalty-free redistribution of the Library by 385 | all those who receive copies directly or indirectly through you, then 386 | the only way you could satisfy both it and this License would be to 387 | refrain entirely from distribution of the Library. 388 | 389 | If any portion of this section is held invalid or unenforceable under 390 | any particular circumstance, the balance of the section is intended to 391 | apply, and the section as a whole is intended to apply in other 392 | circumstances. 393 | 394 | It is not the purpose of this section to induce you to infringe any 395 | patents or other property right claims or to contest validity of any 396 | such claims; this section has the sole purpose of protecting the 397 | integrity of the free software distribution system which is 398 | implemented by public license practices. Many people have made 399 | generous contributions to the wide range of software distributed 400 | through that system in reliance on consistent application of that 401 | system; it is up to the author/donor to decide if he or she is willing 402 | to distribute software through any other system and a licensee cannot 403 | impose that choice. 404 | 405 | This section is intended to make thoroughly clear what is believed to 406 | be a consequence of the rest of this License. 407 | 408 | 12. If the distribution and/or use of the Library is restricted in 409 | certain countries either by patents or by copyrighted interfaces, the 410 | original copyright holder who places the Library under this License 411 | may add an explicit geographical distribution limitation excluding those 412 | countries, so that distribution is permitted only in or among 413 | countries not thus excluded. In such case, this License incorporates 414 | the limitation as if written in the body of this License. 415 | 416 | 13. The Free Software Foundation may publish revised and/or new 417 | versions of the Lesser General Public License from time to time. 418 | Such new versions will be similar in spirit to the present version, 419 | but may differ in detail to address new problems or concerns. 420 | 421 | Each version is given a distinguishing version number. If the Library 422 | specifies a version number of this License which applies to it and 423 | "any later version", you have the option of following the terms and 424 | conditions either of that version or of any later version published by 425 | the Free Software Foundation. If the Library does not specify a 426 | license version number, you may choose any version ever published by 427 | the Free Software Foundation. 428 | ^L 429 | 14. If you wish to incorporate parts of the Library into other free 430 | programs whose distribution conditions are incompatible with these, 431 | write to the author to ask for permission. For software which is 432 | copyrighted by the Free Software Foundation, write to the Free 433 | Software Foundation; we sometimes make exceptions for this. Our 434 | decision will be guided by the two goals of preserving the free status 435 | of all derivatives of our free software and of promoting the sharing 436 | and reuse of software generally. 437 | 438 | NO WARRANTY 439 | 440 | 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO 441 | WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. 442 | EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR 443 | OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY 444 | KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE 445 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 446 | PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE 447 | LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME 448 | THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 449 | 450 | 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN 451 | WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY 452 | AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU 453 | FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR 454 | CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE 455 | LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING 456 | RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A 457 | FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF 458 | SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH 459 | DAMAGES. 460 | 461 | END OF TERMS AND CONDITIONS 462 | ^L 463 | How to Apply These Terms to Your New Libraries 464 | 465 | If you develop a new library, and you want it to be of the greatest 466 | possible use to the public, we recommend making it free software that 467 | everyone can redistribute and change. You can do so by permitting 468 | redistribution under these terms (or, alternatively, under the terms 469 | of the ordinary General Public License). 470 | 471 | To apply these terms, attach the following notices to the library. 472 | It is safest to attach them to the start of each source file to most 473 | effectively convey the exclusion of warranty; and each file should 474 | have at least the "copyright" line and a pointer to where the full 475 | notice is found. 476 | 477 | 478 | 479 | Copyright (C) 480 | 481 | This library is free software; you can redistribute it and/or 482 | modify it under the terms of the GNU Lesser General Public 483 | License as published by the Free Software Foundation; either 484 | version 2.1 of the License, or (at your option) any later version. 485 | 486 | This library is distributed in the hope that it will be useful, 487 | but WITHOUT ANY WARRANTY; without even the implied warranty of 488 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 489 | Lesser General Public License for more details. 490 | 491 | You should have received a copy of the GNU Lesser General Public 492 | License along with this library; if not, write to the Free Software 493 | Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 494 | 495 | Also add information on how to contact you by electronic and paper mail. 496 | 497 | You should also get your employer (if you work as a programmer) or 498 | your school, if any, to sign a "copyright disclaimer" for the library, 499 | if necessary. Here is a sample; alter the names: 500 | 501 | Yoyodyne, Inc., hereby disclaims all copyright interest in the 502 | library `Frob' (a library for tweaking knobs) written by James 503 | Random Hacker. 504 | 505 | , 1 April 1990 506 | Ty Coon, President of Vice 507 | 508 | That's all there is to it! 509 | 510 | 511 | -------------------------------------------------------------------------------- /DEBUGGING: -------------------------------------------------------------------------------- 1 | # The following lines are useful for debugging: they write the 2 | # full command (with arguments quoted and environment 3 | # variables set) to a file named "command.debug" in the 4 | # current directory. See Dialog.__call_program() in dialog.py. 5 | 6 | import commands 7 | 8 | envvar_settings_list = [] 9 | 10 | if new_environ.has_key("DIALOGRC"): 11 | envvar_settings_list.append( 12 | "DIALOGRC='%s'" % new_environ["DIALOGRC"]) 13 | 14 | for var in _dialog_exit_status_vars.keys(): 15 | varname = "DIALOG_" + var 16 | envvar_settings_list.append( 17 | "%s=%s" % (varname, new_environ[varname])) 18 | 19 | envvar_settings = string.join(envvar_settings_list, " ") 20 | 21 | file("command.debug", "wb").write( 22 | envvar_settings + 23 | string.join(map(commands.mkarg, arglist), "")) 24 | -------------------------------------------------------------------------------- /INSTALL: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/turnkeylinux/pythondialog/037798a4d8c889e34fb579f5e924682b237b96c3/INSTALL -------------------------------------------------------------------------------- /MANIFEST.in: -------------------------------------------------------------------------------- 1 | include COPYING INSTALL AUTHORS TODO DEBUGGING demo.py 2 | include MANIFEST.in 3 | # global-exclude *~ 4 | -------------------------------------------------------------------------------- /PKG-INFO: -------------------------------------------------------------------------------- 1 | Metadata-Version: 1.0 2 | Name: pythondialog 3 | Version: 2.7 4 | Summary: A Python interface to the UNIX dialog utility and mostly-compatible programs 5 | Home-page: http://pythondialog.sourceforge.net/ 6 | Author: Peter Astrand 7 | Author-email: peter@cendio.se 8 | License: LGPL 9 | Description: A Python interface to the UNIX dialog utility, designed to provide 10 | an easy, pythonic and as complete as possible way to use the dialog 11 | features from Python code. 12 | Back-end programs that are almost compatible with dialog are also 13 | supported if someone cares about them. 14 | Keywords: dialog,Xdialog,whiptail,text-mode interface 15 | Platform: UNIX 16 | -------------------------------------------------------------------------------- /README: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/turnkeylinux/pythondialog/037798a4d8c889e34fb579f5e924682b237b96c3/README -------------------------------------------------------------------------------- /TODO: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/turnkeylinux/pythondialog/037798a4d8c889e34fb579f5e924682b237b96c3/TODO -------------------------------------------------------------------------------- /debian/.gitignore: -------------------------------------------------------------------------------- 1 | changelog 2 | -------------------------------------------------------------------------------- /debian/compat: -------------------------------------------------------------------------------- 1 | 10 2 | -------------------------------------------------------------------------------- /debian/control: -------------------------------------------------------------------------------- 1 | Source: pythondialog 2 | Section: python 3 | Priority: optional 4 | Maintainer: Adam Cécile (Le_Vert) 5 | Build-Depends: 6 | debhelper (>= 10), 7 | dh-python, 8 | python-all (>= 2.6.6-3~), 9 | Homepage: http://pythondialog.sourceforge.net/ 10 | Standards-Version: 4.0.0 11 | X-Python-Version: >= 2.6 12 | 13 | Package: python-dialog 14 | Architecture: any 15 | Depends: 16 | dialog, 17 | ${misc:Depends}, 18 | ${python:Depends}, 19 | Provides: 20 | ${python:Provides}, 21 | Description: Python module for making simple Text/Console-mode user interfaces 22 | This is a Python module for doing console-mode user interaction. 23 | . 24 | It wraps the dialog/Xdialog program, and provides a nice, object-oriented 25 | programming model. 26 | -------------------------------------------------------------------------------- /debian/copyright: -------------------------------------------------------------------------------- 1 | This package was debianized by Adam Cécile (Le_Vert) on 2 | Sun, 10 Dec 2006 16:30:10 +0100. 3 | 4 | It was downloaded from http://pythondialog.sourceforge.net/ 5 | 6 | Upstream Author: Peter Astrand 7 | Robb Shecter 8 | Sultanbek Tezadov 9 | Florent Rougon 10 | 11 | Copyright: (C) 2000 Robb Shecter, Sultanbek Tezadov 12 | (C) 2002, 2003, 2004 Florent Rougon 13 | 14 | Patched by Alon Swartz and integrated patch 15 | available from http://pythondialog.sourceforge.net authored by 16 | Chris Dew 17 | 18 | License: 19 | 20 | This package is free software; you can redistribute it and/or 21 | modify it under the terms of the GNU Lesser General Public 22 | License as published by the Free Software Foundation; either 23 | version 2 of the License, or (at your option) any later version. 24 | 25 | This package is distributed in the hope that it will be useful, 26 | but WITHOUT ANY WARRANTY; without even the implied warranty of 27 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 28 | Lesser General Public License for more details. 29 | 30 | You should have received a copy of the GNU Lesser General Public 31 | License along with this package; if not, write to the Free Software 32 | Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 33 | 34 | On Debian systems, the complete text of the GNU Lesser General 35 | Public License can be found in `/usr/share/common-licenses/LGPL'. 36 | 37 | The Debian packaging is Copyright 2006, Adam Cécile (Le_Vert) 38 | and is licensed under the GPL, see `/usr/share/common-licenses/GPL'. 39 | -------------------------------------------------------------------------------- /debian/python-dialog.docs: -------------------------------------------------------------------------------- 1 | DEBUGGING 2 | README 3 | TODO 4 | dialog.html 5 | -------------------------------------------------------------------------------- /debian/python-dialog.examples: -------------------------------------------------------------------------------- 1 | demo.py 2 | -------------------------------------------------------------------------------- /debian/rules: -------------------------------------------------------------------------------- 1 | #! /usr/bin/make -f 2 | 3 | DIALOG_PY = /usr/share/pyshared/dialog.py 4 | 5 | %: 6 | dh $@ --with python2 --buildsystem=pybuild 7 | 8 | override_dh_auto_clean: 9 | dh_auto_clean 10 | rm -f dialog.html 11 | 12 | override_dh_auto_build: 13 | dh_auto_build 14 | 15 | pydoc -w dialog 16 | # Fix path to sources 17 | sed -i -e 's!.*!$(DIALOG_PY)!' dialog.html 18 | 19 | override_dh_compress: 20 | dh_compress -Xdemo.py -Xpydoc.html 21 | -------------------------------------------------------------------------------- /debian/source/format: -------------------------------------------------------------------------------- 1 | 3.0 (native) 2 | -------------------------------------------------------------------------------- /demo.py: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env python 2 | 3 | # demo.py --- A simple demonstration program for pythondialog 4 | # Copyright (C) 2000 Robb Shecter, Sultanbek Tezadov 5 | # Copyright (C) 2002, 2004 Florent Rougon 6 | # 7 | # This program is in the public domain. 8 | 9 | """Demonstration program for pythondialog. 10 | 11 | This is a simple program demonstrating the possibilities offered by 12 | the pythondialog module (which is itself a Python interface to the 13 | well-known dialog utility, or any other program compatible with 14 | dialog). 15 | 16 | Please have a look at the documentation for the `handle_exit_code' 17 | function in order to understand the somewhat relaxed error checking 18 | policy for pythondialog calls in this demo. 19 | 20 | """ 21 | 22 | import sys, os, os.path, time, string, dialog 23 | 24 | FAST_DEMO = 0 25 | 26 | 27 | # XXX We should handle the new DIALOG_HELP and DIALOG_EXTRA return codes here. 28 | def handle_exit_code(d, code): 29 | """Sample function showing how to interpret the dialog exit codes. 30 | 31 | This function is not used after every call to dialog in this demo 32 | for two reasons: 33 | 34 | 1. For some boxes, unfortunately, dialog returns the code for 35 | ERROR when the user presses ESC (instead of the one chosen 36 | for ESC). As these boxes only have an OK button, and an 37 | exception is raised and correctly handled here in case of 38 | real dialog errors, there is no point in testing the dialog 39 | exit status (it can't be CANCEL as there is no CANCEL 40 | button; it can't be ESC as unfortunately, the dialog makes 41 | it appear as an error; it can't be ERROR as this is handled 42 | in dialog.py to raise an exception; therefore, it *is* OK). 43 | 44 | 2. To not clutter simple code with things that are 45 | demonstrated elsewhere. 46 | 47 | """ 48 | # d is supposed to be a Dialog instance 49 | if code in (d.DIALOG_CANCEL, d.DIALOG_ESC): 50 | if code == d.DIALOG_CANCEL: 51 | msg = "You chose cancel in the last dialog box. Do you want to " \ 52 | "exit this demo?" 53 | else: 54 | msg = "You pressed ESC in the last dialog box. Do you want to " \ 55 | "exit this demo?" 56 | # "No" or "ESC" will bring the user back to the demo. 57 | # DIALOG_ERROR is propagated as an exception and caught in main(). 58 | # So we only need to handle OK here. 59 | if d.yesno(msg) == d.DIALOG_OK: 60 | sys.exit(0) 61 | return 0 62 | else: 63 | return 1 # code is d.DIALOG_OK 64 | 65 | 66 | def infobox_demo(d): 67 | # Exit code thrown away to keey this demo code simple (however, real 68 | # errors are propagated by an exception) 69 | d.infobox("One moment, please. Just wasting some time here to " 70 | "show you the infobox...") 71 | 72 | if FAST_DEMO: 73 | time.sleep(0.5) 74 | else: 75 | time.sleep(3) 76 | 77 | 78 | def gauge_demo(d): 79 | d.gauge_start("Progress: 0%", title="Still testing your patience...") 80 | for i in range(1, 101): 81 | if i < 50: 82 | d.gauge_update(i, "Progress: %d%%" % i, update_text=1) 83 | elif i == 50: 84 | d.gauge_update(i, "Over %d%%. Good." % i, update_text=1) 85 | elif i == 80: 86 | d.gauge_update(i, "Yeah, this boring crap will be over Really " 87 | "Soon Now.", update_text=1) 88 | else: 89 | d.gauge_update(i) 90 | 91 | if FAST_DEMO: 92 | time.sleep(0.01) 93 | else: 94 | time.sleep(0.1) 95 | d.gauge_stop() 96 | 97 | 98 | def yesno_demo(d): 99 | # Return the answer given to the question (also specifies if ESC was 100 | # pressed) 101 | return d.yesno("Do you like this demo?") 102 | 103 | 104 | def msgbox_demo(d, answer): 105 | if answer == d.DIALOG_OK: 106 | d.msgbox("Excellent! Press OK to see the source code.") 107 | else: 108 | d.msgbox("Well, feel free to send your complaints to /dev/null!") 109 | 110 | 111 | def textbox_demo(d): 112 | d.textbox("demo.py", width=76) 113 | 114 | 115 | def inputbox_demo(d): 116 | # If the user presses Cancel, he is asked (by handle_exit_code) if he 117 | # wants to exit the demo. We loop as long as he tells us he doesn't want 118 | # to do so. 119 | while 1: 120 | (code, answer) = d.inputbox("What's your name?", init="Snow White") 121 | if handle_exit_code(d, code): 122 | break 123 | return answer 124 | 125 | 126 | def menu_demo(d): 127 | while 1: 128 | (code, tag) = d.menu( 129 | "What's your favorite day of the week?", 130 | width=60, 131 | choices=[("Monday", "Being the first day of the week..."), 132 | ("Tuesday", "Comes after Monday"), 133 | ("Wednesday", "Before Thursday day"), 134 | ("Thursday", "Itself after Wednesday"), 135 | ("Friday", "The best day of all"), 136 | ("Saturday", "Well, I've had enough, thanks"), 137 | ("Sunday", "Let's rest a little bit")]) 138 | if handle_exit_code(d, code): 139 | break 140 | return tag 141 | 142 | 143 | def checklist_demo(d): 144 | while 1: 145 | # We could put non-empty items here (not only the tag for each entry) 146 | (code, tag) = d.checklist(text="What sandwich toppings do you like?", 147 | height=15, width=54, list_height=7, 148 | choices=[("Catsup", "", 0), 149 | ("Mustard", "", 0), 150 | ("Pesto", "", 0), 151 | ("Mayonaise", "", 1), 152 | ("Horse radish","", 1), 153 | ("Sun-dried tomatoes", "", 1)], 154 | title="Do you prefer ham or spam?", 155 | backtitle="And now, for something " 156 | "completely different...") 157 | if handle_exit_code(d, code): 158 | break 159 | return tag 160 | 161 | 162 | def form_demo(d): 163 | while 1: 164 | (code, tag) = d.form(text="Which are your favourites?", 165 | height=15, width=54, form_height=7, 166 | fields=[("Colour", "", 10, 30), 167 | ("Rugby Team", "", 20), 168 | ("Car", "", 20), 169 | ("Celebrity", "", 20)], 170 | title="A demo of the form dialog.", 171 | backtitle="And now, for something " 172 | "completely different...") 173 | if handle_exit_code(d, code): 174 | break 175 | return tag 176 | 177 | 178 | def passwordform_demo(d): 179 | while 1: 180 | (code, tag) = d.passwordform(text="Set your password", 181 | height=15, width=54, form_height=7, 182 | fields=[("Admin Password:", "", 10, 20), 183 | (" Confirm:", "", 10, 20)], 184 | title="A demo of the passwordform dialog.", 185 | backtitle="And now, for something " 186 | "completely different...") 187 | if handle_exit_code(d, code): 188 | break 189 | return tag 190 | 191 | 192 | def radiolist_demo(d): 193 | while 1: 194 | (code, tag) = d.radiolist( 195 | "What's your favorite kind of sandwich?", 196 | width=65, 197 | choices=[("Hamburger", "2 slices of bread, a steak...", 0), 198 | ("Hotdog", "doesn't bite any more", 0), 199 | ("Burrito", "no se lo que es", 0), 200 | ("Doener", "Huh?", 0), 201 | ("Falafel", "Erm...", 0), 202 | ("Bagel", "Of course!", 0), 203 | ("Big Mac", "Ah, that's easy!", 1), 204 | ("Whopper", "Erm, sorry", 0), 205 | ("Quarter Pounder", 'called "le Big Mac" in France', 0), 206 | ("Peanut Butter and Jelly", "Well, that's your own " 207 | "business...", 0), 208 | ("Grilled cheese", "And nothing more?", 0)]) 209 | if handle_exit_code(d, code): 210 | break 211 | return tag 212 | 213 | 214 | def calendar_demo(d): 215 | while 1: 216 | (code, date) = d.calendar("When do you think Debian sarge will be " 217 | "released?", year=0) 218 | if handle_exit_code(d, code): 219 | break 220 | return date 221 | 222 | 223 | def passwordbox_demo(d): 224 | while 1: 225 | (code, password) = d.passwordbox("What is your root password, " 226 | "so that I can crack your system " 227 | "right now?") 228 | if handle_exit_code(d, code): 229 | break 230 | return password 231 | 232 | 233 | def comment_on_sarge_release_date(day, month, year): 234 | if year < 2004 or (year == 2004 and month <= 3): 235 | return "Mmm... what about a little tour on http://www.debian.org/?" 236 | elif year == 2004 and month <= 4: 237 | return """\ 238 | Damn, how optimistic! You don't know much about Debian, do you?""" 239 | elif year == 2004 and month <= 7: 240 | return """\ 241 | Well, good guess. But who knows what the future reserves to us? ;-)""" 242 | elif year == 2004: 243 | return """\ 244 | Oh, well. That's plausible. But please, please don't depress 245 | other people with your pronostics... ;-)""" 246 | else: 247 | return "Hey, you're a troll! (or do you know Debian *so* well? ;-)" 248 | 249 | 250 | def scrollbox_demo(d, name, favorite_day, toppings, favourites, passwords, 251 | sandwich, date, password): 252 | day, month, year = date 253 | msg = """\ 254 | Here are some vital statistics about you: 255 | 256 | Name: %s 257 | Favorite day of the week: %s 258 | Favorite sandwich toppings:%s 259 | Favorites:%s 260 | Passwords:%s 261 | Favorite sandwich: %s 262 | 263 | You estimate Debian sarge's release to happen around %04u-%02u-%02u. 264 | %s 265 | 266 | Your root password is: ************************** (looks good!)""" \ 267 | % (name, favorite_day, 268 | string.join([''] + toppings, "\n "), 269 | string.join([] + favourites, ", "), 270 | string.join([] + passwords, ", confirmed "), 271 | sandwich, year, month, day, 272 | comment_on_sarge_release_date(day, month, year)) 273 | d.scrollbox(msg, height=20, width=75, title="Great Report of the Year") 274 | 275 | 276 | def fselect_demo(d): 277 | while 1: 278 | root_dir = os.sep # This is OK for UNIX systems 279 | dir = os.getenv("HOME", root_dir) 280 | # Make sure the directory we chose ends with os.sep() so that dialog 281 | # shows its contents right away 282 | if dir and dir[-1] != os.sep: 283 | dir = dir + os.sep 284 | 285 | (code, path) = d.fselect(dir, 10, 50, 286 | title="Cute little file to show as " 287 | "in a `tail -f'") 288 | if handle_exit_code(d, code): 289 | if not os.path.isfile(path): 290 | d.scrollbox("Hmm. Didn't I ask you to select a *file*?", 291 | width=50, height=10) 292 | else: 293 | break 294 | return path 295 | 296 | 297 | def tailbox_demo(d, file): 298 | d.tailbox(file, 20, 60, title="You are brave. You deserve the " 299 | "right to rest, now." ) 300 | 301 | 302 | def demo(): 303 | # If you want to use Xdialog (pathnames are also OK for the 'dialog' 304 | # argument) 305 | # d = dialog.Dialog(dialog="Xdialog", compat="Xdialog") 306 | d = dialog.Dialog(dialog="dialog") 307 | 308 | d.add_persistent_args(["--backtitle", "pythondialog demo"]) 309 | 310 | infobox_demo(d) 311 | gauge_demo(d) 312 | answer = yesno_demo(d) 313 | msgbox_demo(d, answer) 314 | textbox_demo(d) 315 | name = inputbox_demo(d) 316 | favorite_day = menu_demo(d) 317 | toppings = checklist_demo(d) 318 | favourites = form_demo(d) 319 | passwords = passwordform_demo(d) 320 | sandwich = radiolist_demo(d) 321 | date = calendar_demo(d) 322 | password = passwordbox_demo(d) 323 | scrollbox_demo(d, name, favorite_day, toppings, favourites, passwords, sandwich, date, password) 324 | 325 | d.scrollbox("""\ 326 | Haha. You thought it was over. Wrong. Even More fun is to come! 327 | (well, depending on your definition on "fun") 328 | 329 | Now, please select a file you would like to see growing (or not...).""", 330 | width=75) 331 | 332 | file = fselect_demo(d) 333 | tailbox_demo(d, file) 334 | 335 | d.scrollbox("""\ 336 | Now, you're done. No, I'm not kidding. 337 | 338 | So, why the hell are you sitting here instead of rushing on that EXIT 339 | button? Ah, you did like the demo. Hmm... are you feeling OK? ;-)""", 340 | width=75) 341 | 342 | 343 | def main(): 344 | """This demo shows the main features of the pythondialog Dialog class. 345 | 346 | """ 347 | try: 348 | demo() 349 | except dialog.error, exc_instance: 350 | sys.stderr.write("Error:\n\n%s\n" % exc_instance.complete_message()) 351 | sys.exit(1) 352 | 353 | sys.exit(0) 354 | 355 | 356 | if __name__ == "__main__": main() 357 | -------------------------------------------------------------------------------- /dialog.py: -------------------------------------------------------------------------------- 1 | # dialog.py --- A python interface to the Linux "dialog" utility 2 | # Copyright (C) 2000 Robb Shecter, Sultanbek Tezadov 3 | # Copyright (C) 2002, 2003, 2004 Florent Rougon 4 | # 5 | # This library is free software; you can redistribute it and/or 6 | # modify it under the terms of the GNU Lesser General Public 7 | # License as published by the Free Software Foundation; either 8 | # version 2.1 of the License, or (at your option) any later version. 9 | # 10 | # This library is distributed in the hope that it will be useful, 11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 13 | # Lesser General Public License for more details. 14 | # 15 | # You should have received a copy of the GNU Lesser General Public 16 | # License along with this library; if not, write to the Free Software 17 | # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 18 | 19 | """Python interface to dialog-like programs. 20 | 21 | This module provides a Python interface to dialog-like programs such 22 | as `dialog', `Xdialog' and `whiptail'. 23 | 24 | It provides a Dialog class that retains some parameters such as the 25 | program name and path as well as the values to pass as DIALOG* 26 | environment variables to the chosen program. 27 | 28 | For a quick start, you should look at the demo.py file that comes 29 | with pythondialog. It demonstrates a simple use of each widget 30 | offered by the Dialog class. 31 | 32 | See the Dialog class documentation for general usage information, 33 | list of available widgets and ways to pass options to dialog. 34 | 35 | 36 | Notable exceptions 37 | ------------------ 38 | 39 | Here is the hierarchy of notable exceptions raised by this module: 40 | 41 | error 42 | ExecutableNotFound 43 | BadPythonDialogUsage 44 | PythonDialogSystemError 45 | PythonDialogIOError 46 | PythonDialogOSError 47 | PythonDialogErrorBeforeExecInChildProcess 48 | PythonDialogReModuleError 49 | UnexpectedDialogOutput 50 | DialogTerminatedBySignal 51 | DialogError 52 | UnableToCreateTemporaryDirectory 53 | PythonDialogBug 54 | ProbablyPythonBug 55 | 56 | As you can see, every exception `exc' among them verifies: 57 | 58 | issubclass(exc, error) 59 | 60 | so if you don't need fine-grained error handling, simply catch 61 | `error' (which will probably be accessible as dialog.error from your 62 | program) and you should be safe. 63 | 64 | """ 65 | 66 | from __future__ import nested_scopes 67 | import sys, os, tempfile, random, string, re, types 68 | 69 | 70 | # Python < 2.3 compatibility 71 | if sys.hexversion < 0x02030000: 72 | # The assignments would work with Python >= 2.3 but then, pydoc 73 | # shows them in the DATA section of the module... 74 | True = 0 == 0 75 | False = 0 == 1 76 | 77 | 78 | # Exceptions raised by this module 79 | # 80 | # When adding, suppressing, renaming exceptions or changing their 81 | # hierarchy, don't forget to update the module's docstring. 82 | class error(Exception): 83 | """Base class for exceptions in pythondialog.""" 84 | def __init__(self, message=None): 85 | self.message = message 86 | def __str__(self): 87 | return "<%s: %s>" % (self.__class__.__name__, self.message) 88 | def complete_message(self): 89 | if self.message: 90 | return "%s: %s" % (self.ExceptionShortDescription, self.message) 91 | else: 92 | return "%s" % self.ExceptionShortDescription 93 | ExceptionShortDescription = "pythondialog generic exception" 94 | 95 | # For backward-compatibility 96 | # 97 | # Note: this exception was not documented (only the specific ones were), so 98 | # the backward-compatibility binding could be removed relatively easily. 99 | PythonDialogException = error 100 | 101 | class ExecutableNotFound(error): 102 | """Exception raised when the dialog executable can't be found.""" 103 | ExceptionShortDescription = "Executable not found" 104 | 105 | class PythonDialogBug(error): 106 | """Exception raised when pythondialog finds a bug in his own code.""" 107 | ExceptionShortDescription = "Bug in pythondialog" 108 | 109 | # Yeah, the "Probably" makes it look a bit ugly, but: 110 | # - this is more accurate 111 | # - this avoids a potential clash with an eventual PythonBug built-in 112 | # exception in the Python interpreter... 113 | class ProbablyPythonBug(error): 114 | """Exception raised when pythondialog behaves in a way that seems to \ 115 | indicate a Python bug.""" 116 | ExceptionShortDescription = "Bug in python, probably" 117 | 118 | class BadPythonDialogUsage(error): 119 | """Exception raised when pythondialog is used in an incorrect way.""" 120 | ExceptionShortDescription = "Invalid use of pythondialog" 121 | 122 | class PythonDialogSystemError(error): 123 | """Exception raised when pythondialog cannot perform a "system \ 124 | operation" (e.g., a system call) that should work in "normal" situations. 125 | 126 | This is a convenience exception: PythonDialogIOError, PythonDialogOSError 127 | and PythonDialogErrorBeforeExecInChildProcess all derive from this 128 | exception. As a consequence, watching for PythonDialogSystemError instead 129 | of the aformentioned exceptions is enough if you don't need precise 130 | details about these kinds of errors. 131 | 132 | Don't confuse this exception with Python's builtin SystemError 133 | exception. 134 | 135 | """ 136 | ExceptionShortDescription = "System error" 137 | 138 | class PythonDialogIOError(PythonDialogSystemError): 139 | """Exception raised when pythondialog catches an IOError exception that \ 140 | should be passed to the calling program.""" 141 | ExceptionShortDescription = "IO error" 142 | 143 | class PythonDialogOSError(PythonDialogSystemError): 144 | """Exception raised when pythondialog catches an OSError exception that \ 145 | should be passed to the calling program.""" 146 | ExceptionShortDescription = "OS error" 147 | 148 | class PythonDialogErrorBeforeExecInChildProcess(PythonDialogSystemError): 149 | """Exception raised when an exception is caught in a child process \ 150 | before the exec sytem call (included). 151 | 152 | This can happen in uncomfortable situations like when the system is out 153 | of memory or when the maximum number of open file descriptors has been 154 | reached. This can also happen if the dialog-like program was removed 155 | (or if it is has been made non-executable) between the time we found it 156 | with _find_in_path and the time the exec system call attempted to 157 | execute it... 158 | 159 | """ 160 | ExceptionShortDescription = "Error in a child process before the exec " \ 161 | "system call" 162 | 163 | class PythonDialogReModuleError(PythonDialogSystemError): 164 | """Exception raised when pythondialog catches a re.error exception.""" 165 | ExceptionShortDescription = "'re' module error" 166 | 167 | class UnexpectedDialogOutput(error): 168 | """Exception raised when the dialog-like program returns something not \ 169 | expected by pythondialog.""" 170 | ExceptionShortDescription = "Unexpected dialog output" 171 | 172 | class DialogTerminatedBySignal(error): 173 | """Exception raised when the dialog-like program is terminated by a \ 174 | signal.""" 175 | ExceptionShortDescription = "dialog-like terminated by a signal" 176 | 177 | class DialogError(error): 178 | """Exception raised when the dialog-like program exits with the \ 179 | code indicating an error.""" 180 | ExceptionShortDescription = "dialog-like terminated due to an error" 181 | 182 | class UnableToCreateTemporaryDirectory(error): 183 | """Exception raised when we cannot create a temporary directory.""" 184 | ExceptionShortDescription = "unable to create a temporary directory" 185 | 186 | # Values accepted for checklists 187 | try: 188 | _on_rec = re.compile(r"on", re.IGNORECASE) 189 | _off_rec = re.compile(r"off", re.IGNORECASE) 190 | 191 | _calendar_date_rec = re.compile( 192 | r"(?P\d\d)/(?P\d\d)/(?P\d\d\d\d)$") 193 | _timebox_time_rec = re.compile( 194 | r"(?P\d\d):(?P\d\d):(?P\d\d)$") 195 | except re.error, v: 196 | raise PythonDialogReModuleError(v) 197 | 198 | 199 | # This dictionary allows us to write the dialog common options in a Pythonic 200 | # way (e.g. dialog_instance.checklist(args, ..., title="Foo", no_shadow=1)). 201 | # 202 | # Options such as --separate-output should obviously not be set by the user 203 | # since they affect the parsing of dialog's output: 204 | _common_args_syntax = { 205 | "aspect": lambda ratio: ("--aspect", str(ratio)), 206 | "backtitle": lambda backtitle: ("--backtitle", backtitle), 207 | "beep": lambda enable: _simple_option("--beep", enable), 208 | "beep_after": lambda enable: _simple_option("--beep-after", enable), 209 | # Warning: order = y, x! 210 | "begin": lambda coords: ("--begin", str(coords[0]), str(coords[1])), 211 | "cancel": lambda string: ("--cancel-label", string), 212 | "cancel_label": lambda string: ("--cancel-label", string), 213 | "clear": lambda enable: _simple_option("--clear", enable), 214 | "colors": lambda enable: _simple_option("--colors", enable), 215 | "cr_wrap": lambda enable: _simple_option("--cr-wrap", enable), 216 | "create_rc": lambda file: ("--create-rc", file), 217 | "defaultno": lambda enable: _simple_option("--defaultno", enable), 218 | "default_item": lambda string: ("--default-item", string), 219 | "help": lambda enable: _simple_option("--help", enable), 220 | "help_button": lambda enable: _simple_option("--help-button", enable), 221 | "help_label": lambda string: ("--help-label", string), 222 | "insecure": lambda enable: _simple_option("--insecure", enable), 223 | "ignore": lambda enable: _simple_option("--ignore", enable), 224 | "item_help": lambda enable: _simple_option("--item-help", enable), 225 | "max_input": lambda size: ("--max-input", str(size)), 226 | "no_kill": lambda enable: _simple_option("--no-kill", enable), 227 | "no_cancel": lambda enable: _simple_option("--no-cancel", enable), 228 | "nocancel": lambda enable: _simple_option("--nocancel", enable), 229 | "no_shadow": lambda enable: _simple_option("--no-shadow", enable), 230 | "no_label": lambda string: ("--no-label", string), 231 | "ok_label": lambda string: ("--ok-label", string), 232 | "print_maxsize": lambda enable: _simple_option("--print-maxsize", 233 | enable), 234 | "print_size": lambda enable: _simple_option("--print-size", enable), 235 | "print_version": lambda enable: _simple_option("--print-version", 236 | enable), 237 | "separate_output": lambda enable: _simple_option("--separate-output", 238 | enable), 239 | "separate_widget": lambda string: ("--separate-widget", string), 240 | "shadow": lambda enable: _simple_option("--shadow", enable), 241 | "size_err": lambda enable: _simple_option("--size-err", enable), 242 | "sleep": lambda secs: ("--sleep", str(secs)), 243 | "stderr": lambda enable: _simple_option("--stderr", enable), 244 | "stdout": lambda enable: _simple_option("--stdout", enable), 245 | "tab_correct": lambda enable: _simple_option("--tab-correct", enable), 246 | "tab_len": lambda n: ("--tab-len", str(n)), 247 | "timeout": lambda secs: ("--timeout", str(secs)), 248 | "title": lambda title: ("--title", title), 249 | "trim": lambda enable: _simple_option("--trim", enable), 250 | "version": lambda enable: _simple_option("--version", enable), 251 | "yes_label": lambda string: ("--yes-label", string)} 252 | 253 | 254 | def _simple_option(option, enable): 255 | """Turn on or off the simplest dialog Common Options.""" 256 | if enable: 257 | return (option,) 258 | else: 259 | # This will not add any argument to the command line 260 | return () 261 | 262 | 263 | def _find_in_path(prog_name): 264 | """Search an executable in the PATH. 265 | 266 | If PATH is not defined, the default path ":/bin:/usr/bin" is 267 | used. 268 | 269 | Return a path to the file or None if no readable and executable 270 | file is found. 271 | 272 | Notable exception: PythonDialogOSError 273 | 274 | """ 275 | try: 276 | # Note that the leading empty component in the default value for PATH 277 | # could lead to the returned path not being absolute. 278 | PATH = os.getenv("PATH", ":/bin:/usr/bin") # see the execvp(3) man page 279 | for dir in string.split(PATH, ":"): 280 | file_path = os.path.join(dir, prog_name) 281 | if os.path.isfile(file_path) \ 282 | and os.access(file_path, os.R_OK | os.X_OK): 283 | return file_path 284 | return None 285 | except os.error, v: 286 | raise PythonDialogOSError(v.strerror) 287 | 288 | 289 | def _path_to_executable(f): 290 | """Find a path to an executable. 291 | 292 | Find a path to an executable, using the same rules as the POSIX 293 | exec*p functions (see execvp(3) for instance). 294 | 295 | If `f' contains a '/', it is assumed to be a path and is simply 296 | checked for read and write permissions; otherwise, it is looked 297 | for according to the contents of the PATH environment variable, 298 | which defaults to ":/bin:/usr/bin" if unset. 299 | 300 | The returned path is not necessarily absolute. 301 | 302 | Notable exceptions: 303 | 304 | ExecutableNotFound 305 | PythonDialogOSError 306 | 307 | """ 308 | try: 309 | if '/' in f: 310 | if os.path.isfile(f) and \ 311 | os.access(f, os.R_OK | os.X_OK): 312 | res = f 313 | else: 314 | raise ExecutableNotFound("%s cannot be read and executed" % f) 315 | else: 316 | res = _find_in_path(f) 317 | if res is None: 318 | raise ExecutableNotFound( 319 | "can't find the executable for the dialog-like " 320 | "program") 321 | except os.error, v: 322 | raise PythonDialogOSError(v.strerror) 323 | 324 | return res 325 | 326 | 327 | def _to_onoff(val): 328 | """Convert boolean expressions to "on" or "off" 329 | 330 | This function converts every non-zero integer as well as "on", 331 | "ON", "On" and "oN" to "on" and converts 0, "off", "OFF", etc. to 332 | "off". 333 | 334 | Notable exceptions: 335 | 336 | PythonDialogReModuleError 337 | BadPythonDialogUsage 338 | 339 | """ 340 | if type(val) == types.IntType: 341 | if val: 342 | return "on" 343 | else: 344 | return "off" 345 | elif type(val) == types.StringType: 346 | try: 347 | if _on_rec.match(val): 348 | return "on" 349 | elif _off_rec.match(val): 350 | return "off" 351 | except re.error, v: 352 | raise PythonDialogReModuleError(v) 353 | else: 354 | raise BadPythonDialogUsage("invalid boolean value: %s" % val) 355 | 356 | 357 | def _compute_common_args(dict): 358 | """Compute the list of arguments for dialog common options. 359 | 360 | Compute a list of the command-line arguments to pass to dialog 361 | from a keyword arguments dictionary for options listed as "common 362 | options" in the manual page for dialog. These are the options 363 | that are not tied to a particular widget. 364 | 365 | This allows to specify these options in a pythonic way, such as: 366 | 367 | d.checklist(, 368 | title="...", 369 | backtitle="...") 370 | 371 | instead of having to pass them with strings like "--title foo" or 372 | "--backtitle bar". 373 | 374 | Notable exceptions: None 375 | 376 | """ 377 | args = [] 378 | for key in dict.keys(): 379 | args.extend(_common_args_syntax[key](dict[key])) 380 | return args 381 | 382 | 383 | def _create_temporary_directory(): 384 | """Create a temporary directory (securely). 385 | 386 | Return the directory path. 387 | 388 | Notable exceptions: 389 | - UnableToCreateTemporaryDirectory 390 | - PythonDialogOSError 391 | - exceptions raised by the tempfile module (which are 392 | unfortunately not mentioned in its documentation, at 393 | least in Python 2.3.3...) 394 | 395 | """ 396 | find_temporary_nb_attempts = 5 397 | for i in range(find_temporary_nb_attempts): 398 | try: 399 | # Using something >= 2**31 causes an error in Python 2.2... 400 | tmp_dir = os.path.join(tempfile.gettempdir(), 401 | "%s-%u" \ 402 | % ("pythondialog", 403 | random.randint(0, 2**30-1))) 404 | except os.error, v: 405 | raise PythonDialogOSError(v.strerror) 406 | 407 | try: 408 | os.mkdir(tmp_dir, 0700) 409 | except os.error: 410 | continue 411 | else: 412 | break 413 | else: 414 | raise UnableToCreateTemporaryDirectory( 415 | "somebody may be trying to attack us") 416 | 417 | return tmp_dir 418 | 419 | 420 | # DIALOG_OK, DIALOG_CANCEL, etc. are environment variables controlling 421 | # dialog's exit status in the corresponding situation. 422 | # 423 | # Note: 424 | # - 127 must not be used for any of the DIALOG_* values. It is used 425 | # when a failure occurs in the child process before it exec()s 426 | # dialog (where "before" includes a potential exec() failure). 427 | # - 126 is also used (although in presumably rare situations). 428 | _dialog_exit_status_vars = { "OK": 0, 429 | "CANCEL": 1, 430 | "ESC": 2, 431 | "ERROR": 3, 432 | "EXTRA": 4, 433 | "HELP": 5 } 434 | 435 | 436 | # Main class of the module 437 | class Dialog: 438 | 439 | """Class providing bindings for dialog-compatible programs. 440 | 441 | This class allows you to invoke dialog or a compatible program in 442 | a pythonic way to build quicky and easily simple but nice text 443 | interfaces. 444 | 445 | An application typically creates one instance of the Dialog class 446 | and uses it for all its widgets, but it is possible to use 447 | concurrently several instances of this class with different 448 | parameters (such as the background title) if you have the need 449 | for this. 450 | 451 | The exit code (exit status) returned by dialog is to be 452 | compared with the DIALOG_OK, DIALOG_CANCEL, DIALOG_ESC, 453 | DIALOG_ERROR, DIALOG_EXTRA and DIALOG_HELP attributes of the 454 | Dialog instance (they are integers). 455 | 456 | Note: although this class does all it can to allow the caller to 457 | differentiate between the various reasons that caused a 458 | dialog box to be closed, its backend, dialog 0.9a-20020309a 459 | for my tests, doesn't always return DIALOG_ESC when the 460 | user presses the ESC key, but often returns DIALOG_ERROR 461 | instead. The exit codes returned by the corresponding 462 | Dialog methods are of course just as wrong in these cases. 463 | You've been warned. 464 | 465 | 466 | Public methods of the Dialog class (mainly widgets) 467 | --------------------------------------------------- 468 | 469 | The Dialog class has the following methods: 470 | 471 | add_persistent_args 472 | calendar 473 | checklist 474 | form 475 | fselect 476 | 477 | gauge_start 478 | gauge_update 479 | gauge_stop 480 | 481 | infobox 482 | inputbox 483 | menu 484 | msgbox 485 | passwordbox 486 | passwordform 487 | radiolist 488 | scrollbox 489 | tailbox 490 | textbox 491 | timebox 492 | yesno 493 | 494 | clear (obsolete) 495 | setBackgroundTitle (obsolete) 496 | 497 | 498 | Passing dialog "Common Options" 499 | ------------------------------- 500 | 501 | Every widget method has a **kwargs argument allowing you to pass 502 | dialog so-called Common Options (see the dialog(1) manual page) 503 | to dialog for this widget call. For instance, if `d' is a Dialog 504 | instance, you can write: 505 | 506 | d.checklist(args, ..., title="A Great Title", no_shadow=1) 507 | 508 | The no_shadow option is worth looking at: 509 | 510 | 1. It is an option that takes no argument as far as dialog is 511 | concerned (unlike the "--title" option, for instance). When 512 | you list it as a keyword argument, the option is really 513 | passed to dialog only if the value you gave it evaluates to 514 | true, e.g. "no_shadow=1" will cause "--no-shadow" to be 515 | passed to dialog whereas "no_shadow=0" will cause this 516 | option not to be passed to dialog at all. 517 | 518 | 2. It is an option that has a hyphen (-) in its name, which you 519 | must change into an underscore (_) to pass it as a Python 520 | keyword argument. Therefore, "--no-shadow" is passed by 521 | giving a "no_shadow=1" keyword argument to a Dialog method 522 | (the leading two dashes are also consistently removed). 523 | 524 | 525 | Exceptions 526 | ---------- 527 | 528 | Please refer to the specific methods' docstrings or simply to the 529 | module's docstring for a list of all exceptions that might be 530 | raised by this class' methods. 531 | 532 | """ 533 | 534 | def __init__(self, dialog="dialog", DIALOGRC=None, compat="dialog", 535 | use_stdout=None): 536 | """Constructor for Dialog instances. 537 | 538 | dialog -- name of (or path to) the dialog-like program to 539 | use; if it contains a '/', it is assumed to be a 540 | path and is used as is; otherwise, it is looked 541 | for according to the contents of the PATH 542 | environment variable, which defaults to 543 | ":/bin:/usr/bin" if unset. 544 | DIALOGRC -- string to pass to the dialog-like program as the 545 | DIALOGRC environment variable, or None if no 546 | modification to the environment regarding this 547 | variable should be done in the call to the 548 | dialog-like program 549 | compat -- compatibility mode (see below) 550 | 551 | The officially supported dialog-like program in pythondialog 552 | is the well-known dialog program written in C, based on the 553 | ncurses library. It is also known as cdialog and its home 554 | page is currently (2004-03-15) located at: 555 | 556 | http://dickey.his.com/dialog/dialog.html 557 | 558 | If you want to use a different program such as Xdialog, you 559 | should indicate the executable file name with the `dialog' 560 | argument *and* the compatibility type that you think it 561 | conforms to with the `compat' argument. Currently, `compat' 562 | can be either "dialog" (for dialog; this is the default) or 563 | "Xdialog" (for, well, Xdialog). 564 | 565 | The `compat' argument allows me to cope with minor 566 | differences in behaviour between the various programs 567 | implementing the dialog interface (not the text or graphical 568 | interface, I mean the "API"). However, having to support 569 | various APIs simultaneously is a bit ugly and I would really 570 | prefer you to report bugs to the relevant maintainers when 571 | you find incompatibilities with dialog. This is for the 572 | benefit of pretty much everyone that relies on the dialog 573 | interface. 574 | 575 | Notable exceptions: 576 | 577 | ExecutableNotFound 578 | PythonDialogOSError 579 | 580 | """ 581 | # DIALOGRC differs from the other DIALOG* variables in that: 582 | # 1. It should be a string if not None 583 | # 2. We may very well want it to be unset 584 | if DIALOGRC is not None: 585 | self.DIALOGRC = DIALOGRC 586 | 587 | # After reflexion, I think DIALOG_OK, DIALOG_CANCEL, etc. 588 | # should never have been instance attributes (I cannot see a 589 | # reason why the user would want to change their values or 590 | # even read them), but it is a bit late, now. So, we set them 591 | # based on the (global) _dialog_exit_status_vars.keys. 592 | for var in _dialog_exit_status_vars.keys(): 593 | varname = "DIALOG_" + var 594 | setattr(self, varname, _dialog_exit_status_vars[var]) 595 | 596 | self._dialog_prg = _path_to_executable(dialog) 597 | self.compat = compat 598 | self.dialog_persistent_arglist = [] 599 | 600 | # Use stderr or stdout? 601 | if self.compat == "Xdialog": 602 | # Default to stdout if Xdialog 603 | self.use_stdout = True 604 | else: 605 | self.use_stdout = False 606 | if use_stdout != None: 607 | # Allow explicit setting 608 | self.use_stdout = use_stdout 609 | if self.use_stdout: 610 | self.add_persistent_args(["--stdout"]) 611 | 612 | def add_persistent_args(self, arglist): 613 | self.dialog_persistent_arglist.extend(arglist) 614 | 615 | # For compatibility with the old dialog... 616 | def setBackgroundTitle(self, text): 617 | """Set the background title for dialog. 618 | 619 | This method is obsolete. Please remove calls to it from your 620 | programs. 621 | 622 | """ 623 | self.add_persistent_args(("--backtitle", text)) 624 | 625 | def _call_program(self, redirect_child_stdin, cmdargs, **kwargs): 626 | """Do the actual work of invoking the dialog-like program. 627 | 628 | Communication with the dialog-like program is performed 629 | through one or two pipes, depending on 630 | `redirect_child_stdin'. There is always one pipe that is 631 | created to allow the parent process to read what dialog 632 | writes on its standard error stream. 633 | 634 | If `redirect_child_stdin' is True, an additional pipe is 635 | created whose reading end is connected to dialog's standard 636 | input. This is used by the gauge widget to feed data to 637 | dialog. 638 | 639 | Beware when interpreting the return value: the length of the 640 | returned tuple depends on `redirect_child_stdin'. 641 | 642 | Notable exception: PythonDialogOSError (if pipe() or close() 643 | system calls fail...) 644 | 645 | """ 646 | # We want to define DIALOG_OK, DIALOG_CANCEL, etc. in the 647 | # environment of the child process so that we know (and 648 | # even control) the possible dialog exit statuses. 649 | new_environ = {} 650 | new_environ.update(os.environ) 651 | for var in _dialog_exit_status_vars: 652 | varname = "DIALOG_" + var 653 | new_environ[varname] = str(getattr(self, varname)) 654 | if hasattr(self, "DIALOGRC"): 655 | new_environ["DIALOGRC"] = self.DIALOGRC 656 | 657 | # Create: 658 | # - a pipe so that the parent process can read dialog's output on 659 | # stdout/stderr 660 | # - a pipe so that the parent process can feed data to dialog's 661 | # stdin (this is needed for the gauge widget) if 662 | # redirect_child_stdin is True 663 | try: 664 | # rfd = File Descriptor for Reading 665 | # wfd = File Descriptor for Writing 666 | (child_rfd, child_wfd) = os.pipe() 667 | if redirect_child_stdin: 668 | (child_stdin_rfd, child_stdin_wfd) = os.pipe() 669 | except os.error, v: 670 | raise PythonDialogOSError(v.strerror) 671 | 672 | child_pid = os.fork() 673 | if child_pid == 0: 674 | # We are in the child process. We MUST NOT raise any exception. 675 | try: 676 | # The child process doesn't need these file descriptors 677 | os.close(child_rfd) 678 | if redirect_child_stdin: 679 | os.close(child_stdin_wfd) 680 | # We want: 681 | # - dialog's output on stderr/stdout to go to child_wfd 682 | # - data written to child_stdin_wfd to go to dialog's stdin 683 | # if redirect_child_stdin is True 684 | if self.use_stdout: 685 | os.dup2(child_wfd, 1) 686 | else: 687 | os.dup2(child_wfd, 2) 688 | if redirect_child_stdin: 689 | os.dup2(child_stdin_rfd, 0) 690 | 691 | arglist = [self._dialog_prg] + \ 692 | self.dialog_persistent_arglist + \ 693 | _compute_common_args(kwargs) + \ 694 | cmdargs 695 | # Insert here the contents of the DEBUGGING file if you want 696 | # to obtain a handy string of the complete command line with 697 | # arguments quoted for the shell and environment variables 698 | # set. 699 | os.execve(self._dialog_prg, arglist, new_environ) 700 | except: 701 | os._exit(127) 702 | 703 | # Should not happen unless there is a bug in Python 704 | os._exit(126) 705 | 706 | # We are in the father process. 707 | # 708 | # It is essential to close child_wfd, otherwise we will never 709 | # see EOF while reading on child_rfd and the parent process 710 | # will block forever on the read() call. 711 | # [ after the fork(), the "reference count" of child_wfd from 712 | # the operating system's point of view is 2; after the child exits, 713 | # it is 1 until the father closes it itself; then it is 0 and a read 714 | # on child_rfd encounters EOF once all the remaining data in 715 | # the pipe has been read. ] 716 | try: 717 | os.close(child_wfd) 718 | if redirect_child_stdin: 719 | os.close(child_stdin_rfd) 720 | return (child_pid, child_rfd, child_stdin_wfd) 721 | else: 722 | return (child_pid, child_rfd) 723 | except os.error, v: 724 | raise PythonDialogOSError(v.strerror) 725 | 726 | def _wait_for_program_termination(self, child_pid, child_rfd): 727 | """Wait for a dialog-like process to terminate. 728 | 729 | This function waits for the specified process to terminate, 730 | raises the appropriate exceptions in case of abnormal 731 | termination and returns the exit status and standard error 732 | output of the process as a tuple: (exit_code, stderr_string). 733 | 734 | `child_rfd' must be the file descriptor for the 735 | reading end of the pipe created by self._call_program() 736 | whose writing end was connected by self._call_program() to 737 | the child process's standard error. 738 | 739 | This function reads the process's output on standard error 740 | from `child_rfd' and closes this file descriptor once 741 | this is done. 742 | 743 | Notable exceptions: 744 | 745 | DialogTerminatedBySignal 746 | DialogError 747 | PythonDialogErrorBeforeExecInChildProcess 748 | PythonDialogIOError 749 | PythonDialogBug 750 | ProbablyPythonBug 751 | 752 | """ 753 | exit_info = os.waitpid(child_pid, 0)[1] 754 | if os.WIFEXITED(exit_info): 755 | exit_code = os.WEXITSTATUS(exit_info) 756 | # As we wait()ed for the child process to terminate, there is no 757 | # need to call os.WIFSTOPPED() 758 | elif os.WIFSIGNALED(exit_info): 759 | raise DialogTerminatedBySignal("the dialog-like program was " 760 | "terminated by signal %u" % 761 | os.WTERMSIG(exit_info)) 762 | else: 763 | raise PythonDialogBug("please report this bug to the " 764 | "pythondialog maintainers") 765 | 766 | if exit_code == self.DIALOG_ERROR: 767 | raise DialogError("the dialog-like program exited with " 768 | "code %d (was passed to it as the DIALOG_ERROR " 769 | "environment variable)" % exit_code) 770 | elif exit_code == 127: 771 | raise PythonDialogErrorBeforeExecInChildProcess( 772 | "perhaps the dialog-like program could not be executed; " 773 | "perhaps the system is out of memory; perhaps the maximum " 774 | "number of open file descriptors has been reached") 775 | elif exit_code == 126: 776 | raise ProbablyPythonBug( 777 | "a child process returned with exit status 126; this might " 778 | "be the exit status of the dialog-like program, for some " 779 | "unknown reason (-> probably a bug in the dialog-like " 780 | "program); otherwise, we have probably found a python bug") 781 | 782 | # We might want to check here whether exit_code is really one of 783 | # DIALOG_OK, DIALOG_CANCEL, etc. However, I prefer not doing it 784 | # because it would break pythondialog for no strong reason when new 785 | # exit codes are added to the dialog-like program. 786 | # 787 | # As it is now, if such a thing happens, the program using 788 | # pythondialog may receive an exit_code it doesn't know about. OK, the 789 | # programmer just has to tell the pythondialog maintainer about it and 790 | # can temporarily set the appropriate DIALOG_* environment variable if 791 | # he wants and assign the corresponding value to the Dialog instance's 792 | # DIALOG_FOO attribute from his program. He doesn't even need to use a 793 | # patched pythondialog before he upgrades to a version that knows 794 | # about the new exit codes. 795 | # 796 | # The bad thing that might happen is a new DIALOG_FOO exit code being 797 | # the same by default as one of those we chose for the other exit 798 | # codes already known by pythondialog. But in this situation, the 799 | # check that is being discussed wouldn't help at all. 800 | 801 | # Read dialog's output on its stderr 802 | try: 803 | child_output = os.fdopen(child_rfd, "rb").read() 804 | # Now, since the file object has no reference anymore, the 805 | # standard IO stream behind it will be closed, causing the 806 | # end of the the pipe we used to read dialog's output on its 807 | # stderr to be closed (this is important, otherwise invoking 808 | # dialog enough times will eventually exhaust the maximum number 809 | # of open file descriptors). 810 | except IOError, v: 811 | raise PythonDialogIOError(v) 812 | 813 | return (exit_code, child_output) 814 | 815 | def _perform(self, cmdargs, **kwargs): 816 | """Perform a complete dialog-like program invocation. 817 | 818 | This function invokes the dialog-like program, waits for its 819 | termination and returns its exit status and whatever it wrote 820 | on its standard error stream. 821 | 822 | Notable exceptions: 823 | 824 | any exception raised by self._call_program() or 825 | self._wait_for_program_termination() 826 | 827 | """ 828 | (child_pid, child_rfd) = \ 829 | self._call_program(False, *(cmdargs,), **kwargs) 830 | (exit_code, output) = \ 831 | self._wait_for_program_termination(child_pid, 832 | child_rfd) 833 | return (exit_code, output) 834 | 835 | def _strip_xdialog_newline(self, output): 836 | """Remove trailing newline (if any), if using Xdialog""" 837 | if self.compat == "Xdialog" and output.endswith("\n"): 838 | output = output[:-1] 839 | return output 840 | 841 | # This is for compatibility with the old dialog.py 842 | def _perform_no_options(self, cmd): 843 | """Call dialog without passing any more options.""" 844 | return os.system(self._dialog_prg + ' ' + cmd) 845 | 846 | # For compatibility with the old dialog.py 847 | def clear(self): 848 | """Clear the screen. Equivalent to the dialog --clear option. 849 | 850 | This method is obsolete. Please remove calls to it from your 851 | programs. 852 | 853 | """ 854 | self._perform_no_options('--clear') 855 | 856 | 857 | def form(self, text, height=20, width=50, form_height=20, fields=[], **kwargs): 858 | """Display a form dialog box. 859 | 860 | text -- text to display in the box 861 | height -- height of the box (minus the calendar height) 862 | width -- width of the box 863 | form_height -- height of the form 864 | fields -- a list of tuples, each tuple has the form: 865 | (label, item, len) or 866 | (label, item, field_len, input_len) 867 | 868 | it returns a tuple of the form (code, results), where results is a 869 | list of the results. 870 | 871 | Notable exceptions: 872 | - any exception raised by self._perform() 873 | - UnexpectedDialogOutput 874 | - PythonDialogReModuleError 875 | 876 | """ 877 | cmd = ["--form", text, str(height), str(width), str(form_height)] 878 | 879 | # find the longest label so we can put the input boxes at the 880 | # correct offset 881 | max_label_len = 0 882 | for t in fields: 883 | if len(t[0]) > max_label_len: 884 | max_label_len = len(t[0]); 885 | 886 | line = 1 887 | for t in fields: 888 | label = t[0] 889 | item = t[1] 890 | if item is None: 891 | item = "" 892 | field_len = str(t[2]) 893 | if len(t) < 4: 894 | input_len = field_len 895 | else: 896 | input_len = str(t[3]) 897 | cmd.extend(((label, str(line), "1", item, str(line), str(max_label_len + 2), field_len, input_len))) 898 | line += 1 899 | 900 | (code, output) = self._perform(*(cmd,), **kwargs) 901 | 902 | if output: 903 | return (code, string.split(output, '\n')[:-1]) 904 | else: # empty selection 905 | return (code, []) 906 | 907 | 908 | def passwordform(self, text, height=20, width=50, form_height=20, fields=[], **kwargs): 909 | """Display a form dialog box. 910 | 911 | text -- text to display in the box 912 | height -- height of the box (minus the calendar height) 913 | width -- width of the box 914 | form_height -- height of the form 915 | fields -- a list of tuples, each tuple has the form: 916 | (label, item, len) or 917 | (label, item, field_len, input_len) 918 | 919 | it returns a tuple of the form (code, results), where results is a 920 | list of the results. 921 | 922 | Notable exceptions: 923 | - any exception raised by self._perform() 924 | - UnexpectedDialogOutput 925 | - PythonDialogReModuleError 926 | 927 | """ 928 | cmd = ["--passwordform", text, str(height), str(width), str(form_height)] 929 | 930 | # find the longest label so we can put the input boxes at the 931 | # correct offset 932 | max_label_len = 0 933 | for t in fields: 934 | if len(t[0]) > max_label_len: 935 | max_label_len = len(t[0]); 936 | 937 | line = 1 938 | for t in fields: 939 | label = t[0] 940 | item = t[1] 941 | field_len = str(t[2]) 942 | if len(t) < 4: 943 | input_len = field_len 944 | else: 945 | input_len = str(t[3]) 946 | cmd.extend(((label, str(line), "1", item, str(line), str(max_label_len + 2), field_len, input_len))) 947 | line += 1 948 | 949 | # typing in password without stars is really awkward 950 | kwargs["insecure"] = True 951 | 952 | (code, output) = self._perform(*(cmd,), **kwargs) 953 | 954 | if output: 955 | return (code, string.split(output, '\n')[:-1]) 956 | else: # empty selection 957 | return (code, []) 958 | 959 | 960 | def calendar(self, text, height=6, width=0, day=0, month=0, year=0, 961 | **kwargs): 962 | """Display a calendar dialog box. 963 | 964 | text -- text to display in the box 965 | height -- height of the box (minus the calendar height) 966 | width -- width of the box 967 | day -- inititial day highlighted 968 | month -- inititial month displayed 969 | year -- inititial year selected (0 causes the current date 970 | to be used as the initial date) 971 | 972 | A calendar box displays month, day and year in separately 973 | adjustable windows. If the values for day, month or year are 974 | missing or negative, the current date's corresponding values 975 | are used. You can increment or decrement any of those using 976 | the left, up, right and down arrows. Use tab or backtab to 977 | move between windows. If the year is given as zero, the 978 | current date is used as an initial value. 979 | 980 | Return a tuple of the form (code, date) where `code' is the 981 | exit status (an integer) of the dialog-like program and 982 | `date' is a list of the form [day, month, year] (where `day', 983 | `month' and `year' are integers corresponding to the date 984 | chosen by the user) if the box was closed with OK, or None if 985 | it was closed with the Cancel button. 986 | 987 | Notable exceptions: 988 | - any exception raised by self._perform() 989 | - UnexpectedDialogOutput 990 | - PythonDialogReModuleError 991 | 992 | """ 993 | (code, output) = self._perform( 994 | *(["--calendar", text, str(height), str(width), str(day), 995 | str(month), str(year)],), 996 | **kwargs) 997 | if code == self.DIALOG_OK: 998 | try: 999 | mo = _calendar_date_rec.match(output) 1000 | except re.error, v: 1001 | raise PythonDialogReModuleError(v) 1002 | 1003 | if mo is None: 1004 | raise UnexpectedDialogOutput( 1005 | "the dialog-like program returned the following " 1006 | "unexpected date with the calendar box: %s" % output) 1007 | date = map(int, mo.group("day", "month", "year")) 1008 | else: 1009 | date = None 1010 | return (code, date) 1011 | 1012 | def checklist(self, text, height=15, width=54, list_height=7, 1013 | choices=[], **kwargs): 1014 | """Display a checklist box. 1015 | 1016 | text -- text to display in the box 1017 | height -- height of the box 1018 | width -- width of the box 1019 | list_height -- number of entries displayed in the box (which 1020 | can be scrolled) at a given time 1021 | choices -- a list of tuples (tag, item, status) where 1022 | `status' specifies the initial on/off state of 1023 | each entry; can be 0 or 1 (integers, 1 meaning 1024 | checked, i.e. "on"), or "on", "off" or any 1025 | uppercase variant of these two strings. 1026 | 1027 | Return a tuple of the form (code, [tag, ...]) with the tags 1028 | for the entries that were selected by the user. `code' is the 1029 | exit status of the dialog-like program. 1030 | 1031 | If the user exits with ESC or CANCEL, the returned tag list 1032 | is empty. 1033 | 1034 | Notable exceptions: 1035 | 1036 | any exception raised by self._perform() or _to_onoff() 1037 | 1038 | """ 1039 | cmd = ["--checklist", text, str(height), str(width), str(list_height)] 1040 | for t in choices: 1041 | cmd.extend(((t[0], t[1], _to_onoff(t[2])))) 1042 | 1043 | # The dialog output cannot be parsed reliably (at least in dialog 1044 | # 0.9b-20040301) without --separate-output (because double quotes in 1045 | # tags are escaped with backslashes, but backslashes are not 1046 | # themselves escaped and you have a problem when a tag ends with a 1047 | # backslash--the output makes you think you've encountered an embedded 1048 | # double-quote). 1049 | kwargs["separate_output"] = True 1050 | 1051 | (code, output) = self._perform(*(cmd,), **kwargs) 1052 | 1053 | # Since we used --separate-output, the tags are separated by a newline 1054 | # in the output. There is also a final newline after the last tag. 1055 | if output: 1056 | return (code, string.split(output, '\n')[:-1]) 1057 | else: # empty selection 1058 | return (code, []) 1059 | 1060 | def fselect(self, filepath, height, width, **kwargs): 1061 | """Display a file selection dialog box. 1062 | 1063 | filepath -- initial file path 1064 | height -- height of the box 1065 | width -- width of the box 1066 | 1067 | The file-selection dialog displays a text-entry window in 1068 | which you can type a filename (or directory), and above that 1069 | two windows with directory names and filenames. 1070 | 1071 | Here, filepath can be a file path in which case the file and 1072 | directory windows will display the contents of the path and 1073 | the text-entry window will contain the preselected filename. 1074 | 1075 | Use tab or arrow keys to move between the windows. Within the 1076 | directory or filename windows, use the up/down arrow keys to 1077 | scroll the current selection. Use the space-bar to copy the 1078 | current selection into the text-entry window. 1079 | 1080 | Typing any printable character switches focus to the 1081 | text-entry window, entering that character as well as 1082 | scrolling the directory and filename windows to the closest 1083 | match. 1084 | 1085 | Use a carriage return or the "OK" button to accept the 1086 | current value in the text-entry window, or the "Cancel" 1087 | button to cancel. 1088 | 1089 | Return a tuple of the form (code, path) where `code' is the 1090 | exit status (an integer) of the dialog-like program and 1091 | `path' is the path chosen by the user (whose last element may 1092 | be a directory or a file). 1093 | 1094 | Notable exceptions: 1095 | 1096 | any exception raised by self._perform() 1097 | 1098 | """ 1099 | (code, output) = self._perform( 1100 | *(["--fselect", filepath, str(height), str(width)],), 1101 | **kwargs) 1102 | 1103 | output = self._strip_xdialog_newline(output) 1104 | 1105 | return (code, output) 1106 | 1107 | def gauge_start(self, text="", height=8, width=54, percent=0, **kwargs): 1108 | """Display gauge box. 1109 | 1110 | text -- text to display in the box 1111 | height -- height of the box 1112 | width -- width of the box 1113 | percent -- initial percentage shown in the meter 1114 | 1115 | A gauge box displays a meter along the bottom of the box. The 1116 | meter indicates a percentage. 1117 | 1118 | This function starts the dialog-like program telling it to 1119 | display a gauge box with a text in it and an initial 1120 | percentage in the meter. 1121 | 1122 | Return value: undefined. 1123 | 1124 | 1125 | Gauge typical usage 1126 | ------------------- 1127 | 1128 | Gauge typical usage (assuming that `d' is an instance of the 1129 | Dialog class) looks like this: 1130 | d.gauge_start() 1131 | # do something 1132 | d.gauge_update(10) # 10% of the whole task is done 1133 | # ... 1134 | d.gauge_update(100, "any text here") # work is done 1135 | exit_code = d.gauge_stop() # cleanup actions 1136 | 1137 | 1138 | Notable exceptions: 1139 | - any exception raised by self._call_program() 1140 | - PythonDialogOSError 1141 | 1142 | """ 1143 | (child_pid, child_rfd, child_stdin_wfd) = self._call_program( 1144 | True, 1145 | *(["--gauge", text, str(height), str(width), str(percent)],), 1146 | **kwargs) 1147 | try: 1148 | self._gauge_process = { 1149 | "pid": child_pid, 1150 | "stdin": os.fdopen(child_stdin_wfd, "wb"), 1151 | "child_rfd": child_rfd 1152 | } 1153 | except os.error, v: 1154 | raise PythonDialogOSError(v.strerror) 1155 | 1156 | def gauge_update(self, percent, text="", update_text=0): 1157 | """Update a running gauge box. 1158 | 1159 | percent -- new percentage to show in the gauge meter 1160 | text -- new text to optionally display in the box 1161 | update-text -- boolean indicating whether to update the 1162 | text in the box 1163 | 1164 | This function updates the percentage shown by the meter of a 1165 | running gauge box (meaning `gauge_start' must have been 1166 | called previously). If update_text is true (for instance, 1), 1167 | the text displayed in the box is also updated. 1168 | 1169 | See the `gauge_start' function's documentation for 1170 | information about how to use a gauge. 1171 | 1172 | Return value: undefined. 1173 | 1174 | Notable exception: PythonDialogIOError can be raised if there 1175 | is an I/O error while writing to the pipe 1176 | used to talk to the dialog-like program. 1177 | 1178 | """ 1179 | if update_text: 1180 | gauge_data = "%d\nXXX\n%s\nXXX\n" % (percent, text) 1181 | else: 1182 | gauge_data = "%d\n" % percent 1183 | try: 1184 | self._gauge_process["stdin"].write(gauge_data) 1185 | self._gauge_process["stdin"].flush() 1186 | except IOError, v: 1187 | raise PythonDialogIOError(v) 1188 | 1189 | # For "compatibility" with the old dialog.py... 1190 | gauge_iterate = gauge_update 1191 | 1192 | def gauge_stop(self): 1193 | """Terminate a running gauge. 1194 | 1195 | This function performs the appropriate cleanup actions to 1196 | terminate a running gauge (started with `gauge_start'). 1197 | 1198 | See the `gauge_start' function's documentation for 1199 | information about how to use a gauge. 1200 | 1201 | Return value: undefined. 1202 | 1203 | Notable exceptions: 1204 | - any exception raised by 1205 | self._wait_for_program_termination() 1206 | - PythonDialogIOError can be raised if closing the pipe 1207 | used to talk to the dialog-like program fails. 1208 | 1209 | """ 1210 | p = self._gauge_process 1211 | # Close the pipe that we are using to feed dialog's stdin 1212 | try: 1213 | p["stdin"].close() 1214 | except IOError, v: 1215 | raise PythonDialogIOError(v) 1216 | exit_code = \ 1217 | self._wait_for_program_termination(p["pid"], 1218 | p["child_rfd"])[0] 1219 | return exit_code 1220 | 1221 | def infobox(self, text, height=10, width=30, **kwargs): 1222 | """Display an information dialog box. 1223 | 1224 | text -- text to display in the box 1225 | height -- height of the box 1226 | width -- width of the box 1227 | 1228 | An info box is basically a message box. However, in this 1229 | case, dialog will exit immediately after displaying the 1230 | message to the user. The screen is not cleared when dialog 1231 | exits, so that the message will remain on the screen until 1232 | the calling shell script clears it later. This is useful 1233 | when you want to inform the user that some operations are 1234 | carrying on that may require some time to finish. 1235 | 1236 | Return the exit status (an integer) of the dialog-like 1237 | program. 1238 | 1239 | Notable exceptions: 1240 | 1241 | any exception raised by self._perform() 1242 | 1243 | """ 1244 | return self._perform( 1245 | *(["--infobox", text, str(height), str(width)],), 1246 | **kwargs)[0] 1247 | 1248 | def inputbox(self, text, height=10, width=30, init='', **kwargs): 1249 | """Display an input dialog box. 1250 | 1251 | text -- text to display in the box 1252 | height -- height of the box 1253 | width -- width of the box 1254 | init -- default input string 1255 | 1256 | An input box is useful when you want to ask questions that 1257 | require the user to input a string as the answer. If init is 1258 | supplied it is used to initialize the input string. When 1259 | entering the string, the BACKSPACE key can be used to 1260 | correct typing errors. If the input string is longer than 1261 | can fit in the dialog box, the input field will be scrolled. 1262 | 1263 | Return a tuple of the form (code, string) where `code' is the 1264 | exit status of the dialog-like program and `string' is the 1265 | string entered by the user. 1266 | 1267 | Notable exceptions: 1268 | 1269 | any exception raised by self._perform() 1270 | 1271 | """ 1272 | (code, tag) = self._perform( 1273 | *(["--inputbox", text, str(height), str(width), init],), 1274 | **kwargs) 1275 | 1276 | tag = self._strip_xdialog_newline(tag) 1277 | 1278 | return (code, tag) 1279 | 1280 | def menu(self, text, height=15, width=54, menu_height=7, choices=[], 1281 | **kwargs): 1282 | """Display a menu dialog box. 1283 | 1284 | text -- text to display in the box 1285 | height -- height of the box 1286 | width -- width of the box 1287 | menu_height -- number of entries displayed in the box (which 1288 | can be scrolled) at a given time 1289 | choices -- a sequence of (tag, item) or (tag, item, help) 1290 | tuples (the meaning of each `tag', `item' and 1291 | `help' is explained below) 1292 | 1293 | 1294 | Overview 1295 | -------- 1296 | 1297 | As its name suggests, a menu box is a dialog box that can be 1298 | used to present a list of choices in the form of a menu for 1299 | the user to choose. Choices are displayed in the order given. 1300 | 1301 | Each menu entry consists of a `tag' string and an `item' 1302 | string. The tag gives the entry a name to distinguish it from 1303 | the other entries in the menu. The item is a short 1304 | description of the option that the entry represents. 1305 | 1306 | The user can move between the menu entries by pressing the 1307 | UP/DOWN keys, the first letter of the tag as a hot-key, or 1308 | the number keys 1-9. There are menu-height entries displayed 1309 | in the menu at one time, but the menu will be scrolled if 1310 | there are more entries than that. 1311 | 1312 | 1313 | Providing on-line help facilities 1314 | --------------------------------- 1315 | 1316 | If this function is called with item_help=1 (keyword 1317 | argument), the option --item-help is passed to dialog and the 1318 | tuples contained in `choices' must contain 3 elements each : 1319 | (tag, item, help). The help string for the highlighted item 1320 | is displayed in the bottom line of the screen and updated as 1321 | the user highlights other items. 1322 | 1323 | If item_help=0 or if this keyword argument is not passed to 1324 | this function, the tuples contained in `choices' must contain 1325 | 2 elements each : (tag, item). 1326 | 1327 | If this function is called with help_button=1, it must also 1328 | be called with item_help=1 (this is a limitation of dialog), 1329 | therefore the tuples contained in `choices' must contain 3 1330 | elements each as explained in the previous paragraphs. This 1331 | will cause a Help button to be added to the right of the 1332 | Cancel button (by passing --help-button to dialog). 1333 | 1334 | 1335 | Return value 1336 | ------------ 1337 | 1338 | Return a tuple of the form (exit_info, string). 1339 | 1340 | `exit_info' is either: 1341 | - an integer, being the the exit status of the dialog-like 1342 | program 1343 | - or the string "help", meaning that help_button=1 was 1344 | passed and that the user chose the Help button instead of 1345 | OK or Cancel. 1346 | 1347 | The meaning of `string' depends on the value of exit_info: 1348 | - if `exit_info' is 0, `string' is the tag chosen by the 1349 | user 1350 | - if `exit_info' is "help", `string' is the `help' string 1351 | from the `choices' argument corresponding to the item 1352 | that was highlighted when the user chose the Help button 1353 | - otherwise (the user chose Cancel or pressed Esc, or there 1354 | was a dialog error), the value of `string' is undefined. 1355 | 1356 | Notable exceptions: 1357 | 1358 | any exception raised by self._perform() 1359 | 1360 | """ 1361 | cmd = ["--menu", text, str(height), str(width), str(menu_height)] 1362 | for t in choices: 1363 | cmd.extend(t) 1364 | (code, output) = self._perform(*(cmd,), **kwargs) 1365 | 1366 | output = self._strip_xdialog_newline(output) 1367 | 1368 | if "help_button" in kwargs.keys() and output.startswith("HELP "): 1369 | return ("help", output[5:]) 1370 | else: 1371 | return (code, output) 1372 | 1373 | def msgbox(self, text, height=10, width=30, **kwargs): 1374 | """Display a message dialog box. 1375 | 1376 | text -- text to display in the box 1377 | height -- height of the box 1378 | width -- width of the box 1379 | 1380 | A message box is very similar to a yes/no box. The only 1381 | difference between a message box and a yes/no box is that a 1382 | message box has only a single OK button. You can use this 1383 | dialog box to display any message you like. After reading 1384 | the message, the user can press the ENTER key so that dialog 1385 | will exit and the calling program can continue its 1386 | operation. 1387 | 1388 | Return the exit status (an integer) of the dialog-like 1389 | program. 1390 | 1391 | Notable exceptions: 1392 | 1393 | any exception raised by self._perform() 1394 | 1395 | """ 1396 | return self._perform( 1397 | *(["--msgbox", text, str(height), str(width)],), 1398 | **kwargs)[0] 1399 | 1400 | def passwordbox(self, text, height=10, width=60, init='', **kwargs): 1401 | """Display an password input dialog box. 1402 | 1403 | text -- text to display in the box 1404 | height -- height of the box 1405 | width -- width of the box 1406 | init -- default input password 1407 | 1408 | A password box is similar to an input box, except that the 1409 | text the user enters is not displayed. This is useful when 1410 | prompting for passwords or other sensitive information. Be 1411 | aware that if anything is passed in "init", it will be 1412 | visible in the system's process table to casual snoopers. 1413 | Also, it is very confusing to the user to provide them with a 1414 | default password they cannot see. For these reasons, using 1415 | "init" is highly discouraged. 1416 | 1417 | Return a tuple of the form (code, password) where `code' is 1418 | the exit status of the dialog-like program and `password' is 1419 | the password entered by the user. 1420 | 1421 | Notable exceptions: 1422 | 1423 | any exception raised by self._perform() 1424 | 1425 | """ 1426 | 1427 | # typing in password without stars is really awkward 1428 | kwargs["insecure"] = True 1429 | (code, password) = self._perform( 1430 | *(["--passwordbox", text, str(height), str(width), init],), 1431 | **kwargs) 1432 | 1433 | password = self._strip_xdialog_newline(password) 1434 | 1435 | return (code, password) 1436 | 1437 | def radiolist(self, text, height=15, width=54, list_height=7, 1438 | choices=[], **kwargs): 1439 | """Display a radiolist box. 1440 | 1441 | text -- text to display in the box 1442 | height -- height of the box 1443 | width -- width of the box 1444 | list_height -- number of entries displayed in the box (which 1445 | can be scrolled) at a given time 1446 | choices -- a list of tuples (tag, item, status) where 1447 | `status' specifies the initial on/off state 1448 | each entry; can be 0 or 1 (integers, 1 meaning 1449 | checked, i.e. "on"), or "on", "off" or any 1450 | uppercase variant of these two strings. 1451 | No more than one entry should be set to on. 1452 | 1453 | A radiolist box is similar to a menu box. The main difference 1454 | is that you can indicate which entry is initially selected, 1455 | by setting its status to on. 1456 | 1457 | Return a tuple of the form (code, tag) with the tag for the 1458 | entry that was chosen by the user. `code' is the exit status 1459 | of the dialog-like program. 1460 | 1461 | If the user exits with ESC or CANCEL, or if all entries were 1462 | initially set to off and not altered before the user chose 1463 | OK, the returned tag is the empty string. 1464 | 1465 | Notable exceptions: 1466 | 1467 | any exception raised by self._perform() or _to_onoff() 1468 | 1469 | """ 1470 | cmd = ["--radiolist", text, str(height), str(width), str(list_height)] 1471 | for t in choices: 1472 | cmd.extend(((t[0], t[1], _to_onoff(t[2])))) 1473 | 1474 | (code, tag) = self._perform(*(cmd,), **kwargs) 1475 | 1476 | tag = self._strip_xdialog_newline(tag) 1477 | 1478 | return (code, tag) 1479 | 1480 | def scrollbox(self, text, height=20, width=78, **kwargs): 1481 | """Display a string in a scrollable box. 1482 | 1483 | text -- text to display in the box 1484 | height -- height of the box 1485 | width -- width of the box 1486 | 1487 | This method is a layer on top of textbox. The textbox option 1488 | in dialog allows to display file contents only. This method 1489 | allows you to display any text in a scrollable box. This is 1490 | simply done by creating a temporary file, calling textbox and 1491 | deleting the temporary file afterwards. 1492 | 1493 | Return the dialog-like program's exit status. 1494 | 1495 | Notable exceptions: 1496 | - UnableToCreateTemporaryDirectory 1497 | - PythonDialogIOError 1498 | - PythonDialogOSError 1499 | - exceptions raised by the tempfile module (which are 1500 | unfortunately not mentioned in its documentation, at 1501 | least in Python 2.3.3...) 1502 | 1503 | """ 1504 | # In Python < 2.3, the standard library does not have 1505 | # tempfile.mkstemp(), and unfortunately, tempfile.mktemp() is 1506 | # insecure. So, I create a non-world-writable temporary directory and 1507 | # store the temporary file in this directory. 1508 | try: 1509 | # We want to ensure that f is already bound in the local 1510 | # scope when the finally clause (see below) is executed 1511 | f = 0 1512 | tmp_dir = _create_temporary_directory() 1513 | # If we are here, tmp_dir *is* created (no exception was raised), 1514 | # so chances are great that os.rmdir(tmp_dir) will succeed (as 1515 | # long as tmp_dir is empty). 1516 | # 1517 | # Don't move the _create_temporary_directory() call inside the 1518 | # following try statement, otherwise the user will always see a 1519 | # PythonDialogOSError instead of an 1520 | # UnableToCreateTemporaryDirectory because whenever 1521 | # UnableToCreateTemporaryDirectory is raised, the subsequent 1522 | # os.rmdir(tmp_dir) is bound to fail. 1523 | try: 1524 | fName = os.path.join(tmp_dir, "text") 1525 | # No race condition as with the deprecated tempfile.mktemp() 1526 | # since tmp_dir is not world-writable. 1527 | f = open(fName, "wb") 1528 | f.write(text) 1529 | f.close() 1530 | 1531 | # Ask for an empty title unless otherwise specified 1532 | if not "title" in kwargs.keys(): 1533 | kwargs["title"] = "" 1534 | 1535 | return self._perform( 1536 | *(["--textbox", fName, str(height), str(width)],), 1537 | **kwargs)[0] 1538 | finally: 1539 | if type(f) == types.FileType: 1540 | f.close() # Safe, even several times 1541 | os.unlink(fName) 1542 | os.rmdir(tmp_dir) 1543 | except os.error, v: 1544 | raise PythonDialogOSError(v.strerror) 1545 | except IOError, v: 1546 | raise PythonDialogIOError(v) 1547 | 1548 | def tailbox(self, filename, height=20, width=60, **kwargs): 1549 | """Display the contents of a file in a dialog box, as in "tail -f". 1550 | 1551 | filename -- name of the file whose contents is to be 1552 | displayed in the box 1553 | height -- height of the box 1554 | width -- width of the box 1555 | 1556 | Display the contents of the specified file, updating the 1557 | dialog box whenever the file grows, as with the "tail -f" 1558 | command. 1559 | 1560 | Return the exit status (an integer) of the dialog-like 1561 | program. 1562 | 1563 | Notable exceptions: 1564 | 1565 | any exception raised by self._perform() 1566 | 1567 | """ 1568 | return self._perform( 1569 | *(["--tailbox", filename, str(height), str(width)],), 1570 | **kwargs)[0] 1571 | # No tailboxbg widget, at least for now. 1572 | 1573 | def textbox(self, filename, height=20, width=60, **kwargs): 1574 | """Display the contents of a file in a dialog box. 1575 | 1576 | filename -- name of the file whose contents is to be 1577 | displayed in the box 1578 | height -- height of the box 1579 | width -- width of the box 1580 | 1581 | A text box lets you display the contents of a text file in a 1582 | dialog box. It is like a simple text file viewer. The user 1583 | can move through the file by using the UP/DOWN, PGUP/PGDN 1584 | and HOME/END keys available on most keyboards. If the lines 1585 | are too long to be displayed in the box, the LEFT/RIGHT keys 1586 | can be used to scroll the text region horizontally. For more 1587 | convenience, forward and backward searching functions are 1588 | also provided. 1589 | 1590 | Return the exit status (an integer) of the dialog-like 1591 | program. 1592 | 1593 | Notable exceptions: 1594 | 1595 | any exception raised by self._perform() 1596 | 1597 | """ 1598 | # This is for backward compatibility... not that it is 1599 | # stupid, but I prefer explicit programming. 1600 | if not "title" in kwargs.keys(): 1601 | kwargs["title"] = filename 1602 | return self._perform( 1603 | *(["--textbox", filename, str(height), str(width)],), 1604 | **kwargs)[0] 1605 | 1606 | def timebox(self, text, height=3, width=30, hour=-1, minute=-1, 1607 | second=-1, **kwargs): 1608 | """Display a time dialog box. 1609 | 1610 | text -- text to display in the box 1611 | height -- height of the box 1612 | width -- width of the box 1613 | hour -- inititial hour selected 1614 | minute -- inititial minute selected 1615 | second -- inititial second selected 1616 | 1617 | A dialog is displayed which allows you to select hour, minute 1618 | and second. If the values for hour, minute or second are 1619 | negative (or not explicitely provided, as they default to 1620 | -1), the current time's corresponding values are used. You 1621 | can increment or decrement any of those using the left-, up-, 1622 | right- and down-arrows. Use tab or backtab to move between 1623 | windows. 1624 | 1625 | Return a tuple of the form (code, time) where `code' is the 1626 | exit status (an integer) of the dialog-like program and 1627 | `time' is a list of the form [hour, minute, second] (where 1628 | `hour', `minute' and `second' are integers corresponding to 1629 | the time chosen by the user) if the box was closed with OK, 1630 | or None if it was closed with the Cancel button. 1631 | 1632 | Notable exceptions: 1633 | - any exception raised by self._perform() 1634 | - PythonDialogReModuleError 1635 | - UnexpectedDialogOutput 1636 | 1637 | """ 1638 | (code, output) = self._perform( 1639 | *(["--timebox", text, str(height), str(width), 1640 | str(hour), str(minute), str(second)],), 1641 | **kwargs) 1642 | if code == self.DIALOG_OK: 1643 | try: 1644 | mo = _timebox_time_rec.match(output) 1645 | if mo is None: 1646 | raise UnexpectedDialogOutput( 1647 | "the dialog-like program returned the following " 1648 | "unexpected time with the --timebox option: %s" % output) 1649 | time = map(int, mo.group("hour", "minute", "second")) 1650 | except re.error, v: 1651 | raise PythonDialogReModuleError(v) 1652 | else: 1653 | time = None 1654 | return (code, time) 1655 | 1656 | def yesno(self, text, height=10, width=30, **kwargs): 1657 | """Display a yes/no dialog box. 1658 | 1659 | text -- text to display in the box 1660 | height -- height of the box 1661 | width -- width of the box 1662 | 1663 | A yes/no dialog box of size `height' rows by `width' columns 1664 | will be displayed. The string specified by `text' is 1665 | displayed inside the dialog box. If this string is too long 1666 | to fit in one line, it will be automatically divided into 1667 | multiple lines at appropriate places. The text string can 1668 | also contain the sub-string "\\n" or newline characters to 1669 | control line breaking explicitly. This dialog box is useful 1670 | for asking questions that require the user to answer either 1671 | yes or no. The dialog box has a Yes button and a No button, 1672 | in which the user can switch between by pressing the TAB 1673 | key. 1674 | 1675 | Return the exit status (an integer) of the dialog-like 1676 | program. 1677 | 1678 | Notable exceptions: 1679 | 1680 | any exception raised by self._perform() 1681 | 1682 | """ 1683 | return self._perform( 1684 | *(["--yesno", text, str(height), str(width)],), 1685 | **kwargs)[0] 1686 | -------------------------------------------------------------------------------- /setup.cfg: -------------------------------------------------------------------------------- 1 | [sdist] 2 | formats=bztar,gztar,zip 3 | 4 | [bdist] 5 | formats=bztar,gztar,zip 6 | 7 | [install] 8 | prefix=/usr/local 9 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env python 2 | 3 | # setup.py --- Setup script for pythondialog 4 | # Copyright (c) 2002, 2003, 2004 Florent Rougon 5 | # 6 | # This file is part of pythondialog. 7 | # 8 | # pythondialog is free software; you can redistribute it and/or 9 | # modify it under the terms of the GNU Lesser General Public 10 | # License as published by the Free Software Foundation; either 11 | # version 2.1 of the License, or (at your option) any later version. 12 | # 13 | # pythondialog is distributed in the hope that it will be useful, 14 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 16 | # Lesser General Public License for more details. 17 | # 18 | # You should have received a copy of the GNU Lesser General Public 19 | # License along with this library; if not, write to the Free Software 20 | # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 21 | 22 | import os, string, sys 23 | from distutils.core import setup 24 | 25 | # Note: 26 | # The Distutils included in Python 2.1 don't understand the "license" keyword 27 | # argument of setup correctly (they only understand licence); as I don't want 28 | # to mispell it, if you run the Distutils from Python 2.1, you will get 29 | # License: UNKNOWN. This problem does not appear with the version included in 30 | # Python 2.2. 31 | 32 | PACKAGE = "pythondialog" 33 | VERSION = "2.7" 34 | 35 | def main(): 36 | setup(name=PACKAGE, 37 | version=VERSION, 38 | description="A Python interface to the UNIX dialog utility and " 39 | "mostly-compatible programs", 40 | # Doesn't work great with several authors... 41 | author="Robb Shecter, Sultanbek Tezadov, Florent Rougon, Peter Astrand", 42 | author_email="robb@acm.org, http://sultan.da.ru/, flo@via.ecp.fr", 43 | maintainer="Peter Astrand", 44 | maintainer_email="peter@cendio.se", 45 | url="http://pythondialog.sourceforge.net/", 46 | license="LGPL", 47 | platforms="UNIX", 48 | long_description="""\ 49 | A Python interface to the UNIX dialog utility, designed to provide 50 | an easy, pythonic and as complete as possible way to use the dialog 51 | features from Python code. 52 | Back-end programs that are almost compatible with dialog are also 53 | supported if someone cares about them.""", 54 | keywords=["dialog", "Xdialog", "whiptail", "text-mode interface"], 55 | py_modules=["dialog"]) 56 | 57 | if __name__ == "__main__": main() 58 | --------------------------------------------------------------------------------