Skip to main content
 首页 » 编程设计

Python Tkinter 更改 StringVar 值以将其用作 UI 上的背景指示器

2025年05月04日30傻小

我正在构建一个 UI,用户必须在其中选择一些日期。我添加了一个名为 Date Indicator 的行,它显示了一个带有颜色的框。起始颜色(直到用户选择两个日期)为 grey。此颜色必须根据用户选择的日期而改变。

  • 如果他选择的两个日期之间的天数超过两周,则该框必须变为绿色。
  • 如果他选择的两个日期之间的天数少于两周,则该框必须变为黄色。
    如果他选择的两个日期之间的天数小于 一周,则该框必须变为红色。

这是所需解决方案的 Photoshop 结果:

代码好像有点长,不过只要查到tkCalendar类就可以了。从那里到底部只是您不需要更改的日历代码。 (是的,有一些 imports 不需要用于此代码,我维护它们是因为我必须将它们用于其他东西,但你可以删除它们)

# -*- coding: utf-8 -*- 
import os 
from Tkinter import * 
import Image 
import ImageTk 
import tkFileDialog 
import xlrd 
import csv 
from tkMessageBox import * 
from win32com.client import Dispatch 
import datetime 
import time 
import calendar 
 
year = time.localtime()[0] 
month = time.localtime()[1] 
day =time.localtime()[2] 
strdate = (str(year) +  "/" + str(month) + "/" + str(day)) 
 
fnta = ("Helvetica", 10) 
fnt = ("Helvetica", 10) 
fntc = ("Helvetica", 10, 'bold') 
 
 
strtitle = "Calendario" 
strdays= "Do  Lu  Ma  Mi  Ju  Vi  Sa" 
dictmonths = {'1':'Ene','2':'Feb','3':'Mar','4':'Abr','5':'May', 
    '6':'Jun','7':'Jul','8':'Ago','9':'Sep','10':'Oct','11':'Nov', 
    '12':'Dic'} 
 
 
class Planificador(Frame): 
    def __init__(self,master): 
        Frame.__init__(self, master) 
        self.master = master 
        self.initUI() 
 
    def initUI(self): 
        self.master.title("Planner") 
        self.frameOne = Frame(self.master) 
        self.frameOne.grid(row=0,column=0) 
        self.frameTwo = Frame(self.master) 
        self.frameTwo.grid(row=0, column=1) 
        self.frameThree = Frame(self.master) 
        self.frameThree.grid(row=0, column=2) 
 
        self.frameFour = Frame(self.master) 
        self.frameFour.grid(row=1,column=0, sticky=N) 
        self.frameFive = Frame(self.master) 
        self.frameFive.grid(row=1, column=1) 
        self.frameSix = Frame(self.master) 
        self.frameSix.grid(row=1, column=2, sticky=N) 
 
        self.frameSeven = Frame(self.master) 
        self.frameSeven.grid(row=2,column=0) 
        self.frameEight = Frame(self.master) 
        self.frameEight.grid(row=2, column=1, sticky=S) 
        self.frameNine = Frame(self.master) 
        self.frameNine.grid(row=2, column=2) 
 
        self.start_date_menu() 
 
    def start_date_menu(self): 
        self.initial_num_elements = 4 
        self.TEXT_MENU_ROW = 0  # initial date menu grid row 
        self.COL_WIDTH = 10  # width of each subcolumn of dates 
 
        for frame in (self.frameFour, self.frameSix): 
            self.dayintext = Label(frame, text="Day in", 
                                        width=self.COL_WIDTH, justify="center") 
            self.dayintext.grid(row=self.TEXT_MENU_ROW, column=0) 
            self.dayouttext = Label(frame, text="Day out", width=self.COL_WIDTH, 
                                   justify="center") 
            self.dayouttext.grid(row=self.TEXT_MENU_ROW, column=1) 
            self.status = Label(frame, text="Date indicator", width=self.COL_WIDTH, 
                                   justify="center") 
            self.status.grid(row=self.TEXT_MENU_ROW, column=2) 
 
        self.dates = [self.create_all_entrys(aux_index) 
                        for aux_index in xrange(self.initial_num_elements)] 
 
        self.anadirpiezas = Button(self.frameEight, text="add more", 
                                   command=self.addone, width=self.COL_WIDTH) 
        self.anadirpiezas.grid(row=0, column=3) 
 
 
    def addone(self): 
        self.dates.append(self.create_all_entrys(len(self.dates))) 
        self.printdates() 
 
    def printdates(self): 
        print "IN:" 
        for i in xrange(self.initial_num_elements): 
            print self.dates[i][0].get() # col 0 
        print "OUT:" 
        for i in xrange(self.initial_num_elements): 
            print self.dates[i][2].get() # col 2 
 
    def create_all_entrys(self, aux_index): 
        menu_col = aux_index % 2  # left/right column of the date frame 
        menu_row = self.TEXT_MENU_ROW + aux_index/2 + 1 
        frame = self.frameSix if aux_index % 2 else self.frameFour 
 
        in_var = StringVar(value="--------") 
        in_btn = Button(frame, textvariable=in_var, width=self.COL_WIDTH, 
                        command=lambda v=in_var: self.fnCalendar(v)) 
        in_btn.grid(row=menu_row, column=0) 
 
        out_var = StringVar(value="--------") 
        out_btn = Button(frame, textvariable=out_var, width=self.COL_WIDTH, 
                         command=lambda v=out_var: self.fnCalendar(v)) 
        out_btn.grid(row=menu_row, column=1) 
 
        self.colorvar = StringVar() 
        self.colorvar.set('grey') 
        self.status_color = Label(frame, width=2, bg=self.colorvar.get()) 
        self.status_color.grid(row=menu_row,column=2) 
 
        return in_var, in_btn, out_var, out_btn 
 
    def fnCalendar(self, datebar): 
        tkCalendar(self.master, year, month, day, datebar) 
 
