002.02 Tkinter 圖形介面之文字範例

Jason990420發表於2019-08-26

主題: 002.02 Tkinter圖形介面之文字範例

建檔日期: 2019/08/26
更新日期: None
語言: Python 3.7.4, tkinter 8.6
系統: Win10 Ver. 10.0.17763 繁體中文版(TW)

002.02 Tkinter圖形介面之文字範例

範例以tkinter建立程式, 供使用者建立文字檔案, 在過程中記錄下使用者輸入中文字的關連性, 隨後提供中文輸入協助的快捷鍵, 對輸入比較的人可以起到有效的協助. 主要用到的部件有Tk(), Frame(), Button(), Label()以及Text(), 還有按鍵及滑鼠事件處理; 另外還有檔案對話filedialog及訊息框messagebox的使用.

程式介面

002.02 Tkinter圖形介面之文字範例

'''
使用Tkinter的Text部件, 建立一個簡單的文字編輯器, 主要的功能為:

[1] 系統會按使用者輸的中文字, 記錄下使用者輸入的第二個中文字的使用情形, 下次
    再輸入這個字, 會提供前十二個最常使用的下一個字, 供使用者以F1~F12按鍵直接輸入.
[2] 使用者可以讀入以前的文字檔案, 直接學習, 建立自己的字典
[3] 這個輔助輸入的字典, 是專為個人使用, 不是一般通用的字典, 一般來說, 個人常用
    的詞彙大都在幾百字, 對輸入較慢的人, 可以起到很大的作用.

'''
from tkinter.filedialog import *
from tkinter import *
from tkinter import messagebox

def enter(event):   # 滑鼠進入物件, 背景改為黑色
    event.widget.config(bg='black')

def leave(event):   # 滑鼠離間物件, 背景改為原背景色
    event.widget.config(bg=gnd)

def open_new_file():
    '''
    開啟文字檔案, 格式以utf-8, utf-16, GB18030來讀取
    '''
    # 開啟檔案對話方塊
    filename = filedialog.askopenfilename(title = "選擇檔案",
        filetypes = (("文字檔案","*.txt"),("所有檔案","*.*")))
    new_text = None
    # 開啟檔案, 試以utf-8, utf-16及GB18030來開啟, 無法開啟則顥示錯誤
    if filename != "":
        print(filename)
        try:
            print('utf-8')
            new_f = open(filename, 'rt', encoding = 'utf-8')
            new_text = new_f.read()
        except UnicodeDecodeError as e:
            try:
                print('utf-8 Failed')
                new_f = open(filename, 'rt', encoding = 'utf-16')
                new_text = new_f.read()
            except UnicodeDecodeError as e:
                try:
                    print('utf-16 Failed')
                    new_f = open(filename, 'rt', encoding = 'GB18030')
                    new_text = new_f.read()
                except UnicodeDecodeError as e:
                    print('GB18030 Failed')
                    pass
        # 檔案讀入, 顯示在TEXT部件中
        if new_text != None:
            print(new_f)
            print(new_text)
            main_widget.t1.delete('1.0', END)
            main_widget.t1.insert(END, new_text)
            main_widget.t1.edit_modified(False)
        else:
            messagebox.showinfo(title='檔案開啟錯誤', message='文字檔案格式無法讀取或檔案有問題')

def save_new_file():
    # 開啟存檔對話方塊
    filename = filedialog.asksaveasfilename(title = "儲存檔案",
        filetypes = (("文字檔案","*.txt"),("所有檔案","*.*")))
    if filename != "":
        if filename[-4:].lower() != '.txt':
            filename = filename + '.txt'
        new_f = open(filename, 'wt', encoding = decoder)
        new_f.write(main_widget.t1.get('1.0',END))
        new_f.close()

def end_job():
    # 字典存檔, 下次可再使用
    if dictionary != {}:
        datafile = open(dictionary_file, 'wt', encoding=decoder)
        datafile.write(str(dictionary))
        datafile.close()
    root.destroy()
    exit()

def learn_dictionary():
    # 讀入的檔案內容, 分析內容, 加入字典中
    txt = main_widget.t1.get('1.0',END)
    for i in range(len(txt))[1:]:
        pre_char = txt[i-1]
        new_char = txt[i]
        add_dictionary(pre_char, new_char)

