(^_^) 发表于 2023-4-22 09:10

申请会员ID:ai_po_jie

1、申 请 I D:ai_po_jie
2、个人邮箱:wingerX@126.com
3、原创技术文章
-========================================================-
python 自定义日期控件开发


1. 创建自定义类
import tkinter
from tkinter import ttk
import calendar
import datetime


class DatePicker(ttk.Entry):

    def __init__(self, master=None, **kw):
      self._separator = kw.pop('separator', '-')
      if self._separator not in ['-','/']:
            self._separator = '-'

      super().__init__(master, **kw)

      self.set_state(kw.get('state', ''))

      self.style = ttk.Style(self)
      self._setup_style()
      self.configure(style='DatePicker')

      self._year = tkinter.IntVar()
      self._year.set(datetime.date.today().year)
      self._month = tkinter.IntVar()
      self._month.set(datetime.date.today().month)
      self._day = tkinter.IntVar()
      self._day.set(datetime.date.today().day)
      self._set_text()

      self._frame = tkinter.Toplevel(self, background='gray', borderwidth=1)
      self._frame.withdraw()                     #   隐藏窗口
      self._frame.overrideredirect(True)         #   显示下拉时,不带标题栏 (Toplevel是一个窗口)
      self._set_frame()

      self.bind('<Motion>', self._on_motion)
      self.bind('<Leave>', lambda e: self.state(['!active']))
      self.bind('<Button-1>', self._show_down_frame)
      self._top_frame.bind('<FocusOut>', self._on_focus_out)


    def _setup_style(self):
      """
      设置样式(复制TCombobox样式)
      :return:
      """
      self.style.layout('DatePicker', self.style.layout('TCombobox'))
      conf = self.style.configure('TCombobox')
      if conf:
            self.style.configure('DatePicker', **conf)
      maps = self.style.map('TCombobox')
      if maps:
            self.style.map('DatePicker', **maps)


    def _on_motion(self, event):
      x, y = event.x, event.y
      if 'disabled' not in self.state():
            if self.identify(x, y) == 'Combobox.rightdownarrow':
                self.state(['active'])
                self.configure(cursor='arrow')
            else:
                self.state(['!active'])
                self.configure(cursor='xterm')


    def _show_down_frame(self, event):
      """
      显示下拉
      :param event:
      :return:
      """
      if ('disabled' in self.state()) or (self.identify(event.x, event.y) != 'Combobox.rightdownarrow'):
            return

      if self._sp_year.winfo_ismapped():   #   _btn被绘制(下拉打开状态)
            self._frame.withdraw()             #   隐藏下拉窗口(关闭下拉)
      else:
            x = self.winfo_rootx()
            y = self.winfo_rooty()+ self.winfo_height()

            self._frame.geometry('+%i+%i' % (x,y))
            self._frame.deiconify()                     #   打开下拉(显示窗口)
            self._sp_year.focus_set()


    def _on_focus_out(self, event):
      if self.focus_get() == None and 'active' inself.state():   #下拉打开再次点击下拉
            pass
      else:
            self._frame.withdraw()


    def _set_frame(self):
      """
      设置下拉界面
      :return:
      """
      self._top_frame = ttk.Frame(self._frame)
      self._top_frame.pack(fill='x')

      self._sp_year = ttk.Spinbox(self._top_frame, from_=1900, to=5000, width=6, textvariable=self._year)
      self._sp_year.grid(row=0, column=0, padx=2, pady=1)
      ttk.Label(self._top_frame, text='年').grid(row=0, column=1, padx=2)

      self._sp_month = ttk.Spinbox(self._top_frame, from_=1, to=12, width=4, textvariable=self._month)
      self._sp_month.grid(row=0, column=2, padx=3, pady=1)
      ttk.Label(self._top_frame, text='月').grid(row=0, column=3, padx=2)


      _middle_frame = ttk.Frame(self._frame)
      _middle_frame.pack(fill='x')
      self._initial_labels(_middle_frame)

      _bottom_frame = ttk.Frame(self._frame)
      _bottom_frame.pack(fill='x')
      _label_today = ttk.Label(_bottom_frame, text='今天: '+ str(datetime.date.today()))
      _label_today.pack(anchor='e', ipadx=8)
      _label_today.bind('<Motion>', self._on_label_today_motion)
      _label_today.bind('<Leave>', self._on_label_today_leave)
      _label_today.bind('<Button-1>', self._on_label_today_click)

      self._sp_year.configure(command=self._on_change)
      self._sp_year.bind('<KeyRelease>', self._on_spinbox_press)
      self._sp_year.bind('<Return>', self._on_year_return)

      self._sp_month.configure(command=self._on_change)
      self._sp_month.bind('<KeyRelease>', self._on_spinbox_press)
      self._sp_month.bind('<Return>', self._on_month_return)


    def _on_label_today_motion(self,event):
            event.widget.configure(foreground='DeepSkyBlue')


    def _on_label_today_leave(self, event):
            event.widget.configure(foreground=self.cget('background'))


    def _on_label_today_click(self, event):
      self._year.set(datetime.date.today().year)
      self._month.set(datetime.date.today().month)
      self._day.set(datetime.date.today().day)

      self._on_change()      #通过_on_change()重新设置self._day_list
      self._set_text()
      self._frame.withdraw()


    def _on_year_return(self,event):
      if self._year.get() > int(event.widget.cget('to')):
            self._year.set(event.widget.cget('to'))
      if self._year.get() < int(event.widget.cget('from')):
            self._year.set(event.widget.cget('from'))

      self._sp_month.focus()
      self._on_change()


    def _on_month_return(self,event):
      if self._month.get() > int(event.widget.cget('to')):
            self._month.set(event.widget.cget('to'))
      if self._month.get() < int(event.widget.cget('from')):
            self._month.set(event.widget.cget('from'))

      self._sp_year.focus()
      self._on_change()


    def _on_spinbox_press(self, event):
      txt = event.widget.get()
      if event.keysym:
            if not txt.isdigit():
                event.widget.set(''.join(i for i in txt if i.isdigit()))


    def _initial_labels(self, parent):
      """
      初始化日期标签
      :param parent:
      :return:
      """
      week = ('一', '二', '三', '四', '五', '六', '日')
      for i, item in enumerate(week):
            ttk.Label(parent, text=item.center(3)).grid(row=0, column=i)

      self._day_list = []
      self._label_list = []
      self._set_day_list()
      for i, item in enumerate(self._day_list):
            label = ttk.Label(parent, text=str(item[2]).rjust(2))
            label.grid(row= i // 7 + 1, column=item[3], padx=1)
            label.hint = item                #添加一个属性
            label['foreground'] ='Black'
            if item[1] != self._month.get():
                label['foreground'] = 'Gray'
            label['background'] = self.cget('background')
            if item[2] == self._day.get() anditem[1] == self._month.get():
                label['background'] = 'DeepSkyBlue'

            label.bind('<Button-1>', self._on_label_click)
            label.bind('<Motion>', self._on_label_motion)
            label.bind('<Leave>', self._on_label_leave)
            self._label_list.append(label)


    def _set_day_list(self):
      """
      设置日期列表
      :return:
      """
      for day in calendar.Calendar().itermonthdays4(self._year.get(), self._month.get()):
            self._day_list.append(day)


      if len(self._day_list) == 28:   #本月只有28天且一号为星期一
            for day in calendar.Calendar().itermonthdays4(self._year.get(), self._month.get() + 1):
                self._day_list.append(day)
                if len(self._day_list) == 35:
                  break


    def _on_change(self):
      self._day_list.clear()
      self._set_day_list()

      for i, label in enumerate(self._label_list):
            label.hint = self._day_list
            label['text'] = self._day_list[2]

            label['foreground'] ='Black'
            if self._day_list[1] != self._month.get():
                label['foreground'] = 'Gray'

            label['background'] = self.cget('background')
            if self._day_list[1] == self._month.get() and self._day_list[2] == self._day.get():
                label['background'] = 'DeepSkyBlue'


    def _set_text(self):
      """
      设置日期至文本框
      :return:
      """
      readonly = False
      if 'readonly' in self.state():
            readonly = True
            self.state(['!readonly'])

      txt = self._separator.join([str(self._year.get()), str(self._month.get()), str(self._day.get())])
      self.delete(0, 'end')
      self.insert(0, txt)
      if readonly:
            self.state(['readonly'])


    def _on_label_click(self, event):
      if event.widget.hint is not None:
            for label in self._label_list:
                if label != event.widget:   # 刷新背景色(修改默认日期的背景色)
                  label.configure(background=self.cget('background'))

            self._year.set(event.widget.hint[0])
            self._month.set(event.widget.hint[1])
            self._day.set(event.widget.hint[2])

            self._set_text()
            self._frame.withdraw()


    def _on_label_motion(self, event):
      if event.widget.hint is not None:
            event.widget.configure(background= 'SkyBlue')
            if event.widget.hint[1] == self._month.get() and event.widget.hint[2] == self._day.get():
                event.widget.configure(background='DeepSkyBlue')


    def _on_label_leave(self, event):
      if event.widget.hint is not None:
            event.widget.configure(background= self.cget('background'))
            if event.widget.hint[1] == self._month.get() and event.widget.hint[2] == self._day.get():
                event.widget.configure(background='DeepSkyBlue')


    def _set_date(self, text):
      """
      设置日期
      :param text:被设置的日期文本
      :return:
      """
      try:
            formatstr = self._separator.join(['%Y','%m','%d'])
            cur_date = datetime.datetime.strptime(text, formatstr)
            self._year.set(cur_date.date().year)
            self._month.set(cur_date.date().month)
            self._day.set(cur_date.date().day)
            self._set_text()
      except Exception:
                raise ValueError("%s不是一个合法的日期" % text)

    def _get_date(self):
      return self.get()


    def set_state(self, *args):
      """
      设置状态
      :param args:
      :return:
      """
      if args:
            if ('disabled' in args) or ('readonly' in args):
                self.configure(cursor='arrow')
            elif ('!disabled' in args) or ('!readonly' in args):
                self.configure(cursor='xterm')
            self.state(args)

    date = property(_get_date, _set_date)


2. 测试主界面
import tkinter as tk
import datepicker


class GUI:

    def __init__(self):
      self.root = tk.Tk()
      self.root.title('演示')
      self.root.geometry("300x230+300+150")
      self.interface()

    def interface(self):
      """"界面编写位置"""
      self.Label0 = tk.Label(self.root, text="日期")
      self.Label0.grid(row=0, column=0, padx=2)

      self.date = datepicker.DatePicker(self.root, width='10')
      self.date.grid(row=0, column=1, padx=2)

      self.Button = tk.Button(self.root, text="获取日期", width=7, command=self.show)
      self.Button.grid(row=0, column=2,padx=2)

      self.text = tk.Text(self.root, width=30, height=10)
      self.text.grid(row=1, column=0, columnspan=3)


    def show(self):
      # 获取日期
      self.text.delete(0.0,'end')
      self.text.insert(1.0, f"日期1:{self.date.date}\n")
      self.text.insert(1.0, f"日期2:{self.date.date.replace('-', '/')}\n")


if __name__ == '__main__':
    a = GUI()
    a.root.mainloop()


3. 运行--略(不能上传gif 图片)



Hmily 发表于 2023-4-23 11:24

抱歉,未能达到申请要求,申请不通过,可以关注论坛官方微信(吾爱破解论坛),等待开放注册通知。

(^_^) 发表于 2023-4-24 08:34

Hmily 发表于 2023-4-23 11:24
抱歉,未能达到申请要求,申请不通过,可以关注论坛官方微信(吾爱破解论坛),等待开放注册通知。

版主,你好。是不是没达到精华技术水准?还是需要其他什么要求。我是按照申请要求发的, 这个是我的原创作品。

Hmily 发表于 2023-4-24 11:14

(^_^) 发表于 2023-4-24 08:34
版主,你好。是不是没达到精华技术水准?还是需要其他什么要求。我是按照申请要求发的, 这个是我的原创 ...

是的。。
页: [1]
查看完整版本: 申请会员ID:ai_po_jie