做個課程表吧

抽象

首先,我們需要明確自己的設計需求。

課程表應該能讀取課程,顯示課程,管理課程。

顯示效果

首先,我們顯示的應該是本週的課程,課程信息應該包括 課程名、節、地點、時間等必要信息,不同課程最好有不同顏色區分,同種課程的顏色最好相同。如下:

星期一 星期二 ……
第一節 課程一 課程二 ……
第二節 課程三 課程四 ……
…… …… …… ……

另外,我們有時候需要查看上下週的課程,所以應該有一個切換周次的按鈕。

管理課程

我們需要根據當前是第幾周來顯示課程,所以需要保存第一週的時間;同時,也要顯示當前是第幾周的課程數據,所以需要保存每一節課的時間。

我們需要添加課程、刪除課程,必要時能夠修改課程。

另外,有時不想手動輸入課程數據,我們應該能夠直接從教務系統導入課程,需要一系列的鍵值對。

因此,我們的需要一個啓動文件data.json:

{"start": [2020, 2, 20],
  "local": ["kbList"], 
  "book": {
    "name": "kcmc",
    "teacher": "xm",
    "week": "zcd", 
    "part": "jcor",
    "weekday": "xqjmc",
    "address": "cdmc"}, 
  "time": {"1": "8:50",
    "2": "9:40", 
    "3": "10:40", 
    "4": "11:30", 
    "5": "14:00", 
    "6": "14:50", 
    "7": "15:45", 
    "8": "16:35",
    "9": "19:00", 
    "10": "19:55", 
    "11": "20:50", 
    "12": "21:45"}
}

接下來是保存課程的數據:

{"課程名": {"color": "#665A79", "subject": [{"teacher": "教師", "week": "1-16周(雙)", "weekday": "星期幾", "address": "地點", "part": "3-4"}]}}

json不支持中文,打開後會有很多轉義符。

實現課程表

導入必要包

根據上面需求,我們能夠顯示時間的datetime包,讀取數據的json包,繪製界面的tkinter包,判斷文件是否存在的os包,還要一個隨機數來隨機生成顏色。

import datetime
import json
import tkinter
import os.path
from random import randint

導入數據

首先我們要判斷啓動文件是否存在,如果不存在就生成一個啓動文件。

if os.path.isfile("data.json"):
    # 用外部文件來保存鍵值對
    with open("data.json", "r") as f:
        init = json.loads(f.read())
else:
    with open("data.json", "w") as f:
        init = {"start": [2020, 2, 20],
            "time":
                {"1": "8:50",
                 "2": "9:40",
                 "3": "10:40",
                 "4": "11:30",
                 "5": "14:00",
                 "6": "14:50",
                 "7": "15:45",
                 "8": "16:35",
                 "9": "19:00",
                 "10": "19:55",
                 "11": "20:50",
                 "12": "21:45"}}
        json.dump(init, f)

啓動文件存在後,用就開始讀取課程數據。如果課程數據文件不存在,則創建。

# 刷新
def flesh():
    global js, weeks
    if os.path.isfile("my_class.json"):
        # 保存課程數據
        with open("my_class.json", "rb") as f:
            class_js = f.read()
            js = json.loads(class_js)  # 轉化爲json
    else:
        with open("my_class.json", "w") as f:
            f.write("{}")
            js = {}
    read_class(weeks)

時間初始化

我們要判斷當前是第幾周。

首先,我們要知道第一週在什麼時候。我們把第一週的時間保存在啓動文件的start上面,第一週的開始應該是星期天,如果不是星期天,就更改到星期天。

然後,我們要算出今天離第一週差了幾天,根據天數計算出當前是第幾周,保存到week上。

另外,還需要判斷今天是星期幾,保存在now_week上

def time_init():
    global weeks, now_week
    # 確認開始的日期 年/月/日
    init_year = init["start"][0]
    init_mouth = init["start"][1]
    init_day = init["start"][2]
    # 如果開始的一天不是週日,則將開始的日期變爲週日
    if not datetime.datetime(init_year, init_mouth, init_day).strftime("%w") == 0:
        init_day -= eval(datetime.datetime(init_year, init_mouth, init_day).strftime("%w"))

    # 初始化的時間
    init_date = datetime.datetime(init_year, init_mouth, init_day)
    # 現在的時間
    now_date = datetime.datetime.today()
    # 間隔的天數
    days = (now_date - init_date).days
    # 間隔的週數,第一週爲1
    weeks = int(days / 7) + 1
    # 框出今天星期幾
    now_week = eval(now_date.strftime("%w"))