class tkCalendar : 
  def __init__ (self, master, arg_year, arg_month, arg_day, 
       arg_parent_updatable_var): 
    print arg_parent_updatable_var.get() 
    self.update_var = arg_parent_updatable_var 
    top = self.top = Toplevel(master) 
    top.title("Choose a date") 
    try : self.intmonth = int(arg_month) 
    except: self.intmonth = int(1) 
    self.canvas =Canvas (top, width =200, height =220, 
      relief =RIDGE, background ="#ece9d8", borderwidth =0) 
    self.canvas.create_rectangle(0,0,303,30, fill="#ece9d8",width=0 ) 
    self.canvas.create_text(100,17, text="Choose!",  font=fntc, fill="#BA1111") 
    stryear = str(arg_year) 
 
    self.year_var=StringVar() 
    self.year_var.set(stryear) 
    self.lblYear = Label(top, textvariable = self.year_var, 
        font = fnta, background="#ece9d8") 
    self.lblYear.place(x=85, y = 30) 
 
    self.month_var=StringVar() 
    strnummonth = str(self.intmonth) 
    strmonth = dictmonths[strnummonth] 
    self.month_var.set(strmonth) 
 
    self.lblYear = Label(top, textvariable = self.month_var, 
        font = fnta, background="#ece9d8") 
    self.lblYear.place(x=85, y = 50) 
    #Variable muy usada 
    tagBaseButton = "Arrow" 
    self.tagBaseNumber = "DayButton" 
    #draw year arrows 
    x,y = 40, 43 
    tagThisButton = "leftyear" 
    tagFinalThisButton = tuple((tagBaseButton,tagThisButton)) 
    self.fnCreateLeftArrow(self.canvas, x,y, tagFinalThisButton) 
    x,y = 150, 43 
    tagThisButton = "rightyear" 
    tagFinalThisButton = tuple((tagBaseButton,tagThisButton)) 
    self.fnCreateRightArrow(self.canvas, x,y, tagFinalThisButton) 
    #draw month arrows 
    x,y = 40, 63 
    tagThisButton = "leftmonth" 
    tagFinalThisButton = tuple((tagBaseButton,tagThisButton)) 
    self.fnCreateLeftArrow(self.canvas, x,y, tagFinalThisButton) 
    x,y = 150, 63 
    tagThisButton = "rightmonth" 
    tagFinalThisButton = tuple((tagBaseButton,tagThisButton)) 
    self.fnCreateRightArrow(self.canvas, x,y, tagFinalThisButton) 
    #Print days 
    self.canvas.create_text(100,90, text=strdays, font=fnta) 
    self.canvas.pack (expand =1, fill =BOTH) 
    self.canvas.tag_bind ("Arrow", "<ButtonRelease-1>", self.fnClick) 
    self.canvas.tag_bind ("Arrow", "<Enter>", self.fnOnMouseOver) 
    self.canvas.tag_bind ("Arrow", "<Leave>", self.fnOnMouseOut) 
    self.fnFillCalendar() 
 
  def fnCreateRightArrow(self, canv, x, y, strtagname): 
    canv.create_polygon(x,y, [[x+0,y-5], [x+10, y-5] , [x+10,y-10] , 
        [x+20,y+0], [x+10,y+10] , [x+10,y+5] , [x+0,y+5]], 
        tags = strtagname , fill="black", width=0) 
 
  def fnCreateLeftArrow(self, canv, x, y, strtagname): 
    canv.create_polygon(x,y, [[x+10,y-10], [x+10, y-5] , [x+20,y-5] , 
        [x+20,y+5], [x+10,y+5] , [x+10,y+10] ], 
        tags = strtagname , fill="black", width=0) 
 
 
  def fnClick(self,event): 
    owntags =self.canvas.gettags(CURRENT) 
    if "rightyear" in owntags: 
    intyear = int(self.year_var.get()) 
    intyear +=1 
    stryear = str(intyear) 
    self.year_var.set(stryear) 
    if "leftyear" in owntags: 
    intyear = int(self.year_var.get()) 
    intyear -=1 
    stryear = str(intyear) 
    self.year_var.set(stryear) 
    if "rightmonth" in owntags: 
    if self.intmonth < 12 : 
        self.intmonth += 1 
        strnummonth = str(self.intmonth) 
        strmonth = dictmonths[strnummonth] 
        self.month_var.set(strmonth) 
    else : 
        self.intmonth = 1 
        strnummonth = str(self.intmonth) 
        strmonth = dictmonths[strnummonth] 
        self.month_var.set(strmonth) 
        intyear = int(self.year_var.get()) 
        intyear +=1 
        stryear = str(intyear) 
        self.year_var.set(stryear) 
    if "leftmonth" in owntags: 
    if self.intmonth > 1 : 
        self.intmonth -= 1 
        strnummonth = str(self.intmonth) 
        strmonth = dictmonths[strnummonth] 
        self.month_var.set(strmonth) 
    else : 
        self.intmonth = 12 
        strnummonth = str(self.intmonth) 
        strmonth = dictmonths[strnummonth] 
        self.month_var.set(strmonth) 
        intyear = int(self.year_var.get()) 
        intyear -=1 
        stryear = str(intyear) 
        self.year_var.set(stryear) 
    self.fnFillCalendar() 
  def fnFillCalendar(self): 
    init_x_pos = 20 
    arr_y_pos = [110,130,150,170,190,210] 
    intposarr = 0 
    self.canvas.delete("DayButton") 
    self.canvas.update() 
    intyear = int(self.year_var.get()) 
    monthcal = calendar.monthcalendar(intyear, self.intmonth) 
    for row in monthcal: 
    xpos = init_x_pos 
    ypos = arr_y_pos[intposarr] 
    for item in row: 
        stritem = str(item) 
        if stritem == "0": 
        xpos += 27 
        else : 
        tagNumber = tuple((self.tagBaseNumber,stritem)) 
        self.canvas.create_text(xpos, ypos , text=stritem, 
            font=fnta,tags=tagNumber) 
            xpos += 27 
    intposarr += 1 
    self.canvas.tag_bind ("DayButton", "<ButtonRelease-1>", self.fnClickNumber) 
    self.canvas.tag_bind ("DayButton", "<Enter>", self.fnOnMouseOver) 
    self.canvas.tag_bind ("DayButton", "<Leave>", self.fnOnMouseOut) 
 
  def fnClickNumber(self,event): 
    owntags =self.canvas.gettags(CURRENT) 
    for x in owntags: 
    if (x == "current") or (x == "DayButton"): pass 
    else : 
        strdate = (str(self.year_var.get()) + "/" + 
            str(self.intmonth) + "/" + 
            str(x)) 
        self.update_var.set(strdate) 
        self.top.withdraw() 
 
  def fnOnMouseOver(self,event): 
    self.canvas.move(CURRENT, 1, 1) 
    self.canvas.update() 
 
  def fnOnMouseOut(self,event): 
    self.canvas.move(CURRENT, -1, -1) 
    self.canvas.update() 
 
 
