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

guilherme.polo python-checkins at python.org
Mon Apr 28 03:14:45 CEST 2008


Author: guilherme.polo
Date: Mon Apr 28 03:14:45 2008
New Revision: 62552

Log:
Ttk.Widget no longer subclasses Ttk.Style, now you should
instantiate Ttk.Style in order to deal with styles in general.

I've renamed style_configure (Ttk.Style) to configure, which
is the correct name. Methods configure, map and lookup of
Ttk.Style are now wrapped.

Checking for Tile presence is more adequate now, probably only
some environment checks missing now.

Updated samples so they work with the updated Ttk.Style.

Updated README so it matches my current experiments. Also added
some references so I know where to find them.

Added a new sample to demonstrate widget states and a bit of
font styling.


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

Modified: sandbox/trunk/ttk-gsoc/README
==============================================================================
--- sandbox/trunk/ttk-gsoc/README	(original)
+++ sandbox/trunk/ttk-gsoc/README	Mon Apr 28 03:14:45 2008
@@ -1,13 +1,25 @@
 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.
+You will need Python with a _tkinter compiled against Tcl/Tk 8.5 
+otherwise Tile[1] has to be installed. I have tested it under Linux
+with python 2.5.2 (Ubuntu package), python-trunk and release25-maint 
+repos.
 
-Given that you have the requirements, you should be able to run the
+Given that you meet 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.
+[1] I've tested it with Tile and it works, but I would recommend 
+    upgrading to Tcl/Tk 8.5 so you get antialiased fonts and other 
+    nice features.
+
+
+References
+==========
+
+What I'm using as a reference to know how complete the project is:
+
+Changes in Tcl/Tk 8.5  -  http://wiki.tcl.tk/10630
+Tk Commands  -  http://www.tcl.tk/man/tcl8.5/TkCmd/contents.htm

Modified: sandbox/trunk/ttk-gsoc/lib-tk/Ttk.py
==============================================================================
--- sandbox/trunk/ttk-gsoc/lib-tk/Ttk.py	(original)
+++ sandbox/trunk/ttk-gsoc/lib-tk/Ttk.py	Mon Apr 28 03:14:45 2008
@@ -14,52 +14,64 @@
 
 __version__ = "Not ready for use, and incomplete"
 
+__author__ = "Guilherme Polo <ggpolo at gmail.com>"
+
+__all__ = ["Button", "Checkbutton", "Combobox", "Entry", "Frame", "Label", 
+           "Style", "Widget"]
+
 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')
+# Verify if Tk is new enough to not need Tile checking
+REQUIRE_TILE = True if Tkinter.TkVersion < 8.5 else False
+
+# XXX Looking at FixTk.py it seems something like TILE_LIBRARY env check
+#     should be added there and possibly here before tile package check.
+def _setup(tksetup):
+    # Extend default Tkinter.BaseWidget._setup staticmethod so we can be
+    # sure that Ttk is available for use, or not.
+    def f(self, master, cnf):
+        tksetup(self, master, cnf)
+        if REQUIRE_TILE:
+            self.tk.eval('package require tile')
+        
+    return f
+
+Tkinter.BaseWidget._setup = _setup(Tkinter.BaseWidget._setup)
 
-# If we are here, then Ttk is available for use =)
 
 class Style(object):
     """Manipulate style database."""
-    
-    def style_configure(self, style, **kw):
+   
+    _name = "ttk::style"
+
+    def __init__(self, master=None):
+        if not master:
+            master = Tkinter._default_root
+            if not master:
+                raise RuntimeError("Too early to manipulate styles")
+
+        self.tk = master.tk
+
+    def configure(self, style, *args):
         """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
+        return self.tk.call(self._name, "configure", style, *args)
 
 
-    def map(self, style, **kw):
-        raise NotImplementedError
+    def map(self, style, statespec):
+        self.tk.call(self._name, "map", style, statespec)
 
 
-    def lookup(self, style, option, state="normal", default=None): 
+    def lookup(self, style, option, state=None, 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
+        # XXX Re-write this docstring
+        return self.tk.call(self._name, "lookup", style, option, state, 
+                            default)
 
 
     def layout(self, style, layoutspec=None):
@@ -89,9 +101,9 @@
                     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)
+        # 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)
 
 
     def element_create(self, elementname, type, **kw): 
@@ -101,12 +113,12 @@
 
     def element_names(self):
         """Returns the list of elements defined in the current theme."""
-        return self.tk.call("ttk::style", "element", "names")
+        return self.tk.call(self._name, "element", "names")
 
 
     def element_options(self, element):
         """Return the list of element's options."""
-        return self.tk.call("ttk::style", "element", "options", element)
+        return self.tk.call(self._name, "element", "options", element)
 
 
     def theme_create(self, themename, **kw):
@@ -121,17 +133,15 @@
 
     def theme_names(self):
         """Returns a list of all known themes."""