def cmd(i):
    # 按鈕處理程式
    if i == 0:      # 開啟檔案
        if main_widget.t1.edit_modified():
            answer = messagebox.askquestion ('開啟檔案',
                '內容已修改, 是否捨棄, 另開新檔 ?',icon = 'warning')
            if answer == 'yes':
                open_new_file()
        else:
            open_new_file()

    elif i == 1:    # 存入檔案
        save_new_file()

    elif i == 2:    # 學習本文中的內容
        learn_dictionary()

    elif i == 3:    # 結束離開
        if main_widget.t1.edit_modified():
            answer = messagebox.askquestion ('離開編輯',
                '內容已修改, 是否捨棄離開 ?',icon = 'warning')
            if answer == 'yes':
                end_job()
        else:
            end_job()

def new_button(f, string, column, row):
    # 建立功能按鈕
    obj = Button(f, text=string, font=font, width=len(string)*2, height=1, bg=gnd,
            fg='white', bd=0, activebackground=gnd, command = lambda i = column: cmd(i))
    obj.bind('<Enter>', enter)
    obj.bind('<Leave>', leave)
    obj.pack(side=LEFT)
    return obj

def new_label(f, string, column, row):
    # 建立F1~F12的標籤
    obj = Label(f, text=string, font=font, width=len(string), height=1, bg=gnd,
            fg=l_gnd, bd=0, activebackground=gnd)
    obj.pack(side=LEFT)
    return obj

def read_dictionary():
    # 讀入已有的字典, 格式為{}空字典, {'字':{'字':次數, ....}, ....}
    dic_f = open(dictionary_file, 'rt', encoding = decoder)
    a = dic_f.read()
    dictionary = eval(a)
    dic_f.close()
    return dictionary

def write_dictionary():
    # 字典寫入檔案
    dic_f = open(dictionary_file, 'wt', encoding = decoder)
    dic_f.write(str(dictionary))
    return dictionary

def find_char(char):
    # 取該字的下一個最常用字清單, 共十二個, 沒有補全格空格
    char_dic = dictionary[char].items()
    rev_list = [[v[1], v[0]] for v in char_dic]
    rev_list.sort(reverse=True)
    word_list = [v[1] for v in rev_list]
    if len(word_list)<12:
        for i in range(12-len(word_list)):
            word_list.append(' ')
        else:
            word_list = word_list[:12]
    return word_list

def find_key(char):
    # 取得對應的常用字列表, 不在字典中, 則給十二個全格空格
    if char not in dictionary:
        word_list = [' ' for i in range(12)]
    else:
        word_list = find_char(char)
    return word_list

def update_char(char):
    # 顯示十二個常用字的內容
    word_list = find_key(char)
    for i in range(len(key_list)):
        main_widget.key[i].config(text=word_list[i])

def add_dictionary(pre_char, new_char):
    '''
    檢查是不是中文字 ?
        是中文字, 前一個字也是中文字的話
            是新字, 則加入字典
            不是新字, 該字的次數加1
    '''
    if '\u4e00' <= pre_char <= '\u9fa5':
        if '\u4e00' <= new_char <= '\u9fa5':
            if pre_char in dictionary:
                if new_char in dictionary[pre_char]:
                    dictionary[pre_char][new_char] += 1
                else:
                    dictionary[pre_char][new_char] = 1
            else:
                dictionary[pre_char] = {new_char:1}

def key_action(event):

    if 112<=event.keycode<=123: # 檢查是不是F1~F12按鍵
        insert_char = main_widget.key[event.keycode-112]['text']
        if insert_char != ' ':
            main_widget.t1.insert(INSERT,insert_char)
            update_char(insert_char)
    else:                       # 按輸入字, F1~F12內容更新
        char = event.widget.get("%s-1c" % INSERT, INSERT)
        update_char(char)
    # 檢查前一字, 與現在的字, 字典更新處理
    pre_char = event.widget.get("%s-2c" % INSERT, "%s-1c" % INSERT)
    new_char = event.widget.get("%s-1c" % INSERT, INSERT)
    add_dictionary(pre_char, new_char)

