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=0, max=0, 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 # crete tooltips-object 681 self.tooltips = gtk.Tooltips()
682 683 # "public" functions 684
685 - def reset_to_defaults (self):
686 """Reset all entries for the currently shown object to their default 687 values (the values the object has when it is first created). 688 NOTE: This function resets ALL options, so BE CARFEUL!""" 689 if self.__shown_object: 690 for o in self.__shown_object.__options__: 691 # set default value 692 setattr(self.__shown_object, o.name, o.default)
693
694 - def set_info (self, name, info, copyright='', version='', icon=None):
695 """Update the "About"-page with the given information.""" 696 # convert infotext (remove EOLs and TABs) 697 info = info.replace("\n", "") 698 info = info.replace("\t", " ") 699 # create markup 700 markup = '\n<b><span size="xx-large">' + name + '</span></b>' 701 if version: 702 markup += ' <span size="large"><b>' + version + '</b></span>' 703 markup += '\n\n'+info+'\n<span size="small">\n'+copyright+'</span>' 704 self.infotext.set_markup(markup) 705 # icon? 706 if icon: 707 # remove old icon 708 if self.infoicon: 709 self.infoicon.destroy() 710 # set new icon 711 self.infoicon = icon 712 self.infoicon.set_alignment(0.0, 0.10) 713 self.infoicon.show() 714 self.hbox_about.pack_start(self.infoicon, 0, 1, 10) 715 else: 716 self.infoicon.hide()
717
718 - def show_options_for_object (self, obj):
719 """Update the OptionsEditor to show the options for the given Object. 720 The Object needs to be an EditableOptions-subclass. 721 NOTE: This needs heavy improvement and should use OptionGroups once 722 they exist""" 723 self.__shown_object = obj 724 # create notebook for groups 725 notebook = gtk.Notebook() 726 self.vbox_editor.add(notebook) 727 for group in obj.__options_groups_ordered__: 728 group_data = obj.__options_groups__[group] 729 # create box for tab-page 730 page = gtk.VBox() 731 page.set_border_width(10) 732 if group_data['info'] != '': 733 info = gtk.Label(group_data['info']) 734 info.show() 735 info.set_alignment(0, 0) 736 page.pack_start(info, 0, 0, 7) 737 sep = gtk.HSeparator() 738 sep.show() 739 #page.pack_start(sep, 0, 0, 5) 740 # create VBox for inputs 741 box = gtk.VBox() 742 box.show() 743 box.set_border_width(5) 744 # add box to page 745 page.add(box) 746 page.show() 747 # add new notebook-page 748 label = gtk.Label(group_data['label']) 749 label.show() 750 notebook.append_page(page, label) 751 # and create inputs 752 for option in group_data['options']: 753 if option.hidden == False: 754 val = getattr(obj, option.name)#obj.__dict__[option.name] 755 w = self.get_widget_for_option(option, val) 756 if w: 757 box.pack_start(w, 0, 0) 758 w.show() 759 notebook.show() 760 # show/hide themes tab, depending on whether the screenlet uses themes 761 if obj.uses_theme and obj.theme_name != '': 762 self.show_themes_for_screenlet(obj) 763 else: 764 self.page_themes.hide()
765
766 - def show_themes_for_screenlet (self, obj):
767 """Update the Themes-page to display the available themes for the 768 given Screenlet-object.""" 769 770 771 dircontent = [] 772 screenlets.utils.refresh_available_screenlet_paths() 773 774 for path in screenlets.SCREENLETS_PATH: 775 p = path + '/' + obj.get_short_name() + '/themes' 776 print p 777 #p = '/usr/local/share/screenlets/Clock/themes' # TEMP!!! 778 try: 779 dc = os.listdir(p) 780 for d in dc: 781 dircontent.append({'name':d, 'path':p+'/'}) 782 except: 783 print "Path %s not found." % p 784 785 # list with found themes 786 found_themes = [] 787 788 # check all themes in path 789 for elem in dircontent: 790 # load themes with the same name only once 791 if found_themes.count(elem['name']): 792 continue 793 found_themes.append(elem['name']) 794 # build full path of theme.conf 795 theme_conf = elem['path'] + elem['name'] + '/theme.conf' 796 # if dir contains a theme.conf 797 if os.access(theme_conf, os.F_OK): 798 # load it and create new list entry 799 ini = screenlets.utils.IniReader() 800 if ini.load(theme_conf): 801 # check for section 802 if ini.has_section('Theme'): 803 # get metainfo from theme 804 th_fullname = ini.get_option('name', 805 section='Theme') 806 th_info = ini.get_option('info', 807 section='Theme') 808 th_version = ini.get_option('version', 809 section='Theme') 810 th_author = ini.get_option('author', 811 section='Theme') 812 # create array from metainfo and add it to liststore 813 info = [elem['name'], th_fullname, th_info, th_author, 814 th_version] 815 self.liststore.append([info]) 816 else: 817 # no theme section in theme.conf just add theme-name 818 self.liststore.append([[elem['name'], '-', '-', '-', '-']]) 819 else: 820 # no theme.conf in dir? just add theme-name 821 self.liststore.append([[elem['name'], '-', '-', '-', '-']]) 822 # is it the active theme? 823 if elem['name'] == obj.theme_name: 824 # select it in tree 825 print "active theme is: %s" % elem['name'] 826 sel = self.tree.get_selection() 827 if sel: 828 it = self.liststore.get_iter_from_string(\ 829 str(len(self.liststore)-1)) 830 if it: 831 sel.select_iter(it)
832 833 # UI-creation 834
835 - def create_about_page (self):
836 """Create the "About"-tab.""" 837 self.page_about = gtk.HBox() 838 # create about box 839 self.hbox_about = gtk.HBox() 840 self.hbox_about.show() 841 self.page_about.add(self.hbox_about) 842 # create icon 843 self.infoicon = gtk.Image() 844 self.infoicon.show() 845 self.page_about.pack_start(self.infoicon, 0, 1, 10) 846 # create infotext 847 self.infotext = gtk.Label() 848 self.infotext.use_markup = True 849 self.infotext.set_line_wrap(True) 850 self.infotext.set_alignment(0.0, 0.0) 851 self.infotext.show() 852 self.page_about.pack_start(self.infotext, 1, 1, 5) 853 # add page 854 self.page_about.show() 855 self.main_notebook.append_page(self.page_about, gtk.Label(_('About ')))
856
857 - def create_options_page (self):
858 """Create the "Options"-tab.""" 859 self.page_options = gtk.HBox() 860 # create vbox for options-editor 861 self.vbox_editor = gtk.VBox(spacing=3) 862 self.vbox_editor.set_border_width(5) 863 self.vbox_editor.show() 864 self.page_options.add(self.vbox_editor) 865 # show/add page 866 self.page_options.show() 867 self.main_notebook.append_page(self.page_options, gtk.Label(_('Options ')))
868
869 - def create_themes_page (self):
870 """Create the "Themes"-tab.""" 871 self.page_themes = gtk.VBox(spacing=5) 872 self.page_themes.set_border_width(10) 873 # create info-text list 874 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.')) 875 txt.set_size_request(450, -1) 876 txt.set_line_wrap(True) 877 txt.set_alignment(0.0, 0.0) 878 txt.show() 879 self.page_themes.pack_start(txt, False, True) 880 # create theme-selector list 881 self.tree.set_headers_visible(False) 882 self.tree.connect('cursor-changed', self.__tree_cursor_changed) 883 self.tree.show() 884 col = gtk.TreeViewColumn('') 885 cell = gtk.CellRendererText() 886 col.pack_start(cell, True) 887 #cell.set_property('foreground', 'black') 888 col.set_cell_data_func(cell, self.__render_cell) 889 self.tree.append_column(col) 890 # wrap tree in scrollwin 891 sw = gtk.ScrolledWindow() 892 sw.set_shadow_type(gtk.SHADOW_IN) 893 sw.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC) 894 sw.add(self.tree) 895 sw.show() 896 # add vbox and add tree/buttons 897 vbox = gtk.VBox() 898 vbox.pack_start(sw, True, True) 899 vbox.show() 900 # show/add page 901 self.page_themes.add(vbox) 902 self.page_themes.show() 903 self.main_notebook.append_page(self.page_themes, gtk.Label(_('Themes ')))
904
905 - def __render_cell(self, tvcolumn, cell, model, iter):
906 """Callback for rendering the cells in the theme-treeview.""" 907 # get attributes-list from Treemodel 908 attrib = model.get_value(iter, 0) 909 910 # set colors depending on state 911 col = '555555' 912 name_uc = attrib[0][0].upper() + attrib[0][1:] 913 # create markup depending on info 914 if attrib[1] == '-' and attrib[2] == '-': 915 mu = '<b><span weight="ultrabold" size="large">' + name_uc + \ 916 '</span></b> (' + _('no info available') + ')' 917 else: 918 if attrib[1] == None : attrib[1] = '-' 919 if attrib[2] == None : attrib[2] = '-' 920 if attrib[3] == None : attrib[3] = '-' 921 if attrib[4] == None : attrib[4] = '-' 922 mu = '<b><span weight="ultrabold" size="large">' + name_uc + \ 923 '</span></b> v' + attrib[4] + '\n<small><span color="#555555' +\ 924 '">' + attrib[2].replace('\\n', '\n') + \ 925 '</span></small>\n<i><small>by '+str(attrib[3])+'</small></i>' 926 # set markup 927 cell.set_property('markup', mu)
928 929 # UI-callbacks 930
931 - def __tree_cursor_changed (self, treeview):
932 """Callback for handling selection changes in the Themes-treeview.""" 933 sel = self.tree.get_selection() 934 if sel: 935 s = sel.get_selected() 936 if s: 937 it = s[1] 938 if it: 939 attribs = self.liststore.get_value(it, 0) 940 if attribs and self.__shown_object: 941 #print attribs 942 # set theme in Screenlet (if not already active) 943 if self.__shown_object.theme_name != attribs[0]: 944 self.__shown_object.theme_name = attribs[0]
945 946 # option-widget creation (should be split in several classes) 947
948 - def get_widget_for_option (self, option, value=None):
949 """Return a gtk.*Widget with Label within a HBox for a given option. 950 NOTE: This is incredibly ugly, ideally all Option-subclasses should 951 have their own widgets - like StringOptionWidget, ColorOptionWidget, 952 ... and then be simply created dynamically""" 953 t = option.__class__ 954 widget = None 955 if t == BoolOption: 956 widget = gtk.CheckButton() 957 widget.set_active(value) 958 widget.connect("toggled", self.options_callback, option) 959 elif t == StringOption: 960 if option.choices: 961 # if a list of values is defined, show combobox 962 widget = gtk.combo_box_new_text() 963 p = -1 964 i = 0 965 for s in option.choices: 966 widget.append_text(s) 967 if s==value: 968 p = i 969 i+=1 970 widget.set_active(p) 971 #widget.connect("changed", self.options_callback, option) 972 else: 973 widget = gtk.Entry() 974 widget.set_text(value) 975 # if it is a password, set text to be invisible 976 if option.password: 977 widget.set_visibility(False) 978 #widget.connect("key-press-event", self.options_callback, option) 979 widget.connect("changed", self.options_callback, option) 980 #widget.set_size_request(180, 28) 981 elif t == IntOption or t == FloatOption: 982 widget = gtk.SpinButton() 983 #widget.set_size_request(50, 22) 984 #widget.set_text(str(value)) 985 if t == FloatOption: 986 widget.set_digits(option.digits) 987 widget.set_increments(option.increment, int(option.max/option.increment)) 988 else: 989 widget.set_increments(option.increment, int(option.max/option.increment)) 990 if option.min!=None and option.max!=None: 991 #print "Setting range for input to: %f, %f" % (option.min, option.max) 992 widget.set_range(option.min, option.max) 993 widget.set_value(value) 994 widget.connect("value-changed", self.options_callback, option) 995 elif t == ColorOption: 996 widget = gtk.ColorButton(gtk.gdk.Color(int(value[0]*65535), int(value[1]*65535), int(value[2]*65535))) 997 widget.set_use_alpha(True) 998 # print value 999 # print value[3] 1000 widget.set_alpha(int(value[3]*65535)) 1001 widget.connect("color-set", self.options_callback, option) 1002 elif t == FontOption: 1003 widget = gtk.FontButton() 1004 widget.set_font_name(value) 1005 widget.connect("font-set", self.options_callback, option) 1006 elif t == FileOption: 1007 widget = gtk.FileChooserButton(_("Choose File")) 1008 widget.set_filename(value) 1009 widget.set_size_request(180, 28) 1010 widget.connect("selection-changed", self.options_callback, option) 1011 elif t == DirectoryOption: 1012 dlg = gtk.FileChooserDialog(buttons=(gtk.STOCK_CANCEL, 1013 gtk.RESPONSE_CANCEL, gtk.STOCK_OPEN, gtk.RESPONSE_OK), 1014 action=gtk.FILE_CHOOSER_ACTION_SELECT_FOLDER) 1015 widget = gtk.FileChooserButton(dlg) 1016 widget.set_title(_("Choose Directory")) 1017 widget.set_filename(value) 1018 widget.set_size_request(180, 28) 1019 widget.connect("selection-changed", self.options_callback, option) 1020 elif t == ImageOption: 1021 # create entry and button (entry is hidden) 1022 entry = gtk.Entry() 1023 entry.set_text(value) 1024 entry.set_editable(False) 1025 but = gtk.Button() 1026 # util to reload preview image 1027 def create_preview (filename): 1028 if filename and os.path.isfile(filename): 1029 pb = gtk.gdk.pixbuf_new_from_file_at_size(filename, 64, -1) 1030 if pb: 1031 img = gtk.Image() 1032 img.set_from_pixbuf(pb) 1033 return img 1034 img = gtk.image_new_from_stock(gtk.STOCK_MISSING_IMAGE, 1035 gtk.ICON_SIZE_LARGE_TOOLBAR) 1036 img.set_size_request(64, 64) 1037 return img
1038 # create button 1039 def but_callback (widget): 1040 dlg = gtk.FileChooserDialog(buttons=(gtk.STOCK_CANCEL, 1041 gtk.RESPONSE_CANCEL, gtk.STOCK_OPEN, gtk.RESPONSE_OK)) 1042 dlg.set_title(_("Choose Image")) 1043 dlg.set_keep_above(True) 1044 dlg.set_filename(entry.get_text()) 1045 flt = gtk.FileFilter() 1046 flt.add_pixbuf_formats() 1047 dlg.set_filter(flt) 1048 prev = gtk.Image() 1049 box = gtk.VBox() 1050 box.set_size_request(150, -1) 1051 box.add(prev) 1052 prev.show() 1053 # add preview widget to filechooser 1054 def preview_callback(widget): 1055 fname = dlg.get_preview_filename() 1056 if fname and os.path.isfile(fname): 1057 pb = gtk.gdk.pixbuf_new_from_file_at_size(fname, 150, -1) 1058 if pb: 1059 prev.set_from_pixbuf(pb) 1060 dlg.set_preview_widget_active(True) 1061 else: 1062 dlg.set_preview_widget_active(False)
1063 dlg.set_preview_widget_active(True) 1064 dlg.connect('selection-changed', preview_callback) 1065 dlg.set_preview_widget(box) 1066 # run 1067 response = dlg.run() 1068 if response == gtk.RESPONSE_OK: 1069 entry.set_text(dlg.get_filename()) 1070 but.set_image(create_preview(dlg.get_filename())) 1071 self.options_callback(dlg, option) 1072 dlg.destroy() 1073 # load preview image 1074 but.set_image(create_preview(value)) 1075 but.connect('clicked', but_callback) 1076 # create widget 1077 widget = gtk.HBox() 1078 widget.add(entry) 1079 widget.add(but) 1080 but.show() 1081 widget.show() 1082 # add tooltips 1083 #self.tooltips.set_tip(but, 'Select Image ...') 1084 self.tooltips.set_tip(but, option.desc) 1085 elif t == ListOption: 1086 entry= gtk.Entry() 1087 entry.set_editable(False) 1088 entry.set_text(str(value)) 1089 entry.show() 1090 img = gtk.Image() 1091 img.set_from_stock(gtk.STOCK_EDIT, 1) 1092 but = gtk.Button() 1093 but.set_image(img) 1094 def open_listeditor(event): 1095 # open dialog 1096 dlg = ListOptionDialog() 1097 # read string from entry and import it through option-class 1098 # (this is needed to always have an up-to-date value) 1099 dlg.set_list(option.on_import(entry.get_text())) 1100 resp = dlg.run() 1101 if resp == gtk.RESPONSE_OK: 1102 # set text in entry 1103 entry.set_text(str(dlg.get_list())) 1104 # manually call the options-callback 1105 self.options_callback(dlg, option) 1106 dlg.destroy() 1107 but.show() 1108 but.connect("clicked", open_listeditor) 1109 self.tooltips.set_tip(but, _('Open List-Editor ...')) 1110 self.tooltips.set_tip(entry, option.desc) 1111 widget = gtk.HBox() 1112 widget.add(entry) 1113 widget.add(but) 1114 elif t == AccountOption: 1115 widget = gtk.HBox() 1116 vb = gtk.VBox() 1117 input_name = gtk.Entry() 1118 input_name.set_text(value[0]) 1119 input_name.show() 1120 input_pass = gtk.Entry() 1121 input_pass.set_visibility(False) # password 1122 input_pass.set_text(value[1]) 1123 input_pass.show() 1124 but = gtk.Button('Apply', gtk.STOCK_APPLY) 1125 but.show() 1126 but.connect("clicked", self.apply_options_callback, option, widget) 1127 vb.add(input_name) 1128 vb.add(input_pass) 1129 vb.show() 1130 self.tooltips.set_tip(but, _('Apply username/password ...')) 1131 self.tooltips.set_tip(input_name, _('Enter username here ...')) 1132 self.tooltips.set_tip(input_pass, _('Enter password here ...')) 1133 widget.add(vb) 1134 widget.add(but) 1135 elif t == TimeOption: 1136 widget = gtk.HBox() 1137 input_hour = gtk.SpinButton()#climb_rate=1.0) 1138 input_minute = gtk.SpinButton() 1139 input_second = gtk.SpinButton() 1140 input_hour.set_range(0, 23) 1141 input_hour.set_max_length(2) 1142 input_hour.set_increments(1, 1) 1143 input_hour.set_numeric(True) 1144 input_hour.set_value(value[0]) 1145 input_minute.set_range(0, 59) 1146 input_minute.set_max_length(2) 1147 input_minute.set_increments(1, 1) 1148 input_minute.set_numeric(True) 1149 input_minute.set_value(value[1]) 1150 input_second.set_range(0, 59) 1151 input_second.set_max_length(2) 1152 input_second.set_increments(1, 1) 1153 input_second.set_numeric(True) 1154 input_second.set_value(value[2]) 1155 input_hour.connect('value-changed', self.options_callback, option) 1156 input_minute.connect('value-changed', self.options_callback, option) 1157 input_second.connect('value-changed', self.options_callback, option) 1158 self.tooltips.set_tip(input_hour, option.desc) 1159 self.tooltips.set_tip(input_minute, option.desc) 1160 self.tooltips.set_tip(input_second, option.desc) 1161 widget.add(input_hour) 1162 widget.add(gtk.Label(':')) 1163 widget.add(input_minute) 1164 widget.add(gtk.Label(':')) 1165 widget.add(input_second) 1166 widget.add(gtk.Label('h')) 1167 widget.show_all() 1168 else: 1169 widget = gtk.Entry() 1170 print "unsupported type ''" % str(t) 1171 hbox = gtk.HBox() 1172 label = gtk.Label() 1173 label.set_alignment(0.0, 0.0) 1174 label.set_label(option.label) 1175 label.set_size_request(180, 28) 1176 label.show() 1177 hbox.pack_start(label, 0, 1) 1178 if widget: 1179 if option.disabled: # option disabled? 1180 widget.set_sensitive(False) 1181 label.set_sensitive(False) 1182 #label.set_mnemonic_widget(widget) 1183 self.tooltips.set_tip(widget, option.desc) 1184 widget.show() 1185 # check if needs Apply-button 1186 if option.realtime == False: 1187 but = gtk.Button(_('Apply'), gtk.STOCK_APPLY) 1188 but.show() 1189 but.connect("clicked", self.apply_options_callback, 1190 option, widget) 1191 b = gtk.HBox() 1192 b.show() 1193 b.pack_start(widget, 0, 0) 1194 b.pack_start(but, 0, 0) 1195 hbox.pack_start(b, 0, 0) 1196 else: 1197 #hbox.pack_start(widget, -1, 1) 1198 hbox.pack_start(widget, 0, 0) 1199 return hbox 1200
1201 - def read_option_from_widget (self, widget, option):
1202 """Read an option's value from the widget and return it.""" 1203 if not widget.window: 1204 return False 1205 # get type of option and read the widget's value 1206 val = None 1207 t = option.__class__ 1208 if t == IntOption: 1209 val = int(widget.get_value()) 1210 elif t == FloatOption: 1211 val = widget.get_value() 1212 elif t == StringOption: 1213 if option.choices: 1214 # if default is a list, handle combobox 1215 val = widget.get_active_text() 1216 else: 1217 val = widget.get_text() 1218 elif t == BoolOption: 1219 val = widget.get_active() 1220 elif t == ColorOption: 1221 col = widget.get_color() 1222 al = widget.get_alpha() 1223 val = (col.red/65535.0, col.green/65535.0, 1224 col.blue/65535.0, al/65535.0) 1225 elif t == FontOption: 1226 val = widget.get_font_name() 1227 elif t == FileOption or t == DirectoryOption or t == ImageOption: 1228 val = widget.get_filename() 1229 #print widget 1230 #elif t == ImageOption: 1231 # val = widget.get_text() 1232 elif t == ListOption: 1233 # the widget is a ListOptionDialog here 1234 val = widget.get_list() 1235 elif t == AccountOption: 1236 # the widget is a HBox containing a VBox containing two Entries 1237 # (ideally we should have a custom widget for the AccountOption) 1238 for c in widget.get_children(): 1239 if c.__class__ == gtk.VBox: 1240 c2 = c.get_children() 1241 val = (c2[0].get_text(), c2[1].get_text()) 1242 elif t == TimeOption: 1243 box = widget.get_parent() 1244 inputs = box.get_children() 1245 val = (int(inputs[0].get_value()), int(inputs[2].get_value()), 1246 int(inputs[4].get_value())) 1247 else: 1248 print "OptionsDialog: Unknown option type: %s" % str(t) 1249 return None 1250 # return the value 1251 return val
1252 1253 # option-widget event-handling 1254 1255 # TODO: custom callback/signal for each option?
1256 - def options_callback (self, widget, optionobj):
1257 """Callback for handling changed-events on entries.""" 1258 print "Changed: %s" % optionobj.name 1259 if self.__shown_object: 1260 # if the option is not real-time updated, 1261 if optionobj.realtime == False: 1262 return False 1263 # read option 1264 val = self.read_option_from_widget(widget, optionobj) 1265 if val != None: 1266 #print "SetOption: "+optionobj.name+"="+str(val) 1267 # set option 1268 setattr(self.__shown_object, optionobj.name, val) 1269 # notify option-object's on_changed-handler 1270 optionobj.emit("option_changed", optionobj) 1271 return False
1272
1273 - def apply_options_callback (self, widget, optionobj, entry):
1274 """Callback for handling Apply-button presses.""" 1275 if self.__shown_object: 1276 # read option 1277 val = self.read_option_from_widget(entry, optionobj) 1278 if val != None: 1279 #print "SetOption: "+optionobj.name+"="+str(val) 1280 # set option 1281 setattr(self.__shown_object, optionobj.name, val) 1282 # notify option-object's on_changed-handler 1283 optionobj.emit("option_changed", optionobj) 1284 return False
1285 1286 1287 1288 # ------ ONLY FOR TESTING ------------------: 1289 if __name__ == "__main__": 1290 1291 import os 1292 1293 # this is only for testing - should be a Screenlet
1294 - class TestObject (EditableOptions):
1295 1296 testlist = ['test1', 'test2', 3, 5, 'Noch ein Test'] 1297 pop3_account = ('Username', '') 1298 1299 # TEST 1300 pin_x = 100 1301 pin_y = 6 1302 text_x = 19 1303 text_y = 35 1304 font_name = 'Sans 12' 1305 rgba_color = (0.0, 0.0, 1.0, 1.0) 1306 text_prefix = '<b>' 1307 text_suffix = '</b>' 1308 note_text = "" # hidden option because val has its own editing-dialog 1309 random_pin_pos = True 1310 opt1 = 'testval 1' 1311 opt2 = 'testval 2' 1312 filename2 = '' 1313 filename = '' 1314 dirname = '' 1315 font = 'Sans 12' 1316 color = (0.1, 0.5, 0.9, 0.9) 1317 name = 'a name' 1318 name2 = 'another name' 1319 combo_test = 'el2' 1320 flt = 0.5 1321 x = 10 1322 y = 25 1323 width = 30 1324 height = 50 1325 is_sticky = False 1326 is_widget = False 1327 time = (12, 32, 49) # a time-value (tuple with ints) 1328
1329 - def __init__ (self):
1330 EditableOptions.__init__(self) 1331 # Add group 1332 self.add_options_group('General', 1333 'The general options for this Object ...') 1334 self.add_options_group('Window', 1335 'The Window-related options for this Object ...') 1336 self.add_options_group('Test', 'A Test-group ...') 1337 # Add editable options 1338 self.add_option(ListOption('Test', 'testlist', self.testlist, 1339 'ListOption-Test', 'Testing a ListOption-type ...')) 1340 self.add_option(StringOption('Window', 'name', 'TESTNAME', 1341 'Testname', 'The name/id of this Screenlet-instance ...'), 1342 realtime=False) 1343 self.add_option(AccountOption('Test', 'pop3_account', 1344 self.pop3_account, 'Username/Password', 1345 'Enter username/password here ...')) 1346 self.add_option(StringOption('Window', 'name2', 'TESTNAME2', 1347 'String2', 'Another string-test ...')) 1348 self.add_option(StringOption('Test', 'combo_test', "el1", 'Combo', 1349 'A StringOption displaying a drop-down-list with choices...', 1350 choices=['el1', 'el2', 'element 3'])) 1351 self.add_option(FloatOption('General', 'flt', 30, 1352 'A Float', 'Testing a FLOAT-type ...', 1353 min=0, max=gtk.gdk.screen_width(), increment=0.01, digits=4)) 1354 self.add_option(IntOption('General', 'x', 30, 1355 'X-Position', 'The X-position of this Screenlet ...', 1356 min=0, max=gtk.gdk.screen_width())) 1357 self.add_option(IntOption('General', 'y', 30, 1358 'Y-Position', 'The Y-position of this Screenlet ...', 1359 min=0, max=gtk.gdk.screen_height())) 1360 self.add_option(IntOption('Test', 'width', 300, 1361 'Width', 'The width of this Screenlet ...', min=100, max=1000)) 1362 self.add_option(IntOption('Test', 'height', 150, 1363 'Height', 'The height of this Screenlet ...', 1364 min=100, max=1000)) 1365 self.add_option(BoolOption('General', 'is_sticky', True, 1366 'Stick to Desktop', 'Show this Screenlet always ...')) 1367 self.add_option(BoolOption('General', 'is_widget', False, 1368 'Treat as Widget', 'Treat this Screenlet as a "Widget" ...')) 1369 self.add_option(FontOption('Test', 'font', 'Sans 14', 1370 'Font', 'The font for whatever ...')) 1371 self.add_option(ColorOption('Test', 'color', (1, 0.35, 0.35, 0.7), 1372 'Color', 'The color for whatever ...')) 1373 self.add_option(FileOption('Test', 'filename', os.environ['HOME'], 1374 'Filename-Test', 'Testing a FileOption-type ...', 1375 patterns=['*.py', '*.pyc'])) 1376 self.add_option(ImageOption('Test', 'filename2', os.environ['HOME'], 1377 'Image-Test', 'Testing the ImageOption-type ...')) 1378 self.add_option(DirectoryOption('Test', 'dirname', os.environ['HOME'], 1379 'Directory-Test', 'Testing a FileOption-type ...')) 1380 self.add_option(TimeOption('Test','time', self.time, 1381 'TimeOption-Test', 'Testing a TimeOption-type ...')) 1382 # TEST 1383 self.disable_option('width') 1384 self.disable_option('height')
1385 # TEST: load options from file 1386 #self.add_options_from_file('/home/ryx/Desktop/python/screenlets/screenlets-0.0.9/src/share/screenlets/Notes/options.xml') 1387
1388 - def __setattr__(self, name, value):
1389 self.__dict__[name] = value 1390 print name + "=" + str(value)
1391
1392 - def get_short_name(self):
1393 return self.__class__.__name__[:-6]
1394 1395 1396 1397 # this is only for testing - should be a Screenlet
1398 - class TestChildObject (TestObject):
1399 1400 uses_theme = True 1401 theme_name = 'test' 1402
1403 - def __init__ (self):
1404 TestObject.__init__(self) 1405 self.add_option(StringOption('Test', 'anothertest', 'ksjhsjgd', 1406 'Another Test', 'An attribute in the subclass ...')) 1407 self.add_option(StringOption('Test', 'theme_name', self.theme_name, 1408 'Theme', 'The theme for this Screenelt ...', 1409 choices=['test1', 'test2', 'mytheme', 'blue', 'test']))
1410 1411 1412 # TEST: load/save 1413 # TEST: option-editing 1414 to = TestChildObject() 1415 #print to.export_options_as_list() 1416 se = OptionsDialog(500, 380)#, treeview=True) 1417 #img = gtk.image_new_from_stock(gtk.STOCK_ABOUT, 5) 1418 img = gtk.Image() 1419 img.set_from_file('../share/screenlets/Notes/icon.svg') 1420 se.set_info('TestOptions', 1421 'A test for an extended options-dialog with embedded about-info.' + 1422 ' Can be used for the Screenlets to have all in one ...\nNOTE:' + 1423 '<span color="red"> ONLY A TEST!</span>', 1424 '(c) RYX 2007', version='v0.0.1', icon=img) 1425 se.show_options_for_object(to) 1426 resp = se.run() 1427 if resp == gtk.RESPONSE_OK: 1428 print "OK" 1429 else: 1430 print "Cancelled." 1431 se.destroy() 1432 print to.export_options_as_list() 1433