Package qubx :: Module settingsGTK
[hide private]
[frames] | no frames]

Source Code for Module qubx.settingsGTK

  1  """pygtk GUI for qubx.settings. 
  2   
  3  Copyright 2007-2012 Research Foundation State University of New York  
  4  This file is part of QUB Express.                                           
  5   
  6  QUB Express is free software; you can redistribute it and/or modify           
  7  it under the terms of the GNU General Public License as published by  
  8  the Free Software Foundation, either version 3 of the License, or     
  9  (at your option) any later version.                                   
 10   
 11  QUB Express is distributed in the hope that it will be useful,                
 12  but WITHOUT ANY WARRANTY; without even the implied warranty of        
 13  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         
 14  GNU General Public License for more details.                          
 15   
 16  You should have received a copy of the GNU General Public License,    
 17  named LICENSE.txt, in the QUB Express program directory.  If not, see         
 18  <http://www.gnu.org/licenses/>.                                       
 19   
 20  """ 
 21   
 22  import pygtk 
 23  pygtk.require('2.0') 
 24  import gtk 
 25  from gtk import gdk 
 26  from gtk import keysyms 
 27  import gobject 
 28   
 29  import qubx.settings 
 30  from qubx.settings import * 
 31  from qubx.GTK import * 
 32  import qubx.treeGTK 
 33  import qubx.tree 
 34   
