[Python-checkins] r62479 - in sandbox/trunk/ttk-gsoc: README lib-tk lib-tk/Ttk.py samples samples/combo_themes.py samples/theme_selector.py

guilherme.polo python-checkins at python.org
Thu Apr 24 17:52:15 CEST 2008


Author: guilherme.polo
Date: Thu Apr 24 17:52:14 2008
New Revision: 62479

Log:
Initial code, not complete and several design questions for now.
A short README is included telling what is required to run the
samples included.


Added:
   sandbox/trunk/ttk-gsoc/README
   sandbox/trunk/ttk-gsoc/lib-tk/
   sandbox/trunk/ttk-gsoc/lib-tk/Ttk.py
   sandbox/trunk/ttk-gsoc/samples/
   sandbox/trunk/ttk-gsoc/samples/combo_themes.py
   sandbox/trunk/ttk-gsoc/samples/theme_selector.py

Added: sandbox/trunk/ttk-gsoc/README
==============================================================================
--- (empty file)
+++ sandbox/trunk/ttk-gsoc/README	Thu Apr 24 17:52:14 2008
@@ -0,0 +1,13 @@
+Testing Code
+============
+
+You will need Python with a _tkinter compiled against Tcl/Tk 8.5 or 
+Tile[1] has to be installed. I have tested it with python-trunk and 
+release25-maint repos.
+
+Given that you have the requirements, you should be able to run the
+samples passing the appropriate PYTHONPATH or placing the Ttk module
+somewhere in sys.path.
+
+
+[1] I didn't test it with Tile yet, not sure if it will be detected.

