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