Python Forum

Full Version: Date entry in box format issue
You're currently viewing a stripped down version of our content. View the full version with proper formatting.
Hi everyone,
I've found this code to enter the date and that I'd like to use in tkinter but I'm having several issue:
import tkinter as tk
from datetime import datetime


class DateEntry(tk.Frame):
    def __init__(self, parent, **kwargs):
        years = kwargs.pop('years', (1900, 9999))
        super().__init__(parent, **kwargs)

        vcmd = (self.register(self._validate), '%W', '%V', '%v', '%P', '%S')

        for name, text, v1, v2 in (('day', 'DD', 1, 31),
                                   ('month', 'MM', 1, 12),
                                   ('year', 'YYYY', years[0], years[1])):
            e = tk.Entry(self, name=name, width=len(text) + 2, justify="center")
            e.pack(side=tk.LEFT)
            e.insert(0, text)
            e._valid = (len(text), v1, v2)
            e.config(validate="all", validatecommand=vcmd)

    def get(self):
        data = {}
        for entry in [self.nametowidget(child) for child in self.children]:
            text = entry.get()
            data[entry.winfo_name()] = int(text) if text.isdigit() else None
        return data

    def _validate(self, widget, cmd, validate, value, text):
        # get this entry reference
        w = self.nametowidget(widget)

        # Clear entry or do nothing
        if cmd in ('focusin', 'forced') or value == '':
            if not value.isdigit():
                w.delete(0, tk.END)
                # Set the 'validate' option again after edit
                w.after_idle(w.config, {'validate': validate})
            return True

        # process key
        elif cmd == 'key' and value.isdigit():
            # get from this entry the valid parameter
            l, v1, v2 = w._valid

            # get the startswith chars if YYYY
            if v1 > 1 and len(value) < l:
                l2 = len(value)
                v1, v2 = int(str(v1)[:l2]), int(str(v2)[:l2])

            # allow leading zero in DD / MM
            elif v1 == 1 and len(value) == 1 and int(value) == 0:
                return True

            # return True if all valid else False
            return all((text.isdigit(), v1 <= int(value) <= v2, len(value) <= l))

        # else return False
        return False
This is how I'm using it after importing it

self.birthday = StringVar(self)
        self.birthday.set('')
        self.birthday=DateEntry(self, years=(1935, 2020))
        self.birthday.place(relx=0.22, rely=0.16, height=25, width=100)
I cannot access to the entry input I'm trying this way:
for key in self.birthday.keys():
         birth_year = self.birthday.get('year')
         birth_month = self.birthday.get('month')
         birth_day =  self.birthday.get('day')
         birth_date = datetime.datetime(birth_year, birth_month, birth_day)
But it gives me this error "birth_year = self.birthday.get('year')
TypeError: get() takes 1 positional argument but 2 were given". I've no idea why this is happening.
Also I'd like to edit a little the code and I was wondering if it could be possible to:
1. Po put a little bit of space between the three boxes and add a "/" between them?
2. Also right now it is possible to input incorrect date like 31 November ecc. How could I fix this?

Thank you all for your help!
have you seen the tkcalendar widget? found hereThe benefit is it uses the calendar so you get leap years, super easy to use:
#from my interactive prompt.
>>> from tkcalendar import Calendar, DateEntry
>>> from tkinter import Tk
>>> root= Tk()
>>> date_entry= DateEntry(root)
>>> date_entry.pack()
>>> now= date_entry.get()
>>> now
'4/19/20'
>>> mo_dy_yr= now.split('/')
>>> mo= now[0]
>>> mo
'4'
>>> dy= mo_dy_yr[1]
>>> dy
'19'
>>> yr= mo_dy_yr[2]
>>> yr
'20'
but if you're going to create a new one-
Quote:self.birthday = StringVar(self)
self.birthday.set('')
self.birthday=DateEntry(self, years=(1935, 2020))
self.birthday.place(relx=0.22, rely=0.16, height=25, width=100)
first to create a string var and set it none then you over right the namespace and make it a dateentry widget. if you want the information you need .get()
at the bottom of your script I included a test:
if __name__ =='__main__':
    root= tk.Tk()    
    d=DateEntry(root,years=('2000','2020'))
    d.pack()
    def get_entry():
        txt=d.get()
        print(txt)
    btn= tk.Button(root,text='hello',command= get_entry)
    btn.pack()
I did get an error when I tried to enter 1 in the year:
Quote:File "/home/pi/misc/yk_homeade_datepicker.py", line 46, in _validate
if v1 > 1 and len(value) < l:
TypeError: '>' not supported between instances of 'str' and 'int'
(Apr-18-2020, 09:56 PM)joe_momma Wrote: [ -> ]have you seen the tkcalendar widget? found hereThe benefit is it uses the calendar so you get leap years, super easy to use:
#from my interactive prompt.
>>> from tkcalendar import Calendar, DateEntry
>>> from tkinter import Tk
>>> root= Tk()
>>> date_entry= DateEntry(root)
>>> date_entry.pack()
>>> now= date_entry.get()
>>> now
'4/19/20'
>>> mo_dy_yr= now.split('/')
>>> mo= now[0]
>>> mo
'4'
>>> dy= mo_dy_yr[1]
>>> dy
'19'
>>> yr= mo_dy_yr[2]
>>> yr
'20'
but if you're going to create a new one-
Quote:self.birthday = StringVar(self)
self.birthday.set('')
self.birthday=DateEntry(self, years=(1935, 2020))
self.birthday.place(relx=0.22, rely=0.16, height=25, width=100)
first to create a string var and set it none then you over right the namespace and make it a dateentry widget. if you want the information you need .get()
at the bottom of your script I included a test:
if __name__ =='__main__':
    root= tk.Tk()    
    d=DateEntry(root,years=('2000','2020'))
    d.pack()
    def get_entry():
        txt=d.get()
        print(txt)
    btn= tk.Button(root,text='hello',command= get_entry)
    btn.pack()