Added: sandbox/trunk/ttk-gsoc/lib-tk/Ttk.py
==============================================================================
--- (empty file)
+++ sandbox/trunk/ttk-gsoc/lib-tk/Ttk.py	Thu Apr 24 17:52:14 2008
@@ -0,0 +1,354 @@
+"""Ttk wrapper.
+
+This module provides classes to allow using Tk themed widget set.
+
+Ttk is based on a revised and enhanced version of 
+TIP #48 (http://tip.tcl.tk/48) specified style engine. 
+
+Its basic idea is to separate, to the extent possible, the code 
+implementing a widget's behavior from the code implementing its 
+appearance. Widget class bindings are primarily responsible for 
+maintaining the widget state and invoking callbacks, all aspects 
+of the widgets appearance lies at Themes.
+"""
+
+__version__ = "Not ready for use, and incomplete"
+
+import Tkinter
+import _tkinter
+
+# Check if Ttk is available for use
+if Tkinter.TkVersion < 8.5:
+    # Tk version is "too old", but if Tile is around Ttk still can be used
+    # XXX All this verbosity below could be removed.
+    # XXX Looking at FixTk.py it seems something for tile should be added.
+    _tkinter.create(
+        None,   # screename
+        '',     # basename
+        '',     # className
+        0,      # interactive
+        0,      # wantobjects
+        1,      # wantTk, setting this to False Tk_Init doesn't get called,
+                # but eval won't work then
+        0,      # sync
+        None    # use
+        ).eval('package require tile')
+
+# If we are here, then Ttk is available for use =)
+
+class Style(object):
+    """Manipulate style database."""
+    
+    def style_configure(self, style, **kw):
+        """Sets the default value of the especified option(s) in style."""
+        # XXX I renamed this method so it doesn't conflict with Misc.configure
+        #     but maybe this one could be removed.
+        raise NotImplementedError
+
+
+    def map(self, style, **kw):
+        raise NotImplementedError
+
+
+    def lookup(self, style, option, state="normal", default=None): 
+        """Returns the value specified for option for given style in
+        state, using the standard lookup rules for element options.
+        state is a list of state names; if omitted, it defaults to
+        "normal". If the default argument is present, it is used as a
+        fallback value in case no specification for option is
+        found."""
+        # XXX Redo this docstring after understanding how lookup
+        #     works
+        raise NotImplementedError
+
+
+    def layout(self, style, layoutspec=None):
+        """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:
+
+        LAYOUTS
+            
+            A layout specifies a list of elements, each followed by 
+            one or more 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:
+
+                side: whichside
+                    Specifies which side of the cavity to place the 
+                    element; one of top, right, bottom or left. If 
+                    omitted, the element occupies the entire cavity.
+
+                sticky: nswe
+                    Specifies where the element is placed inside its 
+                    allocated parcel.
+
+                children: [sublayout... ]
+                    Specifies a list of elements to place inside the 
+                    element
+        """
+        # XXX I haven't used this yet, but it looks like a more 
+        #     appropriate way of passing layoutspec should be done
+        return self.tk.call("ttk::style", "layout", style, layoutspec)
+
+
+    def element_create(self, elementname, type, **kw): 
+        """Create a new element in the current theme of given type."""
+        raise NotImplementedError
+
+
+    def element_names(self):
+        """Returns the list of elements defined in the current theme."""
+        return self.tk.call("ttk::style", "element", "names")
+
+
+    def element_options(self, element):
+        """Return the list of element's options."""
+        return self.tk.call("ttk::style", "element", "options", element)
+
+
+    def theme_create(self, themename, **kw):
+        raise NotImplementedError
+
+
+    def theme_settings(self, themename, script):
+        """Temporarily sets the current theme to themename, evaluate 
+        script, then restore the previous theme."""
+        raise NotImplementedError
+
+
+    def theme_names(self):
+        """Returns a list of all known themes."""
+        return self.tk.call("ttk::style", "theme", "names")
+
+
+    def theme_use(self, themename):
+        """Sets the current theme to themename, and refreshes all widgets."""
+        self.tk.call("ttk::style", "theme", "use", themename)
+
+
+class Widget(Tkinter.Widget, Style): 
+    # XXX after rereading the ttk description, it looks like 
+    #     this class shouldn't subclass Style
+    """Base class for Tk themed widgets."""
+
+    def __init__(self, master, widgetName, cnf={}, kw={}, extra=()):
+        """Constructs a Ttk Widget with the parent master.
+
+        STANDARD OPTIONS
+
+            class, cursor, takefocus, style
+
+        SCROLLABLE WIDGET OPTIONS
+
+            xscrollcommand, yscrollcommand
+
+        LABEL WIDGET OPTIONS
+
+            text, textvariable, underline, image, compound, width
+
+        WIDGET STATES
+
+            active, disabled, focus, pressed, selected, background,
+            readonly, alternate, invalid
+        """
+        Tkinter.Widget.__init__(self, master, widgetName, cnf, kw, extra)
+
+
+    def instate(self, statespec, callback=None, *args):
+        """Test the widget's state. 
+        
+        If callback is not especified, returns 1 if the widget state 
+        matches statespec and 0 otherwise. If callback is specified, 
+        then it will be invoked with *args if the widget state matches 
+        statespec."""
+        ret = self.tk.call(self._w, "instate", statespec)
+        if ret and callback:
+            return callback(*args)
+
+        return ret
+
+
+    def state(self, statespec=None):
+        """Modify or inquire widget state.
+
+        Widget state is returned if statespec is None, otherwise it is
+        set according to the statespec flags and then a new state spec
+        is returned indicating which flags were changed."""
+        return self.tk.call(self._w, "state", statespec)
+
+
+class Button(Widget):
+    """Ttk Button widget, displays a textual label and/or image, and 
+    evaluates a command when pressed."""
+
+    def __init__(self, master=None, cnf={}, **kw):
+        """Construct a Ttk Button widget with the parent master.
+
+        STANDARD OPTIONS
+            
+            class, compound, cursor, image, state, style, takefocus,
+            text, textvariable, underline, width
+
+        WIDGET-SPECIFIC OPTIONS
+
+            command, default, width
+        """
+
+        Widget.__init__(self, master, "ttk::button", cnf, kw)
+
+
+class Checkbutton(Widget):
+    """Ttk Checkbutton widget which is either in on- or off-state."""
+
+    def __init__(self, master=None, cnf={}, **kw):
+        """Construct a Ttk Checkbutton widget with the parent master.
+
+        STANDARD OPTIONS
+            
+            class, compound, cursor, image, state, style, takefocus,
+            text, textvariable, underline, width
+
+        WIDGET-SPECIFIC OPTIONS
+
+            command, offvalue, onvalue, variable
+        """
+        Widget.__init__(self, master, "ttk::checkbutton", cnf, kw)
+
+
+    def invoke(self):
+        """Toggles between the selected and deselected states and 
+        invoke the associated command. If the widget is currently 
+        selected, sets the option variable to the option offvalue 
+        option and deselects the widget; otherwise, sets the option
+        variable to the option onvalue.
+
+        Returns the result of the associated command."""
+        return self.tk.call(self._w, "invoke")
+
+
+class Entry(Widget, Tkinter.Entry):
+    """Ttk Entry widget displays a one-line text string and allows that 
+    string to be edited by the user."""
+
+    def __init__(self, master=None, cnf={}, **kw):
+        """Constructs a Ttk Entry widget with the parent master.
+        
+        STANDARD OPTIONS
+            
+            class, cursor, style, takefocus, xscrollcommand
+
+        WIDGET-SPECIFIC OPTIONS
+
+            exportselection, invalidcommand, justify, show, state,
+            textvariable, validate, validatecommand, width
+
+        VALIDATION MODES
+            
+            none, key, focus, focusin, focusout, all
+        """
+        Widget.__init__(self, master, "ttk::entry", cnf, kw)
+
+    # XXX All this following thing will be removed soon
+    #
+    # Tkinter.Entry defines:
+    #   delete, get, icursor, index, insert, selection clear, 
+    #   selection present, selection range, xview, xview moveto, 
+    #   xview scroll
+    #
+    # Ttk.Widget defines:
+    #   instate, state
+    #
+    # Misc (Ttk.Widget -> Tkinter.Widget -> Misc) defines:
+    #   cget, configure
+    #
+    # This leaves bbox, identify and validate to be defined here
+
+    def bbox(self, index):
+        """Return a tuple of (x, y, width, height) which describes the
+        bounding box of the character given by index."""
+        return self.tk.call(self._w, "bbox", index)
+
+
+    def identify(self, x, y):
+        """Returns the name of the element at position x, y, or the 
+        empty string if the coordinates are outside the window."""
+        return self.tk.call(self._w, "identify", x, y)
+
+
+    def validate(self):
+        """Force revalidation, independent of the conditions specified 
+        by the validate option. Returns 0 if validation fails, 1 if it 
+        succeeds. Sets or clears the invalid state accordingly."""
+        return self.tk.call(self._w, "validate")
+
+
+class Combobox(Entry):
+    """Ttk Combobox widget combines a text field with a pop-down list of 
+    values."""
+
+    def __init__(self, master=None, cnf={}, **kw):
+        """Construct a Ttk Combobox widget with the parent master.
+
+        STANDARD OPTIONS
+            
+            class, cursor, style, takefocus
+
+        WIDGET-SPECIFIC OPTIONS
+
+            exportselection, justify, height, postcommand, state, 
+            textvariable, values, width
+        """
+        Widget.__init__(self, master, "ttk::combobox", cnf, kw)
+
+    
+    def current(self, newindex=None):
+        """If newindex is supplied, sets the combobox value to the element
+        at position newindex in the list of values. Otherwise, returns
+        the index of the current value in the list of values or - 1
+        if the current value does not appear in the list."""
+        return self.tk.call(self._w, "current", newindex)
+
+
+    def set(self, value):
+        """Sets the value of the combobox to value."""
+        self.tk.call(self._w, "set", value)
+
+
+class Frame(Widget):
+    """Ttk Frame widget is a container, used to group other widgets 
+    together."""
+
+    def __init__(self, master=None, cnf={}, **kw):
+        """Construct a Ttk Frame with parent master.
+
+        STANDARD OPTIONS
+            
+            class, cursor, style, takefocus
+
+        WIDGET-SPECIFIC OPTIONS
+
+            borderwidth, relief, padding, width, height
+        """
+        Widget.__init__(self, master, "ttk::frame", cnf, kw)
+
+
+class Label(Widget):
+    """Ttk Label widget displays a textual label and/or image."""
+    
+    def __init__(self, master=None, cnf={}, **kw):
+        """Construct a Ttk Label with parent master.
+
+        STANDARD OPTIONS
+            
+            class, compound, cursor, image, style, takefocus, text,
+            textvariable, underline, width
+
+        WIDGET-SPECIFIC OPTIONS
+
+            anchor, background, font, foreground, justify, padding,
+            relief, text, wraplength
+        """
+        Widget.__init__(self, master, "ttk::label", cnf, kw)

