全自动生成、设置课表壁纸【完结】

在这里插入图片描述


0、来由

  • 去官网查课表太慢太麻烦
  • 在手机上留截图看屏幕小(主要那段时间看电脑多)
  • 电脑上存截图也得找
  • 那时候还没发现小爱自带的课表(也可能自动导入功能还没有)

  于是,就有了这个想法,之前也实现过(P好每周的课表蒙版图,存起来,然后写个脚本把课表蒙版跟壁纸合并,然后调用接口更换壁纸)文章考古-点这里。但是P图毕竟费时间,想要修改也比较费劲,也就自己折腾自己。

后来就有了新想法:

  • 根据课表数据,
  • 自动绘制课表蒙版,
  • 自动贴合背景,
  • 自动换壁纸。

1、设计、实现思路

1-1 课表数据文档设计

  将学期课表按一定规则,存储为文本文件,然后程序读取文件,根据当前时间与设定好的学期开始时间计算出当前周数,并设置学期总周数,超过则不显示课表。

当时编程还不是很规范,许多配置参数都只写死到代码中,要修改都只能到代码中找,很不方便。比如:

  • 学期开始时间
  • 总周数
  • 壁纸文件夹路路径
  • 课表文件路径
  • 课表位置偏移量
  • 。。。

其实,可将这些参数,同课表数据一样,抽离出来作为配置项,通过配置文件获取,从而就大大方便了定制化。

学期课表中,需要存储一些必要的信息项:

  • 课程名称。为了方便显示,尽量简写
  • 课程教室。
  • 课程时间。也可以记录为:当天的第几节课
  • 课程跨度。课程的开课周数,如[1, 3-6, 10] 表示1周3-6周10周有课。

  根据上述这些信息,就可以完整提取出每一周的课表。根据一些实际情况,做出一些调整:将教室课程简写合并为课程名称;于是,设置一个wks的列表表示周数,如wks[1:5]+wks[8:9] 表示: 1-4周+第8周
  鉴于Python有个强悍的函数eval(),那就直接把学期课表按照Python列表的格式设计,读取的时候直接对接就行;学期课表设计为一个含有五个元素的文档,每个元素为存储周一到周五中一天的课程信息;每个元素由六个列表单元构成,表示一天中的六节课(上午、下午、晚上各两节);而每个最小单元,也就是每节课,是一个二元列表(或是元组),第一个元素是设计后的 课表名称 如:B211大物,第二个元素为课程跨度,如:wks[1:5]+wks[8:9]

# 学期课表示例
[  # 周一课程
    [('105射频', wks[1:6]+wks[7:12])],
    [('419微测', wks[4:6]+wks[7:13])],
    [],
    [('407微波', wks[3:6]+wks[7:19])],
    [('317信完', wks[1:6]+wks[7:14])],
    []
];
[	# 周二课程
    [('305微原', wks[1:6]+wks[7:11]+wks[12:15])],
    [('114数电', wks[1:4])],
    [],
    [('418随机', wks[1:6]+wks[7:11]+wks[12:13])],
    [('114智能', wks[1:6]+wks[7:11]+wks[12:15])],
    []
];
[	# 周三课程
    [('105射频', wks[1:6]+wks[7:12])],
    [('211专业', wks[8:10])],
    [],
    [('211形政', wks[15:17])],
    [],
    []
];
[	# 周四课程
    [('305微原', wks[1:5]+wks[7:15]+wks[12:15])],
    [('219编程', wks[1:6]+wks[7:10]), ('105工概', wks[10:18])],
    [],
    [('418随机', wks[1:5]+wks[7:13])],
    [],
    []
];
[	# 周五课程
    [('407微波', wks[3:6]+wks[7:17])],
    [],
    [],
    [('518学导', wks[7:8]+wks[15:16])],
    [('onl学导', wks[1:2]+wks[11:12])],
    [],
];

1-2 解析获取周课表

  根据学期课表的设计,便可以逆向解析到周课表。大致思路为:获取到学期课表列表后,循环遍历五天的课表信息,再遍历每天的课程信息,如果当前周数存在于当前课程跨度中,那么向周课表列表中加入该课程,否则置入空字符串表示没课。最终得到周课表。

  • 重要信息:
    • 当前周数
    • 学期课表
