Package screenlets :: Module options
[hide private]
[frames] | no frames]

Source Code for Module screenlets.options

   1  # This application is released under the GNU General Public License  
   2  # v3 (or, at your option, any later version). You can find the full  
   3  # text of the license under http://www.gnu.org/licenses/gpl.txt.  
   4  # By using, editing and/or distributing this software you agree to  
   5  # the terms and conditions of this license.  
   6  # Thank you for using free software! 
   7   
   8  # Options-system (c) RYX (aka Rico Pfaus) 2007 <ryx@ryxperience.com> 
   9  # 
  10  # INFO: 
  11  # - a dynamic Options-system that allows very easy creation of 
  12  #   objects with embedded configuration-system. 
  13  #   NOTE: The Dialog is not very nice yet - it is not good OOP-practice 
  14  #   because too big functions and bad class-layout ... but it works 
  15  #   for now ... :) 
  16  # 
  17  # TODO: 
  18  # - option-widgets for all option-types (e.g. ListOptionWidget, ColorOptionWidget) 
  19  # - OptionGroup-class instead of (or behind) add_options_group 
  20  # - TimeOption, DateOption 
  21  # - FileOption needs filter/limit-attribute 
  22  # - allow options to disable/enable other options 
  23  # - support for EditableOptions-subclasses as options 
  24  # - separate OptionEditorWidget from Editor-Dialog 
  25  # - place ui-code into screenlets.options.ui-module 
  26  # - create own widgets for each Option-subclass 
  27  # 
  28   
  29  import screenlets 
  30  import utils 
  31   
  32  import os                
  33  import gtk, gobject 
  34  import xml.dom.minidom 
  35  from xml.dom.minidom import Node 
  36   
  37   
  38  # ----------------------------------------------------------------------- 
  39  # Option-classes and subclasses 
  40  # ----------------------------------------------------------------------- 
  41   
