1 #!/usr/bin/env python 2 3 """ 4 Simple desktop dialogue box support for Python. 5 6 Copyright (C) 2005, 2006, 2007 Paul Boddie <paul@boddie.org.uk> 7 8 This library 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 This library 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA 21 22 -------- 23 24 Opening Dialogue Boxes (Dialogs) 25 -------------------------------- 26 27 To open a dialogue box (dialog) in the current desktop environment, relying on 28 the automatic detection of that environment, use the appropriate dialogue box 29 class: 30 31 question = desktop.dialog.Question("Are you sure?") 32 question.open() 33 34 To override the detected desktop, specify the desktop parameter to the open 35 function as follows: 36 37 question.open("KDE") # Insists on KDE 38 question.open("GNOME") # Insists on GNOME 39 40 The dialogue box options are documented in each class's docstring. 41 """ 42 43 from desktop import use_desktop, _run, _readfrom, _status 44 45 # Dialogue parameter classes. 46 47 class String: 48 49 "A generic parameter." 50 51 def __init__(self, name): 52 self.name = name 53 54 def convert(self, value, program): 55 return [value or ""] 56 57 class Strings(String): 58 59 "Multiple string parameters." 60 61 def convert(self, value, program): 62 return value or [] 63 64 class StringKeyword: 65 66 "A keyword parameter." 67 68 def __init__(self, keyword, name): 69 self.keyword = keyword 70 self.name = name 71 72 def convert(self, value, program): 73 return [self.keyword + "=" + (value or "")] 74 75 class StringKeywords: 76 77 "Multiple keyword parameters." 78 79 def __init__(self, keyword, name): 80 self.keyword = keyword 81 self.name = name 82 83 def convert(self, value, program): 84 l = [] 85 for v in value or []: 86 l.append(self.keyword + "=" + v) 87 return l 88 89 class Integer(String): 90 91 "An integer parameter." 92 93 defaults = { 94 "width" : 40, 95 "height" : 15, 96 "list_height" : 10 97 } 98 99 def convert(self, value, program): 100 if value is None: 101 value = self.defaults[self.name] 102 return [str(int(value))] 103 104 class IntegerKeyword(Integer): 105 106 "An integer keyword parameter." 107 108 def __init__(self, keyword, name): 109 self.keyword = keyword 110 self.name = name 111 112 def convert(self, value, program): 113 if value is None: 114 value = self.defaults[self.name] 115 return [self.keyword + "=" + str(int(value))] 116 117 class Boolean(String): 118 119 "A boolean parameter." 120 121 values = { 122 "kdialog" : ["off", "on"], 123 "zenity" : ["FALSE", "TRUE"], 124 "Xdialog" : ["off", "on"] 125 } 126 127 def convert(self, value, program): 128 values = self.values[program] 129 if value: 130 return [values[1]] 131 else: 132 return [values[0]] 133 134 class MenuItemList(String): 135 136 "A menu item list parameter." 137 138 def convert(self, value, program): 139 l = [] 140 for v in value: 141 l.append(v.value) 142 l.append(v.text) 143 return l 144 145 class ListItemList(String): 146 147 "A radiolist/checklist item list parameter." 148 149 def __init__(self, name, status_first=0): 150 String.__init__(self, name) 151 self.status_first = status_first 152 153 def convert(self, value, program): 154 l = [] 155 for v in value: 156 boolean = Boolean(None) 157 status = boolean.convert(v.status, program) 158 if self.status_first: 159 l += status 160 l.append(v.value) 161 l.append(v.text) 162 if not self.status_first: 163 l += status 164 return l 165 166 # Dialogue argument values. 167 168 class MenuItem: 169 170 "A menu item which can also be used with radiolists and checklists." 171 172 def __init__(self, value, text, status=0): 173 self.value = value 174 self.text = text 175 self.status = status 176 177 # Dialogue classes. 178 179 class Dialogue: 180 181 commands = { 182 "KDE" : "kdialog", 183 "GNOME" : "zenity", 184 "X11" : "Xdialog" 185 } 186 187 def open(self, desktop=None): 188 189 """ 190 Open a dialogue box (dialog) using a program appropriate to the desktop 191 environment in use. 192 193 If the optional 'desktop' parameter is specified then attempt to use that 194 particular desktop environment's mechanisms to open the dialog instead of 195 guessing or detecting which environment is being used. 196 197 Suggested values for 'desktop' are "standard", "KDE", "GNOME", "Mac OS X", 198 "Windows". 199 200 The result of the dialogue interaction may be a string indicating user 201 input (for input, password, menu, radiolist, pulldown), a list of strings 202 indicating selections of one or more items (for checklist), or a value 203 indicating true or false (for question). 204 """ 205 206 # Decide on the desktop environment in use. 207 208 desktop_in_use = use_desktop(desktop) 209 210 # Get the program. 211 212 try: 213 program = self.commands[desktop_in_use] 214 except KeyError: 215 raise OSError, "Desktop '%s' not supported (no known dialogue box command could be suggested)" % desktop_in_use 216 217 handler, options = self.info[program] 218 219 cmd = [program] 220 for option in options: 221 if isinstance(option, str): 222 cmd.append(option) 223 else: 224 value = getattr(self, option.name, None) 225 cmd += option.convert(value, program) 226 227 print cmd 228 return handler(cmd, 0) 229 230 class Simple(Dialogue): 231 def __init__(self, text, width=None, height=None): 232 self.text = text 233 self.width = width 234 self.height = height 235 236 class Question(Simple): 237 238 """ 239 A dialogue asking a question and showing response buttons. 240 Options: text, width (in characters), height (in characters) 241 """ 242 243 name = "question" 244 info = { 245 "kdialog" : (_status, ["--yesno", String("text")]), 246 "zenity" : (_status, ["--question", StringKeyword("--text", "text")]), 247 "Xdialog" : (_status, ["--stdout", "--yesno", String("text"), Integer("height"), Integer("width")]), 248 } 249 250 class Warning(Simple): 251 252 """ 253 A dialogue asking a question and showing response buttons. 254 Options: text, width (in characters), height (in characters) 255 """ 256 257 name = "warning" 258 info = { 259 "kdialog" : (_status, ["--warningyesno", String("text")]), 260 "zenity" : (_status, ["--warning", StringKeyword("--text", "text")]), 261 "Xdialog" : (_status, ["--stdout", "--yesno", String("text"), Integer("height"), Integer("width")]), 262 } 263 264 class Message(Simple): 265 266 """ 267 A message dialogue. 268 Options: text, width (in characters), height (in characters) 269 """ 270 271 name = "message" 272 info = { 273 "kdialog" : (_status, ["--msgbox", String("text")]), 274 "zenity" : (_status, ["--info", StringKeyword("--text", "text")]), 275 "Xdialog" : (_status, ["--stdout", "--msgbox", String("text"), Integer("height"), Integer("width")]), 276 } 277 278 class Error(Simple): 279 280 """ 281 An error dialogue. 282 Options: text, width (in characters), height (in characters) 283 """ 284 285 name = "error" 286 info = { 287 "kdialog" : (_status, ["--error", String("text")]), 288 "zenity" : (_status, ["--error", StringKeyword("--text", "text")]), 289 "Xdialog" : (_status, ["--stdout", "--msgbox", String("text"), Integer("height"), Integer("width")]), 290 } 291 292 class Menu(Simple): 293 294 """ 295 A menu of options, one of which being selectable. 296 Options: text, width (in characters), height (in characters), 297 list_height (in items), items (MenuItem objects) 298 """ 299 300 name = "menu" 301 info = { 302 "kdialog" : (_readfrom, ["--menu", String("text"), MenuItemList("items")]), 303 "zenity" : (_readfrom, ["--list", StringKeyword("--text", "text"), StringKeywords("--column", "titles"), 304 MenuItemList("items")] 305 ), 306 "Xdialog" : (_readfrom, ["--stdout", "--menubox", 307 String("text"), Integer("height"), Integer("width"), Integer("list_height"), MenuItemList("items")] 308 ), 309 } 310 item = MenuItem 311 312 def __init__(self, text, titles, items=None, width=None, height=None, list_height=None): 313 314 """ 315 Initialise a menu with the given heading 'text', column 'titles', and 316 optional 'items' (which may be added later), 'width' (in characters), 317 'height' (in characters) and 'list_height' (in items). 318 """ 319 320 Simple.__init__(self, text, width, height) 321 self.titles = titles 322 self.items = items or [] 323 self.list_height = list_height 324 325 def add(self, *args, **kw): 326 327 """ 328 Add an item, passing the given arguments to the appropriate item class. 329 """ 330 331 self.items.append(self.item(*args, **kw)) 332 333 class RadioList(Menu): 334 335 """ 336 A list of radio buttons, one of which being selectable. 337 Options: text, width (in characters), height (in characters), 338 list_height (in items), items (MenuItem objects), titles 339 """ 340 341 name = "radiolist" 342 info = { 343 "kdialog" : (_readfrom, ["--radiolist", String("text"), ListItemList("items")]), 344 "zenity" : (_readfrom, 345 ["--list", "--radiolist", StringKeyword("--text", "text"), StringKeywords("--column", "titles"), 346 ListItemList("items", 1)] 347 ), 348 "Xdialog" : (_readfrom, ["--stdout", "--radiolist", 349 String("text"), Integer("height"), Integer("width"), Integer("list_height"), ListItemList("items")] 350 ), 351 } 352 353 class CheckList(Menu): 354 355 """ 356 A list of checkboxes, many being selectable. 357 Options: text, width (in characters), height (in characters), 358 list_height (in items), items (MenuItem objects), titles 359 """ 360 361 name = "checklist" 362 info = { 363 "kdialog" : (_readfrom, ["--checklist", String("text"), ListItemList("items")]), 364 "zenity" : (_readfrom, 365 ["--list", "--checklist", StringKeyword("--text", "text"), StringKeywords("--column", "titles"), 366 ListItemList("items", 1)] 367 ), 368 "Xdialog" : (_readfrom, ["--stdout", "--checklist", 369 String("text"), Integer("height"), Integer("width"), Integer("list_height"), ListItemList("items")] 370 ), 371 } 372 373 class Pulldown(Menu): 374 375 """ 376 A pull-down menu of options, one of which being selectable. 377 Options: text, width (in characters), height (in characters), 378 entries (list of values) 379 """ 380 381 name = "pulldown" 382 info = { 383 "kdialog" : (_readfrom, ["--combobox", String("text"), Strings("items")]), 384 "zenity" : (_readfrom, ["--list", "--radiolist", StringKeyword("--text", "text"), StringKeywords("--column", "titles"), 385 Strings("items")] 386 ), 387 "Xdialog" : (_readfrom, ["--stdout", "--combobox", String("text"), Integer("height"), Integer("width"), Strings("items")]), 388 } 389 390 class Input(Simple): 391 392 """ 393 An input dialogue, consisting of an input field. 394 Options: text, width (in characters), height (in characters), 395 input 396 """ 397 398 name = "input" 399 info = { 400 "kdialog" : (_readfrom, ["--inputbox", String("text"), String("data")]), 401 "zenity" : (_readfrom, ["--entry", StringKeyword("--text", "text"), StringKeyword("--entry-text", "data")]), 402 "Xdialog" : (_readfrom, ["--stdout", "--inputbox", String("text"), Integer("height"), Integer("width"), String("data")]), 403 } 404 405 def __init__(self, text, data, width=None, height=None): 406 Simple.__init__(self, text, width, height) 407 self.data = data 408 409 class Password(Input): 410 411 """ 412 A password dialogue, consisting of a password entry field. 413 Options: text, width (in characters), height (in characters), 414 input 415 """ 416 417 name = "password" 418 info = { 419 "kdialog" : (_readfrom, ["--password", String("text")]), 420 "zenity" : (_readfrom, ["--password", StringKeyword("--text", "text"), "--hide-text"]), 421 "Xdialog" : (_readfrom, ["--stdout", "--password", "--inputbox", String("text"), Integer("height"), Integer("width")]), 422 } 423 424 class TextFile(Simple): 425 426 """ 427 A text file input box. 428 Options: text, width (in characters), height (in characters), 429 filename 430 """ 431 432 name = "textfile" 433 info = { 434 "kdialog" : (_readfrom, ["--textbox", String("filename"), String("width"), String("height")]), 435 "zenity" : (_readfrom, ["--textbox", StringKeyword("--filename", "filename"), IntegerKeyword("--width", "width"), 436 IntegerKeyword("--height", "height")] 437 ), 438 "Xdialog" : (_readfrom, ["--stdout", "--textbox", String("text"), Integer("height"), Integer("width")]), 439 } 440 441 def __init__(self, text, filename, width=None, height=None): 442 Simple.__init__(self, text, width, height) 443 self.filename = filename 444 445 # Available dialogues. 446 447 available = [Question, Warning, Message, Error, Menu, CheckList, RadioList, Input, Password, Pulldown, TextFile] 448 449 # vim: tabstop=4 expandtab shiftwidth=4