Demos for ttk added.
This commit is contained in:
parent
cda93aafde
commit
4eae078448
|
@ -8,3 +8,4 @@ Subdirectories:
|
|||
|
||||
guido my original example set (fairly random collection)
|
||||
matt Matt Conway's examples, to go with his lifesaver document
|
||||
ttk Examples using the ttk module
|
||||
|
|
|
@ -0,0 +1,46 @@
|
|||
"""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.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.style.theme_use(newtheme)
|
||||
|
||||
def _setup_widgets(self):
|
||||
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
|
||||
# than 4 values in total.
|
||||
themes_combo = ttk.Combobox(self, values=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()
|
|
@ -0,0 +1,93 @@
|
|||
"""A directory browser using Ttk Treeview.
|
||||
|
||||
Based on the demo found in Tk 8.5 library/demos/browse
|
||||
"""
|
||||
import os
|
||||
import glob
|
||||
import Tkinter
|
||||
import ttk
|
||||
|
||||
def populate_tree(tree, node):
|
||||
if tree.set(node, "type") != 'directory':
|
||||
return
|
||||
|
||||
path = tree.set(node, "fullpath")
|
||||
tree.delete(*tree.get_children(node))
|
||||
|
||||
parent = tree.parent(node)
|
||||
special_dirs = [] if parent else glob.glob('.') + glob.glob('..')
|
||||
|
||||
for p in special_dirs + os.listdir(path):
|
||||
ptype = None
|
||||
p = os.path.join(path, p).replace('\\', '/')
|
||||
if os.path.isdir(p): ptype = "directory"
|
||||
elif os.path.isfile(p): ptype = "file"
|
||||
|
||||
fname = os.path.split(p)[1]
|
||||
id = tree.insert(node, "end", text=fname, values=[p, ptype])
|
||||
|
||||
if ptype == 'directory':
|
||||
if fname not in ('.', '..'):
|
||||
tree.insert(id, 0, text="dummy")
|
||||
tree.item(id, text=fname)
|
||||
elif ptype == 'file':
|
||||
size = os.stat(p).st_size
|
||||
tree.set(id, "size", "%d bytes" % size)
|
||||
|
||||
|
||||
def populate_roots(tree):
|
||||
dir = os.path.abspath('.').replace('\\', '/')
|
||||
node = tree.insert('', 'end', text=dir, values=[dir, "directory"])
|
||||
populate_tree(tree, node)
|
||||
|
||||
def update_tree(event):
|
||||
tree = event.widget
|
||||
populate_tree(tree, tree.focus())
|
||||
|
||||
def change_dir(event):
|
||||
tree = event.widget
|
||||
node = tree.focus()
|
||||
if tree.parent(node):
|
||||
path = os.path.abspath(tree.set(node, "fullpath"))
|
||||
if os.path.isdir(path):
|
||||
os.chdir(path)
|
||||
tree.delete(tree.get_children(''))
|
||||
populate_roots(tree)
|
||||
|
||||
def autoscroll(sbar, first, last):
|
||||
"""Hide and show scrollbar as needed."""
|
||||
first, last = float(first), float(last)
|
||||
if first <= 0 and last >= 1:
|
||||
sbar.grid_remove()
|
||||
else:
|
||||
sbar.grid()
|
||||
sbar.set(first, last)
|
||||
|
||||
root = Tkinter.Tk()
|
||||
|
||||
vsb = ttk.Scrollbar(orient="vertical")
|
||||
hsb = ttk.Scrollbar(orient="horizontal")
|
||||
|
||||
tree = ttk.Treeview(columns=("fullpath", "type", "size"),
|
||||
displaycolumns="size", yscrollcommand=lambda f, l: autoscroll(vsb, f, l),
|
||||
xscrollcommand=lambda f, l:autoscroll(hsb, f, l))
|
||||
|
||||
vsb['command'] = tree.yview
|
||||
hsb['command'] = tree.xview
|
||||
|
||||
tree.heading("#0", text="Directory Structure", anchor='w')
|
||||
tree.heading("size", text="File Size", anchor='w')
|
||||
tree.column("size", stretch=0, width=100)
|
||||
|
||||
populate_roots(tree)
|
||||
tree.bind('<<TreeviewOpen>>', update_tree)
|
||||
tree.bind('<Double-Button-1>', change_dir)
|
||||
|
||||
# Arrange the tree and its scrollbars in the toplevel
|
||||
tree.grid(column=0, row=0, sticky='nswe')
|
||||
vsb.grid(column=1, row=0, sticky='ns')
|
||||
hsb.grid(column=0, row=1, sticky='ew')
|
||||
root.grid_columnconfigure(0, weight=1)
|
||||
root.grid_rowconfigure(0, weight=1)
|
||||
|
||||
root.mainloop()
|
Binary file not shown.
After Width: | Height: | Size: 101 B |
Binary file not shown.
After Width: | Height: | Size: 80 B |
Binary file not shown.
After Width: | Height: | Size: 101 B |
|
@ -0,0 +1,37 @@
|
|||
"""Sample taken from: http://www.tkdocs.com/tutorial/morewidgets.html and
|
||||
converted to Python, mainly to demonstrate xscrollcommand option.
|
||||
|
||||
grid [tk::listbox .l -yscrollcommand ".s set" -height 5] -column 0 -row 0 -sticky nwes
|
||||
grid [ttk::scrollbar .s -command ".l yview" -orient vertical] -column 1 -row 0 -sticky ns
|
||||
grid [ttk::label .stat -text "Status message here" -anchor w] -column 0 -row 1 -sticky we
|
||||
grid [ttk::sizegrip .sz] -column 1 -row 1 -sticky se
|
||||
grid columnconfigure . 0 -weight 1; grid rowconfigure . 0 -weight 1
|
||||
for {set i 0} {$i<100} {incr i} {
|
||||
.l insert end "Line $i of 100"
|
||||
}
|
||||
"""
|
||||
import Tkinter
|
||||
import ttk
|
||||
|
||||
root = Tkinter.Tk()
|
||||
|
||||
l = Tkinter.Listbox(height=5)
|
||||
l.grid(column=0, row=0, sticky='nwes')
|
||||
|
||||
s = ttk.Scrollbar(command=l.yview, orient='vertical')
|
||||
l['yscrollcommand'] = s.set
|
||||
s.grid(column=1, row=0, sticky="ns")
|
||||
|
||||
stat = ttk.Label(text="Status message here", anchor='w')
|
||||
stat.grid(column=0, row=1, sticky='we')
|
||||
|
||||
sz = ttk.Sizegrip()
|
||||
sz.grid(column=1, row=1, sticky='se')
|
||||
|
||||
root.grid_columnconfigure(0, weight=1)
|
||||
root.grid_rowconfigure(0, weight=1)
|
||||
|
||||
for i in range(100):
|
||||
l.insert('end', "Line %d of 100" % i)
|
||||
|
||||
root.mainloop()
|
|
@ -0,0 +1,78 @@
|
|||
"""Mac style search widget
|
||||
|
||||
Translated from Tcl code by Schelte Bron, http://wiki.tcl.tk/18188
|
||||
"""
|
||||
import Tkinter
|
||||
import ttk
|
||||
|
||||
root = Tkinter.Tk()
|
||||
|
||||
data = """
|
||||
R0lGODlhKgAaAOfnAFdZVllbWFpcWVtdWlxeW11fXF9hXmBiX2ZnZWhpZ2lraGxua25wbXJ0
|
||||
cXR2c3V3dHZ4dXh6d3x+e31/fH6AfYSGg4eJhoiKh4qMiYuNio2PjHmUqnqVq3yXrZGTkJKU
|
||||
kX+asJSWk32cuJWXlIGcs5aYlX6euZeZloOetZial4SftpqbmIWgt4GhvYahuIKivpudmYei
|
||||
uYOjv5yem4ijuoSkwIWlwYmlu56gnYamwp+hnoenw4unvaCin4ioxJCnuZykrImpxZmlsoaq
|
||||
zI2pv6KkoZGouoqqxpqms4erzaOloo6qwYurx5Kqu5untIiszqSmo5CrwoysyJeqtpOrvJyo
|
||||
tZGsw42typSsvaaopZKtxJWtvp6qt4+uy6epppOuxZCvzKiqp5quuZSvxoyx06mrqJWwx42y
|
||||
1JKxzpmwwaqsqZaxyI6z1ZqxwqutqpOzz4+01qyuq56yvpizypS00Jm0y5W10Zq1zJa20rCy
|
||||
rpu3zqizwbGzr6C3yZy4z7K0saG4yp250LO1sqK5y5660Z+70qO7zKy4xaC806S8zba4taG9
|
||||
1KW9zq66x6+7yLi6t6S/1rC8yrm7uLO8xLG9y7q8ubS9xabB2anB07K+zLW+xrO/za7CzrTA
|
||||
zrjAyLXBz77BvbbC0K/G2LjD0bnE0rLK28TGw8bIxcLL07vP28HN28rMycvOyr/T38DU4cnR
|
||||
2s/RztHT0NLU0cTY5MrW5MvX5dHX2c3Z59bY1dPb5Nbb3dLe7Nvd2t3f3NXh797g3d3j5dnl
|
||||
9OPl4eTm4+Ln6tzo9uXn5Obo5eDp8efp5uHq8uXq7ejq5+nr6OPs9Ovu6unu8O3v6+vw8+7w
|
||||
7ezx9O/x7vDy7/Hz8O/19/P18vT38/L3+fb49Pf59vX6/fj69/b7/vn7+Pr8+ff9//v9+vz/
|
||||
+/7//P//////////////////////////////////////////////////////////////////
|
||||
/////////////////////////////////yH/C05FVFNDQVBFMi4wAwEAAAAh+QQJZAD/ACwC
|
||||
AAIAKAAWAAAI/gD/CRz4bwUGCg8eQFjIsGHDBw4iTLAQgqBFgisuePCiyJOpUyBDihRpypMi
|
||||
Lx8qaLhIMIyGFZ5sAUsmjZrNmzhzWpO2DJgtTysqfGDpxoMbW8ekeQsXzty4p1CjRjUXrps3
|
||||
asJsuclQ4uKKSbamMR3n1JzZs2jRkh1HzuxVXX8y4CDYAwqua+DInVrRwMGJU2kDp31KThy1
|
||||
XGWGDlxhi1rTPAUICBBAoEAesoIzn6Vm68MKgVAUHftmzhOCBCtQwQKSoABgzZnJdSMmyIPA
|
||||
FbCotdUQAIhNa9B6DPCAGbZac+SowVIMRVe4pwkA4GpqDlwuAAmMZx4nTtfnf1mO5JEDNy46
|
||||
MHJkxQEDgKC49rPjwC0bqGaZuOoZAKjBPE4NgAzUvYcWOc0QZF91imAnCDHJ5JFAAJN0I2Ba
|
||||
4iRDUC/gOEVNDwIUcEABCAgAAATUTIgWOMBYRFp80ghiAQIIVAAEAwJIYI2JZnUji0XSYAYO
|
||||
NcsQA8wy0hCTwAASXGOiONFcxAtpTokTHznfiLMNMAkcAMuE43jDC0vLeGOWe2R5o4sn1LgH
|
||||
GzkWsvTPMgEOaA433Ag4TjjMuDkQMNi0tZ12sqWoJ0HATMPNffAZZ6U0wLAyqJ62RGoLLrhI
|
||||
aqmlpzwaEAAh+QQJZAD/ACwAAAAAKgAaAAAI/gD/CRw40JEhQoEC+fGjcOHCMRAjRkxDsKLF
|
||||
f5YcAcID582ZjyBDJhmZZIjJIUySEDHiBMhFghrtdNnRAgSHmzhz6sTZQcSLITx+CHn5bxSk
|
||||
Nz5MCMGy55CjTVCjbuJEtSrVQ3uwqDBRQwrFi476SHHxow8qXcemVbPGtm21t3CnTaP27Jgu
|
||||
VHtuiIjBsuImQkRiiEEFTNo2cOTMKV7MuLE5cN68QUOGSgwKG1EqJqJDY8+rZt8UjxtNunTj
|
||||
cY3DgZOWS46KIFgGjiI0ZIsqaqNNjWjgYMUpx8Adc3v2aosNMAI1DbqyI9WycOb4IAggQEAB
|
||||
A3lQBxet/TG4cMpI/tHwYeSfIzxM0uTKNs7UgAQrYL1akaDA7+3bueVqY4NJlUhIcQLNYx8E
|
||||
AIQ01mwjTQ8DeNAdfouNA8440GBCQxJY3MEGD6p4Y844CQCAizcSgpMLAAlAuJ03qOyQRBR3
|
||||
nEHEK+BMGKIui4kDDAAIPKiiYuSYSMQQRCDCxhiziPMYBgDkEaEaAGQA3Y+MjUPOLFoMoUUh
|
||||
cKxRC4ngeILiH8Qkk0cCAUzSDZWpzbLEE1EwggcYqWCj2DNADFDAAQUgIAAAEFDDJmPYqNJF
|
||||
F1s4cscTmCDjDTjdSPOHBQggUAEQDAgggTWDPoYMJkFoUdRmddyyjWLeULMMMcAsIw0x4wkM
|
||||
IME1g25zyxpHxFYUHmyIggw4H4ojITnfiLMNMAkcAAub4BQjihRdDGTJHmvc4Qo1wD6Imje6
|
||||
eILbj+BQ4wqu5Q3ECSJ0FOKKMtv4mBg33Pw4zjbKuBIIE1xYpIkhdQQiyi7OtAucj6dt48wu
|
||||
otQhBRa6VvSJIRwhIkotvgRTzMUYZ6xxMcj4QkspeKDxxRhEmUfIHWjAgQcijEDissuXvCyz
|
||||
zH7Q8YQURxDhUsn/bCInR3AELfTQZBRt9BBJkCGFFVhMwTNBlnBCSCGEIJQQIAklZMXWRBAR
|
||||
RRRWENHwRQEBADs="""
|
||||
|
||||
|
||||
s1 = Tkinter.PhotoImage("search1", data=data, format="gif -index 0")
|
||||
s2 = Tkinter.PhotoImage("search2", data=data, format="gif -index 1")
|
||||
|
||||
style = ttk.Style()
|
||||
|
||||
style.element_create("Search.field", "image", "search1",
|
||||
("focus", "search2"), border=[22, 7, 14], sticky="ew")
|
||||
|
||||
style.layout("Search.entry", [
|
||||
("Search.field", {"sticky": "nswe", "border": 1, "children":
|
||||
[("Entry.padding", {"sticky": "nswe", "children":
|
||||
[("Entry.textarea", {"sticky": "nswe"})]
|
||||
})]
|
||||
})]
|
||||
)
|
||||
|
||||
style.configure("Search.entry", background="#b2b2b2")
|
||||
|
||||
root.configure(background="#b2b2b2")
|
||||
|
||||
e1 = ttk.Entry(style="Search.entry", width=20)
|
||||
e2 = ttk.Entry(style="Search.entry", width=20)
|
||||
|
||||
e1.grid(padx=10, pady=10)
|
||||
e2.grid(padx=10, pady=10)
|
||||
|
||||
root.mainloop()
|
|
@ -0,0 +1,78 @@
|
|||
"""A Ttk Notebook with close buttons.
|
||||
|
||||
Based on an example by patthoyts, http://paste.tclers.tk/896
|
||||
"""
|
||||
import os
|
||||
import Tkinter
|
||||
import ttk
|
||||
|
||||
root = Tkinter.Tk()
|
||||
|
||||
imgdir = os.path.join(os.path.dirname(__file__), 'img')
|
||||
i1 = Tkinter.PhotoImage("img_close", file=os.path.join(imgdir, 'close.gif'))
|
||||
i2 = Tkinter.PhotoImage("img_closeactive",
|
||||
file=os.path.join(imgdir, 'close_active.gif'))
|
||||
i3 = Tkinter.PhotoImage("img_closepressed",
|
||||
file=os.path.join(imgdir, 'close_pressed.gif'))
|
||||
|
||||
style = ttk.Style()
|
||||
|
||||
style.element_create("close", "image", "img_close",
|
||||
("active", "pressed", "!disabled", "img_closepressed"),
|
||||
("active", "!disabled", "img_closeactive"), border=8, sticky='')
|
||||
|
||||
style.layout("ButtonNotebook", [("ButtonNotebook.client", {"sticky": "nswe"})])
|
||||
style.layout("ButtonNotebook.Tab", [
|
||||
("ButtonNotebook.tab", {"sticky": "nswe", "children":
|
||||
[("ButtonNotebook.padding", {"side": "top", "sticky": "nswe",
|
||||
"children":
|
||||
[("ButtonNotebook.focus", {"side": "top", "sticky": "nswe",
|
||||
"children":
|
||||
[("ButtonNotebook.label", {"side": "left", "sticky": ''}),
|
||||
("ButtonNotebook.close", {"side": "left", "sticky": ''})]
|
||||
})]
|
||||
})]
|
||||
})]
|
||||
)
|
||||
|
||||
def btn_press(event):
|
||||
x, y, widget = event.x, event.y, event.widget
|
||||
elem = widget.identify(x, y)
|
||||
index = widget.index("@%d,%d" % (x, y))
|
||||
|
||||
if "close" in elem:
|
||||
widget.state(['pressed'])
|
||||
widget.pressed_index = index
|
||||
|
||||
def btn_release(event):
|
||||
x, y, widget = event.x, event.y, event.widget
|
||||
|
||||
if not widget.instate(['pressed']):
|
||||
return
|
||||
|
||||
elem = widget.identify(x, y)
|
||||
index = widget.index("@%d,%d" % (x, y))
|
||||
|
||||
if "close" in elem and widget.pressed_index == index:
|
||||
widget.forget(index)
|
||||
widget.event_generate("<<NotebookClosedTab>>")
|
||||
|
||||
widget.state(["!pressed"])
|
||||
widget.pressed_index = None
|
||||
|
||||
|
||||
root.bind_class("TNotebook", "<ButtonPress-1>", btn_press, True)
|
||||
root.bind_class("TNotebook", "<ButtonRelease-1>", btn_release)
|
||||
|
||||
# create a ttk notebook with our custom style, and add some tabs to it
|
||||
nb = ttk.Notebook(width=200, height=200, style="ButtonNotebook")
|
||||
nb.pressed_index = None
|
||||
f1 = Tkinter.Frame(nb, background="red")
|
||||
f2 = Tkinter.Frame(nb, background="green")
|
||||
f3 = Tkinter.Frame(nb, background="blue")
|
||||
nb.add(f1, text='Red', padding=3)
|
||||
nb.add(f2, text='Green', padding=3)
|
||||
nb.add(f3, text='Blue', padding=3)
|
||||
nb.pack(expand=1, fill='both')
|
||||
|
||||
root.mainloop()
|
|
@ -0,0 +1,269 @@
|
|||
"""This demonstrates good part of the syntax accepted by theme_create.
|
||||
|
||||
This is a translation of plastik.tcl to python.
|
||||
You will need the images used by the plastik theme to test this. The
|
||||
images (and other tile themes) can be retrived by doing:
|
||||
|
||||
$ cvs -z3 -d:pserver:anonymous@tktable.cvs.sourceforge.net:/cvsroot/tktable \
|
||||
co tile-themes
|
||||
|
||||
To test this module you should do, for example:
|
||||
|
||||
import Tkinter
|
||||
import plastik_theme
|
||||
|
||||
root = Tkinter.Tk()
|
||||
plastik_theme.install(plastik_image_dir)
|
||||
...
|
||||
|
||||
Where plastik_image_dir contains the path to the images directory used by
|
||||
the plastik theme, something like: tile-themes/plastik/plastik
|
||||
"""
|
||||
import os
|
||||
import glob
|
||||
import ttk
|
||||
from Tkinter import PhotoImage
|
||||
|
||||
__all__ = ['install']
|
||||
|
||||
colors = {
|
||||
"frame": "#efefef",
|
||||
"disabledfg": "#aaaaaa",
|
||||
"selectbg": "#657a9e",
|
||||
"selectfg": "#ffffff"
|
||||
}
|
||||
|
||||
imgs = {}
|
||||
def _load_imgs(imgdir):
|
||||
imgdir = os.path.expanduser(imgdir)
|
||||
if not os.path.isdir(imgdir):
|
||||
raise Exception("%r is not a directory, can't load images" % imgdir)
|
||||
for f in glob.glob("%s/*.gif" % imgdir):
|
||||
img = os.path.split(f)[1]
|
||||
name = img[:-4]
|
||||
imgs[name] = PhotoImage(name, file=f, format="gif89")
|
||||
|
||||
def install(imgdir):
|
||||
_load_imgs(imgdir)
|
||||
style = ttk.Style()
|
||||
style.theme_create("plastik", "default", settings={
|
||||
".": {
|
||||
"configure":
|
||||
{"background": colors['frame'],
|
||||
"troughcolor": colors['frame'],
|
||||
"selectbackground": colors['selectbg'],
|
||||
"selectforeground": colors['selectfg'],
|
||||
"fieldbackground": colors['frame'],
|
||||
"font": "TkDefaultFont",
|
||||
"borderwidth": 1},
|
||||
"map": {"foreground": [("disabled", colors['disabledfg'])]}
|
||||
},
|
||||
|
||||
"Vertical.TScrollbar": {"layout": [
|
||||
("Vertical.Scrollbar.uparrow", {"side": "top", "sticky": ''}),
|
||||
("Vertical.Scrollbar.downarrow", {"side": "bottom", "sticky": ''}),
|
||||
("Vertical.Scrollbar.uparrow", {"side": "bottom", "sticky": ''}),
|
||||
("Vertical.Scrollbar.trough", {"sticky": "ns", "children":
|
||||
[("Vertical.Scrollbar.thumb", {"expand": 1, "unit": 1,
|
||||
"children": [("Vertical.Scrollbar.grip", {"sticky": ''})]
|
||||
})]
|
||||
})]
|
||||
},
|
||||
|
||||
"Horizontal.TScrollbar": {"layout": [
|
||||
("Horizontal.Scrollbar.leftarrow", {"side": "left", "sticky": ''}),
|
||||
("Horizontal.Scrollbar.rightarrow",
|
||||
{"side": "right", "sticky": ''}),
|
||||
("Horizontal.Scrollbar.leftarrow",
|
||||
{"side": "right", "sticky": ''}),
|
||||
("Horizontal.Scrollbar.trough", {"sticky": "ew", "children":
|
||||
[("Horizontal.Scrollbar.thumb", {"expand": 1, "unit": 1,
|
||||
"children": [("Horizontal.Scrollbar.grip", {"sticky": ''})]
|
||||
})]
|
||||
})]
|
||||
},
|
||||
|
||||
"TButton": {
|
||||
"configure": {"width": 10, "anchor": "center"},
|
||||
"layout": [
|
||||
("Button.button", {"children":
|
||||
[("Button.focus", {"children":
|
||||
[("Button.padding", {"children":
|
||||
[("Button.label", {"side": "left", "expand": 1})]
|
||||
})]
|
||||
})]
|
||||
})
|
||||
]
|
||||
},
|
||||
|
||||
"Toolbutton": {
|
||||
"configure": {"anchor": "center"},
|
||||
"layout": [
|
||||
("Toolbutton.border", {"children":
|
||||
[("Toolbutton.button", {"children":
|
||||
[("Toolbutton.padding", {"children":
|
||||
[("Toolbutton.label", {"side":"left", "expand":1})]
|
||||
})]
|
||||
})]
|
||||
})
|
||||
]
|
||||
},
|
||||
|
||||
"TMenubutton": {"layout": [
|
||||
("Menubutton.button", {"children":
|
||||
[("Menubutton.indicator", {"side": "right"}),
|
||||
("Menubutton.focus", {"children":
|
||||
[("Menubutton.padding", {"children":
|
||||
[("Menubutton.label", {"side": "left", "expand": 1})]
|
||||
})]
|
||||
})]
|
||||
})]
|
||||
},
|
||||
|
||||
"TNotebook": {"configure": {"tabmargins": [0, 2, 0, 0]}},
|
||||
"TNotebook.tab": {
|
||||
"configure": {"padding": [6, 2, 6, 2], "expand": [0, 0, 2]},
|
||||
"map": {"expand": [("selected", [1, 2, 4, 2])]}
|
||||
},
|
||||
"Treeview": {"configure": {"padding": 0}},
|
||||
|
||||
# elements
|
||||
"Button.button": {"element create":
|
||||
("image", 'button-n',
|
||||
("pressed", 'button-p'), ("active", 'button-h'),
|
||||
{"border": [4, 10], "padding": 4, "sticky":"ewns"}
|
||||
)
|
||||
},
|
||||
|
||||
"Toolbutton.button": {"element create":
|
||||
("image", 'tbutton-n',
|
||||
("selected", 'tbutton-p'), ("pressed", 'tbutton-p'),
|
||||
("active", 'tbutton-h'),
|
||||
{"border": [4, 9], "padding": 3, "sticky": "news"}
|
||||
)
|
||||
},
|
||||
|
||||
"Checkbutton.indicator": {"element create":
|
||||
("image", 'check-nu',
|
||||
('active', 'selected', 'check-hc'),
|
||||
('pressed', 'selected', 'check-pc'),
|
||||
('active', 'check-hu'),
|
||||
("selected", 'check-nc'),
|
||||
{"sticky": ''}
|
||||
)
|
||||
},
|
||||
|
||||
"Radiobutton.indicator": {"element create":
|
||||
("image", 'radio-nu',
|
||||
('active', 'selected', 'radio-hc'),
|
||||
('pressed', 'selected', 'radio-pc'),
|
||||
('active', 'radio-hu'), ('selected', 'radio-nc'),
|
||||
{"sticky": ''}
|
||||
)
|
||||
},
|
||||
|
||||
"Horizontal.Scrollbar.thumb": {"element create":
|
||||
("image", 'hsb-n', {"border": 3, "sticky": "ew"})
|
||||
},
|
||||
|
||||
"Horizontal.Scrollbar.grip": {"element create": ("image", 'hsb-g')},
|
||||
"Horizontal.Scrollbar.trough": {"element create": ("image", 'hsb-t')},
|
||||
"Vertical.Scrollbar.thumb": {"element create":
|
||||
("image", 'vsb-n', {"border": 3, "sticky": "ns"})
|
||||
},
|
||||
"Vertical.Scrollbar.grip": {"element create": ("image", 'vsb-g')},
|
||||
"Vertical.Scrollbar.trough": {"element create": ("image", 'vsb-t')},
|
||||
"Scrollbar.uparrow": {"element create":
|
||||
("image", 'arrowup-n', ("pressed", 'arrowup-p'), {"sticky": ''})
|
||||
},
|
||||
"Scrollbar.downarrow": {"element create":
|
||||
("image", 'arrowdown-n',
|
||||
("pressed", 'arrowdown-p'), {'sticky': ''})
|
||||
},
|
||||
"Scrollbar.leftarrow": {"element create":
|
||||
("image", 'arrowleft-n',
|
||||
("pressed", 'arrowleft-p'), {'sticky': ''})
|
||||
},
|
||||
"Scrollbar.rightarrow": {"element create":
|
||||
("image", 'arrowright-n', ("pressed", 'arrowright-p'),
|
||||
{'sticky': ''})
|
||||
},
|
||||
|
||||
"Horizontal.Scale.slider": {"element create":
|
||||
("image", 'hslider-n', {'sticky': ''})
|
||||
},
|
||||
"Horizontal.Scale.trough": {"element create":
|
||||
("image", 'hslider-t', {'border': 1, 'padding': 0})
|
||||
},
|
||||
"Vertical.Scale.slider": {"element create":
|
||||
("image", 'vslider-n', {'sticky': ''})
|
||||
},
|
||||
"Vertical.Scale.trough": {"element create":
|
||||
("image", 'vslider-t', {'border': 1, 'padding': 0})
|
||||
},
|
||||
|
||||
"Entry.field": {"element create":
|
||||
("image", 'entry-n',
|
||||
("focus", 'entry-f'),
|
||||
{'border': 2, 'padding': [3, 4], 'sticky': 'news'}
|
||||
)
|
||||
},
|
||||
|
||||
"Labelframe.border": {"element create":
|
||||
("image", 'border', {'border': 4, 'padding': 4, 'sticky': 'news'})
|
||||
},
|
||||
|
||||
"Menubutton.button": {"element create":
|
||||
("image", 'combo-r',
|
||||
('active', 'combo-ra'),
|
||||
{'sticky': 'news', 'border': [4, 6, 24, 15],
|
||||
'padding': [4, 4, 5]}
|
||||
)
|
||||
},
|
||||
"Menubutton.indicator": {"element create":
|
||||
("image", 'arrow-d', {"sticky": "e", "border": [15, 0, 0, 0]})
|
||||
},
|
||||
|
||||
"Combobox.field": {"element create":
|
||||
("image", 'combo-n',
|
||||
('readonly', 'active', 'combo-ra'),
|
||||
('focus', 'active', 'combo-fa'),
|
||||
('active', 'combo-a'), ('!readonly', 'focus', 'combo-f'),
|
||||
('readonly', 'combo-r'),
|
||||
{'border': [4, 6, 24, 15], 'padding': [4, 4, 5],
|
||||
'sticky': 'news'}
|
||||
)
|
||||
},
|
||||
"Combobox.downarrow": {"element create":
|
||||
("image", 'arrow-d', {'sticky': 'e', 'border': [15, 0, 0, 0]})
|
||||
},
|
||||
|
||||
"Notebook.client": {"element create":
|
||||
("image", 'notebook-c', {'border': 4})
|
||||
},
|
||||
"Notebook.tab": {"element create":
|
||||
("image", 'notebook-tn',
|
||||
("selected", 'notebook-ts'), ("active", 'notebook-ta'),
|
||||
{'padding': [0, 2, 0, 0], 'border': [4, 10, 4, 10]}
|
||||
)
|
||||
},
|
||||
|
||||
"Progressbar.trough": {"element create":
|
||||
("image", 'hprogress-t', {'border': 2})
|
||||
},
|
||||
"Horizontal.Progressbar.pbar": {"element create":
|
||||
("image", 'hprogress-b', {'border': [2, 9]})
|
||||
},
|
||||
"Vertical.Progressbar.pbar": {"element create":
|
||||
("image", 'vprogress-b', {'border': [9, 2]})
|
||||
},
|
||||
|
||||
"Treeheading.cell": {"element create":
|
||||
("image", 'tree-n',
|
||||
("pressed", 'tree-p'),
|
||||
{'border': [4, 10], 'padding': 4, 'sticky': 'news'}
|
||||
)
|
||||
}
|
||||
|
||||
})
|
||||
style.theme_use("plastik")
|
|
@ -0,0 +1,111 @@
|
|||
"""Ttk Frame with rounded corners.
|
||||
|
||||
Based on an example by Bryan Oakley, found at: http://wiki.tcl.tk/20152"""
|
||||
import Tkinter
|
||||
import ttk
|
||||
|
||||
root = Tkinter.Tk()
|
||||
|
||||
img1 = Tkinter.PhotoImage("frameFocusBorder", data="""
|
||||
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
|
||||
Ry/99NIz//oGrZpUUEAAOw==""")
|
||||
|
||||
img2 = Tkinter.PhotoImage("frameBorder", data="""
|
||||
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""")
|
||||
|
||||
style = ttk.Style()
|
||||
|
||||
style.element_create("RoundedFrame", "image", "frameBorder",
|
||||
("focus", "frameFocusBorder"), border=16, sticky="nsew")
|
||||
|
||||
style.layout("RoundedFrame", [("RoundedFrame", {"sticky": "nsew"})])
|
||||
style.configure("TEntry", borderwidth=0)
|
||||
|
||||
frame = ttk.Frame(style="RoundedFrame", padding=10)
|
||||
frame.pack(fill='x')
|
||||
|
||||
frame2 = ttk.Frame(style="RoundedFrame", padding=10)
|
||||
frame2.pack(fill='both', expand=1)
|
||||
|
||||
entry = ttk.Entry(frame, text='Test')
|
||||
entry.pack(fill='x')
|
||||
entry.bind("<FocusIn>", lambda evt: frame.state(["focus"]))
|
||||
entry.bind("<FocusOut>", lambda evt: frame.state(["!focus"]))
|
||||
|
||||
text = Tkinter.Text(frame2, borderwidth=0, bg="white", highlightthickness=0)
|
||||
text.pack(fill='both', expand=1)
|
||||
text.bind("<FocusIn>", lambda evt: frame2.state(["focus"]))
|
||||
text.bind("<FocusOut>", lambda evt: frame2.state(["!focus"]))
|
||||
|
||||
root.mainloop()
|
|
@ -0,0 +1,61 @@
|
|||
"""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 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)
|
||||
|
||||
self.style = ttk.Style()
|
||||
|
||||
# XXX Ideally I wouldn't want to create a Tkinter.IntVar to make
|
||||
# it works with Checkbutton variable option.
|
||||
self.theme_autochange = Tkinter.IntVar(self, 0)
|
||||
self._setup_widgets()
|
||||
|
||||
def _change_theme(self):
|
||||
self.style.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.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)
|
||||
|
||||
change_btn = ttk.Button(self, text='Change Theme',
|
||||
command=self._change_theme)
|
||||
|
||||
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()
|
|
@ -0,0 +1,107 @@
|
|||
"""Demo based on the demo mclist included with tk source distribution."""
|
||||
import Tkinter
|
||||
import tkFont
|
||||
import ttk
|
||||
|
||||
tree_columns = ("country", "capital", "currency")
|
||||
tree_data = [
|
||||
("Argentina", "Buenos Aires", "ARS"),
|
||||
("Australia", "Canberra", "AUD"),
|
||||
("Brazil", "Brazilia", "BRL"),
|
||||
("Canada", "Ottawa", "CAD"),
|
||||
("China", "Beijing", "CNY"),
|
||||
("France", "Paris", "EUR"),
|
||||
("Germany", "Berlin", "EUR"),
|
||||
("India", "New Delhi", "INR"),
|
||||
("Italy", "Rome", "EUR"),
|
||||
("Japan", "Tokyo", "JPY"),
|
||||
("Mexico", "Mexico City", "MXN"),
|
||||
("Russia", "Moscow", "RUB"),
|
||||
("South Africa", "Pretoria", "ZAR"),
|
||||
("United Kingdom", "London", "GBP"),
|
||||
("United States", "Washington, D.C.", "USD")
|
||||
]
|
||||
|
||||
def sortby(tree, col, descending):
|
||||
"""Sort tree contents when a column is clicked on."""
|
||||
# grab values to sort
|
||||
data = [(tree.set(child, col), child) for child in tree.get_children('')]
|
||||
|
||||
# reorder data
|
||||
data.sort(reverse=descending)
|
||||
for indx, item in enumerate(data):
|
||||
tree.move(item[1], '', indx)
|
||||
|
||||
# switch the heading so that it will sort in the opposite direction
|
||||
tree.heading(col,
|
||||
command=lambda col=col: sortby(tree, col, int(not descending)))
|
||||
|
||||
class App(object):
|
||||
def __init__(self):
|
||||
self.tree = None
|
||||
self._setup_widgets()
|
||||
self._build_tree()
|
||||
|
||||
def _setup_widgets(self):
|
||||
msg = ttk.Label(wraplength="4i", justify="left", anchor="n",
|
||||
padding=(10, 2, 10, 6),
|
||||
text=("Ttk is the new Tk themed widget set. One of the widgets it "
|
||||
"includes is a tree widget, which can be configured to "
|
||||
"display multiple columns of informational data without "
|
||||
"displaying the tree itself. This is a simple way to build "
|
||||
"a listbox that has multiple columns. Clicking on the "
|
||||
"heading for a column will sort the data by that column. "
|
||||
"You can also change the width of the columns by dragging "
|
||||
"the boundary between them."))
|
||||
msg.pack(fill='x')
|
||||
|
||||
container = ttk.Frame()
|
||||
container.pack(fill='both', expand=True)
|
||||
|
||||
# XXX Sounds like a good support class would be one for constructing
|
||||
# a treeview with scrollbars.
|
||||
self.tree = ttk.Treeview(columns=tree_columns, show="headings")
|
||||
vsb = ttk.Scrollbar(orient="vertical", command=self.tree.yview)
|
||||
hsb = ttk.Scrollbar(orient="horizontal", command=self.tree.xview)
|
||||
self.tree.configure(yscrollcommand=vsb.set, xscrollcommand=hsb.set)
|
||||
self.tree.grid(column=0, row=0, sticky='nsew', in_=container)
|
||||
vsb.grid(column=1, row=0, sticky='ns', in_=container)
|
||||
hsb.grid(column=0, row=1, sticky='ew', in_=container)
|
||||
|
||||
container.grid_columnconfigure(0, weight=1)
|
||||
container.grid_rowconfigure(0, weight=1)
|
||||
|
||||
def _build_tree(self):
|
||||
for col in tree_columns:
|
||||
self.tree.heading(col, text=col.title(),
|
||||
command=lambda c=col: sortby(self.tree, c, 0))
|
||||
# XXX tkFont.Font().measure expected args are incorrect according
|
||||
# to the Tk docs
|
||||
self.tree.column(col, width=tkFont.Font().measure(col.title()))
|
||||
|
||||
for item in tree_data:
|
||||
self.tree.insert('', 'end', values=item)
|
||||
|
||||
# adjust columns lenghts if necessary
|
||||
for indx, val in enumerate(item):
|
||||
ilen = tkFont.Font().measure(val)
|
||||
if self.tree.column(tree_columns[indx], width=None) < ilen:
|
||||
self.tree.column(tree_columns[indx], width=ilen)
|
||||
|
||||
def main():
|
||||
root = Tkinter.Tk()
|
||||
root.wm_title("Multi-Column List")
|
||||
root.wm_iconname("mclist")
|
||||
|
||||
import plastik_theme
|
||||
try:
|
||||
plastik_theme.install('~/tile-themes/plastik/plastik')
|
||||
except Exception:
|
||||
import warnings
|
||||
warnings.warn("plastik theme being used without images")
|
||||
|
||||
app = App()
|
||||
root.mainloop()
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
|
@ -0,0 +1,231 @@
|
|||
"""
|
||||
Simple calendar using ttk Treeview together with calendar and datetime
|
||||
classes.
|
||||
"""
|
||||
import calendar
|
||||
import Tkinter
|
||||
import tkFont
|
||||
import ttk
|
||||
|
||||
def get_calendar(locale, fwday):
|
||||
# instantiate proper calendar class
|
||||
if locale is None:
|
||||
return calendar.TextCalendar(fwday)
|
||||
else:
|
||||
return calendar.LocaleTextCalendar(fwday, locale)
|
||||
|
||||
class Calendar(ttk.Frame):
|
||||
# XXX ToDo: cget and configure
|
||||
|
||||
datetime = calendar.datetime.datetime
|
||||
timedelta = calendar.datetime.timedelta
|
||||
|
||||
def __init__(self, master=None, **kw):
|
||||
"""
|
||||
WIDGET-SPECIFIC OPTIONS
|
||||
|
||||
locale, firstweekday, year, month, selectbackground,
|
||||
selectforeground
|
||||
"""
|
||||
# remove custom options from kw before initializating ttk.Frame
|
||||
fwday = kw.pop('firstweekday', calendar.MONDAY)
|
||||
year = kw.pop('year', self.datetime.now().year)
|
||||
month = kw.pop('month', self.datetime.now().month)
|
||||
locale = kw.pop('locale', None)
|
||||
sel_bg = kw.pop('selectbackground', '#ecffc4')
|
||||
sel_fg = kw.pop('selectforeground', '#05640e')
|
||||
|
||||
self._date = self.datetime(year, month, 1)
|
||||
self._selection = None # no date selected
|
||||
|
||||
ttk.Frame.__init__(self, master, **kw)
|
||||
|
||||
self._cal = get_calendar(locale, fwday)
|
||||
|
||||
self.__setup_styles() # creates custom styles
|
||||
self.__place_widgets() # pack/grid used widgets
|
||||
self.__config_calendar() # adjust calendar columns and setup tags
|
||||
# configure a canvas, and proper bindings, for selecting dates
|
||||
self.__setup_selection(sel_bg, sel_fg)
|
||||
|
||||
# store items ids, used for insertion later
|
||||
self._items = [self._calendar.insert('', 'end', values='')
|
||||
for _ in range(6)]
|
||||
# insert dates in the currently empty calendar
|
||||
self._build_calendar()
|
||||
|
||||
# set the minimal size for the widget
|
||||
self._calendar.bind('<Map>', self.__minsize)
|
||||
|
||||
def __setitem__(self, item, value):
|
||||
if item in ('year', 'month'):
|
||||
raise AttributeError("attribute '%s' is not writeable" % item)
|
||||
elif item == 'selectbackground':
|
||||
self._canvas['background'] = value
|
||||
elif item == 'selectforeground':
|
||||
self._canvas.itemconfigure(self._canvas.text, item=value)
|
||||
else:
|
||||
ttk.Frame.__setitem__(self, item, value)
|
||||
|
||||
def __getitem__(self, item):
|
||||
if item in ('year', 'month'):
|
||||
return getattr(self._date, item)
|
||||
elif item == 'selectbackground':
|
||||
return self._canvas['background']
|
||||
elif item == 'selectforeground':
|
||||
return self._canvas.itemcget(self._canvas.text, 'fill')
|
||||
else:
|
||||
r = ttk.tclobjs_to_py({item: ttk.Frame.__getitem__(self, item)})
|
||||
return r[item]
|
||||
|
||||
def __setup_styles(self):
|
||||
# custom ttk styles
|
||||
style = ttk.Style(self.master)
|
||||
arrow_layout = lambda dir: (
|
||||
[('Button.focus', {'children': [('Button.%sarrow' % dir, None)]})]
|
||||
)
|
||||
style.layout('L.TButton', arrow_layout('left'))
|
||||
style.layout('R.TButton', arrow_layout('right'))
|
||||
|
||||
def __place_widgets(self):
|
||||
# header frame and its widgets
|
||||
hframe = ttk.Frame(self)
|
||||
lbtn = ttk.Button(hframe, style='L.TButton', command=self._prev_month)
|
||||
rbtn = ttk.Button(hframe, style='R.TButton', command=self._next_month)
|
||||
self._header = ttk.Label(hframe, width=15, anchor='center')
|
||||
# the calendar
|
||||
self._calendar = ttk.Treeview(show='', selectmode='none', height=7)
|
||||
|
||||
# pack the widgets
|
||||
hframe.pack(in_=self, side='top', pady=4, anchor='center')
|
||||
lbtn.grid(in_=hframe)
|
||||
self._header.grid(in_=hframe, column=1, row=0, padx=12)
|
||||
rbtn.grid(in_=hframe, column=2, row=0)
|
||||
self._calendar.pack(in_=self, expand=1, fill='both', side='bottom')
|
||||
|
||||
def __config_calendar(self):
|
||||
cols = self._cal.formatweekheader(3).split()
|
||||
self._calendar['columns'] = cols
|
||||
self._calendar.tag_configure('header', background='grey90')
|
||||
self._calendar.insert('', 'end', values=cols, tag='header')
|
||||
# adjust its columns width
|
||||
font = tkFont.Font()
|
||||
maxwidth = max(font.measure(col) for col in cols)
|
||||
for col in cols:
|
||||
self._calendar.column(col, width=maxwidth, minwidth=maxwidth,
|
||||
anchor='e')
|
||||
|
||||
def __setup_selection(self, sel_bg, sel_fg):
|
||||
self._font = tkFont.Font()
|
||||
self._canvas = canvas = Tkinter.Canvas(self._calendar,
|
||||
background=sel_bg, borderwidth=0, highlightthickness=0)
|
||||
canvas.text = canvas.create_text(0, 0, fill=sel_fg, anchor='w')
|
||||
|
||||
canvas.bind('<ButtonPress-1>', lambda evt: canvas.place_forget())
|
||||
self._calendar.bind('<Configure>', lambda evt: canvas.place_forget())
|
||||
self._calendar.bind('<ButtonPress-1>', self._pressed)
|
||||
|
||||
def __minsize(self, evt):
|
||||
width, height = self._calendar.master.geometry().split('x')
|
||||
height = height[:height.index('+')]
|
||||
self._calendar.master.minsize(width, height)
|
||||
|
||||
def _build_calendar(self):
|
||||
year, month = self._date.year, self._date.month
|
||||
|
||||
# update header text (Month, YEAR)
|
||||
header = self._cal.formatmonthname(year, month, 0)
|
||||
self._header['text'] = header.title()
|
||||
|
||||
# update calendar shown dates
|
||||
cal = self._cal.monthdayscalendar(year, month)
|
||||
for indx, item in enumerate(self._items):
|
||||
week = cal[indx] if indx < len(cal) else []
|
||||
fmt_week = [('%02d' % day) if day else '' for day in week]
|
||||
self._calendar.item(item, values=fmt_week)
|
||||
|
||||
def _show_selection(self, text, bbox):
|
||||
"""Configure canvas for a new selection."""
|
||||
x, y, width, height = bbox
|
||||
|
||||
textw = self._font.measure(text)
|
||||
|
||||
canvas = self._canvas
|
||||
canvas.configure(width=width, height=height)
|
||||
canvas.coords(canvas.text, width - textw, height / 2 - 1)
|
||||
canvas.itemconfigure(canvas.text, text=text)
|
||||
canvas.place(in_=self._calendar, x=x, y=y)
|
||||
|
||||
# Callbacks
|
||||
|
||||
def _pressed(self, evt):
|
||||
"""Clicked somewhere in the calendar."""
|
||||
x, y, widget = evt.x, evt.y, evt.widget
|
||||
item = widget.identify_row(y)
|
||||
column = widget.identify_column(x)
|
||||
|
||||
if not column or not item in self._items:
|
||||
# clicked in the weekdays row or just outside the columns
|
||||
return
|
||||
|
||||
item_values = widget.item(item)['values']
|
||||
if not len(item_values): # row is empty for this month
|
||||
return
|
||||
|
||||
text = item_values[int(column[1]) - 1]
|
||||
if not text: # date is empty
|
||||
return
|
||||
|
||||
bbox = widget.bbox(item, column)
|
||||
if not bbox: # calendar not visible yet
|
||||
return
|
||||
|
||||
# update and then show selection
|
||||
text = '%02d' % text
|
||||
self._selection = (text, item, column)
|
||||
self._show_selection(text, bbox)
|
||||
|
||||
def _prev_month(self):
|
||||
"""Updated calendar to show the previous month."""
|
||||
self._canvas.place_forget()
|
||||
|
||||
self._date = self._date - self.timedelta(days=1)
|
||||
self._date = self.datetime(self._date.year, self._date.month, 1)
|
||||
self._build_calendar() # reconstuct calendar
|
||||
|
||||
def _next_month(self):
|
||||
"""Update calendar to show the next month."""
|
||||
self._canvas.place_forget()
|
||||
|
||||
year, month = self._date.year, self._date.month
|
||||
self._date = self._date + self.timedelta(
|
||||
days=calendar.monthrange(year, month)[1] + 1)
|
||||
self._date = self.datetime(self._date.year, self._date.month, 1)
|
||||
self._build_calendar() # reconstruct calendar
|
||||
|
||||
# Properties
|
||||
|
||||
@property
|
||||
def selection(self):
|
||||
"""Return a datetime representing the current selected date."""
|
||||
if not self._selection:
|
||||
return None
|
||||
|
||||
year, month = self._date.year, self._date.month
|
||||
return self.datetime(year, month, int(self._selection[0]))
|
||||
|
||||
def test():
|
||||
import sys
|
||||
root = Tkinter.Tk()
|
||||
root.title('Ttk Calendar')
|
||||
ttkcal = Calendar(firstweekday=calendar.SUNDAY)
|
||||
ttkcal.pack(expand=1, fill='both')
|
||||
|
||||
if 'win' not in sys.platform:
|
||||
style = ttk.Style()
|
||||
style.theme_use('clam')
|
||||
|
||||
root.mainloop()
|
||||
|
||||
if __name__ == '__main__':
|
||||
test()
|
|
@ -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 = 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
|
||||
btn_font = self.style.lookup("TButton", "font")
|
||||
fsize = str(self.tk.eval("font configure %s -size" % btn_font))
|
||||
self.font_family = self.tk.eval("font configure %s -family" % btn_font)
|
||||
if ' ' in self.font_family:
|
||||
self.font_family = '{%s}' % self.font_family
|
||||
self.fsize_prefix = fsize[0] if fsize[0] == '-' else ''
|
||||
self.base_fsize = int(fsize[1 if fsize[0] == '-' else 0:])
|
||||
|
||||
# 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 %s%d" % (self.font_family,
|
||||
self.fsize_prefix, self.base_fsize + 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(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()
|
Loading…
Reference in New Issue