[Python-checkins] r62714 - sandbox/trunk/ttk-gsoc/Lib/lib-tk/Ttk.py

guilherme.polo python-checkins at python.org
Mon May 5 00:30:44 CEST 2008


Author: guilherme.polo
Date: Mon May  5 00:30:44 2008
New Revision: 62714

Log:
_format_optdict accepts sequences as values now and format them
correctly according to the script parameter, this function also
accepts an ignore parameter to discard elements (used by 
_format_layoutdict).

Renamed LabelFrame to Labelframe.

ttk::style layout has been wrapped (after redoing it several times
this weekend), updated layout's docstring so it matches the current
implementation, updated theme_settings's docstring so it says that
'layout' key is accepted now.

added the function _format_layoutdict which does all the job
related to ttk::style layout.


Modified:
   sandbox/trunk/ttk-gsoc/Lib/lib-tk/Ttk.py

Modified: sandbox/trunk/ttk-gsoc/Lib/lib-tk/Ttk.py
==============================================================================
--- sandbox/trunk/ttk-gsoc/Lib/lib-tk/Ttk.py	(original)
+++ sandbox/trunk/ttk-gsoc/Lib/lib-tk/Ttk.py	Mon May  5 00:30:44 2008
@@ -44,13 +44,27 @@
 
 Tkinter.Tk._loadtk = _loadttk(Tkinter.Tk._loadtk)
 
-def _format_optdict(optdict):
+def _format_optdict(optdict, script=False, ignore=None):
     """Formats optdict to pass it to tk.call.
     
-    E.g.: 
-      {'foreground': 'blue'} returns:
-      ('-foreground', 'blue')"""
-    opts = [("-%s" % opt, value) for opt, value in optdict.iteritems()]
+    E.g. (script=False): 
+      {'foreground': 'blue', 'padding': [1, 2, 3, 4]} returns:
+      ('-foreground', 'blue', '-padding', '1 2 3 4')"""
+    format = "%s" if not script else "{%s}"
+
+    opts = []
+    for opt, value in optdict.iteritems():
+        if ignore and opt in ignore:
+            continue
+
+        # hasattr is used to guarantee that value is not an int, and isinstance
+        # guarantees it is not a string too
+        if hasattr(value, "__len__") and not isinstance(value, basestring):
+            # value is expected to be a sequence
+            value = format % ' '.join(map(str, value))
+        
+        opts.append(("-%s" % opt, value))
+
     return Tkinter._flatten(opts)
 
 def _format_mapdict(mapdict, script=False):
@@ -85,6 +99,63 @@
     
     return Tkinter._flatten(opts)
 
+def _format_layoutdict(layout, indent=2, child=False):
+    """Formats a layout dict so we can pass the result to ttk::style 
+    layout and ttk::style map.
+    
+    E.g.:
+      {"Menubutton.background": None,
+       "Menubutton.button": {"children": [("Menubutton.focus", 
+                                {"children": [("Menubutton.padding", 
+                                    {"children": [("Menubutton.label", 
+                                        {"side": "left", "expand": 1})]
+                                    })
+                                ]})
+                            ]},
+       "Menubutton.indicator": {"side": "right"}
+      }) 
+
+      returns:
+
+      Menubutton.background
+      Menubutton.button -children {
+        Menubutton.focus -children {
+          Menubutton.padding -children {
+            Menubutton.label -side left -expand 1
+          }
+        }
+      }
+      Menubutton.indicator -side right"""
+    # XXX right now I'm concatenating the text a lot of times, I expect
+    #     to change this to list appending and then use some joins and 
+    #     (ab)use some flattening.
+    text = ""
+
+    if child:
+        sublayouts = [layout.pop("children")] # sublayouts needing processing
+        func = sublayouts.pop
+    else:
+        func = layout.iteritems # will find sublayouts for processing
+
+    for elem, opts in func():
+        opts = opts or {}
+        text += "%s%s" % (' ' * indent, elem)
+
+        fopts = ' '.join(map(str, _format_optdict(opts, ignore="children")))
+        text += " %s" % fopts if fopts else ''
+
+        if "children" in opts:
+            text += " -children {\n"
+            indent += 2
+            newtext, indent = _format_layoutdict(opts, indent, True)
+            indent -= 2
+            text += newtext
+            text += '%s}' % (' ' * indent)
+
+        text += '\n'
+    
+    return text, indent
+
 def _script_from_settings(settings):
     """Returns an appropriate script, based on settings, according to 
     theme_settings definition to be used by theme_settings and 