time_init()

weekday = ["星期日", "星期一", "星期二", "星期三", "星期四", "星期五", "星期六"]

繪製課程表的核心

創建主窗體

創建一個窗體,窗體由幾部分構成,分別是課程表的框架還有一些功能的按鈕。

top = tkinter.Tk()  # 創建一個窗體
top.geometry("1100x650+200+50")  # 改變窗體的大小
top.title('課程表')
top.resizable(0, 0)

# 框架
box = tkinter.LabelFrame(top, text="課程表", background="#F8F8FF", height=600, width=1100)
box.pack()

set = tkinter.LabelFrame(top, text="管理", height=100, width=1100)
set.pack()

label_now_week = tkinter.Label(set, text="現在是第{}周,當前的週數爲".format(weeks))
label_now_week.pack(side=tkinter.LEFT)

week = tkinter.Variable()
week.set(weeks)

entry = tkinter.Entry(set, textvariable=week, width=10)
entry.pack(side=tkinter.LEFT)

last = tkinter.Button(set, text="跳轉", command=jump)
last.pack(side=tkinter.LEFT)

# 上下週按鈕
last = tkinter.Button(set, text="上一週", command=last_week)
next = tkinter.Button(set, text="下一週", command=next_week)
last.pack(side=tkinter.LEFT)
next.pack(side=tkinter.LEFT)

# 數據控制按鈕
chang_button = tkinter.Button(set, text="導入數據", command=data_change)
chang_button.pack(side=tkinter.LEFT)

# 數據控制按鈕
command_button = tkinter.Button(set, text="管理課程", command=command)
command_button.pack(side=tkinter.LEFT)

# 刷新
time_button = tkinter.Button(set, text="管理起始周-上課時間", command=set_time)
time_button.pack(side=tkinter.LEFT)

# 刷新
flesh_button = tkinter.Button(set, text="刷新", command=flesh)
flesh_button.pack(side=tkinter.LEFT)

# 關於
about_button = tkinter.Button(set, text="關於", command=about)
about_button.pack(side=tkinter.LEFT)

實現的效果如下:

在這裏插入圖片描述

繪製課程小格子

接下來讓我們顯示,這個功能是根據當前是第幾周來顯示課程,如果是現在周就顯示現在的課程,如果是點了上一週下一週就顯示該周課程。

這個函數主要用於判斷課程是否是當前周的課程。

在點擊上一週下一週後,該函數會被用到,所以要先刪去課程表上之前周的課程。

如果課程是單週課程(數據包含單字),需要判斷現在是否是單週,雙週同理。

def read_class(_week):
    for widget in box.winfo_children():
        widget.destroy()
    draw_week()
    for c in js:
        name = c
        for i in js[c]['subject']:
            _week = i["week"]
            # 判斷課程是否是單雙週的課程
            if "單" in _week:
                _week = _week.replace("周(單)", "")
                _week = _week.split("-")
                # 開始周/結束周
                start_week, end_week = eval(_week[0]), eval(_week[-1])
                if weeks % 2 == 1:  # 判斷是否是單週
                    if start_week <= weeks <= end_week:  # 判斷該課程是否是當前周的課程
                        if start_week <= weeks <= end_week:  # 判斷該課程是否是當前周的課程
                            # 根據節來優化顯示效果
                            draw_box(name, i)

            elif "雙" in _week:
                _week = _week.replace("周(雙)", "")
                _week = _week.split("-")
                # 開始周/結束周
                start_week, end_week = eval(_week[0]), eval(_week[-1])
                if weeks % 2 == 0:  # 判斷是否是雙週
                    if start_week <= weeks <= end_week:  # 判斷該課程是否是當前周的課程
                        if start_week <= weeks <= end_week:  # 判斷該課程是否是當前周的課程
                            draw_box(name, i)

            else:
                _week = _week.replace("周", "")
                _week = _week.split("-")
                # 開始周/結束周
                start_week, end_week = eval(_week[0]), eval(_week[-1])
                if start_week <= weeks <= end_week:  # 判斷該課程是否是當前周的課程
                    # 根據節來優化顯示效果
                    draw_box(name, i)

如果確實是單週課程,就進行繪製。

