[Python-checkins] r64579 - sandbox/trunk/ttk-gsoc/samples/theming.py

guilherme.polo python-checkins at python.org
Sun Jun 29 01:00:22 CEST 2008


Author: guilherme.polo
Date: Sun Jun 29 01:00:21 2008
New Revision: 64579

Log:
Every widget that needs a factory has one now;
Added sublayouts;
Removed custom layouts since I was using it only for adding sublayouts;
Some other misc changes.


Modified:
   sandbox/trunk/ttk-gsoc/samples/theming.py

Modified: sandbox/trunk/ttk-gsoc/samples/theming.py
==============================================================================
--- sandbox/trunk/ttk-gsoc/samples/theming.py	(original)
+++ sandbox/trunk/ttk-gsoc/samples/theming.py	Sun Jun 29 01:00:21 2008
@@ -5,11 +5,10 @@
 -- Guilherme Polo, 2008.
 """
 
-# ToDO: Add a way to remove options.
+# ToDO: Support element options.
+#       Add a way to remove options.
 #       Add a way to edit images/elements.
-#       Add sublayouts.
 #       Add pre-defined elements for the current theme.
-#       Handling element options.
 #       ...
 
 import sys
@@ -21,19 +20,24 @@
 from tkMessageBox import showwarning
 from tkFileDialog import askopenfilename
 
-def available_widgets():
-    """Returns a list of Ttk widgets."""
+def available_widgets(): # XXX misleading function name
+    """Returns a list of Ttk widgets.""" # XXX misleading docstring
     widgets = {}
     # will discard Style and extension classes
     unwanted_names = ('Style', 'LabeledScale', 'OptionMenu')
     # some widgets contain Vertical and Horizontal layouts
-    special = ('Progressbar', 'Scale', 'Scrollbar')
+    vert_horiz = ('Progressbar', 'Scale', 'Scrollbar')
+    # several widgets contain a single layout named as Twidgetname
+    prefix_t = ('Label', 'Button', 'Checkbutton', 'Radiobutton', 'Menubutton',
+        'Entry', 'Combobox', 'Frame', 'Labelframe', 'Separator', 'Sizegrip')
 
     sample = {'Button': widget_text, 'Checkbutton': widget_text,
         'Label': widget_text, 'Radiobutton': widget_text,
         'Labelframe': widget_text_size, 'Combobox': widget_values,
         'Progressbar': widget_progress, 'Notebook': widget_notebook,
-        'Treeview': widget_treeview, 'Scrollbar': widget_expand}
+        'Treeview': widget_treeview, 'Scrollbar': widget_expand,
+        'Frame': widget_expand, 'Separator': widget_expand,
+        'Menubutton': widget_menubtn, 'Panedwindow': widget_paned}
 
     for name in ttk.__all__:
         if name in unwanted_names:
@@ -46,11 +50,23 @@
 
         widget_d = {'factory': None, 'layouts': None}
 
-        if name in special:
-            widget_d['layouts'] = ('Horizontal', 'Vertical')
+        if name in prefix_t:
+            widget_d['layouts'] = ('T%s' % name, )
+        elif name in vert_horiz:
+            widget_d['layouts'] = ('Horizontal.T%s' % name,
+                'Vertical.T%s' % name)
+        elif name == 'Notebook':
+            widget_d['layouts'] = ('TNotebook', 'Tab')
+        elif name == 'Panedwindow':
+            widget_d['layouts'] = ('TPanedwindow', 'Horizontal.Sash',
+                'Vertical.Sash')
+        elif name == 'Treeview':
+            widget_d['layouts'] = ('Treeview', 'Item', 'Cell', 'Heading', 'Row')
 
         if name in sample:
             widget_d['factory'] = sample[name]
+        if name == 'Scrollbar':
+            widget_d['class'] = 'Vertical.TScrollbar'
 
         widgets[name] = widget_d
 
@@ -94,10 +110,29 @@
         w.insert('', 'end', text="Row %d" % i, values=[i, i + 1])
     return w
 
+def widget_menubtn(widget, master, **kw):
+    """Create a sample menu button with some options."""
+    menu = Tkinter.Menu(tearoff=False)
+    for i in range(5):
+        menu.add_radiobutton(label='Item %d' % i)
+    return widget(master, text="Sample", menu=menu, **kw)
+
+def widget_paned(widget, master, **kw):
+    """Create a sample Panedwindow with two children."""
+    w = widget(master, height=150, **kw)
+    c1 = ttk.Label(w, text="Top")
+    c2 = ttk.Label(w, text="Bottom")
+    w.add(c1)
+    w.add(c2)
+    return w
+
 def widget_expand(widget, master, **kw):
     """Instantiate widget and configure it to expand."""
     w = widget(master, **kw)
-    fill = 'x' if 'horizontal' in str(w['orient']) else 'y'
+    try:
+        fill = 'x' if 'horizontal' in str(w['orient']) else 'y'
+    except Tkinter.TclError:
+        fill = 'both'
     w.pack_configure(expand=True, fill=fill)
     return w
 
@@ -358,31 +393,38 @@
         self.__setup_widgets()
         self.__fill_treeview()
 
-    def _change_preview(self, event):
-        """New treeview selection, update preview area."""
+    def _change_preview(self, event, invalid=False):
+        """New treeview selection (or theme changed), update preview area."""
         treeview = getattr(event, 'widget', event)
-        if not treeview.selection():
-            return
 
-        tv_item = treeview.item(treeview.selection()[0])
+        sel = treeview.selection()
+        if not sel: # nothing selected
+            return
+        
+        sel = sel[0]
+        tv_item = treeview.item(sel)
         widget_name = tv_item['text']
+        widget_style = None
+        opts = {}
+
+        if self._is_layout(widget_name, sel): # not a widget, but a layout
+            self._update_layout_text(widget_name)
+            if 'Horizontal' in widget_name or 'Vertical' in widget_name:
+                opts['orient'] = widget_name.split('.')[0].lower()
+                widget_style = "Custom.%s" % widget_name
+            widget_name = treeview.item(treeview.parent(sel))['text']
+        else:
+            self._empty_layout_text()
+
         widget = self._widget[widget_name]
-        parent = widget['tv_item']
-        if treeview.get_children(parent):
-            return
+        widget_style = widget_style or "Custom.%s" % widget['class']
+        curr_widget = self._current_widget
 
-        complement = ''
-        opts = {}
-        if '.' in widget_name: # horizontal/vertical layout (maybe)
-            complement, widget_name = widget_name.split('.')
-            opts = {'orient': complement.lower()}
-
-        # check if this is a widget or a custom layout
-        if not hasattr(ttk, widget_name): # custom layout
-            self._current_widget['layout'] = "Custom.T%s.%s" % (complement,
-                widget_name)
-            self._update_layout_text()
-            return
+        if not invalid and curr_widget['layout'] == widget_style:
+            if not opts or \
+               str(curr_widget['widget']['orient']) == opts['orient']:
+                # didn't select a new widget/no new options
+                return
 
         # create a sample widget
         if widget.get('factory', None):
@@ -390,30 +432,22 @@
             sample = widget['factory'](widget_class, self._preview_area, **opts)
         else:
             sample = getattr(ttk, widget_name)(self._preview_area, **opts)
-        if widget['class'] is None:
-            widget['class'] = "%s%s%s" % (complement,
-                '.' if complement else '', sample.winfo_class())
-        sample['style'] = 'Custom.%s' % widget['class']
-        if self._current_widget['widget'] is not None:
-            self._current_widget['widget'].pack_forget()
+
+        sample['style'] = widget_style
+        if curr_widget['widget'] is not None:
+            curr_widget['widget'].pack_forget()
         sample.pack(expand=True)
 
-        self._current_widget['layout'] = sample['style']
-        self._current_widget['widget'] = sample
-        self._update_layout_text()
+        curr_widget['layout'] = widget_style
+        curr_widget['widget'] = sample
         self._remove_previous_widgets()
         self._update_style_configframe()
         self._update_style_mapframe()
 
-    def _update_layout_text(self):
-        """Update the layout text for the current widget."""
-        layout_name = self._current_widget['layout']
-        output = cStringIO.StringIO()
-        pprint.pprint(self._style.layout(layout_name), stream=output)
-        layout = output.getvalue()
-        output.close()
-        self.layouttext.delete('1.0', 'end') # clear current text
-        self.layouttext.insert('1.0', layout) # set new text
+    def _is_layout(self, name, selection):
+        """Check if a treeview selection is a layout or a widget."""
+        return (name not in self._widget or
+            self._widget[name]['tv_item'] != selection)
 
     def _remove_previous_widgets(self):
         """Remove widgets from the style frames."""
@@ -442,17 +476,6 @@
         for opt_name, opt_value in options.items():
             self._add_opt_frame(frame, func, layout_name, opt_name, opt_value)
 
-    def _add_custom_layout(self):
-        """Create a new, and empty, custom layout."""
-        result = askstring("New Layout", "Layout name")
-        if result:
-            # create an empty layout
-            self._style.layout(result, '')
-            # create a "semi-widget"
-            treeview = self._tv_custom
-            widget_d = {'factory': None, 'layouts': None}
-            self._add_widget(result, widget_d, treeview)
-
     def _add_opt_frame(self, frame, func, layout_name, opt_name, opt_value):
         """Add a new option to a frame."""
         def change_opt(option, text):
@@ -464,7 +487,7 @@
                 func(layout_name, **{option: text})
             except (SyntaxError, Tkinter.TclError, TypeError):
                 pass
-            return 1
+            return True
 
         lbl = ttk.Label(frame, text=opt_name)
         entry = ttk.Entry(frame)
@@ -477,13 +500,18 @@
         self._current_options = self._current_options or []
         self._current_options.extend([lbl, entry])
 
-    def _change_theme(self, event):
-        """New theme selected at themes combobox, change current theme."""
-        widget = event.widget
-        self._style.theme_use(widget.get())
+    def _update_layout_text(self, layout_name):
+        """Update the layout text for the current widget."""
+        output = cStringIO.StringIO()
+        pprint.pprint(self._style.layout(layout_name), stream=output)
+        layout = output.getvalue()
+        output.close()
+        self._empty_layout_text()
+        self.layouttext.insert('1.0', layout) # set new text
 
-        treeview = self._tv_widgets
-        self._change_preview(treeview)
+    def _empty_layout_text(self):
+        """Clear current text in the layout text widget."""
+        self.layouttext.delete('1.0', 'end')
 
     def _apply_layout(self):
         """Apply the supposed new layout for the current selected widget."""
@@ -492,6 +520,8 @@
             return
 
         text = self.layouttext.get('1.0', 'end')
+        if not text.strip(): # no text
+            return
         # XXX Warning: eval usage!
         self._style.layout(layout, eval(text))
 
@@ -502,8 +532,7 @@
             return
 
         orig_name = widget['layout'][widget['layout'].find('.', 1) + 1:]
-        self._style.layout(widget['layout'], self._style.layout(orig_name))
-        self._update_layout_text()
+        self._update_layout_text(orig_name)
 
     def _ask_new_frame_opt(self, frame, func):
         """Open a dialog asking for a new option to be added in the