if __name__ == "__main__": 
    root = Tk() 
    aplicacion = Planificador(root) 
    root.mainloop() 

我完全迷失了尝试这样做。我知道我必须更改 self.colorvar 但我不知道该怎么做。例如,我尝试使用 self.colorvar.set('red') 但我无法按照上面解释的方式进行操作。 in 和 out 日期可以在 printdates 函数中找到。非常感谢您的帮助。

请您参考如下方法:

我认为您不能将 bg 选项链接到 StringVar。您只能使用选项 textvariable 来完成它。你应该这样做的方式是通过 Label.config() 方法。

所以你可以这样写:

self.status_color.config(bg='red') 

或者,如果您想继续使用 colorvar:

self.colorvar.set('red') 
self.status_color.config(bg=self.colorvar.get()) 

这应该行得通,我想您可以在 2 周内弄清楚。

编辑:

您还可以将 StringVar self.colorvar“链接”到更改 self.status_color 的函数(使用 trace 方法);这样每当 colorvar 改变时,你的函数就会被调用,它会改变状态颜色。 这是我的建议:

def myFunc(*args): 
    self.status_color.config(bg=self.colorvar.get()) 
 
self.colorvar.trace('w',self.myFunc) 

现在每次调用 self.colorvar.set() 时,您也会调用 self.myFunc。

编辑:

至于周的事情,我不打算完全回答,因为它看起来很容易。只需获取 IN 和 OUT 的月份和日期的整数,然后执行如下操作:

monthdiff = monthout-monthin 
if monthdiff!=0: 
    dayout += monthdiff*30 #or 31, depending on the month. Just add an if/else 
 
weeks = ((dayout-dayin)//7)+1 #For python2.7 use only one slash 

然后您的 if/else 具有颜色更改功能。一月份也要小心,如果多的话就加一个。我想您可以自己解析 IN 和 OUT 字符串来获取月份和日期。