@@ -95,7 +166,7 @@
     for name, opts in settings.iteritems():
         # format 'configure' according to Tcl code
         if opts.get('configure'):
-            s = ' '.join(map(str, _format_optdict(opts['configure'])))
+            s = ' '.join(map(str, _format_optdict(opts['configure'], True)))
             script.append("ttk::style configure %s %s;" % (name, s))
 
         # format 'map' according to Tcl code
@@ -103,6 +174,11 @@
             s = ' '.join(map(str, _format_mapdict(opts['map'], True)))
             script.append("ttk::style map %s %s;" % (name, s))
 
+        # format 'layout' according to Tcl code
+        if opts.get('layout'):
+            s = _format_layoutdict(opts['layout'])
+            script.append("ttk::style layout %s {\n%s}" % (name, s))
+
     return '\n'.join(script)
 
 def _dict_from_tcltuple(t):
@@ -148,8 +224,8 @@
     def configure(self, style, **kw):
         """Sets the default value of the specified option(s) in style.
         
-        Each key in kw is an option and each value is a string identifying
-        the value for that option."""
+        Each key in kw is an option and each value is either a string or
+        a sequence identifying the value for that option."""
         opts = _format_optdict(kw)
         return self.tk.call(self._name, "configure", style, *opts)
 
@@ -183,15 +259,16 @@
         """Define the widget layout for given style. If layoutspec is
         omitted, return the layout specification for given style.
         
-        The format of layoutspec is described below:
+        layoutspec is expected to be a dict, where each key is a layout
+        name and the value format is described below:
 
         LAYOUTS
             
-            A layout specifies a list of elements, each followed by 
-            one or more options specifying how to arrange the element. 
+            A layout can contain the value None, if takes no options, or
+            another dict of options specifying how to arrange the element.
             The layout mechanism uses a simplified version of the pack
             geometry manager: given an initial cavity, each element is
-            allocated a parcel. Valid options are:
+            allocated a parcel. Valid options/values are:
 
                 side: whichside
                     Specifies which side of the cavity to place the 
@@ -204,11 +281,12 @@
 
                 children: [sublayout... ]
                     Specifies a list of elements to place inside the 
-                    element
+                    element. Each element is a tuple (or other sequence)
+                    where the first item is the layout name, and the other
+                    is a LAYOUT.
         """
-        # XXX A more appropriate way of passing layoutspec is needed,
-        #     right now it is like if we were programming in Tcl
-        return self.tk.call(self._name, "layout", style, layoutspec)
+        return self.tk.call(self._name, "layout", style, 
+            _format_layoutdict(layoutspec)[0])
 
 
     def element_create(self, elementName, etype, **kw): 
@@ -248,9 +326,9 @@
         settings and then restore the previous theme.
 
         Each key in settings is a style and each value may contain the 
-        keys 'configure' and 'map'. 'configure' and 'map' are expected to 
-        have the same format as specified by the methods configure and map 
-        respectively."""
+        keys 'configure', 'map' and 'layout' and they are expected to have
+        the same format as specified by the methods configure, map and
+        layout respectively."""
         script = _script_from_settings(settings)
         self.tk.call(self._name, "theme", "settings", themeName, script)
 
@@ -477,7 +555,7 @@
         Widget.__init__(self, master, "ttk::label", cnf, kw)
 
 
-class LabelFrame(Widget):
+class Labelframe(Widget):
     """Ttk Labelframe widget is a container used to group other widgets
     together. It has an optional label, which may be a plain text string
     or another widget."""
@@ -495,6 +573,8 @@
         """
         Widget.__init__(self, master, "ttk::labelframe", cnf, kw)
 
+LabelFrame = Labelframe # Tkinter name compatibility
+
 
 class Menubutton(Widget):
     """Ttk Menubutton widget displays a textual label and/or image, and


More information about the Python-checkins mailing list