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