Re: Settings and preferences
- From: perriman <chuchiperriman gmail com>
- To: Ulrik Sverdrup <ulrik sverdrup gmail com>
- Cc: Kupfer list <kupfer-list gnome org>
- Subject: Re: Settings and preferences
- Date: Fri, 24 Jul 2009 20:09:38 +0200
Hi
El vie, 24-07-2009 a las 02:37 +0200, Ulrik Sverdrup escribió:
> Hello!
>
> This is intriguing, I've poured over the code and have been thinking.
> It's very important: Kupfer is simple. If you look around, the coding
> style is very declarative. settings.py is very hardcoded. Even though
> the most important job looks great;
What you want to is not simple, it is very complicated. Really it is
simple for the plugins developers but it is not for the core.
> get_plugins_direct, get_plugins_direct and so on is good in the
> main.py module. But for plugins and the larger program, It's not
> flexible enough. We have to differentiate data from the
> implementation, which is why, for example, kupfer ships default
> settings in a default.cfg and not coded in the data. The same should
> be for all preference keys and locations. Python has a strong culture,
> and part of the Zen of python is "Don't repeat yourself". Perhaps I
> try to take this to the extreme with kupfer, but the truth is that we
> don't want to use the kind of Template and macro code you see in a C
> program.
>
> I want to show you how I think *plugin* configuration should look
> like. For the plugin, it should look like *pure magic*.
>
> First, the changes to the google.py module to illustrate the Gconf backend:
>
> Before:
>
> class GoogleSearch (Action):
> def __init__(self):
> Action.__init__(self, _("Search with Google"))
>
> def activate(self, leaf):
> from urllib import urlencode
> search_url = "http://www.google.com/search?"
> # will encode search=text, where `text` is escaped
> query_url = search_url + urlencode({"q": leaf.object, "ie": "utf
> utils.show_url(query_url)
> def get_description(self):
> return _("Search for this term with Google")
> def get_icon_name(self):
> return "gtk-find"
> def item_types(self):
> yield TextLeaf
>
>
> After:
>
> class GoogleSearch (Action):
> GCONF_INTERFACE_LANG = settings.SettingsController.GCONF_PLUGINS +
> '/google/interface_lang'
> GCONF_SEARCH_LANG =
> settings.SettingsController.GCONF_PLUGINS + '/google/search_lang'
>
> def __init__(self):
> Action.__init__(self, _("Search with Google"))
> self.conf = settings.GetSettingsController ().get_client ()
> page_lang = self.conf.get_string (self.GCONF_INTERFACE_LANG)
> if page_lang is None:
> self.conf.set_string (self.GCONF_INTERFACE_LANG, 'en')
> search_lang = self.conf.get_string (self.GCONF_SEARCH_LANG)
> if search_lang is None:
> self.conf.set_string (self.GCONF_SEARCH_LANG, 'lang_en')
>
> def activate(self, leaf):
> from urllib import urlencode
> page_lang = self.conf.get_string (self.GCONF_INTERFACE_LANG)
> if page_lang is None:
> page_lang = 'en'
> search_lang = self.conf.get_string (self.GCONF_SEARCH_LANG)
> if search_lang is None:
> search_lang = 'lang_en'
> search_url = "http://www.google.com/search?"
> # will encode search=text, where `text` is escaped
> query_url = search_url + urlencode({"q": leaf.object,
> "ie": "utf-8",
> "hl": page_lang,
> "meta": "lr=" + search_lang})
> utils.show_url(query_url)
> def get_description(self):
> return _("Search for this term with Google")
> def get_icon_name(self):
> return "gtk-find"
> def item_types(self):
> yield TextLeaf
>
>
>
> Do you see how much logic has to be implemented on the plugin side?
> All these smarts should be in the settings module, and the word Gconf
> shouldn't exist in any other module, it's an implementation detail,
> just like the fact that configuration keys are paths/with/slashes.
> This is why modules exist.. they take care about something, and no
> other module has to care about it. Plugins should be very simple, to
> be understood by those who want to program kupfer to understand some
> specific data. Plugins are very declarative, they say what they have.
> Also consider the case when you duplicate a plugin to make a new one.
> You should change as few lines as possible before you have a
> standalone plugin. Why mention the plugin name in the plugin, when
> kupfer already knows (yes kupfer.plugin.google.GoogleSearch can
> coexist just fine with, for example,
> kupfer.plugin.crazysearch.GoogleSearch)
>
> For illustration here is a prototype of what I would like plugin
> configuration to look like for the google module:
>
> __kupfer_settings__ = [
> { "key": "interface_lang",
> "label": _("Google interface language"),
> "type" : str,
> "value" : "en",
> },
> { "key": "search_lang",
> "label": _("Google search language"),
> "type": str,
> "value": "en",
> },
> )
>
> def read_settings():
> return dict((setting["key"], setting["value"]) for setting in
> __kupfer_settings__)
>
> def get_setting(key):
> return read_settings()[key]
>
> The plugin *declares* what it has. Kupfer can then produce a
> preferences window for this plugin to set the preference keys. I'd
> like this to be so magic, so that when the plugin is loaded, we modify
> its __kupfer_settings__ attribute to fill in the stored config. The
> read_settings and get_settings functions look clumsy, they are only
> for illustration. Perhaps instead of a dicts in list structure, it's
> better with dicts in dict, only that ordering is lost. (Then
> get_setting = lambda key: __kupfer_settings__[key]["value"])
Well, The idea is very complicated, is not simple. All gnome apps uses
the plugin list and a "preferences" button. The plugin creates the
preferences dialog they want. You are thinking in something like the
firefox "about:config" page. If you are thinking in draw in the kupfer
preferences dialog widgets for all the plugins preferences, it is a full
configuration framework (if the option is like about:config it is
complicated too).
IMO, What you think is very complicated for a simple application
configuration. The get_client().set_string() and
get_client().get_string() is a lot of simple than all the preferences
framework what you want. I'll change it to do it easier and I will not
use the gconf object (and gconf word).
Your configuration ideas are good only for the plugin development. The
implementation will be difficult and the preferences dialog will be ugly
for the end user. IMHO, if someone develop a plugin, can set/get the
preferences without problems and can implement a preferences dialog too
(or modify the kupfer preferences dialog to add a new tab etc), it is
not difficult.
>
> Now it's possible for the config to melt in in the code in a natural
> way. No special backend code (gconf) and no special-casing. We declare
> a default and that's good. (If need arises (If, the word is
> important!), we can let plugins check the values in a callback when
> they are changed later).
>
> This should be:
> def activate(self, leaf):
> from urllib import urlencode
> search_url = "http://www.google.com/search?"
> # will encode search=text, where `text` is escaped
> query_url = search_url + urlencode({"q": leaf.object,
> "ie": "utf-8",
> "hl": get_setting("interface_lang"),
> "meta": "lr=" + get_setting("search_lang")})
> utils.show_url(query_url)
>
>
> I'm looking forward to developing this, but I'm wary that it wouldn't
> be kupfer anymore if the simplicity disappears in the code. If you
> think like a plugin, you don't care about how settings work.
The user is more important than the plugin developer.
> Neither
> does any module. They just want to store settings under a key and be
> done with it.
We will do it with simple methods added to settings.py
(SettingsController)
> (Luckily there are not so many). I suggested I could
> code the get/set plugins interface to the config files system that
> exists now. You obviously know lots of things I don't, like GConf and
> how to build a UI file.
I don't know very well nothing ;)
> To begin in that end I think is good, we
> should make a preferences window prototype. And think about how to
> store keys in gconf without having to repeat ourselves ever. (We have
> to have 1. A gconf scheme to register everything when the application
> is installed and 2. modules that want to store preferences. We
> shouldn't have to hardcode much more than that, nothing in between,
> except the defaults somewhere (gconf scheme file?)) However, it might
> be easier to go with the config file solution for now.
I think the next changes will be simple and the plugin will can get and
set configuration with only some methods.
>
> I'm going to go away for a short week, so I will not be able to
> respond at all, but enjoy this nice summer here in spain! (yes I'm
> still here)
Spain forever!!! have fun on your holidays!!
>
> Take care
> ulrik
>
> 2009/7/24 perriman <chuchiperriman gmail com>:
> > Hi,
> >
> > You can take a look at the upstream-integration branch of
> >
> > https://github.com/chuchiperriman/kupfer/tree
> >
> > Now exists SettingsController in settings.py and I have removed
> > GconfStore.py. The preferences dialog still not work but you can take a
> > look at the SettingsController object. I have removed the get_config
> > function and main.py uses the SC now.
> >
> > I have added some configuration to google.py plugin to show how the
> > plugins can use the gconf_client to store their configurations.
> >
> > Please, take a look at the new structure and give me your opinion about
> > it.
> >
> > The next steps are:
> >
> > 1.- When the user change something in the preferences dialog, we need to
> > change it in the settings controller.
> > 2.- Someone (in main.py or browser.py, I need to see) will connect to
> > the SettingsController signals and will change the kupfer objects on the
> > fly when the configuration changes.
> > 3.- We need to decide what preferences we will add to the preferences
> > dialog. I don't know if directories/direct, directories/catalog etc,
> > need to be shown in the preferences dialog or if they are internal.
> > .
> > .
> > .
> > n.- Show the plugin list
> >
> > I go slow but secure :)
> >
> > Perriman
> >
> > _______________________________________________
> > kupfer-list mailing list
> > kupfer-list gnome org
> > http://mail.gnome.org/mailman/listinfo/kupfer-list
> >
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]