class main_window():
    '''
    主視窗建立顯示
    Frame1放四個功能鍵(save, write, analysis, exit)
    Frame2放十二個標籤(F1 ~ F12)
    Frame3放一個文字再加一個垂直捲軸
    '''
    def __init__(self):

        menu_b = []
        self.f_all = Frame(root, bg=gnd, padx=pad, pady=pad)
        self.f_all.pack()
        self.f1 = Frame(self.f_all, bg=gnd)
        self.f1.pack(side=TOP)
        for i in range(len(menu)):
            menu_b.append(new_button(self.f1, menu[i], i, 0))

        self.f2 = Frame(self.f_all, bg=gnd)
        self.f2.pack(side=TOP)
        self.key=[]
        for i in range(len(key_list)):
            new_label(self.f2, key_list[i], i*2, 1)
            self.key.append(new_button(self.f2, word_list[i], i*2, 1))

        self.f3 = Frame(self.f_all, bg=gnd)
        self.f3.pack(side=TOP)
        self.t1 = Text(self.f3, font=font, fg='white', bg=l_gnd, width=80,
            height=20, padx=pad, pady=pad)
        self.t1.pack(side=LEFT, fill=Y)
        self.s1 = Scrollbar(self.f3)
        self.s1.pack(side=RIGHT, fill=Y)
        self.s1.config(command=self.t1.yview)
        self.t1.config(yscrollcommand=self.s1.set)

        # 按鍵放開處理
        self.t1.bind('<KeyRelease>', key_action)

font_size = 20
font = ('微軟黑體', font_size, 'bold')
pad = 10
gnd = 'gray20'
l_gnd = 'gray40'
title = '輔助輸入編輯器 V1.0'
decoder = "utf-8-sig"
key_list = ["F1 ","F2 ","F3 ","F4 ","F5 ","F6 ", "F7 ","F8 ","F9 ","F10","F11","F12"]
dictionary_file = 'dictionary.txt'  # 字典檔名
menu = ('開啟檔案', '儲存檔案', '學習內容', '離開編輯')

dictionary = read_dictionary()          # 讀入字典
word_list = [" " for i in range(12)]   # F1 ~ F12 空白內容

root = Tk()
root.title(title)
root.resizable(False, False)
main_widget = main_window()             # 建立程式視窗內容

root.mainloop()

# dictionary.txt 新檔自建內容為空字典 {}
# dictionary.txt 已有的內容示範
{'中': {'文': 2, '有': 2, '運': 2, '一': 1, '避': 1, '誕': 1, '類': 1, '繁': 1, '的': 5, '出': 3, '所': 1, '集': 1, '討': 1, '和': 1}, '文': {'維': 1, '件': 11, '本': 9, '獻': 3, '單': 1, '檔': 1, '語': 1, '化': 1, '字': 1}, '維': {'基': 27, '圖': 1, '護': 3}, '基': {'百': 8, '金': 6, '數': 3, '教': 4, '於': 14, '本': 2, '共': 3, '語': 2, '學': 2, '社': 1, '媒': 3}, '百': {'科': 9, '上': 1}, '科': {'條': 1, '全': 1, '書': 5, '學': 9, '技': 1, '的': 1, '標': 1, '免': 1}, '條': {'目': 4, '件': 4, '款': 3}, '目': {'協': 1, '錄': 2, '的': 4, '標': 1, '討': 1, '中': 1}, '協': {'作': 1, '調': 1, '議': 5, '程': 1}, '作': {'計': 1, '系': 7, '為': 11, '簡': 1, '者': 1, '變': 1, '業': 2, '用': 2, '的': 1, '是': 2, '對': 1, '配': 1}, '計': {'劃': 4, '者': 1, '哲': 4, '算': 16, '的': 9, '工': 1, '任': 1, '為': 1, '師': 1, '目': 1, '時': 1, '允': 1, '有': 1, '社': 1, '框': 1, '主': 1}, '劃': {'專': 1, '分': 1, '例': 1, '線': 6, '中': 1}, '專': {'頁': 1, '門': 4, '業': 1}, '頁': {'已': 1, '設': 2, '技': 1, '面': 4}, '已': {'建': 1, '在': 1, '決': 1, '時': 1, '經': 7, '無': 1, '正': 1}, .......... }
本作品採用《CC 協議》,轉載必須註明作者和本文連結
Jason Yang

相關文章