加分程式是我為了班上所做的一個計分軟體,視窗中有數字鍵盤,鍵入號碼就可以加分,主要是看在老師常使用電腦,但卻仍然依靠小老師計分數,手癢做出來的小品。

這是最新版本的運行畫面(裡面的數據是亂輸的)。

先丟網址:https://github.com/lancatlin/AddPoint/

歡迎下載!!

我的加分程式主要是為我班導設計,由於上課通常是1分1分慢慢加,所以我的程式是設計輸入號碼,再按加扣分。比較厲害的功能是還有分組計分功能,不過其實它只是將一組的所有成員分數加總,你並不能把分數直接加在「組」上,(所以才說是為班導設計)。歷史紀錄會儲存最近的五十筆,也有返回功能,但是只要重新啟動程式後的紀錄就不能返回(可以用扣回去的)。還有多班級功能,一個程式下方會有很多個班級的檔案資料夾,所以科任老師可以將程式放於隨身碟,到班上電腦執行,這樣在統計時也可以在自己的電腦處理。最後,還有一個(很沒意義的)加密功能,嗯…真的很沒意義,誰會去想篡改這種資料? 不過我就是做了,老師可以設定一組密碼,在每次打開的時候輸入才能進去程式,不過密碼本身也得找地方存吧? 所以你還得「加密」密碼,然後在加密加密密碼的密碼……好沒這回事,加密密碼的密碼就藏在程式碼中了,文章的下一篇將會詳細說明我的加密方法。(程式要被駭了!!!)

Scratch版本

還不會Python之前我只會scratch,想到這個專案時就從scratch開始,功能不多,只有基本的加分和扣分, 可說是完全的「加分程式」,用一個列表儲存分數,12個鍵盤分身(用不同造型,程式內容雷同)輸入號碼,由於scratch沒有存取外部檔案的功能,所以一定得用scratch開,不能夠用匯出swf檔開,而且每次都必須「儲存」,只要離開時沒存到,上次的紀錄就沒了。要輸出檔案的時候,必須用列表的「輸出」才能取得單純分數的文字檔案,實在不方便。

但是這支程式有一個我很滿意之處,就是透過「分身」來製作鍵盤的十二個按鍵,什麼意思呢?這十二個按鍵是用同一支程式運行的,個體差異在於名稱變數所對應到的動作。這樣寫的優點在於可以重複利用程式碼,對於事後維護比較方便,而且十分精簡!

雖說沒有做到很好,不過班導仍然非常開心XD。

scratch版連結

 

Python Tkinter版

到了二上,學會了python,看著舊的版本實在忍不住,於是就著手做了新的Python版本。

我用的是最簡單的GUI:Tkinter(以下簡稱tk),很輕巧也很方便使用,介面也還可以。(就是上方照片的模樣)。下面針對各個文件的程式進行說明。

 

視窗設計:AddPoint2.py

關於視窗設計,我使用一個ListBox來顯示每位同學的分數,上面裝一個ScrollBar(滑輪?我不清楚中文)來上下拉動。其實一般電腦使用只要滑鼠游標在上頭,滾輪就可以上下移動了,何必裝個ScrollBar?(我真的不是故意想秀英文,而且我也沒東西秀),是為了讓老師在電子白板上能用,電子白板上沒有滑輪,要能夠上下拉動就必須要有ScrollBar。

至於加分的按鍵,就如同上方圖片所看到的,我是用14顆按鈕來做設計。偷偷說個題外話,其實在使用Label或著Button時,不需要寫一個變數去存它,為什麼?因為你根本不會再碰它了!Label是文本標籤,Button是按鈕,文本標籤如果你想改變上面的字,必須要用StringVar來處理,你就算把Label存了也沒用,只是白白浪費你的命名空間而已。Button也是同理,再指定Button的command後,你就不需要理它了,你就只要等待獵物上門按鈕按下,讓函數做對應的動作就好。

一講到題外話,我又想到我過去做Tk的一個愚蠢錯誤,程式碼如下

my_entry = Entry(window, width=8).grid(row = 0, colomn=2)
print(my_entry.get())