I did get an error when I tried to enter 1 in the year:
Quote:File "/home/pi/misc/yk_homeade_datepicker.py", line 46, in _validate
if v1 > 1 and len(value) < l:
TypeError: '>' not supported between instances of 'str' and 'int'
I did saw the calendar script which is really beautiful, but I'm afraid it might need a lot of time and increment the chance of error input if used for birthday.
For what I've understand the script I'm trying to use gives back a dict and I don't know how to access it since it usually gives me back the "TypeError: get() takes 1 positional argument but 2 were given" error and I don't know why.
In my test I used root which is TK as the master you're using self
Quote:self.birthday=DateEntry(self, years=(1935, 2020))
I changed line 48 to len(v1) threw errors on the day and month
(Apr-19-2020, 04:10 PM)joe_momma Wrote: [ -> ]In my test I used root which is TK as the master you're using self
Quote:self.birthday=DateEntry(self, years=(1935, 2020))
I changed line 48 to len(v1) threw errors on the day and month

When I use the def get_entry() it gives me back "none" as result, I manage to solve the "TypeError: get() takes 1 positional argument but 2 were given" error but it gives me back "0". It seems I can't get the user input date with get(). Is there a way to solve this? After my users enter their birthday I want to be able to use .get() or any other way to save the info. How could I do so? Thank you!
After I import it, and run it I get an error type in the year and this output
Quote:{'day': 10, 'month': 11, 'year': 2001}
this larz's hoover example with your date entry, modify the import- it's an example only
import tkinter as tk
from yk_homeade_datepicker import DateEntry 
 
class ButtonHoverExample:
    def __init__(self, parent):
        self.parent = parent
        self.parent.title("HoverExample")
        self.parent.geometry("360x260+10+10")
        self.buttons = {}
        self.btn= tk.Button(self.parent, text='hello',command=self.get_entry)
        self.btn.grid(row=0,column=1)
        self.de= DateEntry(self.parent,years=('2000','2020'))
        self.de.grid(row=5,column=1)
    def get_entry(self):
        text= self.de.get()
        print(text)
                           
 
    def on_enter(self, name):
        button = self.buttons[name]
        # print(f"\nMouse Enter - name: {button['name']}, details: {button}")
        bgcolor = button["mouseexit"]
        button["button"].configure(bg=bgcolor)
 
    def on_exit(self, name):
        button = self.buttons[name]
        bgcolor = button["mouseenter"]
        button["button"].configure(bg=bgcolor)
 
    def new_button(self, name, mecolor, mxcolor, xpos, ypos):
        button = self.buttons
        parent = self.parent
 
        button[name] = {}
        button[name]["name"] = name
        button[name]["mouseenter"] = mecolor
        button[name]["mouseexit"] = mxcolor
        button[name]["xpos"] = xpos
        button[name]["ypos"] = ypos
        button[name]["button"] = tk.Button(
            parent, text=name, bg=button[name]["mouseenter"]
        )
        button[name]["button"].grid(row=xpos, column=ypos)
 
        # For binding options see Shipman 54.3. Event types
        button[name]["button"].bind("<Enter>", lambda event: self.on_enter(name))
        button[name]["button"].bind("<Leave>", lambda event: self.on_exit(name))
        
 
    def show_buttons(self, name):
        for key, value in self.buttons[name].items():
            print(f"{key}: {value}")
 
 
def main():
    root = tk.Tk()
    hx = ButtonHoverExample(root)
 
    # get colors here: https://www.w3schools.com/colors/colors_picker.asp
    hx.new_button(
        name="Button1", mecolor="#ccccff", mxcolor="#ffcccc", xpos=10, ypos=10
    )
    hx.new_button(
        name="Button2", mecolor="#ccffff", mxcolor="#ffffcc", xpos=10, ypos=100
    )
 
    # to show stored values:
    hx.show_buttons("Button1")
 
    root.mainloop()
 
 
if __name__ == "__main__":
    main()
It seems to work now but it gives me back this error when I try to write it in excel
Exception in Tkinter callback
Traceback (most recent call last):
  File "C:\Program Files (x86)\Python38-32\lib\tkinter\__init__.py", line 1883, in __call__
    return self.func(*args)
  File "C:/Users/pc/Desktop/file/project.py", line 196, in insert
    sheet.cell(row=current_row + 1, column=8).value = self.bday_entry.get()
  File "C:\Program Files (x86)\Python38-32\lib\site-packages\openpyxl\cell\cell.py", line 216, in value
    self._bind_value(value)
  File "C:\Program Files (x86)\Python38-32\lib\site-packages\openpyxl\cell\cell.py", line 199, in _bind_value
    raise ValueError("Cannot convert {0!r} to Excel".format(value))
ValueError: Cannot convert {'day': 11, 'month': 11, 'year': 1999} to Excel
I've tried datetime.strptime to convert the date but with no luck.