# 获取此周课表
def getnow_cs(now_wk, cs_path):
	'''
	# now_wk: 当前周数
	# cs_path: 课表文件地址
	'''
	# 临时周课表路径
    path = op.join(op.split(cs_path)[0], 'tem_cls')
    # 如果已经生成过本周课表txt 那么读取后直接返回,否则执行后续
    if op.exists(c_path := op.join(path, f'{now_wk}周课表.txt')):
        f = open(c_path, 'r', encoding='utf8')
        if len(f.read()) > 20:
            with open(c_path, 'r', encoding='utf8') as f:
                now_cs = list(map(eval, f.read().split('\n')[:-1]))
                f.close()
                return now_cs

    clss = [op.join(path, name) for name in os.listdir(path)]
    # 删除过时的周课表
    for k in clss:
        os.remove(k)
    now_cs = [[] for i in range(5)]
    f = open(op.join(path, f'{now_wk}周课表.txt'), 'a', encoding='utf8')
    for wk in range(5):
    	# cls 代表单天的课程列表
        for cls in getall_cs(cs_path)[wk]:
            flag = 1
            # cl 代表每一节课
            for cl in cls:
            	# 如果当前周在当前循环课程的 周跨度中
            	# 将该课程添加到改周课表对应位置
                if now_wk in cl[1]:
                    now_cs[wk].append(cl[0])
                    flag = 0
            # flag = 1 表示当节课没有课程安排
            # 那么该位置添入 空字符串 占位
            if flag:
                now_cs[wk].append('')
        f.write(str(now_cs[wk]) + '\n')
    f.close()
    return now_cs

# 获取学期课表 传入学期课表路径
def getall_cs(cs_path):
	# 周数列表,最多应该不会超过 23 周
    wks = [i for i in range(23)]
    with open(cs_path, 'r', encoding='utf8') as f:
    	# eval 将每天课表的字符串数据转换为 列表;
    	# split 规则参照 1-1 的课表文档
        lh = list(map(eval, f.read().split(';\n')[:-1]))
    f.close()
    return lh

1-3 获取当前周数

先睡了,下次更新…

# 获取周数  
# 传入第一个周 周一的日期  
# 返回:当前周数
def get_imgid(start_time='2020 8 31'):
    #start_time = '2020 8 31'
    start = time.strptime(start_time, '%Y %m %d')
    start_days = int(time.strftime('%j', start))
    now_days = int(time.strftime('%j', time.localtime()))
    img_id = 1 + (now_days - start_days)//7
    return img_id

1-4 绘制周课表蒙版、合并背景壁纸

生成课表蒙版+合并背景壁纸