大致看起來好像沒什麼問題?編譯跑起來也都對,但是當你使用my_entry.get()時,Python就會溫柔的告訴你:「不好意思,您所指定的變數是一個None喔!」(我開玩笑的,Python編譯器不會說中文),那時候我在那邊傻了整整兩個小時,後來還到Python Taiwan去發問(感謝Python Taiwan社團總是有許多熱心的前輩為人解惑),搞了半天,原來是我在設定Entry變數的時候直接在後面放grid(),這怎麼了嗎?grid是無返回值的呀!grid()是Tk的排版管理員,負責將元件擺放到你設定的位置上,但是本來Entry會返回一個Entry給你,但是如果直接在後面加grid,grid又是沒返回值的函數(就算有也不會是那個Entry),結果你寫好的變數,裡面是空的!(None)

又回到剛才所說的,不要把Label或Button存到變數中,這沒有意義!本來可以一行解決的拖到兩行,而且害你少了一個可以使用的名字。

講一講有點遠了,回到剛才的話題:用14個按鈕來當作鍵盤,說到這邊實在也是有點羞愧,因為我的作法是將每個按鍵的文字存在一個陣列裡,用一個for迴圈製造14個Button(這部份還不錯,自動排列),但是我將每一個Button的command都指定到同一個函數—self.button_up,但是每一個按鍵有不同的功能阿!0~9要用同一個函數沒錯,但是像清除、復原這些,功能差這麼多總該要分開放了吧?我在設定command時,偷偷加了一項參數i,就是for迴圈的變數,用途就是區分「這是第幾顆按鈕」,再利用這些索引對應到的名字用if else做分別的動作。雖然現在看來不喜歡,但是也很難提出更好的作法(我那小小的腦袋想不出來),雖然很醜,但是也簡化了很多動作,雖然很醜…

