Welcome, guest | Sign In | My Account | Store | Cart
# Author: Miguel Martinez Lopez
#
# Please let me know if you find a bug
# Uncomment the next line to see my email
#   print("Author's email: %s"%"61706c69636163696f6e616d656469646140676d61696c2e636f6d".decode("hex"))

"""Tkinter Selectors
    Select widgets using selectors synchronously or asynchronously.

    This is the provided API:
        toplevel_titles(widget)
        window_titles(widget)
        full_name_of_widgets(root)
        find_toplevel_by_title(widget, title)
        find_widgets_by_name(root, name)
        find_widget_by_name(root, name)
        find_widgets_by_class(root, class_)
        find_widget_by_class(root, class_)
        find(root, selector, callback, async=True)
        find_all(root, selector, callback, async=True)
        config(root, selector, **kwargs)
        config_async(root, selector, **kwargs)
        config_all(root, selector, **kwargs)
        config_all_async(root, selector, **kwargs)
        tk_print(root, name=True, class_= True, indent="
\t")
"""

# Python 2/3 compatibility
import sys

PY3
= sys.version_info[0] ==3

if PY3:
  basestring
= str
  unicode
= str

class SubSelector(unicode):
   
def __add__(self, txt):
       
return self.__class__(unicode(self)+txt)

class SubSelector_Name(SubSelector):
   
"""Subselector that match the widget name"""

class SubSelector_Class(SubSelector):
   
"""Subselector that match the widget class"""

class Tkinter_Selector(object):

   
def __init__(self, selector_string):

       
if len(selector_string) == 0:
           
raise ValueError("selector can't be zero length")

       
if selector_string[0] == ".":
           
self._anchor_start = True
            last_char
= "."
            selector_string
= selector_string[1:]

       
else:            
           
self._anchor_start = False
           
           
if selector_string[0] == "*":
                last_char
= "*"
                selector_string
= selector_string[1:]
           
else:
                last_char
= None


       
if selector_string[-1] == "*" or selector_string[-1] == ".":
           
raise ValueError("'%s' is not allowed at the end"%selector_string[-1])

        selector_obj
= []
       
        sequence_of_selector_elements
= []
       
        selector_element
= None

       
for position, char in enumerate(selector_string):
           
if char == ".":
               
if last_char == "." or last_char == "*":
                   
raise ValueError("Not allowed '%s' at position %d"%(last_char, position))
               
                sequence_of_selector_elements
.append(selector_element)

                selector_element
= None
           
elif char == "*":
               
if last_char == "." or last_char == "*":
                   
raise ValueError("Not allowed '%s' at position %d"%(last_char, position))
   
                sequence_of_selector_elements
.append(selector_element)
                selector_element
= None

                selector_obj
.append(sequence_of_selector_elements)
                sequence_of_selector_elements
= []
           
else:
               
if selector_element is None:
                   
if char.isupper():
                        selector_element
= SubSelector_Class(char)
                   
else:
                        selector_element
= SubSelector_Name(char)
               
else:
                    selector_element
+= char
               
            last_char
= char

       
if selector_element is not None:
            sequence_of_selector_elements
.append(selector_element)

       
if len(sequence_of_selector_elements) != 0:
            selector_obj
.append(sequence_of_selector_elements)

       
       
self._selector_obj = selector_obj
       
self._selector_string = selector_string

   
def match(self, root, callback, async=True):
        queue
= [(root, 0, 0)]

       
if async:
           
def async_match():
               
self._match(queue, callback)

               
if len(queue) != 0:
                    root
.after(0, async_match)
            async_match
()
       
else:
           
while len(queue) != 0:
               
self._match(queue, callback)

   
def _match(self, queue, callback):
        widget
, index_1, index_2 = queue.pop(0)
        selector_element
= self._selector_obj[index_1][index_2]

       
if len(self._selector_obj) == index_1 + 1 and len(self._selector_obj[-1]) == index_2 + 1:
           