@@ -563,37 +592,39 @@
             self._imagelist['values'] = (self._imagelist['values'] or ()) + \
                 (img.name, )
 
+    def _change_theme(self, event):
+        """New theme selected at themes combobox, change current theme."""
+        widget = event.widget
+        self._style.theme_use(widget.get())
+
+        treeview = self._tv_widgets
+        self._change_preview(treeview, invalid=True)
+
     def _add_widget(self, name, opts, treeview=None):
         """Add a new widget to self._widget."""
         treeview = treeview or self._tv_widgets
-        children = opts.pop('layouts') or ()
+        children = opts.pop('layouts')
 
         parent = treeview.insert('', 'end', text=name)
-        self._widget[name] = {'tv_item': parent, 'class': None}
+        self._widget[name] = {'tv_item': parent, 'class': children[0]}
         self._widget[name].update(opts)
 
         for child in children:
-            child_name = '%s.%s' % (child, name)
-            item = treeview.insert(parent, 'end', text=child_name)
-            self._widget[child_name] = {'tv_item': item, 'class': None}
-            self._widget[child_name].update(opts)
+            treeview.insert(parent, 'end', text=child)
 
     def __create_menu(self):
         menu = Tkinter.Menu()
         self.master['menu'] = menu
 
         file_menu = Tkinter.Menu(menu, tearoff=False)
