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 menu item list parameter." 148 149 def convert(self, value, program): 150 l = [] 151 for v in value: 152 l.append(v.value) 153 l.append(v.text) 154 boolean = Boolean(None) 155 l.append(boolean.convert(v.status, program)) 156 return l 157 158 # Dialogue argument values. 159 160 class MenuItem: 161 def __init__(self, value, text): 162 self.value = value 163 self.text = text 164 165 class ListItem(MenuItem): 166 def __init__(self, value, text, status): 167 MenuItem.__init__(self, value, text) 168 self.status = status 169 170 # Dialogue classes. 171 172 class Dialogue: 173 174 commands = { 175 "KDE" : "kdialog", 176 "GNOME" : "zenity", 177 "X11" : "Xdialog" 178 } 179 180 def open(self, desktop=None): 181 182 """ 183 Open a dialogue box (dialog) using a program appropriate to the desktop 184 environment in use. 185 186 If the optional 'desktop' parameter is specified then attempt to use that 187 particular desktop environment's mechanisms to open the dialog instead of 188 guessing or detecting which environment is being used. 189 190 Suggested values for 'desktop' are "standard", "KDE", "GNOME", "Mac OS X", 191 "Windows". 192 193 The result of the dialogue interaction may be a string indicating user 194 input (for input, password, menu, radiolist, pulldown), a list of strings 195 indicating selections of one or more items (for checklist), or a value 196 indicating true or false (for question). 197 """ 198 199 # Decide on the desktop environment in use. 200 201 desktop_in_use = use_desktop(desktop) 202 203 # Get the program. 204 205 try: 206 program = self.commands[desktop_in_use] 207 except KeyError: 208 raise OSError, "Desktop '%s' not supported (no known dialogue box command could be suggested)" % desktop_in_use 209 210 handler, options = self.info[program] 211 212 cmd = [program] 213 for option in options: 214 if isinstance(option, str): 215 cmd.append(option) 216 else: 217 value = getattr(self, option.name, None) 218 cmd += option.convert(value, program) 219 220 print cmd 221 return handler(cmd, 0) 222 223 class Simple(Dialogue): 224 def __init__(self, text, width=None, height=None): 225 self.text = text 226 self.width = width 227 self.height = height 228 229 class Question(Simple): 230 231 """ 232 A dialogue asking a question and showing response buttons. 233 Options: text, width (in characters), height (in characters) 234 """ 235 236 name = "question" 237 info = { 238 "kdialog" : (_status, ["--yesno", String("text")]), 239 "zenity" : (_status, ["--question", StringKeyword("--text", "text")]), 240 "Xdialog" : (_status, ["--stdout", "--yesno", String("text"), Integer("height"), Integer("width")]), 241 } 242 243 class Warning(Simple): 244 245 """ 246 A dialogue asking a question and showing response buttons. 247 Options: text, width (in characters), height (in characters) 248 """ 249 250 name = "warning" 251 info = { 252 "kdialog" : (_status, ["--warningyesno", String("text")]), 253 "zenity" : (_status, ["--warning", StringKeyword("--text", "text")]), 254 "Xdialog" : (_status, ["--stdout", "--yesno", String("text"), Integer("height"), Integer("width")]), 255 } 256 257 class Message(Simple): 258 259 """ 260 A message dialogue. 261 Options: text, width (in characters), height (in characters) 262 """ 263 264 name = "message" 265 info = { 266 "kdialog" : (_status, ["--msgbox", String("text")]), 267 "zenity" : (_status, ["--info", StringKeyword("--text", "text")]), 268 "Xdialog" : (_status, ["--stdout", "--msgbox", String("text"), Integer("height"), Integer("width")]), 269 } 270 271 class Error(Simple): 272 273 """ 274 An error dialogue. 275 Options: text, width (in characters), height (in characters) 276 """ 277 278 name = "error" 279 info = { 280 "kdialog" : (_status, ["--error", String("text")]), 281 "zenity" : (_status, ["--error", StringKeyword("--text", "text")]), 282 "Xdialog" : (_status, ["--stdout", "--msgbox", String("text"), Integer("height"), Integer("width")]), 283 } 284 285 class Menu(Simple): 286 287 """ 288 A menu of options, one of which being selectable. 289 Options: text, width (in characters), height (in characters), 290 list_height (in items), items (MenuItem objects) 291 """ 292 293 name = "menu" 294 info = { 295 "kdialog" : (_readfrom, ["--menu", String("text"), MenuItemList("items")]), 296 "zenity" : (_readfrom, ["--list", StringKeyword("--text", "text"), StringKeywords("--column", "titles"), 297 MenuItemList("items")] 298 ), 299 "Xdialog" : (_readfrom, ["--stdout", "--menubox", 300 String("text"), Integer("height"), Integer("width"), Integer("list_height"), MenuItemList("items")] 301 ), 302 } 303 304 def __init__(self, text, titles, items, width=None, height=None, list_height=None): 305 Simple.__init__(self, text, width, height) 306 self.titles = titles 307 self.items = items 308 self.list_height = list_height 309 310 class RadioList(Menu): 311 312 """ 313 A list of radio buttons, one of which being selectable. 314 Options: text, width (in characters), height (in characters), 315 list_height (in items), items (ListItem objects), titles 316 """ 317 318 name = "radiolist" 319 info = { 320 "kdialog" : (_readfrom, ["--radiolist", String("text"), ListItemList("items")]), 321 "zenity" : (_readfrom, 322 ["--list", "--radiolist", StringKeyword("--text", "text"), StringKeywords("--column", "titles"), 323 ListItemList("items")] 324 ), 325 "Xdialog" : (_readfrom, ["--stdout", "--radiolist", 326 String("text"), Integer("height"), Integer("width"), Integer("list_height"), ListItemList("items")] 327 ), 328 } 329 330 class CheckList(Menu): 331 332 """ 333 A list of checkboxes, many being selectable. 334 Options: text, width (in characters), height (in characters), 335 list_height (in items), items (ListItem objects), titles 336 """ 337 338 name = "checklist" 339 info = { 340 "kdialog" : (_readfrom, ["--checklist", String("text"), ListItemList("items")]), 341 "zenity" : (_readfrom, 342 ["--list", "--checklist", StringKeyword("--text", "text"), StringKeywords("--column", "titles"), 343 ListItemList("items")] 344 ), 345 "Xdialog" : (_readfrom, ["--stdout", "--checklist", 346 String("text"), Integer("height"), Integer("width"), Integer("list_height"), ListItemList("items")] 347 ), 348 } 349 350 class Pulldown(Menu): 351 352 """ 353 A pull-down menu of options, one of which being selectable. 354 Options: text, width (in characters), height (in characters), 355 entries (list of values) 356 """ 357 358 name = "pulldown" 359 info = { 360 "kdialog" : (_readfrom, ["--combobox", String("text"), Strings("items")]), 361 "zenity" : (_readfrom, ["--list", "--radiolist", StringKeyword("--text", "text"), StringKeywords("--column", "titles"), 362 Strings("items")] 363 ), 364 "Xdialog" : (_readfrom, ["--stdout", "--combobox", String("text"), Integer("height"), Integer("width"), Strings("items")]), 365 } 366 367 class Input(Simple): 368 369 """ 370 An input dialogue, consisting of an input field. 371 Options: text, width (in characters), height (in characters), 372 input 373 """ 374 375 name = "input" 376 info = { 377 "kdialog" : (_readfrom, ["--inputbox", String("text"), String("data")]), 378 "zenity" : (_readfrom, ["--entry", StringKeyword("--text", "text"), StringKeyword("--entry-text", "data")]), 379 "Xdialog" : (_readfrom, ["--stdout", "--inputbox", String("text"), Integer("height"), Integer("width"), String("data")]), 380 } 381 382 def __init__(self, text, data, width=None, height=None): 383 Simple.__init__(self, text, width, height) 384 self.data = data 385 386 class Password(Input): 387 388 """ 389 A password dialogue, consisting of a password entry field. 390 Options: text, width (in characters), height (in characters), 391 input 392 """ 393 394 name = "password" 395 info = { 396 "kdialog" : (_readfrom, ["--password", String("text")]), 397 "zenity" : (_readfrom, ["--password", StringKeyword("--text", "text"), "--hide-text"]), 398 "Xdialog" : (_readfrom, ["--stdout", "--password", "--inputbox", String("text"), Integer("height"), Integer("width")]), 399 } 400 401 class TextFile(Simple): 402 403 """ 404 A text file input box. 405 Options: text, width (in characters), height (in characters), 406 filename 407 """ 408 409 name = "textfile" 410 info = { 411 "kdialog" : (_readfrom, ["--textbox", String("filename"), String("width"), String("height")]), 412 "zenity" : (_readfrom, ["--textbox", StringKeyword("--filename", "filename"), IntegerKeyword("--width", "width"), 413 IntegerKeyword("--height", "height")] 414 ), 415 "Xdialog" : (_readfrom, ["--stdout", "--textbox", String("text"), Integer("height"), Integer("width")]), 416 } 417 418 def __init__(self, text, filename, width=None, height=None): 419 Simple.__init__(self, text, width, height) 420 self.filename = filename 421 422 # vim: tabstop=4 expandtab shiftwidth=4