一个基于Python的tkinter模块实现的游戏活动日历模拟器

时间:2025-01-29 17:16:07
#encoding: utf-8 import os, sys, time, datetime import xlrd import functools from tkinter import * from tkinter import messagebox class ActivityBase: def __init__(self, id, name, type, cycleBeginType, actBeginTime, actEndTime, cycle, cycleContinueTime, beginSvrOpenDays, endSvrOpenDays): # 活动id self.id = id # 名称 self.name = name # 活动类型 self.type = type # 周期起始时间类型 self.cycleBeginType = cycleBeginType # 活动开始时间 self.actBeginTime = actBeginTime # 活动结束时间 self.actEndTime = actEndTime # 循环周期 self.cycle = cycle # 周期内持续时间 self.cycleContinueTime = cycleContinueTime # 开始开服天数 self.beginSvrOpenDays = beginSvrOpenDays # 结束开服天数 self.endSvrOpenDays = endSvrOpenDays def __getOneDateZeroClockSec(self, sec): return int(time.mktime(time.strptime(time.strftime('%Y-%m-%d', time.localtime(sec)), '%Y-%m-%d'))) # 获取活动首次开启时间 def __getActRealBeginTime(self, initTime): #按活动开启时间算 if self.cycleBeginType == 1: return self.actBeginTime #开服当天零点 actBeginTime = initTime + (self.beginSvrOpenDays - 1) * 86400 actBeginTime += (self.actBeginTime - self.__getOneDateZeroClockSec(self.actBeginTime)) return actBeginTime # 获取活动当次循环开启时间 def getCurCycleBeginTime(self, initTime, now): passDay = (now - initTime) // 86400 + 1 if passDay < self.beginSvrOpenDays or passDay > self.endSvrOpenDays: return -1 if now < self.actBeginTime or now > self.actEndTime: return -1 actBeginTime = self.__getActRealBeginTime(initTime) cycle = self.cycle * 86400 if cycle <= 0: return actBeginTime return (now - actBeginTime) // cycle * cycle + actBeginTime # 获取活动当次循环结束时间 def getCurCycleEndTime(self, initTime,now): if self.cycleContinueTime <= 0: return self.actEndTime return self.getCurCycleBeginTime(initTime, now) + self.cycleContinueTime class ContentData: def __init__(self, id, color, name, leaveDay, type): # 活动id self.id = id # 显示颜色 self.color = color # 名称 self.name = name # 活动剩余时间 self.leaveDay = leaveDay # 活动类型 self.type = type class ActivityCalendarMgr: def __init__(self): self.__curDay = 0 self.__initTime = 0 self.__oneDay = 0 self.__startDay = 0 self.__curWeek = 0 self.__labW = 150 self.__labH = 40 self.__labHNum = 18 self.__bottomH = 30 self.__winW = self.__labW * 7 self.__winH = self.__labH * self.__labHNum + self.__bottomH self.__root = Tk() self.__labelList = list() self.__cycleBeginTypeInt = { u'活动开始时间' : 1, u'开始开服天数' : 2, u'角色创建天数' : 3 } self.__actInfoList = list() #初始化 def __init(self, load= True, year = None, month = None, day = None): if year != None and month != None and day != None: self.__curDay = datetime.datetime(year, month, day) else: self.__curDay = datetime.datetime.now() self.__initTime = int(time.mktime(time.strptime(self.__curDay.strftime('%Y-%m-%d'), '%Y-%m-%d'))) # 设置一天为基准的变量 self.__oneDay = datetime.timedelta(days=1) # 这周起始日期,即程序显示起始日期 self.__startDay = self.__curDay - self.__oneDay * self.__curDay.weekday() # 当前处于第几周 self.__curWeek = 0 if load == True: self.__readExcel('活动配置表.xlsm', '活动配置表') self.__componentInit() self.__showContent() #读取excel数据 def __readExcel(self, xls_file, sheet_name): fieldList = [u'ID', u'名称', u'类型', u'周期起始时间类型', u'活动开始时间', u'活动结束时间', u'循环周期', u'周期内持续时间', u'开始开服天数', u'结束开服天数'] titleDic = dict() xls = xlrd.open_workbook(xls_file); sheet = xls.sheet_by_name(sheet_name); # 取出列名 for col in range(0, sheet.ncols): title = sheet.cell_value(0, col) titleDic[title] = col fieldDic = dict() # 一行一行读出列值 for row in range(1, sheet.nrows): for field in fieldList: col = titleDic[field] fieldDic.setdefault(field, []).append(sheet.cell_value(row, col)) for row in range(0, sheet.nrows - 1): id = int(fieldDic[u'ID'][row]) name = fieldDic[u'名称'][row] type = fieldDic[u'类型'][row] cycleBeginType = self.__cycleBeginTypeInt[fieldDic[u'周期起始时间类型'][row]] timeBuf = time.strptime(fieldDic[u'活动开始时间'][row], '%Y/%m/%d %H:%M:%S') actBeginTime = int(time.mktime(timeBuf)) timeBuf = time.strptime(fieldDic[u'活动结束时间'][row], '%Y/%m/%d %H:%M:%S') actEndTime = int(time.mktime(timeBuf)) if actEndTime < self.__initTime: continue toIntValue = lambda x : 0 if x == '' else int(x) cycle = toIntValue(fieldDic[u'循环周期'][row]) cycleContinueTime = toIntValue(fieldDic[u'周期内持续时间'][row]) beginSvrOpenDays = toIntValue(fieldDic[u'开始开服天数'][row]) endSvrOpenDays = toIntValue(fieldDic[u'结束开服天数'][row]) actInfo = ActivityBase(id, name, type, cycleBeginType, actBeginTime, actEndTime, cycle, cycleContinueTime, beginSvrOpenDays, endSvrOpenDays) self.__actInfoList.append(actInfo) #翻页 def __turnPage(self, num): if num < 0 and self.__curWeek <= 0: return self.__curWeek += num #调用一下刷新内容 self.__showContent() #切换开服日期 def __changeOpenDay(self, t1, t2, t3): year = int(t1.get().strip()) month = int(t2.get().strip()) day = int(t3.get().strip()) try: date = datetime.datetime(year, month, day) except: messagebox.showwarning(u'警示', u'输入日期 %d/%d/%d 不合法'%(year, month, day)) self.__root.focus_force() return self.__init(False, year, month, day) #按键输入捕获 def __keyPressBind(self, e, t1, t2, m): #lambda e: e if != 299 and in set('0123456789') else 'break' if (e.keycode == 8 #Space or e.keycode == 9 #Tab or e.keycode == 35 #End or e.keycode == 36 #Home or e.keycode == 46 #Delete ): pass elif e.keycode == 37: #Left if t1 != None and e.widget.index(INSERT) == 0: t1.focus_set() t1.icursor(END) elif e.keycode == 39: #Right if t2 != None and e.widget.index(INSERT) == len(e.widget.get().strip()): t2.focus_set() t2.icursor(0) elif e.char in set('0123456789'): num = len(e.widget.get().strip()) flag = e.widget.select_present() #数据输入满了,偏移光标(讨巧使用了模拟按下TAB键操作) if t2 != None and flag != True and num == m - 1: t2.focus_set() t2.select_range(0, END) t2.icursor(END) if flag != True and num >= m: return 'break' else: return 'break' return '' #ctrl+a捕获 def __selectAllBind(self, e): e.widget.select_range(0, END) e.widget.icursor(END) return 'break' #组件初始化 def __componentInit(self): textHegiht = self.__bottomH - 3 startX = 458 text1Weight = 32 text2Weight = 18 separateWeight = 10 text1 = Entry(self.__root, background = 'white', borderwidth = 1, takefocus = True) text1.insert(0, datetime.date.today().year) text1.bind('<Control-a>', self.__selectAllBind) text1.bind('<Control-A>', self.__selectAllBind) text1.place(x = startX, y = 0, width = text1Weight, height = textHegiht) separate1 = Text(self.__root, background = 'white', borderwidth = 0) separate1.insert(INSERT, '-') separate1.config(state = DISABLED) separate1.place(x = startX + text1Weight, y = 6, width = separateWeight, height = self.__bottomH) text2 = Entry(self.__root, background = 'white', borderwidth = 1, takefocus = True) text2.insert(0, datetime.date.today().month) text2.bind('<Control-a>', self.__selectAllBind) text2.bind('<Control-A>', self.__selectAllBind) text2.place(x = startX + text1Weight + separateWeight, y = 0, width = text2Weight, height = textHegiht) separate2 = Text(self.__root, background = 'white', borderwidth = 0) separate2.insert(INSERT, '-') separate2.config(state = DISABLED) separate2.place(x = startX + text1Weight + text2Weight + separateWeight, y = 6, width = separateWeight, height = self.__bottomH) text3 = Entry(self.__root, background = 'white', borderwidth = 1, takefocus = True) text3.insert(0, datetime.date.today().day) text3.bind('<Control-a>', self.__selectAllBind) text3.bind('<Control-A>', self.__selectAllBind) text3.place(x = startX + text1Weight + text2Weight + separateWeight * 2, y = 0, width = text2Weight, height = textHegiht) #输入框绑定按键行为 text1.bind('<KeyPress>', lambda event : self.__keyPressBind(event, None, text2, 4)) text2.bind('<KeyPress>', lambda event : self.__keyPressBind(event, text1, text3, 2)) text3.bind('<KeyPress>', lambda event : self.__keyPressBind(event, text2, None, 2)) #切换开服日期按钮 button = Button(self.__root, text = '切换开服日期', command = lambda : self.__changeOpenDay(text1, text2, text3), bg = 'white', activebackground = 'white') button.place(x = startX + text1Weight + text2Weight * 2 + separateWeight * 2 + 10, y = 0, width = 78, height = textHegiht) #绑定翻页行为(键盘左右键、鼠标滚轮) self.__root.bind('<Left>', lambda event : self.__turnPage(-1) if event.widget != text1 and event.widget != text2 and event.widget != text3 else 'break') self.__root.bind('<Right>', lambda event : self.__turnPage(1) if event.widget != text1 and event.widget != text2 and event.widget != text3 else 'break') self.__root.bind('<MouseWheel>', lambda event : self.__turnPage(-1) if event.delta > 0 else self.__turnPage(1)) #绑定聚焦行为 self.__root.bind('<Button-1>', lambda event : self.__root.focus_set() if event.widget != text1 and event.widget != text2 and event.widget != text3 else 'break') def __showContent(self): # 边框类型(relief参数: flat(默认), groove, raised, ridge, solid, sunken) frame = 'groove' # 边框宽度(像素) borderwidth = 1 #1.清屏(只执行一次) if len(self.__labelList) == 0: for y_index in range(self.__labHNum): for x_index in range(7): label = Label(self.__root, bg = 'white', relief = frame, bd = borderwidth) label.place(x = self.__labW * x_index, y = self.__labH * y_index + self.__bottomH, width = self.__labW, height = self.__labH) #2.释放旧标签内存 for l in self.__labelList: l.destroy() self.__labelList = list() #3.显示顶部栏 labels = ['星期一', '星期二', '星期三', '星期四', '星期五', '星期六', '星期日'] for index in range(7): day = self.__startDay + self.__oneDay * (self.__curWeek * 7 + index) title = str(labels[index]) + '\n' + day.strftime('%Y-%m-%d') color = 'grey' if day == self.__curDay: color = 'yellow' label = Label(self.__root, bg = color, text = title, relief = frame, bd = borderwidth) label.place(x = + self.__labW * index, y = self.__bottomH, width = self.__labW, height = self.__labH) self.__labelList.append(label) #4.内容栏数据提取 fieldSet = [set() for row in range(7)] contentList = [list() for row in range(7)] for index in range(7): day = self.__startDay + self.__oneDay * (self.__curWeek * 7 + index) color = 'SpringGreen' if day < self.__curDay: continue elif day == self.__curDay: color = 'yellow' for actInfo in self.__actInfoList: # 强行给 curTime 加20个小时,兼容特殊活动 curTime = int(time.mktime(time.strptime(day.strftime('%Y-%m-%d'), '%Y-%m-%d'))) + 72000 beginTime = actInfo.getCurCycleBeginTime(self.__initTime, curTime) if beginTime == -1: continue endTime = actInfo.getCurCycleEndTime(self.__initTime, curTime) if beginTime <= curTime and curTime <= endTime: if actInfo.id in fieldSet[index]: continue leaveDay = (endTime - curTime) // 86400 + 1 if index + leaveDay > 7: leaveDay = 7 - index contentList[index].append(ContentData(actInfo.id, color, actInfo.name, int(leaveDay), actInfo.type)) for i in range(leaveDay): fieldSet[index + i].add(actInfo.id) #5.数据刷新显示 yIndex = 0 for index in range(7): #先排序一下,先比活动剩余时间,时间越长越靠前;再比活动id,id越小越靠前 contentList[index].sort(key = functools.cmp_to_key(lambda x, y : x.leaveDay - y.leaveDay if x.leaveDay != y.leaveDay else y.id - x.id), reverse = True) for data in contentList[index]: yIndex += 1 if yIndex <= self.__labHNum: showText = u'%s\n%d(%s)'%(data.name, data.id, data.type) label = Label(self.__root, bg = data.color, text = showText, relief = frame, bd = borderwidth) label.place(x = + self.__labW * index, y = yIndex * self.__labH + 5 + self.__bottomH, width = self.__labW * data.leaveDay, height = self.__labH - 10) self.__labelList.append(label) #6.显示上限提示 if yIndex > self.__labHNum: messagebox.showwarning(u'警示', u'本周有%d个活动,显示上限为%d个,超出部分不显示'%(yIndex, self.__labHNum - 1)) self.__root.focus_force() return def run(self): #显示活动日历基本框架 self.__root.title('活动日历') self.__root.geometry(str(self.__winW) + 'x' + str(self.__winH) + '+400+100') self.__root.config(background = 'white') self.__root.resizable(False, False) #初始化数据 self.__init() self.__root.mainloop() if __name__== '__main__': mgr = ActivityCalendarMgr() mgr.run()