Python Forum
Date entry in box format issue
Thread Rating:
  • 0 Vote(s) - 0 Average
  • 1
  • 2
  • 3
  • 4
  • 5
Date entry in box format issue
#1
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!
Reply
#2
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'
Reply
#3
(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.
Reply
#4
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
Reply
#5
(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!
Reply
#6
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()
Reply
#7
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.
Reply


Possibly Related Threads…
Thread Author Replies Views Last Post
  Entry Widget issue PA3040 16 6,839 Jan-20-2021, 02:21 PM
Last Post: pitterbrayn
  Transfer Toplevel window entry to root window entry with TKinter HBH 0 4,467 Jan-23-2020, 09:00 PM
Last Post: HBH
  [Tkinter] how to get the entry information using Entry.get() ? SamyPyth 2 3,499 Mar-18-2019, 05:36 PM
Last Post: woooee

Forum Jump:

User Panel Messages

Announcements
Announcement #1 8/1/2020
Announcement #2 8/2/2020
Announcement #3 8/6/2020