-        return self.tk.call("ttk::style", "theme", "names")
+        return self.tk.call(self._name, "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)
+        self.tk.call(self._name, "theme", "use", themename)
 
 
-class Widget(Tkinter.Widget, Style): 
-    # XXX after rereading the ttk description, it looks like 
-    #     this class shouldn't subclass Style
+class Widget(Tkinter.Widget):
     """Base class for Tk themed widgets."""
 
     def __init__(self, master, widgetName, cnf={}, kw={}, extra=()):
@@ -251,20 +261,6 @@
         """
         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
@@ -305,10 +301,10 @@
 
     
     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."""
+        """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)
 
 

Modified: sandbox/trunk/ttk-gsoc/samples/combo_themes.py
==============================================================================
--- sandbox/trunk/ttk-gsoc/samples/combo_themes.py	(original)
+++ sandbox/trunk/ttk-gsoc/samples/combo_themes.py	Mon Apr 28 03:14:45 2008
@@ -8,20 +8,22 @@
 class App(Ttk.Frame):
     def __init__(self):
         Ttk.Frame.__init__(self)
+
+        self.style = Ttk.Style()
         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)                         
+            self.style.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 = list(self.style.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

Modified: sandbox/trunk/ttk-gsoc/samples/theme_selector.py
==============================================================================
--- sandbox/trunk/ttk-gsoc/samples/theme_selector.py	(original)
+++ sandbox/trunk/ttk-gsoc/samples/theme_selector.py	Mon Apr 28 03:14:45 2008
@@ -10,6 +10,8 @@
 class App(Ttk.Frame):
     def __init__(self):
         Ttk.Frame.__init__(self, borderwidth=3)
+        
+        self.style = Ttk.Style()
 
         # XXX Ideally I wouldn't want to create a Tkinter.IntVar to make
         #     it work with Checkbutton variable option.
@@ -17,7 +19,7 @@
         self._setup_widgets()
 
     def _change_theme(self):
-        self.theme_use(self.themes_combo.get())
+        self.style.theme_use(self.themes_combo.get())
 
     def _theme_sel_changed(self, widget):
         if self.theme_autochange.get():
@@ -27,7 +29,7 @@
 
         themes_lbl = Ttk.Label(self, text="Themes")
 
-        themes = self.theme_names()
+        themes = self.style.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)

Added: sandbox/trunk/ttk-gsoc/samples/widget_state.py
==============================================================================
--- (empty file)
+++ sandbox/trunk/ttk-gsoc/samples/widget_state.py	Mon Apr 28 03:14:45 2008
@@ -0,0 +1,83 @@
+"""Sample demo showing widget states and some font styling."""
+import Ttk
+
+states = ['active', 'disabled', 'focus', 'pressed', 'selected', 
+    'background', 'readonly', 'alternate', 'invalid']
+
+for state in states[:]:
+    states.append("!" + state)
+
+def reset_state(widget):
+    nostate = ' '.join(states[len(states) / 2:])
+    widget.state(nostate)
+
+class App(Ttk.Frame):
+    def __init__(self, title=None):
+        Ttk.Frame.__init__(self, borderwidth=6)
+        self.master.title(title)
+
+        self.style = Ttk.Style()
+
+        # get default font size and family
+        button_font = self.style.lookup("TButton", "-font")
+        font_size = self.tk.eval("font configure %s -size" % button_font)
+        font_size = int(font_size[1:])
+        font_family = self.tk.eval("font configure %s -family" % button_font)
+
+        self.font_family = font_family
+        self.base_font_size = font_size
+
+        # a list to hold all the widgets that will have their states changed
+        self.update_widgets = [] 
+
+        self._setup_widgets()
+
+    def _set_font(self, extra=0):
+        self.style.configure("TButton", "-font", 
+            "%s -%d" % (self.font_family, self.base_font_size + extra))
+
+    def _new_state(self, widget, newtext):
+        widget = self.nametowidget(widget)
+
+        if not newtext:
+            goodstates = ["disabled"]
+            font_extra = 0
+        else: 
+            # set widget state according to what has been entered in the entry
+            newstates = set(newtext.split()) # eliminate duplicates
+
+            # keep only the valid states
+            goodstates = [state for state in newstates if state in states]
+            # define a new font size based on amount of states
+            font_extra = 2 * len(goodstates)
+
+        # set new widget state
+        for widget in self.update_widgets:
+           reset_state(widget) # remove any previous state from the widget
+           widget.state(' '.join(goodstates))
+
+        # update Ttk Button font size
+        self._set_font(font_extra)
+        return 1
+
+    def _setup_widgets(self):
+        btn = Ttk.Button(self, text='Enter states and watch')
+
+        entry = Ttk.Entry(self, cursor='xterm', validate="key")
+        entry['validatecommand'] = (self.register(self._new_state), '%W', '%P')
+        entry.focus()
+
+        self.update_widgets.append(btn)
+        entry.validate()
+
+        entry.pack(fill='x', padx=6)
+        btn.pack(side='left', pady=6, padx=6, anchor='n')
+        self.pack(fill='both', expand=1)
+
+
+def main():
+    app = App("Widget State Tester")
+    app.mainloop()
+
+if __name__ == "__main__":
+    main()


More information about the Python-checkins mailing list