Tkinter |自定义小部件:无限(水平)滚动日历 [英] Tkinter | Custom widget: Infinite (horizontal) scrolling calendar

查看:78
本文介绍了Tkinter |自定义小部件:无限(水平)滚动日历的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

上下文



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




  • 每个表示 1天

  • 每个将代表一个人

  • 它具有无限水平滚动->从而可以无限地追溯到过去和现在

  • 日历中的单元格(框)将具有交互性,如果我按住左鼠标按钮,我可以选择用于进一步操作/功能的特定单元格



它看起来像这样:





(图像注:红色列在左边是一个单独的框架部件。包括在草图中以显示日历中各行的目的)






我的计划



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



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




我的进度(代码)

 将tkinter导入为tk 
导入日期时间

CELL_SIZE =(100,50)
FIRST_ROW_HEIGHT = 20

类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 .__ i nit __(self,root,
width = width,
height = height,
背景=背景,
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)

#创建画布和框架
self.calendar_canvas = tk.Canvas (自我)
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)

#建筑物滚动条
self.scrollbar = tk.Scrollbar(self,orient ='horizo​​ntal',command = self.calendar_canvas.xview)
self.scrollbar.pack(side = bottom,fill = x)

#连接滚动条
self.calendar_canvas.configure(xscrollcommand = self.scrollbar.set )
self.calendar_frame.bind(< Configure>,self.onFrameConfigure)

#变量
self.rows =行
self.cells = [ ]


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

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

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

#创建第一行(指示日期)
单元格=单元格(self.calendar_frame,高度= FIRST_ROW_HEIGHT)
单元格。grid(行= 0,列= i)

#将日期标签添加到第一行
单元格中。create_text(
CELL_SIZE [0] / 2,
FIRST _ROW_HEIGHT / 2,
文本=(今天+ datetime.timedelta(days = i))。strftime('%d /%m /%y'))

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

self.cells [i]。 append(cell)

日历是我的开发不足的自定义日历小部件。感谢此答案(来自@Ethan Field ),它也是我的起点(基本代码)。



当前, Calendar 小部件能够通过 Calendar.set()<创建日期(从今天开始)的天数。 / code>函数。



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

  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位









EDIT#1-创建 addFuture()函数



 #下日历:
def addFuture(self,day = 0):
今天=日期时间.date.today()

对于范围(日)中的i:
index = i + self.lastColumn
self.cells.append([])

#创建第一行(指示日期)
cell = Cell(self.calendar_frame,height = FIRST_ROW_HEIGHT)
cell.grid(row = 0,column = index)

#将日期标签添加到第一行
cell.create_text(
CELL_SIZE [0] / 2,
FIRST_ROW_HEIGHT / 2,
text =(今天+日期时间。 timedelta(days = index))。strftime('[%a]%d /%m /%y'))

对于范围内的c(self.rows):
cell = Cell(self.calendar_frame)
cell.grid(row = c + 1,column = index)

self.cells [i] .append(cell)

self.lastColumn = self.lastColumn +日期

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))
,如果self.scrollbar.get()[1]> 0.9:
self.addFuture(day = 10)

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



当滚动条出现时,我可以检测到它使用以下语句接近左侧: if self.scrollbar.get()[1]< 0.1:。但是,我需要某种 self.addPast()命令,该命令的作用与 self.addFuture()命令,但是(顾名思义)在左边增加天。

解决方案

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



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



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


CONTEXT

I am trying to create a custom calendar (Tkinter widget) that has these following attributes/features:

  • Each column represents 1 day
  • Each row will represent a person
  • It has infinite horizontal scrolling -> so it can go infinitely to the past and present
  • the cells (boxes) in the calendar are going to be interactive in a way that if I hold down Left-Mouse-Button, I can select specific cells for further operation/function

It would look something like this:

(Note to the image: the RED column on the left is will be a separate frame-widget. I just included in the sketch to show the purpose of the rows in the calendar)


MY PLAN

To be able to interact with the cells after building the calendar, I am planning to have each widget object stored in an array called self.cells.

Also since I want it to go infinitely to the past and future, at initialization, the calendar scrollbar will be in the middle.


MY PROGRESS (CODE)

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)

The Calendar is my under-development custom calendar widget. I manage to hook up this spreadsheet-like structure with scrollbar thanks to this answer (from @Ethan Field) which also served as my starting point (base code).

Currently, the Calendar widget is capable of creating n number of days (starting today's day) by Calendar.set() function.

You can try the widget using this code:

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


THE ISSUE/QUESTION

How can I implement the infinite scrolling effect? I have no clue how to make it work.


Miscellaneous Notes

  • Python 3, 64-bit
  • Windows 7, 64-bit


EDIT #1 - created addFuture() function

# 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

This addFuture() function is just slightly modified set() function. The addFuture() can be called multiple time and each time it will add day amount of days to the calendar. Just need to hook scrollbar up. However, how should I addPast()?


EDIT #2 - infinite scrolling to the future works!

The onFrameConfigure commands is called whenever the user drags the scrollbar, thus I added if self.scrollbar.get()[1] > 0.9: statement, to check if the x-axis of the scrollbar is getting closer to the rightmost end. If it is, it execute a function to add more days and the scrollbar somehow automatically readjust the scale (I have no clue why but it works).

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)

Thus, my window has infinite scroll to the future. My question is now how to make it infinitely scroll to the past (aka to the left)?.

I can detect the scrollbar when it nears the left-side by using this statement: if self.scrollbar.get()[1] < 0.1:. However, what I need is some kind of self.addPast() command which serve the same purpose as self.addFuture() command but (as the name imply) add days to the left.

解决方案

What you want to do is to make sure that the canvas always has enough columns to scroll one whole screen right or left. The first thing to do is start with that many columns. If you want each column to be 100 pixels, and you want the canvas itself to be 400 pixels, then you need a total of 1200 pixels worth of columns: 4 columns to the left, 4 visible columns, and 4 columns to the right.

Next, create a proxy for the scrollbar -- a custom command that gets called whenever the scrollbar is dragged. The first thing it should do is call the xview method of the canvas to do the actual scrolling. Then, once the canvas has been scrolled you need to calculate if you need to add any more columns to the right or the left to always maintain the buffer.

Once you've added any new columns to the right or left, you need to recompute the scrollregion of the canvas. When you do that, tkinter will automatically adjust the position and size of the scrollbar thumb.

这篇关于Tkinter |自定义小部件:无限(水平)滚动日历的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