def draw_box(courses, course):
    scr = "{}\n講師 {}\n周 {}\n地點 {}".format(
        courses, course["teacher"], course["week"], course["address"])  # 要顯示的課程信息
    part = course["part"]
    part = part.split("-")
    start_part, end_part = eval(part[0]), eval(part[-1])

    # 確認文本的位置
    x = weekday.index(course["weekday"])
    # 創建一個文本控件
    text = tkinter.Label(box, text=scr, width=20, fg="#FFFFFF", bg=js[courses]['color'],
                         height=2 * (end_part - start_part + 1))
    text.place(x=x * 150 + 40, y=start_part * 40 + 20)  # 在屏幕上放置文本控件

繪製效果如下:

在這裏插入圖片描述

其他功能

上下週

兩個函數用於顯示上下週,與兩個按鈕綁定。

上一週時,如果週數爲1,就禁止上一週的按鈕操作。

def next_week():
    global weeks
    weeks += 1
    read_class(weeks)
    week.set(weeks)


def last_week():
    global weeks
    if weeks > 1:
        weeks -= 1
        read_class(weeks)
        week.set(weeks)

跳轉周

上下週按了太多了?直接輸入數字快速跳轉周吧。

需要判斷輸入的是否是數字,如果不是數字就判斷爲無效輸入。

# 跳轉
def jump():
    global weeks

    if entry.get().isnumeric():
        weeks = eval(entry.get())
        read_class(weeks)
        week.set(weeks)
    else:
        week.set(weeks)

## 得到一個彩色

tkinter用的是兩位十六進制保存紅、綠、藍顏色的數據。不同數值混合得到的顏色不同。結果前面需要加個“#”。我們只取中間色,在5到B之間取。最後返回一個類似“#AAAAAA”的數據。

def get_a_color():
    # 多彩效果
    text = "56789AB"
    color = "#"
    for i in range(6):
        index = randint(0, len(text) - 1)
        color = color + text[index]
    return color

管理課程

我們對課程數據的管理有獲取、增加、刪除、保存的操作。

用列表保存課程名,如果獲取之後可以修改。

增加課程時,隨機獲取一種顏色,保存到數據中。

上面兩個,都需要用戶按下保存時才進行保存。

刪除時,直接刪除課程。

# 管理課程
def command():
    def get_info():
        data = js[list.get(tkinter.ACTIVE)]

        subjects = data["subject"]
        data_information.delete(0.0, tkinter.END)
        data_information.insert(0.0, "{} {}\n".format(list.get(tkinter.ACTIVE), data["color"]))
        for subject in subjects:
            if len(subject["teacher"]) > 7:
                teacher = subject["teacher"][0:7] + "等"
            else:
                teacher = subject["teacher"]

            scr = "{} {} {} {} {}\n". \
                format(teacher, subject["week"], subject["weekday"], subject["address"], subject["part"])
            data_information.insert(tkinter.INSERT, scr)

    def new():
        data_information.delete(0.0, tkinter.END)
        data_information.insert(0.0, "課程名 {}\n教師 1-20周(單) 星期一 地點 1-12".format(get_a_color()))

    def save():
        scr = data_information.get(0.0, tkinter.END)
        scr = scr.split("\n")
        name = scr[0]
        subject = []
        for i in scr[1:-1]:
            if i == "":
                pass
            else:
                i = i.split(" ")
                subject.append({"teacher": i[0], "week": i[1], "weekday": i[2], "address": i[3], "part": i[4]})
        class_key = scr[0].split(" ")
        js[class_key[0]] = {"color": class_key[1], "subject": subject}

        with open("my_class.json", "w") as f:
            json.dump(js, f)

        myself_flesh()

    def delete():
        js.pop(list.get(tkinter.ACTIVE))
        with open("my_class.json", "w") as f:
            json.dump(js, f)
            myself_flesh()

    def myself_flesh():
        list.delete(0, tkinter.END)
        n = 0
        for i in js:
            list.insert(n, i)
            n += 1
        list.pack(side=tkinter.LEFT)

    command_win = tkinter.Tk()  # 創建一個窗體
    command_win.geometry("500x200+200+50")  # 改變窗體的大小
    command_win.title('管理數據')
    command_win.resizable(0, 0)

    list = tkinter.Listbox(command_win)
    n = 0
    for i in js:
        list.insert(n, i)
        n += 1
    list.pack(side=tkinter.LEFT)

    data_frame = tkinter.LabelFrame(command_win, text="數據詳情")
    data_frame.pack(side=tkinter.LEFT)

    button_frame = tkinter.Frame(data_frame)

    button_get = tkinter.Button(button_frame, text="獲取", command=get_info)
    button_get.pack(side=tkinter.LEFT)

    button_new = tkinter.Button(button_frame, text="新增", command=new)
    button_new.pack(side=tkinter.LEFT)

    button_save = tkinter.Button(button_frame, text="保存", command=save)
    button_save.pack(side=tkinter.LEFT)

    button_del = tkinter.Button(button_frame, text="刪除", command=delete)
    button_del.pack(side=tkinter.LEFT)

    button_frame.pack()

    data_information = tkinter.Text(data_frame)
    data_information.pack()