Added: sandbox/trunk/ttk-gsoc/samples/combo_themes.py
==============================================================================
--- (empty file)
+++ sandbox/trunk/ttk-gsoc/samples/combo_themes.py	Thu Apr 24 17:52:14 2008
@@ -0,0 +1,48 @@
+"""Ttk Theme Selector.
+
+Although it is a theme selector, you won't notice many changes since
+there is only a combobox and a frame around.
+"""
+import Ttk
+
+class App(Ttk.Frame):
+    def __init__(self):
+        Ttk.Frame.__init__(self)
+        self._setup_widgets()
+
+    def _change_theme(self, event):
+        if event.widget.current(): # value #0 is not a theme
+            newtheme = event.widget.get()
+            # change to the new theme and refresh all the widgets
+            self.theme_use(newtheme)                         
+
+    def _setup_widgets(self):
+        # XXX Not how we have to convert from tuple to list (to add a new
+        #     element) and then convert to tuple again so Combobox displays
+        #     it correctly. This shouldn't be needed, and I will see what
+        #     can be done towards this.
+        themes = list(self.theme_names())
+        themes.insert(0, "Pick a theme")
+        # Create a readonly Combobox which will display 4 values at max, 
+        # which will cause it to create a scrollbar if there are more
+        # than 4 values in total.
+        themes_combo = Ttk.Combobox(self, values=tuple(themes), 
+            state="readonly", height=4)
+        themes_combo.set(themes[0]) # sets the combobox value to "Pick a theme"
+        # Combobox widget generates a <<ComboboxSelected>> virtual event 
+        # when the user selects an element. This event is generated after 
+        # the listbox is unposted (after you select an item, the combobox's
+        # listbox disappears, then it is said that listbox is now unposted).
+        themes_combo.bind("<<ComboboxSelected>>", self._change_theme)
+        themes_combo.pack(fill='x')
+
+        self.pack(fill='both', expand=1)
+        
+
+def main(): 
+    app = App()
+    app.master.title("Ttk Combobox")
+    app.mainloop()
+
+if __name__ == "__main__":
+    main()