for child in self._matched_children(widget, selector_element):
               
try:
                    callback
(child)
               
except StopIteration:
                   
del queue[:]
                   
return
       
else:
           
if len(self._selector_obj[index_1]) == index_2 +1:
                new_index_1
= index_1 + 1
                new_index_2
= 0
           
else:
                new_index_1
= index_1
                new_index_2
= index_2 + 1

           
for child in self._matched_children(widget, selector_element):
                queue
.insert(0, (child, new_index_1, new_index_2))

       
if index_2 == 0 and not (self._anchor_start and index_1 == 0):
           
for child in widget.children.values():
                queue
.append((child, index_1, 0))
           
   
def _matched_children(self, widget, selector_element):
       
for child_name, child in widget.children.items():
           
if isinstance(selector_element, SubSelector_Name):
               
if child_name == selector_element:
                   
yield child
           
else:
               
if child.winfo_class() == selector_element:
                   
yield child
   
@property
   
def anchor_start(self):
       
return self._anchor_start
   
   
def __str__(self):
       
return self._selector_string

def iterate(root):
    queue_of_widgets
= root.children.values()

   
while True:
           
       
if len(queue_of_widgets) == 0:
           
return
       
else:
           
yield queue_of_widgets.pop()
            queue
.extend(widget.children.values())


def get_root(widget):
   
if str(widget) == ".":
        root
= widget
   
else:
        root
= widget.nametowidget(".")
   
return root
           
def find_toplevel_by_title(widget, title):
    root
= get_root(widget)

   
if  isinstance(title, re._pattern_type):
       
for child in root.children.values():
           
if child.winfo_class() == "Toplevel" and title.search(child.wm_title()):
               
return child
   
else:
       
for child in root.children.values():
           
if child.winfo_class() == "Toplevel" and child.wm_title() == title:
               
return child

def toplevel_titles(widget):
    root
= get_root(widget)

    list_of_titles
= []

   
for child in root.children.values():
       
if child.winfo_class() == "Toplevel":
            list_of_titles
.append(child.wm_title())

   
return list_of_titles

def window_titles(widget):
    root
= get_root(widget)

   
return [root.wm_title()] + list_toplevel_titles(root)

def find_widgets_by_name(root, name):
    list_of_found_widgets
= []
   
    queue
= root.children.items()

   
while True:
       
        widget_name
, widget = queue.pop()
       
if widget_name == name:
            list_of_found_widgets
.append(widget)
           
       
if len(queue) == 0:
           
return list_of_found_widgets
       
else:
            queue
.extend(widget.children.items())

def find_widget_by_name(root, name):
    queue
= root.children.items()

   
while True:
       
        widget_name
, widget = queue.pop()
       
if widget_name == name:
           
return widget
           
       
if len(queue) == 0:
           
return
       
else:
            queue
.extend(widget.children.items())
   
def find_widgets_by_class(root, class_):
    list_of_found_widgets
= []
   
   
for widget in iterate(root):
       
if widget.winfo_class() == class_:
            list_of_found_widgets
.append(widget)
       
   
return list_of_found_widgets

def find_widget_by_class(root, class_):
   
for widget in iterate(root):
       
if widget.winfo_class() == class_:
           
return widget

def full_name_of_widgets(root):
    names
= [str(root)]

    queue_of_widgets
= root.children.values()

   
while True:

       
if len(queue_of_widgets) == 0:
           
return names
       
else:
            names
.append(str(queue_of_widgets.pop()))
            queue_of_widgets
.extend(widget.children.values())
 

def find_all(root, selector, callback, async=True):
    list_of_found_widgets
= []
   
   
if isinstance(selector, basestring):
        selector
= Tkinter_Selector(selector)

    selector
.match(root, callback, async)

def find(root, selector, callback, async=True):
   
if isinstance(selector, basestring):
        selector
= Tkinter_Selector(selector)

   
def wrapper_callback(widget):
        callback