35 -def include_all(name):
36 return True
37
38 -class PresetsMenu(gtk.EventBox):
39 """ 40 Combo box for managing a category of presets/settings. 41 42 If you have a presets menu for a category, you must 43 - keep category.active up-to-date, in case the user saves a preset 44 - provide category.OnSet, in case the user chooses a preset 45 """ 46 47 __explore_featured = ['_name', '_appName', '_parent', '_include', '_mask', '_additions'] 48
49 - def __init__(self, name, appName="", parent=None, includeF=include_all, mask='.qprs', caption='Presets...', 50 additions=[]):
51 """ 52 @param name: the category name 53 @param appName: top-level application name, for dialog titles 54 @param parent: parent gtk.Window, for centering dialogs 55 @param includeF: filter function; we show only presets for which includeF(name) is True 56 @param mask: preferred file extension for import/export 57 @param caption: text on menu 58 @param additions: sequence of (caption, action()) to add to menu 59 """ 60 MapSettings() 61 gtk.EventBox.__init__(self) 62 self._name = name 63 self._appName = appName 64 self._parent = parent 65 self._include = includeF 66 self._mask = mask 67 self._perma = 3 68 self._count = 0 69 self._box = gtk.combo_box_new_text() 70 self.add(self._box) 71 self._box.show() 72 self.connect('enter-notify-event', self.enter_notify_event) 73 self._box.connect('changed', self.combo_changed) 74 self._additions = additions 75 self._box.append_text(caption) 76 self._box.append_text('Save in this menu...') 77 self._box.append_text('Manage...') 78 self._box.set_active(0)
79 - def enter_notify_event(self, widget, event):
80 self._box.set_active(0) 81 for i in xrange(self._count): 82 self._box.remove_text(self._perma) 83 self._count = 0 84 self._addActions = {} 85 for lbl, action in self._additions: 86 self._box.append_text(lbl) 87 self._addActions[lbl] = action 88 self._count += 1 89 for name in sorted(SettingsMgr.listNames(self._name)): 90 if self._include(name): 91 self._box.append_text(name) 92 self._count += 1
93 - def combo_changed(self, combo):
94 active_i = combo.get_active() 95 if active_i == 0: 96 pass 97 elif active_i == 1: 98 dlog = InputDialog('Add preset to menu...', None, 'Preset name:', '') 99 if dlog.run() == gtk.RESPONSE_ACCEPT: 100 name = dlog.text 101 if name: # should check for special characters 102 SettingsMgr[self._name].save(name) 103 dlog.destroy() 104 elif active_i == 2: 105 dlog = PresetsDialog(self._name, self._parent, self._appName, self._mask) 106 dlog.run() 107 dlog.destroy() 108 elif active_i >= (self._perma + len(self._additions)): 109 qubx.pyenv.env.OnScriptable('qubx.settings.SettingsMgr[%s].load(%s)' % (repr(self._name), repr(combo.get_active_text()))) 110 SettingsMgr[self._name].load(combo.get_active_text()) 111 else: 112 self._addActions[combo.get_active_text()]() 113 combo.set_active(0)
114
115 -class PresetsDialog(gtk.Dialog):
116 """Manage Presets dialog"""
117 - def __init__(self, cat, parent, appName="", mask=".qprs"):
118 appPrefix = appName and (appName+" - ") or "" 119 gtk.Dialog.__init__(self, appPrefix+"Manage Presets", parent or get_active_window(), gtk.DIALOG_MODAL) 120 self.set_size_request(500, 350) 121 self.cat = cat 122 self.mask = mask 123 self.ref = Reffer() 124 125 btns = pack_item(gtk.HBox(False, 10), self.vbox) 126 self.btnImport = pack_button('Import...', btns, on_click=self._onClickImport) 127 self.btnExport = pack_button('Export...', btns, on_click=self._onClickExport) 128 self.btnRename = pack_button('Rename...', btns, on_click=self._onItemRename) 129 self.btnDelete = pack_button('Delete', btns, on_click=self._onItemDelete) 130 131 lr = pack_item(gtk.HPaned(), self.vbox, expand=True) 132 self.presets = SimpleList(sortable=False) 133 for name in SettingsMgr.listNames(cat): 134 self.presets.append(name) 135 self.presets.OnSelect += self.ref(self._onSelect) 136 scr = pack_scrolled(self.presets, None, size_request=(200, 70)) 137 lr.pack1(scr, False, True) 138 self.treeView = qubx.treeGTK.TreeView() 139 scr = pack_scrolled(self.treeView, None) 140 lr.pack2(scr, True, True) 141 142 self.add_button(gtk.STOCK_CLOSE, gtk.RESPONSE_REJECT) 143 144 popup = gtk.Menu() 145 build_menuitem('Rename', self.ref(self._onItemRename), menu=popup) 146 build_menuitem('Delete', self.ref(self._onItemDelete), menu=popup) 147 self.presets.popup = popup
148 - def _onSelect(self, list, index):
149 if index == self._lastIndex: 150 return 151 if self.promptSave(): 152 self._lastIndex = index 153 self._lastName = self.presets.active 154 self.treeView.root = SettingsMgr[self.cat].read(self.presets.active) 155 self.btnExport.set_sensitive(index >= 0) 156 self.btnRename.set_sensitive(index >= 0) 157 self.btnDelete.set_sensitive(index >= 0) 158 else: 159 gobject.idle_add(self._fixSel)
160 - def _fixSel(self):
161 self.presets.index = self._lastIndex
162 - def run(self):
163 self._lastIndex = -1 164 if len(SettingsMgr.listNames(self.cat)): 165 if self.presets.index < 0: 166 self.presets.index = 0 167 self._onSelect(self.presets, self.presets.index) 168 response = gtk.Dialog.run(self) 169 self.promptSave(False) 170 return response
171 - def promptSave(self, canCancel=True):
172 if self._lastIndex < 0 or not self.treeView.changed: 173 return True 174 dlg = gtk.MessageDialog(self, buttons=gtk.BUTTONS_NONE, 175 flags=gtk.DIALOG_MODAL, 176 message_format='Save changes to %s?' % self._lastName) 177 dlg.add_buttons('Yes',gtk.RESPONSE_YES, 178 'No',gtk.RESPONSE_NO) 179 if canCancel: 180 dlg.add_buttons('Cancel',gtk.RESPONSE_CANCEL) 181 response = dlg.run() 182 dlg.destroy() 183 if response == gtk.RESPONSE_YES: 184 SettingsMgr.save(self.treeView.root, self.cat, self._lastName) 185 return True 186 elif response == gtk.RESPONSE_CANCEL: 187 return False 188 elif response == gtk.RESPONSE_NO: 189 return True
190 - def _onClickImport(self, btn):
191 dlg = gtk.FileChooserDialog('Import presets...', self, gtk.FILE_CHOOSER_ACTION_OPEN, 192 buttons=(gtk.STOCK_CANCEL,gtk.RESPONSE_CANCEL,gtk.STOCK_OPEN,gtk.RESPONSE_OK)) 193 dlg.set_default_response(gtk.RESPONSE_OK) 194 filter = gtk.FileFilter() 195 filter.set_name("Presets files") 196 filter.add_pattern("*"+self.mask) 197 dlg.add_filter(filter) 198 filter = gtk.FileFilter() 199 filter.set_name("All files") 200 filter.add_pattern("*") 201 dlg.add_filter(filter) 202 response = dlg.run() 203 if response == gtk.RESPONSE_OK: 204 try: 205 qprs = qubx.tree.Open(dlg.get_filename(), True) 206 qprs.close() 207 if str(qprs['Target'].data) != self.cat: 208 mdlg = gtk.MessageDialog(self, buttons=gtk.BUTTONS_NONE, 209 flags=gtk.DIALOG_MODAL, 210 message_format='This looks like a preset for a different category, "%s." Import it anyway?'%str(qprs['Target'].data)) 211 mdlg.add_buttons('Yes', gtk.RESPONSE_YES, 212 'No', gtk.RESPONSE_NO) 213 if gtk.RESPONSE_NO == mdlg.run(): 214 qprs = None 215 mdlg.destroy() 216 if qprs: 217 name = str(qprs['Label'].data) or "untitled" 218 try: 219 prev_ix = SettingsMgr.listNames(self.cat).index(name) 220 except: 221 prev_ix = -1 222 SettingsMgr.save(qprs['Presets'], self.cat, name) 223 if prev_ix >= 0: 224 self.presets.index = prev_ix 225 else: 226 self.presets.append(name) 227 self.presets.index = len(self.presets.model) - 1 228 except Exception, e: 229 traceback.print_exc() 230 dlg.destroy()
231 - def _onClickExport(self, btn):
232 dlg = gtk.FileChooserDialog('Export presets as...', self, gtk.FILE_CHOOSER_ACTION_SAVE, 233 buttons=(gtk.STOCK_CANCEL,gtk.RESPONSE_CANCEL,gtk.STOCK_SAVE,gtk.RESPONSE_OK)) 234 dlg.set_default_response(gtk.RESPONSE_OK) 235 filter = gtk.FileFilter() 236 filter.set_name("Presets files") 237 filter.add_pattern('*'+self.mask) 238 dlg.add_filter(filter) 239 filter = gtk.FileFilter() 240 filter.set_name("All files") 241 filter.add_pattern("*") 242 dlg.add_filter(filter) 243 response = dlg.run() 244 if response == gtk.RESPONSE_OK: 245 try: 246 qprs = qubx.tree.Node('Presets') 247 qprs['Target'].data = self.cat 248 qprs['Label'].data = self.presets.active 249 qprs.appendClone(self.treeView.root).name = 'Presets' 250 filename = dlg.get_filename() 251 if not os.path.splitext(filename)[1]: 252 filename = filename + self.mask 253 qprs.saveAs(filename, as_copy=True) 254 except Exception, e: 255 traceback.print_exc() 256 dlg.destroy()
257 - def _onItemRename(self, item):
258 if self.presets.index < 0: 259 return 260 dlog = InputDialog('Rename preset...', None, 'Preset name:', self.presets.active) 261 try: 262 if dlog.run() == gtk.RESPONSE_ACCEPT: 263 name = dlog.text 264 if name: # should check for special characters 265 SettingsMgr.rename(self.cat, self.presets.active, name) 266 self.presets.edit(self.presets.index, name) 267 except Exception, e: 268 d = gtk.MessageDialog(self, buttons=gtk.BUTTONS_NONE, flags=gtk.DIALOG_MODAL, 269 message_format=str(e)) 270 d.add_buttons('OK', gtk.RESPONSE_OK) 271 d.run() 272 d.destroy() 273 dlog.destroy()
274 - def _onItemDelete(self, item):
275 if self.presets.index < 0: 276 return 277 dlg = gtk.MessageDialog(self, buttons=gtk.BUTTONS_NONE, 278 flags=gtk.DIALOG_MODAL, 279 message_format="Permanently delete %s?" % self.presets.active) 280 dlg.add_buttons('Yes',gtk.RESPONSE_YES, 281 'No', gtk.RESPONSE_NO) 282 response = dlg.run() 283 dlg.destroy() 284 if response == gtk.RESPONSE_NO: 285 return 286 try: 287 self.treeView.changed = False 288 self._lastIndex = -1 289 if SettingsMgr.delete(self.cat, self.presets.active): 290 self.presets.remove(self.presets.index) 291 except: 292 traceback.print_exc()
293
294 -class PropertiedDialog(gtk.Dialog, PropertiedBase):
295 """Edits a settings category, with OK, Cancel, Presets."""
296 - def __init__(self, cat_name, properties, title="", parent=None):
297 """ 298 @param properties: list of L{qubx.settings.Property}; if name is blank, doc is displayed as label; if not check/radio, properties need "accept" and "format". 299 """ 300 gtk.Dialog.__init__(self, title, parent or get_active_window(), gtk.DIALOG_MODAL, buttons=(gtk.STOCK_OK, gtk.RESPONSE_ACCEPT, gtk.STOCK_CANCEL, gtk.RESPONSE_REJECT)) 301 self.set_default_response(gtk.RESPONSE_ACCEPT) 302 props_only = [prop for prop in properties if prop.name] 303 self.propertied_specs = props_only 304 spec_dict = dict((spec.name, spec) for spec in props_only) 305 self.propertied_init(spec_dict) 306 self.propertied_connect_settings(cat_name) 307 308 for prop in properties: 309 if prop.doc and not prop.name: 310 pack_label(prop.doc, self.vbox) 311 elif prop.accept == acceptBool: 312 line = pack_item(gtk.HBox(), self.vbox) 313 chk = pack_check(prop.doc, line, expand=True) 314 self.propertied_connect_check(prop.name, chk) 315 elif prop.value_names: 316 line = pack_item(gtk.HBox(True), self.vbox) 317 pack_label(prop.doc, line) 318 radios = [] 319 for val, val_name in prop.value_names.iteritems(): 320 if radios: 321 line = pack_item(gtk.HBox(True), self.vbox) 322 pack_space(line, expand=True) 323 radios.append( (val, pack_radio(val_name, line, group=radios and radios[0][1] or None)) ) 324 self.propertied_connect_radios(prop.name, radios) 325 else: 326 line = pack_item(gtk.HBox(True), self.vbox) 327 pack_label(prop.doc, line) 328 entry = pack_item(qubx.GTK.NumEntry(prop.default, prop.accept, prop.format), line) 329 self.propertied_connect_NumEntry(prop.name, entry) 330 pack_item(PresetsMenu(cat_name, parent=self), self.action_area)
331 - def run(self):
332 """Returns {name : value}, or None if cancelled.""" 333 response = gtk.Dialog.run(self) 334 if response == gtk.RESPONSE_ACCEPT: 335 return dict([(prop.name, self.propertied_items[prop.name].value) for prop in self.propertied_specs]) 336 else: 337 return None
338
339 -def prompt_propertied(cat_name, properties, title="", parent=None):
340 """ 341 @param properties: list of L{qubx.settings.Property}; if name is blank, doc is displayed as label; if not check/radio, properties need "accept" and "format". 342 """ 343 dlg = PropertiedDialog(cat_name, properties, title, parent) 344 try: 345 return dlg.run() 346 finally: 347 dlg.destroy()
348 349
350 -def MapSettings():
351 """Sets up global SettingsMgr.""" 352 global SettingsMgr 353 qubx.settings.InitSettings() 354 SettingsMgr = qubx.settings.SettingsMgr
355