Added: sandbox/trunk/ttk-gsoc/samples/theme_selector.py
==============================================================================
--- (empty file)
+++ sandbox/trunk/ttk-gsoc/samples/theme_selector.py	Thu Apr 24 17:52:14 2008
@@ -0,0 +1,60 @@
+"""Ttk Theme Selector v2. 
+
+This is an improvement from the other theme selector (themes_combo.py)
+since now you can notice theme changes in Ttk Ttk Combobox, Ttk Frame,
+Ttk Label and Ttk Button.
+"""
+import Tkinter
+import Ttk
+
+class App(Ttk.Frame):
+    def __init__(self):
+        Ttk.Frame.__init__(self, borderwidth=3)
+
+        # XXX Ideally I wouldn't want to create a Tkinter.IntVar to make
+        #     it work with Checkbutton variable option.
+        self.theme_autochange = Tkinter.IntVar(self, 0)
+        self._setup_widgets()
+
+    def _change_theme(self):
+        self.theme_use(self.themes_combo.get())
+
+    def _theme_sel_changed(self, widget):
+        if self.theme_autochange.get():
+            self._change_theme()
+
+    def _setup_widgets(self):
+
+        themes_lbl = Ttk.Label(self, text="Themes")
+
+        themes = self.theme_names()
+        self.themes_combo = Ttk.Combobox(self, values=themes, state="readonly")
+        self.themes_combo.set(themes[0])
+        self.themes_combo.bind("<<ComboboxSelected>>", self._theme_sel_changed)
+
+        change_btn = Ttk.Button(self, text='Change Theme', 
+            command=self._change_theme) # XXX how can I set Ttk Button height?
+       
+        theme_change_checkbtn = Ttk.Checkbutton(self, 
+            text="Change themes when combobox item is activated", 
+            variable=self.theme_autochange)
+
+        themes_lbl.grid(ipadx=6, sticky="w")
+        self.themes_combo.grid(row=0, column=1, padx=6, sticky="ew")
+        change_btn.grid(row=0, column=2, padx=6, sticky="e")
+        theme_change_checkbtn.grid(row=1, columnspan=3, sticky="w", pady=6)
+
+        top = self.winfo_toplevel()
+        top.rowconfigure(0, weight=1)
+        top.columnconfigure(0, weight=1)
+        self.columnconfigure(1, weight=1)
+        self.grid(row=0, column=0, sticky="nsew", columnspan=3, rowspan=2)
+
+
+def main(): 
+    app = App()
+    app.master.title("Theme Selector")
+    app.mainloop()
+
+if __name__ == "__main__":
+    main()


More information about the Python-checkins mailing list