(widget)
       
raise StopIteration

    selector
.match(root, wrapper_callback, async)

def config_extended(root, selector, async, config_kwargs):
   
if isinstance(selector, basestring):
        selector
= Tkinter_Selector(selector)
   
   
def callback(widget, config_kwargs=config_kwargs):
        widget
.configure(**config_kwargs)
       
raise StopIteration

    selector
.match(root, callback, async)

def config_all_extended(root, selector, async, config_kwargs):
   
if isinstance(selector, basestring):
        selector
= Tkinter_Selector(selector)
   
    selector
.match(root, lambda widget, config_kwargs = config_kwargs: widget.configure(**config_kwargs), async)


def config(root, selector, **kwargs):
    config_extended
(root, selector, False, kwargs)

def config_async(root, selector, **kwargs):
    config_extended
(root, selector, True, kwargs)

def config_all(root, selector, **kwargs):
    config_all_extended
(root, selector, False, kwargs)

def config_all_async(root, selector, **kwargs):
    config_all_extended
(root, selector, True, kwargs)

def tk_print(root, name=True, class_= True, indent="\t"):
   
"""Print a tree of widget names and/or classes"""
   
print(root)
   
   
if name or class_:
        _print_widget_tree_from
(root, name, class_, 1, indent)

def _print_widget_tree_from(root, print_names, print_classes, level, indent):
   
for child_name, child in root.children.items():
       
       
if print_names:
           
if print_classes:
                output
= "%s (%s)"%(child_name, child.winfo_class())
           
else:
                output
= child_name
       
else:
            output
= child.winfo_class()

       
print(indent*level + output)
        _print_widget_tree_from
(child, print_names, print_classes, level+1, indent)
   


if __name__ == "__main__":
   
try:
       
from Tkinter import Tk, Frame, Label, Button, LabelFrame
   
except ImportError:
       
from tkinter import Tk, Frame, Label, Button, LabelFrame
   
    root
= Tk()
   
    container1
= LabelFrame(root, class_="Container1")
    container1
.pack()
   
    left
= Frame(container1, name="left")
    left
.pack()
   
   
Label(left, name="label0").pack()
   
    center
= Frame(container1, name="center")
    center
.pack()
   
    area1
= Frame(center, class_="Area1")
    area1
.pack()
   
    area2
= Frame(center, class_="Area2")
    area2
.pack()
   
   
Button(area2, name="my_button").pack()
   
Button(area2).pack()
   
    area3
= Frame(center, class_="Area3")
    area3
.pack()
   
    area4
= Frame(center, class_="Area4")
    area4
.pack()
   
    child_frame
= Frame(area4, class_="Child_Frame")
    child_frame
.pack()
   
    child_label
= Label(area4)
    child_label
.pack()
   
    right
= Frame(container1, name="right")
    right
.pack()
   
    container2
= Frame(root, class_="Container2")
    container2
.pack()

    tk_print
(root)

   
def show(widget):
       
print(widget)

    find_all
(root, "Container1*Label", show, False)
    find
(root, "Container1*label0", show)
    find_all
(root, "center*Button", show)
    find_all
(root, ".Container1.left.label0", show)
   
    config_all
(root, "Container1*Label", text="this is a label")
    config
(root, "Container1*label0", text="this is a label number 0")
    config_all
(root, "Container1*Button", text="this is a button")
    config
(root, "Container1", text="this is Container 1")

    root
.mainloop()

Diff to Previous Revision

--- revision 5 2017-01-24 20:43:31
+++ revision 6 2017-01-24 20:45:34
@@ -5,7 +5,7 @@
 
#   print("Author's email: %s"%"61706c69636163696f6e616d656469646140676d61696c2e636f6d".decode("hex"))
 
 
"""Tkinter Selectors
-    Select widgets using selectors synchronously or asynchronously-
+    Select widgets using selectors synchronously or asynchronously.
 
     This is the provided API:
         toplevel_titles(widget)

History