Tkinter |自定义窗口小部件:无限(水平)滚动日历

时间:2018-12-21 23:51:34

标签: python tkinter calendar infinite-scroll tkinter-canvas

上下文

我正在尝试创建具有以下属性/功能的自定义日历(Tkinter小部件):

  • 每个表示 1天
  • 每个将代表一个人
  • 它具有无限个水平滚动->,因此它可以无限地追溯到过去和现在
  • 日历中的
  • 单元格(框)将具有交互性,如果我按住左鼠标按钮,则可以选择用于进一步操作/功能的特定单元格

它看起来像这样:

sketch of how the widget should look like

(图像注释:左侧的RED列将是单独的框架小部件。我只是包括在草图中以显示日历中行的用途)


我的计划

为了能够在构建日历后与单元格进行交互,我计划将每个小部件对象存储在一个名为self.cells的数组中。

此外,由于我希望它无限地追溯到过去和将来,因此在初始化时,日历滚动条将位于中间。


我的进度(代码)

import tkinter as tk
import datetime

CELL_SIZE = (100, 50)
FIRST_ROW_HEIGHT = 20

class Cell(tk.Canvas):
    def __init__(self, root,
        width = CELL_SIZE[0],
        height = CELL_SIZE[1],
        highlightthickness = 1,
        background = 'white',
        highlightbackground = 'black',
        highlightcolor = 'red',
        *args, **kwargs):

        tk.Canvas.__init__(self, root,
            width = width,
            height = height,
            background = background,
            highlightthickness = highlightthickness,
            highlightbackground = highlightbackground,
            highlightcolor = highlightcolor,
            *args, **kwargs)

class Calendar(tk.Frame):
    def __init__(self, root, rows=0, *args, **kwargs):
        tk.Frame.__init__(self, root, *args, **kwargs)

        # create the canvas and frame
        self.calendar_canvas = tk.Canvas(self)
        self.calendar_frame = tk.Frame(self.calendar_canvas)
        self.calendar_canvas.create_window((4,4), window=self.calendar_frame, anchor="nw", tags="self.calendar_frame")
        self.calendar_canvas.pack(side="top", fill="both", expand=True)

        # building scrollbar
        self.scrollbar = tk.Scrollbar(self, orient='horizontal', command=self.calendar_canvas.xview)
        self.scrollbar.pack(side="bottom", fill="x")

        # hooking up scrollbar
        self.calendar_canvas.configure(xscrollcommand=self.scrollbar.set)
        self.calendar_frame.bind("<Configure>", self.onFrameConfigure)

        # variables
        self.rows = rows
        self.cells = []


    def onFrameConfigure(self, event):
        self.calendar_canvas.configure(scrollregion=self.calendar_canvas.bbox("all"))

    def set(self, day=0):
        today = datetime.date.today()

        for i in range(day):
            self.cells.append([])

            # create first row (indicating the date)
            cell = Cell(self.calendar_frame, height=FIRST_ROW_HEIGHT)
            cell.grid(row=0, column=i)

            # add the date label into the first row
            cell.create_text(
                CELL_SIZE[0]/2,
                FIRST_ROW_HEIGHT/2,
                text = (today + datetime.timedelta(days=i)).strftime('%d/%m/%y'))

            for c in range(self.rows):
                cell = Cell(self.calendar_frame)
                cell.grid(row=c+1, column=i)

                self.cells[i].append(cell)

Calendar是我开发中的自定义日历小部件。由于this answer(来自@Ethan Field)的帮助,我设法用滚动条连接了这种类似于电子表格的结构,这也是我的起点(基本代码)。

当前,Calendar小部件能够通过Calendar.set()函数创建 n 天(从今天开始)。

您可以使用以下代码尝试小部件:

root = tk.Tk()
calendar = Calendar(root, rows=3)
calendar.set(day=10)
calendar.pack(fill='both', expand=True)
root.mainloop()

问题/问题

如何实现无限滚动效果?我不知道如何使它工作。


其他注释

  • Python 3,64位
  • Windows 7,64位


编辑#1-创建了addFuture()函数

# under class Calendar:
def addFuture(self, day=0):
    today = datetime.date.today()

    for i in range(day):
        index = i + self.lastColumn
        self.cells.append([])

        # create first row (indicating the date)
        cell = Cell(self.calendar_frame, height=FIRST_ROW_HEIGHT)
        cell.grid(row=0, column=index)

        # add the date label into the first row
        cell.create_text(
            CELL_SIZE[0]/2,
            FIRST_ROW_HEIGHT/2,
            text = (today + datetime.timedelta(days=index)).strftime('[%a] %d/%m/%y'))

        for c in range(self.rows):
            cell = Cell(self.calendar_frame)
            cell.grid(row=c+1, column=index)

            self.cells[i].append(cell)

    self.lastColumn = self.lastColumn + day

addFuture()函数只是稍作修改的set()函数。 addFuture()可以被多次调用,并且每次将day天增加到日历中。只需将滚动条连接起来即可。但是,我应该如何addPast()


编辑#2-无限滚动到将来的作品!

每当用户拖动滚动条时,就会调用onFrameConfigure命令,因此我添加了if self.scrollbar.get()[1] > 0.9:语句,以检查滚动条的x轴是否越来越靠近最右边的 结束。如果是这样,它将执行添加更多天数的功能,并且滚动条以某种方式会自动重新调整比例(我不知道为什么为什么,但是可以使用)。

def onFrameConfigure(self, event):
    self.calendar_canvas.configure(scrollregion=self.calendar_canvas.bbox("all"))
    if self.scrollbar.get()[1] > 0.9:
        self.addFuture(day=10)

因此,我的窗口可以无限滚动到未来。现在,我的问题是如何使其无限滚动到过去(又名左侧)?

我可以使用以下语句if self.scrollbar.get()[1] < 0.1:来检测滚动条在左侧附近。但是,我需要的是某种self.addPast()命令,其功能与self.addFuture()命令相同,但是(顾名思义)在左边增加了几天。

1 个答案:

答案 0 :(得分:1)

您要执行的操作是确保画布始终具有足够的列以向右或向左滚动整个屏幕。首先要做的是从那么多列开始。如果您希望每列为100像素,并且画布本身为400像素,则总共需要包含1200像素的列:左侧4列,可见4列和右侧4列。

接下来,为滚动条创建一个代理-一个自定义命令,每当拖动滚动条时就会调用它。它应该做的第一件事是调用画布的xview方法来进行实际的滚动。然后,在画布滚动之后,您需要计算是否需要在右侧或左侧添加更多列以始终保持缓冲区。

在右侧或左侧添加任何新列后,您需要重新计算画布的scrollregion。当您这样做时,tkinter将自动调整滚动条滑块的位置和大小。