-        #file_menu.add_command(label="Save", underline=0)
         file_menu.add_separator()
-        file_menu.add_command(label="Exit", underline=1,
+        file_menu.add_command(label="Exit", underline=1, accelerator="Ctrl-X",
             command=self.master.destroy)
 
         layout_menu = Tkinter.Menu(menu, tearoff=False)
-        layout_menu.add_command(label="New layout",
-            command=self._add_custom_layout)
 
         menu.add('cascade', menu=file_menu, label="File", underline=0)
-        menu.add('cascade', menu=layout_menu, label="Custom Layouts")
+        self.master.bind('<Control-x>', 'exit')
 
     def __setup_widgets(self):
         """Create and layout widgets."""
@@ -603,7 +634,7 @@
         top = ttk.Frame(paned)
         top.pack(side='top', fill='both', expand=True)
 
-        # top left frame (widget listing, custom layouts, images)
+        # top left frame (widget listing, images)
         left = ttk.Frame(top)
         left.pack(side='left', fill='y')
         # widget listing
@@ -611,22 +642,15 @@
         self._tv_widgets.pack(side='top', fill='y', anchor='w', expand=True)
         self._tv_widgets.heading('#0', text="Widgets")
         self._tv_widgets.bind('<<TreeviewSelect>>', self._change_preview)
-        # custom layouts
-        self._tv_custom = ScrolledTreeview(left, selectmode='browse',
-            height=4)
-        self._tv_custom.pack(side='top', anchor='w')
-        self._tv_custom.heading('#0', text="Custom Layouts")
-        self._tv_custom.bind('<<TreeviewSelect>>', self._change_preview)
 
-        # top right frame (preview, style conf, style map, elements, themes and
-        #                  images)
+        # top right frame (preview, style notebook, images)
         topright = ttk.Frame(top)
-        topright.pack(side='top', fill='both', expand=True, padx=6)
+        topright.pack(side='top', fill='both', expand=True)
 
         # preview area
-        self._preview_area = ttk.Frame(topright, width=200, padding=12)
+        self._preview_area = ttk.Frame(topright, width=200)
         self._preview_area.pack(anchor='center', side='left', expand=True,
-            fill='both')
+            fill='both', padx=12)
 
         # options, images and themes
         frames = ttk.Frame(topright)


More information about the Python-checkins mailing list