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