class ShoppingNutritionalInfoPlugin(ShoppingListPlugin): ui = '''<ui> <menubar name="ShoppingListMenuBar"> <menu name="Tools" action="Tools"> <menuitem action="ShoppingNutritionalInfo"/> </menu> </menubar> <toolbar name="ShoppingListTopToolBar"> <separator/> <toolitem action="ShoppingNutritionalInfo"/> </toolbar> </ui> ''' name = 'shopping_nutritional_info' def setup_action_groups(self): self.nutritionShoppingActionGroup = gtk.ActionGroup( 'NutritionShoppingActionGroup') self.nutritionShoppingActionGroup.add_actions([ ('Tools', None, _('Tools')), ( 'ShoppingNutritionalInfo', # name 'nutritional-info', # stock _('Nutritional Information'), # label '<Ctrl><Shift>N', #key-command _('Get nutritional information for current list'), self.show_nutinfo # callback ) ]) self.action_groups.append(self.nutritionShoppingActionGroup) def show_nutinfo(self, *args): sg = self.pluggable rr = sg.recs rd = gourmet.recipeManager.get_recipe_manager() rg = gourmet.GourmetRecipeManager.get_application() if not hasattr(self, 'nutrition_window'): self.create_nutrition_window() nutinfo = None # Add recipes... for rec in rr: ings = rd.get_ings(rec) ni = rd.nd.get_nutinfo_for_inglist(rd.get_ings(rec), rd) if nutinfo: nutinfo = nutinfo + ni else: nutinfo = ni # Add extras... for amt, unit, item in sg.extras: ni = rd.nd.get_nutinfo_for_item(item, amt, unit) if nutinfo: nutinfo = nutinfo + ni else: nutinfo = ni self.nl.set_nutinfo(nutinfo) self.nutrition_window.present() def create_nutrition_window(self): self.nutrition_window = gtk.Dialog(_('Nutritional Information'), self.pluggable.w, buttons=(gtk.STOCK_CLOSE, gtk.RESPONSE_CLOSE)) self.nutrition_window.set_default_size(400, 550) self.nutrition_window.set_icon( self.nutrition_window.render_icon('nutritional-info', gtk.ICON_SIZE_MENU)) self.nl = NutritionLabel(get_prefs()) self.sw = gtk.ScrolledWindow() self.sw.set_policy(gtk.POLICY_NEVER, gtk.POLICY_AUTOMATIC) self.sw.add_with_viewport(self.nl) self.sw.show() self.nutrition_window.vbox.pack_start(self.sw) self.nutrition_window.connect('response', self.response_cb) self.nutrition_window.connect('close', self.response_cb) self.nl.yieldLabel.set_markup('<b>' + _('Amount for Shopping List') + '</b>') self.nl.show() def response_cb(self, *args): # We only allow one response -- closing the window! self.nutrition_window.hide()
class ShoppingNutritionalInfoPlugin (ShoppingListPlugin): ui = '''<ui> <menubar name="ShoppingListMenuBar"> <menu name="Tools" action="Tools"> <menuitem action="ShoppingNutritionalInfo"/> </menu> </menubar> <toolbar name="ShoppingListTopToolBar"> <separator/> <toolitem action="ShoppingNutritionalInfo"/> </toolbar> </ui> ''' name = 'shopping_nutritional_info' def setup_action_groups (self): self.nutritionShoppingActionGroup = gtk.ActionGroup('NutritionShoppingActionGroup') self.nutritionShoppingActionGroup.add_actions([ ('Tools',None,_('Tools')), ('ShoppingNutritionalInfo', # name 'nutritional-info', # stock _('Nutritional Information'), # label '<Ctrl><Shift>N', #key-command _('Get nutritional information for current list'), self.show_nutinfo # callback ) ]) self.action_groups.append(self.nutritionShoppingActionGroup) def show_nutinfo (self, *args): sg = self.pluggable rr = sg.recs rd = gourmet.recipeManager.get_recipe_manager() rg = gourmet.GourmetRecipeManager.get_application() if not hasattr(self,'nutrition_window'): self.create_nutrition_window() nutinfo = None # Add recipes... for rec in rr: ings = rd.get_ings(rec) ni = rd.nd.get_nutinfo_for_inglist(rd.get_ings(rec), rd) if nutinfo: nutinfo = nutinfo + ni else: nutinfo = ni # Add extras... for amt,unit,item in sg.extras: ni = rd.nd.get_nutinfo_for_item(item,amt,unit) if nutinfo: nutinfo = nutinfo + ni else: nutinfo = ni self.nl.set_nutinfo(nutinfo) self.nutrition_window.present() def create_nutrition_window (self): self.nutrition_window = gtk.Dialog(_('Nutritional Information'), self.pluggable.w, buttons=(gtk.STOCK_CLOSE,gtk.RESPONSE_CLOSE) ) self.nutrition_window.set_default_size(400,550) self.nutrition_window.set_icon( self.nutrition_window.render_icon('nutritional-info', gtk.ICON_SIZE_MENU) ) self.nl = NutritionLabel(get_prefs()) self.sw = gtk.ScrolledWindow(); self.sw.set_policy(gtk.POLICY_NEVER,gtk.POLICY_AUTOMATIC) self.sw.add_with_viewport(self.nl); self.sw.show() self.nutrition_window.vbox.pack_start(self.sw) self.nutrition_window.connect('response',self.response_cb) self.nutrition_window.connect('close',self.response_cb) self.nl.yieldLabel.set_markup('<b>'+_('Amount for Shopping List')+'</b>') self.nl.show() def response_cb (self, *args): # We only allow one response -- closing the window! self.nutrition_window.hide()
class NutritionInfoDruid (gobject.GObject): """A druid (or "wizard") to guide a user through helping Gourmet calculate nutritional information for an ingredient. This consists in finding a USDA equivalent of the ingredient in question and possibly of converting a unit. """ NUT_PAGE = 0 UNIT_PAGE = 1 CUSTOM_PAGE = 2 DENSITY_PAGE = 3 INFO_PAGE = 4 INDEX_PAGE = 5 DEFAULT_AMOUNT = 8 DEFAULT_UNIT = 'oz.' __gsignals__ = { # The key callback will return a tuple (old_key,new_key) 'key-changed':(gobject.SIGNAL_RUN_LAST,gobject.TYPE_PYOBJECT,(gobject.TYPE_PYOBJECT,)), # The unit callback will return a tuple ((old_unit,old_key),(new_unit,new_key)) 'unit-changed':(gobject.SIGNAL_RUN_LAST,gobject.TYPE_PYOBJECT,(gobject.TYPE_PYOBJECT,)), 'finish':(gobject.SIGNAL_RUN_LAST,gobject.TYPE_NONE,()) } def __init__ (self, nd, prefs, rec=None, in_string=''): self.ui = gtk.Builder() self.ui.add_from_file(os.path.join(current_path,'nutritionDruid.ui')) self.mm = MnemonicManager() self.mm.add_builder(self.ui) self.mm.fix_conflicts_peacefully() self.prefs = prefs self.nd = nd self.rec = rec self.in_string = in_string or (rec and _('recipe') or _('selection')) self.rd = self.nd.db self.def_ingredient_amounts = {} # For default amounts for nutritional label... self.amounts = {} # List amounts by ingredient self.ing_to_index = {} # A way to keep track of the order of our ingredients... self._setup_widgets_() # keep track of pages/setups we've been on self.path = [] self.curpage = 0 self.prevDruidButton.set_sensitive(False) # Initiate our gobject-ness so we can emit signals. gobject.GObject.__init__(self) # Save our position with our widget saver... WidgetSaver.WindowSaver(self.ui.get_object('window'), self.prefs.get('nutritionDruid',{}) ) def _setup_widgets_ (self): self.controls = [] self.widgets = ['notebook', # ingKey Changing Stuff 'ingKeyLabel','ingKeyEntry','changeKeyButton','applyKeyButton', 'ingKeyLabel2', # Search stuff 'usdaSearchEntry','usdaSearchAsYouTypeToggle','usdaFindButton', 'usdaFirstButton','usdaBackButton','usdaForwardButton', 'usdaLastButton','usdaShowingLabel', 'usdaTreeview','foodGroupComboBox', 'customBox','customButton', # Unit adjusting stuff 'convertUnitLabel','fromUnitComboBoxEntry','fromUnitLabel', 'changeUnitButton','cancelUnitButton','saveUnitButton', 'fromAmountEntry','toUnitCombo','toAmountEntry', # Wizard buttons 'prevDruidButton','ignoreButton','applyButton', # Custom nutritional information screen 'massUnitComboBox','customNutritionAmountEntry', # Density-Choosing page 'densityLabel','densityBox', # Index page... 'editButton', # INFO PAGE 'infoIngredientKeyLabel','infoUSDALabel','nutritionLabelBox', 'infoDensityLabel','infoOtherEquivalentsLabel', 'infoCustomEquivalentsTable', ] for widget_name in self.widgets: setattr(self,widget_name,self.ui.get_object(widget_name)) if not getattr(self,widget_name): print "WIDGET: ",widget_name,"NOT FOUND." # make a list of all core control widgets if widget_name!='notebook': self.controls.append(getattr(self,widget_name)) self.usdaIndex = NutritionUSDAIndex(self.rd, prefs=self.prefs, widgets=[(w,getattr(self,w)) for w in self.widgets]) self.ui.connect_signals( {'previousPage':self.previous_page_cb, 'applyPage':self.apply_cb, 'ignorePage':self.ignore_cb, 'customPage':self.custom_cb, 'usdaPage':self.usda_cb, 'close':self.close, 'on_foodGroupComboBox_changed':self.usdaIndex.food_group_filter_changed_cb, 'edit':self.view_nutritional_info, 'infoEditUSDAAssociation':self.info_edit_usda_association, } ) # hide our tabs... self.notebook.set_show_tabs(False) # custom widgety stuff self.changeIngKeyAction = SpecialAction(highlight_widgets=[self.ingKeyEntry,self.applyKeyButton, ], initially_hidden=True, hide_on_highlight=[self.ingKeyLabel,self.changeKeyButton, ], all_controls=self.controls) self.changeKeyButton.connect('clicked',self.changeIngKeyAction.highlight_action) self.applyKeyButton.connect('clicked',self.apply_ingkey) self.changeUnitAction = SpecialAction(highlight_widgets=[self.fromUnitComboBoxEntry, self.saveUnitButton, self.cancelUnitButton,], initially_hidden=True, hide_on_highlight=[self.fromUnitLabel,self.changeUnitButton], all_controls=self.controls) self.changeUnitButton.connect('clicked',self.changeUnitAction.highlight_action) self.cancelUnitButton.connect('clicked',self.changeUnitAction.dehighlight_action) self.saveUnitButton.connect('clicked',self.save_unit_cb) # Nutrition box... self.custom_box=self.ui.get_object('customBox') self.customNutritionAmountEntry.connect('changed',self.custom_unit_changed) self.massUnitComboBox.connect('changed',self.custom_unit_changed) self._setup_custom_box() ### BEGIN METHODS FOR NUTRITIONAL INFORMATION INDEX def setup_nutrition_index (self): if not hasattr(self,'full_inglist'): self.add_ingredients([]) if not hasattr(self,'nutInfoIndex'): self.nutInfoIndex = NutritionInfoIndex( self.rd, prefs=self.prefs, ui=self.ui, ingredients=self.full_inglist, in_string=self.in_string, ) self.path.append((self.goto_page_index,[])) self.goto_page_index() ### END METHODS FOR NUTRITIONAL INFORMATION INDEX ### BEGIN METHODS FOR DISPLAYING CURRENT NUTRITIONAL INFO def view_nutritional_info (self, *args): nutalias = self.nutInfoIndex.get_selected_ingredient() if nutalias.ndbno == 0: # Then this is not really an item... we better edit it! self.add_ingredients( [( nutalias.ingkey, self.get_amounts_and_units_for_ingkey(nutalias.ingkey) )] ) else: self.show_info_page(nutalias) self.path.append( (self.show_info_page,[nutalias]) ) def show_info_page (self, nutalias): self.infoIngredientKeyLabel.set_text(nutalias.ingkey) self.infoUSDALabel.set_text(nutalias.desc) self.goto_page_info() self.prevDruidButton.set_sensitive(True) self.set_nutritional_label(nutalias) self.set_density_info(nutalias) self.info_nutalias = nutalias def set_density_info (self, nutalias): densities,extra_units = self.nd.get_conversions(nutalias.ingkey) density_texts = [] for k,v in densities.items(): if not k: density_texts = ['%.2f'%v] + density_texts else: density_texts.append('%s: %.2f'%(k,v)) self.infoDensityLabel.set_text('\n'.join(density_texts) or 'None') eutexts = ['%s: %s g'%(k,v) for k,v in extra_units.items() ] eutexts.sort() extra_units_text = '\n'.join(eutexts) self.infoOtherEquivalentsLabel.set_text( extra_units_text or 'None' ) others = self.rd.fetch_all(self.rd.nutritionconversions_table,ingkey=nutalias.ingkey) other_label = '\n'.join(['%s: %.1f g'%( conv.unit or '100 %s'%_('ml'),1.0/conv.factor ) for conv in others]) if others: self.populate_custom_equivalents_table(others) else: self.infoCustomEquivalentsTable.hide() def populate_custom_equivalents_table (self, equivalents): # Remove previous children... for c in self.infoCustomEquivalentsTable.get_children(): self.infoCustomEquivalentsTable.remove(c); c.unparent() for n,eq in enumerate(equivalents): lab = gtk.Label("%s: %.1f g"%( eq.unit or 'No unit', 1.0/eq.factor) ) rembut = gtk.Button('C_hange'); rembut.set_use_underline(True) rembut.connect('clicked', self.info_edit_equivalent, eq) self.infoCustomEquivalentsTable.attach(lab, 0,1,n,n+1) self.infoCustomEquivalentsTable.attach(rembut, 1,2,n,n+1) self.infoCustomEquivalentsTable.show_all() def set_nutritional_label (self, nutalias): if not hasattr(self,'nutritionLabel'): from nutritionLabel import NutritionLabel self.nutritionAmountLabel = gtk.Label() self.nutritionLabel = NutritionLabel(self.prefs, custom_label=' ') self.nutritionLabelBox.pack_start(self.nutritionAmountLabel, fill=0, expand=0) self.nutritionLabelBox.pack_start(self.nutritionLabel, fill=0, expand=0, ) self.nutritionAmountLabel.set_alignment(0.0,0.0) self.nutritionAmountLabel.show() self.nutritionLabel.show() amount,unit = self.def_ingredient_amounts.get(nutalias.ingkey, (self.DEFAULT_AMOUNT, self.DEFAULT_UNIT) ) nutinfo = self.nd.get_nutinfo_for_inglist([ MockObject(amount=amount, unit=unit, ingkey=nutalias.ingkey) ], self.rd ) if nutinfo._get_vapor(): amount = self.DEFAULT_AMOUNT; unit = self.DEFAULT_UNIT nutinfo = self.nd.get_nutinfo_for_inglist([ MockObject(amount=amount, unit=unit, ingkey=nutalias.ingkey) ], self.rd ) self.nutritionLabel.set_nutinfo( nutinfo ) self.nutritionAmountLabel.set_markup( '<i>Nutritional information for %(amount)s %(unit)s</i>'%{ 'amount':amount, 'unit':unit, }) def get_amounts_and_units_for_ingkey (self, ingkey): """Return a list of amounts and units present in database for ingkey""" amounts_and_units = [] ings = self.rd.fetch_all(self.rd.ingredients_table,ingkey=ingkey) for i in ings: a,u = i.amount,i.unit if (a,u) not in amounts_and_units: amounts_and_units.append((a,u)) return amounts_and_units # start nutritional-info callbacks def info_edit_usda_association (self, *args): """Edit the USDA association for the item on the information page.""" self.edit_nutinfo(ingkey=self.info_nutalias.ingkey, desc=self.info_nutalias.desc) self.path.append( (self.edit_nutinfo, [self.info_nutalias.ingkey, self.info_nutalias.desc]) ) def info_edit_equivalent (self, button, eq): """Edit equivalents callback. eq is a nutalias DB object. """ self.amounts[self.ingkey] = {} self.amount = 1 self.ingkey = self.info_nutalias.ingkey self.set_from_unit(eq.unit) self.fromAmountEntry.set_text('1') conv = self.nd.get_conversion_for_amt(1,eq.unit,self.info_nutalias.ingkey) amt_in_grams = conv * 100 self.setup_to_units() to_unit = cb.cb_set_active_text(self.toUnitCombo,'g') self.toAmountEntry.set_text(convert.float_to_frac(amt_in_grams, fractions=convert.FRACTIONS_ASCII )) # Hack to avoid adding ourselves to the path on a "back" event # -- if button is None, then we know we were called # artificially from previous_page_cb (not from a button press # event) if button: self.path.append( (self.info_edit_equivalent, [None,eq]) ) self.goto_page_unit_convert() # end nutritional-info callbacks ### END METHODS FOR DISPLAYING CURRENT NUTRITIONAL INFO ### BEGIN METHODS FOR CUSTOM NUTRITIONAL INTERFACE def _setup_custom_box (self): """Setup the interface for entering custom nutritional information. """ t = gtk.Table() masses = [i[0] for i in defaults.UNIT_GROUPS['metric mass']\ + defaults.UNIT_GROUPS['imperial weight']] cb.set_model_from_list( self.massUnitComboBox, masses) cb.cb_set_active_text(self.massUnitComboBox,'g') self.customNutritionAmountEntry.set_value(100) self.nutrition_info = {} self.custom_box.add(t) self.changing_percent_internally = False self.changing_number_internally = False l=gtk.Label('%RDA'); l.show() t.attach(l,2,3,0,1) for n,nutstuff in enumerate(NUT_LAYOUT): if nutstuff == SEP: hs = gtk.HSeparator() t.attach(hs,0,2,n+1,n+2,xoptions=gtk.FILL) hs.show() continue label_txt,typ,name,properties,show_percent,unit = nutstuff if unit: label_txt += " (" + unit + ")" label = gtk.Label(label_txt); label.show() label.set_alignment(0,0.5) t.attach(label,0,1,n+1,n+2,xoptions=gtk.FILL) entry = NumberEntry(default_to_fractions=False) entry.show() t.attach(entry,1,2,n+1,n+2,xoptions=gtk.FILL) if show_percent: percent_entry = NumberEntry(default_to_fractions=False, decimals=0) percent_entry.entry.set_width_chars(4) percent_entry.show() percent_label = gtk.Label('%'); percent_label.show() t.attach(percent_entry,2,3,n+1,n+2) t.attach(percent_label,3,4,n+1,n+2) percent_label.set_alignment(0,0.5) percent_entry.connect('changed',self.percent_changed_cb,name,entry) percent_entry.entry.set_width_chars(5) else: percent_entry = None entry.connect('changed',self.number_changed_cb,name,percent_entry) t.set_row_spacings(6) t.set_col_spacings(12) t.show() def number_changed_cb (self, widget, name, percent_widget): v = widget.get_value() self.nutrition_info[name]=v if not v: return if self.changing_number_internally: return if percent_widget: rda = RECOMMENDED_INTAKE.get(name,None)*2000 if rda: self.changing_percent_internally = True percent_widget.set_value((float(v)/rda)*100) self.changing_percent_internally = False def percent_changed_cb (self, widget, name, number_widget): if self.changing_percent_internally: return v = widget.get_value() if not v: return if number_widget: rda = RECOMMENDED_INTAKE.get(name,None)*2000 if rda: self.changing_number_internally = True number_widget.set_value( v*0.01*rda ) self.changing_number_internally = False def custom_unit_changed (self, *args): amount = self.customNutritionAmountEntry.get_value() unit = cb.cb_get_active_text(self.massUnitComboBox) if amount and unit: base_convert = self.nd.conv.converter(unit,'g')/float(100) self.custom_factor = 1/(base_convert * amount) def apply_custom (self, *args): nutinfo = self.nutrition_info.copy() for k,v in nutinfo.items(): if type(v)==int or type(v)==float: nutinfo[k]=v*self.custom_factor # Special case fat, which is listed as one item but is in # fact a combination of 3. We'll have to fudge the info # about mono- v. poly- unsaturated fats. if k=='fat': totfat = v * self.custom_factor unsatfat = totfat - nutinfo.get('fasat',0) del nutinfo['fat'] nutinfo['fapoly']=unsatfat # Fudge nutinfo['desc']=self.ingkey ndbno = self.nd.add_custom_nutrition_info(nutinfo) ### END METHODS FOR CUSTOM NUTRITIONAL INTERFACE ### METHODS TO SET CURRENT ITEM AND UNIT INFO def set_ingkey (self, txt): self.ingKeyEntry.set_text(txt) self.ingKeyLabel.set_markup('<i><b>'+txt+'</b></i>') # USDA Page self.ingKeyLabel2.set_markup('<i><b>'+txt+'</b></i>') # Custom Page self.nutrition_info['desc']=txt self.ingkey = txt ### BEGIN METHODS FOR SETTING UNIT EQUIVALENTS def set_from_unit (self, txt): if not self.ingkey: return if txt: self.fromUnitLabel.set_text(txt) self.fromUnitComboBoxEntry.get_children()[0].set_text(txt) self.fromUnit = txt curamt = ' '.join([convert.float_to_frac(self.amount, fractions=convert.FRACTIONS_ASCII), self.fromUnit,self.ingkey]) else: self.fromUnitLabel.set_text(self.ingkey+' (no unit)') self.fromUnit = '' curamt = convert.float_to_frac( self.amount, fractions=convert.FRACTIONS_ASCII)+' '+self.ingkey self.convertUnitLabel.set_markup( '<span weight="bold" size="larger">' + \ _('Convert unit for %s')%self.ingkey + \ '</span>' + \ '\n<i>' + \ _('In order to calculate nutritional information, Gourmet needs you to help it convert "%s" into a unit it understands.')%curamt + \ '</i>') def setup_to_units (self): """Setup list of units we need to convert to. Usually, this will be a list of mass units. """ masses = [i[0] for i in defaults.UNIT_GROUPS['metric mass'] + defaults.UNIT_GROUPS['imperial weight']] volumes = [i[0] for i in defaults.UNIT_GROUPS['metric volume'] + defaults.UNIT_GROUPS['imperial volume']] to_units = masses self.densities,self.extra_units = self.nd.get_conversions(self.ingkey) for d in self.densities.keys(): if d: to_units.extend(["%s (%s)"%(u,d) for u in volumes]) else: to_units.extend(volumes) to_units.sort() for u in self.extra_units: to_units = [u]+to_units cb.set_model_from_list(self.toUnitCombo, to_units) self.toUnitCombo.set_active(0) self.toUnitCombo.set_wrap_width(3) def apply_amt_convert (self,*args): to_unit = cb.cb_get_active_text(self.toUnitCombo) base_convert = self.nd.conv.converter('g',to_unit) if not base_convert: self.densities,self.extra_units = self.nd.get_conversions(self.ingkey) if self.extra_units.has_key(to_unit): base_convert = 1/self.extra_units[to_unit] else: # this is a density, we hope... if to_unit.find(' (')>0: to_unit,describer = to_unit.split(' (') describer = describer[0:-1] density = self.densities[describer] else: if not self.densities.has_key(None): raise RuntimeError("Unable to make sense of conversion from %s %s"%(to_unit,self.ingkey)) density = self.densities[None] base_convert = self.nd.conv.converter('g',to_unit,density=density) to_amount = convert.frac_to_float(self.toAmountEntry.get_text()) from_amount = convert.frac_to_float(self.fromAmountEntry.get_text()) ratio = from_amount / to_amount factor = base_convert * ratio from_unit = self.fromUnit self.nd.set_conversion(self.ingkey,from_unit,factor) ### END METHODS FOR SETTING UNIT EQUIVALENTS ### BEGIN METHODS FOR SEARCHING USDA INDEX def autosearch_ingkey (self): """Automatically do a search for our current ingkey. We're pretty smart about this: in other words, we won't do a search that doesn't have results. """ self.usdaIndex.set_search(self.ingkey) def apply_nut_equivalent (self,*args): nut = self.usdaIndex.get_selected_usda_item() self.nd.set_key_from_ndbno(self.ingkey,nut) # Now see if we need to do any conversion or not self.setup_to_units() ### END METHODS FOR SEARCHING USDA INDEX ### BEGIN CALLBACKS FOR QUICK-CHANGES OF INGREDIENT KEY / UNIT def apply_ingkey (self,*args): key = self.ingKeyEntry.get_text() if key==self.ingkey: self.changeIngKeyAction.dehighlight_action() return #ings = self.rd.fetch_all(self.rd.ingredients_table,ingkey=self.ingkey) #self.rd.modify_ings(ings,{'ingkey':key}) if self.rec: try: user_says_yes = de.getBoolean( label=_('Change ingredient key'), sublabel=_( 'Change ingredient key from %(old_key)s to %(new_key)s everywhere or just in the recipe %(title)s?' )%{'old_key':self.ingkey, 'new_key':key, 'title':self.rec.title }, custom_no=_('Change _everywhere'), custom_yes=_('_Just in recipe %s')%self.rec.title ) except de.UserCancelledError: self.changeIngKeyAction.dehighlight_action() return else: if not de.getBoolean(label=_('Change ingredient key'), sublabel=_('Change ingredient key from %(old_key)s to %(new_key)s everywhere?' )%{'old_key':self.ingkey, 'new_key':key, }, cancel=False, ): self.changeIngKeyAction.dehighlight_action() return if self.rec and user_says_yes: self.rd.update_by_criteria(self.rd.ingredients_table, {'ingkey':self.ingkey, 'recipe_id':self.rec.id}, {'ingkey':key} ) else: self.rd.update_by_criteria(self.rd.ingredients_table, {'ingkey':self.ingkey}, {'ingkey':key} ) old_key = self.ingkey self.set_ingkey(key) # Update amounts dictionary... self.amounts[key] = self.amounts[old_key] del self.amounts[old_key] self.autosearch_ingkey() self.changeIngKeyAction.dehighlight_action() if self.nd.get_nutinfo(key): self.setup_to_units() self.check_next_amount() self.emit('key-changed',(old_key,key)) def save_unit_cb (self,*args): from_unit = self.fromUnitComboBoxEntry.get_children()[0].get_text() old_from_unit = self.fromUnit #ings = self.rd.fetch_all(self.rd.ingredients_table,ingkey=self.ingkey,unit=old_from_unit) #self.rd.modify_ings(ings,{'unit':from_unit}) if self.rec and de.getBoolean( label=_('Change unit'), sublabel=_( 'Change unit from %(old_unit)s to %(new_unit)s for all ingredients %(ingkey)s or just in the recipe %(title)s?' )%{'old_unit':old_from_unit, 'new_unit':from_unit, 'ingkey':self.ingkey, 'title':self.rec.title }, custom_no=_('Change _everywhere'), custom_yes=_('_Just in recipe %s')%self.rec.title ): self.rd.update_by_criteria(self.rd.ingredients_table, {'ingkey':self.ingkey, 'unit':old_from_unit, 'recipe_id':self.rec.id}, {'unit':from_unit} ) else: self.rd.update_by_criteria(self.rd.ingredients_table, {'ingkey':self.ingkey, 'unit':old_from_unit}, {'unit':from_unit} ) self.set_from_unit(self.fromUnitComboBoxEntry.get_children()[0].get_text()) self.changeUnitAction.dehighlight_action() self.emit('unit-changed',((old_from_unit,self.ingkey),(from_unit,self.ingkey))) ### END CALLBACKS FOR QUICK-CHANGES OF INGREDIENT KEY / UNIT ### BEGIN METHODS FOR DENSITY-CHOOSING INTERFACE def get_density (self,amount,unit): self.densityLabel.set_text( _("""In order to calculate nutritional information for "%(amount)s %(unit)s %(ingkey)s", Gourmet needs to know its density. Our nutritional database has several descriptions of this food with different densities. Please select the correct one below.""")%({'amount':amount,'unit':unit,'ingkey':self.ingkey}) ) for c in self.densityBox.get_children(): self.densityBox.remove(c) c.unparent() group = None def density_callback (rb, name): self.custom_density = name for d in self.densities.keys(): group = gtk.RadioButton(group,str(d)+' '+'(%.2f)'%self.densities[d]) group.connect('toggled',density_callback,d) self.densityBox.pack_start(group,expand=False,fill=False) group.show() group.set_active(True) self.custom_density = d self.goto_page_density() def apply_density (self): self.nd.set_density_for_key( self.ingkey, self.custom_density ) for c in self.densityBox.get_children(): c.hide() ### END METHODS FOR DENSITY CHANGING INTERFACE ### BEGIN CALLBACKS TO WALK THROUGH INGREDIENTS def add_ingredients (self, inglist, full_inglist=[]): """Add a list of ingredients for our druid to guide the user through. Our ingredient list is in the following form for, believe it or not, good reason: [(ingkey, [(amount,unit),(amount,unit),(amount,unit)]), (ingkey, [(amount,unit),(amount,unit),(amount,unit)]), ... ] The ingkey is a string, of course. amount can be a float or None unit can be a string or None For each item in the list, we will ask the user to select a USDA equivalent. Once we've done that, we'll check if the user needs to convert the unit as well. """ # to start, we take our first ing self.inglist = inglist if not full_inglist: if self.rec: self.full_inglist = [] for i in self.rd.get_ings(self.rec): self.full_inglist.append(i.ingkey) self.def_ingredient_amounts[i.ingkey] = (i.amount,i.unit) else: self.full_inglist = [] for ingkey,amounts_and_units in self.inglist: self.full_inglist.append(ingkey) if amounts_and_units: self.def_ingredient_amounts[ingkey] = amounts_and_units[0] self.ing_index = 0 self.setup_next_ing() def setup_next_ing (self): """Move to next ingredient.""" if self.ing_index >= len(self.inglist): self.finish() return ing = self.inglist[self.ing_index] self.ing_index+=1 if not ing: return ingkey,amounts = ing self.ing_to_index[ingkey] = self.ing_index self.amounts[ingkey] = amounts self.amount_index = 0 self.set_ingkey(ingkey) if not self.nd.get_nutinfo(ingkey): self.edit_nutinfo() self.path.append((self.edit_nutinfo,[ingkey])) else: self.setup_to_units() self.check_next_amount() def edit_nutinfo (self, ingkey=None, desc=None): self.amounts[ingkey or desc] = self.get_amounts_and_units_for_ingkey(ingkey) self.amount_index = 0 if ingkey: self.set_ingkey(ingkey) if desc: self.usdaIndex.set_search(desc) else: self.autosearch_ingkey() self.goto_page_key_to_nut() ing_index = self.ing_to_index.get(ingkey,None) if ing_index: self.ing_index = ing_index def check_next_amount (self): """Check the next amount on our amounts list. If the amount is already convertible, we don't do anything. If the amount is not convertible, we ask our user for help! """ if self.amount_index >= len(self.amounts[self.ingkey]): self.setup_next_ing() return amount,unit = self.amounts[self.ingkey][self.amount_index] if not amount: amount=1 self.amount = amount self.amount_index += 1 existing_conversion = self.nd.get_conversion_for_amt(amount,unit,self.ingkey,fudge=True) existing_conversion_fudged = (existing_conversion and (not self.nd.get_conversion_for_amt(amount,unit,self.ingkey,fudge=False) )) if existing_conversion_fudged: self.get_density(amount,unit) elif existing_conversion: self.check_next_amount() else: self.edit_units(amount, unit, self.ingkey) self.path.append((self.edit_units, [amount,unit,self.ingkey,self.amount_index]) ) def edit_units (self, amount, unit, ingkey, indx=None): self.set_ingkey(ingkey) self.set_from_unit(unit) if indx is not None: self.amount_index = indx self.fromAmountEntry.set_text(convert.float_to_frac(amount, fractions=convert.FRACTIONS_ASCII) ) self.toAmountEntry.set_text(convert.float_to_frac(amount, fractions=convert.FRACTIONS_ASCII) ) self.goto_page_unit_convert() def previous_page_cb (self, *args): """Move to the previous item in self.path PATH ITEMS are in the form: (CUSTOM_METHOD,ARGS) We'll call CUSTOM_METHOD(ARGS) """ self.path.pop() # pop off current page... method,args = self.path[-1] if callable(method): method(*args) else: # for convenience, if the method isn't callable, we take # it to be a page self.notebook.set_current_page(method) if len(self.path) <= 1: self.prevDruidButton.set_sensitive(False) return def apply_cb (self, *args): page = self.notebook.get_current_page() if page == self.NUT_PAGE: self.apply_nut_equivalent() self.check_next_amount() elif page == self.UNIT_PAGE: self.apply_amt_convert() # if out of amounts, this will move to the next ingredient self.check_next_amount() elif page == self.CUSTOM_PAGE: if not self.custom_factor: de.show_message(_("To apply nutritional information, Gourmet needs a valid amount and unit.")) return self.apply_custom() self.check_next_amount() elif page == self.DENSITY_PAGE: self.apply_density() self.check_next_amount() self.curpage += 1 self.prevDruidButton.set_sensitive(True) def ignore_cb (self, *args): page = self.notebook.get_current_page() self.curpage += 1 self.prevDruidButton.set_sensitive(True) if page == self.NUT_PAGE: self.setup_next_ing() else: self.check_next_amount() ### END CALLBACKS TO WALK THROUGH INGREDIENTS ### BEGIN CONVENIENCE METHODS FOR SWITCHING PAGES def goto_page_key_to_nut (self): for b in [self.applyButton,self.ignoreButton]: b.show() for b in [self.editButton]: b.hide() self.notebook.set_current_page(self.NUT_PAGE) def goto_page_unit_convert(self): for b in [self.applyButton,self.ignoreButton]: b.show() for b in [self.editButton]: b.hide() self.notebook.set_current_page(self.UNIT_PAGE) def goto_page_custom (self): for b in [self.applyButton,self.ignoreButton]: b.show() for b in [self.editButton]: b.hide() self.notebook.set_current_page(self.CUSTOM_PAGE) def goto_page_density (self): for b in [self.applyButton,self.ignoreButton]: b.show() for b in [self.editButton]: b.hide() self.notebook.set_current_page(self.DENSITY_PAGE) def goto_page_index (self): for b in [self.editButton]: b.show() for b in [self.applyButton,self.ignoreButton]: b.hide() self.notebook.set_current_page(self.INDEX_PAGE) def goto_page_info (self): for b in [self.editButton,self.applyButton,self.ignoreButton]: b.hide() self.notebook.set_current_page(self.INFO_PAGE) ### END CONVENIENCE METHODS FOR SWITCHING PAGES def custom_cb (self, *args): self.goto_page_custom() def usda_cb (self, *args): self.goto_page_key_to_nut() ### BEGIN METHODS FOR STARTING AND FINISHING def show (self): self.ui.get_object('window').show() def finish (self): # When done -- goto nutritional index page... if not hasattr(self,'nutInfoIndex'): self.setup_nutrition_index() else: self.nutInfoIndex.reset() self.goto_page_index() def close (self, *args): self.ui.get_object('window').hide() self.emit('finish')