self.button=['1','2','3','4','5','6','7','8','9','C','0','回復','扣分','加分']
for i in range(14):
    Button(f32, text=self.button[i], width=4, height=1, font=_font(30), command=lambda x=i:self.button_up(x))\
    .grid(row=i//3, colomn=i%3, padx=9, pady=10)

加了分,那加錯怎麼辦?不要加錯就好啦!要做復原。在每一次的加分動作,都會把這一次的紀錄一起加到self.history裡面,history的詳細內容到下面data時再講,這裡主要講GUI界面。跟score一樣,我使用ListBox來顯示history,並且讓使用者藉由「回復」按鈕來撤銷上一次的動作,沒有撤銷紀錄上限(還是有啦!你做多少動作就是最高的上限嘛),這個部份就跟個人分數一樣啦!

 

資料處理:data.py

最早的版本是單純計分,並自動存檔。還蠻容易的,只要簡單做些資料處理,再用open()就完成了,不過在資料處理上倒繞了個彎,本來想說可能會需要很多功能,用物件來儲存每個人的分數,後來發現是多次一舉,改成用字典dict來儲存每個人的分數。

說到多此一舉可能還不只這個,我的分數管理是由score_manager物件負責的(後來發現沒有按照命名規則,應該為ScoreManager),有一個陣列裡面有每一個人的資料,將每一個人的分數由一個字典(dict)做儲存,裡面的標籤有name, score,現在回來看實在覺得可笑,既然只有兩個那兩個標籤,幹嘛不直接用dict儲存每個人的資料就好了?像是[1]:5,省下一層陣列還有後面for迴圈的使用。不過這還不是最誇張的,為了這些分數的更新和讀取,我還另外寫了兩個函數update和get,其中get為了格式化字串還寫了落落長的迴圈...看到這邊我實在恨不得回去全部重寫一次。

還記得剛剛提到的history嗎?在data.py也有專門一個物件負責處理history,名字就叫做history(好爛),功能很少,就只有read跟write而已,只有這樣實在沒什麼寫成物件的意義。在程式啟動時,它就會先去讀取資料夾內的history.sc檔案,如果沒有就寫一個新的,這就是read,然後write就是主程序把歷史的陣列傳給它,它再挑出前50寫進文件裡,並且用換行符號相隔。(剛剛接獲通知,我有個地方的路徑寫錯了,難怪第一次啟動都會有個不在班級內的history.sc跑出來)

class history:
    def __init__(self, room):
        self.path = room+'//history.sc'            #設定路徑

    def read(self):
        try:
            with open(self.path, 'r') as file:
                data = file.read().split('\n')
                print(data)
                return data
        except FileNotFoundError:
            with open('history.sc', 'w') as file:   #這裡的路徑寫錯了,應該為self.path
                return []

後來因應班上英文課的分組,想說如果能將每組的個人分數加總顯示出來,應該會更好,於是開始製作分組顯示的程式。關於這部份的設計我必須先澄清,小組並沒有「自己的」分數,小組只是將組員個別的分數加總,並顯示出來,你並不能加分加在一個「組」上,可以將它想像成標籤,為每一個組員貼上標籤,並將都有這標籤的組員分數加總顯示。

等等!程式要怎麼知道誰跟誰一組?當然是寫進文件裡阿!在程式執行前,使用者需要先用我的設定工具creat來做各種設定,creat程式後面會詳細說明。

就跟你想的一樣,我寫了一個物件來處理分組的資訊,剛剛看了程式碼,我發現我的team寫成了青少年teen......我的英文真的有待加強(還好班導看不到程式碼)。

team這個物件呢它使用的資料是前面的score_manager,主程式將自己的score_manager物件用參數傳送給team物件,這樣team物件就能夠存取score_manager中各個組員的分數,進而加總回報到主程式。在程式剛啟動時,team會去找有沒有team.sc這個文件,如果有就讀取,如果沒有那就自己腦補 回報說使用者沒有分組。這邊說個笑話,使用file.readlines在尾端會有一個換行符號'\n',而我為了要移除這個換行符號,大費周章用了[0: -1]切片來把它弄掉,實在很好笑,其實直接用file.read().split('\n')就好了嘛!

team.sc組員名單文件它的格式是這樣的,第一個字放小組名稱,接下來放個冒號,把組員用逗號相隔一個一個擺上去,實在是還滿麻煩的,而且格式很脆弱,你多加個空白程式就會把'6'變成' 6',結果變成完全不同的人,所以我的creat.py也有放專門幫助你輸入組員名單的程式,它會帶著你一步一步輸入每一個人,輸入空白就是換組,輸入exit就結束...好像也沒有比較簡單?

組員名單範例:

1:1,2,3,4
2:5,6,7,8
3:9,10,11,12

當然組員不需要照號碼排,而是要照實際情況做排列。

team的資料處理是用陣列裡面放字典,字典有'name', 'people', 'score'等key,這裡我比較能接受是因為好歹它也有三個key,不像剛剛只有兩個key的,實在是罪該萬死(喂!),有update和get兩個method,update會每個組的加總分數,get會給你格式化後的字串列表,以「1組(6) : 5分」的形式顯示出來,( )裡面放的是每組的第一位成員,用來幫助判斷自己是哪一組。這個team有一個不太好的地方,就是我把update和get分開來做,update是更新目前組的總分,基本上你要get當然就是要最新的啊!多做一個要幹嘛?

class teen:
    def __init__(self, manager, room):
        self.path = room+'/teen.sc'
        self.manager = manager
        self.mode = True
        try:
            file = open(self.path, 'r')
            t = file.readlines()    #就是因為這個弄了好久
            self.data = []
            for i in t:
                i = i[0:-1]         #為了把換行符號弄掉
                n, a = i.split(':')
                p = []
                for x in a.split(','):  #呃...沒有錯只是有點遜XD
                    p.append(int(x))
                self.data.append({'name': n, 'people': p, 'score': 0})
            print(self.data)
        except FileNotFoundError:
            self.mode = False       #沒有組員名單就說沒有

    def update(self):
        for i in self.data:
            i['score'] = 0
            for j in i['people']:
                i['score'] += self.manager.get(False,j)  #將同一組所有成員分數加總
        return self.data

    def get(self):
        self.update()
        result = []
        for i in self.data:
            result.append('%s組(%s): %s' % (i['name'], i['people'][0], i['score']))
        return result
資料處理大概就到這樣啦!其實這些事都不難,是我把它做複雜了,過去的我真的是把「簡單的事做難」了。

下一篇將會介紹我是如何做加密及設定的,敬請期待。

後記:

話說我過去也說要把雙系統安裝的第二篇給發一發,但已經斷坑很久了(茶)

希望大家喜歡我的作品分享!

 

文章標籤
全站熱搜
創作者介紹
創作者 Cat Lan 的頭像
Cat Lan

Lancat Server 懶貓伺服器

Cat Lan 發表在 痞客邦 留言(0) 人氣(461)