-
-
Save MaxMorais/5388218 to your computer and use it in GitHub Desktop.
#!/usr/bin/env python | |
# -*- coding: UTF-8 -*- | |
#------------------------------------------------------------------------------- | |
# Name: maskedit.py | |
# Purpose: | |
# | |
# Author: Maxwell Morais (max.morais.dmm@gmail.com) | |
# | |
# Created: 10/04/2013 | |
# Copyright: (c) Maxwell Morais 2013 | |
# Licence: MIT | |
#------------------------------------------------------------------------------- | |
import sys | |
import re | |
import calendar | |
if sys.version[0]=='2': | |
import Tkinter as tk | |
import ttk | |
import tkFont | |
elif sys.version[0]=='3': | |
import tkinter as tk | |
import tkinter.font as tkFont | |
from tkinter import ttk | |
basestring = type(u'') | |
else: | |
print("Python version not supported, try 2 or 3 versions") | |
definitions = { | |
'9': '[0-9]', | |
'a': '[a-zA-Z]', | |
'x': '[a-zA-z0-9]' | |
} | |
class ToolTip(tk.Toplevel): | |
def __init__(self, root, **kw): | |
self.fields = { | |
'tooltip': None, | |
'variable': None, | |
'showdelay': 1, | |
'hidedelay': 10, | |
'root': root, | |
} | |
self.__hide_id = None | |
self.__show_id = None | |
for k in list(kw.keys()): | |
if k in self.fields: | |
self.fields[k]=kw.pop(k) | |
if not 'variable' in kw and not self.fields['variable']: | |
self.fields['variable']=tk.StringVar(root) | |
tk.Toplevel.__init__(self, root, **kw) | |
self.withdraw() | |
self.overrideredirect(True) | |
self.setup() | |
def setup(self): | |
self._tooltip() | |
self._bind() | |
self._grid() | |
def set_text(self, value): | |
self.fields['variable'].set(value) | |
def get_text(self): | |
return self.fields['variable'].get() | |
def _tooltip(self): | |
if not isinstance(self.fields.get('variable', None), tk.StringVar): | |
self.fields['variable'] = tk.StringVar(self) | |
tooltiptext = self.fields['tooltip'] or self.fields['variable'].get() | |
self.fields['variable'].set(tooltiptext) | |
self._widget = ttk.Label(self, | |
textvariable=self.fields['variable'], | |
#aspect=1000, | |
background='#ffffcc' | |
) | |
def _grid(self): | |
self._widget.grid(row=0, column=0) | |
def _bind(self): | |
self.fields['root'].bind('<Enter>', self._init_tooltip, True) | |
self.fields['root'].bind('<Leave>', self._hide_tooltip, True) | |
def _init_tooltip(self, event): | |
self.__show_id = self.after( | |
int(self.fields['showdelay']*1000), | |
self._show_tooltip | |
) | |
def _show_tooltip(self, event=None): | |
self.__show_id = None | |
x = self.fields['root'].winfo_rootx() | |
y = self.fields['root'].winfo_rooty() | |
height = self.fields['root'].winfo_height() | |
width = self.fields['root'].winfo_width() | |
lx = x + self.winfo_width() + 5 | |
if lx < self.fields['root'].winfo_screenwidth() - 5: | |
x += 5 | |
else: | |
x += self.fields['root'].winfo_screenwidth() - lx | |
ly = (y + height + self.winfo_height() + 5) | |
if ly < self.fields['root'].winfo_height() - 5: | |
y += height + 5 | |
else: | |
y += -(self.winfo_height()+5) | |
self.wm_geometry('+%d+%d'%(x,y)) | |
self.deiconify() | |
self.__hide_id = self.after( | |
int(self.fields['hidedelay'])*1000, | |
self._hide_tooltip | |
) | |
def _hide_tooltip(self, event): | |
if self.__show_id is not None: | |
self.after_cancel( self.__show_id ) | |
self.__show_id = None | |
if self.__hide_id is not None: | |
self.after_cancel( self.__hide_id ) | |
self.__hide_id = None | |
self.withdraw() | |
class FormWidget(ttk.Frame): | |
_fields= { | |
'field': None, | |
'label': None, | |
'help': None, | |
'variable': None, | |
'labelvariable': None, | |
'helpvariable': None | |
} | |
_factory = { | |
'variable': tk.StringVar, | |
'widget': tk.Entry | |
} | |
def __init__(self, root, **kw): | |
self.fields = self._fields.copy() | |
for k in list(kw.keys()): | |
if k in self.fields: | |
self.fields[k]=kw.pop(k) | |
if not 'variable' in kw: | |
self.fields['variable']=self._factory.get( | |
'variable', | |
tk.StringVar)(root) | |
ttk.Frame.__init__(self, root, **kw) | |
self.setup() | |
def setup(self): | |
self._label() | |
self._widget() | |
self._help() | |
self._grid() | |
def set(self, value): self.fields['variable'].set(value) | |
def get(self): return self.fields['variable'].get() | |
def set_label(self, value): self.fields['labelvariable'].set(value) | |
def get_label(self): return self.fields['labelvariable'].get() | |
def set_help(self, value): self.fields['helpvariable'].set(value) | |
def get_help(self): return self.fields['helpvariable'].get() | |
def _label(self): | |
labeltext = self.fields['label'] or self.fields['field']['label'] or '' | |
if not isinstance(self.fields.get('labelvariable', None), tk.StringVar): | |
self.fields['labelvariable'] = tk.StringVar(self) | |
self.fields['labelvariable'].set(labeltext) | |
self._wgt_label = ttk.Label( | |
self, | |
textvariable=self.fields['labelvariable'] | |
) | |
def _widget(self): | |
self._widget = self._factory.get('widget', ttk.Entry)( | |
self, | |
textvariable=self.fields['variable'] | |
) | |
def _help(self): | |
helptext = self.fields['help'] or '' | |
if not helptext: return | |
if not isinstance(self.fields.get('helpvariable', None), tk.StringVar): | |
self.fields['helpvariable'] = tk.StringVar(self) | |
self.fields['helpvariable'].set(helptext) | |
self._wgt_help = ToolTip( | |
self._widget, | |
variable=self.fields['helpvariable'] | |
) | |
def _grid(self): | |
self._wgt_label.grid(row=0, column=0) | |
self._widget.grid(row=1, column=0) | |
class StringWidget(ttk.Entry): | |
@property | |
def variable(self): | |
return self.fields['textvariable'] | |
class MaskedWidget(ttk.Entry): | |
def __init__(self, master, format_type, **kw): | |
self.fields = { | |
'type': format_type, | |
'mask': None, | |
'monetary': False, | |
'dec_places': 2, | |
'dec_sep': '.', | |
'tho_places': 3, | |
'tho_sep': ',', | |
'symbol': '', | |
'fmt_neg': '-%(symbol)s%(amount)s', | |
'fmt_pos': '%(symbol)s%(amount)s', | |
'placeholder': '_', | |
'textvariable': None, | |
} | |
if str(format_type).lower() == 'fixed': | |
assert 'mask' in kw, 'the fixed mask, is not present' | |
self.fields['mask'] = kw.pop('mask', '').lower() | |
for k in list(kw.keys()): | |
if k in self.fields: | |
if k!='textvariable': | |
self.fields[k]=kw.pop(k) | |
else: | |
self.fields[k]=kw[k] | |
if not 'textvariable' in kw: | |
self.fields['textvariable']=tk.StringVar(master) | |
kw['textvariable'] = self.fields['textvariable'] | |
ttk.Entry.__init__(self, master, **kw) | |
self.defs = definitions | |
self.tests = [] | |
self.partialPosition = None | |
self.firstNonMaskPosition = None | |
self.len = len(self.fields['mask']) | |
for i,c in enumerate(self.fields['mask'].lower()): | |
if c == '?': | |
self.len -= 1 | |
self.partialPosition = i | |
atom = self.defs.get(c, None) | |
self.tests.append(re.compile('(%s)'%atom) if atom else atom) | |
if not atom and self.firstNonMaskPosition==None: | |
self.firstNonMaskPosition = len(self.tests)-1 | |
self.writeBuffer() | |
if str(self.cget("state")).upper()!="DISABLED": | |
self.bind('<KeyPress>', self._onKeyPress, True) | |
self.bind('<KeyRelease>', lambda e: 'break', True) | |
self.bind('<FocusIn>', self._onFocusIn, True) | |
def clean(self): | |
self.fields['textvariable'].set('') | |
self.writeBuffer() | |
def clean_numeric(self, string): | |
if not isinstance(string, basestring): string = str(string) | |
string = string.replace(self.fields['symbol']+' ', '')\ | |
.replace(self.fields['tho_sep'], '')\ | |
.replace(self.fields['dec_sep'], '.') | |
if not '.' in string: | |
string = list(string) | |
string.insert(-2, '.') | |
string = ''.join(string) | |
return string.partition('.') | |
def fmt_numeric( self, amount): | |
temp = '00' if not '.' in str(amount) \ | |
else str(amount).split('.')[1] | |
l = [] | |
amount = amount.split('.')[0] | |
try: | |
minus = float(''.join(self.clean_numeric(amount)))<0 | |
except ValueError: | |
minus = 0 | |
if len(amount)> self.fields['tho_places']: | |
nn = amount[-self.fields['tho_places']:] | |
l.append(nn) | |
amount = amount[:len(amount)-self.fields['tho_places']] | |
while len(amount) > self.fields['tho_places']: | |
nn = amount[len(amount)-self.fields['tho_places']:] | |
l.insert(0, nn) | |
amount = amount[0:len(amount)-self.fields['tho_places']] | |
if len(''.join(self.clean_numeric(amount)))>0: l.insert(0, amount) | |
amount = self.fields['tho_sep'].join(l)+self.fields['dec_sep']+temp | |
if minus: | |
amount = self.fields['fmt_neg']%{ | |
'symbol':self.fields['symbol'], | |
'amount': amount | |
} | |
else: | |
amount = self.fields['fmt_pos']%{ | |
'symbol': (self.fields['symbol']+' ') if self.fields['symbol'] else '', | |
'amount': amount | |
} | |
return amount | |
def seekNext(self, pos): | |
if 0 <= pos+1<self.len: | |
if self.tests[pos+1]: | |
return pos+1 | |
return self.seekNext(pos+1) | |
return pos | |
def seekPrev(self, pos): | |
if 0 <= pos-1 < self.len: | |
if self.tests[pos-1]: | |
return pos-1 | |
return self.seekPrev(pos-1) | |
return pos | |
def shiftL(self, begin, end): | |
if begin < 0: return | |
for i in range(self.len): | |
j = self.seekNext(begin) | |
if self.tests[i]: | |
if j < self.len and self.tests[i].match(self.buffer[i]): | |
self.buffer[i] = self.buffer[j] | |
self.buffer[j] = self.fields['placeholder'] | |
else: | |
break | |
def shiftR(self, pos, c): | |
if pos in range(len(self.len)): | |
j = self.seekNext(pos) | |
t = self.buffer[pos] | |
if not t == c and j < self.len and t == self.fields['placeholder']: | |
self.buffer[pos] = c | |
def writeBuffer(self): | |
self.fields['textvariable'].set( | |
''.join( | |
filter( | |
lambda x: x!=None, | |
map( | |
lambda c, self=self: | |
(self.fields['placeholder'] | |
if self.defs.get(c, None) | |
else c) | |
if c!='?' else None, self.fields['mask']) | |
) | |
) | |
) | |
return self.get() | |
def _onFocusIn(self, event): | |
if self.len>0 and self.tests[0]: | |
self.icursor(0) | |
else: | |
self.icursor(self.seekNext(0)) | |
def _onKeyPress(self, event): | |
if event.keysym == 'Tab': | |
return | |
elif event.keysym == 'Escape': | |
if self.fields['type'] == 'fixed': | |
self.writeBuffer() | |
else: | |
self.delete(0, len(event.widget.get())) | |
widget = event.widget | |
val = widget.get() | |
idx = widget.index(tk.INSERT) | |
if event.keysym == 'Left': | |
if 0 <= idx < self.len: | |
if idx < self.firstNonMaskPosition: | |
return 'break' | |
elif not self.tests[idx]: | |
widget.icursor(self.seekPrev(idx)) | |
elif event.keysym == 'Right': | |
if 0 <= idx < self.len: | |
if idx >= self.len: | |
return 'break' | |
elif not self.tests[idx]: | |
widget.icursor(self.seekNext(idx)) | |
elif event.keysym == 'BackSpace' and self.fields['type'] != 'numeric': | |
def repl_or_stop(cls, wget, pos): | |
if 0 <= pos <= cls.len: | |
if not cls.tests[pos]: | |
pos = cls.seekPrev(pos) | |
cls._write_char(pos, cls.fields['placeholder'], -1) | |
return 'break' | |
repl_or_stop(self, widget, idx - 1) | |
return 'break' | |
else: | |
if self.fields['type'] == 'fixed': | |
if self._write_char(idx, event.char) == 'break': | |
return 'break' | |
elif self.fields['type'] == 'numeric' and event.char.isdigit(): | |
if val: | |
widget.delete(0, len(val)) | |
head, sep, tail = self.clean_numeric(val) | |
else: | |
head, sep, tail = '0', '.', '00' | |
if not head: | |
head = '0' | |
if len(tail) < 2: | |
tail = '0' + tail | |
if tail and len(tail + event.char) <= 2 and (int(tail+event.char))<99: | |
tail = tail[1:] + event.char | |
else: | |
if not int(head): | |
head = tail[0] if tail else '0' | |
else: | |
head += tail[0] | |
tail = tail[1:] + event.char | |
widget.insert(0, ''.join([head, sep, tail])) | |
return 'break' | |
elif self.fields['type'] == 'numeric' and event.keysym == 'BackSpace': | |
if val: | |
widget.delete(0, len(val)) | |
head, sep, tail = self.clean_numeric(val[:-1]) | |
else: | |
head, sep, tail = '0', '.', '00' | |
widget.insert(0, ''.join([head, sep, tail])) | |
return 'break' | |
else: | |
self.bell() | |
return 'break' | |
def insert(self, index, value): | |
if self.fields['type']=='numeric': | |
ttk.Entry.insert(self, index, self.fmt_numeric(value)) | |
else: | |
for c in str(value): | |
while (not self.tests[index] or not self.tests[index].match(c)): | |
index += 1 | |
self._write_char(index,c) | |
index += 1 | |
def _write_char(self, idx, char, direction=+1): | |
if 0<=idx<self.len and self.tests[idx]: | |
if char != self.fields['placeholder'] and not self.tests[idx].match(char): | |
self.bell() | |
return 'break' | |
self.delete(idx) | |
ttk.Entry.insert(self, idx, char) | |
if direction == +1: | |
if idx + 1 < self.len and not self.tests[idx+1]: | |
idx = self.seekNext(idx) | |
else: | |
idx += 1 | |
elif direction == -1 and \ | |
idx - 1 >= 0 and \ | |
not self.tests[idx]: | |
idx = self.seekPrev(idx) | |
self.icursor(idx) | |
return 'break' | |
else: | |
self.bell() | |
return 'break' | |
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.SUNDAY) | |
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 = tk.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 main(): | |
root = tk.Tk() | |
page = tk.Frame(root) | |
c = MaskedWidget(page, 'fixed', mask='+99 (99) 999-999-999', width=20) | |
c.insert(0, '6611122266647') | |
c.pack() | |
c = MaskedWidget(page, 'fixed', mask='99/99/9999') | |
c.insert(0, '29091991') | |
c.pack() | |
MaskedWidget(page, 'numeric').pack() | |
c = MaskedWidget(page, 'numeric', dec_sep=",", tho_sep='.', symbol="R$") | |
c.insert(0, '12659.96') | |
c.pack() | |
# Cleaning text | |
c = MaskedWidget(page, 'numeric', dec_sep=",", tho_sep='.', symbol="R$") | |
c.insert(0, '12659.96') | |
c.pack() | |
c.clean() | |
c = Calendar(page) | |
c.pack() | |
FormWidget(page, | |
label=u'Olá', | |
help='Seu Nome' | |
).pack() | |
page.pack() | |
root.mainloop() | |
if __name__ == '__main__': | |
main() |
Various improvements regarding the BackSpace key issue, and the formatting of numeric fields
Max, tudo bem?
Implementei o seu código em uma aplicação que estou desenvolvendo.
O único problema é que quando preciso limpar os campos usando o método ".delete(0,END)" ele limpa também a máscara inserida, e transforma o widget em um entry normal.
Alguma sugestão? Tentei usar o método ".select_range(0,END)" e depois ".select_clear()" mas não estão funcionando não sei porque. Eles nao executam nada.
Pesquisei várias alternativas e a que me deixou mais perto do que quero foi o seu código.
Desde já muito obrigado.
@bernardesrodrigo eu só vi sua mensagem agora, para limpar vc pode usar
widgets.fields['textvariable'].set("")
Max, poderia dar uma exemplificada melhor na dúvida do Bernardes, por favor!
Estava tentando aqui com o que você repassou - widgets.fields['textvariable'].set("") - porém não tá dando certo :/
@wesleygurgel vou implementar um método dedicado para isso, assim que estiver em ordem, te aviso, e ao @bernardesrodrigo também
Added clean
as dedicated method to cleanup the field
@wesleygurgel agora basta utilizar o método clean
para limpar o widget
@wesleygurgel agora basta utilizar o método
clean
para limpar o widget
@MaxMorais tu é brabo, obg de verdade
bom dia, estou com duvida em relação a atribuir a data selecionada no Calendar à uma variavel.
@vitortitz, qual a sua pergunta?
após fazer a seleção no calendário como faço para poder atribuir a data selecionada à uma variável? Desculpa a pergunta, sou novato em python
@vitortitz utilize algo assim no teu codigo
minha_var = meu_calendar.selection
@MaxMorais desculpa a demora, obrigado pela ajuda, irei fazer o teste.
@MaxMorais desculpa incomodar novamente, mas existe alguma forma de selecionar o ano tbm, sem ser voltando de mês em mês?
como faço para adicionar maskara cpf no Entry do Tkinter?
Me ajudou muito mesmo, parabéns pela sua iniciativa!!
Fixed a delete error using BackSpace key, reported by Juan Carvalho