實現的效果如下:

在這裏插入圖片描述

設置周

用戶可以直接輸入起始周,也可以更改時間。

def set_time():
    def save():
        # 判斷是否有效日期
        try:
            datetime.datetime.strptime(start_time.get(), "%Y-%m-%d")
            split_time = start_time.get().split("-")
            split_time = [eval(split_time[0]), eval(split_time[1]), eval(split_time[2])]
            init["start"] = split_time

        except Exception:
            start_time.delete(0, tkinter.END)
            start_time.insert(0, "{}-{}-{}".format(init_year, init_mouth, init_day))

        # 修改課程時間
        part = text.get(0.0, tkinter.END).split("\n")
        dic = {}
        n = 0
        for little_part in part:
            if little_part == "":
                pass
            else:
                n += 1
                dic[str(n)] = little_part
        init["time"] = dic

        # 保存數據
        with open("data.json", "w") as f:
            json.dump(init, f)

    set_time_win = tkinter.Tk()  # 創建一個窗體
    set_time_win.geometry("250x200+200+50")  # 改變窗體的大小
    set_time_win.title('關於軟件')
    set_time_win.resizable(0, 0)

    init_year = init["start"][0]
    init_mouth = init["start"][1]
    init_day = init["start"][2]

    frame = tkinter.Frame(set_time_win)
    frame.pack()

    label = tkinter.Label(frame, text="起始周的時間")
    label.pack(side=tkinter.LEFT)

    start_time = tkinter.Entry(frame)
    start_time.insert(0, "{}-{}-{}".format(init_year, init_mouth, init_day))
    start_time.pack()

    times = tkinter.LabelFrame(set_time_win, text="每節課的時間")
    times.pack(side=tkinter.LEFT)

    text = tkinter.Text(times, width=25, height=10)
    dic = init["time"]
    for i in dic:
        text.insert(tkinter.END, dic[i] + "\n")
    text.pack()
    print(dic)

    button_save = tkinter.Button(set_time_win, text="保存", command=save)
    button_save.pack(side=tkinter.RIGHT)

實現的效果如下:

在這裏插入圖片描述

轉換數據

參照我的上一篇博客,從教務處導入數據,並把轉換的結果顯示在窗口的右邊。

# 改變
def data_change():
    change_win = tkinter.Tk()  # 創建一個窗體
    change_win.geometry("500x265+200+50")  # 改變窗體的大小
    change_win.title('導入數據')
    change_win.resizable(0, 0)

    data_change.origin = tkinter.Text(change_win, height=20, width=30)
    data_change.origin.pack(side=tkinter.LEFT)

    frame_button = tkinter.Frame(change_win, height=300, width=100)
    frame_button.pack(side=tkinter.LEFT)

    data_change.translate = tkinter.Text(change_win, height=20, width=30)
    data_change.translate.pack(side=tkinter.LEFT)

    button_translate = tkinter.Button(frame_button, text="轉換並保存", command=write)
    button_translate.pack(side=tkinter.TOP)

    change_win.mainloop()

然後要寫入數據,

def write():
    key = init["book"]
    text = json.loads(data_change.origin.get(0.0, tkinter.END))  # 轉化爲json
    data = {}

    # 抽象化讀取字典
    for i in init["local"]:
        text = text[i]

    for course in text:
        class_data = {}
        print(course[key["name"]])
        if course[key["name"]] in data:
            subject = data[course[key["name"]]]["subject"]
            print(course[key["name"]])
        else:
            subject = []
        subject.append({"teacher": course[key["teacher"]], "week": course[key["week"]],
                        "weekday": course[key["weekday"]],
                        "address": course[key["address"]], "part": course[key["part"]]})

        class_data["color"] = get_a_color()
        class_data["subject"] = subject
        data[course[key["name"]]] = class_data

    with open("my_class.json", "w") as f:
        json.dump(data, f)

    data_change.translate.insert(0.0, data)

實現的效果如下:

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章