42 -class Option(gobject.GObject):
43 """An Option stores information about a certain object-attribute. It doesn't 44 carry information about the value or the object it belongs to - it is only a 45 one-way data-storage for describing how to handle attributes.""" 46 47 __gsignals__ = dict(option_changed=(gobject.SIGNAL_RUN_FIRST, 48 gobject.TYPE_NONE, (gobject.TYPE_OBJECT,))) 49
50 - def __init__ (self, group, name, default, label, desc, 51 disabled=False, hidden=False, callback=None, protected=False):
52 """Creates a new Option with the given information.""" 53 super(Option, self).__init__() 54 self.name = name 55 self.label = label 56 self.desc = desc 57 self.default = default 58 self.disabled = disabled 59 self.hidden = hidden 60 # for groups (TODO: OptionGroup) 61 self.group= group 62 # callback to be notified when this option changes 63 self.callback = callback 64 # real-time update? 65 self.realtime = True 66 # protected from get/set through service 67 self.protected = protected
68
69 - def on_import (self, strvalue):
70 """Callback - called when an option gets imported from a string. 71 This function MUST return the string-value converted to the required 72 type!""" 73 return strvalue.replace("\\n", "\n")
74
75 - def on_export (self, value):
76 """Callback - called when an option gets exported to a string. The 77 value-argument needs to be converted to a string that can be imported 78 by the on_import-handler. This handler MUST return the value 79 converted to a string!""" 80 return str(value).replace("\n", "\\n")
81 82
83 -class FileOption (Option):
84 """An Option-subclass for string-values that contain filenames. Adds 85 a patterns-attribute that can contain a list of patterns to be shown 86 in the assigned file selection dialog. The show_pixmaps-attribute 87 can be set to True to make the filedialog show all image-types 88 supported by gtk.Pixmap. If the directory-attributue is true, the 89 dialog will ony allow directories.""" 90
91 - def __init__ (self, group, name, default, label, desc, 92 patterns=['*'], image=False, directory=False, **keyword_args):
93 Option.__init__(self, group, name, default,label, desc, **keyword_args) 94 self.patterns = patterns 95 self.image = image 96 self.directory = False
97 98
99 -class ImageOption (Option):
100 """An Option-subclass for string-values that contain filenames of 101 image-files."""
102 103
104 -class DirectoryOption (Option):
105 """An Option-subclass for filename-strings that contain directories."""
106 107
108 -class BoolOption (Option):
109 """An Option for boolean values.""" 110
111 - def on_import (self, strvalue):
112 if strvalue == "True": 113 return True 114 return False
115 116
117 -class StringOption (Option):
118 """An Option for values of type string.""" 119
120 - def __init__ (self, group, name, default, label, desc, 121 choices=None, password=False, **keyword_args):
122 Option.__init__(self, group, name, default,label, desc, **keyword_args) 123 self.choices = choices 124 self.password = password
125 126
127 -class IntOption (Option):
128 """An Option for values of type number (can be int or float).""" 129
130 - def __init__ (self, group, name, default, label, desc, min=0, max=0, 131 increment=1, **keyword_args):
132 Option.__init__(self, group, name, default, label, desc, **keyword_args) 133 self.min = min 134 self.max = max 135 self.increment = increment
136
137 - def on_import (self, strvalue):
138 """Called when IntOption gets imported. Converts str to int.""" 139 try: 140 if strvalue[0]=='-': 141 return int(strvalue[1:]) * -1 142 return int(strvalue) 143 except: 144 print "Error during on_import - option: %s." % self.name 145 return 0
146 147
148 -class FloatOption (IntOption):
149 """An Option for values of type float.""" 150
151 - def __init__ (self, group, name, default, label, desc, digits=1, 152 **keyword_args):
153 IntOption.__init__(self, group, name, default, label, desc, 154 **keyword_args) 155 self.digits = digits
156
157 - def on_import (self, strvalue):
158 """Called when FloatOption gets imported. Converts str to float.""" 159 if strvalue[0]=='-': 160 return float(strvalue[1:]) * -1.0 161 return float(strvalue)
162 163
164 -class FontOption (Option):
165 """An Option for fonts (a simple StringOption)."""
166 167
168 -class ColorOption (Option):
169 """An Option for colors. Stored as a list with 4 values (r, g, b, a).""" 170
171 - def on_import (self, strvalue):
172 """Import (r, g, b, a) from comma-separated string.""" 173 # strip braces and spaces 174 strvalue = strvalue.lstrip('(') 175 strvalue = strvalue.rstrip(')') 176 strvalue = strvalue.strip() 177 # split value on commas 178 tmpval = strvalue.split(',') 179 outval = [] 180 for f in tmpval: 181 # create list and again remove spaces 182 outval.append(float(f.strip())) 183 return outval
184
185 - def on_export (self, value):
186 """Export r, g, b, a to comma-separated string.""" 187 l = len(value) 188 outval = '' 189 for i in xrange(l): 190 outval += str(value[i]) 191 if i < l-1: 192 outval += ',' 193 return outval
194 195
196 -class ListOption (Option):
197 """An Option-type for list of strings.""" 198
199 - def on_import (self, strvalue):
200 """Import python-style list from a string (like [1, 2, 'test'])""" 201 lst = eval(strvalue) 202 return lst
203
204 - def on_export (self, value):
205 """Export list as string.""" 206 return str(value)
207 208 209 import gnomekeyring
210 -class AccountOption (Option):
211 """An Option-type for username/password combos. Stores the password in 212 the gnome-keyring (if available) and only saves username and auth_token 213 through the screenlets-backend. 214 TODO: 215 - not create new token for any change (use "set" instead of "create" if 216 the given item already exists) 217 - use usual storage if no keyring is available but output warning 218 - on_delete-function for removing the data from keyring when the 219 Screenlet holding the option gets deleted""" 220
221 - def __init__ (self, group, name, default, label, desc, **keyword_args):
222 Option.__init__ (self, group, name, default, label, desc, 223 protected=True, **keyword_args) 224 # check for availability of keyring 225 if not gnomekeyring.is_available(): 226 raise Exception('GnomeKeyring is not available!!') # TEMP!!! 227 # THIS IS A WORKAROUND FOR A BUG IN KEYRING (usually we would use 228 # gnomekeyring.get_default_keyring_sync() here): 229 # find first available keyring 230 keyring_list = gnomekeyring.list_keyring_names_sync() 231 if len(keyring_list) == 0: 232 raise Exception('No keyrings found. Create one first!') 233 else: 234 # we prefer the default keyring 235 if keyring_list.count('default') > 0: 236 self.keyring = 'default' 237 else: 238 print "Warn: No default keyring found, storage not permanent!" 239 self.keyring = keyring_list[0]
240
241 - def on_import (self, strvalue):
242 """Import account info from a string (like 'username:auth_token'), 243 retrieve the password from the storage and return a tuple containing 244 username and password.""" 245 # split string into username/auth_token 246 #data = strvalue.split(':', 1) 247 (name, auth_token) = strvalue.split(':', 1) 248 if name and auth_token: 249 # read pass from storage 250 try: 251 pw = gnomekeyring.item_get_info_sync('session', 252 int(auth_token)).get_secret() 253 except Exception, ex: 254 print "ERROR: Unable to read password from keyring: %s" % ex 255 pw = '' 256 # return 257 return (name, pw) 258 else: 259 raise Exception('Illegal value in AccountOption.on_import.')
260
261 - def on_export (self, value):
262 """Export the given tuple/list containing a username and a password. The 263 function stores the password in the gnomekeyring and returns a 264 string in form 'username:auth_token'.""" 265 # store password in storage 266 attribs = dict(name=value[0]) 267 auth_token = gnomekeyring.item_create_sync('session', 268 gnomekeyring.ITEM_GENERIC_SECRET, value[0], attribs, value[1], True) 269 # build value from username and auth_token 270 return value[0] + ':' + str(auth_token)
271 272 """#TEST: 273 o = AccountOption('None', 'pop3_account', ('',''), 'Username/Password', 'Enter username/password here ...') 274 # save option to keyring 275 exported_account = o.on_export(('RYX', 'mysecretpassword')) 276 print exported_account 277 # and read option back from keyring 278 print o.on_import(exported_account) 279 280 281 import sys 282 sys.exit(0)""" 283
284 -class TimeOption (ColorOption):
285 """An Option-subclass for string-values that contain dates."""
286 287 288 # ----------------------------------------------------------------------- 289 # EditableOptions-class and needed functions 290 # ----------------------------------------------------------------------- 291
292 -def create_option_from_node (node, groupname):
293 """Create an Option from an XML-node with option-metadata.""" 294 #print "TODO OPTION: " + str(cn) 295 otype = node.getAttribute("type") 296 oname = node.getAttribute("name") 297 ohidden = node.getAttribute("hidden") 298 odefault = None 299 oinfo = '' 300 olabel = '' 301 omin = None 302 omax = None 303 oincrement = 1 304 ochoices = '' 305 odigits = None 306 if otype and oname: 307 # parse children of option-node and save all useful attributes 308 for attr in node.childNodes: 309 if attr.nodeType == Node.ELEMENT_NODE: 310 if attr.nodeName == 'label': 311 olabel = attr.firstChild.nodeValue 312 elif attr.nodeName == 'info': 313 oinfo = attr.firstChild.nodeValue 314 elif attr.nodeName == 'default': 315 odefault = attr.firstChild.nodeValue 316 elif attr.nodeName == 'min': 317 omin = attr.firstChild.nodeValue 318 elif attr.nodeName == 'max': 319 omax = attr.firstChild.nodeValue 320 elif attr.nodeName == 'increment': 321 oincrement = attr.firstChild.nodeValue 322 elif attr.nodeName == 'choices': 323 ochoices = attr.firstChild.nodeValue 324 elif attr.nodeName == 'digits': 325 odigits = attr.firstChild.nodeValue 326 # if we have all needed values, create the Option 327 if odefault: 328 # create correct classname here 329 cls = otype[0].upper() + otype.lower()[1:] + 'Option' 330 #print 'Create: ' +cls +' / ' + oname + ' ('+otype+')' 331 # and build new instance (we use on_import for setting default val) 332 clsobj = getattr(__import__(__name__), cls) 333 opt = clsobj(groupname, oname, None, olabel, oinfo) 334 opt.default = opt.on_import(odefault) 335 # set values to the correct types 336 if cls == 'IntOption': 337 if omin: 338 opt.min = int(omin) 339 if omax: 340 opt.max = int(omax) 341 if oincrement: 342 opt.increment = int(oincrement) 343 elif cls == 'FloatOption': 344 if odigits: 345 opt.digits = int(odigits) 346 if omin: 347 opt.min = float(omin) 348 if omax: 349 opt.max = float(omax) 350 if oincrement: 351 opt.increment = float(oincrement) 352 elif cls == 'StringOption': 353 if ochoices: 354 opt.choices = ochoices 355 return opt 356 return None
357 358
359 -class EditableOptions:
360 """The EditableOptions can be inherited from to allow objects to export 361 editable options for editing them with the OptionsEditor-class. 362 NOTE: This could use some improvement and is very poorly coded :) ...""" 363
364 - def __init__ (self):
365 self.__options__ = [] 366 self.__options_groups__ = {} 367 # This is a workaround to remember the order of groups 368 self.__options_groups_ordered__ = []
369
370 - def add_option (self, option, callback=None, realtime=True):
371 """Add an editable option to this object. Editable Options can be edited 372 and configured using the OptionsDialog. The optional callback-arg can be 373 used to set a callback that gets notified when the option changes its 374 value.""" 375 #print "Add option: "+option.name 376 # if option already editable (i.e. initialized), return 377 for o in self.__options__: 378 if o.name == option.name: 379 return False 380 self.__dict__[option.name] = option.default 381 # set auto-update (TEMPORARY?) 382 option.realtime = realtime 383 # add option to group (output error if group is undefined) 384 try: 385 self.__options_groups__[option.group]['options'].append(option) 386 except: 387 print "Options: Error- group " + option.group + " not defined." 388 return False 389 # now add the option 390 self.__options__.append(option) 391 # if callback is set, add callback 392 if callback: 393 option.connect("option_changed", callback) 394 return True
395 396
397 - def add_options_group (self, name, group_info):
398 """Add a new options-group to this Options-object""" 399 self.__options_groups__[name] = {'label':name, 400 'info':group_info, 'options':[]} 401 self.__options_groups_ordered__.append(name)
402 #print self.options_groups 403
404 - def disable_option (self, name):
405 """Disable the inputs for a certain Option.""" 406 for o in self.__options__: 407 if o.name == name: 408 o.disabled = True 409 return True 410 return False
411
412 - def export_options_as_list (self):
413 """Returns all editable options within a list (without groups) 414 as key/value tuples.""" 415 lst = [] 416 for o in self.__options__: 417 lst.append((o.name, getattr(self, o.name))) 418 return lst
419
420 - def get_option_by_name (self, name):
421 """Returns an option in this Options by it's name (or None). 422 TODO: this gives wrong results in childclasses ... maybe access 423 as class-attribute??""" 424 for o in self.__options__: 425 if o.name == name: 426 return o 427 return None
428
429 - def remove_option (self, name):
430 """Remove an option from this Options.""" 431 for o in self.__options__: 432 if o.name == name: 433 del o 434 return True 435 return True
436
437 - def add_options_from_file (self, filename):
438 """This function creates options from an XML-file with option-metadata. 439 TODO: make this more reusable and place it into module (once the groups 440 are own objects)""" 441 # create xml document 442 try: 443 doc = xml.dom.minidom.parse(filename) 444 except: 445 raise Exception('Invalid XML in metadata-file (or file missing): "%s".' % filename) 446 # get rootnode 447 root = doc.firstChild 448 if not root or root.nodeName != 'screenlet': 449 raise Exception('Missing or invalid rootnode in metadata-file: "%s".' % filename) 450 # ok, let's check the nodes: this one should contain option-groups 451 groups = [] 452 for node in root.childNodes: 453 # we only want element-nodes 454 if node.nodeType == Node.ELEMENT_NODE: 455 #print node 456 if node.nodeName != 'group' or not node.hasChildNodes(): 457 # we only allow groups in the first level (groups need children) 458 raise Exception('Error in metadata-file "%s" - only <group>-tags allowed in first level. Groups must contain at least one <info>-element.' % filename) 459 else: 460 # ok, create a new group and parse its elements 461 group = {} 462 group['name'] = node.getAttribute("name") 463 if not group['name']: 464 raise Exception('No name for group defined in "%s".' % filename) 465 group['info'] = '' 466 group['options'] = [] 467 # check all children in group 468 for on in node.childNodes: 469 if on.nodeType == Node.ELEMENT_NODE: 470 if on.nodeName == 'info': 471 # info-node? set group-info 472 group['info'] = on.firstChild.nodeValue 473 elif on.nodeName == 'option': 474 # option node? parse option node 475 opt = create_option_from_node (on, group['name']) 476 # ok? add it to list 477 if opt: 478 group['options'].append(opt) 479 else: 480 raise Exception('Invalid option-node found in "%s".' % filename) 481 482 # create new group 483 if len(group['options']): 484 self.add_options_group(group['name'], group['info']) 485 for o in group['options']: 486 self.add_option(o)
487 # add group to list 488 #groups.append(group) 489 490 # ----------------------------------------------------------------------- 491 # OptionsDialog and UI-classes 492 # ----------------------------------------------------------------------- 493
494 -class ListOptionDialog (gtk.Dialog):
495 """An editing dialog used for editing options of the ListOption-type.""" 496 497 model = None 498 tree = None 499 buttonbox = None 500 501 # call gtk.Dialog.__init__
502 - def __init__ (self):
503 super(ListOptionDialog, self).__init__("Edit List", 504 flags=gtk.DIALOG_DESTROY_WITH_PARENT | gtk.DIALOG_NO_SEPARATOR, 505 buttons = (gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL, 506 gtk.STOCK_OK, gtk.RESPONSE_OK)) 507 # set size 508 self.resize(300, 370) 509 self.set_keep_above(True) # to avoid confusion 510 # init vars 511 self.model = gtk.ListStore(str) 512 # create UI 513 self.create_ui()
514
515 - def create_ui (self):
516 """Create the user-interface for this dialog.""" 517 # create outer hbox (tree|buttons) 518 hbox = gtk.HBox() 519 hbox.set_border_width(10) 520 hbox.set_spacing(10) 521 # create tree 522 self.tree = gtk.TreeView(model=self.model) 523 self.tree.set_headers_visible(False) 524 self.tree.set_reorderable(True) 525 #self.tree.set_grid_lines(gtk.TREE_VIEW_GRID_LINES_HORIZONTAL) 526 col = gtk.TreeViewColumn('') 527 cell = gtk.CellRendererText() 528 #cell.set_property('cell-background', 'cyan') 529 cell.set_property('foreground', 'black') 530 col.pack_start(cell, False) 531 col.set_attributes(cell, text=0) 532 self.tree.append_column(col) 533 self.tree.show() 534 hbox.pack_start(self.tree, True, True) 535 #sep = gtk.VSeparator() 536 #sep.show() 537 #hbox.add(sep) 538 # create buttons 539 self.buttonbox = bb = gtk.VButtonBox() 540 self.buttonbox.set_layout(gtk.BUTTONBOX_START) 541 b1 = gtk.Button(stock=gtk.STOCK_ADD) 542 b2 = gtk.Button(stock=gtk.STOCK_EDIT) 543 b3 = gtk.Button(stock=gtk.STOCK_REMOVE) 544 b1.connect('clicked', self.button_callback, 'add') 545 b2.connect('clicked', self.button_callback, 'edit') 546 b3.connect('clicked', self.button_callback, 'remove') 547 bb.add(b1) 548 bb.add(b2) 549 bb.add(b3) 550 self.buttonbox.show_all() 551 #hbox.add(self.buttonbox) 552 hbox.pack_end(self.buttonbox, False) 553 # add everything to outer hbox and show it 554 hbox.show() 555 self.vbox.add(hbox)
556
557 - def set_list (self, lst):
558 """Set the list to be edited in this editor.""" 559 for el in lst: 560 self.model.append([el])
561
562 - def get_list (self):
563 """Return the list that is currently being edited in this editor.""" 564 lst = [] 565 for i in self.model: 566 lst.append(i[0]) 567 return lst
568
569 - def remove_selected_item (self):
570 """Remove the currently selected item.""" 571 sel = self.tree.get_selection() 572 if sel: 573 it = sel.get_selected()[1] 574 if it: 575 print self.model.get_value(it, 0) 576 self.model.remove(it)
577
578 - def entry_dialog (self, default = ''):
579 """Show entry-dialog and return string.""" 580 entry = gtk.Entry() 581 entry.set_text(default) 582 entry.show() 583 dlg = gtk.Dialog("Add/Edit Item", flags=gtk.DIALOG_DESTROY_WITH_PARENT, 584 buttons = (gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL, gtk.STOCK_OK, 585 gtk.RESPONSE_OK)) 586 dlg.set_keep_above(True) 587 dlg.vbox.add(entry) 588 resp = dlg.run() 589 ret = None 590 if resp == gtk.RESPONSE_OK: 591 ret = entry.get_text() 592 dlg.destroy() 593 return ret
594
595 - def button_callback (self, widget, id):
596 print "PRESS: "+id 597 if id == 'remove': 598 self.remove_selected_item() 599 if id == 'add': 600 new = self.entry_dialog() 601 if new != None: 602 self.model.append([new]) 603 if id == 'edit': 604 sel = self.tree.get_selection() 605 if sel: 606 it = sel.get_selected()[1] 607 if it: 608 new = self.entry_dialog(self.model.get_value(it, 0)) 609 if new != None: 610 #self.model.append([new]) 611 self.model.set_value(it, 0, new)
612 613 614 # TEST 615 """dlg = ListOptionDialog() 616 dlg.set_list(['test1', 'afarew34s', 'fhjh23faj', 'yxcdfs58df', 'hsdf7jsdfh']) 617 dlg.run() 618 print "RESULT: " + str(dlg.get_list()) 619 dlg.destroy() 620 import sys 621 sys.exit(1)""" 622 # /TEST 623
624 -class OptionsDialog (gtk.Dialog):
625 """A dynamic options-editor for editing Screenlets which are implementing 626 the EditableOptions-class.""" 627 628 __shown_object = None 629
630 - def __init__ (self, width, height):
631 # call gtk.Dialog.__init__ 632 super(OptionsDialog, self).__init__( 633 "Edit Options", flags=gtk.DIALOG_DESTROY_WITH_PARENT | 634 gtk.DIALOG_NO_SEPARATOR, 635 buttons = (#gtk.STOCK_REVERT_TO_SAVED, gtk.RESPONSE_APPLY, 636 gtk.STOCK_CLOSE, gtk.RESPONSE_OK)) 637 # set size 638 self.resize(width, height) 639 self.set_keep_above(True) # to avoid confusion 640 self.set_border_width(10) 641 # create attribs 642 self.page_about = None 643 self.page_options = None 644 self.page_themes = None 645 self.vbox_editor = None 646 self.hbox_about = None 647 self.infotext = None 648 self.infoicon = None 649 # create theme-list 650 self.liststore = gtk.ListStore(object) 651 self.tree = gtk.TreeView(model=self.liststore) 652 # create/add outer notebook 653 self.main_notebook = gtk.Notebook() 654 self.main_notebook.show() 655 self.vbox.add(self.main_notebook) 656 # create/init notebook pages 657 self.create_about_page() 658 self.create_themes_page() 659 self.create_options_page() 660 # crete tooltips-object 661 self.tooltips = gtk.Tooltips()
662 663 # "public" functions 664
665 - def reset_to_defaults (self):
666 """Reset all entries for the currently shown object to their default 667 values (the values the object has when it is first created). 668 NOTE: This function resets ALL options, so BE CARFEUL!""" 669 if self.__shown_object: 670 for o in self.__shown_object.__options__: 671 # set default value 672 setattr(self.__shown_object, o.name, o.default)
673
674 - def set_info (self, name, info, copyright='', version='', icon=None):
675 """Update the "About"-page with the given information.""" 676 # convert infotext (remove EOLs and TABs) 677 info = info.replace("\n", "") 678 info = info.replace("\t", " ") 679 # create markup 680 markup = '\n<b><span size="xx-large">' + name + '</span></b>' 681 if version: 682 markup += ' <span size="large"><b>' + version + '</b></span>' 683 markup += '\n\n'+info+'\n<span size="small">\n'+copyright+'</span>' 684 self.infotext.set_markup(markup) 685 # icon? 686 if icon: 687 # remove old icon 688 if self.infoicon: 689 self.infoicon.destroy() 690 # set new icon 691 self.infoicon = icon 692 self.infoicon.set_alignment(0.0, 0.10) 693 self.infoicon.show() 694 self.hbox_about.pack_start(self.infoicon, 0, 1, 10) 695 else: 696 self.infoicon.hide()
697
698 - def show_options_for_object (self, obj):
699 """Update the OptionsEditor to show the options for the given Object. 700 The Object needs to be an EditableOptions-subclass. 701 NOTE: This needs heavy improvement and should use OptionGroups once 702 they exist""" 703 self.__shown_object = obj 704 # create notebook for groups 705 notebook = gtk.Notebook() 706 self.vbox_editor.add(notebook) 707 for group in obj.__options_groups_ordered__: 708 group_data = obj.__options_groups__[group] 709 # create box for tab-page 710 page = gtk.VBox() 711 page.set_border_width(10) 712 if group_data['info'] != '': 713 info = gtk.Label(group_data['info']) 714 info.show() 715 info.set_alignment(0, 0) 716 page.pack_start(info, 0, 0, 7) 717 sep = gtk.HSeparator() 718 sep.show() 719 #page.pack_start(sep, 0, 0, 5) 720 # create VBox for inputs 721 box = gtk.VBox() 722 box.show() 723 box.set_border_width(5) 724 # add box to page 725 page.add(box) 726 page.show() 727 # add new notebook-page 728 label = gtk.Label(group_data['label']) 729 label.show() 730 notebook.append_page(page, label) 731 # and create inputs 732 for option in group_data['options']: 733 if option.hidden == False: 734 val = getattr(obj, option.name)#obj.__dict__[option.name] 735 w = self.get_widget_for_option(option, val) 736 if w: 737 box.pack_start(w, 0, 0) 738 w.show() 739 notebook.show() 740 # show/hide themes tab, depending on whether the screenlet uses themes 741 if obj.uses_theme and obj.theme_name != '': 742 self.show_themes_for_screenlet(obj) 743 else: 744 self.page_themes.hide()
745
746 - def show_themes_for_screenlet (self, obj):
747 """Update the Themes-page to display the available themes for the 748 given Screenlet-object.""" 749 # list with found themes 750 found_themes = [] 751 # now check all paths for themes 752 for path in screenlets.SCREENLETS_PATH: 753 p = path + '/' + obj.get_short_name() + '/themes' 754 print p 755 #p = '/usr/local/share/screenlets/Clock/themes' # TEMP!!! 756 try: 757 dircontent = os.listdir(p) 758 except: 759 print "Path %s not found." % p 760 continue 761 # check all themes in path 762 for name in dircontent: 763 # load themes with the same name only once 764 if found_themes.count(name): 765 continue 766 found_themes.append(name) 767 # build full path of theme.conf 768 theme_conf = p + '/' + name + '/theme.conf' 769 # if dir contains a theme.conf 770 if os.access(theme_conf, os.F_OK): 771 # load it and create new list entry 772 ini = screenlets.utils.IniReader() 773 if ini.load(theme_conf): 774 # check for section 775 if ini.has_section('Theme'): 776 # get metainfo from theme 777 th_fullname = ini.get_option('name', 778 section='Theme') 779 th_info = ini.get_option('info', 780 section='Theme') 781 th_version = ini.get_option('version', 782 section='Theme') 783 th_author = ini.get_option('author', 784 section='Theme') 785 # create array from metainfo and add it to liststore 786 info = [name, th_fullname, th_info, th_author, 787 th_version] 788 self.liststore.append([info]) 789 else: 790 # no theme.conf in dir? just add theme-name 791 self.liststore.append([[name, '-', '-', '-', '-']]) 792 # is it the active theme? 793 if name == obj.theme_name: 794 # select it in tree 795 print "active theme is: %s" % name 796 sel = self.tree.get_selection() 797 if sel: 798 it = self.liststore.get_iter_from_string(\ 799 str(len(self.liststore)-1)) 800 if it: 801 sel.select_iter(it)
802 803 # UI-creation 804
805 - def create_about_page (self):
806 """Create the "About"-tab.""" 807 self.page_about = gtk.HBox() 808 # create about box 809 self.hbox_about = gtk.HBox() 810 self.hbox_about.show() 811 self.page_about.add(self.hbox_about) 812 # create icon 813 self.infoicon = gtk.Image() 814 self.infoicon.show() 815 self.page_about.pack_start(self.infoicon, 0, 1, 10) 816 # create infotext 817 self.infotext = gtk.Label() 818 self.infotext.use_markup = True 819 self.infotext.set_line_wrap(True) 820 self.infotext.set_alignment(0.0, 0.0) 821 self.infotext.show() 822 self.page_about.pack_start(self.infotext, 1, 1, 5) 823 # add page 824 self.page_about.show() 825 self.main_notebook.append_page(self.page_about, gtk.Label('About '))
826
827 - def create_options_page (self):
828 """Create the "Options"-tab.""" 829 self.page_options = gtk.HBox() 830 # create vbox for options-editor 831 self.vbox_editor = gtk.VBox(spacing=3) 832 self.vbox_editor.set_border_width(5) 833 self.vbox_editor.show() 834 self.page_options.add(self.vbox_editor) 835 # show/add page 836 self.page_options.show() 837 self.main_notebook.append_page(self.page_options, gtk.Label('Options '))
838
839 - def create_themes_page (self):
840 """Create the "Themes"-tab.""" 841 self.page_themes = gtk.VBox(spacing=5) 842 self.page_themes.set_border_width(10) 843 # create info-text list 844 txt = gtk.Label('Themes allow you to easily switch the appearance of your Screenlets. On this page you find a list of all available themes for this Screenlet.') 845 txt.set_size_request(450, -1) 846 txt.set_line_wrap(True) 847 txt.set_alignment(0.0, 0.0) 848 txt.show() 849 self.page_themes.pack_start(txt, False, True) 850 # create theme-selector list 851 self.tree.set_headers_visible(False) 852 self.tree.connect('cursor-changed', self.__tree_cursor_changed) 853 self.tree.show() 854 col = gtk.TreeViewColumn('') 855 cell = gtk.CellRendererText() 856 col.pack_start(cell, True) 857 #cell.set_property('foreground', 'black') 858 col.set_cell_data_func(cell, self.__render_cell) 859 self.tree.append_column(col) 860 # wrap tree in scrollwin 861 sw = gtk.ScrolledWindow() 862 sw.set_shadow_type(gtk.SHADOW_IN) 863 sw.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC) 864 sw.add(self.tree) 865 sw.show() 866 # add vbox and add tree/buttons 867 vbox = gtk.VBox() 868 vbox.pack_start(sw, True, True) 869 vbox.show() 870 # show/add page 871 self.page_themes.add(vbox) 872 self.page_themes.show() 873 self.main_notebook.append_page(self.page_themes, gtk.Label('Themes '))
874
875 - def __render_cell(self, tvcolumn, cell, model, iter):
876 """Callback for rendering the cells in the theme-treeview.""" 877 # get attributes-list from Treemodel 878 attrib = model.get_value(iter, 0) 879 # set colors depending on state 880 col = '555555' 881 name_uc = attrib[0][0].upper() + attrib[0][1:] 882 # create markup depending on info 883 if attrib[1] == '-' and attrib[2] == '-': 884 mu = '<b><span weight="ultrabold" size="large">' + name_uc + \ 885 '</span></b> (no info available)' 886 else: 887 if attrib[1] == None : attrib[1] = '-' 888 if attrib[2] == None : attrib[2] = '-' 889 if attrib[3] == None : attrib[3] = '-' 890 if attrib[4] == None : attrib[4] = '-' 891 mu = '<b><span weight="ultrabold" size="large">' + name_uc + \ 892 '</span></b> v' + attrib[4] + '\n<small><span color="#555555' +\ 893 '">' + attrib[2].replace('\\n', '\n') + \ 894 '</span></small>\n<i><small>by '+str(attrib[3])+'</small></i>' 895 # set markup 896 cell.set_property('markup', mu)
897 898 # UI-callbacks 899
900 - def __tree_cursor_changed (self, treeview):
901 """Callback for handling selection changes in the Themes-treeview.""" 902 sel = self.tree.get_selection() 903 if sel: 904 s = sel.get_selected() 905 if s: 906 it = s[1] 907 if it: 908 attribs = self.liststore.get_value(it, 0) 909 if attribs and self.__shown_object: 910 #print attribs 911 # set theme in Screenlet (if not already active) 912 if self.__shown_object.theme_name != attribs[0]: 913 self.__shown_object.theme_name = attribs[0]
914 915 # option-widget creation (should be split in several classes) 916
917 - def get_widget_for_option (self, option, value=None):
918 """Return a gtk.*Widget with Label within a HBox for a given option. 919 NOTE: This is incredibly ugly, ideally all Option-subclasses should 920 have their own widgets - like StringOptionWidget, ColorOptionWidget, 921 ... and then be simply created dynamically""" 922 t = option.__class__ 923 widget = None 924 if t == BoolOption: 925 widget = gtk.CheckButton() 926 widget.set_active(value) 927 widget.connect("toggled", self.options_callback, option) 928 elif t == StringOption: 929 if option.choices: 930 # if a list of values is defined, show combobox 931 widget = gtk.combo_box_new_text() 932 p = -1 933 i = 0 934 for s in option.choices: 935 widget.append_text(s) 936 if s==value: 937 p = i 938 i+=1 939 widget.set_active(p) 940 #widget.connect("changed", self.options_callback, option) 941 else: 942 widget = gtk.Entry() 943 widget.set_text(value) 944 # if it is a password, set text to be invisible 945 if option.password: 946 widget.set_visibility(False) 947 #widget.connect("key-press-event", self.options_callback, option) 948 widget.connect("changed", self.options_callback, option) 949 #widget.set_size_request(180, 28) 950 elif t == IntOption or t == FloatOption: 951 widget = gtk.SpinButton() 952 #widget.set_size_request(50, 22) 953 #widget.set_text(str(value)) 954 if t == FloatOption: 955 widget.set_digits(option.digits) 956 widget.set_increments(option.increment, int(option.max/option.increment)) 957 else: 958 widget.set_increments(option.increment, int(option.max/option.increment)) 959 if option.min!=None and option.max!=None: 960 #print "Setting range for input to: %f, %f" % (option.min, option.max) 961 widget.set_range(option.min, option.max) 962 widget.set_value(value) 963 widget.connect("value-changed", self.options_callback, option) 964 elif t == ColorOption: 965 widget = gtk.ColorButton(gtk.gdk.Color(int(value[0]*65535), int(value[1]*65535), int(value[2]*65535))) 966 widget.set_use_alpha(True) 967 print value 968 print value[3] 969 widget.set_alpha(int(value[3]*65535)) 970 widget.connect("color-set", self.options_callback, option) 971 elif t == FontOption: 972 widget = gtk.FontButton() 973 widget.set_font_name(value) 974 widget.connect("font-set", self.options_callback, option) 975 elif t == FileOption: 976 widget = gtk.FileChooserButton("Choose File") 977 widget.set_filename(value) 978 widget.set_size_request(180, 28) 979 widget.connect("selection-changed", self.options_callback, option) 980 elif t == DirectoryOption: 981 dlg = gtk.FileChooserDialog(buttons=(gtk.STOCK_CANCEL, 982 gtk.RESPONSE_CANCEL, gtk.STOCK_OPEN, gtk.RESPONSE_OK), 983 action=gtk.FILE_CHOOSER_ACTION_SELECT_FOLDER) 984 widget = gtk.FileChooserButton(dlg) 985 widget.set_title("Choose Directory") 986 widget.set_filename(value) 987 widget.set_size_request(180, 28) 988 widget.connect("selection-changed", self.options_callback, option) 989 elif t == ImageOption: 990 # create entry and button (entry is hidden) 991 entry = gtk.Entry() 992 entry.set_text(value) 993 entry.set_editable(False) 994 but = gtk.Button('') 995 # util to reload preview image 996 def create_preview (filename): 997 if filename and os.path.isfile(filename): 998 pb = gtk.gdk.pixbuf_new_from_file_at_size(filename, 64, -1) 999 if pb: 1000 img = gtk.Image() 1001 img.set_from_pixbuf(pb) 1002 return img 1003 img = gtk.image_new_from_stock(gtk.STOCK_MISSING_IMAGE, 1004 gtk.ICON_SIZE_LARGE_TOOLBAR) 1005 img.set_size_request(64, 64) 1006 return img
1007 # create button 1008 def but_callback (widget): 1009 dlg = gtk.FileChooserDialog(buttons=(gtk.STOCK_CANCEL, 1010 gtk.RESPONSE_CANCEL, gtk.STOCK_OPEN, gtk.RESPONSE_OK)) 1011 dlg.set_title("Choose Image") 1012 dlg.set_keep_above(True) 1013 dlg.set_filename(entry.get_text()) 1014 flt = gtk.FileFilter() 1015 flt.add_pixbuf_formats() 1016 dlg.set_filter(flt) 1017 prev = gtk.Image() 1018 box = gtk.VBox() 1019 box.set_size_request(150, -1) 1020 box.add(prev) 1021 prev.show() 1022 # add preview widget to filechooser 1023 def preview_callback(widget): 1024 fname = dlg.get_preview_filename() 1025 if fname and os.path.isfile(fname): 1026 pb = gtk.gdk.pixbuf_new_from_file_at_size(fname, 150, -1) 1027 if pb: 1028 prev.set_from_pixbuf(pb) 1029 dlg.set_preview_widget_active(True) 1030 else: 1031 dlg.set_preview_widget_active(False)
1032 dlg.set_preview_widget_active(True) 1033 dlg.connect('selection-changed', preview_callback) 1034 dlg.set_preview_widget(box) 1035 # run 1036 response = dlg.run() 1037 if response == gtk.RESPONSE_OK: 1038 entry.set_text(dlg.get_filename()) 1039 but.set_image(create_preview(dlg.get_filename())) 1040 self.options_callback(dlg, option) 1041 dlg.destroy() 1042 # load preview image 1043 but.set_image(create_preview(value)) 1044 but.connect('clicked', but_callback) 1045 # create widget 1046 widget = gtk.HBox() 1047 widget.add(entry) 1048 widget.add(but) 1049 but.show() 1050 widget.show() 1051 # add tooltips 1052 #self.tooltips.set_tip(but, 'Select Image ...') 1053 self.tooltips.set_tip(but, option.desc) 1054 elif t == ListOption: 1055 entry= gtk.Entry() 1056 entry.set_editable(False) 1057 entry.set_text(str(value)) 1058 entry.show() 1059 img = gtk.Image() 1060 img.set_from_stock(gtk.STOCK_EDIT, 1) 1061 but = gtk.Button('') 1062 but.set_image(img) 1063 def open_listeditor(event): 1064 # open dialog 1065 dlg = ListOptionDialog() 1066 # read string from entry and import it through option-class 1067 # (this is needed to always have an up-to-date value) 1068 dlg.set_list(option.on_import(entry.get_text())) 1069 resp = dlg.run() 1070 if resp == gtk.RESPONSE_OK: 1071 # set text in entry 1072 entry.set_text(str(dlg.get_list())) 1073 # manually call the options-callback 1074 self.options_callback(dlg, option) 1075 dlg.destroy() 1076 but.show() 1077 but.connect("clicked", open_listeditor) 1078 self.tooltips.set_tip(but, 'Open List-Editor ...') 1079 self.tooltips.set_tip(entry, option.desc) 1080 widget = gtk.HBox() 1081 widget.add(entry) 1082 widget.add(but) 1083 elif t == AccountOption: 1084 widget = gtk.HBox() 1085 vb = gtk.VBox() 1086 input_name = gtk.Entry() 1087 input_name.set_text(value[0]) 1088 input_name.show() 1089 input_pass = gtk.Entry() 1090 input_pass.set_visibility(False) # password 1091 input_pass.set_text(value[1]) 1092 input_pass.show() 1093 but = gtk.Button('Apply', gtk.STOCK_APPLY) 1094 but.show() 1095 but.connect("clicked", self.apply_options_callback, option, widget) 1096 vb.add(input_name) 1097 vb.add(input_pass) 1098 vb.show() 1099 self.tooltips.set_tip(but, 'Apply username/password ...') 1100 self.tooltips.set_tip(input_name, 'Enter username here ...') 1101 self.tooltips.set_tip(input_pass, 'Enter password here ...') 1102 widget.add(vb) 1103 widget.add(but) 1104 elif t == TimeOption: 1105 widget = gtk.HBox() 1106 input_hour = gtk.SpinButton()#climb_rate=1.0) 1107 input_minute = gtk.SpinButton() 1108 input_second = gtk.SpinButton() 1109 input_hour.set_range(0, 23) 1110 input_hour.set_max_length(2) 1111 input_hour.set_increments(1, 1) 1112 input_hour.set_numeric(True) 1113 input_hour.set_value(value[0]) 1114 input_minute.set_range(0, 59) 1115 input_minute.set_max_length(2) 1116 input_minute.set_increments(1, 1) 1117 input_minute.set_numeric(True) 1118 input_minute.set_value(value[1]) 1119 input_second.set_range(0, 59) 1120 input_second.set_max_length(2) 1121 input_second.set_increments(1, 1) 1122 input_second.set_numeric(True) 1123 input_second.set_value(value[2]) 1124 input_hour.connect('value-changed', self.options_callback, option) 1125 input_minute.connect('value-changed', self.options_callback, option) 1126 input_second.connect('value-changed', self.options_callback, option) 1127 self.tooltips.set_tip(input_hour, option.desc) 1128 self.tooltips.set_tip(input_minute, option.desc) 1129 self.tooltips.set_tip(input_second, option.desc) 1130 widget.add(input_hour) 1131 widget.add(gtk.Label(':')) 1132 widget.add(input_minute) 1133 widget.add(gtk.Label(':')) 1134 widget.add(input_second) 1135 widget.add(gtk.Label('h')) 1136 widget.show_all() 1137 else: 1138 widget = gtk.Entry() 1139 print "unsupported type '"+str(t)+"'" 1140 hbox = gtk.HBox() 1141 label = gtk.Label() 1142 label.set_alignment(0.0, 0.0) 1143 label.set_label(option.label) 1144 label.set_size_request(180, 28) 1145 label.show() 1146 hbox.pack_start(label, 0, 1) 1147 if widget: 1148 if option.disabled: # option disabled? 1149 widget.set_sensitive(False) 1150 label.set_sensitive(False) 1151 #label.set_mnemonic_widget(widget) 1152 self.tooltips.set_tip(widget, option.desc) 1153 widget.show() 1154 # check if needs Apply-button 1155 if option.realtime == False: 1156 but = gtk.Button('Apply', gtk.STOCK_APPLY) 1157 but.show() 1158 but.connect("clicked", self.apply_options_callback, 1159 option, widget) 1160 b = gtk.HBox() 1161 b.show() 1162 b.pack_start(widget, 0, 0) 1163 b.pack_start(but, 0, 0) 1164 hbox.pack_start(b, 0, 0) 1165 else: 1166 #hbox.pack_start(widget, -1, 1) 1167 hbox.pack_start(widget, 0, 0) 1168 return hbox 1169
1170 - def read_option_from_widget (self, widget, option):
1171 """Read an option's value from the widget and return it.""" 1172 if not widget.window: 1173 return False 1174 # get type of option and read the widget's value 1175 val = None 1176 t = option.__class__ 1177 if t == IntOption: 1178 val = int(widget.get_value()) 1179 elif t == FloatOption: 1180 val = widget.get_value() 1181 elif t == StringOption: 1182 if option.choices: 1183 # if default is a list, handle combobox 1184 val = widget.get_active_text() 1185 else: 1186 val = widget.get_text() 1187 elif t == BoolOption: 1188 val = widget.get_active() 1189 elif t == ColorOption: 1190 col = widget.get_color() 1191 al = widget.get_alpha() 1192 val = (col.red/65535.0, col.green/65535.0, 1193 col.blue/65535.0, al/65535.0) 1194 elif t == FontOption: 1195 val = widget.get_font_name() 1196 elif t == FileOption or t == DirectoryOption or t == ImageOption: 1197 val = widget.get_filename() 1198 #print widget 1199 #elif t == ImageOption: 1200 # val = widget.get_text() 1201 elif t == ListOption: 1202 # the widget is a ListOptionDialog here 1203 val = widget.get_list() 1204 elif t == AccountOption: 1205 # the widget is a HBox containing a VBox containing two Entries 1206 # (ideally we should have a custom widget for the AccountOption) 1207 for c in widget.get_children(): 1208 if c.__class__ == gtk.VBox: 1209 c2 = c.get_children() 1210 val = (c2[0].get_text(), c2[1].get_text()) 1211 elif t == TimeOption: 1212 box = widget.get_parent() 1213 inputs = box.get_children() 1214 val = (int(inputs[0].get_value()), int(inputs[2].get_value()), 1215 int(inputs[4].get_value())) 1216 else: 1217 print "OptionsDialog: Unknown option type: " + str(t) 1218 return None 1219 # return the value 1220 return val
1221 1222 # option-widget event-handling 1223 1224 # TODO: custom callback/signal for each option?
1225 - def options_callback (self, widget, optionobj):
1226 """Callback for handling changed-events on entries.""" 1227 print "Changed: "+optionobj.name 1228 if self.__shown_object: 1229 # if the option is not real-time updated, 1230 if optionobj.realtime == False: 1231 return False 1232 # read option 1233 val = self.read_option_from_widget(widget, optionobj) 1234 if val != None: 1235 #print "SetOption: "+optionobj.name+"="+str(val) 1236 # set option 1237 setattr(self.__shown_object, optionobj.name, val) 1238 # notify option-object's on_changed-handler 1239 optionobj.emit("option_changed", optionobj) 1240 return False
1241
1242 - def apply_options_callback (self, widget, optionobj, entry):
1243 """Callback for handling Apply-button presses.""" 1244 if self.__shown_object: 1245 # read option 1246 val = self.read_option_from_widget(entry, optionobj) 1247 if val != None: 1248 #print "SetOption: "+optionobj.name+"="+str(val) 1249 # set option 1250 setattr(self.__shown_object, optionobj.name, val) 1251 # notify option-object's on_changed-handler 1252 optionobj.emit("option_changed", optionobj) 1253 return False
1254 1255 1256 1257 # ------ ONLY FOR TESTING ------------------: 1258 if __name__ == "__main__": 1259 1260 import os 1261 1262 # this is only for testing - should be a Screenlet
1263 - class TestObject (EditableOptions):
1264 1265 testlist = ['test1', 'test2', 3, 5, 'Noch ein Test'] 1266 pop3_account = ('Username', '') 1267 1268 # TEST 1269 pin_x = 100 1270 pin_y = 6 1271 text_x = 19 1272 text_y = 35 1273 font_name = 'Sans 12' 1274 rgba_color = (0.0, 0.0, 1.0, 1.0) 1275 text_prefix = '<b>' 1276 text_suffix = '</b>' 1277 note_text = "" # hidden option because val has its own editing-dialog 1278 random_pin_pos = True 1279 opt1 = 'testval 1' 1280 opt2 = 'testval 2' 1281 filename2 = '' 1282 filename = '' 1283 dirname = '' 1284 font = 'Sans 12' 1285 color = (0.1, 0.5, 0.9, 0.9) 1286 name = 'a name' 1287 name2 = 'another name' 1288 combo_test = 'el2' 1289 flt = 0.5 1290 x = 10 1291 y = 25 1292 width = 30 1293 height = 50 1294 is_sticky = False 1295 is_widget = False 1296 time = (12, 32, 49) # a time-value (tuple with ints) 1297
1298 - def __init__ (self):
1299 EditableOptions.__init__(self) 1300 # Add group 1301 self.add_options_group('General', 1302 'The general options for this Object ...') 1303 self.add_options_group('Window', 1304 'The Window-related options for this Object ...') 1305 self.add_options_group('Test', 'A Test-group ...') 1306 # Add editable options 1307 self.add_option(ListOption('Test', 'testlist', self.testlist, 1308 'ListOption-Test', 'Testing a ListOption-type ...')) 1309 self.add_option(StringOption('Window', 'name', 'TESTNAME', 1310 'Testname', 'The name/id of this Screenlet-instance ...'), 1311 realtime=False) 1312 self.add_option(AccountOption('Test', 'pop3_account', 1313 self.pop3_account, 'Username/Password', 1314 'Enter username/password here ...')) 1315 self.add_option(StringOption('Window', 'name2', 'TESTNAME2', 1316 'String2', 'Another string-test ...')) 1317 self.add_option(StringOption('Test', 'combo_test', "el1", 'Combo', 1318 'A StringOption displaying a drop-down-list with choices...', 1319 choices=['el1', 'el2', 'element 3'])) 1320 self.add_option(FloatOption('General', 'flt', 30, 1321 'A Float', 'Testing a FLOAT-type ...', 1322 min=0, max=gtk.gdk.screen_width(), increment=0.01, digits=4)) 1323 self.add_option(IntOption('General', 'x', 30, 1324 'X-Position', 'The X-position of this Screenlet ...', 1325 min=0, max=gtk.gdk.screen_width())) 1326 self.add_option(IntOption('General', 'y', 30, 1327 'Y-Position', 'The Y-position of this Screenlet ...', 1328 min=0, max=gtk.gdk.screen_height())) 1329 self.add_option(IntOption('Test', 'width', 300, 1330 'Width', 'The width of this Screenlet ...', min=100, max=1000)) 1331 self.add_option(IntOption('Test', 'height', 150, 1332 'Height', 'The height of this Screenlet ...', 1333 min=100, max=1000)) 1334 self.add_option(BoolOption('General', 'is_sticky', True, 1335 'Stick to Desktop', 'Show this Screenlet always ...')) 1336 self.add_option(BoolOption('General', 'is_widget', False, 1337 'Treat as Widget', 'Treat this Screenlet as a "Widget" ...')) 1338 self.add_option(FontOption('Test', 'font', 'Sans 14', 1339 'Font', 'The font for whatever ...')) 1340 self.add_option(ColorOption('Test', 'color', (1, 0.35, 0.35, 0.7), 1341 'Color', 'The color for whatever ...')) 1342 self.add_option(FileOption('Test', 'filename', os.environ['HOME'], 1343 'Filename-Test', 'Testing a FileOption-type ...', 1344 patterns=['*.py', '*.pyc'])) 1345 self.add_option(ImageOption('Test', 'filename2', os.environ['HOME'], 1346 'Image-Test', 'Testing the ImageOption-type ...')) 1347 self.add_option(DirectoryOption('Test', 'dirname', os.environ['HOME'], 1348 'Directory-Test', 'Testing a FileOption-type ...')) 1349 self.add_option(TimeOption('Test','time', self.time, 1350 'TimeOption-Test', 'Testing a TimeOption-type ...')) 1351 # TEST 1352 self.disable_option('width') 1353 self.disable_option('height')
1354 # TEST: load options from file 1355 #self.add_options_from_file('/home/ryx/Desktop/python/screenlets/screenlets-0.0.9/src/share/screenlets/Notes/options.xml') 1356
1357 - def __setattr__(self, name, value):
1358 self.__dict__[name] = value 1359 print name + "=" + str(value)
1360
1361 - def get_short_name(self):
1362 return self.__class__.__name__[:-6]
1363 1364 1365 1366 # this is only for testing - should be a Screenlet
1367 - class TestChildObject (TestObject):
1368 1369 uses_theme = True 1370 theme_name = 'test' 1371
1372 - def __init__ (self):
1373 TestObject.__init__(self) 1374 self.add_option(StringOption('Test', 'anothertest', 'ksjhsjgd', 1375 'Another Test', 'An attribute in the subclass ...')) 1376 self.add_option(StringOption('Test', 'theme_name', self.theme_name, 1377 'Theme', 'The theme for this Screenelt ...', 1378 choices=['test1', 'test2', 'mytheme', 'blue', 'test']))
1379 1380 1381 # TEST: load/save 1382 # TEST: option-editing 1383 to = TestChildObject() 1384 #print to.export_options_as_list() 1385 se = OptionsDialog(500, 380)#, treeview=True) 1386 #img = gtk.image_new_from_stock(gtk.STOCK_ABOUT, 5) 1387 img = gtk.Image() 1388 img.set_from_file('../share/screenlets/Notes/icon.svg') 1389 se.set_info('TestOptions', 1390 'A test for an extended options-dialog with embedded about-info.' + 1391 ' Can be used for the Screenlets to have all in one ...\nNOTE:' + 1392 '<span color="red"> ONLY A TEST!</span>', 1393 '(c) RYX 2007', version='v0.0.1', icon=img) 1394 se.show_options_for_object(to) 1395 resp = se.run() 1396 if resp == gtk.RESPONSE_OK: 1397 print "OK" 1398 else: 1399 print "Cancelled." 1400 se.destroy() 1401 print to.export_options_as_list() 1402