# 制作、存储壁纸
# 传入本周课表、图片路径、输出路径
# 最终函数保存一课表图片文件
def get_wap(now_cs, img_path, out_path):
    now_wk = op.split(out_path)[-1].split('周')[0][1:]
    path = img_path
    days = ['周一', '周二', '周三', '周四', '周五']
    times = ['1-2', '3-4', '午休', '5-6', '7-8', '晚']
    color = 'black'          # 课颜色
    tim_color = 'crimson'     # 节数颜色
    week_color = 'blueviolet'    # 周N颜色
    wek_color = 'azure'     # N周颜色
    color0 = 'lightgrey'    # 矩形填充颜色
    ret_color = None      # 矩形图层颜色
    alpha = .6
    line_color = 'darkorange'     # 竖线颜色
    xline_color = 'turquoise'     # 横线颜色
    width = 2               # 线宽

    font_size = 40          # 字体大小
    font = ImageFont.truetype(r'c:\windows\fonts\simkai.TTF', font_size)
    font1 = ImageFont.truetype(r'c:\windows\fonts\simkai.ttf', font_size+2)     # 字体背景
    color1 = 'grey'        # 字体背景颜色
    wx_font = ImageFont.truetype(r'c:\windows\fonts\simsun.ttc', 31)    # 午休

    # 横竖排间隔
    dy = (font_size*1.2)//1
    dx = (font_size*3.78)//1

    ddy = 35 # 控制整体上下移动
    dyy = 6  # 控制标题组上下移动

    # 打开图片--转为RGBA模式
    im = Image.open(path).convert('RGB')
    # 圆角矩形底色
    newi = Image.new('RGB', (1920, 1080))
    dd = ImageDraw.Draw(newi)

    x0, y0 = 4, 1080-(4*font_size+4*dy)-24 + ddy # 起始坐标  !!-1
    r = 34                  # 圆角半径
    w, h = 817, 1080-y0     # 矩形框尺寸
    '''Rounds'''  # 四个角先画4个圆
    dd.ellipse((x0, y0, x0 + r, y0 + r), fill=color0)
    dd.ellipse((x0 + w - r, y0, x0 + w, y0 + r), fill=color0)
    dd.ellipse((x0, y0 + h - r, x0 + r, y0 + h), fill=color0)
    dd.ellipse((x0 + w - r, y0 + h - r, x0 + w, y0 + h), fill=color0)
    '''rec.s'''  # 两个方框相切
    dd.rectangle((x0 + r / 2, y0, x0 + w - (r / 2), y0 + h), fill=color0)
    dd.rectangle((x0, y0 + r / 2, x0 + w, y0 + h - (r / 2)), fill=color0)
    # 遮罩, alpha 透明度调节
    im1 = Image.blend(newi, im, alpha=alpha)
    im.close()
    newi.close()
    # 获得带有底矩形框的背景 draw/im
    draw = ImageDraw.Draw(im1)

    # '第几周' 字样绘制
    draw.text((x0+w//2.5, y0+8-dyy), f'第{now_wk}周', font=font, fill=wek_color)
    draw.line((x0, y0+dy-dyy, w+x0, y0+dy-dyy), fill='gold', width=width+1)
    # 课程 时间点 绘制  节数
    xx, yy = x0+3, 1080-(4*font_size+3*dy)+25 + ddy  # 节数起始坐标  !!-2
    draw.line((xx + font_size*1.82//1-4, yy-4, w+x0, yy-4), fill=xline_color, width=width)    # 分割线 周下边
    for tim in times:
        if tim == '午休':
            # 先获取午休两线的坐标参数  --先不画
            wx_y1, wx_y2 = yy+6, yy+dy-22
            draw.text((x0, yy+2), tim, font=wx_font, fill='lawngreen')
        else:
            if tim=='晚':
                draw.text((xx+10, yy-12), tim, font=font, fill=tim_color)
            else:
                draw.text((xx, yy), tim, font=font, fill=tim_color)
            if tim != '3-4' and tim != '晚':
                # 横线
                draw.line((xx + font_size*1.82//1-4, yy+dy-8, w+x0, yy+dy-8), fill=xline_color, width=2)
        if times.index(tim) == 2 or times.index(tim) == 1:
            yy += dy-15
        else:
            yy += dy

    # 课表内容绘制+周N
    x = xx + font_size*1.82//1  # 周+课 起始坐标   !!-3
    day = 0
    for i_cls in now_cs:
        y = 1080-(4*font_size+4*dy)+25 + ddy    # 周+课 起始坐标   !!-3
        draw.text((x + 30, y), days[day], font=font1, fill=color1)  # 星期
        draw.text((x+30, y), days[day], font=font, fill=week_color)  # 星期
        day += 1
        # 竖线
        draw.line((x-.045*dx//1, y+30, x-.045*dx//1, y+h-64), fill=line_color, width=width)
        cnt = 0
        for cl in i_cls:
            if cnt == 2 or cnt == 3:
                y += dy - 15
            elif cnt == 5:
                y += dy - 10
            else:
                y += dy
            if cl:
                draw.text((x-2, y-1), cl, font=font1, fill=color1)
                draw.text((x, y), cl, font=font, fill=color)  # 周n遍历每一节课
            cnt += 1
        x += dx
    # 午休两根线
    draw.line((xx + font_size*1.82//1-4, wx_y1, w+x0, wx_y1), fill='red', width=width)
    draw.line((xx + font_size*1.82//1-4, wx_y2, w+x0, wx_y2), fill='red', width=width)
    im1.save(out_path, quality=100)

1-5 调用接口更换壁纸

先睡了,下次更新…

# 换壁纸 具体 函数
def setWallpaper(image_path):
    # 这是剽窃的,有时间研究一下啦
    key = win32api.RegOpenKeyEx(win32con.HKEY_CURRENT_USER, "Control Panel\\Desktop", 0, win32con.KEY_SET_VALUE)
    win32api.RegSetValueEx(key, "WallpaperStyle", 0, win32con.REG_SZ, "2")
    win32api.RegSetValueEx(key, "TileWallpaper", 0, win32con.REG_SZ, "0")
    win32gui.SystemParametersInfo(win32con.SPI_SETDESKWALLPAPER, image_path, 1 + 2)

# 换壁纸 框架
def setWallPaperBMP(imagePath):
    bmpImage = Image.open(imagePath)
    newPath = imagePath.replace('.jpg', '.bmp')
    bmpImage.save(newPath, "BMP")
    setWallpaper(newPath)
def ch_wallpaper(img_id, all_wek=18):
    test = 1
    if test:
        root_path = r'c:\wallp\Schedule'
    else:
        root_path = os.getcwd()

    bgs_path = op.join(root_path, 'bg')  # 壁纸路径
    cs_path = op.join(root_path, 'cls.txt')    # 总课表路径
    to_path = op.join(root_path, 'tem')     # 课表背景路径
    flag = op.join(root_path, r'flag\nshow')

    bg_list = [op.join(bgs_path, name) for name in os.listdir(bgs_path)]
    bg_path = random.choice(bg_list)
    while not bg_path.endswith('.jpg'):
        bg_path = random.choice(bg_list)
    # 每次运行都要删除一遍文件 -- 课表图片
    if len(lst := os.listdir(to_path)):
        tems = [op.join(to_path, name) for name in lst]
        for tem in tems:
            os.remove(tem)

    # 这里想办法搞一搞  周数大于18 或者 不想显示课表(判断某个文件在不在)
    if (img_id > all_wek) or (op.exists(flag)):  # 超过总周数就不贴课表
        setWallpaper(bg_path)
    else:
        if img_id <= 0:
            img_id = 1

        img_name = f'第{img_id}周.png'
        out_img = op.join(to_path, img_name)
        get_wap(now_cs=getnow_cs(img_id, cs_path), img_path=bg_path, out_path=out_img)
        if not op.exists(out_img):
            return None
        setWallpaper(out_img)

1-6 设置定时任务

先运行试试:

if __name__ == '__main__':
    start_time = '2022 4 30'
    screen_size = (1920, 1080)
    all_wek = 18
    img_id = get_imgid(start_time)
    ch_wallpaper(img_id, all_wek=all_wek)
    sys.exit()

定时任务

那么问题来了,代码是写好了,说好的自动换壁纸呢??!代码写好了就会自己 运行? 不急,还有最后的操作,跟我一步一步来

  • 首先在开始菜单搜索 任务计划程序,点击进入,如下图:
    是他
  • 然后点击右上角创建基本任务
    在这里插入图片描述
  • 起个名称,下一步
    在这里插入图片描述
  • 选择 每天 ->下一步
    在这里插入图片描述
  • 这里设置一下,然后下一步在这里插入图片描述
  • 直接下一步
  • 这里需要注意了在这里插入图片描述
    程序脚本|就只用填 python.exe
    —|—
    添加参数|填源代码的地址【例如:C:\users\lalala\desktop\ch_wallpaper.py ]
    起始于|填的是你Python.exe的安装目录【例如:C:\Program Files\Python38\Python38】
  • 然后回到主界面,找到刚添加好的任务,点击属性,修改一下设置在这里插入图片描述在这里插入图片描述在这里插入图片描述
    最后,如果还是不能每天运行的话,可以参考我的暴保守做法,修改触发器,让它每次登录都运行一下在这里插入图片描述

2、总结

2-1 用到的关键技术

  • Python eval() :从文件获取周课表数据列表
  • Python time 库:时间处理
  • Python os 库:路径、文件处理
  • Python Pillow 库:图像处理
  • Python Windows接口 更换桌面壁纸

2-2 不规范、不足之处、改进方向

  • 大部分参数与代码耦合度太高,调参是个大问题
  • 配置文件功能简单,可以发挥更多作用:将更多参数配置迁移到配置文件中,代码打包后便可以只修改配置文件而定制程序。
  • 配置文件按可以考虑采用JSON YAML
  • 可以编写图形化界面导入学期课表 或者 使用爬虫到网站直接爬取学期课表。
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

薛定谔的壳

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值